-
Notifications
You must be signed in to change notification settings - Fork 2
/
test_convert.go
118 lines (101 loc) · 2.65 KB
/
test_convert.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
107
108
109
110
111
112
113
114
115
116
117
118
package tstat
import (
"fmt"
"sort"
"strings"
"time"
"github.com/nickfiggins/tstat/internal/gotest"
"golang.org/x/exp/maps"
)
const testDelim = "/"
// convertTests converts a gotest.PackageEvents into a PackageRun.
func convertEvents(pkg *gotest.PackageEvents) (PackageRun, error) {
tests := getPackageTests(pkg.Events)
nested, err := nestSubtests(tests)
if err != nil {
return PackageRun{}, err
}
var start, end time.Time
var failed bool
if pkg.Start != nil {
start = pkg.Start.Time
}
if pkg.End != nil {
end = pkg.End.Time
failed = pkg.End.Action == gotest.Fail
}
return PackageRun{
pkgName: pkg.Package,
start: start, end: end,
Tests: nested,
Seed: pkg.Seed,
failed: failed,
}, nil
}
func getPackageTests(events []gotest.Event) []*Test {
packageTests := make(map[string]*Test, 0)
for _, event := range events {
if event.Test == "" {
continue
}
test, ok := packageTests[event.Test]
if !ok {
test = newTest(event.Package, event.Test)
}
packageTests[event.Test] = test.withEvent(event)
}
testsByName := maps.Values(packageTests)
sort.Sort(byTestName(testsByName))
return testsByName
}
func newTest(pkg, name string) *Test {
// add 1 to pull the part after the slash, and conveniently
// handle the case of no subtests as well
subStart := strings.LastIndex(name, "/") + 1
return &Test{
Subtests: make([]*Test, 0),
actions: []gotest.Action{},
FullName: name,
Package: pkg,
Name: name[subStart:],
}
}
// nestSubtests takes a list of tests and nests subtests under their parent.
// It returns a list of root tests.
func nestSubtests(tests []*Test) ([]*Test, error) {
rootTests := map[string]*Test{}
for _, to := range tests {
subs := removeEmpty(strings.Split(to.FullName, testDelim))
subDepth := len(subs)
if subDepth == 1 { // root test; no subtests
out := to
rootTests[to.FullName] = out
} else if subDepth > 1 { // at least one subtest
test, ok := rootTests[subs[0]]
if !ok {
return nil, fmt.Errorf("subtest found without corresponding parent: %v", to.FullName)
}
test.addSubtests(to)
}
}
return maps.Values(rootTests), nil
}
// removeEmpty removes empty strings from a slice of strings.
func removeEmpty(s []string) []string {
out := make([]string, 0)
for _, v := range s {
if v == "" {
continue
}
out = append(out, v)
}
return out
}
// byTestName sorts tests by their name, which ensures that all parent tests come
// before the subtests.
type byTestName []*Test
func (b byTestName) Len() int { return len(b) }
func (b byTestName) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b byTestName) Less(i, j int) bool {
return b[i].FullName < b[j].FullName
}