-
Notifications
You must be signed in to change notification settings - Fork 2
/
test_coverage.go
106 lines (91 loc) · 3.25 KB
/
test_coverage.go
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
package main
import (
"context"
"database/sql"
"fmt"
"go/ast"
"go/parser"
"go/token"
"os/exec"
"path/filepath"
"golang.org/x/tools/cover"
)
// TestCoverageResult represents the structure of a test-specific coverage result
type TestCoverageResult struct {
TestName string `json:"test_name"`
Package string `json:"package"`
File string `json:"file"`
StartLine int `json:"start_line"`
StartColumn int `json:"start_col"`
EndLine int `json:"end_line"`
EndColumn int `json:"end_col"`
StatementNumber int `json:"stmt_num"`
Count int `json:"count"`
FunctionName string `json:"function_name"`
}
func collectTestCoverageResults(pkgDir string, testResults []TestEvent) ([]TestCoverageResult, error) {
var results []TestCoverageResult
for _, test := range testResults {
cmd := exec.Command("go", "test", pkgDir, "-run", "^"+test.Test+"$", "-coverprofile="+test.Test+".out")
cmd.Run()
profiles, err := cover.ParseProfiles(test.Test + ".out")
if err != nil {
return nil, err
}
for _, profile := range profiles {
packageName := filepath.Dir(profile.FileName)
fileName := filepath.Base(profile.FileName)
for _, block := range profile.Blocks {
functionName, err := getFunctionName(pkgDir+"/"+fileName, block.StartLine)
if err != nil {
return nil, fmt.Errorf("failed to retrieve function name: %w", err)
}
results = append(results, TestCoverageResult{
TestName: test.Test,
Package: packageName,
File: fileName,
StartLine: block.StartLine,
StartColumn: block.StartCol,
EndLine: block.EndLine,
EndColumn: block.EndCol,
StatementNumber: block.NumStmt,
Count: block.Count,
FunctionName: functionName,
})
}
}
}
return results, nil
}
// getFunctionName returns the name of the function at the given line number
func getFunctionName(fileName string, lineNumber int) (string, error) {
fs := token.NewFileSet()
node, err := parser.ParseFile(fs, fileName, nil, 0)
if err != nil {
return "", fmt.Errorf("failed to parse file: %w", err)
}
for _, decl := range node.Decls {
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
start := fs.Position(funcDecl.Pos()).Line
end := fs.Position(funcDecl.End()).Line
if start <= lineNumber && lineNumber <= end {
return funcDecl.Name.Name, nil
}
}
}
return "", nil
}
func populateTestCoverageResults(ctx context.Context, db *sql.DB, pkgDir string, testResults []TestEvent) error {
testCoverageResults, err := collectTestCoverageResults(pkgDir, testResults)
if err != nil {
return fmt.Errorf("failed to collect coverage results by test: %w", err)
}
for _, result := range testCoverageResults {
insertSQL := `INSERT INTO test_coverage (test_name, package, file, start_line, start_col, end_line, end_col, stmt_num, count, function_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`
_, err := db.ExecContext(ctx, insertSQL, result.TestName, result.Package, result.File, result.StartLine, result.StartColumn, result.EndLine, result.EndColumn, result.StatementNumber, result.Count, result.FunctionName)
if err != nil {
return fmt.Errorf("failed to insert test coverage results: %w", err)
}
}
return nil
}