diff --git a/cmd/go.mod b/cmd/go.mod index 401f45e9..737eabe0 100644 --- a/cmd/go.mod +++ b/cmd/go.mod @@ -3,6 +3,8 @@ module gioui.org/cmd go 1.13 require ( + gioui.org/ui v0.0.0-20190922141907-2d8072fbbbc6 + github.com/chromedp/chromedp v0.4.0 golang.org/x/image v0.0.0-20190802002840-cff245a6509b golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 ) diff --git a/cmd/go.sum b/cmd/go.sum index fed120b4..31bb2db4 100644 --- a/cmd/go.sum +++ b/cmd/go.sum @@ -1,5 +1,24 @@ +gioui.org/ui v0.0.0-20190922141907-2d8072fbbbc6 h1:zK+bNIQHKtFYsK8WjI3A+mChXOoHpQbmSzTXFvZg+2o= +gioui.org/ui v0.0.0-20190922141907-2d8072fbbbc6/go.mod h1:PssKPKlqVIeyaed+0w492Xc2NgX5M3n6oZKOAj5rxoE= +github.com/chromedp/cdproto v0.0.0-20190812224334-39ef923dcb8d h1:00kLGv5nKzpFchNhGDXDRbKtYx/WoT983Ka2t8/pzRE= +github.com/chromedp/cdproto v0.0.0-20190812224334-39ef923dcb8d/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0= +github.com/chromedp/chromedp v0.4.0 h1:0AJC5ejETuh/6n7Tcsw4u4G0eKZkI9aVRwckWaImLUE= +github.com/chromedp/chromedp v0.4.0/go.mod h1:DC3QUn4mJ24dwjcaGQLoZrhm4X/uPHZ6spDbS2uFhm4= +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/knq/sysutil v0.0.0-20181215143952-f05b59f0f307 h1:vl4eIlySbjertFaNwiMjXsGrFVK25aOWLq7n+3gh2ls= +github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/cmd/gogio/js_test.go b/cmd/gogio/js_test.go new file mode 100644 index 00000000..9241891b --- /dev/null +++ b/cmd/gogio/js_test.go @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package main_test + +import ( + "bytes" + "context" + "image/png" + "io/ioutil" + "net/http" + "net/http/httptest" + "os" + "os/exec" + "testing" + + "github.com/chromedp/chromedp" + + _ "gioui.org/ui" // the build tool adds it to go.mod, so keep it there +) + +func TestJSOnChrome(t *testing.T) { + // First, build the app. + dir, err := ioutil.TempDir("", "gio-endtoend-js") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + // TODO(mvdan): This is inefficient, as we link the gogio tool every time. + // Consider options in the future. On the plus side, this is simple. + cmd := exec.Command("go", "run", ".", "-target=js", "-o="+dir, "testdata/red.go") + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("could not build app: %s:\n%s", err, out) + } + + // Second, start Chrome. + opts := append(chromedp.DefaultExecAllocatorOptions[:], + // Uncomment to get the browser's GUI. + // chromedp.Flag("headless", false), + // We need use-gl=egl instead of the default of use-gl=desktop; + // "desktop" doesn't seem to work when we're in headless mode. + // TODO(mvdan): Does egl require a GPU? If so, consider + // use-gl=swiftshader, which will use CPU-based rendering. That + // might be necessary for some CI or headless environments. + chromedp.Flag("use-gl", "egl"), + ) + + actx, cancel := chromedp.NewExecAllocator(context.Background(), opts...) + defer cancel() + + ctx, cancel := chromedp.NewContext(actx) + defer cancel() + + if err := chromedp.Run(ctx); err != nil { + // TODO(mvdan): Skip the test if chrome/chromium/headless-shell + // aren't installed. + t.Fatal(err) + } + + // Third, serve the app folder, set the browser tab dimensions, and + // navigate to the folder. + ts := httptest.NewServer(http.FileServer(http.Dir(dir))) + defer ts.Close() + + if err := chromedp.Run(ctx, + // A small window with 2x HiDPI. + chromedp.EmulateViewport(300, 300, chromedp.EmulateScale(2.0)), + chromedp.Navigate(ts.URL), + ); err != nil { + t.Fatal(err) + } + + // Finally, run the test. + + // 1: Once the canvas is ready, grab a screenshot to check that the + // entirety of the viewport is red, as per the background color. + var buf []byte + if err := chromedp.Run(ctx, + chromedp.WaitReady("canvas", chromedp.ByQuery), + chromedp.CaptureScreenshot(&buf), + ); err != nil { + t.Fatal(err) + } + img, err := png.Decode(bytes.NewReader(buf)) + if err != nil { + t.Fatal(err) + } + size := img.Bounds().Size() + wantSize := 600 // 300px at 2.0 scaling factor + if size.X != wantSize || size.Y != wantSize { + t.Fatalf("expected dimensions to be %d*%d, got %d*%d", + wantSize, wantSize, size.X, size.Y) + } + wantColor := func(x, y int, r, g, b, a uint32) { + color := img.At(x, y) + r_, g_, b_, a_ := color.RGBA() + if r_ != r || g_ != g || b_ != b || a_ != a { + t.Errorf("got 0x%04x%04x%04x%04x at (%d,%d), want 0x%04x%04x%04x%04x", + r_, g_, b_, a_, x, y, r, g, b, a) + } + } + wantColor(5, 5, 0xffff, 0x0, 0x0, 0xffff) + wantColor(595, 595, 0xffff, 0x0, 0x0, 0xffff) +} diff --git a/cmd/gogio/testdata/red.go b/cmd/gogio/testdata/red.go new file mode 100644 index 00000000..0de41e65 --- /dev/null +++ b/cmd/gogio/testdata/red.go @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// A dead simple app that just paints the background red. +package main + +import ( + "image/color" + "log" + + "gioui.org/ui" + "gioui.org/ui/app" + "gioui.org/ui/f32" + "gioui.org/ui/paint" +) + +func main() { + go func() { + w := app.NewWindow() + if err := loop(w); err != nil { + log.Fatal(err) + } + }() + app.Main() +} + +func loop(w *app.Window) error { + background := color.RGBA{255, 0, 0, 255} + ops := new(ui.Ops) + for { + e := <-w.Events() + switch e := e.(type) { + case app.DestroyEvent: + return e.Err + case app.UpdateEvent: + ops.Reset() + paint.ColorOp{Color: background}.Add(ops) + paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{ + X: float32(e.Size.X), + Y: float32(e.Size.Y), + }}}.Add(ops) + w.Update(ops) + } + } +}