]> Cypherpunks repositories - gostls13.git/commitdiff
testing: fix panicking tests hang if Cleanup calls FailNow
authorChangkun Ou <hi@changkun.us>
Mon, 14 Sep 2020 08:24:59 +0000 (10:24 +0200)
committerIan Lance Taylor <iant@golang.org>
Mon, 14 Sep 2020 20:27:49 +0000 (20:27 +0000)
Previously, it was impossible to call FailNow in a Cleanup.
Because it can terminate a panicking goroutine and cause its
parent hangs on t.signal channel. This CL sends the signal
in a deferred call to prevent the hang.

Fixes #41355

Change-Id: I4552d3a7ea763ef86817bf9b50c0e37fb34bf20f
Reviewed-on: https://go-review.googlesource.com/c/go/+/254637
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Emmanuel Odeke <emm.odeke@gmail.com>

src/cmd/go/testdata/script/test_cleanup_failnow.txt [new file with mode: 0644]
src/testing/testing.go

diff --git a/src/cmd/go/testdata/script/test_cleanup_failnow.txt b/src/cmd/go/testdata/script/test_cleanup_failnow.txt
new file mode 100644 (file)
index 0000000..5ad4185
--- /dev/null
@@ -0,0 +1,33 @@
+# For issue 41355
+[short] skip
+
+! go test -v cleanup_failnow/panic_nocleanup_test.go
+stdout '(?s)panic: die \[recovered\].*panic: die'
+! stdout '(?s)panic: die \[recovered\].*panic: die.*panic: die'
+
+! go test -v cleanup_failnow/panic_withcleanup_test.go
+stdout '(?s)panic: die \[recovered\].*panic: die'
+! stdout '(?s)panic: die \[recovered\].*panic: die.*panic: die'
+
+-- cleanup_failnow/panic_nocleanup_test.go --
+package panic_nocleanup_test
+import "testing"
+func TestX(t *testing.T) {
+       t.Run("x", func(t *testing.T) {
+               panic("die")
+       })
+}
+
+-- cleanup_failnow/panic_withcleanup_test.go --
+package panic_withcleanup_test
+import "testing"
+func TestCleanupWithFailNow(t *testing.T) {
+       t.Cleanup(func() {
+               t.FailNow()
+       })
+       t.Run("x", func(t *testing.T) {
+               t.Run("y", func(t *testing.T) {
+                       panic("die")
+               })
+       })
+}
\ No newline at end of file
index 66f296234a5b1a7ffa12b18d47091c6abb158b79..d86354093a4f5da30dafebc6df80d81031df175f 100644 (file)
@@ -1075,6 +1075,7 @@ func tRunner(t *T, fn func(t *T)) {
                // If the test panicked, print any test output before dying.
                err := recover()
                signal := true
+
                if !t.finished && err == nil {
                        err = errNilPanicOrGoexit
                        for p := t.parent; p != nil; p = p.parent {
@@ -1086,6 +1087,15 @@ func tRunner(t *T, fn func(t *T)) {
                                }
                        }
                }
+               // Use a deferred call to ensure that we report that the test is
+               // complete even if a cleanup function calls t.FailNow. See issue 41355.
+               didPanic := false
+               defer func() {
+                       t.signal <- signal
+                       if err != nil && !didPanic {
+                               panic(err)
+                       }
+               }()
 
                doPanic := func(err interface{}) {
                        t.Fail()
@@ -1103,6 +1113,7 @@ func tRunner(t *T, fn func(t *T)) {
                                        fmt.Fprintf(root.parent.w, "cleanup panicked with %v", r)
                                }
                        }
+                       didPanic = true
                        panic(err)
                }
                if err != nil {
@@ -1144,7 +1155,6 @@ func tRunner(t *T, fn func(t *T)) {
                if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 {
                        t.setRan()
                }
-               t.signal <- signal
        }()
        defer func() {
                if len(t.sub) == 0 {