Commit ac56baa0 authored by Emmanuel T Odeke's avatar Emmanuel T Odeke Committed by Brad Fitzpatrick

testing: enable examples on js/wasm with non os.Pipe runExample

os.Pipe is not implemented on wasm/js so for that purpose use
a temporary file for js/wasm. This change creates two versions
of runExample:

* runExample verbatim that still uses os.Pipe for non js/wasm
* runExample that uses a temporary file

Also added a TODO to re-unify these function versions back into
example.go wasm/js gets an os.Pipe implementation.

Change-Id: I9f418a49b2c397e1667724c7442b7bbe8942225e
Reviewed-on: https://go-review.googlesource.com/c/go/+/165357
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 48d3c32b
...@@ -6,7 +6,6 @@ package testing ...@@ -6,7 +6,6 @@ package testing
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"sort" "sort"
"strings" "strings"
...@@ -56,68 +55,39 @@ func sortLines(output string) string { ...@@ -56,68 +55,39 @@ func sortLines(output string) string {
return strings.Join(lines, "\n") return strings.Join(lines, "\n")
} }
func runExample(eg InternalExample) (ok bool) { // processRunResult computes a summary and status of the result of running an example test.
if *chatty { // stdout is the captured output from stdout of the test.
fmt.Printf("=== RUN %s\n", eg.Name) // recovered is the result of invoking recover after running the test, in case it panicked.
} //
// If stdout doesn't match the expected output or if recovered is non-nil, it'll print the cause of failure to stdout.
// Capture stdout. // If the test is chatty/verbose, it'll print a success message to stdout.
stdout := os.Stdout // If recovered is non-nil, it'll panic with that value.
r, w, err := os.Pipe() func (eg *InternalExample) processRunResult(stdout string, timeSpent time.Duration, recovered interface{}) (passed bool) {
if err != nil { passed = true
fmt.Fprintln(os.Stderr, err)
os.Exit(1) dstr := fmtDuration(timeSpent)
}
os.Stdout = w
outC := make(chan string)
go func() {
var buf strings.Builder
_, err := io.Copy(&buf, r)
r.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "testing: copying pipe: %v\n", err)
os.Exit(1)
}
outC <- buf.String()
}()
start := time.Now()
ok = true
// Clean up in a deferred call so we can recover if the example panics.
defer func() {
dstr := fmtDuration(time.Since(start))
// Close pipe, restore stdout, get output.
w.Close()
os.Stdout = stdout
out := <-outC
var fail string var fail string
err := recover() got := strings.TrimSpace(stdout)
got := strings.TrimSpace(out)
want := strings.TrimSpace(eg.Output) want := strings.TrimSpace(eg.Output)
if eg.Unordered { if eg.Unordered {
if sortLines(got) != sortLines(want) && err == nil { if sortLines(got) != sortLines(want) && recovered == nil {
fail = fmt.Sprintf("got:\n%s\nwant (unordered):\n%s\n", out, eg.Output) fail = fmt.Sprintf("got:\n%s\nwant (unordered):\n%s\n", stdout, eg.Output)
} }
} else { } else {
if got != want && err == nil { if got != want && recovered == nil {
fail = fmt.Sprintf("got:\n%s\nwant:\n%s\n", got, want) fail = fmt.Sprintf("got:\n%s\nwant:\n%s\n", got, want)
} }
} }
if fail != "" || err != nil { if fail != "" || recovered != nil {
fmt.Printf("--- FAIL: %s (%s)\n%s", eg.Name, dstr, fail) fmt.Printf("--- FAIL: %s (%s)\n%s", eg.Name, dstr, fail)
ok = false passed = false
} else if *chatty { } else if *chatty {
fmt.Printf("--- PASS: %s (%s)\n", eg.Name, dstr) fmt.Printf("--- PASS: %s (%s)\n", eg.Name, dstr)
} }
if err != nil { if recovered != nil {
panic(err) // Propagate the previously recovered result, by panicking.
panic(recovered)
} }
}()
// Run example.
eg.F()
return return
} }
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !js
// TODO(@musiol, @odeke-em): re-unify this entire file back into
// example.go when js/wasm gets an os.Pipe implementation
// and no longer needs this separation.
package testing
import (
"fmt"
"io"
"os"
"strings"
"time"
)
func runExample(eg InternalExample) (ok bool) {
if *chatty {
fmt.Printf("=== RUN %s\n", eg.Name)
}
// Capture stdout.
stdout := os.Stdout
r, w, err := os.Pipe()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Stdout = w
outC := make(chan string)
go func() {
var buf strings.Builder
_, err := io.Copy(&buf, r)
r.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "testing: copying pipe: %v\n", err)
os.Exit(1)
}
outC <- buf.String()
}()
start := time.Now()
// Clean up in a deferred call so we can recover if the example panics.
defer func() {
timeSpent := time.Since(start)
// Close pipe, restore stdout, get output.
w.Close()
os.Stdout = stdout
out := <-outC
err := recover()
ok = eg.processRunResult(out, timeSpent, err)
}()
// Run example.
eg.F()
return
}
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build js
package testing
import (
"fmt"
"io"
"os"
"strings"
"time"
)
// TODO(@musiol, @odeke-em): unify this code back into
// example.go when js/wasm gets an os.Pipe implementation.
func runExample(eg InternalExample) (ok bool) {
if *chatty {
fmt.Printf("=== RUN %s\n", eg.Name)
}
// Capture stdout to temporary file. We're not using
// os.Pipe because it is not supported on js/wasm.
stdout := os.Stdout
f := createTempFile(eg.Name)
os.Stdout = f
start := time.Now()
// Clean up in a deferred call so we can recover if the example panics.
defer func() {
timeSpent := time.Since(start)
// Restore stdout, get output and remove temporary file.
os.Stdout = stdout
var buf strings.Builder
_, seekErr := f.Seek(0, os.SEEK_SET)
_, readErr := io.Copy(&buf, f)
out := buf.String()
f.Close()
os.Remove(f.Name())
if seekErr != nil {
fmt.Fprintf(os.Stderr, "testing: seek temp file: %v\n", seekErr)
os.Exit(1)
}
if readErr != nil {
fmt.Fprintf(os.Stderr, "testing: read temp file: %v\n", readErr)
os.Exit(1)
}
err := recover()
ok = eg.processRunResult(out, timeSpent, err)
}()
// Run example.
eg.F()
return
}
func createTempFile(exampleName string) *os.File {
for i := 0; ; i++ {
name := fmt.Sprintf("%s/go-example-stdout-%s-%d.txt", os.TempDir(), exampleName, i)
f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
if err != nil {
if os.IsExist(err) {
continue
}
fmt.Fprintf(os.Stderr, "testing: open temp file: %v\n", err)
os.Exit(1)
}
return f
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment