/
test.go
154 lines (134 loc) · 3.26 KB
/
test.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
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
146
147
148
149
150
151
152
153
154
package ginkgo
import (
"bytes"
"context"
"fmt"
"os/exec"
"strconv"
"strings"
"syscall"
"time"
"github.com/onsi/ginkgo/types"
)
type testCase struct {
name string
spec ginkgoSpec
location types.CodeLocation
// identifies which tests can be run in parallel (ginkgo runs suites linearly)
testExclusion string
start time.Time
end time.Time
duration time.Duration
out []byte
success bool
failed bool
skipped bool
previous *testCase
}
func newTestCase(spec ginkgoSpec) *testCase {
name := spec.ConcatenatedString()
name = strings.TrimPrefix(name, "[Top Level] ")
summary := spec.Summary("")
return &testCase{
name: name,
spec: spec,
location: summary.ComponentCodeLocations[len(summary.ComponentCodeLocations)-1],
}
}
func (t *testCase) Retry() *testCase {
copied := &testCase{
name: t.name,
spec: t.spec,
location: t.location,
testExclusion: t.testExclusion,
previous: t,
}
return copied
}
type TestSuite struct {
Name string
Description string
Matches func(name string) bool
// Init should be run once before a test in this suite is run. Not called by
// methods in this package.
Init func() error
Parallelism int
// The number of flakes that may occur before this test is marked as a failure.
MaximumAllowedFlakes int
TestTimeout time.Duration
}
func (s *TestSuite) Filter(tests []*testCase) []*testCase {
matches := make([]*testCase, 0, len(tests))
for _, test := range tests {
if !s.Matches(test.name) {
continue
}
matches = append(matches, test)
}
return matches
}
func newSuiteFromFile(name string, contents []byte) (*TestSuite, error) {
suite := &TestSuite{
Name: name,
}
tests := make(map[string]int)
for _, line := range strings.Split(string(contents), "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "\"") {
var err error
line, err = strconv.Unquote(line)
if err != nil {
return nil, err
}
tests[line]++
}
}
suite.Matches = func(name string) bool {
_, ok := tests[name]
return ok
}
return suite, nil
}
func testNames(tests []*testCase) []string {
var names []string
for _, t := range tests {
names = append(names, t.name)
}
return names
}
// SuitesString returns a string with the provided suites formatted. Prefix is
// printed at the beginning of the output.
func SuitesString(suites []*TestSuite, prefix string) string {
buf := &bytes.Buffer{}
fmt.Fprintf(buf, prefix)
for _, suite := range suites {
fmt.Fprintf(buf, "%s\n %s\n\n", suite.Name, suite.Description)
}
return buf.String()
}
func runWithTimeout(ctx context.Context, c *exec.Cmd, timeout time.Duration) ([]byte, error) {
if timeout > 0 {
go func() {
select {
// interrupt tests after timeout, and abort if they don't complete quick enough
case <-time.After(timeout):
if c.Process != nil {
c.Process.Signal(syscall.SIGINT)
}
// if the process appears to be hung a significant amount of time after the timeout
// send an ABRT so we get a stack dump
select {
case <-time.After(time.Minute):
if c.Process != nil {
c.Process.Signal(syscall.SIGABRT)
}
}
case <-ctx.Done():
if c.Process != nil {
c.Process.Signal(syscall.SIGINT)
}
}
}()
}
return c.CombinedOutput()
}