Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
3.2 KiB
120 lines
3.2 KiB
// Copyright 2020 The Gitea Authors. All rights reserved. |
|
// Copyright (c) 2015, Wade Simmons |
|
// Use of this source code is governed by a MIT-style |
|
// license that can be found in the LICENSE file. |
|
|
|
// gocovmerge takes the results from multiple `go test -coverprofile` runs and |
|
// merges them into one profile |
|
|
|
//go:build ignore |
|
// +build ignore |
|
|
|
package main |
|
|
|
import ( |
|
"flag" |
|
"fmt" |
|
"io" |
|
"log" |
|
"os" |
|
"sort" |
|
|
|
"golang.org/x/tools/cover" |
|
) |
|
|
|
func mergeProfiles(p *cover.Profile, merge *cover.Profile) { |
|
if p.Mode != merge.Mode { |
|
log.Fatalf("cannot merge profiles with different modes") |
|
} |
|
// Since the blocks are sorted, we can keep track of where the last block |
|
// was inserted and only look at the blocks after that as targets for merge |
|
startIndex := 0 |
|
for _, b := range merge.Blocks { |
|
startIndex = mergeProfileBlock(p, b, startIndex) |
|
} |
|
} |
|
|
|
func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) int { |
|
sortFunc := func(i int) bool { |
|
pi := p.Blocks[i+startIndex] |
|
return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol) |
|
} |
|
|
|
i := 0 |
|
if sortFunc(i) != true { |
|
i = sort.Search(len(p.Blocks)-startIndex, sortFunc) |
|
} |
|
i += startIndex |
|
if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol { |
|
if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol { |
|
log.Fatalf("OVERLAP MERGE: %v %v %v", p.FileName, p.Blocks[i], pb) |
|
} |
|
switch p.Mode { |
|
case "set": |
|
p.Blocks[i].Count |= pb.Count |
|
case "count", "atomic": |
|
p.Blocks[i].Count += pb.Count |
|
default: |
|
log.Fatalf("unsupported covermode: '%s'", p.Mode) |
|
} |
|
} else { |
|
if i > 0 { |
|
pa := p.Blocks[i-1] |
|
if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) { |
|
log.Fatalf("OVERLAP BEFORE: %v %v %v", p.FileName, pa, pb) |
|
} |
|
} |
|
if i < len(p.Blocks)-1 { |
|
pa := p.Blocks[i+1] |
|
if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) { |
|
log.Fatalf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb) |
|
} |
|
} |
|
p.Blocks = append(p.Blocks, cover.ProfileBlock{}) |
|
copy(p.Blocks[i+1:], p.Blocks[i:]) |
|
p.Blocks[i] = pb |
|
} |
|
return i + 1 |
|
} |
|
|
|
func addProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile { |
|
i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName }) |
|
if i < len(profiles) && profiles[i].FileName == p.FileName { |
|
mergeProfiles(profiles[i], p) |
|
} else { |
|
profiles = append(profiles, nil) |
|
copy(profiles[i+1:], profiles[i:]) |
|
profiles[i] = p |
|
} |
|
return profiles |
|
} |
|
|
|
func dumpProfiles(profiles []*cover.Profile, out io.Writer) { |
|
if len(profiles) == 0 { |
|
return |
|
} |
|
fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode) |
|
for _, p := range profiles { |
|
for _, b := range p.Blocks { |
|
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) |
|
} |
|
} |
|
} |
|
|
|
func main() { |
|
flag.Parse() |
|
|
|
var merged []*cover.Profile |
|
|
|
for _, file := range flag.Args() { |
|
profiles, err := cover.ParseProfiles(file) |
|
if err != nil { |
|
log.Fatalf("failed to parse profile '%s': %v", file, err) |
|
} |
|
for _, p := range profiles { |
|
merged = addProfile(merged, p) |
|
} |
|
} |
|
|
|
dumpProfiles(merged, os.Stdout) |
|
}
|
|
|