Skip to content

Commit 7d94d73

Browse files
flimzydmitshur
authored andcommitted
Use temporary file rather than os.Pipe for testing examples. (#590)
This change works around the issues discussed in #529 by using a temporary file for capturing the stdout of a runnable example, rather than os.Pipe. This allows examples to work in cases where they would previously stall. ioutil.ReadFile and its supporting functions are copied from ioutil into testing, in order to avoid having "testing" package import extra packages that the real "testing" package does not. gopherjs tool testFuncs.load does not take into account the overridden natives, see https://github.com/gopherjs/gopherjs/blob/b9bcb1da229a59cc1e1d168401662cb6450aae08/tool.go#L827. So we still need to print the expected output for the example to pass. Given there are only 2 disabled examples, it's easier to just do this in short term. But it might be good to eventually improve code for overriding natives so that examples are properly overridden as well. Regenerate compiler/natives.
1 parent 34726b3 commit 7d94d73

File tree

6 files changed

+293
-83
lines changed

6 files changed

+293
-83
lines changed

circle.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ test:
1818
- go tool vet *.go # Go package in root directory.
1919
- for d in */; do echo $d; done | grep -v tests/ | grep -v third_party/ | xargs go tool vet # All subdirectories except "tests", "third_party".
2020
- >
21-
gopherjs test --short --minify --run='^Test'
21+
gopherjs test --short --minify
2222
github.com/gopherjs/gopherjs/tests
2323
github.com/gopherjs/gopherjs/tests/main
2424
github.com/gopherjs/gopherjs/js

compiler/natives/fs_vfsdata.go

Lines changed: 128 additions & 82 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// +build js
2+
3+
package cookiejar_test
4+
5+
import "fmt"
6+
7+
func ExampleNew() {
8+
// network access not supported by GopherJS, and this test depends on httptest.NewServer
9+
10+
fmt.Println(`After 1st request:
11+
Flavor: Chocolate Chip
12+
After 2nd request:
13+
Flavor: Oatmeal Raisin`)
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// +build js
2+
3+
package reflect_test
4+
5+
import "fmt"
6+
7+
func ExampleStructOf() {
8+
// GopherJS does not implement reflect.addReflectOff needed for this test.
9+
// See https://github.com/gopherjs/gopherjs/issues/499
10+
11+
fmt.Println(`value: &{Height:0.4 Age:2}
12+
json: {"height":0.4,"age":2}
13+
value: &{Height:1.5 Age:10}`)
14+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// +build js
2+
3+
package testing
4+
5+
import (
6+
"fmt"
7+
"os"
8+
"strings"
9+
"time"
10+
)
11+
12+
func runExample(eg InternalExample) (ok bool) {
13+
if *chatty {
14+
fmt.Printf("=== RUN %s\n", eg.Name)
15+
}
16+
17+
// Capture stdout.
18+
stdout := os.Stdout
19+
w, err := tempFile("." + eg.Name + ".stdout.")
20+
if err != nil {
21+
fmt.Fprintln(os.Stderr, err)
22+
os.Exit(1)
23+
}
24+
os.Stdout = w
25+
26+
start := time.Now()
27+
ok = true
28+
29+
// Clean up in a deferred call so we can recover if the example panics.
30+
defer func() {
31+
dstr := fmtDuration(time.Now().Sub(start))
32+
33+
// Close file, restore stdout, get output.
34+
w.Close()
35+
os.Stdout = stdout
36+
out, readFileErr := readFile(w.Name())
37+
_ = os.Remove(w.Name())
38+
if readFileErr != nil {
39+
fmt.Fprintf(os.Stderr, "testing: reading stdout file: %v\n", readFileErr)
40+
os.Exit(1)
41+
}
42+
43+
var fail string
44+
err := recover()
45+
got := strings.TrimSpace(out)
46+
want := strings.TrimSpace(eg.Output)
47+
if eg.Unordered {
48+
if sortLines(got) != sortLines(want) && err == nil {
49+
fail = fmt.Sprintf("got:\n%s\nwant (unordered):\n%s\n", out, eg.Output)
50+
}
51+
} else {
52+
if got != want && err == nil {
53+
fail = fmt.Sprintf("got:\n%s\nwant:\n%s\n", got, want)
54+
}
55+
}
56+
if fail != "" || err != nil {
57+
fmt.Printf("--- FAIL: %s (%s)\n%s", eg.Name, dstr, fail)
58+
ok = false
59+
} else if *chatty {
60+
fmt.Printf("--- PASS: %s (%s)\n", eg.Name, dstr)
61+
}
62+
if err != nil {
63+
panic(err)
64+
}
65+
}()
66+
67+
// Run example.
68+
eg.F()
69+
return
70+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// +build js
2+
3+
package testing
4+
5+
import (
6+
"bytes"
7+
"io"
8+
"os"
9+
"strconv"
10+
"sync"
11+
"time"
12+
)
13+
14+
var rand uint32
15+
var randmu sync.Mutex
16+
17+
func reseed() uint32 {
18+
return uint32(time.Now().UnixNano() + int64(os.Getpid()))
19+
}
20+
21+
func nextSuffix() string {
22+
randmu.Lock()
23+
r := rand
24+
if r == 0 {
25+
r = reseed()
26+
}
27+
r = r*1664525 + 1013904223 // constants from Numerical Recipes
28+
rand = r
29+
randmu.Unlock()
30+
return strconv.Itoa(int(1e9 + r%1e9))[1:]
31+
}
32+
33+
// A functional copy of ioutil.TempFile, to avoid extra imports.
34+
func tempFile(prefix string) (f *os.File, err error) {
35+
dir := os.TempDir()
36+
37+
nconflict := 0
38+
for i := 0; i < 10000; i++ {
39+
name := dir + string(os.PathSeparator) + prefix + nextSuffix()
40+
f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
41+
if os.IsExist(err) {
42+
if nconflict++; nconflict > 10 {
43+
randmu.Lock()
44+
rand = reseed()
45+
randmu.Unlock()
46+
}
47+
continue
48+
}
49+
break
50+
}
51+
return
52+
}
53+
54+
func readFile(filename string) (string, error) {
55+
f, err := os.Open(filename)
56+
if err != nil {
57+
return "", err
58+
}
59+
defer f.Close()
60+
var buf bytes.Buffer
61+
_, err = io.Copy(&buf, f)
62+
if err != nil {
63+
return "", err
64+
}
65+
return buf.String(), nil
66+
}

0 commit comments

Comments
 (0)