From 53ce12998e5a940e29d1b86cf7d1740f8e0fa762 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 1 Mar 2019 15:55:29 -0800 Subject: [PATCH] add module-import tests to most of runtime package (#125) --- objects/callable_func.go | 2 +- objects/conversion.go | 2 - runtime/vm_cond_test.go | 1 + runtime/vm_for_in_test.go | 24 ++-- runtime/vm_for_test.go | 14 +++ runtime/vm_inc_dec_test.go | 4 +- runtime/vm_module_test.go | 2 +- runtime/vm_test.go | 238 +++++++++++++++++++------------------ 8 files changed, 155 insertions(+), 132 deletions(-) diff --git a/objects/callable_func.go b/objects/callable_func.go index cf9b43a..ad25e65 100644 --- a/objects/callable_func.go +++ b/objects/callable_func.go @@ -1,4 +1,4 @@ package objects // CallableFunc is a function signature for the callable functions. -type CallableFunc func(args ...Object) (ret Object, err error) +type CallableFunc = func(args ...Object) (ret Object, err error) diff --git a/objects/conversion.go b/objects/conversion.go index f80090a..714f261 100644 --- a/objects/conversion.go +++ b/objects/conversion.go @@ -253,8 +253,6 @@ func FromInterface(v interface{}) (Object, error) { return v, nil case CallableFunc: return &UserFunction{Value: v}, nil - case func(...Object) (Object, error): - return &UserFunction{Value: v}, nil } return nil, fmt.Errorf("cannot convert to object: %T", v) diff --git a/runtime/vm_cond_test.go b/runtime/vm_cond_test.go index 1dd36e5..37edaf0 100644 --- a/runtime/vm_cond_test.go +++ b/runtime/vm_cond_test.go @@ -11,6 +11,7 @@ func TestCondExpr(t *testing.T) { expect(t, `out = (1 == 1) ? false ? 10 - 8 : 1 + 3 : 12 - 2`, 4) expect(t, ` +out = 0 f1 := func() { out += 10 } f2 := func() { out = -out } true ? f1() : f2() diff --git a/runtime/vm_for_in_test.go b/runtime/vm_for_in_test.go index cb69f81..fe8fde4 100644 --- a/runtime/vm_for_in_test.go +++ b/runtime/vm_for_in_test.go @@ -6,20 +6,20 @@ import ( func TestForIn(t *testing.T) { // array - expect(t, `for x in [1, 2, 3] { out += x }`, 6) // value - expect(t, `for i, x in [1, 2, 3] { out += i + x }`, 9) // index, value - expect(t, `func() { for i, x in [1, 2, 3] { out += i + x } }()`, 9) // index, value - expect(t, `for i, _ in [1, 2, 3] { out += i }`, 3) // index, _ - expect(t, `func() { for i, _ in [1, 2, 3] { out += i } }()`, 3) // index, _ + expect(t, `out = 0; for x in [1, 2, 3] { out += x }`, 6) // value + expect(t, `out = 0; for i, x in [1, 2, 3] { out += i + x }`, 9) // index, value + expect(t, `out = 0; func() { for i, x in [1, 2, 3] { out += i + x } }()`, 9) // index, value + expect(t, `out = 0; for i, _ in [1, 2, 3] { out += i }`, 3) // index, _ + expect(t, `out = 0; func() { for i, _ in [1, 2, 3] { out += i } }()`, 3) // index, _ // map - expect(t, `for v in {a:2,b:3,c:4} { out += v }`, 9) // value - expect(t, `for k, v in {a:2,b:3,c:4} { out = k; if v==3 { break } }`, "b") // key, value - expect(t, `for k, _ in {a:2} { out += k }`, "a") // key, _ - expect(t, `for _, v in {a:2,b:3,c:4} { out += v }`, 9) // _, value - expect(t, `func() { for k, v in {a:2,b:3,c:4} { out = k; if v==3 { break } } }()`, "b") // key, value + expect(t, `out = 0; for v in {a:2,b:3,c:4} { out += v }`, 9) // value + expect(t, `out = ""; for k, v in {a:2,b:3,c:4} { out = k; if v==3 { break } }`, "b") // key, value + expect(t, `out = ""; for k, _ in {a:2} { out += k }`, "a") // key, _ + expect(t, `out = 0; for _, v in {a:2,b:3,c:4} { out += v }`, 9) // _, value + expect(t, `out = ""; func() { for k, v in {a:2,b:3,c:4} { out = k; if v==3 { break } } }()`, "b") // key, value // string - expect(t, `for c in "abcde" { out += c }`, "abcde") - expect(t, `for i, c in "abcde" { if i == 2 { continue }; out += c }`, "abde") + expect(t, `out = ""; for c in "abcde" { out += c }`, "abcde") + expect(t, `out = ""; for i, c in "abcde" { if i == 2 { continue }; out += c }`, "abde") } diff --git a/runtime/vm_for_test.go b/runtime/vm_for_test.go index c51e425..134aecc 100644 --- a/runtime/vm_for_test.go +++ b/runtime/vm_for_test.go @@ -6,6 +6,7 @@ import ( func TestFor(t *testing.T) { expect(t, ` + out = 0 for { out++ if out == 5 { @@ -14,6 +15,7 @@ func TestFor(t *testing.T) { }`, 5) expect(t, ` + out = 0 for { out++ if out == 5 { @@ -22,6 +24,7 @@ func TestFor(t *testing.T) { }`, 5) expect(t, ` + out = 0 a := 0 for { a++ @@ -31,6 +34,7 @@ func TestFor(t *testing.T) { }`, 7) // 1 + 2 + 4 expect(t, ` + out = 0 a := 0 for { a++ @@ -40,6 +44,7 @@ func TestFor(t *testing.T) { }`, 12) // 1 + 2 + 4 + 5 expect(t, ` + out = 0 for true { out++ if out == 5 { @@ -58,6 +63,7 @@ func TestFor(t *testing.T) { out = a`, 5) expect(t, ` + out = 0 a := 0 for true { a++ @@ -67,6 +73,7 @@ func TestFor(t *testing.T) { }`, 7) // 1 + 2 + 4 expect(t, ` + out = 0 a := 0 for true { a++ @@ -76,6 +83,7 @@ func TestFor(t *testing.T) { }`, 12) // 1 + 2 + 4 + 5 expect(t, ` + out = 0 func() { for true { out++ @@ -86,11 +94,13 @@ func TestFor(t *testing.T) { }()`, 5) expect(t, ` + out = 0 for a:=1; a<=10; a++ { out += a }`, 55) expect(t, ` + out = 0 for a:=1; a<=3; a++ { for b:=3; b<=6; b++ { out += b @@ -98,6 +108,7 @@ func TestFor(t *testing.T) { }`, 54) expect(t, ` + out = 0 func() { for { out++ @@ -108,6 +119,7 @@ func TestFor(t *testing.T) { }()`, 5) expect(t, ` + out = 0 func() { for true { out++ @@ -199,6 +211,7 @@ func TestFor(t *testing.T) { out = a`, 5) expect(t, ` + out = 0 for a:=1; a<=10; a++ { if a == 3 { continue @@ -210,6 +223,7 @@ func TestFor(t *testing.T) { }`, 12) // 1 + 2 + 4 + 5 expect(t, ` + out = 0 for a:=1; a<=10; { if a == 3 { a++ diff --git a/runtime/vm_inc_dec_test.go b/runtime/vm_inc_dec_test.go index 94fc80a..f01ad99 100644 --- a/runtime/vm_inc_dec_test.go +++ b/runtime/vm_inc_dec_test.go @@ -5,8 +5,8 @@ import ( ) func TestIncDec(t *testing.T) { - expect(t, `out++`, 1) - expect(t, `out--`, -1) + expect(t, `out = 0; out++`, 1) + expect(t, `out = 0; out--`, -1) expect(t, `a := 0; a++; out = a`, 1) expect(t, `a := 0; a++; a--; out = a`, 0) diff --git a/runtime/vm_module_test.go b/runtime/vm_module_test.go index d8fac1d..84eaf06 100644 --- a/runtime/vm_module_test.go +++ b/runtime/vm_module_test.go @@ -193,7 +193,7 @@ export func() { }) // 'export' statement is ignored outside module - expect(t, `a := 5; export func() { a = 10 }(); out = a`, 5) + expectNoMod(t, `a := 5; export func() { a = 10 }(); out = a`, 5) // 'export' must be in the top-level expectErrorWithUserModules(t, `import("mod1")`, map[string]string{ diff --git a/runtime/vm_test.go b/runtime/vm_test.go index c058938..ee097f6 100644 --- a/runtime/vm_test.go +++ b/runtime/vm_test.go @@ -30,68 +30,31 @@ func expect(t *testing.T, input string, expected interface{}) { expectWithUserModules(t, input, expected, nil) } -func expectWithSymbols(t *testing.T, input string, expected interface{}, symbols map[string]objects.Object) { - // parse - file := parse(t, input) - if file == nil { - return - } +func expectNoMod(t *testing.T, input string, expected interface{}) { + runVM(t, input, expected, nil, nil, true) +} - // compiler/VM - runVM(t, file, expected, symbols, nil) +func expectWithSymbols(t *testing.T, input string, expected interface{}, symbols map[string]objects.Object) { + runVM(t, input, expected, symbols, nil, true) } func expectWithUserModules(t *testing.T, input string, expected interface{}, userModules map[string]string) { - // parse - file := parse(t, input) - if file == nil { - return - } - - // compiler/VM - runVM(t, file, expected, nil, userModules) + runVM(t, input, expected, nil, userModules, false) } func expectError(t *testing.T, input, expected string) { - expected = strings.TrimSpace(expected) - if expected == "" { - panic("expected must not be empty") - } - - expectErrorWithUserModules(t, input, nil, expected) + runVMError(t, input, nil, nil, expected) } func expectErrorWithUserModules(t *testing.T, input string, userModules map[string]string, expected string) { - // parse - program := parse(t, input) - if program == nil { - return - } - - // compiler/VM - _, trace, err := traceCompileRun(program, nil, userModules) - if !assert.Error(t, err) || - !assert.True(t, strings.Contains(err.Error(), expected), "expected error string: %s, got: %s", expected, err.Error()) { - t.Log("\n" + strings.Join(trace, "\n")) - } + runVMError(t, input, nil, userModules, expected) } func expectErrorWithSymbols(t *testing.T, input string, symbols map[string]objects.Object, expected string) { - // parse - program := parse(t, input) - if program == nil { - return - } - - // compiler/VM - _, trace, err := traceCompileRun(program, symbols, nil) - if !assert.Error(t, err) || - !assert.True(t, strings.Contains(err.Error(), expected), "expected error string: %s, got: %s", expected, err.Error()) { - t.Log("\n" + strings.Join(trace, "\n")) - } + runVMError(t, input, symbols, nil, expected) } -func runVM(t *testing.T, file *ast.File, expected interface{}, symbols map[string]objects.Object, userModules map[string]string) (ok bool) { +func runVM(t *testing.T, input string, expected interface{}, symbols map[string]objects.Object, userModules map[string]string, skipModuleTest bool) { expectedObj := toObject(expected) if symbols == nil { @@ -99,81 +62,68 @@ func runVM(t *testing.T, file *ast.File, expected interface{}, symbols map[strin } symbols[testOut] = objectZeroCopy(expectedObj) - res, trace, err := traceCompileRun(file, symbols, userModules) + // first pass: run the code normally + { + // parse + file := parse(t, input) + if file == nil { + return + } - defer func() { - if !ok { + // compiler/VM + res, trace, err := traceCompileRun(file, symbols, userModules) + if !assert.NoError(t, err) || + !assert.Equal(t, expectedObj, res[testOut]) { t.Log("\n" + strings.Join(trace, "\n")) } - }() + } - if !assert.NoError(t, err) { + // second pass: run the code as import module + if !skipModuleTest { + file := parse(t, `out = import("__code__")`) + if file == nil { + return + } + + expectedObj := toObject(expected) + switch eo := expectedObj.(type) { + case *objects.Array: + expectedObj = &objects.ImmutableArray{Value: eo.Value} + case *objects.Map: + expectedObj = &objects.ImmutableMap{Value: eo.Value} + } + + if userModules == nil { + userModules = make(map[string]string) + } + userModules["__code__"] = fmt.Sprintf("out := undefined; %s; export out", input) + + res, trace, err := traceCompileRun(file, symbols, userModules) + if !assert.NoError(t, err) || + !assert.Equal(t, expectedObj, res[testOut]) { + t.Log("\n" + strings.Join(trace, "\n")) + } + } +} + +func runVMError(t *testing.T, input string, symbols map[string]objects.Object, userModules map[string]string, expected string) { + expected = strings.TrimSpace(expected) + if expected == "" { + panic("expected must not be empty") + } + + // parse + program := parse(t, input) + if program == nil { return } - ok = assert.Equal(t, expectedObj, res[testOut]) - - return -} - -func errorObject(v interface{}) *objects.Error { - return &objects.Error{Value: toObject(v)} -} - -func toObject(v interface{}) objects.Object { - switch v := v.(type) { - case objects.Object: - return v - case string: - return &objects.String{Value: v} - case int64: - return &objects.Int{Value: v} - case int: // for convenience - return &objects.Int{Value: int64(v)} - case bool: - if v { - return objects.TrueValue - } - return objects.FalseValue - case rune: - return &objects.Char{Value: v} - case byte: // for convenience - return &objects.Char{Value: rune(v)} - case float64: - return &objects.Float{Value: v} - case []byte: - return &objects.Bytes{Value: v} - case MAP: - objs := make(map[string]objects.Object) - for k, v := range v { - objs[k] = toObject(v) - } - - return &objects.Map{Value: objs} - case ARR: - var objs []objects.Object - for _, e := range v { - objs = append(objs, toObject(e)) - } - - return &objects.Array{Value: objs} - case IMAP: - objs := make(map[string]objects.Object) - for k, v := range v { - objs[k] = toObject(v) - } - - return &objects.ImmutableMap{Value: objs} - case IARR: - var objs []objects.Object - for _, e := range v { - objs = append(objs, toObject(e)) - } - - return &objects.ImmutableArray{Value: objs} + // compiler/VM + _, trace, err := traceCompileRun(program, symbols, userModules) + if !assert.Error(t, err) || + !assert.True(t, strings.Contains(err.Error(), expected), "expected error string: %s, got: %s", expected, err.Error()) { + t.Log("\n" + strings.Join(trace, "\n")) } - - panic(fmt.Errorf("unknown type: %T", v)) } type tracer struct { @@ -296,6 +246,66 @@ func parse(t *testing.T, input string) *ast.File { return file } +func errorObject(v interface{}) *objects.Error { + return &objects.Error{Value: toObject(v)} +} + +func toObject(v interface{}) objects.Object { + switch v := v.(type) { + case objects.Object: + return v + case string: + return &objects.String{Value: v} + case int64: + return &objects.Int{Value: v} + case int: // for convenience + return &objects.Int{Value: int64(v)} + case bool: + if v { + return objects.TrueValue + } + return objects.FalseValue + case rune: + return &objects.Char{Value: v} + case byte: // for convenience + return &objects.Char{Value: rune(v)} + case float64: + return &objects.Float{Value: v} + case []byte: + return &objects.Bytes{Value: v} + case MAP: + objs := make(map[string]objects.Object) + for k, v := range v { + objs[k] = toObject(v) + } + + return &objects.Map{Value: objs} + case ARR: + var objs []objects.Object + for _, e := range v { + objs = append(objs, toObject(e)) + } + + return &objects.Array{Value: objs} + case IMAP: + objs := make(map[string]objects.Object) + for k, v := range v { + objs[k] = toObject(v) + } + + return &objects.ImmutableMap{Value: objs} + case IARR: + var objs []objects.Object + for _, e := range v { + objs = append(objs, toObject(e)) + } + + return &objects.ImmutableArray{Value: objs} + } + + panic(fmt.Errorf("unknown type: %T", v)) +} + func objectZeroCopy(o objects.Object) objects.Object { switch o.(type) { case *objects.Int: