dnscrypt-proxy/vendor/github.com/mattn/goveralls/gocover.go
2021-07-03 10:56:53 +02:00

178 lines
4.6 KiB
Go

package main
// Much of the core of this is copied from go's cover tool itself.
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The rest is written by Dustin Sallings
import (
"fmt"
"go/build"
"io/ioutil"
"os"
"path/filepath"
"strings"
"golang.org/x/tools/cover"
)
func findFile(rootPackage string, rootDir string, file string) (string, error) {
// If we find a file that is inside the root package, we already know
// where it should be!
if rootPackage != "" {
if relPath, _ := filepath.Rel(rootPackage, file); !strings.HasPrefix(relPath, "..") {
// The file is inside the root package...
return filepath.Join(rootDir, relPath), nil
}
}
dir, file := filepath.Split(file)
pkg, err := build.Import(dir, ".", build.FindOnly)
if err != nil {
return "", fmt.Errorf("can't find %q: %v", file, err)
}
return filepath.Join(pkg.Dir, file), nil
}
// mergeProfs merges profiles for same target packages.
// It assumes each profiles have same sorted FileName and Blocks.
func mergeProfs(pfss [][]*cover.Profile) []*cover.Profile {
if len(pfss) == 0 {
return nil
}
for len(pfss) > 1 {
i := 0
for ; 2*i+1 < len(pfss); i++ {
pfss[i] = mergeTwoProfs(pfss[2*i], pfss[2*i+1])
}
if 2*i < len(pfss) {
pfss[i] = pfss[2*i]
i++
}
pfss = pfss[:i]
}
return pfss[0]
}
func mergeTwoProfs(left, right []*cover.Profile) []*cover.Profile {
ret := make([]*cover.Profile, 0, len(left)+len(right))
for len(left) > 0 && len(right) > 0 {
if left[0].FileName == right[0].FileName {
profile := &cover.Profile{
FileName: left[0].FileName,
Mode: left[0].Mode,
Blocks: mergeTwoProfBlock(left[0].Blocks, right[0].Blocks),
}
ret = append(ret, profile)
left = left[1:]
right = right[1:]
} else if left[0].FileName < right[0].FileName {
ret = append(ret, left[0])
left = left[1:]
} else {
ret = append(ret, right[0])
right = right[1:]
}
}
ret = append(ret, left...)
ret = append(ret, right...)
return ret
}
func mergeTwoProfBlock(left, right []cover.ProfileBlock) []cover.ProfileBlock {
ret := make([]cover.ProfileBlock, 0, len(left)+len(right))
for len(left) > 0 && len(right) > 0 {
a, b := left[0], right[0]
if a.StartLine == b.StartLine && a.StartCol == b.StartCol && a.EndLine == b.EndLine && a.EndCol == b.EndCol {
ret = append(ret, cover.ProfileBlock{
StartLine: a.StartLine,
StartCol: a.StartCol,
EndLine: a.EndLine,
EndCol: a.EndCol,
NumStmt: a.NumStmt,
Count: a.Count + b.Count,
})
left = left[1:]
right = right[1:]
} else if a.StartLine < b.StartLine || (a.StartLine == b.StartLine && a.StartCol < b.StartCol) {
ret = append(ret, a)
left = left[1:]
} else {
ret = append(ret, b)
right = right[1:]
}
}
ret = append(ret, left...)
ret = append(ret, right...)
return ret
}
// toSF converts profiles to sourcefiles for coveralls.
func toSF(profs []*cover.Profile) ([]*SourceFile, error) {
// find root package to reduce build.Import calls when importing files from relative root
// https://github.com/mattn/goveralls/pull/195
rootDirectory, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("get working dir: %v", err)
}
rootPackage := findRootPackage(rootDirectory)
var rv []*SourceFile
for _, prof := range profs {
path, err := findFile(rootPackage, rootDirectory, prof.FileName)
if err != nil {
return nil, fmt.Errorf("cannot find file %q: %v", prof.FileName, err)
}
sf := &SourceFile{
Name: getCoverallsSourceFileName(path),
}
lineLookup := map[int]int{}
maxLineNo := 0
for _, block := range prof.Blocks {
for i := block.StartLine; i <= block.EndLine; i++ {
lineLookup[i] += block.Count
}
if block.EndLine > maxLineNo {
maxLineNo = block.EndLine
}
}
sf.Coverage = make([]interface{}, maxLineNo)
for i := 1; i <= maxLineNo; i++ {
if c, ok := lineLookup[i]; ok {
sf.Coverage[i-1] = c
}
}
if *uploadSource {
fb, err := ioutil.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("cannot read source of file %q: %v", path, err)
}
sf.Source = string(fb)
}
rv = append(rv, sf)
}
return rv, nil
}
func parseCover(fn string) ([]*SourceFile, error) {
var pfss [][]*cover.Profile
for _, p := range strings.Split(fn, ",") {
profs, err := cover.ParseProfiles(p)
if err != nil {
return nil, fmt.Errorf("error parsing coverage: %v", err)
}
pfss = append(pfss, profs)
}
sourceFiles, err := toSF(mergeProfs(pfss))
if err != nil {
return nil, err
}
return sourceFiles, nil
}