Codebase list golang-github-frankban-quicktest / f6efa49
Merge pull request #56 from rogpeppe-contrib/010-defer-vs-cleanup integrate Defer with Cleanup Francesco Banconi authored 4 years ago GitHub committed 4 years ago
6 changed file(s) with 259 addition(s) and 138 deletion(s). Raw diff Collapse all Expand all
0 // +build go1.14
1
2 package quicktest_test
3
4 import (
5 "testing"
6
7 qt "github.com/frankban/quicktest"
8 )
9
10 // This file defines tests that are only valid since the Cleanup
11 // method was added in Go 1.14.
12
13 func TestCCleanup(t *testing.T) {
14 c := qt.New(t)
15 cleanups := 0
16 c.Run("defer", func(c *qt.C) {
17 c.Cleanup(func() {
18 cleanups++
19 })
20 })
21 c.Assert(cleanups, qt.Equals, 1)
22 }
23
24 func TestCDeferWithoutDone(t *testing.T) {
25 c := qt.New(t)
26 tc := &testingTWithCleanup{
27 TB: t,
28 cleanup: func() {},
29 }
30 c1 := qt.New(tc)
31 c1.Defer(func() {})
32 c1.Defer(func() {})
33 c.Assert(tc.cleanup, qt.PanicMatches, `Done not called after Defer`)
34 }
35
36 func TestCDeferVsCleanupOrder(t *testing.T) {
37 c := qt.New(t)
38 var defers []int
39 testDefer(c, func(c *qt.C) {
40 c.Defer(func() {
41 defers = append(defers, 0)
42 })
43 c.Cleanup(func() {
44 defers = append(defers, 1)
45 })
46 c.Defer(func() {
47 defers = append(defers, 2)
48 })
49 c.Cleanup(func() {
50 defers = append(defers, 3)
51 })
52 })
53 c.Assert(defers, qt.DeepEquals, []int{3, 2, 1, 0})
54 }
55
56 type testingTWithCleanup struct {
57 testing.TB
58 cleanup func()
59 }
60
61 func (t *testingTWithCleanup) Cleanup(f func()) {
62 oldCleanup := t.cleanup
63 t.cleanup = func() {
64 defer oldCleanup()
65 f()
66 }
67 }
0 // +build !go1.14
1
2 package quicktest_test
3
4 import (
5 "testing"
6
7 qt "github.com/frankban/quicktest"
8 )
9
10 func TestCDeferCalledEvenAfterDeferPanic(t *testing.T) {
11 // This test doesn't test anything useful under go 1.14 and
12 // later when Cleanup is built in.
13 c := qt.New(t)
14 deferred1 := 0
15 deferred2 := 0
16 c.Defer(func() {
17 deferred1++
18 })
19 c.Defer(func() {
20 panic("scream and shout")
21 })
22 c.Defer(func() {
23 deferred2++
24 })
25 c.Defer(func() {
26 panic("run in circles")
27 })
28 func() {
29 defer func() {
30 c.Check(recover(), qt.Equals, "scream and shout")
31 }()
32 c.Done()
33 }()
34 c.Assert(deferred1, qt.Equals, 1)
35 c.Assert(deferred2, qt.Equals, 1)
36 // Check that calling Done again doesn't panic.
37 c.Done()
38 c.Assert(deferred1, qt.Equals, 1)
39 c.Assert(deferred2, qt.Equals, 1)
40 }
1313 func TestPatchSetInt(t *testing.T) {
1414 c := qt.New(t)
1515 i := 99
16 c.Patch(&i, 88)
17 c.Assert(i, qt.Equals, 88)
18 c.Done()
16 testDefer(c, func(c *qt.C) {
17 c.Patch(&i, 88)
18 c.Assert(i, qt.Equals, 88)
19 })
1920 c.Assert(i, qt.Equals, 99)
2021 }
2122
2425 oldErr := errors.New("foo")
2526 newErr := errors.New("bar")
2627 err := oldErr
27 c.Patch(&err, newErr)
28 c.Assert(err, qt.Equals, newErr)
29 c.Done()
28 testDefer(c, func(c *qt.C) {
29 c.Patch(&err, newErr)
30 c.Assert(err, qt.Equals, newErr)
31 })
3032 c.Assert(err, qt.Equals, oldErr)
3133 }
3234
3436 c := qt.New(t)
3537 oldErr := errors.New("foo")
3638 err := oldErr
37 c.Patch(&err, nil)
38 c.Assert(err, qt.Equals, nil)
39 c.Done()
39 testDefer(c, func(c *qt.C) {
40 c.Patch(&err, nil)
41 c.Assert(err, qt.Equals, nil)
42 })
4043 c.Assert(err, qt.Equals, oldErr)
4144 }
4245
4447 c := qt.New(t)
4548 oldMap := map[string]int{"foo": 1234}
4649 m := oldMap
47 c.Patch(&m, nil)
48 c.Assert(m, qt.IsNil)
49 c.Done()
50 testDefer(c, func(c *qt.C) {
51 c.Patch(&m, nil)
52 c.Assert(m, qt.IsNil)
53 })
5054 c.Assert(m, qt.DeepEquals, oldMap)
5155 }
5256
6165 c := qt.New(t)
6266 const envName = "SOME_VAR"
6367 os.Setenv(envName, "initial")
64 c.Setenv(envName, "new value")
65 c.Check(os.Getenv(envName), qt.Equals, "new value")
66 c.Done()
68 testDefer(c, func(c *qt.C) {
69 c.Setenv(envName, "new value")
70 c.Check(os.Getenv(envName), qt.Equals, "new value")
71 })
6772 c.Check(os.Getenv(envName), qt.Equals, "initial")
6873 }
6974
7075 func TestMkdir(t *testing.T) {
7176 c := qt.New(t)
72 dir := c.Mkdir()
73 c.Assert(c, qt.Not(qt.Equals), "")
74 info, err := os.Stat(dir)
75 c.Assert(err, qt.Equals, nil)
76 c.Assert(info.IsDir(), qt.Equals, true)
77 f, err := os.Create(filepath.Join(dir, "hello"))
78 c.Assert(err, qt.Equals, nil)
79 f.Close()
80 c.Done()
81 _, err = os.Stat(dir)
77 var dir string
78 testDefer(c, func(c *qt.C) {
79 dir = c.Mkdir()
80 c.Assert(dir, qt.Not(qt.Equals), "")
81 info, err := os.Stat(dir)
82 c.Assert(err, qt.Equals, nil)
83 c.Assert(info.IsDir(), qt.Equals, true)
84 f, err := os.Create(filepath.Join(dir, "hello"))
85 c.Assert(err, qt.Equals, nil)
86 f.Close()
87 })
88 _, err := os.Stat(dir)
8289 c.Assert(err, qt.Not(qt.IsNil))
8390 }
5757 type C struct {
5858 testing.TB
5959
60 mu sync.Mutex
61 deferred func()
62 format formatFunc
60 mu sync.Mutex
61 doneNeeded bool
62 deferred func()
63 format formatFunc
64 }
65
66 // cleaner is implemented by testing.TB on Go 1.14 and later.
67 type cleaner interface {
68 Cleanup(func())
6369 }
6470
6571 // Defer registers a function to be called when c.Done is
6672 // called. Deferred functions will be called in last added, first called
67 // order.
73 // order. If c.Done is not called by the end of the test, the test
74 // may panic. Note that if Cleanup is called, there is no
75 // need to call Done.
6876 func (c *C) Defer(f func()) {
6977 c.mu.Lock()
7078 defer c.mu.Unlock()
79 if cleaner, ok := c.TB.(cleaner); ok {
80 // Use TB.Cleanup when available, but add a check
81 // that Done has been called so that we don't run
82 // into unexpected Go version incompatibilities.
83 if c.doneNeeded {
84 // We've already installed the wrapper func that checks for Done
85 // so we can avoid doing it again.
86 cleaner.Cleanup(f)
87 return
88 }
89 c.doneNeeded = true
90 cleaner.Cleanup(func() {
91 c.mu.Lock()
92 doneNeeded := c.doneNeeded
93 c.mu.Unlock()
94 if doneNeeded {
95 panic("Done not called after Defer")
96 }
97 f()
98 })
99 return
100 }
101
71102 oldDeferred := c.deferred
72103 c.deferred = func() {
73104 if oldDeferred != nil {
87118 c.mu.Lock()
88119 deferred := c.deferred
89120 c.deferred = nil
121 c.doneNeeded = false
90122 c.mu.Unlock()
91123
92124 if deferred != nil {
55 "bytes"
66 "errors"
77 "fmt"
8 "runtime"
98 "strings"
109 "testing"
1110
510509 func TestCDefer(t *testing.T) {
511510 c := qt.New(t)
512511 var defers []int
513 c.Defer(func() { defers = append(defers, 1) })
514 c.Defer(func() { defers = append(defers, 2) })
515 c.Done()
512 testDefer(c, func(c *qt.C) {
513 c.Defer(func() { defers = append(defers, 1) })
514 c.Defer(func() { defers = append(defers, 2) })
515 // Calling Done twice should not do anything more.
516 c.Done()
517 })
516518 c.Assert(defers, qt.DeepEquals, []int{2, 1})
517 // Calling Done again should not do anything.
518 c.Done()
519 c.Assert(defers, qt.DeepEquals, []int{2, 1})
520 }
521
522 func TestCDeferCalledEvenAfterDeferPanic(t *testing.T) {
523 c := qt.New(t)
524 deferred1 := 0
525 deferred2 := 0
526 c.Defer(func() {
527 deferred1++
528 })
529 c.Defer(func() {
530 panic("scream and shout")
531 })
532 c.Defer(func() {
533 deferred2++
534 })
535 c.Defer(func() {
536 panic("run in circles")
537 })
538 func() {
539 defer func() {
540 c.Check(recover(), qt.Equals, "scream and shout")
541 }()
542 c.Done()
543 }()
544 c.Assert(deferred1, qt.Equals, 1)
545 c.Assert(deferred2, qt.Equals, 1)
546 c.Done()
547 c.Assert(deferred1, qt.Equals, 1)
548 c.Assert(deferred2, qt.Equals, 1)
549519 }
550520
551521 func TestCDeferCalledEvenAfterGoexit(t *testing.T) {
554524 // called in that case.
555525 c := qt.New(t)
556526 defers := 0
557 c.Defer(func() {
558 defers++
559 })
560 c.Defer(func() {
561 runtime.Goexit()
562 })
563 done := make(chan struct{})
564 go func() {
565 defer close(done)
566 c.Done()
567 select {}
568 }()
569 <-done
527 testDefer(c, func(c *qt.C) {
528 c.Defer(func() {
529 defers++
530 })
531 c.Defer(func() {
532 c.SkipNow()
533 })
534 })
570535 c.Assert(defers, qt.Equals, 1)
571 c.Done()
536 }
537
538 func TestCRunDefer(t *testing.T) {
539 c := qt.New(t)
540 defers := 0
541 testDefer(c, func(c *qt.C) {
542 c.Run("x", func(c *qt.C) {
543 c.Defer(func() { defers++ })
544 })
545 })
572546 c.Assert(defers, qt.Equals, 1)
573 }
574
575 func TestCRunDefer(t *testing.T) {
576 c := qt.New(&testingT{})
577 outerDefer := 0
578 innerDefer := 0
579 c.Defer(func() { outerDefer++ })
580 c.Run("x", func(c *qt.C) {
581 c.Defer(func() { innerDefer++ })
582 })
583 c.Assert(innerDefer, qt.Equals, 1)
584 c.Assert(outerDefer, qt.Equals, 0)
585547 }
586548
587549 type customT struct {
755717 func (c *testingChecker) ArgNames() []string {
756718 return c.argNames
757719 }
720
721 func testDefer(c *qt.C, f func(c *qt.C)) {
722 c.Run("defer", func(c *qt.C) {
723 defer c.Done()
724 f(c)
725 })
726 }
1919 const N = 100
2020
2121 var x, y int32
22 c := qt.New(dummyT{})
23 var wg sync.WaitGroup
24 // start calls f in two goroutines, each
25 // running it N times.
26 // All the goroutines get started before we actually
27 // start them running, so that the race detector
28 // has a better chance of catching issues.
29 gogogo := make(chan struct{})
30 start := func(f func()) {
31 repeat := func() {
32 defer wg.Done()
33 <-gogogo
34 for i := 0; i < N; i++ {
35 f()
22 c := qt.New(dummyT{t})
23 testDefer(c, func(c *qt.C) {
24 var wg sync.WaitGroup
25 // start calls f in two goroutines, each
26 // running it N times.
27 // All the goroutines get started before we actually
28 // start them running, so that the race detector
29 // has a better chance of catching issues.
30 gogogo := make(chan struct{})
31 start := func(f func()) {
32 repeat := func() {
33 defer wg.Done()
34 <-gogogo
35 for i := 0; i < N; i++ {
36 f()
37 }
3638 }
39 wg.Add(2)
40 go repeat()
41 go repeat()
3742 }
38 wg.Add(2)
39 go repeat()
40 go repeat()
41 }
42 start(func() {
43 c.Defer(func() {
44 atomic.AddInt32(&x, 1)
43 start(func() {
44 c.Defer(func() {
45 atomic.AddInt32(&x, 1)
46 })
47 c.Defer(func() {
48 atomic.AddInt32(&y, 1)
49 })
4550 })
46 c.Defer(func() {
47 atomic.AddInt32(&y, 1)
51 start(func() {
52 c.Done()
4853 })
54 start(func() {
55 c.SetFormat(func(v interface{}) string {
56 return "x"
57 })
58 })
59 start(func() {
60 // Do an assert to exercise the formatter.
61 c.Check(true, qt.Equals, false)
62 })
63 start(func() {
64 c.Run("", func(c *qt.C) {})
65 })
66 close(gogogo)
67 wg.Wait()
4968 })
50 start(func() {
51 c.Done()
52 })
53 start(func() {
54 c.SetFormat(func(v interface{}) string {
55 return "x"
56 })
57 })
58 start(func() {
59 // Do an assert to exercise the formatter.
60 c.Check(true, qt.Equals, false)
61 })
62 start(func() {
63 c.Run("", func(c *qt.C) {})
64 })
65 close(gogogo)
66 wg.Wait()
67 c.Done()
68
6969 // Check that all the defer functions ran OK.
7070 if x != N*2 || y != N*2 {
7171 t.Fatalf("unexpected x, y counts; got %d, %d; want %d, %d", x, y, N*2, N*2)
7272 }
7373 }
7474
75 // dummyT implements the testing.TB methods
76 // required for TestConcurentMethods.
75 // dummyT wraps a *testing.T value suitable
76 // for TestConcurrentMethods so that calling Error
77 // won't fail the test and that it implements
78 // Run correctly.
7779 type dummyT struct {
78 testing.TB
80 *testing.T
7981 }
8082
8183 func (dummyT) Error(...interface{}) {}
8284
83 func (dummyT) Run(name string, f func(t *testing.T)) bool {
84 return false
85 func (t dummyT) Run(name string, f func(t dummyT)) bool {
86 return t.T.Run(name, func(t *testing.T) {
87 f(dummyT{t})
88 })
8589 }