code.gitea.io/gitea@v1.21.7/build/gocovmerge.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // Copyright (c) 2015, Wade Simmons 3 // SPDX-License-Identifier: MIT 4 5 // gocovmerge takes the results from multiple `go test -coverprofile` runs and 6 // merges them into one profile 7 8 //go:build ignore 9 10 package main 11 12 import ( 13 "flag" 14 "fmt" 15 "io" 16 "log" 17 "os" 18 "sort" 19 20 "golang.org/x/tools/cover" 21 ) 22 23 func mergeProfiles(p, merge *cover.Profile) { 24 if p.Mode != merge.Mode { 25 log.Fatalf("cannot merge profiles with different modes") 26 } 27 // Since the blocks are sorted, we can keep track of where the last block 28 // was inserted and only look at the blocks after that as targets for merge 29 startIndex := 0 30 for _, b := range merge.Blocks { 31 startIndex = mergeProfileBlock(p, b, startIndex) 32 } 33 } 34 35 func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) int { 36 sortFunc := func(i int) bool { 37 pi := p.Blocks[i+startIndex] 38 return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol) 39 } 40 41 i := 0 42 if sortFunc(i) != true { 43 i = sort.Search(len(p.Blocks)-startIndex, sortFunc) 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 switch p.Mode { 51 case "set": 52 p.Blocks[i].Count |= pb.Count 53 case "count", "atomic": 54 p.Blocks[i].Count += pb.Count 55 default: 56 log.Fatalf("unsupported covermode: '%s'", p.Mode) 57 } 58 } else { 59 if i > 0 { 60 pa := p.Blocks[i-1] 61 if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) { 62 log.Fatalf("OVERLAP BEFORE: %v %v %v", p.FileName, pa, pb) 63 } 64 } 65 if i < len(p.Blocks)-1 { 66 pa := p.Blocks[i+1] 67 if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) { 68 log.Fatalf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb) 69 } 70 } 71 p.Blocks = append(p.Blocks, cover.ProfileBlock{}) 72 copy(p.Blocks[i+1:], p.Blocks[i:]) 73 p.Blocks[i] = pb 74 } 75 return i + 1 76 } 77 78 func addProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile { 79 i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName }) 80 if i < len(profiles) && profiles[i].FileName == p.FileName { 81 mergeProfiles(profiles[i], p) 82 } else { 83 profiles = append(profiles, nil) 84 copy(profiles[i+1:], profiles[i:]) 85 profiles[i] = p 86 } 87 return profiles 88 } 89 90 func dumpProfiles(profiles []*cover.Profile, out io.Writer) { 91 if len(profiles) == 0 { 92 return 93 } 94 fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode) 95 for _, p := range profiles { 96 for _, b := range p.Blocks { 97 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) 98 } 99 } 100 } 101 102 func main() { 103 flag.Parse() 104 105 var merged []*cover.Profile 106 107 for _, file := range flag.Args() { 108 profiles, err := cover.ParseProfiles(file) 109 if err != nil { 110 log.Fatalf("failed to parse profile '%s': %v", file, err) 111 } 112 for _, p := range profiles { 113 merged = addProfile(merged, p) 114 } 115 } 116 117 dumpProfiles(merged, os.Stdout) 118 }