1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
19 // Make benchmark tests run 10* faster.
20 *benchTime
= 100 * time
.Millisecond
23 func TestTestContext(t
*T
) {
28 // After each of the calls are applied to the context, the
30 typ
int // run or done
31 // result from applying the call
36 testCases
:= []struct {
42 {typ
: add1
, running
: 1, waiting
: 0, started
: true},
43 {typ
: done
, running
: 0, waiting
: 0, started
: false},
48 {typ
: add1
, running
: 1, waiting
: 0, started
: true},
49 {typ
: add1
, running
: 1, waiting
: 1, started
: false},
50 {typ
: done
, running
: 1, waiting
: 0, started
: true},
51 {typ
: done
, running
: 0, waiting
: 0, started
: false},
52 {typ
: add1
, running
: 1, waiting
: 0, started
: true},
57 {typ
: add1
, running
: 1, waiting
: 0, started
: true},
58 {typ
: add1
, running
: 2, waiting
: 0, started
: true},
59 {typ
: add1
, running
: 3, waiting
: 0, started
: true},
60 {typ
: add1
, running
: 3, waiting
: 1, started
: false},
61 {typ
: add1
, running
: 3, waiting
: 2, started
: false},
62 {typ
: add1
, running
: 3, waiting
: 3, started
: false},
63 {typ
: done
, running
: 3, waiting
: 2, started
: true},
64 {typ
: add1
, running
: 3, waiting
: 3, started
: false},
65 {typ
: done
, running
: 3, waiting
: 2, started
: true},
66 {typ
: done
, running
: 3, waiting
: 1, started
: true},
67 {typ
: done
, running
: 3, waiting
: 0, started
: true},
68 {typ
: done
, running
: 2, waiting
: 0, started
: false},
69 {typ
: done
, running
: 1, waiting
: 0, started
: false},
70 {typ
: done
, running
: 0, waiting
: 0, started
: false},
73 for i
, tc
:= range testCases
{
75 startParallel
: make(chan bool),
78 for j
, call
:= range tc
.run
{
79 doCall
:= func(f
func()) chan bool {
80 done
:= make(chan bool)
90 signal
:= doCall(ctx
.waitParallel
)
94 case ctx
.startParallel
<- true:
98 signal
:= doCall(ctx
.release
)
101 case <-ctx
.startParallel
:
106 if started
!= call
.started
{
107 t
.Errorf("%d:%d:started: got %v; want %v", i
, j
, started
, call
.started
)
109 if ctx
.running
!= call
.running
{
110 t
.Errorf("%d:%d:running: got %v; want %v", i
, j
, ctx
.running
, call
.running
)
112 if ctx
.numWaiting
!= call
.waiting
{
113 t
.Errorf("%d:%d:waiting: got %v; want %v", i
, j
, ctx
.numWaiting
, call
.waiting
)
119 func TestTRun(t
*T
) {
121 testCases
:= []struct {
129 desc
: "failnow skips future sequential and parallel tests at same level",
133 --- FAIL: failnow skips future sequential and parallel tests at same level (N.NNs)
134 --- FAIL: failnow skips future sequential and parallel tests at same level/#00 (N.NNs)
139 t
.Run("", func(t
*T
) {
140 t
.Run("par", func(t
*T
) {
144 t
.Run("seq", func(t
*T
) {
148 t
.Run("seq", func(t
*T
) {
149 realTest
.Error("test must be skipped")
151 t
.Run("par", func(t
*T
) {
153 realTest
.Error("test must be skipped.")
157 realTest
.Error("parallel test was not run")
160 realTest
.Error("sequential test was not run")
164 desc
: "failure in parallel test propagates upwards",
168 --- FAIL: failure in parallel test propagates upwards (N.NNs)
169 --- FAIL: failure in parallel test propagates upwards/#00 (N.NNs)
170 --- FAIL: failure in parallel test propagates upwards/#00/par (N.NNs)
173 t
.Run("", func(t
*T
) {
175 t
.Run("par", func(t
*T
) {
182 desc
: "skipping without message, chatty",
186 === RUN skipping without message, chatty
187 --- SKIP: skipping without message, chatty (N.NNs)`,
188 f
: func(t
*T
) { t
.SkipNow() },
190 desc
: "chatty with recursion",
194 === RUN chatty with recursion
195 === RUN chatty with recursion/#00
196 === RUN chatty with recursion/#00/#00
197 --- PASS: chatty with recursion (N.NNs)
198 --- PASS: chatty with recursion/#00 (N.NNs)
199 --- PASS: chatty with recursion/#00/#00 (N.NNs)`,
201 t
.Run("", func(t
*T
) {
202 t
.Run("", func(t
*T
) {})
206 desc
: "skipping without message, not chatty",
208 f
: func(t
*T
) { t
.SkipNow() },
210 desc
: "skipping after error",
212 --- FAIL: skipping after error (N.NNs)
213 sub_test.go:NNN: an error
214 sub_test.go:NNN: skipped`,
220 desc
: "use Run to locally synchronize parallelism",
225 t
.Run("waitGroup", func(t
*T
) {
226 for i
:= 0; i
< 4; i
++ {
227 t
.Run("par", func(t
*T
) {
229 atomic
.AddUint32(&count
, 1)
234 t
.Errorf("count was %d; want 4", count
)
238 desc
: "alternate sequential and parallel",
239 // Sequential tests should partake in the counting of running threads.
240 // Otherwise, if one runs parallel subtests in sequential tests that are
241 // itself subtests of parallel tests, the counts can get askew.
245 t
.Run("a", func(t
*T
) {
247 t
.Run("b", func(t
*T
) {
248 // Sequential: ensure running count is decremented.
249 t
.Run("c", func(t
*T
) {
257 desc
: "alternate sequential and parallel 2",
258 // Sequential tests should partake in the counting of running threads.
259 // Otherwise, if one runs parallel subtests in sequential tests that are
260 // itself subtests of parallel tests, the counts can get askew.
264 for i
:= 0; i
< 2; i
++ {
265 t
.Run("a", func(t
*T
) {
267 time
.Sleep(time
.Nanosecond
)
268 for i
:= 0; i
< 2; i
++ {
269 t
.Run("b", func(t
*T
) {
270 time
.Sleep(time
.Nanosecond
)
271 for i
:= 0; i
< 2; i
++ {
272 t
.Run("c", func(t
*T
) {
274 time
.Sleep(time
.Nanosecond
)
289 for i
:= 0; i
< 12; i
++ {
290 t
.Run("a", func(t
*T
) {
292 time
.Sleep(time
.Nanosecond
)
293 for i
:= 0; i
< 12; i
++ {
294 t
.Run("b", func(t
*T
) {
295 time
.Sleep(time
.Nanosecond
)
296 for i
:= 0; i
< 12; i
++ {
297 t
.Run("c", func(t
*T
) {
299 time
.Sleep(time
.Nanosecond
)
300 t
.Run("d1", func(t
*T
) {})
301 t
.Run("d2", func(t
*T
) {})
302 t
.Run("d3", func(t
*T
) {})
303 t
.Run("d4", func(t
*T
) {})
319 desc
: "panic on goroutine fail after test exit",
323 ch
:= make(chan bool)
324 t
.Run("", func(t
*T
) {
328 if r
:= recover(); r
== nil {
329 realTest
.Errorf("expected panic")
333 t
.Errorf("failed after success")
340 for _
, tc
:= range testCases
{
341 ctx
:= newTestContext(tc
.maxPar
, newMatcher(regexp
.MatchString
, "", ""))
342 buf
:= &bytes
.Buffer
{}
345 signal
: make(chan bool),
352 ok
:= root
.Run(tc
.desc
, tc
.f
)
356 t
.Errorf("%s:ok: got %v; want %v", tc
.desc
, ok
, tc
.ok
)
358 if ok
!= !root
.Failed() {
359 t
.Errorf("%s:root failed: got %v; want %v", tc
.desc
, !ok
, root
.Failed())
361 if ctx
.running
!= 0 || ctx
.numWaiting
!= 0 {
362 t
.Errorf("%s:running and waiting non-zero: got %d and %d", tc
.desc
, ctx
.running
, ctx
.numWaiting
)
364 got
:= strings
.TrimSpace(buf
.String())
365 want
:= strings
.TrimSpace(tc
.output
)
366 re
:= makeRegexp(want
)
367 if ok
, err
:= regexp
.MatchString(re
, got
); !ok || err
!= nil {
368 t
.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc
.desc
, got
, want
)
373 func TestBRun(t
*T
) {
375 for i
:= 0; i
< b
.N
; i
++ {
376 time
.Sleep(time
.Nanosecond
)
379 testCases
:= []struct {
386 desc
: "simulate sequential run of subbenchmarks.",
388 b
.Run("", func(b
*B
) { work(b
) })
389 time1
:= b
.result
.NsPerOp()
390 b
.Run("", func(b
*B
) { work(b
) })
391 time2
:= b
.result
.NsPerOp()
393 t
.Errorf("no time spent in benchmark t1 >= t2 (%d >= %d)", time1
, time2
)
397 desc
: "bytes set by all benchmarks",
399 b
.Run("", func(b
*B
) { b
.SetBytes(10); work(b
) })
400 b
.Run("", func(b
*B
) { b
.SetBytes(10); work(b
) })
401 if b
.result
.Bytes
!= 20 {
402 t
.Errorf("bytes: got: %d; want 20", b
.result
.Bytes
)
406 desc
: "bytes set by some benchmarks",
407 // In this case the bytes result is meaningless, so it must be 0.
409 b
.Run("", func(b
*B
) { b
.SetBytes(10); work(b
) })
410 b
.Run("", func(b
*B
) { work(b
) })
411 b
.Run("", func(b
*B
) { b
.SetBytes(10); work(b
) })
412 if b
.result
.Bytes
!= 0 {
413 t
.Errorf("bytes: got: %d; want 0", b
.result
.Bytes
)
417 desc
: "failure carried over to root",
419 output
: "--- FAIL: root",
420 f
: func(b
*B
) { b
.Fail() },
422 desc
: "skipping without message, chatty",
424 output
: "--- SKIP: root",
425 f
: func(b
*B
) { b
.SkipNow() },
427 desc
: "skipping with message, chatty",
431 sub_test.go:NNN: skipping`,
432 f
: func(b
*B
) { b
.Skip("skipping") },
434 desc
: "chatty with recursion",
437 b
.Run("", func(b
*B
) {
438 b
.Run("", func(b
*B
) {})
442 desc
: "skipping without message, not chatty",
443 f
: func(b
*B
) { b
.SkipNow() },
445 desc
: "skipping after error",
449 sub_test.go:NNN: an error
450 sub_test.go:NNN: skipped`,
456 desc
: "memory allocation",
459 alloc
:= func(b
*B
) {
460 var buf
[bufSize
]byte
461 for i
:= 0; i
< b
.N
; i
++ {
462 _
= append([]byte(nil), buf
[:]...)
465 b
.Run("", func(b
*B
) {
469 b
.Run("", func(b
*B
) {
473 // runtime.MemStats sometimes reports more allocations than the
474 // benchmark is responsible for. Luckily the point of this test is
475 // to ensure that the results are not underreported, so we can
476 // simply verify the lower bound.
477 if got
:= b
.result
.MemAllocs
; got
< 2 {
478 t
.Errorf("MemAllocs was %v; want 2", got
)
480 if got
:= b
.result
.MemBytes
; got
< 2*bufSize
{
481 t
.Errorf("MemBytes was %v; want %v", got
, 2*bufSize
)
485 for _
, tc
:= range testCases
{
487 buf
:= &bytes
.Buffer
{}
488 // This is almost like the Benchmark function, except that we override
489 // the benchtime and catch the failure result of the subbenchmark.
492 signal
: make(chan bool),
497 benchFunc
: func(b
*B
) { ok
= b
.Run("test", tc
.f
) }, // Use Run to catch failure.
498 benchTime
: time
.Microsecond
,
501 if ok
!= !tc
.failed
{
502 t
.Errorf("%s:ok: got %v; want %v", tc
.desc
, ok
, !tc
.failed
)
504 if !ok
!= root
.Failed() {
505 t
.Errorf("%s:root failed: got %v; want %v", tc
.desc
, !ok
, root
.Failed())
507 // All tests are run as subtests
508 if root
.result
.N
!= 1 {
509 t
.Errorf("%s: N for parent benchmark was %d; want 1", tc
.desc
, root
.result
.N
)
511 got
:= strings
.TrimSpace(buf
.String())
512 want
:= strings
.TrimSpace(tc
.output
)
513 re
:= makeRegexp(want
)
514 if ok
, err
:= regexp
.MatchString(re
, got
); !ok || err
!= nil {
515 t
.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc
.desc
, got
, want
)
520 func makeRegexp(s
string) string {
521 s
= strings
.Replace(s
, ":NNN:", `:\d\d\d:`, -1)
522 s
= strings
.Replace(s
, "(N.NNs)", `\(\d*\.\d*s\)`, -1)
526 func TestBenchmarkOutput(t
*T
) {
527 // Ensure Benchmark initialized common.w by invoking it with an error and
529 Benchmark(func(b
*B
) { b
.Error("do not print this output") })
530 Benchmark(func(b
*B
) {})
533 func TestBenchmarkStartsFrom1(t
*T
) {
535 Benchmark(func(b
*B
) {
536 if first
&& b
.N
!= 1 {
537 panic(fmt
.Sprintf("Benchmark() first N=%v; want 1", b
.N
))
543 func TestBenchmarkReadMemStatsBeforeFirstRun(t
*T
) {
545 Benchmark(func(b
*B
) {
546 if first
&& (b
.startAllocs
== 0 || b
.startBytes
== 0) {
547 panic(fmt
.Sprintf("ReadMemStats not called before first run"))
553 func TestParallelSub(t
*T
) {
555 block
:= make(chan int)
556 for i
:= 0; i
< 10; i
++ {
559 t
.Run(fmt
.Sprint(i
), func(t
*T
) {})
564 for i
:= 0; i
< 10; i
++ {
569 type funcWriter
func([]byte) (int, error
)
571 func (fw funcWriter
) Write(b
[]byte) (int, error
) { return fw(b
) }
573 func TestRacyOutput(t
*T
) {
574 var runs
int32 // The number of running Writes
575 var races
int32 // Incremented for each race detected
576 raceDetector
:= func(b
[]byte) (int, error
) {
577 // Check if some other goroutine is concurrently calling Write.
578 if atomic
.LoadInt32(&runs
) > 0 {
579 atomic
.AddInt32(&races
, 1) // Race detected!
581 atomic
.AddInt32(&runs
, 1)
582 defer atomic
.AddInt32(&runs
, -1)
583 runtime
.Gosched() // Increase probability of a race
587 var wg sync
.WaitGroup
589 common
: common
{w
: funcWriter(raceDetector
), chatty
: true},
590 context
: newTestContext(1, newMatcher(regexp
.MatchString
, "", "")),
592 root
.Run("", func(t
*T
) {
593 for i
:= 0; i
< 100; i
++ {
597 t
.Run(fmt
.Sprint(i
), func(t
*T
) {
598 t
.Logf("testing run %d", i
)
606 t
.Errorf("detected %d racy Writes", races
)
610 func TestBenchmark(t
*T
) {
611 res
:= Benchmark(func(b
*B
) {
612 for i
:= 0; i
< 5; i
++ {
613 b
.Run("", func(b
*B
) {
614 for i
:= 0; i
< b
.N
; i
++ {
615 time
.Sleep(time
.Millisecond
)
620 if res
.NsPerOp() < 4000000 {
621 t
.Errorf("want >5ms; got %v", time
.Duration(res
.NsPerOp()))