1
0
mirror of https://github.com/robertkrimen/otto synced 2025-10-12 20:27:30 +08:00
otto/reflect_test.go
Steven Hartland 7009038f79
fix: linting errors (#441)
Disable new linters which aren't compatible with this code module.

Upgrade github actions to fix caching issues.

Run go mod to bring in new styling.

Remove space on nolint declarations.

Apply all changes to whitespace as required to pass goimports linter.

Only trigger checks on pull_request which works for pulls from other
forks, where as push only works from the same repo.
2022-10-08 00:12:19 +01:00

938 lines
18 KiB
Go

package otto
import (
"bytes"
"encoding/json"
"fmt"
"math"
"reflect"
"strings"
"testing"
)
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() {
return
}
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 (abs _abcStruct) Func1Int(i int) int {
return i + 1
}
func (abs _abcStruct) Func1Int8(i int8) int8 {
return i + 1
}
func (abs _abcStruct) Func1Int16(i int16) int16 {
return i + 1
}
func (abs _abcStruct) Func1Int32(i int32) int32 {
return i + 1
}
func (abs _abcStruct) Func1Int64(i int64) int64 {
return i + 1
}
func (abs _abcStruct) Func1Uint(i uint) uint {
return i + 1
}
func (abs _abcStruct) Func1Uint8(i uint8) uint8 {
return i + 1
}
func (abs _abcStruct) Func1Uint16(i uint16) uint16 {
return i + 1
}
func (abs _abcStruct) Func1Uint32(i uint32) uint32 {
return i + 1
}
func (abs _abcStruct) Func1Uint64(i uint64) uint64 {
return i + 1
}
func (abs _abcStruct) Func2Int(i, j int) int {
return i + j
}
func (abs _abcStruct) Func2StringInt(s string, i int) string {
return fmt.Sprintf("%v:%v", s, i)
}
func (abs _abcStruct) Func1IntVariadic(a ...int) int {
t := 0
for _, i := range a {
t += i
}
return t
}
func (abs _abcStruct) Func2IntVariadic(s string, a ...int) string {
t := 0
for _, i := range a {
t += i
}
return fmt.Sprintf("%v:%v", s, t)
}
func (abs _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) {
if true {
return
}
tt(t, func() {
// Testing dbgf
// These should panic
toValue("Xyzzy").toReflectValue(reflect.Ptr)
stringToReflectValue("Xyzzy", reflect.Ptr)
})
}
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"], "[object Object]")
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 TestDynamicFunctionReturningInterface_MyStruct1 struct{} //nolint: errname
func (m *TestDynamicFunctionReturningInterface_MyStruct1) Error() string { return "MyStruct1" }
type TestDynamicFunctionReturningInterface_MyStruct2 struct{} //nolint: errname
func (m *TestDynamicFunctionReturningInterface_MyStruct2) 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 &TestDynamicFunctionReturningInterface_MyStruct1{} })
vm.Set("e2", func() error { return &TestDynamicFunctionReturningInterface_MyStruct2{} })
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 TestTextUnmarshallerCallParameterConversion_MyStruct struct{}
func (m *TestTextUnmarshallerCallParameterConversion_MyStruct) 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 TestTextUnmarshallerCallParameterConversion_MyStruct) 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()
vm.Set("f", func(m json.RawMessage) json.RawMessage { return m })
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()
}
})
}
}