From b70182d2507bf7e104a66f0438769b7b70209459 Mon Sep 17 00:00:00 2001 From: Brad Peabody Date: Sat, 16 May 2020 18:58:32 -0700 Subject: [PATCH 01/10] wasm test suite --- .circleci/config.yml | 1 + Makefile | 5 + go.mod | 2 + go.sum | 15 ++ tests/wasm/.gitignore | 4 + tests/wasm/0docker_entrypoint.sh | 10 ++ tests/wasm/0dockerfile | 10 ++ tests/wasm/0setup_server.go | 91 +++++++++++++ tests/wasm/0setup_test.go | 227 +++++++++++++++++++++++++++++++ tests/wasm/chan_pgm.go | 18 +++ tests/wasm/chan_test.go | 32 +++++ tests/wasm/event_pgm.go | 33 +++++ tests/wasm/event_test.go | 35 +++++ tests/wasm/fmtprint_pgm.go | 13 ++ tests/wasm/fmtprint_test.go | 31 +++++ 15 files changed, 527 insertions(+) create mode 100644 tests/wasm/.gitignore create mode 100644 tests/wasm/0docker_entrypoint.sh create mode 100644 tests/wasm/0dockerfile create mode 100644 tests/wasm/0setup_server.go create mode 100644 tests/wasm/0setup_test.go create mode 100644 tests/wasm/chan_pgm.go create mode 100644 tests/wasm/chan_test.go create mode 100644 tests/wasm/event_pgm.go create mode 100644 tests/wasm/event_test.go create mode 100644 tests/wasm/fmtprint_pgm.go create mode 100644 tests/wasm/fmtprint_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index e311b6a1c4..5cb9f1405e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -88,6 +88,7 @@ commands: - run: go test -v -tags=llvm<> ./cgo ./compileopts ./interp ./transform . - run: make gen-device -j4 - run: make smoketest + - run: make wasmtest-docker-headless - save_cache: key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }} paths: diff --git a/Makefile b/Makefile index fcdc72f7c3..3cfef37d6d 100644 --- a/Makefile +++ b/Makefile @@ -310,6 +310,11 @@ endif $(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export $(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/main +wasmtest: + $(GO) test ./tests/wasm +wasmtest-docker-headless: + $(GO) test ./tests/wasm -docker-headless + build/release: tinygo gen-device wasi-libc @mkdir -p build/release/tinygo/bin @mkdir -p build/release/tinygo/lib/clang/include diff --git a/go.mod b/go.mod index a2cbe4a787..a35767d3b0 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.11 require ( github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 + github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac + github.com/chromedp/chromedp v0.5.3 github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46 go.bug.st/serial v1.0.0 diff --git a/go.sum b/go.sum index 09327350e7..60fdd4db1a 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,25 @@ github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI= github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= +github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac h1:T7V5BXqnYd55Hj/g5uhDYumg9Fp3rMTS6bykYtTIFX4= +github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g= +github.com/chromedp/chromedp v0.5.3 h1:F9LafxmYpsQhWQBdCs+6Sret1zzeeFyHS5LkRF//Ffg= +github.com/chromedp/chromedp v0.5.3/go.mod h1:YLdPtndaHQ4rCpSpBG+IPpy9JvX0VD+7aaLxYgYj28w= github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0= github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg= github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= +github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 h1:V0an7KRw92wmJysvFvtqtKMAPmvS5O0jtB0nYo6t+gs= +github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46 h1:wXG2bA8fO7Vv7lLk2PihFMTqmbT173Tje39oKzQ50Mo= github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -27,6 +41,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef h1:ymc9FeDom3RIEA3coKokSllBB1hRcMT0tZ1W3Jf9Ids= golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/tests/wasm/.gitignore b/tests/wasm/.gitignore new file mode 100644 index 0000000000..44023ec003 --- /dev/null +++ b/tests/wasm/.gitignore @@ -0,0 +1,4 @@ +*.wasm +server +server.pid +wasm_exec.js diff --git a/tests/wasm/0docker_entrypoint.sh b/tests/wasm/0docker_entrypoint.sh new file mode 100644 index 0000000000..e935d343b7 --- /dev/null +++ b/tests/wasm/0docker_entrypoint.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -e + +# launch headless chrome and our test server, exit immediately if one of them terminates +/headless-shell/headless-shell --no-sandbox --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 & + +cd /wasm +/bin/server & + +wait -n diff --git a/tests/wasm/0dockerfile b/tests/wasm/0dockerfile new file mode 100644 index 0000000000..3bef1ca9e5 --- /dev/null +++ b/tests/wasm/0dockerfile @@ -0,0 +1,10 @@ + +FROM chromedp/headless-shell:latest + +RUN apt-get update && apt-get install dumb-init + +COPY server /bin/server +COPY 0docker_entrypoint.sh /0docker_entrypoint.sh +RUN chmod 755 /bin/server /0docker_entrypoint.sh + +ENTRYPOINT ["/usr/bin/dumb-init", "--", "/0docker_entrypoint.sh"] diff --git a/tests/wasm/0setup_server.go b/tests/wasm/0setup_server.go new file mode 100644 index 0000000000..6ab2f5fb7c --- /dev/null +++ b/tests/wasm/0setup_server.go @@ -0,0 +1,91 @@ +// +build ignore + +package main + +import ( + "flag" + "fmt" + "log" + "net/http" + "path/filepath" +) + +// This is a standalone static file server which is used to serve pages for browser testing. +// It is a separate program so we can run it inside the same docker container as headless chrome, +// in order to avoid port mapping issues. + +var addr = flag.String("addr", ":8826", "Host:port to listen on") +var dir = flag.String("dir", ".", "Directory to serve static files from") + +func main() { + + absDir, err := filepath.Abs(*dir) + if err != nil { + panic(err) + } + fsh := http.FileServer(http.Dir(absDir)) + h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + if r.URL.Path == "/run" { + fmt.Fprintf(w, ` + + +Test + + + +
+

+
+
+
+
+`, r.FormValue("file"))
+			return
+		}
+
+		fsh.ServeHTTP(w, r)
+	})
+
+	log.Printf("Starting server at %q for dir: %s", *addr, absDir)
+	log.Fatal(http.ListenAndServe(*addr, h))
+}
diff --git a/tests/wasm/0setup_test.go b/tests/wasm/0setup_test.go
new file mode 100644
index 0000000000..dd1e8d7360
--- /dev/null
+++ b/tests/wasm/0setup_test.go
@@ -0,0 +1,227 @@
+package wasm
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/chromedp/cdproto/cdp"
+	"github.com/chromedp/chromedp"
+)
+
+var dockerHeadless = flag.Bool("docker-headless", false, "Launch headless Chrome via docker instead looking for it on local filesystem")
+var noCleanup = flag.Bool("no-cleanup", false, "Skip cleanup operations (useful for manual viewing and debugging)")
+
+func TestMain(m *testing.M) {
+	flag.Parse()
+
+	setup()
+	ret := m.Run()
+	cleanup()
+	os.Exit(ret)
+}
+
+func setup() {
+
+	// copy wasm_exec.js from targets to this dir
+	b, err := ioutil.ReadFile("../../targets/wasm_exec.js")
+	must(err)
+	must(ioutil.WriteFile("wasm_exec.js", b, 0644))
+
+	if *dockerHeadless {
+
+		// build server for arch that works in docker
+		args := []string{"go", "build", "-o", "server", "0setup_server.go"}
+		cmd := exec.Command(args[0], args[1:]...)
+		cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux")
+		b, err := cmd.CombinedOutput()
+		log.Printf("Command: %s; err=%v; full output:\n%s", strings.Join(args, " "), err, b)
+		must(err)
+
+		absDir, err := filepath.Abs(".")
+		must(err)
+		// launch headless chrome and server via docker if requested
+		must(run("docker build -t tinygo_wasm_test -f 0dockerfile ."))
+		run("docker stop tinygo_wasm_test") // ignore error
+
+		must(runargs("docker", "run",
+			"-d", "--rm",
+			"--name=tinygo_wasm_test",
+			"--mount", "type=bind,target=/wasm,source="+absDir,
+			"-p", "9222:9222", "-p", "8826:8826",
+			"tinygo_wasm_test:latest"))
+
+	} else {
+
+		killServer()
+
+		// build server for local arch
+		must(run("go build -o server 0setup_server.go"))
+
+		// or otherwise just launch server
+		serverCmd = exec.Command("./server")
+		serverCmd.Stdout = os.Stdout
+		serverCmd.Stderr = os.Stderr
+		must(serverCmd.Start())
+
+		must(ioutil.WriteFile("server.pid", []byte(fmt.Sprintf("%d\n", serverCmd.Process.Pid)), 0644))
+
+	}
+
+}
+
+var serverCmd *exec.Cmd
+
+func cleanup() {
+	// log.Println("performing cleanup")
+
+	if *noCleanup {
+		return
+	}
+	if *dockerHeadless {
+		run("docker stop tinygo_wasm_test") // ignore error
+	} else {
+		killServer()
+	}
+	os.Remove("wasm_exec.js")
+	os.Remove("server")
+	f, err := os.Open(".")
+	must(err)
+	defer f.Close()
+	names, err := f.Readdirnames(-1)
+	must(err)
+	for _, name := range names {
+		if filepath.Ext(name) == ".wasm" {
+			os.Remove(name)
+		}
+	}
+}
+
+func run(cmdline string) error {
+	args := strings.Fields(cmdline)
+	return runargs(args...)
+}
+
+func runargs(args ...string) error {
+	cmd := exec.Command(args[0], args[1:]...)
+	b, err := cmd.CombinedOutput()
+	log.Printf("Command: %s; err=%v; full output:\n%s", strings.Join(args, " "), err, b)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func must(err error) {
+	if err != nil {
+		panic(err)
+	}
+}
+
+func chromectx(timeout time.Duration) (context.Context, context.CancelFunc) {
+
+	var ctx context.Context
+
+	if *dockerHeadless {
+
+		// look up endpoint needed for chromedp (port 9222 is exposed by docker container)
+		debugURL := func() string {
+			resp, err := http.Get("http://localhost:9222/json/version")
+			if err != nil {
+				panic(err)
+			}
+
+			var result map[string]interface{}
+
+			if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
+				panic(err)
+			}
+			return result["webSocketDebuggerUrl"].(string)
+		}()
+		allocCtx, _ := chromedp.NewRemoteAllocator(context.Background(), debugURL)
+		ctx, _ = chromedp.NewContext(allocCtx)
+
+	} else {
+
+		// looks for locally installed Chrome
+		ctx, _ = chromedp.NewContext(context.Background())
+	}
+
+	ctx, cancel := context.WithTimeout(ctx, timeout)
+
+	return ctx, cancel
+}
+
+func killServer() {
+
+	// log.Printf("killServer")
+
+	defer os.Remove("server.pid")
+
+	b, err := ioutil.ReadFile("server.pid")
+	if err == nil {
+		pid, _ := strconv.Atoi(strings.TrimSpace(string(b)))
+		if pid > 0 {
+			proc, err := os.FindProcess(pid)
+			if err == nil {
+				proc.Kill()
+			}
+		}
+	}
+
+}
+
+func waitLog(logText string) chromedp.QueryAction {
+	return waitInnerTextTrimEq("#log", logText)
+}
+
+// waitInnerTextTrimEq will wait for the innerText of the specified element to match a specific string after whitespace trimming.
+func waitInnerTextTrimEq(sel, innerText string) chromedp.QueryAction {
+
+	return chromedp.Query(sel, func(s *chromedp.Selector) {
+
+		chromedp.WaitFunc(func(ctx context.Context, cur *cdp.Frame, ids ...cdp.NodeID) ([]*cdp.Node, error) {
+
+			nodes := make([]*cdp.Node, len(ids))
+			cur.RLock()
+			for i, id := range ids {
+				nodes[i] = cur.Nodes[id]
+				if nodes[i] == nil {
+					cur.RUnlock()
+					// not yet ready
+					return nil, nil
+				}
+			}
+			cur.RUnlock()
+
+			var ret string
+			err := chromedp.EvaluateAsDevTools("document.querySelector('"+sel+"').innerText", &ret).Do(ctx)
+			if err != nil {
+				return nodes, err
+			}
+			if strings.TrimSpace(ret) != innerText {
+				// log.Printf("found text: %s", ret)
+				return nodes, errors.New("unexpected value: " + ret)
+			}
+
+			// log.Printf("NodeValue: %#v", nodes[0])
+
+			// return nil, errors.New("not ready yet")
+			return nodes, nil
+		})(s)
+
+	})
+
+}
diff --git a/tests/wasm/chan_pgm.go b/tests/wasm/chan_pgm.go
new file mode 100644
index 0000000000..f613af0aff
--- /dev/null
+++ b/tests/wasm/chan_pgm.go
@@ -0,0 +1,18 @@
+// +build tinygo
+
+package main
+
+func main() {
+
+	ch := make(chan bool, 1)
+	println("1")
+	go func() {
+		println("2")
+		ch <- true
+		println("3")
+	}()
+	println("4")
+	v := <-ch
+	println(v)
+
+}
diff --git a/tests/wasm/chan_test.go b/tests/wasm/chan_test.go
new file mode 100644
index 0000000000..33671c9654
--- /dev/null
+++ b/tests/wasm/chan_test.go
@@ -0,0 +1,32 @@
+package wasm
+
+import (
+	"testing"
+	"time"
+
+	"github.com/chromedp/chromedp"
+)
+
+func TestChan(t *testing.T) {
+
+	err := run("tinygo build -o chan_pgm.wasm -target wasm chan_pgm.go")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	ctx, cancel := chromectx(5 * time.Second)
+	defer cancel()
+
+	err = chromedp.Run(ctx,
+		chromedp.Navigate("http://localhost:8826/run?file=chan_pgm.wasm"),
+		waitLog(`1
+2
+4
+3
+true`),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+}
diff --git a/tests/wasm/event_pgm.go b/tests/wasm/event_pgm.go
new file mode 100644
index 0000000000..efa738e17f
--- /dev/null
+++ b/tests/wasm/event_pgm.go
@@ -0,0 +1,33 @@
+// +build tinygo
+
+package main
+
+import "syscall/js"
+
+func main() {
+
+	ch := make(chan bool, 1)
+
+	println("1")
+
+	js.Global().
+		Get("document").
+		Call("querySelector", "#main").
+		Set("innerHTML", ``)
+
+	js.Global().
+		Get("document").
+		Call("querySelector", "#testbtn").
+		Call("addEventListener", "click",
+			js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+				println("2")
+				ch <- true
+				println("3")
+				return nil
+			}))
+
+	println("4")
+	v := <-ch
+	println(v)
+
+}
diff --git a/tests/wasm/event_test.go b/tests/wasm/event_test.go
new file mode 100644
index 0000000000..9ed8b3e8a8
--- /dev/null
+++ b/tests/wasm/event_test.go
@@ -0,0 +1,35 @@
+package wasm
+
+import (
+	"testing"
+	"time"
+
+	"github.com/chromedp/chromedp"
+)
+
+func TestEvent(t *testing.T) {
+
+	err := run("tinygo build -o event_pgm.wasm -target wasm event_pgm.go")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	ctx, cancel := chromectx(5 * time.Second)
+	defer cancel()
+
+	err = chromedp.Run(ctx,
+		chromedp.Navigate("http://localhost:8826/run?file=event_pgm.wasm"),
+		waitLog(`1
+4`),
+		chromedp.Click("#testbtn"),
+		waitLog(`1
+4
+2
+3
+true`),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+}
diff --git a/tests/wasm/fmtprint_pgm.go b/tests/wasm/fmtprint_pgm.go
new file mode 100644
index 0000000000..e06fc147be
--- /dev/null
+++ b/tests/wasm/fmtprint_pgm.go
@@ -0,0 +1,13 @@
+// +build tinygo
+
+package main
+
+import "fmt"
+
+func main() {
+	fmt.Println("test from fmtprint_pgm 1")
+	fmt.Print("test from fmtprint_pgm 2\n")
+	fmt.Print("test from fmtp")
+	fmt.Print("rint_pgm 3\n")
+	fmt.Printf("test from fmtprint_pgm %d\n", 4)
+}
diff --git a/tests/wasm/fmtprint_test.go b/tests/wasm/fmtprint_test.go
new file mode 100644
index 0000000000..efd9192ce1
--- /dev/null
+++ b/tests/wasm/fmtprint_test.go
@@ -0,0 +1,31 @@
+package wasm
+
+import (
+	"testing"
+	"time"
+
+	"github.com/chromedp/chromedp"
+)
+
+func TestFmtprint(t *testing.T) {
+
+	err := run("tinygo build -o fmtprint_pgm.wasm -target wasm fmtprint_pgm.go")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	ctx, cancel := chromectx(5 * time.Second)
+	defer cancel()
+
+	err = chromedp.Run(ctx,
+		chromedp.Navigate("http://localhost:8826/run?file=fmtprint_pgm.wasm"),
+		waitLog(`test from fmtprint_pgm 1
+test from fmtprint_pgm 2
+test from fmtprint_pgm 3
+test from fmtprint_pgm 4`),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+}

From 9e5decb4563c6ec9f396513700cdd496a4a54559 Mon Sep 17 00:00:00 2001
From: Brad Peabody 
Date: Sat, 16 May 2020 20:16:46 -0700
Subject: [PATCH 02/10] testing setup_remote_docker per
 https://circleci.com/docs/2.0/building-docker-images/

---
 .circleci/config.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 5cb9f1405e..75d287ef01 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -335,6 +335,7 @@ jobs:
     docker:
       - image: circleci/golang:1.14-buster
     steps:
+      - setup_remote_docker
       - test-linux:
           llvm: "10"
   assert-test-linux:

From f7d903cde0273c6a6877a28f8d75a1844388d7cf Mon Sep 17 00:00:00 2001
From: Brad Peabody 
Date: Sat, 16 May 2020 20:28:45 -0700
Subject: [PATCH 03/10] try just installing chrome instead of using docker for
 circleci

---
 .circleci/config.yml | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 75d287ef01..c72d939dd5 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -37,6 +37,14 @@ commands:
             sudo tar -C /usr/local -xf node-v10.15.1-linux-x64.tar.xz
             sudo ln -s /usr/local/node-v10.15.1-linux-x64/bin/node /usr/bin/node
             rm node-v10.15.1-linux-x64.tar.xz
+  install-chrome:
+    steps:
+      - run:
+          name: "Install Chrome"
+          command: |
+            wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
+            sudo apt install ./google-chrome-stable_current_amd64.deb
+            rm ./google-chrome-stable_current_amd64.deb
   llvm-source-linux:
     steps:
       - restore_cache:
@@ -71,6 +79,7 @@ commands:
       - apt-dependencies:
           llvm: "<>"
       - install-node
+      - install-chrome
       - restore_cache:
           keys:
             - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
@@ -88,7 +97,7 @@ commands:
       - run: go test -v -tags=llvm<> ./cgo ./compileopts ./interp ./transform .
       - run: make gen-device -j4
       - run: make smoketest
-      - run: make wasmtest-docker-headless
+      - run: make wasmtest
       - save_cache:
           key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
           paths:
@@ -335,7 +344,6 @@ jobs:
     docker:
       - image: circleci/golang:1.14-buster
     steps:
-      - setup_remote_docker
       - test-linux:
           llvm: "10"
   assert-test-linux:

From 5c3af3519f9376c86fa11bbbfb01e2cee9c31768 Mon Sep 17 00:00:00 2001
From: Brad Peabody 
Date: Sat, 16 May 2020 20:49:36 -0700
Subject: [PATCH 04/10] build go1.12+ and add debug output

---
 tests/wasm/event_test.go | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/tests/wasm/event_test.go b/tests/wasm/event_test.go
index 9ed8b3e8a8..39ca6b3c29 100644
--- a/tests/wasm/event_test.go
+++ b/tests/wasm/event_test.go
@@ -1,3 +1,5 @@
+// +build go1.12
+
 package wasm
 
 import (
@@ -17,17 +19,25 @@ func TestEvent(t *testing.T) {
 	ctx, cancel := chromectx(5 * time.Second)
 	defer cancel()
 
+	var log1, log2 string
 	err = chromedp.Run(ctx,
 		chromedp.Navigate("http://localhost:8826/run?file=event_pgm.wasm"),
+		chromedp.WaitVisible("#log"),
+		chromedp.Sleep(time.Second),
+		chromedp.InnerHTML("#log", &log1),
 		waitLog(`1
 4`),
 		chromedp.Click("#testbtn"),
+		chromedp.Sleep(time.Second),
+		chromedp.InnerHTML("#log", &log2),
 		waitLog(`1
 4
 2
 3
 true`),
 	)
+	t.Logf("log1: %s", log1)
+	t.Logf("log2: %s", log2)
 	if err != nil {
 		t.Fatal(err)
 	}

From c3686c688125250909945352b2dc2b4635cdf3cb Mon Sep 17 00:00:00 2001
From: Brad Peabody 
Date: Sat, 16 May 2020 21:01:32 -0700
Subject: [PATCH 05/10] making event test go1.14+ for now

---
 tests/wasm/event_test.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/wasm/event_test.go b/tests/wasm/event_test.go
index 39ca6b3c29..2f4a3f2d8a 100644
--- a/tests/wasm/event_test.go
+++ b/tests/wasm/event_test.go
@@ -1,4 +1,4 @@
-// +build go1.12
+// +build go1.14
 
 package wasm
 

From 16a7aecb9943d4108479c5bb8dc9f0662e8f58a1 Mon Sep 17 00:00:00 2001
From: Brad Peabody 
Date: Sat, 16 May 2020 21:15:57 -0700
Subject: [PATCH 06/10] debug output

---
 tests/wasm/fmtprint_test.go | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tests/wasm/fmtprint_test.go b/tests/wasm/fmtprint_test.go
index efd9192ce1..525a5f0d1c 100644
--- a/tests/wasm/fmtprint_test.go
+++ b/tests/wasm/fmtprint_test.go
@@ -17,13 +17,17 @@ func TestFmtprint(t *testing.T) {
 	ctx, cancel := chromectx(5 * time.Second)
 	defer cancel()
 
+	var log1 string
 	err = chromedp.Run(ctx,
 		chromedp.Navigate("http://localhost:8826/run?file=fmtprint_pgm.wasm"),
+		chromedp.Sleep(time.Second),
+		chromedp.InnerHTML("#log", &log1),
 		waitLog(`test from fmtprint_pgm 1
 test from fmtprint_pgm 2
 test from fmtprint_pgm 3
 test from fmtprint_pgm 4`),
 	)
+	t.Logf("log1: %s", log1)
 	if err != nil {
 		t.Fatal(err)
 	}

From 569d4e783a3a5b00f97fc42a8a3d19e5e45ac694 Mon Sep 17 00:00:00 2001
From: Brad Peabody 
Date: Sat, 16 May 2020 21:24:35 -0700
Subject: [PATCH 07/10] making this go1.14+ - 1.13 is giving a panic
 syscall/js: call of Value.Get on string, and I do not see where it is coming
 from. but it works on the latest which is ok for now

---
 tests/wasm/fmtprint_test.go | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/wasm/fmtprint_test.go b/tests/wasm/fmtprint_test.go
index 525a5f0d1c..769b592972 100644
--- a/tests/wasm/fmtprint_test.go
+++ b/tests/wasm/fmtprint_test.go
@@ -1,3 +1,5 @@
+// +build go1.14
+
 package wasm
 
 import (

From e42b5634bd46b116a333bf44f7996b08c6f9236b Mon Sep 17 00:00:00 2001
From: Brad Peabody 
Date: Fri, 22 May 2020 14:32:10 -0700
Subject: [PATCH 08/10] cleanup to wasm test suite remove docker and other
 fixes

- all the docker stuff is now gone, the only requirement is a local install of Chrome
- test programs moved into tests/wasm/testdata and "_pgm" removed from the name, unnecessary tinygo build tags removed
- all of the test setup stuff now lives in one file, setup_test.go, and it runs the necessary http server internally
- a temp dir is used for all output files
- any remaining `must` calls replaced with `mustf` and explanations added
- added t.Parallel(), seems to work fine, can be easily removed later if there are memory problems with too many open headless Chrome instances

testing fmtprint against all versions again in the pipeline to see if i can debug
---
 Makefile                                      |   2 -
 tests/wasm/.gitignore                         |   4 -
 tests/wasm/0docker_entrypoint.sh              |  10 -
 tests/wasm/0dockerfile                        |  10 -
 tests/wasm/0setup_server.go                   |  91 -------
 tests/wasm/0setup_test.go                     | 227 ------------------
 tests/wasm/chan_test.go                       |   6 +-
 tests/wasm/event_test.go                      |   6 +-
 tests/wasm/fmtprint_pgm.go                    |  13 -
 tests/wasm/fmtprint_test.go                   |  16 +-
 tests/wasm/setup_test.go                      | 190 +++++++++++++++
 tests/wasm/{chan_pgm.go => testdata/chan.go}  |   2 -
 .../wasm/{event_pgm.go => testdata/event.go}  |   2 -
 tests/wasm/testdata/fmtprint.go               |  11 +
 14 files changed, 217 insertions(+), 373 deletions(-)
 delete mode 100644 tests/wasm/.gitignore
 delete mode 100644 tests/wasm/0docker_entrypoint.sh
 delete mode 100644 tests/wasm/0dockerfile
 delete mode 100644 tests/wasm/0setup_server.go
 delete mode 100644 tests/wasm/0setup_test.go
 delete mode 100644 tests/wasm/fmtprint_pgm.go
 create mode 100644 tests/wasm/setup_test.go
 rename tests/wasm/{chan_pgm.go => testdata/chan.go} (90%)
 rename tests/wasm/{event_pgm.go => testdata/event.go} (96%)
 create mode 100644 tests/wasm/testdata/fmtprint.go

diff --git a/Makefile b/Makefile
index 3cfef37d6d..82307e128e 100644
--- a/Makefile
+++ b/Makefile
@@ -312,8 +312,6 @@ endif
 
 wasmtest:
 	$(GO) test ./tests/wasm
-wasmtest-docker-headless:
-	$(GO) test ./tests/wasm -docker-headless
 
 build/release: tinygo gen-device wasi-libc
 	@mkdir -p build/release/tinygo/bin
diff --git a/tests/wasm/.gitignore b/tests/wasm/.gitignore
deleted file mode 100644
index 44023ec003..0000000000
--- a/tests/wasm/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-*.wasm
-server
-server.pid
-wasm_exec.js
diff --git a/tests/wasm/0docker_entrypoint.sh b/tests/wasm/0docker_entrypoint.sh
deleted file mode 100644
index e935d343b7..0000000000
--- a/tests/wasm/0docker_entrypoint.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-set -e
-
-# launch headless chrome and our test server, exit immediately if one of them terminates
-/headless-shell/headless-shell --no-sandbox --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 &
-
-cd /wasm
-/bin/server &
-
-wait -n
diff --git a/tests/wasm/0dockerfile b/tests/wasm/0dockerfile
deleted file mode 100644
index 3bef1ca9e5..0000000000
--- a/tests/wasm/0dockerfile
+++ /dev/null
@@ -1,10 +0,0 @@
-
-FROM chromedp/headless-shell:latest
-
-RUN apt-get update && apt-get install dumb-init
-
-COPY server /bin/server
-COPY 0docker_entrypoint.sh /0docker_entrypoint.sh
-RUN chmod 755 /bin/server /0docker_entrypoint.sh
-
-ENTRYPOINT ["/usr/bin/dumb-init", "--", "/0docker_entrypoint.sh"]
diff --git a/tests/wasm/0setup_server.go b/tests/wasm/0setup_server.go
deleted file mode 100644
index 6ab2f5fb7c..0000000000
--- a/tests/wasm/0setup_server.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// +build ignore
-
-package main
-
-import (
-	"flag"
-	"fmt"
-	"log"
-	"net/http"
-	"path/filepath"
-)
-
-// This is a standalone static file server which is used to serve pages for browser testing.
-// It is a separate program so we can run it inside the same docker container as headless chrome,
-// in order to avoid port mapping issues.
-
-var addr = flag.String("addr", ":8826", "Host:port to listen on")
-var dir = flag.String("dir", ".", "Directory to serve static files from")
-
-func main() {
-
-	absDir, err := filepath.Abs(*dir)
-	if err != nil {
-		panic(err)
-	}
-	fsh := http.FileServer(http.Dir(absDir))
-	h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-
-		if r.URL.Path == "/run" {
-			fmt.Fprintf(w, `
-
-
-Test
-
-
-
-
-

-
-
-
-
-`, r.FormValue("file"))
-			return
-		}
-
-		fsh.ServeHTTP(w, r)
-	})
-
-	log.Printf("Starting server at %q for dir: %s", *addr, absDir)
-	log.Fatal(http.ListenAndServe(*addr, h))
-}
diff --git a/tests/wasm/0setup_test.go b/tests/wasm/0setup_test.go
deleted file mode 100644
index dd1e8d7360..0000000000
--- a/tests/wasm/0setup_test.go
+++ /dev/null
@@ -1,227 +0,0 @@
-package wasm
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"flag"
-	"fmt"
-	"io/ioutil"
-	"log"
-	"net/http"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"strconv"
-	"strings"
-	"testing"
-	"time"
-
-	"github.com/chromedp/cdproto/cdp"
-	"github.com/chromedp/chromedp"
-)
-
-var dockerHeadless = flag.Bool("docker-headless", false, "Launch headless Chrome via docker instead looking for it on local filesystem")
-var noCleanup = flag.Bool("no-cleanup", false, "Skip cleanup operations (useful for manual viewing and debugging)")
-
-func TestMain(m *testing.M) {
-	flag.Parse()
-
-	setup()
-	ret := m.Run()
-	cleanup()
-	os.Exit(ret)
-}
-
-func setup() {
-
-	// copy wasm_exec.js from targets to this dir
-	b, err := ioutil.ReadFile("../../targets/wasm_exec.js")
-	must(err)
-	must(ioutil.WriteFile("wasm_exec.js", b, 0644))
-
-	if *dockerHeadless {
-
-		// build server for arch that works in docker
-		args := []string{"go", "build", "-o", "server", "0setup_server.go"}
-		cmd := exec.Command(args[0], args[1:]...)
-		cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux")
-		b, err := cmd.CombinedOutput()
-		log.Printf("Command: %s; err=%v; full output:\n%s", strings.Join(args, " "), err, b)
-		must(err)
-
-		absDir, err := filepath.Abs(".")
-		must(err)
-		// launch headless chrome and server via docker if requested
-		must(run("docker build -t tinygo_wasm_test -f 0dockerfile ."))
-		run("docker stop tinygo_wasm_test") // ignore error
-
-		must(runargs("docker", "run",
-			"-d", "--rm",
-			"--name=tinygo_wasm_test",
-			"--mount", "type=bind,target=/wasm,source="+absDir,
-			"-p", "9222:9222", "-p", "8826:8826",
-			"tinygo_wasm_test:latest"))
-
-	} else {
-
-		killServer()
-
-		// build server for local arch
-		must(run("go build -o server 0setup_server.go"))
-
-		// or otherwise just launch server
-		serverCmd = exec.Command("./server")
-		serverCmd.Stdout = os.Stdout
-		serverCmd.Stderr = os.Stderr
-		must(serverCmd.Start())
-
-		must(ioutil.WriteFile("server.pid", []byte(fmt.Sprintf("%d\n", serverCmd.Process.Pid)), 0644))
-
-	}
-
-}
-
-var serverCmd *exec.Cmd
-
-func cleanup() {
-	// log.Println("performing cleanup")
-
-	if *noCleanup {
-		return
-	}
-	if *dockerHeadless {
-		run("docker stop tinygo_wasm_test") // ignore error
-	} else {
-		killServer()
-	}
-	os.Remove("wasm_exec.js")
-	os.Remove("server")
-	f, err := os.Open(".")
-	must(err)
-	defer f.Close()
-	names, err := f.Readdirnames(-1)
-	must(err)
-	for _, name := range names {
-		if filepath.Ext(name) == ".wasm" {
-			os.Remove(name)
-		}
-	}
-}
-
-func run(cmdline string) error {
-	args := strings.Fields(cmdline)
-	return runargs(args...)
-}
-
-func runargs(args ...string) error {
-	cmd := exec.Command(args[0], args[1:]...)
-	b, err := cmd.CombinedOutput()
-	log.Printf("Command: %s; err=%v; full output:\n%s", strings.Join(args, " "), err, b)
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-func must(err error) {
-	if err != nil {
-		panic(err)
-	}
-}
-
-func chromectx(timeout time.Duration) (context.Context, context.CancelFunc) {
-
-	var ctx context.Context
-
-	if *dockerHeadless {
-
-		// look up endpoint needed for chromedp (port 9222 is exposed by docker container)
-		debugURL := func() string {
-			resp, err := http.Get("http://localhost:9222/json/version")
-			if err != nil {
-				panic(err)
-			}
-
-			var result map[string]interface{}
-
-			if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
-				panic(err)
-			}
-			return result["webSocketDebuggerUrl"].(string)
-		}()
-		allocCtx, _ := chromedp.NewRemoteAllocator(context.Background(), debugURL)
-		ctx, _ = chromedp.NewContext(allocCtx)
-
-	} else {
-
-		// looks for locally installed Chrome
-		ctx, _ = chromedp.NewContext(context.Background())
-	}
-
-	ctx, cancel := context.WithTimeout(ctx, timeout)
-
-	return ctx, cancel
-}
-
-func killServer() {
-
-	// log.Printf("killServer")
-
-	defer os.Remove("server.pid")
-
-	b, err := ioutil.ReadFile("server.pid")
-	if err == nil {
-		pid, _ := strconv.Atoi(strings.TrimSpace(string(b)))
-		if pid > 0 {
-			proc, err := os.FindProcess(pid)
-			if err == nil {
-				proc.Kill()
-			}
-		}
-	}
-
-}
-
-func waitLog(logText string) chromedp.QueryAction {
-	return waitInnerTextTrimEq("#log", logText)
-}
-
-// waitInnerTextTrimEq will wait for the innerText of the specified element to match a specific string after whitespace trimming.
-func waitInnerTextTrimEq(sel, innerText string) chromedp.QueryAction {
-
-	return chromedp.Query(sel, func(s *chromedp.Selector) {
-
-		chromedp.WaitFunc(func(ctx context.Context, cur *cdp.Frame, ids ...cdp.NodeID) ([]*cdp.Node, error) {
-
-			nodes := make([]*cdp.Node, len(ids))
-			cur.RLock()
-			for i, id := range ids {
-				nodes[i] = cur.Nodes[id]
-				if nodes[i] == nil {
-					cur.RUnlock()
-					// not yet ready
-					return nil, nil
-				}
-			}
-			cur.RUnlock()
-
-			var ret string
-			err := chromedp.EvaluateAsDevTools("document.querySelector('"+sel+"').innerText", &ret).Do(ctx)
-			if err != nil {
-				return nodes, err
-			}
-			if strings.TrimSpace(ret) != innerText {
-				// log.Printf("found text: %s", ret)
-				return nodes, errors.New("unexpected value: " + ret)
-			}
-
-			// log.Printf("NodeValue: %#v", nodes[0])
-
-			// return nil, errors.New("not ready yet")
-			return nodes, nil
-		})(s)
-
-	})
-
-}
diff --git a/tests/wasm/chan_test.go b/tests/wasm/chan_test.go
index 33671c9654..1cd08e664d 100644
--- a/tests/wasm/chan_test.go
+++ b/tests/wasm/chan_test.go
@@ -9,7 +9,9 @@ import (
 
 func TestChan(t *testing.T) {
 
-	err := run("tinygo build -o chan_pgm.wasm -target wasm chan_pgm.go")
+	t.Parallel()
+
+	err := run("tinygo build -o " + wasmTmpDir + "/chan.wasm -target wasm testdata/chan.go")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -18,7 +20,7 @@ func TestChan(t *testing.T) {
 	defer cancel()
 
 	err = chromedp.Run(ctx,
-		chromedp.Navigate("http://localhost:8826/run?file=chan_pgm.wasm"),
+		chromedp.Navigate("http://localhost:8826/run?file=chan.wasm"),
 		waitLog(`1
 2
 4
diff --git a/tests/wasm/event_test.go b/tests/wasm/event_test.go
index 2f4a3f2d8a..d2b8340ce2 100644
--- a/tests/wasm/event_test.go
+++ b/tests/wasm/event_test.go
@@ -11,7 +11,9 @@ import (
 
 func TestEvent(t *testing.T) {
 
-	err := run("tinygo build -o event_pgm.wasm -target wasm event_pgm.go")
+	t.Parallel()
+
+	err := run("tinygo build -o " + wasmTmpDir + "/event.wasm -target wasm testdata/event.go")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -21,7 +23,7 @@ func TestEvent(t *testing.T) {
 
 	var log1, log2 string
 	err = chromedp.Run(ctx,
-		chromedp.Navigate("http://localhost:8826/run?file=event_pgm.wasm"),
+		chromedp.Navigate("http://localhost:8826/run?file=event.wasm"),
 		chromedp.WaitVisible("#log"),
 		chromedp.Sleep(time.Second),
 		chromedp.InnerHTML("#log", &log1),
diff --git a/tests/wasm/fmtprint_pgm.go b/tests/wasm/fmtprint_pgm.go
deleted file mode 100644
index e06fc147be..0000000000
--- a/tests/wasm/fmtprint_pgm.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// +build tinygo
-
-package main
-
-import "fmt"
-
-func main() {
-	fmt.Println("test from fmtprint_pgm 1")
-	fmt.Print("test from fmtprint_pgm 2\n")
-	fmt.Print("test from fmtp")
-	fmt.Print("rint_pgm 3\n")
-	fmt.Printf("test from fmtprint_pgm %d\n", 4)
-}
diff --git a/tests/wasm/fmtprint_test.go b/tests/wasm/fmtprint_test.go
index 769b592972..60a8ba58ec 100644
--- a/tests/wasm/fmtprint_test.go
+++ b/tests/wasm/fmtprint_test.go
@@ -1,5 +1,3 @@
-// +build go1.14
-
 package wasm
 
 import (
@@ -11,7 +9,9 @@ import (
 
 func TestFmtprint(t *testing.T) {
 
-	err := run("tinygo build -o fmtprint_pgm.wasm -target wasm fmtprint_pgm.go")
+	t.Parallel()
+
+	err := run("tinygo build -o " + wasmTmpDir + "/fmtprint.wasm -target wasm testdata/fmtprint.go")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -21,13 +21,13 @@ func TestFmtprint(t *testing.T) {
 
 	var log1 string
 	err = chromedp.Run(ctx,
-		chromedp.Navigate("http://localhost:8826/run?file=fmtprint_pgm.wasm"),
+		chromedp.Navigate("http://localhost:8826/run?file=fmtprint.wasm"),
 		chromedp.Sleep(time.Second),
 		chromedp.InnerHTML("#log", &log1),
-		waitLog(`test from fmtprint_pgm 1
-test from fmtprint_pgm 2
-test from fmtprint_pgm 3
-test from fmtprint_pgm 4`),
+		waitLog(`test from fmtprint 1
+test from fmtprint 2
+test from fmtprint 3
+test from fmtprint 4`),
 	)
 	t.Logf("log1: %s", log1)
 	if err != nil {
diff --git a/tests/wasm/setup_test.go b/tests/wasm/setup_test.go
new file mode 100644
index 0000000000..484373849d
--- /dev/null
+++ b/tests/wasm/setup_test.go
@@ -0,0 +1,190 @@
+package wasm
+
+import (
+	"context"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"os/exec"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/chromedp/cdproto/cdp"
+	"github.com/chromedp/chromedp"
+)
+
+var addr = flag.String("addr", ":8826", "Host:port to listen on for wasm test server")
+
+var wasmTmpDir string // set in TestMain to a temp directory for build output
+
+func TestMain(m *testing.M) {
+	flag.Parse()
+
+	var err error
+	wasmTmpDir, err = ioutil.TempDir("", "wasm_test")
+	if err != nil {
+		log.Fatalf("unable to create temp dir: %v", err)
+	}
+
+	startServer(wasmTmpDir)
+
+	var ret int
+	func() {
+		defer os.RemoveAll(wasmTmpDir) // cleanup even on panic
+		ret = m.Run()
+	}()
+
+	os.Exit(ret)
+}
+
+func run(cmdline string) error {
+	args := strings.Fields(cmdline)
+	return runargs(args...)
+}
+
+func runargs(args ...string) error {
+	cmd := exec.Command(args[0], args[1:]...)
+	b, err := cmd.CombinedOutput()
+	log.Printf("Command: %s; err=%v; full output:\n%s", strings.Join(args, " "), err, b)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func chromectx(timeout time.Duration) (context.Context, context.CancelFunc) {
+
+	var ctx context.Context
+
+	// looks for locally installed Chrome
+	ctx, _ = chromedp.NewContext(context.Background())
+
+	ctx, cancel := context.WithTimeout(ctx, timeout)
+
+	return ctx, cancel
+}
+
+func startServer(tmpDir string) {
+
+	fsh := http.FileServer(http.Dir(tmpDir))
+	h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
+		if r.URL.Path == "/wasm_exec.js" {
+			http.ServeFile(w, r, "../../targets/wasm_exec.js")
+			return
+		}
+
+		if r.URL.Path == "/run" {
+			fmt.Fprintf(w, `
+
+
+Test
+
+
+
+
+

+
+
+
+
+`, r.FormValue("file"))
+			return
+		}
+
+		fsh.ServeHTTP(w, r)
+	})
+
+	log.Printf("Starting server at %q for dir: %s", *addr, tmpDir)
+	go func() {
+		log.Fatal(http.ListenAndServe(*addr, h))
+	}()
+
+}
+
+func waitLog(logText string) chromedp.QueryAction {
+	return waitInnerTextTrimEq("#log", logText)
+}
+
+// waitInnerTextTrimEq will wait for the innerText of the specified element to match a specific string after whitespace trimming.
+func waitInnerTextTrimEq(sel, innerText string) chromedp.QueryAction {
+
+	return chromedp.Query(sel, func(s *chromedp.Selector) {
+
+		chromedp.WaitFunc(func(ctx context.Context, cur *cdp.Frame, ids ...cdp.NodeID) ([]*cdp.Node, error) {
+
+			nodes := make([]*cdp.Node, len(ids))
+			cur.RLock()
+			for i, id := range ids {
+				nodes[i] = cur.Nodes[id]
+				if nodes[i] == nil {
+					cur.RUnlock()
+					// not yet ready
+					return nil, nil
+				}
+			}
+			cur.RUnlock()
+
+			var ret string
+			err := chromedp.EvaluateAsDevTools("document.querySelector('"+sel+"').innerText", &ret).Do(ctx)
+			if err != nil {
+				return nodes, err
+			}
+			if strings.TrimSpace(ret) != innerText {
+				// log.Printf("found text: %s", ret)
+				return nodes, errors.New("unexpected value: " + ret)
+			}
+
+			// log.Printf("NodeValue: %#v", nodes[0])
+
+			// return nil, errors.New("not ready yet")
+			return nodes, nil
+		})(s)
+
+	})
+
+}
diff --git a/tests/wasm/chan_pgm.go b/tests/wasm/testdata/chan.go
similarity index 90%
rename from tests/wasm/chan_pgm.go
rename to tests/wasm/testdata/chan.go
index f613af0aff..8f10d1f8d8 100644
--- a/tests/wasm/chan_pgm.go
+++ b/tests/wasm/testdata/chan.go
@@ -1,5 +1,3 @@
-// +build tinygo
-
 package main
 
 func main() {
diff --git a/tests/wasm/event_pgm.go b/tests/wasm/testdata/event.go
similarity index 96%
rename from tests/wasm/event_pgm.go
rename to tests/wasm/testdata/event.go
index efa738e17f..4153774ff1 100644
--- a/tests/wasm/event_pgm.go
+++ b/tests/wasm/testdata/event.go
@@ -1,5 +1,3 @@
-// +build tinygo
-
 package main
 
 import "syscall/js"
diff --git a/tests/wasm/testdata/fmtprint.go b/tests/wasm/testdata/fmtprint.go
new file mode 100644
index 0000000000..1bad3361c7
--- /dev/null
+++ b/tests/wasm/testdata/fmtprint.go
@@ -0,0 +1,11 @@
+package main
+
+import "fmt"
+
+func main() {
+	fmt.Println("test from fmtprint 1")
+	fmt.Print("test from fmtprint 2\n")
+	fmt.Print("test from fmtp")
+	fmt.Print("rint 3\n")
+	fmt.Printf("test from fmtprint %d\n", 4)
+}

From 8e79bb602d313e8d8628dd74c274e213b3bc33fe Mon Sep 17 00:00:00 2001
From: Brad Peabody 
Date: Fri, 22 May 2020 14:38:03 -0700
Subject: [PATCH 09/10] rm unnecessary rm

---
 .circleci/config.yml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index c72d939dd5..060d665028 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -44,7 +44,6 @@ commands:
           command: |
             wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
             sudo apt install ./google-chrome-stable_current_amd64.deb
-            rm ./google-chrome-stable_current_amd64.deb
   llvm-source-linux:
     steps:
       - restore_cache:

From 0540e6737f30404c9075020ab5ae69a6be7a1213 Mon Sep 17 00:00:00 2001
From: Brad Peabody 
Date: Fri, 22 May 2020 15:16:30 -0700
Subject: [PATCH 10/10] cleanup and added fmt test to show go 1.13 issue

---
 tests/wasm/fmt_test.go      | 43 +++++++++++++++++++++++++++++++++++++
 tests/wasm/fmtprint_test.go |  2 ++
 tests/wasm/setup_test.go    | 23 ++++++++++----------
 tests/wasm/testdata/fmt.go  |  8 +++++++
 4 files changed, 64 insertions(+), 12 deletions(-)
 create mode 100644 tests/wasm/fmt_test.go
 create mode 100644 tests/wasm/testdata/fmt.go

diff --git a/tests/wasm/fmt_test.go b/tests/wasm/fmt_test.go
new file mode 100644
index 0000000000..8b4fe8c751
--- /dev/null
+++ b/tests/wasm/fmt_test.go
@@ -0,0 +1,43 @@
+// +build go1.14
+
+package wasm
+
+// NOTE: this should work in go1.13 but panics with:
+// panic: syscall/js: call of Value.Get on string
+// which is coming from here: https://github.com/golang/go/blob/release-branch.go1.13/src/syscall/js/js.go#L252
+// But I'm not sure how import "fmt" results in this.
+// To reproduce, install Go 1.13.x and change the build tag above
+// to go1.13 and run this test.
+
+import (
+	"testing"
+	"time"
+
+	"github.com/chromedp/chromedp"
+)
+
+func TestFmt(t *testing.T) {
+
+	t.Parallel()
+
+	err := run("tinygo build -o " + wasmTmpDir + "/fmt.wasm -target wasm testdata/fmt.go")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	ctx, cancel := chromectx(5 * time.Second)
+	defer cancel()
+
+	var log1 string
+	err = chromedp.Run(ctx,
+		chromedp.Navigate("http://localhost:8826/run?file=fmt.wasm"),
+		chromedp.Sleep(time.Second),
+		chromedp.InnerHTML("#log", &log1),
+		waitLog(`did not panic`),
+	)
+	t.Logf("log1: %s", log1)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+}
diff --git a/tests/wasm/fmtprint_test.go b/tests/wasm/fmtprint_test.go
index 60a8ba58ec..ebd1ffa997 100644
--- a/tests/wasm/fmtprint_test.go
+++ b/tests/wasm/fmtprint_test.go
@@ -1,3 +1,5 @@
+// +build go1.14
+
 package wasm
 
 import (
diff --git a/tests/wasm/setup_test.go b/tests/wasm/setup_test.go
index 484373849d..7d5ffa675f 100644
--- a/tests/wasm/setup_test.go
+++ b/tests/wasm/setup_test.go
@@ -25,21 +25,20 @@ var wasmTmpDir string // set in TestMain to a temp directory for build output
 func TestMain(m *testing.M) {
 	flag.Parse()
 
-	var err error
-	wasmTmpDir, err = ioutil.TempDir("", "wasm_test")
-	if err != nil {
-		log.Fatalf("unable to create temp dir: %v", err)
-	}
+	os.Exit(func() int {
 
-	startServer(wasmTmpDir)
+		var err error
+		wasmTmpDir, err = ioutil.TempDir("", "wasm_test")
+		if err != nil {
+			log.Fatalf("unable to create temp dir: %v", err)
+		}
+		defer os.RemoveAll(wasmTmpDir) // cleanup even on panic and before os.Exit
 
-	var ret int
-	func() {
-		defer os.RemoveAll(wasmTmpDir) // cleanup even on panic
-		ret = m.Run()
-	}()
+		startServer(wasmTmpDir)
+
+		return m.Run()
+	}())
 
-	os.Exit(ret)
 }
 
 func run(cmdline string) error {
diff --git a/tests/wasm/testdata/fmt.go b/tests/wasm/testdata/fmt.go
new file mode 100644
index 0000000000..b51c564cd5
--- /dev/null
+++ b/tests/wasm/testdata/fmt.go
@@ -0,0 +1,8 @@
+package main
+
+import "fmt"
+
+func main() {
+	var _ fmt.Stringer
+	println("did not panic")
+}