2018-03-04 13:40:08 +01:00
|
|
|
// Copyright 2018 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
all: gofmt more (but vendor, testdata, and top-level test directories)
CL 294430 made packages in std and cmd modules use Go 1.17 gofmt format,
adding //go:build lines. This change applies the same formatting to some
more packages that 'go fmt' missed (e.g., syscall/js, runtime/msan), and
everything else that is easy and safe to modify in bulk.
Consider the top-level test directory, testdata, and vendor directories
out of scope, since there are many files that don't follow strict gofmt
formatting, often for intentional and legitimate reasons (testing gofmt
itself, invalid Go programs that shouldn't crash the compiler, etc.).
That makes it easy and safe to gofmt -w the .go files that are found
with gofmt -l with aforementioned directories filtered out:
$ gofmt -l . 2>/dev/null | \
grep -v '^test/' | \
grep -v '/testdata/' | \
grep -v '/vendor/' | wc -l
51
None of the 51 files are generated. After this change, the same command
prints 0.
For #41184.
Change-Id: Ia96ee2a0f998d6a167d4473bcad17ad09bc1d86e
Reviewed-on: https://go-review.googlesource.com/c/go/+/341009
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Dmitri Shuralyov <dmitshur@golang.org>
2021-08-09 20:29:14 -04:00
|
|
|
//go:build js && wasm
|
2018-03-04 13:40:08 +01:00
|
|
|
|
2018-10-26 01:32:27 +00:00
|
|
|
// To run these tests:
|
|
|
|
//
|
|
|
|
// - Install Node
|
2024-08-24 08:39:47 +00:00
|
|
|
// - Add /path/to/go/lib/wasm to your $PATH (so that "go test" can find
|
2018-10-26 01:32:27 +00:00
|
|
|
// "go_js_wasm_exec").
|
|
|
|
// - GOOS=js GOARCH=wasm go test
|
|
|
|
//
|
|
|
|
// See -exec in "go help test", and "go help run" for details.
|
|
|
|
|
2018-03-04 13:40:08 +01:00
|
|
|
package js_test
|
|
|
|
|
|
|
|
import (
|
2018-05-20 00:56:36 +02:00
|
|
|
"fmt"
|
2018-06-24 17:23:38 +02:00
|
|
|
"math"
|
2019-10-26 21:01:32 +02:00
|
|
|
"runtime"
|
2018-03-04 13:40:08 +01:00
|
|
|
"syscall/js"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2018-06-25 12:30:31 +02:00
|
|
|
var dummys = js.Global().Call("eval", `({
|
2018-03-04 13:40:08 +01:00
|
|
|
someBool: true,
|
|
|
|
someString: "abc\u1234",
|
|
|
|
someInt: 42,
|
|
|
|
someFloat: 42.123,
|
|
|
|
someArray: [41, 42, 43],
|
2018-10-26 01:32:27 +00:00
|
|
|
someDate: new Date(),
|
2018-03-04 13:40:08 +01:00
|
|
|
add: function(a, b) {
|
|
|
|
return a + b;
|
|
|
|
},
|
2018-10-18 15:53:38 +02:00
|
|
|
zero: 0,
|
2018-10-26 01:32:27 +00:00
|
|
|
stringZero: "0",
|
2018-06-24 17:23:38 +02:00
|
|
|
NaN: NaN,
|
2018-10-26 01:32:27 +00:00
|
|
|
emptyObj: {},
|
|
|
|
emptyArray: [],
|
|
|
|
Infinity: Infinity,
|
|
|
|
NegInfinity: -Infinity,
|
|
|
|
objNumber0: new Number(0),
|
|
|
|
objBooleanFalse: new Boolean(false),
|
2018-03-04 13:40:08 +01:00
|
|
|
})`)
|
|
|
|
|
2023-01-22 15:30:59 -08:00
|
|
|
//go:wasmimport _gotest add
|
|
|
|
func testAdd(uint32, uint32) uint32
|
|
|
|
|
|
|
|
func TestWasmImport(t *testing.T) {
|
|
|
|
a := uint32(3)
|
|
|
|
b := uint32(5)
|
|
|
|
want := a + b
|
|
|
|
if got := testAdd(a, b); got != want {
|
|
|
|
t.Errorf("got %v, want %v", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-19 15:17:04 -04:00
|
|
|
// testCallExport is imported from host (wasm_exec.js), which calls testExport.
|
|
|
|
//
|
|
|
|
//go:wasmimport _gotest callExport
|
|
|
|
func testCallExport(a int32, b int64) int64
|
|
|
|
|
|
|
|
//go:wasmexport testExport
|
|
|
|
func testExport(a int32, b int64) int64 {
|
|
|
|
testExportCalled = true
|
|
|
|
// test stack growth
|
|
|
|
growStack(1000)
|
|
|
|
// force a goroutine switch
|
|
|
|
ch := make(chan int64)
|
|
|
|
go func() {
|
|
|
|
ch <- int64(a)
|
|
|
|
ch <- b
|
|
|
|
}()
|
|
|
|
return <-ch + <-ch
|
|
|
|
}
|
|
|
|
|
|
|
|
var testExportCalled bool
|
|
|
|
|
|
|
|
func growStack(n int64) {
|
|
|
|
if n > 0 {
|
|
|
|
growStack(n - 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWasmExport(t *testing.T) {
|
|
|
|
testExportCalled = false
|
|
|
|
a := int32(123)
|
|
|
|
b := int64(456)
|
|
|
|
want := int64(a) + b
|
|
|
|
if got := testCallExport(a, b); got != want {
|
|
|
|
t.Errorf("got %v, want %v", got, want)
|
|
|
|
}
|
|
|
|
if !testExportCalled {
|
|
|
|
t.Error("testExport not called")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-04 13:40:08 +01:00
|
|
|
func TestBool(t *testing.T) {
|
|
|
|
want := true
|
|
|
|
o := dummys.Get("someBool")
|
|
|
|
if got := o.Bool(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
dummys.Set("otherBool", want)
|
|
|
|
if got := dummys.Get("otherBool").Bool(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
2019-10-26 21:01:32 +02:00
|
|
|
if !dummys.Get("someBool").Equal(dummys.Get("someBool")) {
|
2018-06-24 17:23:38 +02:00
|
|
|
t.Errorf("same value not equal")
|
|
|
|
}
|
2018-03-04 13:40:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestString(t *testing.T) {
|
|
|
|
want := "abc\u1234"
|
|
|
|
o := dummys.Get("someString")
|
|
|
|
if got := o.String(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
dummys.Set("otherString", want)
|
|
|
|
if got := dummys.Get("otherString").String(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
2019-10-26 21:01:32 +02:00
|
|
|
if !dummys.Get("someString").Equal(dummys.Get("someString")) {
|
2018-06-24 17:23:38 +02:00
|
|
|
t.Errorf("same value not equal")
|
|
|
|
}
|
2018-07-05 19:55:08 +02:00
|
|
|
|
2019-03-28 01:22:37 +01:00
|
|
|
if got, want := js.Undefined().String(), "<undefined>"; got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.Null().String(), "<null>"; got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.ValueOf(true).String(), "<boolean: true>"; got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.ValueOf(42.5).String(), "<number: 42.5>"; got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.Global().Call("Symbol").String(), "<symbol>"; got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.Global().String(), "<object>"; got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.Global().Get("setTimeout").String(), "<function>"; got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
2018-07-05 19:55:08 +02:00
|
|
|
}
|
2018-03-04 13:40:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestInt(t *testing.T) {
|
|
|
|
want := 42
|
|
|
|
o := dummys.Get("someInt")
|
|
|
|
if got := o.Int(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
dummys.Set("otherInt", want)
|
|
|
|
if got := dummys.Get("otherInt").Int(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
2019-10-26 21:01:32 +02:00
|
|
|
if !dummys.Get("someInt").Equal(dummys.Get("someInt")) {
|
2018-06-24 17:23:38 +02:00
|
|
|
t.Errorf("same value not equal")
|
|
|
|
}
|
2018-10-18 15:53:38 +02:00
|
|
|
if got := dummys.Get("zero").Int(); got != 0 {
|
|
|
|
t.Errorf("got %#v, want %#v", got, 0)
|
|
|
|
}
|
2018-03-04 13:40:08 +01:00
|
|
|
}
|
|
|
|
|
2018-05-15 00:52:18 +02:00
|
|
|
func TestIntConversion(t *testing.T) {
|
|
|
|
testIntConversion(t, 0)
|
|
|
|
testIntConversion(t, 1)
|
|
|
|
testIntConversion(t, -1)
|
|
|
|
testIntConversion(t, 1<<20)
|
|
|
|
testIntConversion(t, -1<<20)
|
|
|
|
testIntConversion(t, 1<<40)
|
|
|
|
testIntConversion(t, -1<<40)
|
|
|
|
testIntConversion(t, 1<<60)
|
|
|
|
testIntConversion(t, -1<<60)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testIntConversion(t *testing.T, want int) {
|
|
|
|
if got := js.ValueOf(want).Int(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-04 13:40:08 +01:00
|
|
|
func TestFloat(t *testing.T) {
|
|
|
|
want := 42.123
|
|
|
|
o := dummys.Get("someFloat")
|
|
|
|
if got := o.Float(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
dummys.Set("otherFloat", want)
|
|
|
|
if got := dummys.Get("otherFloat").Float(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
2019-10-26 21:01:32 +02:00
|
|
|
if !dummys.Get("someFloat").Equal(dummys.Get("someFloat")) {
|
2018-06-24 17:23:38 +02:00
|
|
|
t.Errorf("same value not equal")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestObject(t *testing.T) {
|
2019-10-26 21:01:32 +02:00
|
|
|
if !dummys.Get("someArray").Equal(dummys.Get("someArray")) {
|
2018-06-24 17:23:38 +02:00
|
|
|
t.Errorf("same value not equal")
|
|
|
|
}
|
2018-06-29 23:09:34 -04:00
|
|
|
|
|
|
|
// An object and its prototype should not be equal.
|
|
|
|
proto := js.Global().Get("Object").Get("prototype")
|
|
|
|
o := js.Global().Call("eval", "new Object()")
|
2019-10-26 21:01:32 +02:00
|
|
|
if proto.Equal(o) {
|
2018-06-29 23:09:34 -04:00
|
|
|
t.Errorf("object equals to its prototype")
|
|
|
|
}
|
2018-06-24 17:23:38 +02:00
|
|
|
}
|
|
|
|
|
2018-07-02 08:08:14 +01:00
|
|
|
func TestFrozenObject(t *testing.T) {
|
|
|
|
o := js.Global().Call("eval", "(function () { let o = new Object(); o.field = 5; Object.freeze(o); return o; })()")
|
|
|
|
want := 5
|
|
|
|
if got := o.Get("field").Int(); want != got {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-26 21:01:32 +02:00
|
|
|
func TestEqual(t *testing.T) {
|
|
|
|
if !dummys.Get("someFloat").Equal(dummys.Get("someFloat")) {
|
|
|
|
t.Errorf("same float is not equal")
|
|
|
|
}
|
|
|
|
if !dummys.Get("emptyObj").Equal(dummys.Get("emptyObj")) {
|
|
|
|
t.Errorf("same object is not equal")
|
|
|
|
}
|
|
|
|
if dummys.Get("someFloat").Equal(dummys.Get("someInt")) {
|
|
|
|
t.Errorf("different values are not unequal")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-24 17:23:38 +02:00
|
|
|
func TestNaN(t *testing.T) {
|
2019-10-26 21:01:32 +02:00
|
|
|
if !dummys.Get("NaN").IsNaN() {
|
|
|
|
t.Errorf("JS NaN is not NaN")
|
|
|
|
}
|
|
|
|
if !js.ValueOf(math.NaN()).IsNaN() {
|
|
|
|
t.Errorf("Go NaN is not NaN")
|
|
|
|
}
|
|
|
|
if dummys.Get("NaN").Equal(dummys.Get("NaN")) {
|
|
|
|
t.Errorf("NaN is equal to NaN")
|
2018-06-24 17:23:38 +02:00
|
|
|
}
|
2018-03-04 13:40:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestUndefined(t *testing.T) {
|
2019-10-26 21:01:32 +02:00
|
|
|
if !js.Undefined().IsUndefined() {
|
|
|
|
t.Errorf("undefined is not undefined")
|
|
|
|
}
|
|
|
|
if !js.Undefined().Equal(js.Undefined()) {
|
|
|
|
t.Errorf("undefined is not equal to undefined")
|
|
|
|
}
|
|
|
|
if dummys.IsUndefined() {
|
|
|
|
t.Errorf("object is undefined")
|
|
|
|
}
|
|
|
|
if js.Undefined().IsNull() {
|
|
|
|
t.Errorf("undefined is null")
|
|
|
|
}
|
|
|
|
if dummys.Set("test", js.Undefined()); !dummys.Get("test").IsUndefined() {
|
|
|
|
t.Errorf("could not set undefined")
|
2018-03-04 13:40:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNull(t *testing.T) {
|
2019-10-26 21:01:32 +02:00
|
|
|
if !js.Null().IsNull() {
|
|
|
|
t.Errorf("null is not null")
|
|
|
|
}
|
|
|
|
if !js.Null().Equal(js.Null()) {
|
|
|
|
t.Errorf("null is not equal to null")
|
|
|
|
}
|
|
|
|
if dummys.IsNull() {
|
|
|
|
t.Errorf("object is null")
|
|
|
|
}
|
|
|
|
if js.Null().IsUndefined() {
|
|
|
|
t.Errorf("null is undefined")
|
|
|
|
}
|
|
|
|
if dummys.Set("test", js.Null()); !dummys.Get("test").IsNull() {
|
|
|
|
t.Errorf("could not set null")
|
|
|
|
}
|
|
|
|
if dummys.Set("test", nil); !dummys.Get("test").IsNull() {
|
|
|
|
t.Errorf("could not set nil")
|
2018-03-04 13:40:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestLength(t *testing.T) {
|
|
|
|
if got := dummys.Get("someArray").Length(); got != 3 {
|
|
|
|
t.Errorf("got %#v, want %#v", got, 3)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-25 00:31:41 +01:00
|
|
|
func TestGet(t *testing.T) {
|
|
|
|
// positive cases get tested per type
|
|
|
|
|
|
|
|
expectValueError(t, func() {
|
|
|
|
dummys.Get("zero").Get("badField")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSet(t *testing.T) {
|
|
|
|
// positive cases get tested per type
|
|
|
|
|
|
|
|
expectValueError(t, func() {
|
|
|
|
dummys.Get("zero").Set("badField", 42)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-09-28 23:47:37 +02:00
|
|
|
func TestDelete(t *testing.T) {
|
|
|
|
dummys.Set("test", 42)
|
|
|
|
dummys.Delete("test")
|
|
|
|
if dummys.Call("hasOwnProperty", "test").Bool() {
|
|
|
|
t.Errorf("property still exists")
|
|
|
|
}
|
|
|
|
|
|
|
|
expectValueError(t, func() {
|
|
|
|
dummys.Get("zero").Delete("badField")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-03-04 13:40:08 +01:00
|
|
|
func TestIndex(t *testing.T) {
|
|
|
|
if got := dummys.Get("someArray").Index(1).Int(); got != 42 {
|
|
|
|
t.Errorf("got %#v, want %#v", got, 42)
|
|
|
|
}
|
2019-03-25 00:31:41 +01:00
|
|
|
|
|
|
|
expectValueError(t, func() {
|
|
|
|
dummys.Get("zero").Index(1)
|
|
|
|
})
|
2018-03-04 13:40:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSetIndex(t *testing.T) {
|
|
|
|
dummys.Get("someArray").SetIndex(2, 99)
|
|
|
|
if got := dummys.Get("someArray").Index(2).Int(); got != 99 {
|
|
|
|
t.Errorf("got %#v, want %#v", got, 99)
|
|
|
|
}
|
2019-03-25 00:31:41 +01:00
|
|
|
|
|
|
|
expectValueError(t, func() {
|
|
|
|
dummys.Get("zero").SetIndex(2, 99)
|
|
|
|
})
|
2018-03-04 13:40:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestCall(t *testing.T) {
|
|
|
|
var i int64 = 40
|
|
|
|
if got := dummys.Call("add", i, 2).Int(); got != 42 {
|
|
|
|
t.Errorf("got %#v, want %#v", got, 42)
|
|
|
|
}
|
2018-06-25 12:30:31 +02:00
|
|
|
if got := dummys.Call("add", js.Global().Call("eval", "40"), 2).Int(); got != 42 {
|
2018-03-04 13:40:08 +01:00
|
|
|
t.Errorf("got %#v, want %#v", got, 42)
|
|
|
|
}
|
2019-03-25 00:31:41 +01:00
|
|
|
|
|
|
|
expectPanic(t, func() {
|
|
|
|
dummys.Call("zero")
|
|
|
|
})
|
|
|
|
expectValueError(t, func() {
|
|
|
|
dummys.Get("zero").Call("badMethod")
|
|
|
|
})
|
2018-03-04 13:40:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestInvoke(t *testing.T) {
|
|
|
|
var i int64 = 40
|
|
|
|
if got := dummys.Get("add").Invoke(i, 2).Int(); got != 42 {
|
|
|
|
t.Errorf("got %#v, want %#v", got, 42)
|
|
|
|
}
|
2019-03-25 00:31:41 +01:00
|
|
|
|
|
|
|
expectValueError(t, func() {
|
|
|
|
dummys.Get("zero").Invoke()
|
|
|
|
})
|
2018-03-04 13:40:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestNew(t *testing.T) {
|
2018-06-25 12:30:31 +02:00
|
|
|
if got := js.Global().Get("Array").New(42).Length(); got != 42 {
|
2018-03-04 13:40:08 +01:00
|
|
|
t.Errorf("got %#v, want %#v", got, 42)
|
|
|
|
}
|
2019-03-25 00:31:41 +01:00
|
|
|
|
|
|
|
expectValueError(t, func() {
|
|
|
|
dummys.Get("zero").New()
|
|
|
|
})
|
2018-03-04 13:40:08 +01:00
|
|
|
}
|
2018-05-20 00:56:36 +02:00
|
|
|
|
2018-06-15 10:45:04 +02:00
|
|
|
func TestInstanceOf(t *testing.T) {
|
2018-06-25 12:30:31 +02:00
|
|
|
someArray := js.Global().Get("Array").New()
|
|
|
|
if got, want := someArray.InstanceOf(js.Global().Get("Array")), true; got != want {
|
2018-06-15 10:45:04 +02:00
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
2018-06-25 12:30:31 +02:00
|
|
|
if got, want := someArray.InstanceOf(js.Global().Get("Function")), false; got != want {
|
2018-06-15 10:45:04 +02:00
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-05 19:30:00 +02:00
|
|
|
func TestType(t *testing.T) {
|
|
|
|
if got, want := js.Undefined().Type(), js.TypeUndefined; got != want {
|
|
|
|
t.Errorf("got %s, want %s", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.Null().Type(), js.TypeNull; got != want {
|
|
|
|
t.Errorf("got %s, want %s", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.ValueOf(true).Type(), js.TypeBoolean; got != want {
|
|
|
|
t.Errorf("got %s, want %s", got, want)
|
|
|
|
}
|
2018-10-18 15:53:38 +02:00
|
|
|
if got, want := js.ValueOf(0).Type(), js.TypeNumber; got != want {
|
|
|
|
t.Errorf("got %s, want %s", got, want)
|
|
|
|
}
|
2018-07-05 19:30:00 +02:00
|
|
|
if got, want := js.ValueOf(42).Type(), js.TypeNumber; got != want {
|
|
|
|
t.Errorf("got %s, want %s", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.ValueOf("test").Type(), js.TypeString; got != want {
|
|
|
|
t.Errorf("got %s, want %s", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.Global().Get("Symbol").Invoke("test").Type(), js.TypeSymbol; got != want {
|
|
|
|
t.Errorf("got %s, want %s", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.Global().Get("Array").New().Type(), js.TypeObject; got != want {
|
|
|
|
t.Errorf("got %s, want %s", got, want)
|
|
|
|
}
|
|
|
|
if got, want := js.Global().Get("Array").Type(), js.TypeFunction; got != want {
|
|
|
|
t.Errorf("got %s, want %s", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-01 12:15:45 -05:00
|
|
|
type object = map[string]any
|
|
|
|
type array = []any
|
2018-07-31 15:12:57 +02:00
|
|
|
|
|
|
|
func TestValueOf(t *testing.T) {
|
|
|
|
a := js.ValueOf(array{0, array{0, 42, 0}, 0})
|
|
|
|
if got := a.Index(1).Index(1).Int(); got != 42 {
|
|
|
|
t.Errorf("got %v, want %v", got, 42)
|
|
|
|
}
|
|
|
|
|
|
|
|
o := js.ValueOf(object{"x": object{"y": 42}})
|
|
|
|
if got := o.Get("x").Get("y").Int(); got != 42 {
|
|
|
|
t.Errorf("got %v, want %v", got, 42)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-18 15:53:38 +02:00
|
|
|
func TestZeroValue(t *testing.T) {
|
|
|
|
var v js.Value
|
2019-10-26 21:01:32 +02:00
|
|
|
if !v.IsUndefined() {
|
2018-10-18 15:53:38 +02:00
|
|
|
t.Error("zero js.Value is not js.Undefined()")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-11 14:23:17 +01:00
|
|
|
func TestFuncOf(t *testing.T) {
|
2018-05-20 00:56:36 +02:00
|
|
|
c := make(chan struct{})
|
2021-12-01 12:15:45 -05:00
|
|
|
cb := js.FuncOf(func(this js.Value, args []js.Value) any {
|
2018-05-20 00:56:36 +02:00
|
|
|
if got := args[0].Int(); got != 42 {
|
|
|
|
t.Errorf("got %#v, want %#v", got, 42)
|
|
|
|
}
|
|
|
|
c <- struct{}{}
|
2018-10-11 12:46:14 +02:00
|
|
|
return nil
|
2018-05-20 00:56:36 +02:00
|
|
|
})
|
2018-06-27 20:19:30 +02:00
|
|
|
defer cb.Release()
|
2018-06-25 12:30:31 +02:00
|
|
|
js.Global().Call("setTimeout", cb, 0, 42)
|
2018-05-20 00:56:36 +02:00
|
|
|
<-c
|
|
|
|
}
|
|
|
|
|
2018-12-11 14:23:17 +01:00
|
|
|
func TestInvokeFunction(t *testing.T) {
|
2018-10-11 12:46:14 +02:00
|
|
|
called := false
|
2021-12-01 12:15:45 -05:00
|
|
|
cb := js.FuncOf(func(this js.Value, args []js.Value) any {
|
|
|
|
cb2 := js.FuncOf(func(this js.Value, args []js.Value) any {
|
2018-10-11 12:46:14 +02:00
|
|
|
called = true
|
|
|
|
return 42
|
2018-05-20 00:56:36 +02:00
|
|
|
})
|
2018-10-11 12:46:14 +02:00
|
|
|
defer cb2.Release()
|
|
|
|
return cb2.Invoke()
|
|
|
|
})
|
|
|
|
defer cb.Release()
|
|
|
|
if got := cb.Invoke().Int(); got != 42 {
|
|
|
|
t.Errorf("got %#v, want %#v", got, 42)
|
|
|
|
}
|
|
|
|
if !called {
|
2018-12-11 14:23:17 +01:00
|
|
|
t.Error("function not called")
|
2018-05-20 00:56:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-02 21:20:24 +01:00
|
|
|
func TestInterleavedFunctions(t *testing.T) {
|
|
|
|
c1 := make(chan struct{})
|
|
|
|
c2 := make(chan struct{})
|
|
|
|
|
2021-12-01 12:15:45 -05:00
|
|
|
js.Global().Get("setTimeout").Invoke(js.FuncOf(func(this js.Value, args []js.Value) any {
|
2019-11-02 21:20:24 +01:00
|
|
|
c1 <- struct{}{}
|
|
|
|
<-c2
|
|
|
|
return nil
|
|
|
|
}), 0)
|
|
|
|
|
|
|
|
<-c1
|
|
|
|
c2 <- struct{}{}
|
|
|
|
// this goroutine is running, but the callback of setTimeout did not return yet, invoke another function now
|
2021-12-01 12:15:45 -05:00
|
|
|
f := js.FuncOf(func(this js.Value, args []js.Value) any {
|
2019-11-02 21:20:24 +01:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
f.Invoke()
|
|
|
|
}
|
|
|
|
|
2018-12-11 14:23:17 +01:00
|
|
|
func ExampleFuncOf() {
|
|
|
|
var cb js.Func
|
2021-12-01 12:15:45 -05:00
|
|
|
cb = js.FuncOf(func(this js.Value, args []js.Value) any {
|
2018-05-20 00:56:36 +02:00
|
|
|
fmt.Println("button clicked")
|
2018-12-11 14:23:17 +01:00
|
|
|
cb.Release() // release the function if the button will not be clicked again
|
2018-10-11 12:46:14 +02:00
|
|
|
return nil
|
2018-05-20 00:56:36 +02:00
|
|
|
})
|
2018-06-25 12:30:31 +02:00
|
|
|
js.Global().Get("document").Call("getElementById", "myButton").Call("addEventListener", "click", cb)
|
2018-05-20 00:56:36 +02:00
|
|
|
}
|
2018-10-26 01:32:27 +00:00
|
|
|
|
|
|
|
// See
|
|
|
|
// - https://developer.mozilla.org/en-US/docs/Glossary/Truthy
|
|
|
|
// - https://stackoverflow.com/questions/19839952/all-falsey-values-in-javascript/19839953#19839953
|
|
|
|
// - http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
|
|
|
|
func TestTruthy(t *testing.T) {
|
|
|
|
want := true
|
|
|
|
for _, key := range []string{
|
|
|
|
"someBool", "someString", "someInt", "someFloat", "someArray", "someDate",
|
|
|
|
"stringZero", // "0" is truthy
|
|
|
|
"add", // functions are truthy
|
|
|
|
"emptyObj", "emptyArray", "Infinity", "NegInfinity",
|
|
|
|
// All objects are truthy, even if they're Number(0) or Boolean(false).
|
|
|
|
"objNumber0", "objBooleanFalse",
|
|
|
|
} {
|
|
|
|
if got := dummys.Get(key).Truthy(); got != want {
|
|
|
|
t.Errorf("%s: got %#v, want %#v", key, got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
want = false
|
|
|
|
if got := dummys.Get("zero").Truthy(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
if got := dummys.Get("NaN").Truthy(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
if got := js.ValueOf("").Truthy(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
if got := js.Null().Truthy(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
if got := js.Undefined().Truthy(); got != want {
|
|
|
|
t.Errorf("got %#v, want %#v", got, want)
|
|
|
|
}
|
|
|
|
}
|
2019-03-25 00:31:41 +01:00
|
|
|
|
|
|
|
func expectValueError(t *testing.T, fn func()) {
|
|
|
|
defer func() {
|
|
|
|
err := recover()
|
|
|
|
if _, ok := err.(*js.ValueError); !ok {
|
|
|
|
t.Errorf("expected *js.ValueError, got %T", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
fn()
|
|
|
|
}
|
|
|
|
|
|
|
|
func expectPanic(t *testing.T, fn func()) {
|
|
|
|
defer func() {
|
|
|
|
err := recover()
|
|
|
|
if err == nil {
|
|
|
|
t.Errorf("expected panic")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
fn()
|
|
|
|
}
|
2019-05-16 01:03:10 +02:00
|
|
|
|
|
|
|
var copyTests = []struct {
|
|
|
|
srcLen int
|
|
|
|
dstLen int
|
|
|
|
copyLen int
|
|
|
|
}{
|
|
|
|
{5, 3, 3},
|
|
|
|
{3, 5, 3},
|
|
|
|
{0, 0, 0},
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCopyBytesToGo(t *testing.T) {
|
|
|
|
for _, tt := range copyTests {
|
|
|
|
t.Run(fmt.Sprintf("%d-to-%d", tt.srcLen, tt.dstLen), func(t *testing.T) {
|
|
|
|
src := js.Global().Get("Uint8Array").New(tt.srcLen)
|
|
|
|
if tt.srcLen >= 2 {
|
|
|
|
src.SetIndex(1, 42)
|
|
|
|
}
|
|
|
|
dst := make([]byte, tt.dstLen)
|
|
|
|
|
|
|
|
if got, want := js.CopyBytesToGo(dst, src), tt.copyLen; got != want {
|
|
|
|
t.Errorf("copied %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
if tt.dstLen >= 2 {
|
|
|
|
if got, want := int(dst[1]), 42; got != want {
|
|
|
|
t.Errorf("got %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCopyBytesToJS(t *testing.T) {
|
|
|
|
for _, tt := range copyTests {
|
|
|
|
t.Run(fmt.Sprintf("%d-to-%d", tt.srcLen, tt.dstLen), func(t *testing.T) {
|
|
|
|
src := make([]byte, tt.srcLen)
|
|
|
|
if tt.srcLen >= 2 {
|
|
|
|
src[1] = 42
|
|
|
|
}
|
|
|
|
dst := js.Global().Get("Uint8Array").New(tt.dstLen)
|
|
|
|
|
|
|
|
if got, want := js.CopyBytesToJS(dst, src), tt.copyLen; got != want {
|
|
|
|
t.Errorf("copied %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
if tt.dstLen >= 2 {
|
|
|
|
if got, want := dst.Index(1).Int(), 42; got != want {
|
|
|
|
t.Errorf("got %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-06-22 13:07:57 +05:30
|
|
|
|
2019-10-26 21:01:32 +02:00
|
|
|
func TestGarbageCollection(t *testing.T) {
|
|
|
|
before := js.JSGo.Get("_values").Length()
|
|
|
|
for i := 0; i < 1000; i++ {
|
|
|
|
_ = js.Global().Get("Object").New().Call("toString").String()
|
|
|
|
runtime.GC()
|
|
|
|
}
|
|
|
|
after := js.JSGo.Get("_values").Length()
|
|
|
|
if after-before > 500 {
|
|
|
|
t.Errorf("garbage collection ineffective")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-18 16:24:24 +00:00
|
|
|
// This table is used for allocation tests. We expect a specific allocation
|
|
|
|
// behavior to be seen, depending on the number of arguments applied to various
|
|
|
|
// JavaScript functions.
|
|
|
|
// Note: All JavaScript functions return a JavaScript array, which will cause
|
|
|
|
// one allocation to be created to track the Value.gcPtr for the Value finalizer.
|
|
|
|
var allocTests = []struct {
|
2024-08-19 15:17:04 -04:00
|
|
|
argLen int // The number of arguments to use for the syscall
|
2024-04-18 16:24:24 +00:00
|
|
|
expected int // The expected number of allocations
|
|
|
|
}{
|
2024-08-20 17:54:39 +00:00
|
|
|
// For less than or equal to 16 arguments, we expect 1 allocation:
|
2024-04-18 16:24:24 +00:00
|
|
|
// - makeValue new(ref)
|
2024-08-19 15:17:04 -04:00
|
|
|
{0, 1},
|
|
|
|
{2, 1},
|
2024-04-18 16:24:24 +00:00
|
|
|
{15, 1},
|
|
|
|
{16, 1},
|
2024-08-20 17:54:39 +00:00
|
|
|
// For greater than 16 arguments, we expect 3 allocation:
|
2024-04-18 16:24:24 +00:00
|
|
|
// - makeValue: new(ref)
|
|
|
|
// - makeArgSlices: argVals = make([]Value, size)
|
|
|
|
// - makeArgSlices: argRefs = make([]ref, size)
|
|
|
|
{17, 3},
|
|
|
|
{32, 3},
|
|
|
|
{42, 3},
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestCallAllocations ensures the correct allocation profile for Value.Call
|
|
|
|
func TestCallAllocations(t *testing.T) {
|
|
|
|
for _, test := range allocTests {
|
|
|
|
args := make([]any, test.argLen)
|
|
|
|
|
|
|
|
tmpArray := js.Global().Get("Array").New(0)
|
|
|
|
numAllocs := testing.AllocsPerRun(100, func() {
|
|
|
|
tmpArray.Call("concat", args...)
|
2024-08-19 15:17:04 -04:00
|
|
|
})
|
2024-04-18 16:24:24 +00:00
|
|
|
|
|
|
|
if numAllocs != float64(test.expected) {
|
|
|
|
t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestInvokeAllocations ensures the correct allocation profile for Value.Invoke
|
|
|
|
func TestInvokeAllocations(t *testing.T) {
|
|
|
|
for _, test := range allocTests {
|
|
|
|
args := make([]any, test.argLen)
|
|
|
|
|
|
|
|
tmpArray := js.Global().Get("Array").New(0)
|
|
|
|
concatFunc := tmpArray.Get("concat").Call("bind", tmpArray)
|
|
|
|
numAllocs := testing.AllocsPerRun(100, func() {
|
|
|
|
concatFunc.Invoke(args...)
|
2024-08-19 15:17:04 -04:00
|
|
|
})
|
2024-04-18 16:24:24 +00:00
|
|
|
|
|
|
|
if numAllocs != float64(test.expected) {
|
|
|
|
t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestNewAllocations ensures the correct allocation profile for Value.New
|
|
|
|
func TestNewAllocations(t *testing.T) {
|
|
|
|
arrayConstructor := js.Global().Get("Array")
|
|
|
|
|
|
|
|
for _, test := range allocTests {
|
|
|
|
args := make([]any, test.argLen)
|
|
|
|
|
|
|
|
numAllocs := testing.AllocsPerRun(100, func() {
|
|
|
|
arrayConstructor.New(args...)
|
2024-08-19 15:17:04 -04:00
|
|
|
})
|
2024-04-18 16:24:24 +00:00
|
|
|
|
|
|
|
if numAllocs != float64(test.expected) {
|
|
|
|
t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-22 13:07:57 +05:30
|
|
|
// BenchmarkDOM is a simple benchmark which emulates a webapp making DOM operations.
|
|
|
|
// It creates a div, and sets its id. Then searches by that id and sets some data.
|
|
|
|
// Finally it removes that div.
|
|
|
|
func BenchmarkDOM(b *testing.B) {
|
|
|
|
document := js.Global().Get("document")
|
2019-10-26 21:01:32 +02:00
|
|
|
if document.IsUndefined() {
|
2019-06-22 13:07:57 +05:30
|
|
|
b.Skip("Not a browser environment. Skipping.")
|
|
|
|
}
|
|
|
|
const data = "someString"
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
div := document.Call("createElement", "div")
|
|
|
|
div.Call("setAttribute", "id", "myDiv")
|
|
|
|
document.Get("body").Call("appendChild", div)
|
|
|
|
myDiv := document.Call("getElementById", "myDiv")
|
|
|
|
myDiv.Set("innerHTML", data)
|
|
|
|
|
|
|
|
if got, want := myDiv.Get("innerHTML").String(), data; got != want {
|
|
|
|
b.Errorf("got %s, want %s", got, want)
|
|
|
|
}
|
|
|
|
document.Get("body").Call("removeChild", div)
|
|
|
|
}
|
|
|
|
}
|
2020-05-06 23:00:58 +09:00
|
|
|
|
|
|
|
func TestGlobal(t *testing.T) {
|
2021-12-01 12:15:45 -05:00
|
|
|
ident := js.FuncOf(func(this js.Value, args []js.Value) any {
|
2020-05-06 23:00:58 +09:00
|
|
|
return args[0]
|
|
|
|
})
|
|
|
|
defer ident.Release()
|
|
|
|
|
|
|
|
if got := ident.Invoke(js.Global()); !got.Equal(js.Global()) {
|
|
|
|
t.Errorf("got %#v, want %#v", got, js.Global())
|
|
|
|
}
|
|
|
|
}
|