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 package testing
40
41 import (
42 "flag"
43 "fmt"
44 "os"
45 "runtime"
46 "runtime/pprof"
47 "strconv"
48 "strings"
49 "time"
50 )
51
52 var (
53
54
55
56
57
58 short = flag.Bool("test.short", false, "run smaller test suite to save time")
59
60
61 chatty = flag.Bool("test.v", false, "verbose: print additional output")
62 match = flag.String("test.run", "", "regular expression to select tests to run")
63 memProfile = flag.String("test.memprofile", "", "write a memory profile to the named file after execution")
64 memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate")
65 cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
66 timeout = flag.Duration("test.timeout", 0, "if positive, sets an aggregate time limit for all tests")
67 cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
68 parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism")
69
70 cpuList []int
71 )
72
73
74
75 type common struct {
76 output []byte
77 failed bool
78 start time.Time
79 duration time.Duration
80 self any
81 signal chan any
82 }
83
84
85 func Short() bool {
86 return *short
87 }
88
89
90
91 func decorate(s string, addFileLine bool) string {
92 if addFileLine {
93 _, file, line, ok := runtime.Caller(3)
94 if ok {
95
96 if index := strings.LastIndex(file, "/"); index >= 0 {
97 file = file[index+1:]
98 } else if index = strings.LastIndex(file, "\\"); index >= 0 {
99 file = file[index+1:]
100 }
101 } else {
102 file = "???"
103 line = 1
104 }
105 s = fmt.Sprintf("%s:%d: %s", file, line, s)
106 }
107 s = "\t" + s
108 n := len(s)
109 if n > 0 && s[n-1] != '\n' {
110 s += "\n"
111 n++
112 }
113 for i := 0; i < n-1; i++ {
114 if s[i] == '\n' {
115
116 return s[0:i+1] + "\t" + decorate(s[i+1:n], false)
117 }
118 }
119 return s
120 }
121
122
123
124 type T struct {
125 common
126 name string
127 startParallel chan bool
128 }
129
130
131 func (c *common) Fail() { c.failed = true }
132
133
134 func (c *common) Failed() bool { return c.failed }
135
136
137
138 func (c *common) FailNow() {
139 c.Fail()
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 runtime.Goexit()
161 }
162
163
164 func (c *common) log(s string) {
165 c.output = append(c.output, decorate(s, true)...)
166 }
167
168
169
170 func (c *common) Log(args ...any) { c.log(fmt.Sprintln(args...)) }
171
172
173
174 func (c *common) Logf(format string, args ...any) { c.log(fmt.Sprintf(format, args...)) }
175
176
177 func (c *common) Error(args ...any) {
178 c.log(fmt.Sprintln(args...))
179 c.Fail()
180 }
181
182
183 func (c *common) Errorf(format string, args ...any) {
184 c.log(fmt.Sprintf(format, args...))
185 c.Fail()
186 }
187
188
189 func (c *common) Fatal(args ...any) {
190 c.log(fmt.Sprintln(args...))
191 c.FailNow()
192 }
193
194
195 func (c *common) Fatalf(format string, args ...any) {
196 c.log(fmt.Sprintf(format, args...))
197 c.FailNow()
198 }
199
200
201
202 func (t *T) Parallel() {
203 t.signal <- (*T)(nil)
204 <-t.startParallel
205 }
206
207
208
209 type InternalTest struct {
210 Name string
211 F func(*T)
212 }
213
214 func tRunner(t *T, test *InternalTest) {
215 t.start = time.Now()
216
217
218
219
220
221 defer func() {
222 t.duration = time.Now().Sub(t.start)
223 t.signal <- t
224 }()
225
226 test.F(t)
227 }
228
229
230
231 func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
232 flag.Parse()
233 parseCpuList()
234
235 before()
236 startAlarm()
237 testOk := RunTests(matchString, tests)
238 exampleOk := RunExamples(examples)
239 if !testOk || !exampleOk {
240 fmt.Println("FAIL")
241 os.Exit(1)
242 }
243 fmt.Println("PASS")
244 stopAlarm()
245 RunBenchmarks(matchString, benchmarks)
246 after()
247 }
248
249 func (t *T) report() {
250 tstr := fmt.Sprintf("(%.2f seconds)", t.duration.Seconds())
251 format := "--- %s: %s %s\n%s"
252 if t.failed {
253 fmt.Printf(format, "FAIL", t.name, tstr, t.output)
254 } else if *chatty {
255 fmt.Printf(format, "PASS", t.name, tstr, t.output)
256 }
257 }
258
259 func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) {
260 ok = true
261 if len(tests) == 0 {
262 fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
263 return
264 }
265 for _, procs := range cpuList {
266 runtime.GOMAXPROCS(procs)
267
268
269
270
271
272 var collector = make(chan any)
273
274 numParallel := 0
275 startParallel := make(chan bool)
276
277 for i := 0; i < len(tests); i++ {
278 matched, err := matchString(*match, tests[i].Name)
279 if err != nil {
280 fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %s\n", err)
281 os.Exit(1)
282 }
283 if !matched {
284 continue
285 }
286 testName := tests[i].Name
287 if procs != 1 {
288 testName = fmt.Sprintf("%s-%d", tests[i].Name, procs)
289 }
290 t := &T{
291 common: common{
292 signal: make(chan any),
293 },
294 name: testName,
295 startParallel: startParallel,
296 }
297 t.self = t
298 if *chatty {
299 fmt.Printf("=== RUN %s\n", t.name)
300 }
301 go tRunner(t, &tests[i])
302 out := (<-t.signal).(*T)
303 if out == nil {
304 go func() {
305 collector <- <-t.signal
306 }()
307 numParallel++
308 continue
309 }
310 t.report()
311 ok = ok && !out.failed
312 }
313
314 running := 0
315 for numParallel+running > 0 {
316 if running < *parallel && numParallel > 0 {
317 startParallel <- true
318 running++
319 numParallel--
320 continue
321 }
322 t := (<-collector).(*T)
323 t.report()
324 ok = ok && !t.failed
325 running--
326 }
327 }
328 return
329 }
330
331
332 func before() {
333 if *memProfileRate > 0 {
334 runtime.MemProfileRate = *memProfileRate
335 }
336 if *cpuProfile != "" {
337 f, err := os.Create(*cpuProfile)
338 if err != nil {
339 fmt.Fprintf(os.Stderr, "testing: %s", err)
340 return
341 }
342 if err := pprof.StartCPUProfile(f); err != nil {
343 fmt.Fprintf(os.Stderr, "testing: can't start cpu profile: %s", err)
344 f.Close()
345 return
346 }
347
348 }
349
350 }
351
352
353 func after() {
354 if *cpuProfile != "" {
355 pprof.StopCPUProfile()
356 }
357 if *memProfile != "" {
358 f, err := os.Create(*memProfile)
359 if err != nil {
360 fmt.Fprintf(os.Stderr, "testing: %s", err)
361 return
362 }
363 if err = pprof.WriteHeapProfile(f); err != nil {
364 fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", *memProfile, err)
365 }
366 f.Close()
367 }
368 }
369
370 var timer *time.Timer
371
372
373 func startAlarm() {
374 if *timeout > 0 {
375 timer = time.AfterFunc(*timeout, alarm)
376 }
377 }
378
379
380 func stopAlarm() {
381 if *timeout > 0 {
382 timer.Stop()
383 }
384 }
385
386
387 func alarm() {
388 panic("test timed out")
389 }
390
391 func parseCpuList() {
392 if len(*cpuListStr) == 0 {
393 cpuList = append(cpuList, runtime.GOMAXPROCS(-1))
394 } else {
395 for _, val := range strings.Split(*cpuListStr, ",") {
396 cpu, err := strconv.Atoi(val)
397 if err != nil || cpu <= 0 {
398 fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu", val)
399 os.Exit(1)
400 }
401 cpuList = append(cpuList, cpu)
402 }
403 }
404 }
405
View as plain text