From dab498651a7ff2b615e673f084a5b44ba78b64cf Mon Sep 17 00:00:00 2001 From: Daniel Kang Date: Mon, 14 Jan 2019 05:22:28 -0800 Subject: [PATCH] add tests for Object.BinaryOp --- assert/assert.go | 2 + objects/array_test.go | 54 +++++++++++ objects/float_test.go | 126 +++++++++++++++++++++++++ objects/int_test.go | 199 ++++++++++++++++++++++++++++++++++++++++ objects/objects_test.go | 17 ++++ objects/string_test.go | 23 +++++ 6 files changed, 421 insertions(+) create mode 100644 objects/array_test.go create mode 100644 objects/float_test.go create mode 100644 objects/int_test.go create mode 100644 objects/objects_test.go create mode 100644 objects/string_test.go diff --git a/assert/assert.go b/assert/assert.go index 58374ad..208fa56 100644 --- a/assert/assert.go +++ b/assert/assert.go @@ -241,6 +241,8 @@ func equalArray(t *testing.T, expected, actual objects.Object) bool { } func equalObjectSlice(t *testing.T, expected, actual []objects.Object) bool { + // TODO: this test does not differentiate nil vs empty slice + if !Equal(t, len(expected), len(actual)) { return false } diff --git a/objects/array_test.go b/objects/array_test.go new file mode 100644 index 0000000..ce47411 --- /dev/null +++ b/objects/array_test.go @@ -0,0 +1,54 @@ +package objects_test + +import ( + "testing" + + "github.com/d5/tengo/compiler/token" + "github.com/d5/tengo/objects" +) + +func TestArray_BinaryOp(t *testing.T) { + testBinaryOp(t, &objects.Array{Value: nil}, token.Add, &objects.Array{Value: nil}, &objects.Array{Value: nil}) + testBinaryOp(t, &objects.Array{Value: nil}, token.Add, &objects.Array{Value: []objects.Object{}}, &objects.Array{Value: nil}) + testBinaryOp(t, &objects.Array{Value: []objects.Object{}}, token.Add, &objects.Array{Value: nil}, &objects.Array{Value: []objects.Object{}}) + testBinaryOp(t, &objects.Array{Value: []objects.Object{}}, token.Add, &objects.Array{Value: []objects.Object{}}, &objects.Array{Value: []objects.Object{}}) + testBinaryOp(t, &objects.Array{Value: nil}, token.Add, &objects.Array{Value: []objects.Object{ + &objects.Int{Value: 1}, + }}, &objects.Array{Value: []objects.Object{ + &objects.Int{Value: 1}, + }}) + testBinaryOp(t, &objects.Array{Value: nil}, token.Add, &objects.Array{Value: []objects.Object{ + &objects.Int{Value: 1}, + &objects.Int{Value: 2}, + &objects.Int{Value: 3}, + }}, &objects.Array{Value: []objects.Object{ + &objects.Int{Value: 1}, + &objects.Int{Value: 2}, + &objects.Int{Value: 3}, + }}) + testBinaryOp(t, &objects.Array{Value: []objects.Object{ + &objects.Int{Value: 1}, + &objects.Int{Value: 2}, + &objects.Int{Value: 3}, + }}, token.Add, &objects.Array{Value: nil}, &objects.Array{Value: []objects.Object{ + &objects.Int{Value: 1}, + &objects.Int{Value: 2}, + &objects.Int{Value: 3}, + }}) + testBinaryOp(t, &objects.Array{Value: []objects.Object{ + &objects.Int{Value: 1}, + &objects.Int{Value: 2}, + &objects.Int{Value: 3}, + }}, token.Add, &objects.Array{Value: []objects.Object{ + &objects.Int{Value: 4}, + &objects.Int{Value: 5}, + &objects.Int{Value: 6}, + }}, &objects.Array{Value: []objects.Object{ + &objects.Int{Value: 1}, + &objects.Int{Value: 2}, + &objects.Int{Value: 3}, + &objects.Int{Value: 4}, + &objects.Int{Value: 5}, + &objects.Int{Value: 6}, + }}) +} diff --git a/objects/float_test.go b/objects/float_test.go new file mode 100644 index 0000000..971b26e --- /dev/null +++ b/objects/float_test.go @@ -0,0 +1,126 @@ +package objects_test + +import ( + "testing" + + "github.com/d5/tengo/compiler/token" + "github.com/d5/tengo/objects" +) + +func TestFloat_BinaryOp(t *testing.T) { + // float + float + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := float64(-2); r <= 2.1; r += 0.4 { + testBinaryOp(t, &objects.Float{Value: l}, token.Add, &objects.Float{Value: r}, &objects.Float{Value: l + r}) + } + } + + // float - float + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := float64(-2); r <= 2.1; r += 0.4 { + testBinaryOp(t, &objects.Float{Value: l}, token.Sub, &objects.Float{Value: r}, &objects.Float{Value: l - r}) + } + } + + // float * float + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := float64(-2); r <= 2.1; r += 0.4 { + testBinaryOp(t, &objects.Float{Value: l}, token.Mul, &objects.Float{Value: r}, &objects.Float{Value: l * r}) + } + } + + // float / float + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := float64(-2); r <= 2.1; r += 0.4 { + if r != 0 { + testBinaryOp(t, &objects.Float{Value: l}, token.Quo, &objects.Float{Value: r}, &objects.Float{Value: l / r}) + } + } + } + + // float < float + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := float64(-2); r <= 2.1; r += 0.4 { + testBinaryOp(t, &objects.Float{Value: l}, token.Less, &objects.Float{Value: r}, &objects.Bool{Value: l < r}) + } + } + + // float > float + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := float64(-2); r <= 2.1; r += 0.4 { + testBinaryOp(t, &objects.Float{Value: l}, token.Greater, &objects.Float{Value: r}, &objects.Bool{Value: l > r}) + } + } + + // float <= float + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := float64(-2); r <= 2.1; r += 0.4 { + testBinaryOp(t, &objects.Float{Value: l}, token.LessEq, &objects.Float{Value: r}, &objects.Bool{Value: l <= r}) + } + } + + // float >= float + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := float64(-2); r <= 2.1; r += 0.4 { + testBinaryOp(t, &objects.Float{Value: l}, token.GreaterEq, &objects.Float{Value: r}, &objects.Bool{Value: l >= r}) + } + } + + // float + int + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Float{Value: l}, token.Add, &objects.Int{Value: r}, &objects.Float{Value: l + float64(r)}) + } + } + + // float - int + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Float{Value: l}, token.Sub, &objects.Int{Value: r}, &objects.Float{Value: l - float64(r)}) + } + } + + // float * int + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Float{Value: l}, token.Mul, &objects.Int{Value: r}, &objects.Float{Value: l * float64(r)}) + } + } + + // float / int + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := int64(-2); r <= 2; r++ { + if r != 0 { + testBinaryOp(t, &objects.Float{Value: l}, token.Quo, &objects.Int{Value: r}, &objects.Float{Value: l / float64(r)}) + } + } + } + + // float < int + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Float{Value: l}, token.Less, &objects.Int{Value: r}, &objects.Bool{Value: l < float64(r)}) + } + } + + // float > int + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Float{Value: l}, token.Greater, &objects.Int{Value: r}, &objects.Bool{Value: l > float64(r)}) + } + } + + // float <= int + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Float{Value: l}, token.LessEq, &objects.Int{Value: r}, &objects.Bool{Value: l <= float64(r)}) + } + } + + // float >= int + for l := float64(-2); l <= 2.1; l += 0.4 { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Float{Value: l}, token.GreaterEq, &objects.Int{Value: r}, &objects.Bool{Value: l >= float64(r)}) + } + } +} diff --git a/objects/int_test.go b/objects/int_test.go new file mode 100644 index 0000000..3e896c4 --- /dev/null +++ b/objects/int_test.go @@ -0,0 +1,199 @@ +package objects_test + +import ( + "testing" + + "github.com/d5/tengo/compiler/token" + "github.com/d5/tengo/objects" +) + +func TestInt_BinaryOp(t *testing.T) { + // int + int + for l := int64(-2); l <= 2; l++ { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Int{Value: l}, token.Add, &objects.Int{Value: r}, &objects.Int{Value: l + r}) + } + } + + // int - int + for l := int64(-2); l <= 2; l++ { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Int{Value: l}, token.Sub, &objects.Int{Value: r}, &objects.Int{Value: l - r}) + } + } + + // int * int + for l := int64(-2); l <= 2; l++ { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Int{Value: l}, token.Mul, &objects.Int{Value: r}, &objects.Int{Value: l * r}) + } + } + + // int / int + for l := int64(-2); l <= 2; l++ { + for r := int64(-2); r <= 2; r++ { + if r != 0 { + testBinaryOp(t, &objects.Int{Value: l}, token.Quo, &objects.Int{Value: r}, &objects.Int{Value: l / r}) + } + } + } + + // int % int + for l := int64(-4); l <= 4; l++ { + for r := -int64(-4); r <= 4; r++ { + if r == 0 { + testBinaryOp(t, &objects.Int{Value: l}, token.Rem, &objects.Int{Value: r}, &objects.Int{Value: l % r}) + } + } + } + + // int & int + testBinaryOp(t, &objects.Int{Value: 0}, token.And, &objects.Int{Value: 0}, &objects.Int{Value: int64(0) & int64(0)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.And, &objects.Int{Value: 0}, &objects.Int{Value: int64(1) & int64(0)}) + testBinaryOp(t, &objects.Int{Value: 0}, token.And, &objects.Int{Value: 1}, &objects.Int{Value: int64(0) & int64(1)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.And, &objects.Int{Value: 1}, &objects.Int{Value: int64(1) & int64(1)}) + testBinaryOp(t, &objects.Int{Value: 0}, token.And, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(0) & int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.And, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(1) & int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: int64(0xffffffff)}, token.And, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(0xffffffff) & int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: 1984}, token.And, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(1984) & int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: -1984}, token.And, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(-1984) & int64(0xffffffff)}) + + // int | int + testBinaryOp(t, &objects.Int{Value: 0}, token.Or, &objects.Int{Value: 0}, &objects.Int{Value: int64(0) | int64(0)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.Or, &objects.Int{Value: 0}, &objects.Int{Value: int64(1) | int64(0)}) + testBinaryOp(t, &objects.Int{Value: 0}, token.Or, &objects.Int{Value: 1}, &objects.Int{Value: int64(0) | int64(1)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.Or, &objects.Int{Value: 1}, &objects.Int{Value: int64(1) | int64(1)}) + testBinaryOp(t, &objects.Int{Value: 0}, token.Or, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(0) | int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.Or, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(1) | int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: int64(0xffffffff)}, token.Or, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(0xffffffff) | int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: 1984}, token.Or, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(1984) | int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: -1984}, token.Or, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(-1984) | int64(0xffffffff)}) + + // int ^ int + testBinaryOp(t, &objects.Int{Value: 0}, token.Xor, &objects.Int{Value: 0}, &objects.Int{Value: int64(0) ^ int64(0)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.Xor, &objects.Int{Value: 0}, &objects.Int{Value: int64(1) ^ int64(0)}) + testBinaryOp(t, &objects.Int{Value: 0}, token.Xor, &objects.Int{Value: 1}, &objects.Int{Value: int64(0) ^ int64(1)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.Xor, &objects.Int{Value: 1}, &objects.Int{Value: int64(1) ^ int64(1)}) + testBinaryOp(t, &objects.Int{Value: 0}, token.Xor, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(0) ^ int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.Xor, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(1) ^ int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: int64(0xffffffff)}, token.Xor, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(0xffffffff) ^ int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: 1984}, token.Xor, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(1984) ^ int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: -1984}, token.Xor, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(-1984) ^ int64(0xffffffff)}) + + // int &^ int + testBinaryOp(t, &objects.Int{Value: 0}, token.AndNot, &objects.Int{Value: 0}, &objects.Int{Value: int64(0) &^ int64(0)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.AndNot, &objects.Int{Value: 0}, &objects.Int{Value: int64(1) &^ int64(0)}) + testBinaryOp(t, &objects.Int{Value: 0}, token.AndNot, &objects.Int{Value: 1}, &objects.Int{Value: int64(0) &^ int64(1)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.AndNot, &objects.Int{Value: 1}, &objects.Int{Value: int64(1) &^ int64(1)}) + testBinaryOp(t, &objects.Int{Value: 0}, token.AndNot, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(0) &^ int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.AndNot, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(1) &^ int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: int64(0xffffffff)}, token.AndNot, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(0xffffffff) &^ int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: 1984}, token.AndNot, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(1984) &^ int64(0xffffffff)}) + testBinaryOp(t, &objects.Int{Value: -1984}, token.AndNot, &objects.Int{Value: int64(0xffffffff)}, &objects.Int{Value: int64(-1984) &^ int64(0xffffffff)}) + + // int << int + for s := int64(0); s < 64; s++ { + testBinaryOp(t, &objects.Int{Value: 0}, token.Shl, &objects.Int{Value: s}, &objects.Int{Value: int64(0) << uint(s)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.Shl, &objects.Int{Value: s}, &objects.Int{Value: int64(1) << uint(s)}) + testBinaryOp(t, &objects.Int{Value: 2}, token.Shl, &objects.Int{Value: s}, &objects.Int{Value: int64(2) << uint(s)}) + testBinaryOp(t, &objects.Int{Value: -1}, token.Shl, &objects.Int{Value: s}, &objects.Int{Value: int64(-1) << uint(s)}) + testBinaryOp(t, &objects.Int{Value: -2}, token.Shl, &objects.Int{Value: s}, &objects.Int{Value: int64(-2) << uint(s)}) + testBinaryOp(t, &objects.Int{Value: int64(0xffffffff)}, token.Shl, &objects.Int{Value: s}, &objects.Int{Value: int64(0xffffffff) << uint(s)}) + } + + // int >> int + for s := int64(0); s < 64; s++ { + testBinaryOp(t, &objects.Int{Value: 0}, token.Shr, &objects.Int{Value: s}, &objects.Int{Value: int64(0) >> uint(s)}) + testBinaryOp(t, &objects.Int{Value: 1}, token.Shr, &objects.Int{Value: s}, &objects.Int{Value: int64(1) >> uint(s)}) + testBinaryOp(t, &objects.Int{Value: 2}, token.Shr, &objects.Int{Value: s}, &objects.Int{Value: int64(2) >> uint(s)}) + testBinaryOp(t, &objects.Int{Value: -1}, token.Shr, &objects.Int{Value: s}, &objects.Int{Value: int64(-1) >> uint(s)}) + testBinaryOp(t, &objects.Int{Value: -2}, token.Shr, &objects.Int{Value: s}, &objects.Int{Value: int64(-2) >> uint(s)}) + testBinaryOp(t, &objects.Int{Value: int64(0xffffffff)}, token.Shr, &objects.Int{Value: s}, &objects.Int{Value: int64(0xffffffff) >> uint(s)}) + } + + // int < int + for l := int64(-2); l <= 2; l++ { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Int{Value: l}, token.Less, &objects.Int{Value: r}, &objects.Bool{Value: l < r}) + } + } + + // int > int + for l := int64(-2); l <= 2; l++ { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Int{Value: l}, token.Greater, &objects.Int{Value: r}, &objects.Bool{Value: l > r}) + } + } + + // int <= int + for l := int64(-2); l <= 2; l++ { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Int{Value: l}, token.LessEq, &objects.Int{Value: r}, &objects.Bool{Value: l <= r}) + } + } + + // int >= int + for l := int64(-2); l <= 2; l++ { + for r := int64(-2); r <= 2; r++ { + testBinaryOp(t, &objects.Int{Value: l}, token.GreaterEq, &objects.Int{Value: r}, &objects.Bool{Value: l >= r}) + } + } + + // int + float + for l := int64(-2); l <= 2; l++ { + for r := float64(-2); r <= 2.1; r += 0.5 { + testBinaryOp(t, &objects.Int{Value: l}, token.Add, &objects.Float{Value: r}, &objects.Float{Value: float64(l) + r}) + } + } + + // int - float + for l := int64(-2); l <= 2; l++ { + for r := float64(-2); r <= 2.1; r += 0.5 { + testBinaryOp(t, &objects.Int{Value: l}, token.Sub, &objects.Float{Value: r}, &objects.Float{Value: float64(l) - r}) + } + } + + // int * float + for l := int64(-2); l <= 2; l++ { + for r := float64(-2); r <= 2.1; r += 0.5 { + testBinaryOp(t, &objects.Int{Value: l}, token.Mul, &objects.Float{Value: r}, &objects.Float{Value: float64(l) * r}) + } + } + + // int / float + for l := int64(-2); l <= 2; l++ { + for r := float64(-2); r <= 2.1; r += 0.5 { + if r != 0 { + testBinaryOp(t, &objects.Int{Value: l}, token.Quo, &objects.Float{Value: r}, &objects.Float{Value: float64(l) / r}) + } + } + } + + // int < float + for l := int64(-2); l <= 2; l++ { + for r := float64(-2); r <= 2.1; r += 0.5 { + testBinaryOp(t, &objects.Int{Value: l}, token.Less, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) < r}) + } + } + + // int > float + for l := int64(-2); l <= 2; l++ { + for r := float64(-2); r <= 2.1; r += 0.5 { + testBinaryOp(t, &objects.Int{Value: l}, token.Greater, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) > r}) + } + } + + // int <= float + for l := int64(-2); l <= 2; l++ { + for r := float64(-2); r <= 2.1; r += 0.5 { + testBinaryOp(t, &objects.Int{Value: l}, token.LessEq, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) <= r}) + } + } + + // int >= float + for l := int64(-2); l <= 2; l++ { + for r := float64(-2); r <= 2.1; r += 0.5 { + testBinaryOp(t, &objects.Int{Value: l}, token.GreaterEq, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) >= r}) + } + } +} diff --git a/objects/objects_test.go b/objects/objects_test.go new file mode 100644 index 0000000..23e8872 --- /dev/null +++ b/objects/objects_test.go @@ -0,0 +1,17 @@ +package objects_test + +import ( + "testing" + + "github.com/d5/tengo/assert" + "github.com/d5/tengo/compiler/token" + "github.com/d5/tengo/objects" +) + +func testBinaryOp(t *testing.T, lhs objects.Object, op token.Token, rhs objects.Object, expected objects.Object) bool { + t.Helper() + + actual, err := lhs.BinaryOp(op, rhs) + + return assert.NoError(t, err) && assert.Equal(t, expected, actual) +} diff --git a/objects/string_test.go b/objects/string_test.go new file mode 100644 index 0000000..e6df899 --- /dev/null +++ b/objects/string_test.go @@ -0,0 +1,23 @@ +package objects_test + +import ( + "testing" + + "github.com/d5/tengo/compiler/token" + "github.com/d5/tengo/objects" +) + +func TestString_BinaryOp(t *testing.T) { + lstr := "abcde" + rstr := "01234" + for l := 0; l < len(lstr); l++ { + for r := 0; r < len(rstr); r++ { + ls := lstr[l:] + rs := rstr[r:] + testBinaryOp(t, &objects.String{Value: ls}, token.Add, &objects.String{Value: rs}, &objects.String{Value: ls + rs}) + + rc := []rune(rstr)[r] + testBinaryOp(t, &objects.String{Value: ls}, token.Add, &objects.Char{Value: rc}, &objects.String{Value: ls + string(rc)}) + } + } +}