-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconvert.go
More file actions
145 lines (127 loc) · 3.6 KB
/
convert.go
File metadata and controls
145 lines (127 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright (c) 2016, Hotolab. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cov
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"path/filepath"
"strings"
"golang.org/x/tools/cover"
)
type converter struct {
packages map[string]*Package
}
type extent struct {
startOffset int
startLine int
startCol int
endOffset int
endLine int
endCol int
coverage float64
}
// convertProfile converts a Go coverage profile into an intelligent
// structure containing the percent of coverage, etc...
func (c *converter) convertProfile(p *cover.Profile) error {
name, file, pkgpath, abspath, err := c.findFile(p.FileName)
if err != nil {
return err
}
pkg := c.packages[name]
if pkg == nil {
pkg = &Package{Name: name, Path: pkgpath}
c.packages[name] = pkg
}
// Find function and statement extents; create corresponding
// cov.Functions and cov.Statements, and keep a separate
// slice of gocov.Statements so we can match them with profile
// blocks.
extents, err := c.findFuncs(file)
if err != nil {
return err
}
var stmts []statement
for _, fe := range extents {
f := &Function{
Name: fe.name,
File: abspath,
Start: fe.startLine,
End: fe.endLine,
}
for _, stmt := range fe.stmts {
s := statement{
Statement: &Statement{Start: stmt.startLine, End: stmt.endLine},
StmtExtent: stmt,
}
f.Statements = append(f.Statements, s.Statement)
stmts = append(stmts, s)
}
pkg.Functions = append(pkg.Functions, f)
}
// For each profile block in the file, find the statement(s) it
// covers and increment the Reached field(s).
blocks := p.Blocks
for len(stmts) > 0 {
s := stmts[0]
for i, b := range blocks {
if b.StartLine > s.endLine || (b.StartLine == s.endLine && b.StartCol >= s.endCol) {
// Past the end of the statement
stmts = stmts[1:]
blocks = blocks[i:]
break
}
if b.EndLine < s.startLine || (b.EndLine == s.startLine && b.EndCol <= s.startCol) {
// Before the beginning of the statement
continue
}
s.Reached += int64(b.Count)
stmts = stmts[1:]
break
}
}
// Loop on each statement and determine coverage and TLOC by function
var totalStmts int
var totalReached int64
for _, fn := range pkg.Functions {
var reached int64
totalStmts += len(fn.Statements)
for _, stmt := range fn.Statements {
if stmt.Reached > 0 {
reached++
}
}
totalReached += reached
fn.Coverage = 100.0 * float64(reached) / float64(len(fn.Statements))
}
pkg.Coverage = 100.0 * float64(totalReached) / float64(totalStmts)
return nil
}
// findFile finds the location of the named file in GOROOT, GOPATH etc.
func (c *converter) findFile(file string) (pkgname string, filename string, pkgpath string, abspath string, err error) {
dir, file := filepath.Split(file)
if dir != "" {
dir = dir[:len(dir)-1] // drop trailing '/'
}
pkg, err := build.Import(dir, ".", build.IgnoreVendor)
if err != nil {
return "", "", "", "", fmt.Errorf("can't find %q: %v", file, err)
}
dir = strings.Replace(filepath.Join(pkg.Dir, file), pkg.SrcRoot, "$GOPATH", 1)
return pkg.Name, filepath.Join(pkg.Dir, file), strings.Replace(pkg.ImportPath, pkg.SrcRoot, "", 1), dir, nil
}
// findFuncs parses the file and returns a slice of FuncExtent descriptors.
func (c *converter) findFuncs(name string) ([]*FuncExtent, error) {
fset := token.NewFileSet()
parsedFile, err := parser.ParseFile(fset, name, nil, 0)
if err != nil {
return nil, err
}
visitor := &FuncVisitor{fset: fset}
ast.Walk(visitor, parsedFile)
return visitor.funcs, nil
}