github.com/prysmaticlabs/prysm@v1.4.4/tools/gocovmerge/main.go (about)

     1  // gocovmerge takes the results from multiple `go test -coverprofile` runs and
     2  // merges them into one profile
     3  //
     4  // Copied, with minor changes, from https://github.com/wadey/gocovmerge under BSD-2-Clause License
     5  package main
     6  
     7  import (
     8  	"flag"
     9  	"fmt"
    10  	"io"
    11  	"log"
    12  	"os"
    13  	"sort"
    14  
    15  	"golang.org/x/tools/cover"
    16  )
    17  
    18  func mergeProfiles(p, merge *cover.Profile) {
    19  	if p.Mode != merge.Mode {
    20  		log.Fatalf("cannot merge profiles with different modes")
    21  
    22  	}
    23  	// Since the blocks are sorted, we can keep track of where the last block
    24  	// was inserted and only look at the blocks after that as targets for merge
    25  	startIndex := 0
    26  	for _, b := range merge.Blocks {
    27  		startIndex = mergeProfileBlock(p, b, startIndex)
    28  
    29  	}
    30  
    31  }
    32  
    33  func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) int {
    34  	sortFunc := func(i int) bool {
    35  		pi := p.Blocks[i+startIndex]
    36  		return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol)
    37  
    38  	}
    39  
    40  	i := 0
    41  	if !sortFunc(i) {
    42  		i = sort.Search(len(p.Blocks)-startIndex, sortFunc)
    43  
    44  	}
    45  	i += startIndex
    46  	if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol {
    47  		if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol {
    48  			log.Fatalf("OVERLAP MERGE: %v %v %v", p.FileName, p.Blocks[i], pb)
    49  
    50  		}
    51  		switch p.Mode {
    52  		case "set":
    53  			p.Blocks[i].Count |= pb.Count
    54  		case "count", "atomic":
    55  			p.Blocks[i].Count += pb.Count
    56  		default:
    57  			log.Fatalf("unsupported covermode: '%s'", p.Mode)
    58  
    59  		}
    60  
    61  	} else {
    62  		if i > 0 {
    63  			pa := p.Blocks[i-1]
    64  			if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) {
    65  				log.Fatalf("OVERLAP BEFORE: %v %v %v", p.FileName, pa, pb)
    66  
    67  			}
    68  
    69  		}
    70  		if i < len(p.Blocks)-1 {
    71  			pa := p.Blocks[i+1]
    72  			if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) {
    73  				log.Fatalf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb)
    74  
    75  			}
    76  
    77  		}
    78  		p.Blocks = append(p.Blocks, cover.ProfileBlock{})
    79  		copy(p.Blocks[i+1:], p.Blocks[i:])
    80  		p.Blocks[i] = pb
    81  
    82  	}
    83  	return i + 1
    84  
    85  }
    86  
    87  func addProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile {
    88  	i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName })
    89  	if i < len(profiles) && profiles[i].FileName == p.FileName {
    90  		mergeProfiles(profiles[i], p)
    91  
    92  	} else {
    93  		profiles = append(profiles, nil)
    94  		copy(profiles[i+1:], profiles[i:])
    95  		profiles[i] = p
    96  
    97  	}
    98  	return profiles
    99  
   100  }
   101  
   102  func dumpProfiles(profiles []*cover.Profile, out io.Writer) {
   103  	if len(profiles) == 0 {
   104  		return
   105  
   106  	}
   107  	if _, err := fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode); err != nil {
   108  		panic(err)
   109  	}
   110  	for _, p := range profiles {
   111  		for _, b := range p.Blocks {
   112  			if _, err := fmt.Fprintf(out, "%s:%d.%d,%d.%d %d %d\n", p.FileName, b.StartLine, b.StartCol, b.EndLine, b.EndCol, b.NumStmt, b.Count); err != nil {
   113  				panic(err)
   114  			}
   115  
   116  		}
   117  
   118  	}
   119  
   120  }
   121  
   122  func main() {
   123  	flag.Parse()
   124  
   125  	var merged []*cover.Profile
   126  
   127  	for _, file := range flag.Args() {
   128  		profiles, err := cover.ParseProfiles(file)
   129  		if err != nil {
   130  			log.Fatalf("failed to parse profiles: %v", err)
   131  
   132  		}
   133  		for _, p := range profiles {
   134  			merged = addProfile(merged, p)
   135  
   136  		}
   137  
   138  	}
   139  
   140  	dumpProfiles(merged, os.Stdout)
   141  
   142  }