mirror of https://github.com/tinygo-org/tinygo.git
wasmstm32webassemblymicrocontrollerarmavrspiwasiadafruitarduinocircuitplayground-expressgpioi2cllvmmicrobitnrf51nrf52nrf52840samd21tinygo
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
4.7 KiB
193 lines
4.7 KiB
package wasm
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/chromedp/cdproto/cdp"
|
|
"github.com/chromedp/cdproto/runtime"
|
|
"github.com/chromedp/chromedp"
|
|
)
|
|
|
|
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(t *testing.T) (string, *httptest.Server, func()) {
|
|
// In Go 1.15, all this can be replaced by t.TempDir()
|
|
tmpDir, err := ioutil.TempDir("", "wasm_test")
|
|
if err != nil {
|
|
t.Fatalf("unable to create temp dir: %v", err)
|
|
}
|
|
|
|
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)
|
|
})
|
|
|
|
server := httptest.NewServer(h)
|
|
t.Logf("Started server at %q for dir: %s", server.URL, tmpDir)
|
|
|
|
// In Go 1.14+, this can be replaced by t.Cleanup()
|
|
cleanup := func() {
|
|
err := os.RemoveAll(tmpDir)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
server.Close()
|
|
}
|
|
|
|
return tmpDir, server, cleanup
|
|
}
|
|
|
|
// waitLog blocks until the log output equals the text provided (ignoring whitespace before and after)
|
|
func waitLog(logText string) chromedp.QueryAction {
|
|
return waitInnerTextTrimEq("#log", strings.TrimSpace(logText))
|
|
}
|
|
|
|
// waitLogRe blocks until the log output matches this regular expression
|
|
func waitLogRe(restr string) chromedp.QueryAction {
|
|
return waitInnerTextMatch("#log", regexp.MustCompile(restr))
|
|
}
|
|
|
|
// waitInnerTextTrimEq will wait for the innerText of the specified element to match a specific text pattern (ignoring whitespace before and after)
|
|
func waitInnerTextTrimEq(sel string, innerText string) chromedp.QueryAction {
|
|
return waitInnerTextMatch(sel, regexp.MustCompile(`^\s*`+regexp.QuoteMeta(innerText)+`\s*$`))
|
|
}
|
|
|
|
// waitInnerTextMatch will wait for the innerText of the specified element to match a specific regexp pattern
|
|
func waitInnerTextMatch(sel string, re *regexp.Regexp) chromedp.QueryAction {
|
|
|
|
return chromedp.Query(sel, func(s *chromedp.Selector) {
|
|
|
|
chromedp.WaitFunc(func(ctx context.Context, cur *cdp.Frame, execCtx runtime.ExecutionContextID, 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 !re.MatchString(ret) {
|
|
// 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)
|
|
|
|
})
|
|
|
|
}
|
|
|