api/vendor/github.com/mattn/goveralls/gocover.go
2020-06-26 11:11:27 +02:00

132 lines
3.1 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 (
"bytes"
"fmt"
"go/build"
"io/ioutil"
"log"
"path/filepath"
"strings"
"golang.org/x/tools/cover"
)
func findFile(file string) (string, error) {
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 {
// skip empty profiles ([no test files])
for i := 0; i < len(pfss); i++ {
if len(pfss[i]) > 0 {
pfss = pfss[i:]
break
}
}
if len(pfss) == 0 {
return nil
} else if len(pfss) == 1 {
return pfss[0]
}
head, rest := pfss[0], pfss[1:]
ret := make([]*cover.Profile, 0, len(head))
for i, profile := range head {
for _, ps := range rest {
// find profiles
if len(ps) == 0 {
continue
} else if len(ps) < i+1 {
continue
} else if ps[i].FileName != profile.FileName {
continue
}
profile.Blocks = mergeProfBlocks(profile.Blocks, ps[i].Blocks)
}
ret = append(ret, profile)
}
return ret
}
func mergeProfBlocks(as, bs []cover.ProfileBlock) []cover.ProfileBlock {
if len(as) != len(bs) {
log.Fatal("Two block length should be same")
}
// cover.ProfileBlock generated by cover.ParseProfiles() is sorted by
// StartLine and StartCol, so we can use index.
ret := make([]cover.ProfileBlock, 0, len(as))
for i, a := range as {
b := bs[i]
if a.StartLine != b.StartLine || a.StartCol != b.StartCol {
log.Fatal("Blocks are not sorted")
}
a.Count += b.Count
ret = append(ret, a)
}
return ret
}
// toSF converts profiles to sourcefiles for coveralls.
func toSF(profs []*cover.Profile) ([]*SourceFile, error) {
var rv []*SourceFile
for _, prof := range profs {
path, err := findFile(prof.FileName)
if err != nil {
log.Fatalf("Can't find %v", err)
}
fb, err := ioutil.ReadFile(path)
if err != nil {
log.Fatalf("Error reading %v: %v", path, err)
}
sf := &SourceFile{
Name: getCoverallsSourceFileName(path),
Source: string(fb),
Coverage: make([]interface{}, 1+bytes.Count(fb, []byte{'\n'})),
}
for _, block := range prof.Blocks {
for i := block.StartLine; i <= block.EndLine; i++ {
count, _ := sf.Coverage[i-1].(int)
sf.Coverage[i-1] = count + block.Count
}
}
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
}