From e57bdb7e4ebb97f322db40a20129027f997a1bef Mon Sep 17 00:00:00 2001 From: Alex Palaistras Date: Sun, 24 Jan 2016 22:06:25 +0000 Subject: [PATCH] Add seperate testsuites for value and receiver This completes work on seperating tests and brings test coverage to the ~90%. More corner cases will be covered as they arise. --- engine/context_test.go | 44 ++--- engine/engine_test.go | 43 ++++- engine/receiver.go | 16 +- engine/receiver_test.go | 133 ++++++++++++++ engine/value.go | 17 +- engine/value_test.go | 393 +++++++++++++++++++++++++++++++++++++++ php_test.go | 395 ---------------------------------------- 7 files changed, 603 insertions(+), 438 deletions(-) create mode 100644 engine/receiver_test.go create mode 100644 engine/value_test.go delete mode 100644 php_test.go diff --git a/engine/context_test.go b/engine/context_test.go index d367455..e81562c 100644 --- a/engine/context_test.go +++ b/engine/context_test.go @@ -7,33 +7,18 @@ package engine import ( "bytes" "fmt" - "io/ioutil" "net/http" - "os" "reflect" "testing" ) -func writeTempScript(name, script string) (*os.File, error) { - file, err := ioutil.TempFile("", name) - if err != nil { - return nil, err - } - - if _, err := file.WriteString(script); err != nil { - file.Close() - os.Remove(file.Name()) - - return nil, err - } - - return file, nil +func TestContextStart(t *testing.T) { + e, _ = New() + t.SkipNow() } func TestContextNew(t *testing.T) { - e, _ = New() c, err := NewContext() - if err != nil { t.Fatalf("NewContext(): %s", err) } @@ -69,13 +54,13 @@ func TestContextExec(t *testing.T) { c.Output = &w for _, tt := range execTests { - file, err := writeTempScript(tt.name, tt.script) + script, err := NewScript(tt.name, tt.script) if err != nil { t.Errorf("Could not create temporary file for testing: %s", tt.name, err) continue } - if err := c.Exec(file.Name()); err != nil { + if err := c.Exec(script.Name()); err != nil { t.Errorf("Context.Exec('%s'): Execution failed: %s", tt.name, err) continue } @@ -87,8 +72,7 @@ func TestContextExec(t *testing.T) { t.Errorf("Context.Exec('%s'): Expected `%s', actual `%s'", tt.name, tt.expected, actual) } - file.Close() - os.Remove(file.Name()) + script.Remove() } c.Destroy() @@ -276,7 +260,7 @@ func TestContextBind(t *testing.T) { c, _ := NewContext() c.Output = &w - file, err := writeTempScript("evaltest.php", ` 1 { t := make([]interface{}, len(ret)) - for _, v := range ret { - t = append(t, v.Interface()) + for i, v := range ret { + t[i] = v.Interface() } result = t diff --git a/engine/receiver_test.go b/engine/receiver_test.go new file mode 100644 index 0000000..92dbfd2 --- /dev/null +++ b/engine/receiver_test.go @@ -0,0 +1,133 @@ +// Copyright 2016 Alexander Palaistras. All rights reserved. +// Use of this source code is governed by the MIT license that can be found in +// the LICENSE file. + +package engine + +import ( + "bytes" + "testing" +) + +func TestReceiverStart(t *testing.T) { + e, _ = New() + t.SkipNow() +} + +type testReceiver struct { + Var string + hidden int64 +} + +func (t *testReceiver) Ignore() { +} + +func (t *testReceiver) Hello(p string) string { + return "Hello " + p +} + +func (t *testReceiver) Goodbye(p string) (string, string) { + return "Goodbye", p +} + +func (t *testReceiver) invalid() string { + return "I'm afraid I can't let you do that, Dave" +} + +func newTestReceiver(args []interface{}) interface{} { + value := "Foo" + + if len(args) > 0 { + switch v := args[0].(type) { + case bool: + return nil + case string: + value = v + } + } + + return &testReceiver{Var: value, hidden: 42} +} + +var newReceiverTests = []struct { + script string + expected string +}{ + {"$t = new TestReceiver; echo is_object($t);", "1"}, + {`try { + $t = new TestReceiver(false); + } catch (Exception $e) { + echo $e->getMessage(); + }`, "Failed to instantiate method receiver"}, + + {"$t = new TestReceiver; echo $t->Var;", "Foo"}, + {"$t = new TestReceiver; echo $t->hidden;", ""}, + {"$t = new TestReceiver('wow'); echo $t->Var;", "wow"}, + + {"$t = new TestReceiver; $t->Var = 'Bar'; echo $t->Var;", "Bar"}, + {"$t = new TestReceiver; $t->hello = 'wow'; echo $t->hello;", ""}, + + {"$t = new TestReceiver; echo $t->Ignore();", ""}, + {"$t = new TestReceiver; echo $t->Hello('World');", "Hello World"}, + {"$t = new TestReceiver; echo json_encode($t->Goodbye('Doge'));", `["Goodbye","Doge"]`}, + {"$t = new TestReceiver; echo $t->invalid();", ""}, + + {"$t = new TestReceiver; echo ($t->Var) ? 1 : 0;", "1"}, + {"$t = new TestReceiver; echo isset($t->Var) ? 1 : 0;", "1"}, + {"$t = new TestReceiver; echo empty($t->Var) ? 1 : 0;", "0"}, + + {"$t = new TestReceiver; echo isset($t->hidden) ? 1 : 0;", "0"}, +} + +func TestNewReceiver(t *testing.T) { + var w bytes.Buffer + + c, _ := NewContext() + c.Output = &w + + r, err := NewReceiver("TestReceiver", newTestReceiver) + if err != nil { + t.Fatalf("NewReceiver(): Failed to define method receiver: %s", err) + } + + for _, tt := range newReceiverTests { + _, err := c.Eval(tt.script) + if err != nil { + t.Errorf("Context.Eval('%s'): %s", tt.script, err) + continue + } + + actual := w.String() + w.Reset() + + if actual != tt.expected { + t.Errorf("Context.Eval('%s'): Expected output '%s', actual '%s'", tt.script, tt.expected, actual) + } + } + + r.Destroy() + c.Destroy() +} + +func TestReceiverDestroy(t *testing.T) { + c, _ := NewContext() + defer c.Destroy() + + r, err := NewReceiver("TestReceiver", newTestReceiver) + if err != nil { + t.Fatalf("NewReceiver(): Failed to define method receiver: %s", err) + } + + r.Destroy() + if r.create != nil || r.objects != nil { + t.Errorf("Receiver.Destroy(): Did not set internal fields to `nil`") + } + + // Attempting to destroy a receiver twice should be a no-op. + r.Destroy() +} + +func TestReceiverEnd(t *testing.T) { + e.Destroy() + t.SkipNow() +} diff --git a/engine/value.go b/engine/value.go index 9dbf53a..0879bb1 100644 --- a/engine/value.go +++ b/engine/value.go @@ -20,10 +20,6 @@ import ( "unsafe" ) -var errInvalidType = func(v interface{}) error { - return fmt.Errorf("Unable to create value of unknown type '%T'", v) -} - // ValueKind represents the specific kind of type represented in Value. type ValueKind int @@ -62,7 +58,7 @@ type Value struct { func NewValue(val interface{}) (*Value, error) { ptr, err := C.value_new() if err != nil { - return nil, fmt.Errorf("Unable to create PHP value from Go value '%v'", val) + return nil, fmt.Errorf("Unable to instantiate PHP value") } v := reflect.ValueOf(val) @@ -70,10 +66,10 @@ func NewValue(val interface{}) (*Value, error) { // Determine interface value type and create PHP value from the concrete type. switch v.Kind() { // Bind integer to PHP int type. - case reflect.Int: + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: C.value_set_long(ptr, C.long(v.Int())) // Bind floating point number to PHP double type. - case reflect.Float64: + case reflect.Float32, reflect.Float64: C.value_set_double(ptr, C.double(v.Float())) // Bind boolean to PHP bool type. case reflect.Bool: @@ -121,7 +117,7 @@ func NewValue(val interface{}) (*Value, error) { } } } else { - return nil, errInvalidType(val) + return nil, fmt.Errorf("Unable to create value of unknown type '%T'", val) } // Bind struct to PHP object (stdClass) type. case reflect.Struct: @@ -145,8 +141,11 @@ func NewValue(val interface{}) (*Value, error) { C.value_object_property_set(ptr, str, fv.value) } + case reflect.Invalid: + C.value_set_null(ptr) default: - return nil, errInvalidType(val) + C.value_destroy(ptr) + return nil, fmt.Errorf("Unable to create value of unknown type '%T'", val) } return &Value{value: ptr}, nil diff --git a/engine/value_test.go b/engine/value_test.go new file mode 100644 index 0000000..e0cbddf --- /dev/null +++ b/engine/value_test.go @@ -0,0 +1,393 @@ +// Copyright 2016 Alexander Palaistras. All rights reserved. +// Use of this source code is governed by the MIT license that can be found in +// the LICENSE file. + +package engine + +import ( + "reflect" + "testing" +) + +func TestValueStart(t *testing.T) { + e, _ = New() + t.SkipNow() +} + +var valueNewTests = []struct { + value interface{} + expected interface{} +}{ + {nil, nil}, + {42, int64(42)}, + {3.14159, float64(3.14159)}, + {true, true}, + {"Hello World", "Hello World"}, + {[]string{"Knick", "Knack"}, []interface{}{"Knick", "Knack"}}, + {[][]string{{"1", "2"}, {"3"}}, []interface{}{[]interface{}{"1", "2"}, []interface{}{"3"}}}, + {map[string]int{"biggs": 23, "wedge": 16}, map[string]interface{}{"biggs": int64(23), "wedge": int64(16)}}, + {map[int]string{10: "this", 20: "that"}, map[string]interface{}{"10": "this", "20": "that"}}, + {struct { + I int + S string + B bool + h string + }{66, "wow", true, "hidden"}, map[string]interface{}{"I": int64(66), "S": "wow", "B": true}}, +} + +func TestValueNew(t *testing.T) { + c, _ := NewContext() + + for _, tt := range valueNewTests { + val, err := NewValue(tt.value) + if err != nil { + t.Errorf("NewValue('%v'): %s", tt.value, err) + continue + } + + if val == nil { + t.Errorf("NewValue('%v'): No error returned but value is `nil`", tt.value) + continue + } + + actual := val.Interface() + + if reflect.DeepEqual(actual, tt.expected) == false { + t.Errorf("NewValue('%v'): expected '%#v', actual '%#v'", tt.value, tt.expected, actual) + } + + val.Destroy() + } + + c.Destroy() +} + +var valueNewInvalidTests = []interface{}{ + uint(10), + make(chan int), + func() {}, + []interface{}{uint(2)}, + map[string]interface{}{"t": make(chan bool)}, + map[bool]interface{}{false: true}, + struct { + T interface{} + }{func() {}}, +} + +func TestValueNewInvalid(t *testing.T) { + c, _ := NewContext() + + for _, value := range valueNewInvalidTests { + val, err := NewValue(value) + if err == nil { + val.Destroy() + t.Errorf("NewValue('%v'): Value is invalid but no error occured", value) + } + } + + c.Destroy() +} + +var valueKindTests = []struct { + value interface{} + expected ValueKind +}{ + {42, Long}, + {3.14159, Double}, + {true, Bool}, + {"Hello World", String}, + {[]string{"Knick", "Knack"}, Array}, + {map[string]int{"t": 1, "c": 2}, Map}, + {struct { + I int + S string + }{66, "wow"}, Object}, +} + +func TestValueKind(t *testing.T) { + c, _ := NewContext() + + for _, tt := range valueKindTests { + val, err := NewValue(tt.value) + if err != nil { + t.Errorf("NewValue('%v'): %s", tt.value, err) + continue + } + + actual := val.Kind() + + if actual != tt.expected { + t.Errorf("Value.Kind('%v'): expected '%#v', actual '%#v'", tt.value, tt.expected, actual) + } + + val.Destroy() + } + + c.Destroy() +} + +var valueIntTests = []struct { + value interface{} + expected int64 +}{ + {42, int64(42)}, + {3.14159, int64(3)}, + {true, int64(1)}, + {"Hello World", int64(0)}, + {[]string{"Knick", "Knack"}, int64(1)}, + {map[string]int{"t": 1, "c": 2}, int64(1)}, + {struct { + I int + S string + }{66, "wow"}, int64(1)}, +} + +func TestValueInt(t *testing.T) { + c, _ := NewContext() + + for _, tt := range valueIntTests { + val, err := NewValue(tt.value) + if err != nil { + t.Errorf("NewValue('%v'): %s", tt.value, err) + continue + } + + actual := val.Int() + + if reflect.DeepEqual(actual, tt.expected) == false { + t.Errorf("Value.Int('%v'): expected '%#v', actual '%#v'", tt.value, tt.expected, actual) + } + + val.Destroy() + } + + c.Destroy() +} + +var valueFloatTests = []struct { + value interface{} + expected float64 +}{ + {42, float64(42)}, + {3.14159, float64(3.14159)}, + {true, float64(1)}, + {"Hello World", float64(0)}, + {[]string{"Knick", "Knack"}, float64(1)}, + {map[string]int{"t": 1, "c": 2}, float64(1)}, + {struct { + I int + S string + }{66, "wow"}, float64(1)}, +} + +func TestValueFloat(t *testing.T) { + c, _ := NewContext() + + for _, tt := range valueFloatTests { + val, err := NewValue(tt.value) + if err != nil { + t.Errorf("NewValue('%v'): %s", tt.value, err) + continue + } + + actual := val.Float() + + if reflect.DeepEqual(actual, tt.expected) == false { + t.Errorf("Value.Float('%v'): expected '%#v', actual '%#v'", tt.value, tt.expected, actual) + } + + val.Destroy() + } + + c.Destroy() +} + +var valueBoolTests = []struct { + value interface{} + expected bool +}{ + {42, true}, + {3.14159, true}, + {true, true}, + {"Hello World", true}, + {[]string{"Knick", "Knack"}, true}, + {map[string]int{"t": 1, "c": 2}, true}, + {struct { + I int + S string + }{66, "wow"}, true}, +} + +func TestValueBool(t *testing.T) { + c, _ := NewContext() + + for _, tt := range valueBoolTests { + val, err := NewValue(tt.value) + if err != nil { + t.Errorf("NewValue('%v'): %s", tt.value, err) + continue + } + + actual := val.Bool() + + if reflect.DeepEqual(actual, tt.expected) == false { + t.Errorf("Value.Bool('%v'): expected '%#v', actual '%#v'", tt.value, tt.expected, actual) + } + + val.Destroy() + } + + c.Destroy() +} + +var valueStringTests = []struct { + value interface{} + expected string +}{ + {42, "42"}, + {3.14159, "3.14159"}, + {true, "1"}, + {"Hello World", "Hello World"}, + {[]string{"Knick", "Knack"}, "Array"}, + {map[string]int{"t": 1, "c": 2}, "Array"}, + {struct { + I int + S string + }{66, "wow"}, ""}, +} + +func TestValueString(t *testing.T) { + c, _ := NewContext() + + for _, tt := range valueStringTests { + val, err := NewValue(tt.value) + if err != nil { + t.Errorf("NewValue('%v'): %s", tt.value, err) + continue + } + + actual := val.String() + + if reflect.DeepEqual(actual, tt.expected) == false { + t.Errorf("Value.String('%v'): expected '%#v', actual '%#v'", tt.value, tt.expected, actual) + } + + val.Destroy() + } + + c.Destroy() +} + +var valueSliceTests = []struct { + value interface{} + expected interface{} +}{ + {42, []interface{}{int64(42)}}, + {3.14159, []interface{}{float64(3.14159)}}, + {true, []interface{}{true}}, + {"Hello World", []interface{}{"Hello World"}}, + {[]string{"Knick", "Knack"}, []interface{}{"Knick", "Knack"}}, + {map[string]int{"t": 1, "c": 2}, []interface{}{int64(1), int64(2)}}, + {struct { + I int + S string + }{66, "wow"}, []interface{}{int64(66), "wow"}}, +} + +func TestValueSlice(t *testing.T) { + c, _ := NewContext() + + for _, tt := range valueSliceTests { + val, err := NewValue(tt.value) + if err != nil { + t.Errorf("NewValue('%v'): %s", tt.value, err) + continue + } + + actual := val.Slice() + + if reflect.DeepEqual(actual, tt.expected) == false { + t.Errorf("Value.Slice('%v'): expected '%#v', actual '%#v'", tt.value, tt.expected, actual) + } + + val.Destroy() + } + + c.Destroy() +} + +var valueMapTests = []struct { + value interface{} + expected map[string]interface{} +}{ + {42, map[string]interface{}{"0": int64(42)}}, + {3.14159, map[string]interface{}{"0": float64(3.14159)}}, + {true, map[string]interface{}{"0": true}}, + {"Hello World", map[string]interface{}{"0": "Hello World"}}, + {[]string{"Knick", "Knack"}, map[string]interface{}{"0": "Knick", "1": "Knack"}}, + {map[string]int{"t": 1, "c": 2}, map[string]interface{}{"t": int64(1), "c": int64(2)}}, + {struct { + I int + S string + }{66, "wow"}, map[string]interface{}{"I": int64(66), "S": "wow"}}, +} + +func TestValueMap(t *testing.T) { + c, _ := NewContext() + + for _, tt := range valueMapTests { + val, err := NewValue(tt.value) + if err != nil { + t.Errorf("NewValue('%v'): %s", tt.value, err) + continue + } + + actual := val.Map() + + if reflect.DeepEqual(actual, tt.expected) == false { + t.Errorf("Value.Map('%v'): expected '%#v', actual '%#v'", tt.value, tt.expected, actual) + } + + val.Destroy() + } + + c.Destroy() +} + +func TestValuePtr(t *testing.T) { + c, _ := NewContext() + defer c.Destroy() + + val, err := NewValue(42) + if err != nil { + t.Fatalf("NewValue('%v'): %s", 42, err) + } + + if val.Ptr() == nil { + t.Errorf("Value.Ptr('%v'): Unable to create pointer from value") + } +} + +func TestValueDestroy(t *testing.T) { + c, _ := NewContext() + defer c.Destroy() + + val, err := NewValue(42) + if err != nil { + t.Fatalf("NewValue('%v'): %s", 42, err) + } + + val.Destroy() + + if val.value != nil { + t.Errorf("Value.Destroy(): Did not set internal fields to `nil`") + } + + // Attempting to destroy a value twice should be a no-op. + val.Destroy() +} + +func TestValueEnd(t *testing.T) { + e.Destroy() + t.SkipNow() +} diff --git a/php_test.go b/php_test.go deleted file mode 100644 index e261b33..0000000 --- a/php_test.go +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright 2016 Alexander Palaistras. All rights reserved. -// Use of this source code is governed by the MIT license that can be found in -// the LICENSE file. - -package php - -import ( - "bytes" - "fmt" - "os" - "path" - "reflect" - "strconv" - "testing" -) - -var testDir string - -func TestNewEngineContext(t *testing.T) { - e, err := New() - if err != nil { - t.Errorf("New(): %s", err) - return - } - - _, err = e.NewContext() - if err != nil { - t.Errorf("NewContext(): %s", err) - } - - e.Destroy() -} - -var execTests = []struct { - file string // Filename to run - expected string // Expected output -}{ - {"echo.php", "Hello World"}, -} - -func TestContextExec(t *testing.T) { - var w bytes.Buffer - - e, _ := New() - - ctx, _ := e.NewContext() - ctx.Output = &w - - for _, tt := range execTests { - file := path.Join(testDir, tt.file) - if err := ctx.Exec(file); err != nil { - t.Errorf("Context.Exec(%s): %s", tt.file, err) - continue - } - - actual := w.String() - w.Reset() - - if actual != tt.expected { - t.Errorf("Context.Exec(%s): expected '%s', actual '%s'", tt.file, tt.expected, actual) - } - } - - e.Destroy() -} - -var evalTests = []struct { - script string // Script to run - expected string // Expected output -}{ - {"echo 'Hello World';", "Hello World"}, - {"$i = 10; $d = 20; echo $i + $d;", "30"}, -} - -func TestContextEval(t *testing.T) { - var w bytes.Buffer - - e, _ := New() - - ctx, _ := e.NewContext() - ctx.Output = &w - - for _, tt := range evalTests { - if _, err := ctx.Eval(tt.script); err != nil { - t.Errorf("Context.Eval(%s): %s", tt.script, err) - continue - } - - actual := w.String() - w.Reset() - - if actual != tt.expected { - t.Errorf("Context.Eval(%s): expected '%s', actual '%s'", tt.script, tt.expected, actual) - } - } - - e.Destroy() -} - -type TestEngineReceiver struct { - Var string -} - -func (t *TestEngineReceiver) Test(p string) string { - return "Hello " + p -} - -var defineTests = []struct { - script string // Script to run - expected string // Expected output -}{ - {"$t = new TestEngineReceiver; echo is_object($t);", "1"}, - {"$t = new TestEngineReceiver; echo $t->Var;", "hello"}, - {"$t = new TestEngineReceiver; $t->Var = 'world'; echo $t->Var;", "world"}, - {"$t = new TestEngineReceiver; echo $t->Test('World');", "Hello World"}, - {"$t = new TestEngineReceiver; echo ($t->Var) ? 1 : 0;", "1"}, - {"$t = new TestEngineReceiver; echo isset($t->Var) ? 1 : 0;", "1"}, - {"$t = new TestEngineReceiver; echo empty($t->Var) ? 1 : 0;", "0"}, -} - -func TestEngineDefine(t *testing.T) { - var w bytes.Buffer - var ctor = func(args []interface{}) interface{} { - return &TestEngineReceiver{Var: "hello"} - } - - e, _ := New() - if err := e.Define("TestEngineReceiver", ctor); err != nil { - t.Errorf("Engine.Define(%s): %s", ctor, err) - } - - ctx, _ := e.NewContext() - ctx.Output = &w - - for _, tt := range defineTests { - if _, err := ctx.Eval(tt.script); err != nil { - t.Errorf("Context.Eval(%s): %s", tt.script, err) - continue - } - - actual := w.String() - w.Reset() - - if actual != tt.expected { - t.Errorf("Context.Eval(%s): expected '%s', actual '%s'", tt.script, tt.expected, actual) - } - } - - e.Destroy() -} - -var headerTests = []struct { - script string // Script to run - expected string // Expected output -}{ - {"header('X-Testing: Hello');", `http.Header{"X-Testing":[]string{"Hello"}}`}, - {"header('X-Testing: World', false);", `http.Header{"X-Testing":[]string{"Hello", "World"}}`}, - {"header_remove('X-Testing');", `http.Header{}`}, - {"header('X-Testing: Done', false);", `http.Header{"X-Testing":[]string{"Done"}}`}, -} - -func TestContextHeader(t *testing.T) { - e, _ := New() - ctx, _ := e.NewContext() - - for _, tt := range headerTests { - if _, err := ctx.Eval(tt.script); err != nil { - t.Errorf("Context.Header(%s): %s", tt.script, err) - continue - } - - actual := fmt.Sprintf("%#v", ctx.Header) - - if actual != tt.expected { - t.Errorf("Context.Header(%s): expected '%s', actual '%s'", tt.script, tt.expected, actual) - } - } - - e.Destroy() -} - -var logTests = []struct { - script string // Script to run - expected string // Expected output -}{ - {"$a = 10; $a + $b;", "PHP Notice: Undefined variable: b in gophp-engine on line 1"}, - {"strlen();", "PHP Warning: strlen() expects exactly 1 parameter, 0 given in gophp-engine on line 1"}, - {"trigger_error('Test Error');", "PHP Notice: Test Error in gophp-engine on line 1"}, -} - -func TestContextLog(t *testing.T) { - var w bytes.Buffer - - e, _ := New() - - ctx, _ := e.NewContext() - ctx.Log = &w - - for _, tt := range logTests { - if _, err := ctx.Eval(tt.script); err != nil { - t.Errorf("Context.Eval(%s): %s", tt.script, err) - continue - } - - actual := w.String() - w.Reset() - - if actual != tt.expected { - t.Errorf("Context.Eval(%s): expected '%s', actual '%s'", tt.script, tt.expected, actual) - } - } - - e.Destroy() -} - -var bindTests = []struct { - value interface{} // Value to bind - expected string // Serialized form of value -}{ - // Integer to integer. - {42, "i:42;"}, - // Float to double. - {3.14159, "d:3.1415899999999999;"}, - // Boolean to boolean. - {true, "b:1;"}, - // String to string. - {"Such bind", `s:9:"Such bind";`}, - // Simple slice of strings to indexed array. - {[]string{"this", "that"}, `a:2:{i:0;s:4:"this";i:1;s:4:"that";}`}, - // Nested slice of integers to indexed array. - {[][]int{{1, 2}, {3}}, `a:2:{i:0;a:2:{i:0;i:1;i:1;i:2;}i:1;a:1:{i:0;i:3;}}`}, - // Struct to object, with nested struct. - {struct { - I int - C string - F struct { - G bool - } - h bool - }{3, "test", struct { - G bool - }{false}, true}, `O:8:"stdClass":3:{s:1:"I";i:3;s:1:"C";s:4:"test";s:1:"F";O:8:"stdClass":1:{s:1:"G";b:0;}}`}, -} - -func TestContextBind(t *testing.T) { - var w bytes.Buffer - - e, _ := New() - - ctx, _ := e.NewContext() - ctx.Output = &w - - for i, tt := range bindTests { - if err := ctx.Bind(strconv.FormatInt(int64(i), 10), tt.value); err != nil { - t.Errorf("Context.Bind(%v): %s", tt.value, err) - continue - } - - ctx.Exec(path.Join(testDir, "bind.php")) - - actual := w.String() - w.Reset() - - if actual != tt.expected { - t.Errorf("Context.Bind(%v): expected '%s', actual '%s'", tt.value, tt.expected, actual) - } - } - - e.Destroy() -} - -var reverseBindTests = []struct { - script string // Script to run - expected []interface{} // Expected value -}{ - {"return 'Hello World';", []interface{}{ - "Hello World", - int64(0), - float64(0), - true, - "Hello World", - []interface{}{"Hello World"}, - map[string]interface{}{"0": "Hello World"}, - }}, - {"$i = 10; $d = 20; return $i + $d;", []interface{}{ - int64(30), - int64(30), - float64(30), - true, - "30", - []interface{}{int64(30)}, - map[string]interface{}{"0": int64(30)}, - }}, - {"$i = 1.2; $d = 2.4; return $i + $d;", []interface{}{ - float64(3.5999999999999996), - int64(3), - float64(3.5999999999999996), - true, - "3.6", - []interface{}{float64(3.5999999999999996)}, - map[string]interface{}{"0": float64(3.5999999999999996)}, - }}, - {"$what = true; return $what;", []interface{}{ - true, - int64(1), - float64(1.0), - true, - "1", - []interface{}{true}, - map[string]interface{}{"0": true}, - }}, - {"return [];", []interface{}{ - []interface{}{}, - int64(0), - float64(0), - false, - "Array", - []interface{}{}, - map[string]interface{}{}, - }}, - {"return [1, 'w', 3.1, false];", []interface{}{ - []interface{}{(int64)(1), "w", 3.1, false}, - int64(1), - float64(1.0), - true, - "Array", - []interface{}{(int64)(1), "w", 3.1, false}, - map[string]interface{}{"0": (int64)(1), "1": "w", "2": 3.1, "3": false}, - }}, - {"return [0 => 'a', 2 => 'b', 1 => 'c'];", []interface{}{ - map[string]interface{}{"0": "a", "2": "b", "1": "c"}, - int64(1), - float64(1), - true, - "Array", - []interface{}{"a", "b", "c"}, - map[string]interface{}{"0": "a", "2": "b", "1": "c"}, - }}, - {"return ['h' => 'hello', 'w' => 'world'];", []interface{}{ - map[string]interface{}{"h": "hello", "w": "world"}, - int64(1), - float64(1.0), - true, - "Array", - []interface{}{"hello", "world"}, - map[string]interface{}{"h": "hello", "w": "world"}, - }}, - {"return (object) ['test' => 1, 2 => 'hello'];", []interface{}{ - map[string]interface{}{"test": int64(1), "2": "hello"}, - int64(1), - float64(1.0), - true, - "", - []interface{}{int64(1), "hello"}, - map[string]interface{}{"test": int64(1), "2": "hello"}, - }}, - {"'This returns nothing';", []interface{}{ - nil, - int64(0), - float64(0.0), - false, - "", - []interface{}{}, - map[string]interface{}{}, - }}, -} - -func TestContextReverseBind(t *testing.T) { - e, _ := New() - ctx, _ := e.NewContext() - - for _, tt := range reverseBindTests { - val, err := ctx.Eval(tt.script) - if err != nil { - t.Errorf(`Context.Eval("%s"): %s`, tt.script, err) - continue - } - - actual := []interface{}{val.Interface(), val.Int(), val.Float(), val.Bool(), val.String(), val.Slice(), val.Map()} - - for i, expected := range tt.expected { - actual := actual[i] - if reflect.DeepEqual(actual, expected) == false { - t.Errorf(`Context.Eval("%s") to '%[3]T': expected '%[2]v', actual '%[3]v'`, tt.script, expected, actual) - } - } - } - - e.Destroy() -} - -func init() { - wd, _ := os.Getwd() - testDir = path.Join(wd, "engine/tests") -}