Source file
src/testing/sub_test.go
Documentation: testing
1
2
3
4
5 package testing
6
7 import (
8 "bytes"
9 "fmt"
10 "reflect"
11 "regexp"
12 "runtime"
13 "strings"
14 "sync"
15 "sync/atomic"
16 "time"
17 )
18
19 func init() {
20
21 benchTime.d = 100 * time.Millisecond
22 }
23
24 func TestTestContext(t *T) {
25 const (
26 add1 = 0
27 done = 1
28 )
29
30 type call struct {
31 typ int
32
33 running int
34 waiting int
35 started bool
36 }
37 testCases := []struct {
38 max int
39 run []call
40 }{{
41 max: 1,
42 run: []call{
43 {typ: add1, running: 1, waiting: 0, started: true},
44 {typ: done, running: 0, waiting: 0, started: false},
45 },
46 }, {
47 max: 1,
48 run: []call{
49 {typ: add1, running: 1, waiting: 0, started: true},
50 {typ: add1, running: 1, waiting: 1, started: false},
51 {typ: done, running: 1, waiting: 0, started: true},
52 {typ: done, running: 0, waiting: 0, started: false},
53 {typ: add1, running: 1, waiting: 0, started: true},
54 },
55 }, {
56 max: 3,
57 run: []call{
58 {typ: add1, running: 1, waiting: 0, started: true},
59 {typ: add1, running: 2, waiting: 0, started: true},
60 {typ: add1, running: 3, waiting: 0, started: true},
61 {typ: add1, running: 3, waiting: 1, started: false},
62 {typ: add1, running: 3, waiting: 2, started: false},
63 {typ: add1, running: 3, waiting: 3, started: false},
64 {typ: done, running: 3, waiting: 2, started: true},
65 {typ: add1, running: 3, waiting: 3, started: false},
66 {typ: done, running: 3, waiting: 2, started: true},
67 {typ: done, running: 3, waiting: 1, started: true},
68 {typ: done, running: 3, waiting: 0, started: true},
69 {typ: done, running: 2, waiting: 0, started: false},
70 {typ: done, running: 1, waiting: 0, started: false},
71 {typ: done, running: 0, waiting: 0, started: false},
72 },
73 }}
74 for i, tc := range testCases {
75 ctx := &testContext{
76 startParallel: make(chan bool),
77 maxParallel: tc.max,
78 }
79 for j, call := range tc.run {
80 doCall := func(f func()) chan bool {
81 done := make(chan bool)
82 go func() {
83 f()
84 done <- true
85 }()
86 return done
87 }
88 started := false
89 switch call.typ {
90 case add1:
91 signal := doCall(ctx.waitParallel)
92 select {
93 case <-signal:
94 started = true
95 case ctx.startParallel <- true:
96 <-signal
97 }
98 case done:
99 signal := doCall(ctx.release)
100 select {
101 case <-signal:
102 case <-ctx.startParallel:
103 started = true
104 <-signal
105 }
106 }
107 if started != call.started {
108 t.Errorf("%d:%d:started: got %v; want %v", i, j, started, call.started)
109 }
110 if ctx.running != call.running {
111 t.Errorf("%d:%d:running: got %v; want %v", i, j, ctx.running, call.running)
112 }
113 if ctx.numWaiting != call.waiting {
114 t.Errorf("%d:%d:waiting: got %v; want %v", i, j, ctx.numWaiting, call.waiting)
115 }
116 }
117 }
118 }
119
120 func TestTRun(t *T) {
121 realTest := t
122 testCases := []struct {
123 desc string
124 ok bool
125 maxPar int
126 chatty bool
127 output string
128 f func(*T)
129 }{{
130 desc: "failnow skips future sequential and parallel tests at same level",
131 ok: false,
132 maxPar: 1,
133 output: `
134 --- FAIL: failnow skips future sequential and parallel tests at same level (N.NNs)
135 --- FAIL: failnow skips future sequential and parallel tests at same level/#00 (N.NNs)
136 `,
137 f: func(t *T) {
138 ranSeq := false
139 ranPar := false
140 t.Run("", func(t *T) {
141 t.Run("par", func(t *T) {
142 t.Parallel()
143 ranPar = true
144 })
145 t.Run("seq", func(t *T) {
146 ranSeq = true
147 })
148 t.FailNow()
149 t.Run("seq", func(t *T) {
150 realTest.Error("test must be skipped")
151 })
152 t.Run("par", func(t *T) {
153 t.Parallel()
154 realTest.Error("test must be skipped.")
155 })
156 })
157 if !ranPar {
158 realTest.Error("parallel test was not run")
159 }
160 if !ranSeq {
161 realTest.Error("sequential test was not run")
162 }
163 },
164 }, {
165 desc: "failure in parallel test propagates upwards",
166 ok: false,
167 maxPar: 1,
168 output: `
169 --- FAIL: failure in parallel test propagates upwards (N.NNs)
170 --- FAIL: failure in parallel test propagates upwards/#00 (N.NNs)
171 --- FAIL: failure in parallel test propagates upwards/#00/par (N.NNs)
172 `,
173 f: func(t *T) {
174 t.Run("", func(t *T) {
175 t.Parallel()
176 t.Run("par", func(t *T) {
177 t.Parallel()
178 t.Fail()
179 })
180 })
181 },
182 }, {
183 desc: "skipping without message, chatty",
184 ok: true,
185 chatty: true,
186 output: `
187 === RUN skipping without message, chatty
188 --- SKIP: skipping without message, chatty (N.NNs)`,
189 f: func(t *T) { t.SkipNow() },
190 }, {
191 desc: "chatty with recursion",
192 ok: true,
193 chatty: true,
194 output: `
195 === RUN chatty with recursion
196 === RUN chatty with recursion/#00
197 === RUN chatty with recursion/#00/#00
198 --- PASS: chatty with recursion (N.NNs)
199 --- PASS: chatty with recursion/#00 (N.NNs)
200 --- PASS: chatty with recursion/#00/#00 (N.NNs)`,
201 f: func(t *T) {
202 t.Run("", func(t *T) {
203 t.Run("", func(t *T) {})
204 })
205 },
206 }, {
207 desc: "skipping without message, not chatty",
208 ok: true,
209 f: func(t *T) { t.SkipNow() },
210 }, {
211 desc: "skipping after error",
212 output: `
213 --- FAIL: skipping after error (N.NNs)
214 sub_test.go:NNN: an error
215 sub_test.go:NNN: skipped`,
216 f: func(t *T) {
217 t.Error("an error")
218 t.Skip("skipped")
219 },
220 }, {
221 desc: "use Run to locally synchronize parallelism",
222 ok: true,
223 maxPar: 1,
224 f: func(t *T) {
225 var count uint32
226 t.Run("waitGroup", func(t *T) {
227 for i := 0; i < 4; i++ {
228 t.Run("par", func(t *T) {
229 t.Parallel()
230 atomic.AddUint32(&count, 1)
231 })
232 }
233 })
234 if count != 4 {
235 t.Errorf("count was %d; want 4", count)
236 }
237 },
238 }, {
239 desc: "alternate sequential and parallel",
240
241
242
243 ok: true,
244 maxPar: 1,
245 f: func(t *T) {
246 t.Run("a", func(t *T) {
247 t.Parallel()
248 t.Run("b", func(t *T) {
249
250 t.Run("c", func(t *T) {
251 t.Parallel()
252 })
253
254 })
255 })
256 },
257 }, {
258 desc: "alternate sequential and parallel 2",
259
260
261
262 ok: true,
263 maxPar: 2,
264 f: func(t *T) {
265 for i := 0; i < 2; i++ {
266 t.Run("a", func(t *T) {
267 t.Parallel()
268 time.Sleep(time.Nanosecond)
269 for i := 0; i < 2; i++ {
270 t.Run("b", func(t *T) {
271 time.Sleep(time.Nanosecond)
272 for i := 0; i < 2; i++ {
273 t.Run("c", func(t *T) {
274 t.Parallel()
275 time.Sleep(time.Nanosecond)
276 })
277 }
278
279 })
280 }
281 })
282 }
283 },
284 }, {
285 desc: "stress test",
286 ok: true,
287 maxPar: 4,
288 f: func(t *T) {
289 t.Parallel()
290 for i := 0; i < 12; i++ {
291 t.Run("a", func(t *T) {
292 t.Parallel()
293 time.Sleep(time.Nanosecond)
294 for i := 0; i < 12; i++ {
295 t.Run("b", func(t *T) {
296 time.Sleep(time.Nanosecond)
297 for i := 0; i < 12; i++ {
298 t.Run("c", func(t *T) {
299 t.Parallel()
300 time.Sleep(time.Nanosecond)
301 t.Run("d1", func(t *T) {})
302 t.Run("d2", func(t *T) {})
303 t.Run("d3", func(t *T) {})
304 t.Run("d4", func(t *T) {})
305 })
306 }
307 })
308 }
309 })
310 }
311 },
312 }, {
313 desc: "skip output",
314 ok: true,
315 maxPar: 4,
316 f: func(t *T) {
317 t.Skip()
318 },
319 }, {
320 desc: "subtest calls error on parent",
321 ok: false,
322 output: `
323 --- FAIL: subtest calls error on parent (N.NNs)
324 sub_test.go:NNN: first this
325 sub_test.go:NNN: and now this!
326 sub_test.go:NNN: oh, and this too`,
327 maxPar: 1,
328 f: func(t *T) {
329 t.Errorf("first this")
330 outer := t
331 t.Run("", func(t *T) {
332 outer.Errorf("and now this!")
333 })
334 t.Errorf("oh, and this too")
335 },
336 }, {
337 desc: "subtest calls fatal on parent",
338 ok: false,
339 output: `
340 --- FAIL: subtest calls fatal on parent (N.NNs)
341 sub_test.go:NNN: first this
342 sub_test.go:NNN: and now this!
343 --- FAIL: subtest calls fatal on parent/#00 (N.NNs)
344 testing.go:NNN: test executed panic(nil) or runtime.Goexit: subtest may have called FailNow on a parent test`,
345 maxPar: 1,
346 f: func(t *T) {
347 outer := t
348 t.Errorf("first this")
349 t.Run("", func(t *T) {
350 outer.Fatalf("and now this!")
351 })
352 t.Errorf("Should not reach here.")
353 },
354 }, {
355 desc: "subtest calls error on ancestor",
356 ok: false,
357 output: `
358 --- FAIL: subtest calls error on ancestor (N.NNs)
359 sub_test.go:NNN: Report to ancestor
360 --- FAIL: subtest calls error on ancestor/#00 (N.NNs)
361 sub_test.go:NNN: Still do this
362 sub_test.go:NNN: Also do this`,
363 maxPar: 1,
364 f: func(t *T) {
365 outer := t
366 t.Run("", func(t *T) {
367 t.Run("", func(t *T) {
368 outer.Errorf("Report to ancestor")
369 })
370 t.Errorf("Still do this")
371 })
372 t.Errorf("Also do this")
373 },
374 }, {
375 desc: "subtest calls fatal on ancestor",
376 ok: false,
377 output: `
378 --- FAIL: subtest calls fatal on ancestor (N.NNs)
379 sub_test.go:NNN: Nope`,
380 maxPar: 1,
381 f: func(t *T) {
382 outer := t
383 t.Run("", func(t *T) {
384 for i := 0; i < 4; i++ {
385 t.Run("", func(t *T) {
386 outer.Fatalf("Nope")
387 })
388 t.Errorf("Don't do this")
389 }
390 t.Errorf("And neither do this")
391 })
392 t.Errorf("Nor this")
393 },
394 }, {
395 desc: "panic on goroutine fail after test exit",
396 ok: false,
397 maxPar: 4,
398 f: func(t *T) {
399 ch := make(chan bool)
400 t.Run("", func(t *T) {
401 go func() {
402 <-ch
403 defer func() {
404 if r := recover(); r == nil {
405 realTest.Errorf("expected panic")
406 }
407 ch <- true
408 }()
409 t.Errorf("failed after success")
410 }()
411 })
412 ch <- true
413 <-ch
414 },
415 }, {
416 desc: "log in finished sub test logs to parent",
417 ok: false,
418 output: `
419 --- FAIL: log in finished sub test logs to parent (N.NNs)
420 sub_test.go:NNN: message2
421 sub_test.go:NNN: message1
422 sub_test.go:NNN: error`,
423 maxPar: 1,
424 f: func(t *T) {
425 ch := make(chan bool)
426 t.Run("sub", func(t2 *T) {
427 go func() {
428 <-ch
429 t2.Log("message1")
430 ch <- true
431 }()
432 })
433 t.Log("message2")
434 ch <- true
435 <-ch
436 t.Errorf("error")
437 },
438 }, {
439
440
441 desc: "log in finished sub test with chatty",
442 ok: false,
443 chatty: true,
444 output: `
445 --- FAIL: log in finished sub test with chatty (N.NNs)`,
446 maxPar: 1,
447 f: func(t *T) {
448 ch := make(chan bool)
449 t.Run("sub", func(t2 *T) {
450 go func() {
451 <-ch
452 t2.Log("message1")
453 ch <- true
454 }()
455 })
456 t.Log("message2")
457 ch <- true
458 <-ch
459 t.Errorf("error")
460 },
461 }, {
462
463 desc: "cleanup when subtest panics",
464 ok: false,
465 chatty: false,
466 output: `
467 --- FAIL: cleanup when subtest panics (N.NNs)
468 --- FAIL: cleanup when subtest panics/sub (N.NNs)
469 sub_test.go:NNN: running cleanup`,
470 f: func(t *T) {
471 t.Cleanup(func() { t.Log("running cleanup") })
472 t.Run("sub", func(t2 *T) {
473 t2.FailNow()
474 })
475 },
476 }}
477 for _, tc := range testCases {
478 t.Run(tc.desc, func(t *T) {
479 ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", ""))
480 buf := &bytes.Buffer{}
481 root := &T{
482 common: common{
483 signal: make(chan bool),
484 name: "Test",
485 w: buf,
486 },
487 context: ctx,
488 }
489 if tc.chatty {
490 root.chatty = newChattyPrinter(root.w)
491 }
492 ok := root.Run(tc.desc, tc.f)
493 ctx.release()
494
495 if ok != tc.ok {
496 t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, tc.ok)
497 }
498 if ok != !root.Failed() {
499 t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed())
500 }
501 if ctx.running != 0 || ctx.numWaiting != 0 {
502 t.Errorf("%s:running and waiting non-zero: got %d and %d", tc.desc, ctx.running, ctx.numWaiting)
503 }
504 got := strings.TrimSpace(buf.String())
505 want := strings.TrimSpace(tc.output)
506 re := makeRegexp(want)
507 if ok, err := regexp.MatchString(re, got); !ok || err != nil {
508 t.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc.desc, got, want)
509 }
510 })
511 }
512 }
513
514 func TestBRun(t *T) {
515 work := func(b *B) {
516 for i := 0; i < b.N; i++ {
517 time.Sleep(time.Nanosecond)
518 }
519 }
520 testCases := []struct {
521 desc string
522 failed bool
523 chatty bool
524 output string
525 f func(*B)
526 }{{
527 desc: "simulate sequential run of subbenchmarks.",
528 f: func(b *B) {
529 b.Run("", func(b *B) { work(b) })
530 time1 := b.result.NsPerOp()
531 b.Run("", func(b *B) { work(b) })
532 time2 := b.result.NsPerOp()
533 if time1 >= time2 {
534 t.Errorf("no time spent in benchmark t1 >= t2 (%d >= %d)", time1, time2)
535 }
536 },
537 }, {
538 desc: "bytes set by all benchmarks",
539 f: func(b *B) {
540 b.Run("", func(b *B) { b.SetBytes(10); work(b) })
541 b.Run("", func(b *B) { b.SetBytes(10); work(b) })
542 if b.result.Bytes != 20 {
543 t.Errorf("bytes: got: %d; want 20", b.result.Bytes)
544 }
545 },
546 }, {
547 desc: "bytes set by some benchmarks",
548
549 f: func(b *B) {
550 b.Run("", func(b *B) { b.SetBytes(10); work(b) })
551 b.Run("", func(b *B) { work(b) })
552 b.Run("", func(b *B) { b.SetBytes(10); work(b) })
553 if b.result.Bytes != 0 {
554 t.Errorf("bytes: got: %d; want 0", b.result.Bytes)
555 }
556 },
557 }, {
558 desc: "failure carried over to root",
559 failed: true,
560 output: "--- FAIL: root",
561 f: func(b *B) { b.Fail() },
562 }, {
563 desc: "skipping without message, chatty",
564 chatty: true,
565 output: "--- SKIP: root",
566 f: func(b *B) { b.SkipNow() },
567 }, {
568 desc: "chatty with recursion",
569 chatty: true,
570 f: func(b *B) {
571 b.Run("", func(b *B) {
572 b.Run("", func(b *B) {})
573 })
574 },
575 }, {
576 desc: "skipping without message, not chatty",
577 f: func(b *B) { b.SkipNow() },
578 }, {
579 desc: "skipping after error",
580 failed: true,
581 output: `
582 --- FAIL: root
583 sub_test.go:NNN: an error
584 sub_test.go:NNN: skipped`,
585 f: func(b *B) {
586 b.Error("an error")
587 b.Skip("skipped")
588 },
589 }, {
590 desc: "memory allocation",
591 f: func(b *B) {
592 const bufSize = 256
593 alloc := func(b *B) {
594 var buf [bufSize]byte
595 for i := 0; i < b.N; i++ {
596 _ = append([]byte(nil), buf[:]...)
597 }
598 }
599 b.Run("", func(b *B) {
600 alloc(b)
601 b.ReportAllocs()
602 })
603 b.Run("", func(b *B) {
604 alloc(b)
605 b.ReportAllocs()
606 })
607
608
609
610
611 if got := b.result.MemAllocs; got < 2 {
612 t.Errorf("MemAllocs was %v; want 2", got)
613 }
614 if got := b.result.MemBytes; got < 2*bufSize {
615 t.Errorf("MemBytes was %v; want %v", got, 2*bufSize)
616 }
617 },
618 }, {
619 desc: "cleanup is called",
620 f: func(b *B) {
621 var calls, cleanups, innerCalls, innerCleanups int
622 b.Run("", func(b *B) {
623 calls++
624 b.Cleanup(func() {
625 cleanups++
626 })
627 b.Run("", func(b *B) {
628 b.Cleanup(func() {
629 innerCleanups++
630 })
631 innerCalls++
632 })
633 work(b)
634 })
635 if calls == 0 || calls != cleanups {
636 t.Errorf("mismatched cleanups; got %d want %d", cleanups, calls)
637 }
638 if innerCalls == 0 || innerCalls != innerCleanups {
639 t.Errorf("mismatched cleanups; got %d want %d", cleanups, calls)
640 }
641 },
642 }, {
643 desc: "cleanup is called on failure",
644 failed: true,
645 f: func(b *B) {
646 var calls, cleanups int
647 b.Run("", func(b *B) {
648 calls++
649 b.Cleanup(func() {
650 cleanups++
651 })
652 b.Fatalf("failure")
653 })
654 if calls == 0 || calls != cleanups {
655 t.Errorf("mismatched cleanups; got %d want %d", cleanups, calls)
656 }
657 },
658 }}
659 for _, tc := range testCases {
660 t.Run(tc.desc, func(t *T) {
661 var ok bool
662 buf := &bytes.Buffer{}
663
664
665 root := &B{
666 common: common{
667 signal: make(chan bool),
668 name: "root",
669 w: buf,
670 },
671 benchFunc: func(b *B) { ok = b.Run("test", tc.f) },
672 benchTime: benchTimeFlag{d: 1 * time.Microsecond},
673 }
674 if tc.chatty {
675 root.chatty = newChattyPrinter(root.w)
676 }
677 root.runN(1)
678 if ok != !tc.failed {
679 t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, !tc.failed)
680 }
681 if !ok != root.Failed() {
682 t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed())
683 }
684
685 if root.result.N != 1 {
686 t.Errorf("%s: N for parent benchmark was %d; want 1", tc.desc, root.result.N)
687 }
688 got := strings.TrimSpace(buf.String())
689 want := strings.TrimSpace(tc.output)
690 re := makeRegexp(want)
691 if ok, err := regexp.MatchString(re, got); !ok || err != nil {
692 t.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc.desc, got, want)
693 }
694 })
695 }
696 }
697
698 func makeRegexp(s string) string {
699 s = regexp.QuoteMeta(s)
700 s = strings.ReplaceAll(s, ":NNN:", `:\d\d\d\d?:`)
701 s = strings.ReplaceAll(s, "N\\.NNs", `\d*\.\d*s`)
702 return s
703 }
704
705 func TestBenchmarkOutput(t *T) {
706
707
708 Benchmark(func(b *B) { b.Error("do not print this output") })
709 Benchmark(func(b *B) {})
710 }
711
712 func TestBenchmarkStartsFrom1(t *T) {
713 var first = true
714 Benchmark(func(b *B) {
715 if first && b.N != 1 {
716 panic(fmt.Sprintf("Benchmark() first N=%v; want 1", b.N))
717 }
718 first = false
719 })
720 }
721
722 func TestBenchmarkReadMemStatsBeforeFirstRun(t *T) {
723 var first = true
724 Benchmark(func(b *B) {
725 if first && (b.startAllocs == 0 || b.startBytes == 0) {
726 panic(fmt.Sprintf("ReadMemStats not called before first run"))
727 }
728 first = false
729 })
730 }
731
732 func TestParallelSub(t *T) {
733 c := make(chan int)
734 block := make(chan int)
735 for i := 0; i < 10; i++ {
736 go func(i int) {
737 <-block
738 t.Run(fmt.Sprint(i), func(t *T) {})
739 c <- 1
740 }(i)
741 }
742 close(block)
743 for i := 0; i < 10; i++ {
744 <-c
745 }
746 }
747
748 type funcWriter struct {
749 write func([]byte) (int, error)
750 }
751
752 func (fw *funcWriter) Write(b []byte) (int, error) {
753 return fw.write(b)
754 }
755
756 func TestRacyOutput(t *T) {
757 var runs int32
758 var races int32
759 raceDetector := func(b []byte) (int, error) {
760
761 if atomic.LoadInt32(&runs) > 0 {
762 atomic.AddInt32(&races, 1)
763 }
764 atomic.AddInt32(&runs, 1)
765 defer atomic.AddInt32(&runs, -1)
766 runtime.Gosched()
767 return len(b), nil
768 }
769
770 var wg sync.WaitGroup
771 root := &T{
772 common: common{w: &funcWriter{raceDetector}},
773 context: newTestContext(1, newMatcher(regexp.MatchString, "", "")),
774 }
775 root.chatty = newChattyPrinter(root.w)
776 root.Run("", func(t *T) {
777 for i := 0; i < 100; i++ {
778 wg.Add(1)
779 go func(i int) {
780 defer wg.Done()
781 t.Run(fmt.Sprint(i), func(t *T) {
782 t.Logf("testing run %d", i)
783 })
784 }(i)
785 }
786 })
787 wg.Wait()
788
789 if races > 0 {
790 t.Errorf("detected %d racy Writes", races)
791 }
792 }
793
794
795 func TestLogAfterComplete(t *T) {
796 ctx := newTestContext(1, newMatcher(regexp.MatchString, "", ""))
797 var buf bytes.Buffer
798 t1 := &T{
799 common: common{
800
801
802 signal: make(chan bool, 1),
803 w: &buf,
804 },
805 context: ctx,
806 }
807
808 c1 := make(chan bool)
809 c2 := make(chan string)
810 tRunner(t1, func(t *T) {
811 t.Run("TestLateLog", func(t *T) {
812 go func() {
813 defer close(c2)
814 defer func() {
815 p := recover()
816 if p == nil {
817 c2 <- "subtest did not panic"
818 return
819 }
820 s, ok := p.(string)
821 if !ok {
822 c2 <- fmt.Sprintf("subtest panic with unexpected value %v", p)
823 return
824 }
825 const want = "Log in goroutine after TestLateLog has completed: log after test"
826 if !strings.Contains(s, want) {
827 c2 <- fmt.Sprintf("subtest panic %q does not contain %q", s, want)
828 }
829 }()
830
831 <-c1
832 t.Log("log after test")
833 }()
834 })
835 })
836 close(c1)
837
838 if s := <-c2; s != "" {
839 t.Error(s)
840 }
841 }
842
843 func TestBenchmark(t *T) {
844 if Short() {
845 t.Skip("skipping in short mode")
846 }
847 res := Benchmark(func(b *B) {
848 for i := 0; i < 5; i++ {
849 b.Run("", func(b *B) {
850 for i := 0; i < b.N; i++ {
851 time.Sleep(time.Millisecond)
852 }
853 })
854 }
855 })
856 if res.NsPerOp() < 4000000 {
857 t.Errorf("want >5ms; got %v", time.Duration(res.NsPerOp()))
858 }
859 }
860
861 func TestCleanup(t *T) {
862 var cleanups []int
863 t.Run("test", func(t *T) {
864 t.Cleanup(func() { cleanups = append(cleanups, 1) })
865 t.Cleanup(func() { cleanups = append(cleanups, 2) })
866 })
867 if got, want := cleanups, []int{2, 1}; !reflect.DeepEqual(got, want) {
868 t.Errorf("unexpected cleanup record; got %v want %v", got, want)
869 }
870 }
871
872 func TestConcurrentCleanup(t *T) {
873 cleanups := 0
874 t.Run("test", func(t *T) {
875 done := make(chan struct{})
876 for i := 0; i < 2; i++ {
877 i := i
878 go func() {
879 t.Cleanup(func() {
880 cleanups |= 1 << i
881 })
882 done <- struct{}{}
883 }()
884 }
885 <-done
886 <-done
887 })
888 if cleanups != 1|2 {
889 t.Errorf("unexpected cleanup; got %d want 3", cleanups)
890 }
891 }
892
893 func TestCleanupCalledEvenAfterGoexit(t *T) {
894 cleanups := 0
895 t.Run("test", func(t *T) {
896 t.Cleanup(func() {
897 cleanups++
898 })
899 t.Cleanup(func() {
900 runtime.Goexit()
901 })
902 })
903 if cleanups != 1 {
904 t.Errorf("unexpected cleanup count; got %d want 1", cleanups)
905 }
906 }
907
908 func TestRunCleanup(t *T) {
909 outerCleanup := 0
910 innerCleanup := 0
911 t.Run("test", func(t *T) {
912 t.Cleanup(func() { outerCleanup++ })
913 t.Run("x", func(t *T) {
914 t.Cleanup(func() { innerCleanup++ })
915 })
916 })
917 if innerCleanup != 1 {
918 t.Errorf("unexpected inner cleanup count; got %d want 1", innerCleanup)
919 }
920 if outerCleanup != 1 {
921 t.Errorf("unexpected outer cleanup count; got %d want 0", outerCleanup)
922 }
923 }
924
925 func TestCleanupParallelSubtests(t *T) {
926 ranCleanup := 0
927 t.Run("test", func(t *T) {
928 t.Cleanup(func() { ranCleanup++ })
929 t.Run("x", func(t *T) {
930 t.Parallel()
931 if ranCleanup > 0 {
932 t.Error("outer cleanup ran before parallel subtest")
933 }
934 })
935 })
936 if ranCleanup != 1 {
937 t.Errorf("unexpected cleanup count; got %d want 1", ranCleanup)
938 }
939 }
940
941 func TestNestedCleanup(t *T) {
942 ranCleanup := 0
943 t.Run("test", func(t *T) {
944 t.Cleanup(func() {
945 if ranCleanup != 2 {
946 t.Errorf("unexpected cleanup count in first cleanup: got %d want 2", ranCleanup)
947 }
948 ranCleanup++
949 })
950 t.Cleanup(func() {
951 if ranCleanup != 0 {
952 t.Errorf("unexpected cleanup count in second cleanup: got %d want 0", ranCleanup)
953 }
954 ranCleanup++
955 t.Cleanup(func() {
956 if ranCleanup != 1 {
957 t.Errorf("unexpected cleanup count in nested cleanup: got %d want 1", ranCleanup)
958 }
959 ranCleanup++
960 })
961 })
962 })
963 if ranCleanup != 3 {
964 t.Errorf("unexpected cleanup count: got %d want 3", ranCleanup)
965 }
966 }
967
View as plain text