mirror of
https://github.com/robertkrimen/otto
synced 2025-10-12 20:27:30 +08:00

Enable more linters, address the issues and do a major naming refactor to use golang lower camelCase identifiers for types, functions, methods and variable names. Also: * Clean up inline generation so it doesn't rely on temporary variables. * Remove unused functions generated by inline.pl.
941 lines
18 KiB
Go
941 lines
18 KiB
Go
package otto
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type abcStruct struct {
|
|
Abc bool
|
|
Def int
|
|
Ghi string
|
|
Jkl interface{}
|
|
Mno _mnoStruct
|
|
Pqr map[string]int8
|
|
}
|
|
|
|
func (abc abcStruct) String() string {
|
|
return abc.Ghi
|
|
}
|
|
|
|
func (abc *abcStruct) FuncPointer() string {
|
|
return "abc"
|
|
}
|
|
|
|
func (abc abcStruct) Func() {
|
|
}
|
|
|
|
func (abc abcStruct) FuncReturn1() string {
|
|
return "abc"
|
|
}
|
|
|
|
func (abc abcStruct) FuncReturn2() (string, error) {
|
|
return "def", nil
|
|
}
|
|
|
|
func (abc abcStruct) Func1Return1(a string) string {
|
|
return a
|
|
}
|
|
|
|
func (abc abcStruct) Func2Return1(x, y string) string {
|
|
return x + y
|
|
}
|
|
|
|
func (abc abcStruct) FuncEllipsis(xyz ...string) int {
|
|
return len(xyz)
|
|
}
|
|
|
|
func (abc abcStruct) FuncReturnStruct() _mnoStruct {
|
|
return _mnoStruct{}
|
|
}
|
|
|
|
func (abc abcStruct) Func1Int(i int) int {
|
|
return i + 1
|
|
}
|
|
|
|
func (abc abcStruct) Func1Int8(i int8) int8 {
|
|
return i + 1
|
|
}
|
|
|
|
func (abc abcStruct) Func1Int16(i int16) int16 {
|
|
return i + 1
|
|
}
|
|
|
|
func (abc abcStruct) Func1Int32(i int32) int32 {
|
|
return i + 1
|
|
}
|
|
|
|
func (abc abcStruct) Func1Int64(i int64) int64 {
|
|
return i + 1
|
|
}
|
|
|
|
func (abc abcStruct) Func1Uint(i uint) uint {
|
|
return i + 1
|
|
}
|
|
|
|
func (abc abcStruct) Func1Uint8(i uint8) uint8 {
|
|
return i + 1
|
|
}
|
|
|
|
func (abc abcStruct) Func1Uint16(i uint16) uint16 {
|
|
return i + 1
|
|
}
|
|
|
|
func (abc abcStruct) Func1Uint32(i uint32) uint32 {
|
|
return i + 1
|
|
}
|
|
|
|
func (abc abcStruct) Func1Uint64(i uint64) uint64 {
|
|
return i + 1
|
|
}
|
|
|
|
func (abc abcStruct) Func2Int(i, j int) int {
|
|
return i + j
|
|
}
|
|
|
|
func (abc abcStruct) Func2StringInt(s string, i int) string {
|
|
return fmt.Sprintf("%v:%v", s, i)
|
|
}
|
|
|
|
func (abc abcStruct) Func1IntVariadic(a ...int) int {
|
|
t := 0
|
|
for _, i := range a {
|
|
t += i
|
|
}
|
|
return t
|
|
}
|
|
|
|
func (abc abcStruct) Func2IntVariadic(s string, a ...int) string {
|
|
t := 0
|
|
for _, i := range a {
|
|
t += i
|
|
}
|
|
return fmt.Sprintf("%v:%v", s, t)
|
|
}
|
|
|
|
func (abc abcStruct) Func2IntArrayVariadic(s string, a ...[]int) string {
|
|
t := 0
|
|
for _, i := range a {
|
|
for _, j := range i {
|
|
t += j
|
|
}
|
|
}
|
|
return fmt.Sprintf("%v:%v", s, t)
|
|
}
|
|
|
|
type _mnoStruct struct {
|
|
Ghi string
|
|
}
|
|
|
|
func (mno _mnoStruct) Func() string {
|
|
return "mno"
|
|
}
|
|
|
|
func TestReflect(t *testing.T) {
|
|
tt(t, func() {
|
|
// Testing dbgf
|
|
// These should panic
|
|
str := "test"
|
|
require.Panics(t, func() {
|
|
_, err := toValue("Xyzzy").toReflectValue(reflect.ValueOf(&str).Type())
|
|
require.NoError(t, err)
|
|
})
|
|
require.Panics(t, func() {
|
|
_, err := stringToReflectValue("Xyzzy", reflect.Ptr)
|
|
require.NoError(t, err)
|
|
})
|
|
})
|
|
}
|
|
|
|
func Test_reflectStruct(t *testing.T) {
|
|
tt(t, func() {
|
|
test, vm := test()
|
|
|
|
// _abcStruct
|
|
{
|
|
abc := &abcStruct{}
|
|
vm.Set("abc", abc)
|
|
|
|
test(`
|
|
[ abc.Abc, abc.Ghi ];
|
|
`, "false,")
|
|
|
|
abc.Abc = true
|
|
abc.Ghi = "Nothing happens."
|
|
|
|
test(`
|
|
[ abc.Abc, abc.Ghi ];
|
|
`, "true,Nothing happens.")
|
|
|
|
*abc = abcStruct{}
|
|
|
|
test(`
|
|
[ abc.Abc, abc.Ghi ];
|
|
`, "false,")
|
|
|
|
abc.Abc = true
|
|
abc.Ghi = "Xyzzy"
|
|
vm.Set("abc", abc)
|
|
|
|
test(`
|
|
[ abc.Abc, abc.Ghi ];
|
|
`, "true,Xyzzy")
|
|
|
|
is(abc.Abc, true)
|
|
test(`
|
|
abc.Abc = false;
|
|
abc.Def = 451;
|
|
abc.Ghi = "Nothing happens.";
|
|
abc.abc = "Something happens.";
|
|
[ abc.Def, abc.abc ];
|
|
`, "451,Something happens.")
|
|
is(abc.Abc, false)
|
|
is(abc.Def, 451)
|
|
is(abc.Ghi, "Nothing happens.")
|
|
|
|
test(`
|
|
delete abc.Def;
|
|
delete abc.abc;
|
|
[ abc.Def, abc.abc ];
|
|
`, "451,")
|
|
is(abc.Def, 451)
|
|
|
|
test(`
|
|
abc.FuncPointer();
|
|
`, "abc")
|
|
|
|
test(`
|
|
abc.Func();
|
|
`, "undefined")
|
|
|
|
test(`
|
|
abc.FuncReturn1();
|
|
`, "abc")
|
|
|
|
test(`
|
|
abc.Func1Return1("abc");
|
|
`, "abc")
|
|
|
|
test(`
|
|
abc.Func2Return1("abc", "def");
|
|
`, "abcdef")
|
|
|
|
test(`
|
|
abc.FuncEllipsis("abc", "def", "ghi");
|
|
`, 3)
|
|
|
|
test(`
|
|
ret = abc.FuncReturn2();
|
|
if (ret && ret.length && ret.length == 2 && ret[0] == "def" && ret[1] === undefined) {
|
|
true;
|
|
} else {
|
|
false;
|
|
}
|
|
`, true)
|
|
|
|
test(`
|
|
abc.FuncReturnStruct();
|
|
`, "[object Object]")
|
|
|
|
test(`
|
|
abc.FuncReturnStruct().Func();
|
|
`, "mno")
|
|
|
|
test(`
|
|
abc.Func1Int(1);
|
|
`, 2)
|
|
|
|
test(`
|
|
abc.Func1Int(0x01 & 0x01);
|
|
`, 2)
|
|
|
|
test(`raise:
|
|
abc.Func1Int(1.1);
|
|
`, "RangeError: converting float64 to int would cause loss of precision")
|
|
|
|
test(`
|
|
var v = 1;
|
|
abc.Func1Int(v + 1);
|
|
`, 3)
|
|
|
|
test(`
|
|
abc.Func2Int(1, 2);
|
|
`, 3)
|
|
|
|
test(`
|
|
abc.Func1Int8(1);
|
|
`, 2)
|
|
|
|
test(`
|
|
abc.Func1Int16(1);
|
|
`, 2)
|
|
|
|
test(`
|
|
abc.Func1Int32(1);
|
|
`, 2)
|
|
|
|
test(`
|
|
abc.Func1Int64(1);
|
|
`, 2)
|
|
|
|
test(`
|
|
abc.Func1Uint(1);
|
|
`, 2)
|
|
|
|
test(`
|
|
abc.Func1Uint8(1);
|
|
`, 2)
|
|
|
|
test(`
|
|
abc.Func1Uint16(1);
|
|
`, 2)
|
|
|
|
test(`
|
|
abc.Func1Uint32(1);
|
|
`, 2)
|
|
|
|
test(`
|
|
abc.Func1Uint64(1);
|
|
`, 2)
|
|
|
|
test(`
|
|
abc.Func2StringInt("test", 1);
|
|
`, "test:1")
|
|
|
|
test(`
|
|
abc.Func1IntVariadic(1, 2);
|
|
`, 3)
|
|
|
|
test(`
|
|
abc.Func2IntVariadic("test", 1, 2);
|
|
`, "test:3")
|
|
|
|
test(`
|
|
abc.Func2IntVariadic("test", [1, 2]);
|
|
`, "test:3")
|
|
|
|
test(`
|
|
abc.Func2IntArrayVariadic("test", [1, 2]);
|
|
`, "test:3")
|
|
|
|
test(`
|
|
abc.Func2IntArrayVariadic("test", [1, 2], [3, 4]);
|
|
`, "test:10")
|
|
|
|
test(`
|
|
abc.Func2IntArrayVariadic("test", [[1, 2], [3, 4]]);
|
|
`, "test:10")
|
|
}
|
|
})
|
|
}
|
|
|
|
func Test_reflectMap(t *testing.T) {
|
|
tt(t, func() {
|
|
test, vm := test()
|
|
|
|
// map[string]string
|
|
{
|
|
abc := map[string]string{
|
|
"Xyzzy": "Nothing happens.",
|
|
"def": "1",
|
|
}
|
|
vm.Set("abc", abc)
|
|
|
|
test(`
|
|
abc.xyz = "pqr";
|
|
[ abc.Xyzzy, abc.def, abc.ghi ];
|
|
`, "Nothing happens.,1,")
|
|
|
|
is(abc["xyz"], "pqr")
|
|
}
|
|
|
|
// map[string]float64
|
|
{
|
|
abc := map[string]float64{
|
|
"Xyzzy": math.Pi,
|
|
"def": 1,
|
|
}
|
|
vm.Set("abc", abc)
|
|
|
|
test(`
|
|
abc.xyz = "pqr";
|
|
abc.jkl = 10;
|
|
[ abc.Xyzzy, abc.def, abc.ghi ];
|
|
`, "3.141592653589793,1,")
|
|
|
|
is(abc["xyz"], math.NaN())
|
|
is(abc["jkl"], float64(10))
|
|
}
|
|
|
|
// map[string]int32
|
|
{
|
|
abc := map[string]int32{
|
|
"Xyzzy": 3,
|
|
"def": 1,
|
|
}
|
|
vm.Set("abc", abc)
|
|
|
|
test(`
|
|
abc.xyz = "pqr";
|
|
abc.jkl = 10;
|
|
[ abc.Xyzzy, abc.def, abc.ghi ];
|
|
`, "3,1,")
|
|
|
|
is(abc["xyz"], 0)
|
|
is(abc["jkl"], int32(10))
|
|
|
|
test(`
|
|
delete abc["Xyzzy"];
|
|
`)
|
|
|
|
_, exists := abc["Xyzzy"]
|
|
is(exists, false)
|
|
is(abc["Xyzzy"], 0)
|
|
}
|
|
|
|
// map[int32]string
|
|
{
|
|
abc := map[int32]string{
|
|
0: "abc",
|
|
1: "def",
|
|
}
|
|
vm.Set("abc", abc)
|
|
|
|
test(`
|
|
abc[2] = "pqr";
|
|
//abc.jkl = 10;
|
|
abc[3] = 10;
|
|
[ abc[0], abc[1], abc[2], abc[3] ]
|
|
`, "abc,def,pqr,10")
|
|
|
|
is(abc[2], "pqr")
|
|
is(abc[3], "10")
|
|
|
|
test(`
|
|
delete abc[2];
|
|
`)
|
|
|
|
_, exists := abc[2]
|
|
is(exists, false)
|
|
}
|
|
})
|
|
}
|
|
|
|
func Test_reflectMapIterateKeys(t *testing.T) {
|
|
tt(t, func() {
|
|
test, vm := test()
|
|
|
|
// map[string]interface{}
|
|
{
|
|
abc := map[string]interface{}{
|
|
"Xyzzy": "Nothing happens.",
|
|
"def": 1,
|
|
}
|
|
vm.Set("abc", abc)
|
|
test(`
|
|
var keys = [];
|
|
for (var key in abc) {
|
|
keys.push(key);
|
|
}
|
|
keys.sort();
|
|
keys;
|
|
`, "Xyzzy,def")
|
|
}
|
|
|
|
// map[uint]interface{}
|
|
{
|
|
abc := map[uint]interface{}{
|
|
456: "Nothing happens.",
|
|
123: 1,
|
|
}
|
|
vm.Set("abc", abc)
|
|
test(`
|
|
var keys = [];
|
|
for (var key in abc) {
|
|
keys.push(key);
|
|
}
|
|
keys.sort();
|
|
keys;
|
|
`, "123,456")
|
|
}
|
|
|
|
// map[byte]interface{}
|
|
{
|
|
abc := map[byte]interface{}{
|
|
10: "Nothing happens.",
|
|
20: 1,
|
|
}
|
|
vm.Set("abc", abc)
|
|
test(`
|
|
for (var key in abc) {
|
|
abc[key] = "123";
|
|
}
|
|
`)
|
|
is(abc[10], "123")
|
|
is(abc[20], "123")
|
|
}
|
|
})
|
|
}
|
|
|
|
func Test_reflectSlice(t *testing.T) {
|
|
tt(t, func() {
|
|
test, vm := test()
|
|
|
|
// []bool
|
|
{
|
|
abc := []bool{
|
|
false,
|
|
true,
|
|
true,
|
|
false,
|
|
}
|
|
vm.Set("abc", abc)
|
|
|
|
test(`
|
|
abc;
|
|
`, "false,true,true,false")
|
|
|
|
test(`
|
|
abc[0] = true;
|
|
abc[abc.length-1] = true;
|
|
delete abc[2];
|
|
abc;
|
|
`, "true,true,false,true")
|
|
|
|
is(abc, []bool{true, true, false, true})
|
|
is(abc[len(abc)-1], true)
|
|
}
|
|
|
|
// []int32
|
|
{
|
|
abc := make([]int32, 4)
|
|
vm.Set("abc", abc)
|
|
|
|
test(`
|
|
abc;
|
|
`, "0,0,0,0")
|
|
|
|
test(`raise:
|
|
abc[0] = "42";
|
|
abc[1] = 4.2;
|
|
abc[2] = 3.14;
|
|
abc;
|
|
`, "RangeError: 4.2 to reflect.Kind: int32")
|
|
|
|
is(abc, []int32{42, 0, 0, 0})
|
|
|
|
test(`
|
|
delete abc[1];
|
|
delete abc[2];
|
|
`)
|
|
is(abc[1], 0)
|
|
is(abc[2], 0)
|
|
}
|
|
})
|
|
}
|
|
|
|
func Test_reflectArray(t *testing.T) {
|
|
tt(t, func() {
|
|
test, vm := test()
|
|
|
|
// []bool
|
|
{
|
|
abc := [4]bool{
|
|
false,
|
|
true,
|
|
true,
|
|
false,
|
|
}
|
|
vm.Set("abc", abc)
|
|
|
|
test(`
|
|
abc;
|
|
`, "false,true,true,false")
|
|
// Unaddressable array
|
|
|
|
test(`
|
|
abc[0] = true;
|
|
abc[abc.length-1] = true;
|
|
abc;
|
|
`, "false,true,true,false")
|
|
// Again, unaddressable array
|
|
|
|
is(abc, [4]bool{false, true, true, false})
|
|
is(abc[len(abc)-1], false)
|
|
// ...
|
|
}
|
|
// []int32
|
|
{
|
|
abc := make([]int32, 4)
|
|
vm.Set("abc", abc)
|
|
|
|
test(`
|
|
abc;
|
|
`, "0,0,0,0")
|
|
|
|
test(`raise:
|
|
abc[0] = "42";
|
|
abc[1] = 4.2;
|
|
abc[2] = 3.14;
|
|
abc;
|
|
`, "RangeError: 4.2 to reflect.Kind: int32")
|
|
|
|
is(abc, []int32{42, 0, 0, 0})
|
|
}
|
|
|
|
// []bool
|
|
{
|
|
abc := [4]bool{
|
|
false,
|
|
true,
|
|
true,
|
|
false,
|
|
}
|
|
vm.Set("abc", &abc)
|
|
|
|
test(`
|
|
abc;
|
|
`, "false,true,true,false")
|
|
|
|
test(`
|
|
abc[0] = true;
|
|
abc[abc.length-1] = true;
|
|
delete abc[2];
|
|
abc;
|
|
`, "true,true,false,true")
|
|
|
|
is(abc, [4]bool{true, true, false, true})
|
|
is(abc[len(abc)-1], true)
|
|
}
|
|
|
|
// no common type
|
|
{
|
|
test(`
|
|
abc = [1, 2.2, "str"];
|
|
abc;
|
|
`, "1,2.2,str")
|
|
val, err := vm.Get("abc")
|
|
is(err, nil)
|
|
abc, err := val.Export()
|
|
is(err, nil)
|
|
is(abc, []interface{}{int64(1), 2.2, "str"})
|
|
}
|
|
|
|
// common type int
|
|
{
|
|
test(`
|
|
abc = [1, 2, 3];
|
|
abc;
|
|
`, "1,2,3")
|
|
val, err := vm.Get("abc")
|
|
is(err, nil)
|
|
abc, err := val.Export()
|
|
is(err, nil)
|
|
is(abc, []int64{1, 2, 3})
|
|
}
|
|
|
|
// common type string
|
|
{
|
|
test(`
|
|
abc = ["str1", "str2", "str3"];
|
|
abc;
|
|
`, "str1,str2,str3")
|
|
|
|
val, err := vm.Get("abc")
|
|
is(err, nil)
|
|
abc, err := val.Export()
|
|
is(err, nil)
|
|
is(abc, []string{"str1", "str2", "str3"})
|
|
}
|
|
|
|
// issue #269
|
|
{
|
|
called := false
|
|
vm.Set("blah", func(c FunctionCall) Value {
|
|
v, err := c.Argument(0).Export()
|
|
is(err, nil)
|
|
is(v, []int64{3})
|
|
called = true
|
|
return UndefinedValue()
|
|
})
|
|
is(called, false)
|
|
test(`var x = 3; blah([x])`)
|
|
is(called, true)
|
|
}
|
|
})
|
|
}
|
|
|
|
func Test_reflectArray_concat(t *testing.T) {
|
|
tt(t, func() {
|
|
test, vm := test()
|
|
|
|
vm.Set("ghi", []string{"jkl", "mno"})
|
|
vm.Set("pqr", []interface{}{"jkl", 42, 3.14159, true})
|
|
test(`
|
|
var def = {
|
|
"abc": ["abc"],
|
|
"xyz": ["xyz"]
|
|
};
|
|
xyz = pqr.concat(ghi, def.abc, def, def.xyz);
|
|
[ xyz, xyz.length ];
|
|
`, "jkl,42,3.14159,true,jkl,mno,abc,[object Object],xyz,9")
|
|
})
|
|
}
|
|
|
|
func Test_reflectMapInterface(t *testing.T) {
|
|
tt(t, func() {
|
|
test, vm := test()
|
|
|
|
{
|
|
abc := map[string]interface{}{
|
|
"Xyzzy": "Nothing happens.",
|
|
"def": "1",
|
|
"jkl": "jkl",
|
|
}
|
|
vm.Set("abc", abc)
|
|
vm.Set("mno", &abcStruct{})
|
|
|
|
test(`
|
|
abc.xyz = "pqr";
|
|
abc.ghi = {};
|
|
abc.jkl = 3.14159;
|
|
abc.mno = mno;
|
|
mno.Abc = true;
|
|
mno.Ghi = "Something happens.";
|
|
[ abc.Xyzzy, abc.def, abc.ghi, abc.mno ];
|
|
`, "Nothing happens.,1,[object Object],[object Object]")
|
|
|
|
is(abc["xyz"], "pqr")
|
|
is(abc["ghi"], map[string]interface{}{})
|
|
is(abc["jkl"], float64(3.14159))
|
|
mno, valid := abc["mno"].(*abcStruct)
|
|
is(valid, true)
|
|
is(mno.Abc, true)
|
|
is(mno.Ghi, "Something happens.")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPassthrough(t *testing.T) {
|
|
tt(t, func() {
|
|
test, vm := test()
|
|
|
|
{
|
|
abc := &abcStruct{
|
|
Mno: _mnoStruct{
|
|
Ghi: "<Mno.Ghi>",
|
|
},
|
|
}
|
|
vm.Set("abc", abc)
|
|
|
|
test(`
|
|
abc.Mno.Ghi;
|
|
`, "<Mno.Ghi>")
|
|
|
|
vm.Set("pqr", map[string]int8{
|
|
"xyzzy": 0,
|
|
"Nothing happens.": 1,
|
|
})
|
|
|
|
test(`
|
|
abc.Ghi = "abc";
|
|
abc.Pqr = pqr;
|
|
abc.Pqr["Nothing happens."];
|
|
`, 1)
|
|
|
|
mno := _mnoStruct{
|
|
Ghi: "<mno.Ghi>",
|
|
}
|
|
vm.Set("mno", mno)
|
|
|
|
test(`
|
|
abc.Mno = mno;
|
|
abc.Mno.Ghi;
|
|
`, "<mno.Ghi>")
|
|
}
|
|
})
|
|
}
|
|
|
|
type TestDynamicFunctionReturningInterfaceMyStruct1 struct{} //nolint: errname
|
|
|
|
func (m *TestDynamicFunctionReturningInterfaceMyStruct1) Error() string { return "MyStruct1" }
|
|
|
|
type TestDynamicFunctionReturningInterfaceMyStruct2 struct{} //nolint: errname
|
|
|
|
func (m *TestDynamicFunctionReturningInterfaceMyStruct2) Error() string { return "MyStruct2" }
|
|
|
|
func TestDynamicFunctionReturningInterface(t *testing.T) {
|
|
tt(t, func() {
|
|
test, vm := test()
|
|
|
|
var l []func() error
|
|
|
|
vm.Set("r", func(cb func() error) { l = append(l, cb) })
|
|
vm.Set("e1", func() error { return &TestDynamicFunctionReturningInterfaceMyStruct1{} })
|
|
vm.Set("e2", func() error { return &TestDynamicFunctionReturningInterfaceMyStruct2{} })
|
|
vm.Set("e3", func() error { return nil })
|
|
|
|
test("r(function() { return e1(); })", UndefinedValue())
|
|
test("r(function() { return e2(); })", UndefinedValue())
|
|
test("r(function() { return e3(); })", UndefinedValue())
|
|
test("r(function() { return null; })", UndefinedValue())
|
|
|
|
if l[0]() == nil {
|
|
t.Fail()
|
|
}
|
|
if l[1]() == nil {
|
|
t.Fail()
|
|
}
|
|
if l[2]() != nil {
|
|
t.Fail()
|
|
}
|
|
if l[3]() != nil {
|
|
t.Fail()
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestStructCallParameterConversion(t *testing.T) {
|
|
tt(t, func() {
|
|
test, vm := test()
|
|
|
|
type T struct {
|
|
StringValue string `json:"s"`
|
|
BooleanValue bool `json:"b"`
|
|
IntegerValue int `json:"i"`
|
|
}
|
|
|
|
var x T
|
|
|
|
vm.Set("f", func(t T) bool { return t == x })
|
|
|
|
// test set properties
|
|
|
|
x = T{"A", true, 1}
|
|
test("f({s: 'A', b: true, i: 1})", true)
|
|
|
|
// test zero-value properties
|
|
|
|
x = T{"", false, 0}
|
|
test("f({s: '', b: false, i: 0})", true)
|
|
|
|
// test missing properties
|
|
|
|
x = T{"", true, 1}
|
|
test("f({b: true, i: 1})", true)
|
|
|
|
x = T{"A", false, 1}
|
|
test("f({s: 'A', i: 1})", true)
|
|
|
|
x = T{"A", true, 0}
|
|
test("f({s: 'A', b: true})", true)
|
|
|
|
x = T{"", false, 0}
|
|
test("f({})", true)
|
|
|
|
// make sure it fails with extra properties
|
|
|
|
x = T{"", false, 0}
|
|
if _, err := vm.Run("f({x: true})"); err == nil {
|
|
t.Fail()
|
|
}
|
|
})
|
|
}
|
|
|
|
type TestTextUnmarshallerCallParameterConversionMyStruct struct{}
|
|
|
|
func (m *TestTextUnmarshallerCallParameterConversionMyStruct) UnmarshalText(b []byte) error {
|
|
if string(b) == "good" {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("NOT_GOOD: %s", string(b))
|
|
}
|
|
|
|
func TestTextUnmarshallerCallParameterConversion(t *testing.T) {
|
|
tt(t, func() {
|
|
test, vm := test()
|
|
|
|
vm.Set("f", func(t TestTextUnmarshallerCallParameterConversionMyStruct) bool { return true })
|
|
|
|
// success
|
|
test("f('good')", true)
|
|
|
|
// explicit failure, should pass error message up
|
|
if _, err := vm.Run("f('bad')"); err == nil || !strings.Contains(err.Error(), "NOT_GOOD: bad") {
|
|
t.Fail()
|
|
}
|
|
|
|
// wrong input
|
|
if _, err := vm.Run("f(null)"); err == nil {
|
|
t.Fail()
|
|
}
|
|
|
|
// no input
|
|
if _, err := vm.Run("f()"); err == nil {
|
|
t.Fail()
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestJSONRawMessageCallParameterConversion(t *testing.T) {
|
|
for _, e := range []struct {
|
|
c string
|
|
r string
|
|
e bool
|
|
}{
|
|
{"f({a:1})", `{"a":1}`, false},
|
|
{"f(null)", `null`, false},
|
|
{"f(1)", `1`, false},
|
|
{"f('x')", `"x"`, false},
|
|
{"f([1,2,3])", `[1,2,3]`, false},
|
|
{"f(function(){})", `{}`, false},
|
|
{"f()", `[1,2,3]`, true},
|
|
} {
|
|
t.Run(e.c, func(t *testing.T) {
|
|
vm := New()
|
|
err := vm.Set("f", func(m json.RawMessage) json.RawMessage { return m })
|
|
require.NoError(t, err)
|
|
r, err := vm.Run(e.c)
|
|
if err != nil {
|
|
if !e.e {
|
|
t.Error("err should be nil")
|
|
t.Fail()
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if e.e {
|
|
t.Error("err should not be nil")
|
|
t.Fail()
|
|
return
|
|
}
|
|
|
|
v, err := r.Export()
|
|
if err != nil {
|
|
t.Error(err)
|
|
t.Fail()
|
|
}
|
|
|
|
m, ok := v.(json.RawMessage)
|
|
if !ok {
|
|
t.Error("result should be json.RawMessage")
|
|
t.Fail()
|
|
}
|
|
|
|
if !bytes.Equal(m, json.RawMessage(e.r)) {
|
|
t.Errorf("output is wrong\nexpected: %s\nactual: %s\n", e.r, m)
|
|
t.Fail()
|
|
}
|
|
})
|
|
}
|
|
}
|