aboutsummaryrefslogtreecommitdiff
path: root/src/testing/sub_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/testing/sub_test.go')
-rw-r--r--src/testing/sub_test.go263
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)
+ }
+}