github.com/capsule8/goveralls@v0.0.3-0.20190325144123-900af2b6e486/gocover.go (about)

     1  package main
     2  
     3  // Much of the core of this is copied from go's cover tool itself.
     4  
     5  // Copyright 2013 The Go Authors. All rights reserved.
     6  // Use of this source code is governed by a BSD-style
     7  // license that can be found in the LICENSE file.
     8  
     9  // The rest is written by Dustin Sallings
    10  
    11  import (
    12  	"bytes"
    13  	"fmt"
    14  	"go/build"
    15  	"io/ioutil"
    16  	"log"
    17  	"path/filepath"
    18  	"strings"
    19  
    20  	"golang.org/x/tools/cover"
    21  )
    22  
    23  func findFile(file string) (string, error) {
    24  	dir, file := filepath.Split(file)
    25  	pkg, err := build.Import(dir, ".", build.FindOnly)
    26  	if err != nil {
    27  		return "", fmt.Errorf("can't find %q: %v", file, err)
    28  	}
    29  	return filepath.Join(pkg.Dir, file), nil
    30  }
    31  
    32  // mergeProfs merges profiles for same target packages.
    33  // It assumes each profiles have same sorted FileName and Blocks.
    34  func mergeProfs(pfss [][]*cover.Profile) []*cover.Profile {
    35  	// skip empty profiles ([no test files])
    36  	for i := 0; i < len(pfss); i++ {
    37  		if len(pfss[i]) > 0 {
    38  			pfss = pfss[i:]
    39  			break
    40  		}
    41  	}
    42  	if len(pfss) == 0 {
    43  		return nil
    44  	} else if len(pfss) == 1 {
    45  		return pfss[0]
    46  	}
    47  	head, rest := pfss[0], pfss[1:]
    48  	ret := make([]*cover.Profile, 0, len(head))
    49  	for i, profile := range head {
    50  		for _, ps := range rest {
    51  			// find profiles
    52  			if len(ps) == 0 {
    53  				continue
    54  			} else if len(ps) < i+1 {
    55  				continue
    56  			} else if ps[i].FileName != profile.FileName {
    57  				continue
    58  			}
    59  			profile.Blocks = mergeProfBlocks(profile.Blocks, ps[i].Blocks)
    60  		}
    61  		ret = append(ret, profile)
    62  	}
    63  	return ret
    64  }
    65  
    66  func mergeProfBlocks(as, bs []cover.ProfileBlock) []cover.ProfileBlock {
    67  	if len(as) != len(bs) {
    68  		log.Fatal("Two block length should be same")
    69  	}
    70  	// cover.ProfileBlock genereated by cover.ParseProfiles() is sorted by
    71  	// StartLine and StartCol, so we can use index.
    72  	ret := make([]cover.ProfileBlock, 0, len(as))
    73  	for i, a := range as {
    74  		b := bs[i]
    75  		if a.StartLine != b.StartLine || a.StartCol != b.StartCol {
    76  			log.Fatal("Blocks are not sorted")
    77  		}
    78  		a.Count += b.Count
    79  		ret = append(ret, a)
    80  	}
    81  	return ret
    82  }
    83  
    84  // toSF converts profiles to sourcefiles for coveralls.
    85  func toSF(profs []*cover.Profile) ([]*SourceFile, error) {
    86  	var rv []*SourceFile
    87  	for _, prof := range profs {
    88  		path, err := findFile(prof.FileName)
    89  		if err != nil {
    90  			log.Fatalf("Can't find %v", err)
    91  		}
    92  		fb, err := ioutil.ReadFile(path)
    93  		if err != nil {
    94  			log.Fatalf("Error reading %v: %v", path, err)
    95  		}
    96  		sf := &SourceFile{
    97  			Name:     getCoverallsSourceFileName(path),
    98  			Source:   string(fb),
    99  			Coverage: make([]interface{}, 1+bytes.Count(fb, []byte{'\n'})),
   100  		}
   101  
   102  		for _, block := range prof.Blocks {
   103  			for i := block.StartLine; i <= block.EndLine; i++ {
   104  				count, _ := sf.Coverage[i-1].(int)
   105  				sf.Coverage[i-1] = count + block.Count
   106  			}
   107  		}
   108  
   109  		rv = append(rv, sf)
   110  	}
   111  
   112  	return rv, nil
   113  }
   114  
   115  func parseCover(fn string) ([]*SourceFile, error) {
   116  	var pfss [][]*cover.Profile
   117  	for _, p := range strings.Split(fn, ",") {
   118  		profs, err := cover.ParseProfiles(p)
   119  		if err != nil {
   120  			return nil, fmt.Errorf("Error parsing coverage: %v", err)
   121  		}
   122  		pfss = append(pfss, profs)
   123  	}
   124  
   125  	sourceFiles, err := toSF(mergeProfs(pfss))
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	return sourceFiles, nil
   131  }