Source file
src/testing/sub_test.go
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 barrier: make(chan bool),
485 name: "Test",
486 w: buf,
487 },
488 context: ctx,
489 }
490 if tc.chatty {
491 root.chatty = newChattyPrinter(root.w)
492 }
493 ok := root.Run(tc.desc, tc.f)
494 ctx.release()
495
496 if ok != tc.ok {
497 t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, tc.ok)
498 }
499 if ok != !root.Failed() {
500 t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed())
501 }
502 if ctx.running != 0 || ctx.numWaiting != 0 {
503 t.Errorf("%s:running and waiting non-zero: got %d and %d", tc.desc, ctx.running, ctx.numWaiting)
504 }
505 got := strings.TrimSpace(buf.String())
506 want := strings.TrimSpace(tc.output)
507 re := makeRegexp(want)
508 if ok, err := regexp.MatchString(re, got); !ok || err != nil {
509 t.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc.desc, got, want)
510 }
511 })
512 }
513 }
514
515 func TestBRun(t *T) {
516 work := func(b *B) {
517 for i := 0; i < b.N; i++ {
518 time.Sleep(time.Nanosecond)
519 }
520 }
521 testCases := []struct {
522 desc string
523 failed bool
524 chatty bool
525 output string
526 f func(*B)
527 }{{
528 desc: "simulate sequential run of subbenchmarks.",
529 f: func(b *B) {
530 b.Run("", func(b *B) { work(b) })
531 time1 := b.result.NsPerOp()
532 b.Run("", func(b *B) { work(b) })
533 time2 := b.result.NsPerOp()
534 if time1 >= time2 {
535 t.Errorf("no time spent in benchmark t1 >= t2 (%d >= %d)", time1, time2)
536 }
537 },
538 }, {
539 desc: "bytes set by all benchmarks",
540 f: func(b *B) {
541 b.Run("", func(b *B) { b.SetBytes(10); work(b) })
542 b.Run("", func(b *B) { b.SetBytes(10); work(b) })
543 if b.result.Bytes != 20 {
544 t.Errorf("bytes: got: %d; want 20", b.result.Bytes)
545 }
546 },
547 }, {
548 desc: "bytes set by some benchmarks",
549
550 f: func(b *B) {
551 b.Run("", func(b *B) { b.SetBytes(10); work(b) })
552 b.Run("", func(b *B) { work(b) })
553 b.Run("", func(b *B) { b.SetBytes(10); work(b) })
554 if b.result.Bytes != 0 {
555 t.Errorf("bytes: got: %d; want 0", b.result.Bytes)
556 }
557 },
558 }, {
559 desc: "failure carried over to root",
560 failed: true,
561 output: "--- FAIL: root",
562 f: func(b *B) { b.Fail() },
563 }, {
564 desc: "skipping without message, chatty",
565 chatty: true,
566 output: "--- SKIP: root",
567 f: func(b *B) { b.SkipNow() },
568 }, {
569 desc: "chatty with recursion",
570 chatty: true,
571 f: func(b *B) {
572 b.Run("", func(b *B) {
573 b.Run("", func(b *B) {})
574 })
575 },
576 }, {
577 desc: "skipping without message, not chatty",
578 f: func(b *B) { b.SkipNow() },
579 }, {
580 desc: "skipping after error",
581 failed: true,
582 output: `
583 --- FAIL: root
584 sub_test.go:NNN: an error
585 sub_test.go:NNN: skipped`,
586 f: func(b *B) {
587 b.Error("an error")
588 b.Skip("skipped")
589 },
590 }, {
591 desc: "memory allocation",
592 f: func(b *B) {
593 const bufSize = 256
594 alloc := func(b *B) {
595 var buf [bufSize]byte
596 for i := 0; i < b.N; i++ {
597 _ = append([]byte(nil), buf[:]...)
598 }
599 }
600 b.Run("", func(b *B) {
601 alloc(b)
602 b.ReportAllocs()
603 })
604 b.Run("", func(b *B) {
605 alloc(b)
606 b.ReportAllocs()
607 })
608
609
610
611
612 if got := b.result.MemAllocs; got < 2 {
613 t.Errorf("MemAllocs was %v; want 2", got)
614 }
615 if got := b.result.MemBytes; got < 2*bufSize {
616 t.Errorf("MemBytes was %v; want %v", got, 2*bufSize)
617 }
618 },
619 }, {
620 desc: "cleanup is called",
621 f: func(b *B) {
622 var calls, cleanups, innerCalls, innerCleanups int
623 b.Run("", func(b *B) {
624 calls++
625 b.Cleanup(func() {
626 cleanups++
627 })
628 b.Run("", func(b *B) {
629 b.Cleanup(func() {
630 innerCleanups++
631 })
632 innerCalls++
633 })
634 work(b)
635 })
636 if calls == 0 || calls != cleanups {
637 t.Errorf("mismatched cleanups; got %d want %d", cleanups, calls)
638 }
639 if innerCalls == 0 || innerCalls != innerCleanups {
640 t.Errorf("mismatched cleanups; got %d want %d", cleanups, calls)
641 }
642 },
643 }, {
644 desc: "cleanup is called on failure",
645 failed: true,
646 f: func(b *B) {
647 var calls, cleanups int
648 b.Run("", func(b *B) {
649 calls++
650 b.Cleanup(func() {
651 cleanups++
652 })
653 b.Fatalf("failure")
654 })
655 if calls == 0 || calls != cleanups {
656 t.Errorf("mismatched cleanups; got %d want %d", cleanups, calls)
657 }
658 },
659 }}
660 for _, tc := range testCases {
661 t.Run(tc.desc, func(t *T) {
662 var ok bool
663 buf := &bytes.Buffer{}
664
665
666 root := &B{
667 common: common{
668 signal: make(chan bool),
669 name: "root",
670 w: buf,
671 },
672 benchFunc: func(b *B) { ok = b.Run("test", tc.f) },
673 benchTime: durationOrCountFlag{d: 1 * time.Microsecond},
674 }
675 if tc.chatty {
676 root.chatty = newChattyPrinter(root.w)
677 }
678 root.runN(1)
679 if ok != !tc.failed {
680 t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, !tc.failed)
681 }
682 if !ok != root.Failed() {
683 t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed())
684 }
685
686 if root.result.N != 1 {
687 t.Errorf("%s: N for parent benchmark was %d; want 1", tc.desc, root.result.N)
688 }
689 got := strings.TrimSpace(buf.String())
690 want := strings.TrimSpace(tc.output)
691 re := makeRegexp(want)
692 if ok, err := regexp.MatchString(re, got); !ok || err != nil {
693 t.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc.desc, got, want)
694 }
695 })
696 }
697 }
698
699 func makeRegexp(s string) string {
700 s = regexp.QuoteMeta(s)
701 s = strings.ReplaceAll(s, ":NNN:", `:\d\d\d\d?:`)
702 s = strings.ReplaceAll(s, "N\\.NNs", `\d*\.\d*s`)
703 return s
704 }
705
706 func TestBenchmarkOutput(t *T) {
707
708
709 Benchmark(func(b *B) { b.Error("do not print this output") })
710 Benchmark(func(b *B) {})
711 }
712
713 func TestBenchmarkStartsFrom1(t *T) {
714 var first = true
715 Benchmark(func(b *B) {
716 if first && b.N != 1 {
717 panic(fmt.Sprintf("Benchmark() first N=%v; want 1", b.N))
718 }
719 first = false
720 })
721 }
722
723 func TestBenchmarkReadMemStatsBeforeFirstRun(t *T) {
724 var first = true
725 Benchmark(func(b *B) {
726 if first && (b.startAllocs == 0 || b.startBytes == 0) {
727 panic(fmt.Sprintf("ReadMemStats not called before first run"))
728 }
729 first = false
730 })
731 }
732
733 func TestParallelSub(t *T) {
734 c := make(chan int)
735 block := make(chan int)
736 for i := 0; i < 10; i++ {
737 go func(i int) {
738 <-block
739 t.Run(fmt.Sprint(i), func(t *T) {})
740 c <- 1
741 }(i)
742 }
743 close(block)
744 for i := 0; i < 10; i++ {
745 <-c
746 }
747 }
748
749 type funcWriter struct {
750 write func([]byte) (int, error)
751 }
752
753 func (fw *funcWriter) Write(b []byte) (int, error) {
754 return fw.write(b)
755 }
756
757 func TestRacyOutput(t *T) {
758 var runs int32
759 var races int32
760 raceDetector := func(b []byte) (int, error) {
761
762 if atomic.LoadInt32(&runs) > 0 {
763 atomic.AddInt32(&races, 1)
764 }
765 atomic.AddInt32(&runs, 1)
766 defer atomic.AddInt32(&runs, -1)
767 runtime.Gosched()
768 return len(b), nil
769 }
770
771 var wg sync.WaitGroup
772 root := &T{
773 common: common{w: &funcWriter{raceDetector}},
774 context: newTestContext(1, newMatcher(regexp.MatchString, "", "")),
775 }
776 root.chatty = newChattyPrinter(root.w)
777 root.Run("", func(t *T) {
778 for i := 0; i < 100; i++ {
779 wg.Add(1)
780 go func(i int) {
781 defer wg.Done()
782 t.Run(fmt.Sprint(i), func(t *T) {
783 t.Logf("testing run %d", i)
784 })
785 }(i)
786 }
787 })
788 wg.Wait()
789
790 if races > 0 {
791 t.Errorf("detected %d racy Writes", races)
792 }
793 }
794
795
796 func TestLogAfterComplete(t *T) {
797 ctx := newTestContext(1, newMatcher(regexp.MatchString, "", ""))
798 var buf bytes.Buffer
799 t1 := &T{
800 common: common{
801
802
803 signal: make(chan bool, 1),
804 w: &buf,
805 },
806 context: ctx,
807 }
808
809 c1 := make(chan bool)
810 c2 := make(chan string)
811 tRunner(t1, func(t *T) {
812 t.Run("TestLateLog", func(t *T) {
813 go func() {
814 defer close(c2)
815 defer func() {
816 p := recover()
817 if p == nil {
818 c2 <- "subtest did not panic"
819 return
820 }
821 s, ok := p.(string)
822 if !ok {
823 c2 <- fmt.Sprintf("subtest panic with unexpected value %v", p)
824 return
825 }
826 const want = "Log in goroutine after TestLateLog has completed: log after test"
827 if !strings.Contains(s, want) {
828 c2 <- fmt.Sprintf("subtest panic %q does not contain %q", s, want)
829 }
830 }()
831
832 <-c1
833 t.Log("log after test")
834 }()
835 })
836 })
837 close(c1)
838
839 if s := <-c2; s != "" {
840 t.Error(s)
841 }
842 }
843
844 func TestBenchmark(t *T) {
845 if Short() {
846 t.Skip("skipping in short mode")
847 }
848 res := Benchmark(func(b *B) {
849 for i := 0; i < 5; i++ {
850 b.Run("", func(b *B) {
851 for i := 0; i < b.N; i++ {
852 time.Sleep(time.Millisecond)
853 }
854 })
855 }
856 })
857 if res.NsPerOp() < 4000000 {
858 t.Errorf("want >5ms; got %v", time.Duration(res.NsPerOp()))
859 }
860 }
861
862 func TestCleanup(t *T) {
863 var cleanups []int
864 t.Run("test", func(t *T) {
865 t.Cleanup(func() { cleanups = append(cleanups, 1) })
866 t.Cleanup(func() { cleanups = append(cleanups, 2) })
867 })
868 if got, want := cleanups, []int{2, 1}; !reflect.DeepEqual(got, want) {
869 t.Errorf("unexpected cleanup record; got %v want %v", got, want)
870 }
871 }
872
873 func TestConcurrentCleanup(t *T) {
874 cleanups := 0
875 t.Run("test", func(t *T) {
876 done := make(chan struct{})
877 for i := 0; i < 2; i++ {
878 i := i
879 go func() {
880 t.Cleanup(func() {
881 cleanups |= 1 << i
882 })
883 done <- struct{}{}
884 }()
885 }
886 <-done
887 <-done
888 })
889 if cleanups != 1|2 {
890 t.Errorf("unexpected cleanup; got %d want 3", cleanups)
891 }
892 }
893
894 func TestCleanupCalledEvenAfterGoexit(t *T) {
895 cleanups := 0
896 t.Run("test", func(t *T) {
897 t.Cleanup(func() {
898 cleanups++
899 })
900 t.Cleanup(func() {
901 runtime.Goexit()
902 })
903 })
904 if cleanups != 1 {
905 t.Errorf("unexpected cleanup count; got %d want 1", cleanups)
906 }
907 }
908
909 func TestRunCleanup(t *T) {
910 outerCleanup := 0
911 innerCleanup := 0
912 t.Run("test", func(t *T) {
913 t.Cleanup(func() { outerCleanup++ })
914 t.Run("x", func(t *T) {
915 t.Cleanup(func() { innerCleanup++ })
916 })
917 })
918 if innerCleanup != 1 {
919 t.Errorf("unexpected inner cleanup count; got %d want 1", innerCleanup)
920 }
921 if outerCleanup != 1 {
922 t.Errorf("unexpected outer cleanup count; got %d want 0", outerCleanup)
923 }
924 }
925
926 func TestCleanupParallelSubtests(t *T) {
927 ranCleanup := 0
928 t.Run("test", func(t *T) {
929 t.Cleanup(func() { ranCleanup++ })
930 t.Run("x", func(t *T) {
931 t.Parallel()
932 if ranCleanup > 0 {
933 t.Error("outer cleanup ran before parallel subtest")
934 }
935 })
936 })
937 if ranCleanup != 1 {
938 t.Errorf("unexpected cleanup count; got %d want 1", ranCleanup)
939 }
940 }
941
942 func TestNestedCleanup(t *T) {
943 ranCleanup := 0
944 t.Run("test", func(t *T) {
945 t.Cleanup(func() {
946 if ranCleanup != 2 {
947 t.Errorf("unexpected cleanup count in first cleanup: got %d want 2", ranCleanup)
948 }
949 ranCleanup++
950 })
951 t.Cleanup(func() {
952 if ranCleanup != 0 {
953 t.Errorf("unexpected cleanup count in second cleanup: got %d want 0", ranCleanup)
954 }
955 ranCleanup++
956 t.Cleanup(func() {
957 if ranCleanup != 1 {
958 t.Errorf("unexpected cleanup count in nested cleanup: got %d want 1", ranCleanup)
959 }
960 ranCleanup++
961 })
962 })
963 })
964 if ranCleanup != 3 {
965 t.Errorf("unexpected cleanup count: got %d want 3", ranCleanup)
966 }
967 }
968
View as plain text