diff options
-rw-r--r-- | .circleci/config.yml | 9 | ||||
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 15 | ||||
-rw-r--r-- | tests/wasm/chan_test.go | 34 | ||||
-rw-r--r-- | tests/wasm/event_test.go | 47 | ||||
-rw-r--r-- | tests/wasm/fmt_test.go | 43 | ||||
-rw-r--r-- | tests/wasm/fmtprint_test.go | 39 | ||||
-rw-r--r-- | tests/wasm/setup_test.go | 189 | ||||
-rw-r--r-- | tests/wasm/testdata/chan.go | 16 | ||||
-rw-r--r-- | tests/wasm/testdata/event.go | 31 | ||||
-rw-r--r-- | tests/wasm/testdata/fmt.go | 8 | ||||
-rw-r--r-- | tests/wasm/testdata/fmtprint.go | 11 |
13 files changed, 447 insertions, 0 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index e311b6a1c..060d66502 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -37,6 +37,13 @@ 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 llvm-source-linux: steps: - restore_cache: @@ -71,6 +78,7 @@ commands: - apt-dependencies: llvm: "<<parameters.llvm>>" - install-node + - install-chrome - restore_cache: keys: - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} @@ -88,6 +96,7 @@ commands: - run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform . - run: make gen-device -j4 - run: make smoketest + - run: make wasmtest - save_cache: key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }} paths: @@ -310,6 +310,9 @@ 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 + build/release: tinygo gen-device wasi-libc @mkdir -p build/release/tinygo/bin @mkdir -p build/release/tinygo/lib/clang/include @@ -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 @@ -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/chan_test.go b/tests/wasm/chan_test.go new file mode 100644 index 000000000..1cd08e664 --- /dev/null +++ b/tests/wasm/chan_test.go @@ -0,0 +1,34 @@ +package wasm + +import ( + "testing" + "time" + + "github.com/chromedp/chromedp" +) + +func TestChan(t *testing.T) { + + t.Parallel() + + err := run("tinygo build -o " + wasmTmpDir + "/chan.wasm -target wasm testdata/chan.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.wasm"), + waitLog(`1 +2 +4 +3 +true`), + ) + if err != nil { + t.Fatal(err) + } + +} diff --git a/tests/wasm/event_test.go b/tests/wasm/event_test.go new file mode 100644 index 000000000..d2b8340ce --- /dev/null +++ b/tests/wasm/event_test.go @@ -0,0 +1,47 @@ +// +build go1.14 + +package wasm + +import ( + "testing" + "time" + + "github.com/chromedp/chromedp" +) + +func TestEvent(t *testing.T) { + + t.Parallel() + + err := run("tinygo build -o " + wasmTmpDir + "/event.wasm -target wasm testdata/event.go") + if err != nil { + t.Fatal(err) + } + + ctx, cancel := chromectx(5 * time.Second) + defer cancel() + + var log1, log2 string + err = chromedp.Run(ctx, + chromedp.Navigate("http://localhost:8826/run?file=event.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) + } + +} diff --git a/tests/wasm/fmt_test.go b/tests/wasm/fmt_test.go new file mode 100644 index 000000000..8b4fe8c75 --- /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 new file mode 100644 index 000000000..ebd1ffa99 --- /dev/null +++ b/tests/wasm/fmtprint_test.go @@ -0,0 +1,39 @@ +// +build go1.14 + +package wasm + +import ( + "testing" + "time" + + "github.com/chromedp/chromedp" +) + +func TestFmtprint(t *testing.T) { + + t.Parallel() + + err := run("tinygo build -o " + wasmTmpDir + "/fmtprint.wasm -target wasm testdata/fmtprint.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=fmtprint.wasm"), + chromedp.Sleep(time.Second), + chromedp.InnerHTML("#log", &log1), + 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 { + t.Fatal(err) + } + +} diff --git a/tests/wasm/setup_test.go b/tests/wasm/setup_test.go new file mode 100644 index 000000000..7d5ffa675 --- /dev/null +++ b/tests/wasm/setup_test.go @@ -0,0 +1,189 @@ +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() + + os.Exit(func() int { + + 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 + + startServer(wasmTmpDir) + + return m.Run() + }()) + +} + +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, `<!doctype html> +<html> +<head> +<title>Test</title> +<meta charset="utf-8"/> +</head> +<body> +<div id="main"></div> +<pre id="log"></pre> +<script> +window.wasmLogOutput = []; +(function() { + var logdiv = document.getElementById('log'); + var cl = console.log; + console.log = function() { + var a = []; + for (var i = 0; i < arguments.length; i++) { + a.push(arguments[i]); + } + var line = a.join(' ') + "\n"; + window.wasmLogOutput.push(line); + var ret = cl.apply(console, arguments) + var el = document.createElement('span'); + el.innerText = line; + logdiv.appendChild(el); + return ret + } +})() +</script> +<script src="/wasm_exec.js"></script> +<script> +var wasmSupported = (typeof WebAssembly === "object"); +if (wasmSupported) { + var mainWasmReq = fetch("/%s").then(function(res) { + if (res.ok) { + const go = new Go(); + WebAssembly.instantiateStreaming(res, go.importObject).then((result) => { + go.run(result.instance); + }); + } else { + res.text().then(function(txt) { + var el = document.getElementById("main"); + el.style = 'font-family: monospace; background: black; color: red; padding: 10px'; + el.innerText = txt; + }) + } + }) +} else { + document.getElementById("main").innerHTML = 'This application requires WebAssembly support. Please upgrade your browser.'; +} +</script> +</body> +</html>`, 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/testdata/chan.go b/tests/wasm/testdata/chan.go new file mode 100644 index 000000000..8f10d1f8d --- /dev/null +++ b/tests/wasm/testdata/chan.go @@ -0,0 +1,16 @@ +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/testdata/event.go b/tests/wasm/testdata/event.go new file mode 100644 index 000000000..4153774ff --- /dev/null +++ b/tests/wasm/testdata/event.go @@ -0,0 +1,31 @@ +package main + +import "syscall/js" + +func main() { + + ch := make(chan bool, 1) + + println("1") + + js.Global(). + Get("document"). + Call("querySelector", "#main"). + Set("innerHTML", `<button id="testbtn">Test</button>`) + + 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/testdata/fmt.go b/tests/wasm/testdata/fmt.go new file mode 100644 index 000000000..b51c564cd --- /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") +} diff --git a/tests/wasm/testdata/fmtprint.go b/tests/wasm/testdata/fmtprint.go new file mode 100644 index 000000000..1bad3361c --- /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) +} |