Skip to content

Commit d90b2fa

Browse files
authored
chore: integrate gocovmerge as a local dependency (#6458)
1 parent 0e9909d commit d90b2fa

File tree

6 files changed

+156
-2
lines changed

6 files changed

+156
-2
lines changed

.golangci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ issues:
3333
- path: tests
3434
linters:
3535
- gosec
36-
36+
- path: pkg/third_party
37+
linters:
38+
- gosimple
3739
- linters:
3840
- staticcheck
3941
text: "SA1019:" # Rule: Using a deprecated function, variable, constant or field

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ e2e-examples:
181181
./hack/e2e-examples.sh
182182

183183
gocovmerge:
184-
GOBIN=$(shell pwd)/bin/ $(GO) install github.com/zhouqiang-cl/gocovmerge@latest
184+
mkdir -p bin
185+
$(GO_BUILD) -o bin/gocovmerge ./pkg/third_party/gocovmerge
185186

186187
fault-trigger:
187188
$(GO_BUILD) -ldflags '$(LDFLAGS)' -o tests/images/fault-trigger/bin/fault-trigger tests/cmd/fault-trigger/*.go
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2015, Wade Simmons
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
7+
1. Redistributions of source code must retain the above copyright notice, this
8+
list of conditions and the following disclaimer.
9+
2. Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation
11+
and/or other materials provided with the distribution.
12+
13+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Upstream: https://github.com/zhouqiang-cl/gocovmerge
2+
Commit: 5256314471af44fe8bccbf0a1db1dd1f85637c1a
3+
Retrieved: 2025-09-19
4+
License: BSD-2-Clause (see LICENSE in this directory)
5+
6+
Local changes:
7+
- None. File copied as-is into `cmd/gocovmerge/main.go` with a short header comment pointing to this directory for license details.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ require (
5656
gocloud.dev v0.18.0
5757
golang.org/x/sync v0.10.0
5858
golang.org/x/time v0.5.0
59+
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
5960
gomodules.xyz/jsonpatch/v2 v2.4.0
6061
google.golang.org/grpc v1.59.0
6162
gopkg.in/yaml.v2 v2.4.0

pkg/third_party/gocovmerge/main.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Upstream: github.com/zhouqiang-cl/gocovmerge (commit 5256314471af44fe8bccbf0a1db1dd1f85637c1a)
2+
// License: BSD-2-Clause, see THIRD_PARTY_LICENSES/gocovmerge/LICENSE
3+
//
4+
// The source code below is copied from the upstream repository as-is,
5+
// to reduce external build-time dependencies in CI.
6+
//
7+
// gocovmerge takes the results from multiple `go test -coverprofile` runs and
8+
// merges them into one profile
9+
package main
10+
11+
import (
12+
"flag"
13+
"fmt"
14+
"io"
15+
"log"
16+
"os"
17+
"sort"
18+
19+
"golang.org/x/tools/cover"
20+
)
21+
22+
func mergeProfiles(p *cover.Profile, merge *cover.Profile) {
23+
if p.Mode != merge.Mode {
24+
log.Fatalf("cannot merge profiles with different modes")
25+
}
26+
// Since the blocks are sorted, we can keep track of where the last block
27+
// was inserted and only look at the blocks after that as targets for merge
28+
startIndex := 0
29+
for _, b := range merge.Blocks {
30+
startIndex = mergeProfileBlock(p, b, startIndex)
31+
}
32+
}
33+
34+
func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) int {
35+
if startIndex >= len(p.Blocks) { // no more to merge
36+
return startIndex
37+
}
38+
39+
sortFunc := func(i int) bool {
40+
pi := p.Blocks[i+startIndex]
41+
return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol)
42+
}
43+
44+
i := 0
45+
if sortFunc(i) != true {
46+
i = sort.Search(len(p.Blocks)-startIndex, sortFunc)
47+
}
48+
i += startIndex
49+
if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol {
50+
if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol {
51+
log.Fatalf("OVERLAP MERGE: %v %v %v", p.FileName, p.Blocks[i], pb)
52+
}
53+
switch p.Mode {
54+
case "set":
55+
p.Blocks[i].Count |= pb.Count
56+
case "count", "atomic":
57+
p.Blocks[i].Count += pb.Count
58+
default:
59+
log.Fatalf("unsupported covermode: '%s'", p.Mode)
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+
if i < len(p.Blocks)-1 {
69+
pa := p.Blocks[i+1]
70+
if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) {
71+
log.Fatalf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb)
72+
}
73+
}
74+
p.Blocks = append(p.Blocks, cover.ProfileBlock{})
75+
copy(p.Blocks[i+1:], p.Blocks[i:])
76+
p.Blocks[i] = pb
77+
}
78+
return i + 1
79+
}
80+
81+
func addProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile {
82+
i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName })
83+
if i < len(profiles) && profiles[i].FileName == p.FileName {
84+
mergeProfiles(profiles[i], p)
85+
} else {
86+
profiles = append(profiles, nil)
87+
copy(profiles[i+1:], profiles[i:])
88+
profiles[i] = p
89+
}
90+
return profiles
91+
}
92+
93+
func dumpProfiles(profiles []*cover.Profile, out io.Writer) {
94+
if len(profiles) == 0 {
95+
return
96+
}
97+
fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode)
98+
for _, p := range profiles {
99+
for _, b := range p.Blocks {
100+
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)
101+
}
102+
}
103+
}
104+
105+
func main() {
106+
flag.Parse()
107+
108+
var merged []*cover.Profile
109+
110+
for _, file := range flag.Args() {
111+
profiles, err := cover.ParseProfiles(file)
112+
if err != nil {
113+
log.Fatalf("failed to parse profiles: %v", err)
114+
}
115+
for _, p := range profiles {
116+
merged = addProfile(merged, p)
117+
}
118+
}
119+
120+
dumpProfiles(merged, os.Stdout)
121+
}

0 commit comments

Comments
 (0)