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 }