github.com/vlal/goveralls@v0.0.2-0.20171114042957-b71a1e4855f8/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 }