Skip to content

Commit a32b5af

Browse files
committed
Add test cases for kernel prints to stdout/stderr
1 parent 9e646b7 commit a32b5af

File tree

1 file changed

+136
-3
lines changed

1 file changed

+136
-3
lines changed

kernel_test.go

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func TestEvaluate(t *testing.T) {
7474
Output string
7575
}{
7676
{[]string{
77-
"import \"fmt\"",
77+
`import "fmt"`,
7878
"a := 1",
7979
"fmt.Println(a)",
8080
}, "1\n"},
@@ -86,7 +86,7 @@ func TestEvaluate(t *testing.T) {
8686
"func myFunc(x int) int {",
8787
" return x+1",
8888
"}",
89-
"fmt.Println(\"func defined\")",
89+
`fmt.Println("func defined")`,
9090
}, "func defined\n"},
9191
{[]string{
9292
"b := myFunc(1)",
@@ -146,7 +146,7 @@ func TestPanicGeneratesError(t *testing.T) {
146146
client, closeClient := newTestJupyterClient(t)
147147
defer closeClient()
148148

149-
content, pub := client.executeCode(t, `panic("error"`)
149+
content, pub := client.executeCode(t, `panic("error")`)
150150

151151
status := getString(t, "content", content, "status")
152152

@@ -167,6 +167,111 @@ func TestPanicGeneratesError(t *testing.T) {
167167
}
168168
}
169169

170+
// TestPrintStdout tests that data written to stdout publishes the same data in a "stdout" "stream" message.
171+
func TestPrintStdout(t *testing.T) {
172+
cases := []struct {
173+
Input []string
174+
Output []string
175+
}{
176+
{[]string{
177+
`import "fmt"`,
178+
"a := 1",
179+
"fmt.Println(a)",
180+
}, []string{"1\n"}},
181+
{[]string{
182+
"a = 2",
183+
"fmt.Print(a)",
184+
}, []string{"2"}},
185+
{[]string{
186+
`import "os"`,
187+
`os.Stdout.WriteString("3")`,
188+
}, []string{"3"}},
189+
{[]string{
190+
`fmt.Fprintf(os.Stdout, "%d\n", 4)`,
191+
}, []string{"4\n"}},
192+
{[]string{
193+
`import "time"`,
194+
"for i := 0; i < 3; i++ {",
195+
" fmt.Println(i)",
196+
" time.Sleep(500 * time.Millisecond)", // Stall to prevent prints from buffering into single message.
197+
"}",
198+
}, []string{"0\n", "1\n", "2\n"}},
199+
}
200+
201+
t.Logf("Should produce stdout stream messages when writing to stdout")
202+
203+
cases:
204+
for k, tc := range cases {
205+
// Give a progress report.
206+
t.Logf(" Evaluating code snippet %d/%d.", k+1, len(cases))
207+
208+
// Get the result.
209+
stdout, _ := testOutputStream(t, strings.Join(tc.Input, "\n"))
210+
211+
// Compare the result.
212+
if len(stdout) != len(tc.Output) {
213+
t.Errorf("\t%s Test case expected %d message(s) on stdout but got %d.", failure, len(tc.Output), len(stdout))
214+
continue
215+
}
216+
for i, expected := range tc.Output {
217+
if stdout[i] != expected {
218+
t.Errorf("\t%s Test case returned unexpected messages on stdout.", failure)
219+
continue cases
220+
}
221+
}
222+
t.Logf("\t%s Returned the expected messages on stdout.", success)
223+
}
224+
}
225+
226+
// TestPrintStderr tests that data written to stderr publishes the same data in a "stderr" "stream" message.
227+
func TestPrintStderr(t *testing.T) {
228+
cases := []struct {
229+
Input []string
230+
Output []string
231+
}{
232+
{[]string{
233+
`import "fmt"`,
234+
`import "os"`,
235+
"a := 1",
236+
"fmt.Fprintln(os.Stderr, a)",
237+
}, []string{"1\n"}},
238+
{[]string{
239+
`os.Stderr.WriteString("2")`,
240+
}, []string{"2"}},
241+
{[]string{
242+
`import "time"`,
243+
"for i := 0; i < 3; i++ {",
244+
" fmt.Fprintln(os.Stderr, i)",
245+
" time.Sleep(500 * time.Millisecond)", // Stall to prevent prints from buffering into single message.
246+
"}",
247+
}, []string{"0\n", "1\n", "2\n"}},
248+
}
249+
250+
t.Logf("Should produce stderr stream messages when writing to stderr")
251+
252+
cases:
253+
for k, tc := range cases {
254+
// Give a progress report.
255+
t.Logf(" Evaluating code snippet %d/%d.", k+1, len(cases))
256+
257+
// Get the result.
258+
_, stderr := testOutputStream(t, strings.Join(tc.Input, "\n"))
259+
260+
// Compare the result.
261+
if len(stderr) != len(tc.Output) {
262+
t.Errorf("\t%s Test case expected %d message(s) on stderr but got %d.", failure, len(tc.Output), len(stderr))
263+
continue
264+
}
265+
for i, expected := range tc.Output {
266+
if stderr[i] != expected {
267+
t.Errorf("\t%s Test case returned unexpected messages on stderr.", failure)
268+
continue cases
269+
}
270+
}
271+
t.Logf("\t%s Returned the expected messages on stderr.", success)
272+
}
273+
}
274+
170275
//==============================================================================
171276

172277
// testJupyterClient holds references to the 2 sockets it uses to communicate with the kernel.
@@ -437,3 +542,31 @@ func getJSONObject(t *testing.T, jsonObjectName string, content map[string]inter
437542

438543
return value
439544
}
545+
546+
// testOutputStream is a test helper that collects "stream" messages upon executing the codeIn.
547+
func testOutputStream(t *testing.T, codeIn string) (stdout []string, stderr []string) {
548+
t.Helper()
549+
550+
client, closeClient := newTestJupyterClient(t)
551+
defer closeClient()
552+
553+
_, pub := client.executeCode(t, codeIn)
554+
555+
for _, pubMsg := range pub {
556+
if pubMsg.Header.MsgType == "stream" {
557+
content := getMsgContentAsJSONObject(t, pubMsg)
558+
streamType := getString(t, "content", content, "name")
559+
streamData := getString(t, "content", content, "text")
560+
561+
if streamType == "stdout" {
562+
stdout = append(stdout, streamData)
563+
} else if streamType == "stderr" {
564+
stderr = append(stderr, streamData)
565+
} else {
566+
t.Fatalf("Unknown stream type '%s'", streamType)
567+
}
568+
}
569+
}
570+
571+
return
572+
}

0 commit comments

Comments
 (0)