mirror of
https://github.com/romychs/Ocean-240.2-Emulator.git
synced 2026-04-21 11:03:21 +03:00
397 lines
9.6 KiB
Go
397 lines
9.6 KiB
Go
package gval
|
|
|
|
/*
|
|
Tests to make sure evaluation fails in the expected ways.
|
|
*/
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
)
|
|
|
|
func TestModifierTyping(test *testing.T) {
|
|
var (
|
|
invalidOperator = "invalid operation"
|
|
unknownParameter = "unknown parameter"
|
|
invalidRegex = "error parsing regex"
|
|
tooFewArguments = "reflect: Call with too few input arguments"
|
|
tooManyArguments = "reflect: Call with too many input arguments"
|
|
mismatchedParameters = "reflect: Call using"
|
|
custom = "test error"
|
|
)
|
|
evaluationTests := []evaluationTest{
|
|
//ModifierTyping
|
|
{
|
|
name: "PLUS literal number to literal bool",
|
|
expression: "1 + true",
|
|
want: "1true", // + on string is defined
|
|
},
|
|
{
|
|
name: "PLUS number to bool",
|
|
expression: "number + bool",
|
|
want: "1true", // + on string is defined
|
|
},
|
|
{
|
|
name: "MINUS number to bool",
|
|
expression: "number - bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "MINUS number to bool",
|
|
expression: "number - bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "MULTIPLY number to bool",
|
|
expression: "number * bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "DIVIDE number to bool",
|
|
expression: "number / bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "EXPONENT number to bool",
|
|
expression: "number ** bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "MODULUS number to bool",
|
|
expression: "number % bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "XOR number to bool",
|
|
expression: "number % bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "BITWISE_OR number to bool",
|
|
expression: "number | bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "BITWISE_AND number to bool",
|
|
expression: "number & bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "BITWISE_XOR number to bool",
|
|
expression: "number ^ bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "BITWISE_LSHIFT number to bool",
|
|
expression: "number << bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "BITWISE_RSHIFT number to bool",
|
|
expression: "number >> bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
//LogicalOperatorTyping
|
|
{
|
|
name: "AND number to number",
|
|
expression: "number && number",
|
|
want: true, // number != 0 is true
|
|
},
|
|
{
|
|
|
|
name: "OR number to number",
|
|
expression: "number || number",
|
|
want: true, // number != 0 is true
|
|
},
|
|
{
|
|
name: "AND string to string",
|
|
expression: "string && string",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "OR string to string",
|
|
expression: "string || string",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "AND number to string",
|
|
expression: "number && string",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "OR number to string",
|
|
expression: "number || string",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "AND bool to string",
|
|
expression: "bool && string",
|
|
wantErr: invalidOperator,
|
|
},
|
|
{
|
|
name: "OR string to bool",
|
|
expression: "string || bool",
|
|
wantErr: invalidOperator,
|
|
},
|
|
//ComparatorTyping
|
|
{
|
|
name: "GT literal bool to literal bool",
|
|
expression: "true > true",
|
|
want: false, //lexical order on "true"
|
|
},
|
|
{
|
|
name: "GT bool to bool",
|
|
expression: "bool > bool",
|
|
want: false, //lexical order on "true"
|
|
},
|
|
{
|
|
name: "GTE bool to bool",
|
|
expression: "bool >= bool",
|
|
want: true, //lexical order on "true"
|
|
},
|
|
{
|
|
name: "LT bool to bool",
|
|
expression: "bool < bool",
|
|
want: false, //lexical order on "true"
|
|
},
|
|
{
|
|
name: "LTE bool to bool",
|
|
expression: "bool <= bool",
|
|
want: true, //lexical order on "true"
|
|
},
|
|
{
|
|
name: "GT number to string",
|
|
expression: "number > string",
|
|
want: false, //lexical order "1" < "foo"
|
|
},
|
|
{
|
|
|
|
name: "GTE number to string",
|
|
expression: "number >= string",
|
|
want: false, //lexical order "1" < "foo"
|
|
},
|
|
{
|
|
name: "LT number to string",
|
|
expression: "number < string",
|
|
want: true, //lexical order "1" < "foo"
|
|
},
|
|
{
|
|
name: "REQ number to string",
|
|
expression: "number =~ string",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "REQ number to bool",
|
|
expression: "number =~ bool",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "REQ bool to number",
|
|
expression: "bool =~ number",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "REQ bool to string",
|
|
expression: "bool =~ string",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "NREQ number to string",
|
|
expression: "number !~ string",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "NREQ number to bool",
|
|
expression: "number !~ bool",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "NREQ bool to number",
|
|
expression: "bool !~ number",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "NREQ bool to string",
|
|
expression: "bool !~ string",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "IN non-array numeric",
|
|
expression: "1 in 2",
|
|
wantErr: "expected type []interface{} for in operator but got float64",
|
|
},
|
|
{
|
|
name: "IN non-array string",
|
|
expression: `1 in "foo"`,
|
|
wantErr: "expected type []interface{} for in operator but got string",
|
|
},
|
|
{
|
|
|
|
name: "IN non-array boolean",
|
|
expression: "1 in true",
|
|
wantErr: "expected type []interface{} for in operator but got bool",
|
|
},
|
|
//TernaryTyping
|
|
{
|
|
name: "Ternary with number",
|
|
expression: "10 ? true",
|
|
want: true, // 10 != nil && 10 != false
|
|
},
|
|
{
|
|
name: "Ternary with string",
|
|
expression: `"foo" ? true`,
|
|
want: true, // "foo" != nil && "foo" != false
|
|
},
|
|
//RegexParameterCompilation
|
|
{
|
|
name: "Regex equality runtime parsing",
|
|
expression: `"foo" =~ foo`,
|
|
parameter: map[string]interface{}{
|
|
"foo": "[foo",
|
|
},
|
|
wantErr: invalidRegex,
|
|
},
|
|
{
|
|
name: "Regex inequality runtime parsing",
|
|
expression: `"foo" !~ foo`,
|
|
parameter: map[string]interface{}{
|
|
"foo": "[foo",
|
|
},
|
|
wantErr: invalidRegex,
|
|
},
|
|
{
|
|
name: "Regex equality runtime right side evaluation",
|
|
expression: `"foo" =~ error()`,
|
|
wantErr: custom,
|
|
},
|
|
{
|
|
name: "Regex inequality runtime right side evaluation",
|
|
expression: `"foo" !~ error()`,
|
|
wantErr: custom,
|
|
},
|
|
{
|
|
name: "Regex equality runtime left side evaluation",
|
|
expression: `error() =~ "."`,
|
|
wantErr: custom,
|
|
},
|
|
{
|
|
name: "Regex inequality runtime left side evaluation",
|
|
expression: `error() !~ "."`,
|
|
wantErr: custom,
|
|
},
|
|
//FuncExecution
|
|
{
|
|
name: "Func error bubbling",
|
|
expression: "error()",
|
|
extension: Function("error", func(arguments ...interface{}) (interface{}, error) {
|
|
return nil, errors.New("Huge problems")
|
|
}),
|
|
wantErr: "Huge problems",
|
|
},
|
|
//InvalidParameterCalls
|
|
{
|
|
name: "Missing parameter field reference",
|
|
expression: "foo.NotExists",
|
|
parameter: fooFailureParameters,
|
|
wantErr: unknownParameter,
|
|
},
|
|
{
|
|
name: "Parameter method call on missing function",
|
|
expression: "foo.NotExist()",
|
|
parameter: fooFailureParameters,
|
|
wantErr: unknownParameter,
|
|
},
|
|
{
|
|
name: "Nested missing parameter field reference",
|
|
expression: "foo.Nested.NotExists",
|
|
parameter: fooFailureParameters,
|
|
wantErr: unknownParameter,
|
|
},
|
|
{
|
|
name: "Parameter method call returns error",
|
|
expression: "foo.AlwaysFail()",
|
|
parameter: fooFailureParameters,
|
|
wantErr: "function should always fail",
|
|
},
|
|
{
|
|
name: "Too few arguments to parameter call",
|
|
expression: "foo.FuncArgStr()",
|
|
parameter: fooFailureParameters,
|
|
wantErr: tooFewArguments,
|
|
},
|
|
{
|
|
name: "Too many arguments to parameter call",
|
|
expression: `foo.FuncArgStr("foo", "bar", 15)`,
|
|
parameter: fooFailureParameters,
|
|
wantErr: tooManyArguments,
|
|
},
|
|
{
|
|
name: "Mismatched parameters",
|
|
expression: "foo.FuncArgStr(5)",
|
|
parameter: fooFailureParameters,
|
|
wantErr: mismatchedParameters,
|
|
},
|
|
{
|
|
name: "Negative Array Index",
|
|
expression: "foo[-1]",
|
|
parameter: map[string]interface{}{
|
|
"foo": []int{1, 2, 3},
|
|
},
|
|
wantErr: unknownParameter,
|
|
},
|
|
{
|
|
name: "Nested slice call index out of bound",
|
|
expression: `foo.Nested.Slice[10]`,
|
|
parameter: map[string]interface{}{"foo": foo},
|
|
wantErr: unknownParameter,
|
|
},
|
|
{
|
|
name: "Nested map call missing key",
|
|
expression: `foo.Nested.Map["d"]`,
|
|
parameter: map[string]interface{}{"foo": foo},
|
|
wantErr: unknownParameter,
|
|
},
|
|
{
|
|
name: "invalid selector",
|
|
expression: "hello[world()]",
|
|
extension: NewLanguage(Base(), Function("world", func() (int, error) {
|
|
return 0, fmt.Errorf("test error")
|
|
})),
|
|
wantErr: "test error",
|
|
},
|
|
{
|
|
name: "eval `nil > 1` returns true #23",
|
|
expression: `nil > 1`,
|
|
wantErr: "invalid operation (<nil>) > (float64)",
|
|
},
|
|
{
|
|
name: "map with unknown func",
|
|
expression: `foo.MapWithFunc.NotExist()`,
|
|
parameter: map[string]interface{}{"foo": foo},
|
|
wantErr: unknownParameter,
|
|
},
|
|
{
|
|
name: "map with unknown func",
|
|
expression: `foo.SliceWithFunc.NotExist()`,
|
|
parameter: map[string]interface{}{"foo": foo},
|
|
wantErr: unknownParameter,
|
|
},
|
|
}
|
|
|
|
for i := range evaluationTests {
|
|
if evaluationTests[i].parameter == nil {
|
|
evaluationTests[i].parameter = map[string]interface{}{
|
|
"number": 1,
|
|
"string": "foo",
|
|
"bool": true,
|
|
"error": func() (int, error) {
|
|
return 0, fmt.Errorf("test error")
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
testEvaluate(evaluationTests, test)
|
|
}
|