diff options
Diffstat (limited to 'src/testing/sub_test.go')
| -rw-r--r-- | src/testing/sub_test.go | 263 |
1 files changed, 262 insertions, 1 deletions
diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go index 82ec5809e5..1a6cfed594 100644 --- a/src/testing/sub_test.go +++ b/src/testing/sub_test.go @@ -503,6 +503,100 @@ func TestTRun(t *T) { t2.FailNow() }) }, + }, { + desc: "buffered output gets flushed at test end", + ok: false, + output: ` +--- FAIL: buffered output gets flushed at test end (0.00s) + --- FAIL: buffered output gets flushed at test end/#00 (0.00s) + a + b`, + f: func(t *T) { + t.Run("", func(t *T) { + o := t.Output() + o.Write([]byte("a\n")) + o.Write([]byte("b")) + t.Fail() + }) + }, + }, { + desc: "output with chatty", + ok: true, + chatty: true, + output: ` +=== RUN output with chatty +=== RUN output with chatty/#00 + a + b +--- PASS: output with chatty (0.00s) + --- PASS: output with chatty/#00 (0.00s)`, + f: func(t *T) { + t.Run("", func(t *T) { + o := t.Output() + o.Write([]byte("a\n")) + o.Write([]byte("b")) + }) + }, + }, { + desc: "output with chatty and json", + ok: true, + chatty: true, + json: true, + output: ` +^V=== RUN output with chatty and json +^V=== RUN output with chatty and json/#00 + a + b +^V--- PASS: output with chatty and json/#00 (0.00s) +^V=== NAME output with chatty and json +^V--- PASS: output with chatty and json (0.00s) +^V=== NAME +`, + f: func(t *T) { + t.Run("", func(t *T) { + o := t.Output() + o.Write([]byte("a\n")) + o.Write([]byte("b")) + }) + }, + }, { + desc: "output in finished sub test outputs to parent", + ok: false, + output: ` + --- FAIL: output in finished sub test outputs to parent (N.NNs) + message2 + message1 + sub_test.go:NNN: error`, + f: func(t *T) { + ch := make(chan bool) + t.Run("sub", func(t2 *T) { + go func() { + <-ch + t2.Output().Write([]byte("message1\n")) + ch <- true + }() + }) + t.Output().Write([]byte("message2\n")) + ch <- true + <-ch + t.Errorf("error") + }, + }, { + desc: "newline between buffered log and log", + ok: false, + output: ` +--- FAIL: newline between buffered log and log (0.00s) + --- FAIL: newline between buffered log and log/#00 (0.00s) + buffered message + sub_test.go:NNN: log`, + f: func(t *T) { + t.Run("", func(t *T) { + o := t.Output() + o.Write([]byte("buffered message")) + t.Log("log") + t.Fail() + }) + }, }} for _, tc := range testCases { t.Run(tc.desc, func(t *T) { @@ -840,7 +934,7 @@ func TestLogAfterComplete(t *T) { } s, ok := p.(string) if !ok { - c2 <- fmt.Sprintf("subtest panic with unexpected value %v", p) + c2 <- fmt.Sprintf("subtest panic with unexpected value %v of type %T", p, p) return } const want = "Log in goroutine after TestLateLog has completed: log after test" @@ -989,3 +1083,170 @@ func TestNestedCleanup(t *T) { t.Errorf("unexpected cleanup count: got %d want 3", ranCleanup) } } + +// TestOutput checks that log messages are written, +// formatted and buffered as expected by Output. It +// checks both the chatty and non-chatty cases. +func TestOutput(t *T) { + tstate := newTestState(1, allMatcher()) + root := &T{ + tstate: tstate, + } + root.setOutputWriter() + o := root.Output() + + // Chatty case + tstateChatty := newTestState(1, allMatcher()) + bufChatty := &strings.Builder{} + rootChatty := &T{ + common: common{ + w: bufChatty, + }, + tstate: tstateChatty, + } + rootChatty.setOutputWriter() + rootChatty.chatty = newChattyPrinter(rootChatty.w) + oChatty := rootChatty.Output() + + testCases := []struct { + in string + out string + buf string + }{{ + in: "a", + out: "", + buf: "a", + }, { + in: "b", + out: "", + buf: "ab", + }, { + in: "\n", + out: " ab\n", + buf: "", + }, { + in: "\nc", + out: " ab\n \n", + buf: "c", + }, { + in: "d", + out: " ab\n \n", + buf: "cd", + }} + for _, tc := range testCases { + o.Write([]byte(tc.in)) + if string(root.output) != tc.out { + t.Errorf("output:\ngot:\n%s\nwant:\n%s", root.output, tc.out) + } + if string(root.o.partial) != tc.buf { + t.Errorf("buffer:\ngot:\n%s\nwant:\n%s", root.o.partial, tc.buf) + } + + // Chatty case + oChatty.Write([]byte(tc.in)) + if got := bufChatty.String(); got != tc.out { + t.Errorf("output:\ngot:\n%s\nwant:\n%s", got, tc.out) + } + } +} + +// TestOutputAfterComplete ensures that Output panics +// if called after a test function returns. +func TestOutputAfterComplete(t *T) { + tstate := newTestState(1, allMatcher()) + var buf bytes.Buffer + t1 := &T{ + common: common{ + // Use a buffered channel so that tRunner can write + // to it although nothing is reading from it. + signal: make(chan bool, 1), + w: &buf, + }, + tstate: tstate, + } + + c1 := make(chan bool) + c2 := make(chan string) + tRunner(t1, func(t *T) { + t.Run("TestLateOutput", func(t *T) { + go func() { + defer close(c2) + defer func() { + p := recover() + if p == nil { + c2 <- "subtest did not panic" + return + } + s, ok := p.(string) + if !ok { + c2 <- fmt.Sprintf("subtest panic with unexpected value %v of type %T", p, p) + return + } + const want = "Output called after TestLateOutput has completed" + if !strings.Contains(s, want) { + c2 <- fmt.Sprintf("subtest panic %q does not contain %q", s, want) + } + }() + + <-c1 + t.Output() + }() + }) + }) + close(c1) + + if s := <-c2; s != "" { + t.Error(s) + } +} + +// TestOutputWriteAfterComplete ensures that Write panics +// if called on t.Output() of a finished test t. +func TestOutputWriteAfterComplete(t *T) { + tstate := newTestState(1, allMatcher()) + var buf bytes.Buffer + t1 := &T{ + common: common{ + // Use a buffered channel so that tRunner can write + // to it although nothing is reading from it. + signal: make(chan bool, 1), + w: &buf, + }, + tstate: tstate, + } + + c1 := make(chan bool) + c2 := make(chan string) + tRunner(t1, func(t *T) { + t.Run("TestLateWrite", func(t *T) { + o := t.Output() + go func() { + defer close(c2) + defer func() { + p := recover() + if p == nil { + c2 <- "subtest did not panic" + return + } + s, ok := p.(string) + if !ok { + c2 <- fmt.Sprintf("subtest panic with unexpected value %v of type %T", p, p) + return + } + const want = "Write called after TestLateWrite has completed" + if !strings.Contains(s, want) { + c2 <- fmt.Sprintf("subtest panic %q does not contain %q", s, want) + } + }() + + <-c1 + o.Write([]byte("write after test")) + }() + }) + }) + close(c1) + + if s := <-c2; s != "" { + t.Error(s) + } +} |
