diff --git a/.travis.yml b/.travis.yml index 24435c8..76a57d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,9 @@ go: - 1.11.x - 1.12.x +env: + - GO111MODULE=on + # Only clone the most recent commit. git: depth: 1 @@ -26,7 +29,7 @@ notifications: # build and immediately stop. It's sorta like having set -e enabled in bash. # Make sure golangci-lint is vendored. before_script: - - go get -u github.com/golangci/golangci-lint/cmd/golangci-lint + - go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.17.0 - go get -u github.com/kisielk/errcheck - go get golang.org/x/tools/cmd/cover # coveralls.io - go get github.com/mattn/goveralls # coveralls.io diff --git a/Makefile b/Makefile index a22d199..6fd5845 100644 --- a/Makefile +++ b/Makefile @@ -24,4 +24,4 @@ golangcilint: #go get github.com/fzipp/gocyclo gocyclo: - gocyclo -top 10 . \ No newline at end of file + gocyclo -top 10 . diff --git a/buffer.go b/buffer.go index 0e7d035..0970eaf 100644 --- a/buffer.go +++ b/buffer.go @@ -117,44 +117,51 @@ func (b *buffer) skipAny(s map[byte]bool) error { func (b *buffer) numeric() error { var c byte + const ( + _none = 0 // 0 + _sign = 1 << 0 // 1 + _dot = 1 << 1 // 2 + _num = 1 << 2 // 4 + _exp = 1 << 3 // 8 + ) find := 0 for ; b.index < b.length; b.index++ { c = b.data[b.index] switch true { case c >= '0' && c <= '9': - find |= 4 + find |= _num case c == '.': - if find&2 == 0 && find&8 == 0 { // exp part of numeric MUST contains only digits - find &= 2 + if find&_exp != 0 { // exp part of numeric MUST contains only digits + return errorSymbol(b) + } + if find&_dot == 0 { + find |= _dot } else { - if find&4 == 0 { - return errorSymbol(b) - } - return nil + return errorSymbol(b) } case c == '+' || c == '-': - if find == 0 || find == 8 { - find |= 1 + if find == _none || find == _exp { + find |= _sign } else { - if find&4 == 0 { + if find&_num == 0 { return errorSymbol(b) } return nil } case c == 'e' || c == 'E': - if find&8 == 0 && find&4 != 0 { // exp without base part - find = 8 + if find&_exp == 0 && find&_num != 0 { // exp without base part + find = _exp } else { return errorSymbol(b) } default: - if find&4 != 0 { + if find&_num != 0 { return nil } return errorSymbol(b) } } - if find&4 != 0 { + if find&_num != 0 { return io.EOF } return errorEOF(b) @@ -226,22 +233,23 @@ tokenLoop: switch { case c == quote: find = true - start = b.index err = b.step() if err != nil { return b.errorEOF() } err = b.skip(quote) - if err == nil || err == io.EOF { - continue + if err == io.EOF { + return b.errorEOF() } - b.index = start case c == bracketL: find = true stack = append(stack, c) case c == bracketR: find = true if len(stack) == 0 { + if first == b.index { + return b.errorSymbol() + } break tokenLoop } if stack[len(stack)-1] != bracketL { @@ -254,6 +262,9 @@ tokenLoop: case c == parenthesesR: find = true if len(stack) == 0 { + if first == b.index { + return b.errorSymbol() + } break tokenLoop } if stack[len(stack)-1] != parenthesesL { diff --git a/buffer_test.go b/buffer_test.go index cac8b5f..580a9a1 100644 --- a/buffer_test.go +++ b/buffer_test.go @@ -2,6 +2,7 @@ package ajson import ( "io" + "strings" "testing" ) @@ -33,6 +34,31 @@ func TestBuffer_Token(t *testing.T) { {name: "fail 1", value: "@.foo[", fail: true}, {name: "fail 2", value: "@.foo[(]", fail: true}, + {name: "fail 3", value: "'", fail: true}, + {name: "fail 4", value: "'x", fail: true}, + + {name: "parentheses 0", value: "()", index: 2, fail: false}, + {name: "parentheses 1", value: "(@)", index: 3, fail: false}, + {name: "parentheses 2", value: "(", fail: true}, + {name: "parentheses 3", value: ")", fail: true}, + {name: "parentheses 4", value: "(x", fail: true}, + {name: "parentheses 5", value: "((())", fail: true}, + {name: "parentheses 6", value: "@)", index: 1, fail: false}, + {name: "parentheses 7", value: "[)", fail: true}, + {name: "parentheses 8", value: "[())", fail: true}, + + {name: "bracket 0", value: "[]", index: 2, fail: false}, + {name: "bracket 1", value: "[@]", index: 3, fail: false}, + {name: "bracket 2", value: "[", fail: true}, + {name: "bracket 3", value: "]", fail: true}, + {name: "bracket 4", value: "[x", fail: true}, + {name: "bracket 5", value: "[[[]]", fail: true}, + {name: "bracket 6", value: "@]", index: 1, fail: false}, + {name: "bracket 7", value: "(]", fail: true}, + {name: "bracket 8", value: "([]]", fail: true}, + + {name: "sign 1", value: "+X", index: 1}, + {name: "sign 2", value: "-X", index: 1}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -65,6 +91,12 @@ func TestBuffer_RPN(t *testing.T) { {name: "example_8", value: "@.length-1", expected: []string{"@.length", "1", "-"}}, {name: "example_9", value: "@.length+-1", expected: []string{"@.length", "-1", "+"}}, {name: "example_10", value: "@.length/e", expected: []string{"@.length", "e", "/"}}, + {name: "example_11", value: "", expected: []string{}}, + + {name: "1 /", value: "1 /", expected: []string{"1", "/"}}, + {name: "1 + ", value: "1 + ", expected: []string{"1", "+"}}, + {name: "1 -", value: "1 -", expected: []string{"1", "-"}}, + {name: "1 * ", value: "1 * ", expected: []string{"1", "*"}}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -79,11 +111,50 @@ func TestBuffer_RPN(t *testing.T) { } } +func TestBuffer_RPNError(t *testing.T) { + tests := []struct { + value string + }{ + {value: "1 + / 1"}, + {value: "1 * / 1"}, + {value: "1 - / 1"}, + {value: "1 / / 1"}, + + {value: "1 + * 1"}, + {value: "1 * * 1"}, + {value: "1 - * 1"}, + {value: "1 / * 1"}, + + {value: "1e1.1 + 1"}, + + {value: "len('string)"}, + {value: "'Hello ' + 'World"}, + + {value: "@.length + $['length')"}, + {value: "2 + 2)"}, + {value: "(2 + 2"}, + + {value: "e + q"}, + {value: "foo(e)"}, + {value: "++2"}, + } + for _, test := range tests { + t.Run(test.value, func(t *testing.T) { + buf := newBuffer([]byte(test.value)) + result, err := buf.rpn() + if err == nil { + t.Errorf("Expected error, nil given, with result: %v", strings.Join(result, ", ")) + } + }) + } +} + func TestTokenize(t *testing.T) { tests := []struct { name string value string expected []string + fail bool }{ {name: "example_1", value: "@.length", expected: []string{"@.length"}}, {name: "example_2", value: "1 + 2", expected: []string{"1", "+", "2"}}, @@ -96,11 +167,19 @@ func TestTokenize(t *testing.T) { {name: "example_9", value: "'foo'", expected: []string{"'foo'"}}, {name: "example_10", value: "$.foo[(@.length - 3):3:]", expected: []string{"$.foo[(@.length - 3):3:]"}}, {name: "example_11", value: "$..", expected: []string{"$.."}}, + {name: "blank", value: "", expected: []string{}}, + {name: "number", value: "1e", fail: true}, + {name: "string", value: "'foo", fail: true}, + {name: "fail", value: "@.[", fail: true}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result, err := tokenize(test.value) - if err != nil { + if test.fail { + if err == nil { + t.Error("Expected error: nil given") + } + } else if err != nil { t.Errorf("Unexpected error: %s", err.Error()) } else if !sliceEqual(test.expected, result) { t.Errorf("Error on RPN(%s): result doesn't match\nExpected: %s\nActual: %s", test.value, sliceString(test.expected), sliceString(result)) @@ -108,3 +187,55 @@ func TestTokenize(t *testing.T) { }) } } + +func TestBuffer_Current(t *testing.T) { + buf := newBuffer([]byte{}) + _, err := buf.current() + if err != io.EOF { + t.Error("Unexpected result: io.EOF expected") + } +} + +func TestBuffer_Numeric(t *testing.T) { + tests := []struct { + value string + index int + fail bool + }{ + {value: "1", index: 1, fail: false}, + {value: "0", index: 1, fail: false}, + {value: "1.3e2", index: 5, fail: false}, + {value: "-1.3e2", index: 6, fail: false}, + {value: "-1.3e-2", index: 7, fail: false}, + {value: "..3", index: 0, fail: true}, + {value: "e.", index: 0, fail: true}, + {value: ".e.", index: 0, fail: true}, + {value: "1.e1", index: 4, fail: false}, + {value: "0.e0", index: 4, fail: false}, + {value: "0.e0", index: 4, fail: false}, + {value: "0+0", index: 1, fail: false}, + {value: "0-1", index: 1, fail: false}, + {value: "++1", index: 0, fail: true}, + {value: "--1", index: 0, fail: true}, + {value: "-+1", index: 0, fail: true}, + {value: "+-1", index: 0, fail: true}, + {value: "+", index: 0, fail: true}, + {value: "-", index: 0, fail: true}, + {value: ".", index: 0, fail: true}, + {value: "e", index: 0, fail: true}, + {value: "+a", index: 0, fail: true}, + } + for _, test := range tests { + t.Run(test.value, func(t *testing.T) { + buf := newBuffer([]byte(test.value)) + err := buf.numeric() + if !test.fail && err != nil && err != io.EOF { + t.Errorf("Unexpected error: %s", err.Error()) + } else if test.fail && (err == nil || err == io.EOF) { + t.Errorf("Expected error, got nothing") + } else if !test.fail && test.index != buf.index { + t.Errorf("Wrong index: expected %d, got %d", test.index, buf.index) + } + }) + } +} diff --git a/decode.go b/decode.go index 0e5f08f..3e16d71 100644 --- a/decode.go +++ b/decode.go @@ -14,16 +14,16 @@ package ajson import "io" -//UnmarshalSafe do the same thing as Unmarshal, but copy data to the local variable, to make it editable. +// UnmarshalSafe do the same thing as Unmarshal, but copy data to the local variable, to make it editable. func UnmarshalSafe(data []byte) (root *Node, err error) { var safe []byte safe = append(safe, data...) return Unmarshal(safe) } -//Unmarshal parses the JSON-encoded data and return the root node of struct. +// Unmarshal parses the JSON-encoded data and return the root node of struct. // -//Doesn't calculate values, just type of stored value. It will store link to the data, on all life long. +// Doesn't calculate values, just type of stored value. It will store link to the data, on all life long. func Unmarshal(data []byte) (root *Node, err error) { buf := newBuffer(data) var ( diff --git a/decode_test.go b/decode_test.go index c532ce1..31f4ed7 100644 --- a/decode_test.go +++ b/decode_test.go @@ -202,6 +202,7 @@ func TestUnmarshal_StringSimpleCorrupted(t *testing.T) { {name: "one quote", input: []byte("\"")}, {name: "one quote char", input: []byte("\"c")}, {name: "wrong quotes", input: []byte("'cat'")}, + {name: "double string", input: []byte("\"Hello\" \"World\"")}, {name: "quotes in quotes", input: []byte("\"good \"cat\"\"")}, } for _, test := range tests { @@ -231,6 +232,7 @@ func TestUnmarshal_NullSimpleCorrupted(t *testing.T) { {name: "NILL", input: []byte("NILL")}, {name: "spaces", input: []byte("Nu ll")}, {name: "null1", input: []byte("null1")}, + {name: "double", input: []byte("null null")}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -263,6 +265,7 @@ func TestUnmarshal_BoolSimpleCorrupted(t *testing.T) { simpleCorrupted("fals"), simpleCorrupted("tre"), simpleCorrupted("fal se"), + simpleCorrupted("true false"), } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -345,6 +348,8 @@ func TestUnmarshal_ObjectSimpleCorrupted(t *testing.T) { simpleCorrupted(`{}1`), simpleCorrupted(`1{}`), simpleCorrupted(`{"x"::1}`), + simpleCorrupted(`{null:null}`), + simpleCorrupted(`{"foo:"bar"}`), } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/errors.go b/errors.go index 0bb8b3e..f4b05a1 100644 --- a/errors.go +++ b/errors.go @@ -2,7 +2,7 @@ package ajson import "fmt" -//Error is common struct to provide internal errors +// Error is common struct to provide internal errors type Error struct { Type ErrorType Index int @@ -10,17 +10,17 @@ type Error struct { Message string } -//ErrorType is container for reflection type of error +// ErrorType is container for reflection type of error type ErrorType int const ( - //WrongSymbol means that system found symbol than not allowed to be + // WrongSymbol means that system found symbol than not allowed to be WrongSymbol ErrorType = iota - //UnexpectedEOF means that data ended, leaving the node undone + // UnexpectedEOF means that data ended, leaving the node undone UnexpectedEOF - //WrongType means that wrong type requested + // WrongType means that wrong type requested WrongType - //WrongRequest means that wrong range requested + // WrongRequest means that wrong range requested WrongRequest ) @@ -44,15 +44,15 @@ func errorRequest(format string, args ...interface{}) error { return &Error{Type: WrongRequest, Message: fmt.Sprintf(format, args...)} } -//Error interface implementation +// Error interface implementation func (err *Error) Error() string { switch err.Type { case WrongSymbol: return fmt.Sprintf("wrong symbol '%s' at %d", []byte{err.Char}, err.Index) case UnexpectedEOF: - return fmt.Sprintf("unexpected end of file") + return "unexpected end of file" case WrongType: - return fmt.Sprintf("wrong type of Node") + return "wrong type of Node" case WrongRequest: return fmt.Sprintf("wrong request: %s", err.Message) } diff --git a/errors_test.go b/errors_test.go new file mode 100644 index 0000000..7f44a88 --- /dev/null +++ b/errors_test.go @@ -0,0 +1,30 @@ +package ajson + +import "testing" + +func TestError_Error(t *testing.T) { + tests := []struct { + name string + _type ErrorType + message string + }{ + {name: "WrongSymbol", _type: WrongSymbol, message: "wrong symbol 'S' at 10"}, + {name: "UnexpectedEOF", _type: UnexpectedEOF, message: "unexpected end of file"}, + {name: "WrongType", _type: WrongType, message: "wrong type of Node"}, + {name: "WrongRequest", _type: WrongRequest, message: "wrong request: example error"}, + {name: "unknown", _type: -666, message: "unknown error: 'S' at 10"}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := &Error{ + Type: test._type, + Index: 10, + Char: 'S', + Message: "example error", + } + if result.Error() != test.message { + t.Errorf("Wrong error message: %s", result.Error()) + } + }) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1959fae --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/spyzhov/ajson + +go 1.12 diff --git a/jsonpath.go b/jsonpath.go index 5cdf387..fb5ff72 100644 --- a/jsonpath.go +++ b/jsonpath.go @@ -159,7 +159,7 @@ func JSONPath(data []byte, path string) (result []*Node, err error) { return deReference(node, commands) } -//Paths returns calculated paths of underlying nodes +// Paths returns calculated paths of underlying nodes func Paths(array []*Node) []string { result := make([]string, 0, len(array)) for _, element := range array { @@ -187,8 +187,8 @@ func recursiveChildren(node *Node) (result []*Node) { // ParseJSONPath will parse current path and return all commands tobe run. // Example: // -// result, _ := ParseJSONPath("$.store.book[?(@.price < 10)].title") -// result == []string{"$", "store", "book", "?(@.price < 10)", "title"} +// result, _ := ParseJSONPath("$.store.book[?(@.price < 10)].title") +// result == []string{"$", "store", "book", "?(@.price < 10)", "title"} // func ParseJSONPath(path string) (result []string, err error) { buf := newBuffer([]byte(path)) @@ -473,7 +473,7 @@ func deReference(node *Node, commands []string) (result []*Node, err error) { } temporary = make([]*Node, 0) - for _, key = range keys { + for _, key = range keys { // fixme for _, element := range result { if element.IsArray() { if key == "length" || key == "'length'" { @@ -544,7 +544,7 @@ func Eval(node *Node, cmd string) (result *Node, err error) { func eval(node *Node, expression rpn, cmd string) (result *Node, err error) { var ( - stack []*Node + stack = make([]*Node, 0) slice []*Node temp *Node fn Function diff --git a/math.go b/math.go index 1c391a0..fd896fa 100644 --- a/math.go +++ b/math.go @@ -96,7 +96,7 @@ var ( if err != nil { return } - rnum, err := left.getInteger() + rnum, err := right.getInteger() if err != nil { return } @@ -107,7 +107,7 @@ var ( if err != nil { return } - rnum, err := left.getUInteger() + rnum, err := right.getUInteger() if err != nil { return } @@ -118,7 +118,7 @@ var ( if err != nil { return } - rnum, err := left.getUInteger() + rnum, err := right.getUInteger() if err != nil { return } @@ -136,7 +136,7 @@ var ( if err != nil { return } - return valueNode(nil, "bit clear (AND NOT)", Numeric, float64(lnum&rnum)), nil + return valueNode(nil, "bit clear (AND NOT)", Numeric, float64(lnum&^rnum)), nil }, "+": func(left *Node, right *Node) (result *Node, err error) { if left.IsString() { @@ -160,24 +160,18 @@ var ( return valueNode(nil, "sub", Numeric, float64(lnum-rnum)), nil }, "|": func(left *Node, right *Node) (result *Node, err error) { - if left.IsNumeric() && right.IsNumeric() { - lnum, rnum, err := _ints(left, right) - if err != nil { - return nil, err - } - return valueNode(nil, "bitwise OR", Numeric, float64(lnum|rnum)), nil + lnum, rnum, err := _ints(left, right) + if err != nil { + return } - return nil, errorRequest("function 'bitwise OR' was called from non numeric node") + return valueNode(nil, "bitwise OR", Numeric, float64(lnum|rnum)), nil }, "^": func(left *Node, right *Node) (result *Node, err error) { - if left.IsNumeric() && right.IsNumeric() { - lnum, rnum, err := _ints(left, right) - if err != nil { - return nil, err - } - return valueNode(nil, "bitwise XOR", Numeric, float64(lnum^rnum)), nil + lnum, rnum, err := _ints(left, right) + if err != nil { + return nil, err } - return nil, errorRequest("function 'bitwise XOR' was called from non numeric node") + return valueNode(nil, "bitwise XOR", Numeric, float64(lnum^rnum)), nil }, "==": func(left *Node, right *Node) (result *Node, err error) { res, err := left.Eq(right) diff --git a/math_test.go b/math_test.go index eba3753..a495a31 100644 --- a/math_test.go +++ b/math_test.go @@ -3,6 +3,7 @@ package ajson import ( "errors" "math" + "testing" ) func ExampleAddFunction() { @@ -45,3 +46,327 @@ func ExampleAddOperation() { return BoolNode("neq", !res), nil }) } + +type operationTest struct { + name string + operation string + left *Node + right *Node + result *Node + fail bool +} + +func testNumOperation(operator string, results [3]float64) []*operationTest { + return []*operationTest{ + {name: "2" + operator + "2", operation: operator, left: NumericNode("", 2), right: NumericNode("", 2), result: NumericNode("", results[0])}, + {name: "3" + operator + "3", operation: operator, left: NumericNode("", 3), right: NumericNode("", 3), result: NumericNode("", results[1])}, + {name: "10" + operator + "3", operation: operator, left: NumericNode("", 10), right: NumericNode("", 3), result: NumericNode("", results[2])}, + {name: "X" + operator + "2", operation: operator, left: StringNode("", "X"), right: NumericNode("", 2), fail: true}, + {name: "2" + operator + "Y", operation: operator, left: NumericNode("", 2), right: StringNode("", "Y"), fail: true}, + } +} + +func testBoolOperation(operator string, results [4]bool) []*operationTest { + return []*operationTest{ + {name: "2" + operator + "2", operation: operator, left: NumericNode("", 2), right: NumericNode("", 2), result: BoolNode("", results[0])}, + {name: "3" + operator + "3", operation: operator, left: NumericNode("", 3), right: NumericNode("", 3), result: BoolNode("", results[1])}, + {name: "10" + operator + "0", operation: operator, left: NumericNode("", 10), right: NumericNode("", 0), result: BoolNode("", results[2])}, + {name: "0" + operator + "10", operation: operator, left: NumericNode("", 0), right: NumericNode("", 10), result: BoolNode("", results[3])}, + {name: "left error: " + operator, operation: operator, left: valueNode(nil, "", Numeric, "foo"), right: NumericNode("", 10), fail: true}, + {name: "right error: " + operator, operation: operator, left: NumericNode("", 10), right: valueNode(nil, "", Numeric, "foo"), fail: true}, + } +} +func testBooleanOperation(operator string, results [4]bool) []*operationTest { + return []*operationTest{ + {name: "2" + operator + "2", operation: operator, left: NumericNode("", 2), right: NumericNode("", 2), result: BoolNode("", results[0])}, + {name: "3" + operator + "3", operation: operator, left: NumericNode("", 3), right: NumericNode("", 3), result: BoolNode("", results[1])}, + {name: "10" + operator + "0", operation: operator, left: NumericNode("", 10), right: NumericNode("", 0), result: BoolNode("", results[2])}, + {name: "0" + operator + "10", operation: operator, left: NumericNode("", 0), right: NumericNode("", 10), result: BoolNode("", results[3])}, + } +} + +func TestOperations(t *testing.T) { + tests := []*operationTest{ + {name: "0/0", operation: "/", left: NumericNode("", 0), right: NumericNode("", 0), fail: true}, + {name: "1/0", operation: "/", left: NumericNode("", 1), right: NumericNode("", 0), fail: true}, + {name: "X+Y", operation: "+", left: StringNode("", "X"), right: StringNode("", "Y"), result: StringNode("", "XY")}, + } + tests = append(tests, testNumOperation("**", [3]float64{4, 27, 1000})...) + + tests = append(tests, testNumOperation("*", [3]float64{4, 9, 30})...) + tests = append(tests, testNumOperation("+", [3]float64{4, 6, 13})...) + tests = append(tests, testNumOperation("-", [3]float64{0, 0, 7})...) + tests = append(tests, testNumOperation("/", [3]float64{1, 1, 10. / 3.})...) + tests = append(tests, testNumOperation("%", [3]float64{0, 0, 1})...) + + tests = append(tests, testNumOperation("<<", [3]float64{8, 24, 80})...) + tests = append(tests, testNumOperation(">>", [3]float64{0, 0, 1})...) + tests = append(tests, testNumOperation("&", [3]float64{2, 3, 2})...) + tests = append(tests, testNumOperation("&^", [3]float64{0, 0, 8})...) + tests = append(tests, testNumOperation("|", [3]float64{2, 3, 11})...) + tests = append(tests, testNumOperation("^", [3]float64{0, 0, 9})...) + + tests = append(tests, testBoolOperation("==", [4]bool{true, true, false, false})...) + tests = append(tests, testBoolOperation("!=", [4]bool{false, false, true, true})...) + tests = append(tests, testBoolOperation("<", [4]bool{false, false, false, true})...) + tests = append(tests, testBoolOperation("<=", [4]bool{true, true, false, true})...) + tests = append(tests, testBoolOperation(">", [4]bool{false, false, true, false})...) + tests = append(tests, testBoolOperation(">=", [4]bool{true, true, true, false})...) + + tests = append(tests, testBooleanOperation("&&", [4]bool{true, true, false, false})...) + tests = append(tests, testBooleanOperation("||", [4]bool{true, true, true, true})...) + + _e := valueNode(nil, "", Numeric, "foo") + _t := NumericNode("", 1) + _f := NumericNode("", 0) + _false := BoolNode("", false) + _true := BoolNode("", true) + tests = append( + tests, + &operationTest{name: "error && true", operation: "&&", left: _e, right: _t, fail: true}, + &operationTest{name: "error && error", operation: "&&", left: _e, right: _e, fail: true}, + &operationTest{name: "error && false", operation: "&&", left: _e, right: _f, fail: true}, + &operationTest{name: "false && error", operation: "&&", left: _f, right: _e, result: _false}, + &operationTest{name: "true && error", operation: "&&", left: _t, right: _e, fail: true}, + + &operationTest{name: "error || true", operation: "||", left: _e, right: _t, fail: true}, + &operationTest{name: "error || error", operation: "||", left: _e, right: _e, fail: true}, + &operationTest{name: "error || false", operation: "||", left: _e, right: _f, fail: true}, + &operationTest{name: "false || error", operation: "||", left: _f, right: _e, fail: true}, + &operationTest{name: "true || error", operation: "||", left: _t, right: _e, result: _true}, + ) + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result, err := operations[test.operation](test.left, test.right) + if test.fail { + if err == nil { + t.Error("Expected error: nil given") + } + } else if err != nil { + t.Errorf("Unexpected error: %s", err.Error()) + } else if ok, err := result.Eq(test.result); !ok { + if err != nil { + t.Errorf("Unexpected error on comparation: %s", err.Error()) + } + t.Errorf("Wrong value: %v != %v", result.value.Load(), test.result.value.Load()) + } + }) + } +} + +func TestAddConstant(t *testing.T) { + name := "new_constant_name" + if _, ok := constants[name]; ok { + t.Error("test constant already exists") + } + AddConstant(name, NumericNode(name, 3.14)) + if _, ok := constants[name]; !ok { + t.Error("test constant was not added") + } +} + +func TestAddOperation(t *testing.T) { + name := "new_operation_name" + if _, ok := operations[name]; ok { + t.Error("test operation already exists") + } + AddOperation(name, 1, true, func(left *Node, right *Node) (result *Node, err error) { + return NumericNode("example", 1), nil + }) + if _, ok := operations[name]; !ok { + t.Error("test operation was not added") + } +} + +func TestAddFunction(t *testing.T) { + name := "new_function_name" + if _, ok := functions[name]; ok { + t.Error("test constant already exists") + } + AddFunction(name, func(node *Node) (result *Node, err error) { + return NumericNode("example", 2), nil + }) + if _, ok := functions[name]; !ok { + t.Error("test function was not added") + } +} + +func TestFunctions(t *testing.T) { + tests := []struct { + name string + fname string + value float64 + result float64 + }{ + {name: "abs 1", fname: "abs", value: float64(-100), result: 100}, + {name: "abs 2", fname: "abs", value: float64(100), result: 100}, + {name: "abs 3", fname: "abs", value: float64(0), result: 0}, + {name: "acos 1", fname: "acos", value: float64(0.5), result: math.Acos(0.5)}, + {name: "acosh 1", fname: "acosh", value: float64(100), result: math.Acosh(100)}, + {name: "asin 1", fname: "asin", value: float64(0.5), result: math.Asin(0.5)}, + {name: "asinh 1", fname: "asinh", value: float64(100), result: math.Asinh(100)}, + {name: "atan 1", fname: "atan", value: float64(100), result: math.Atan(100)}, + {name: "atanh 1", fname: "atanh", value: float64(0.5), result: math.Atanh(0.5)}, + {name: "cbrt 1", fname: "cbrt", value: float64(100), result: math.Cbrt(100)}, + {name: "ceil 1", fname: "ceil", value: float64(100), result: math.Ceil(100)}, + {name: "cos 1", fname: "cos", value: float64(100), result: math.Cos(100)}, + {name: "cosh 1", fname: "cosh", value: float64(100), result: math.Cosh(100)}, + {name: "erf 1", fname: "erf", value: float64(100), result: math.Erf(100)}, + {name: "erfc 1", fname: "erfc", value: float64(100), result: math.Erfc(100)}, + {name: "erfcinv 1", fname: "erfcinv", value: float64(0.5), result: math.Erfcinv(0.5)}, + {name: "erfinv 1", fname: "erfinv", value: float64(0.5), result: math.Erfinv(0.5)}, + {name: "exp 1", fname: "exp", value: float64(100), result: math.Exp(100)}, + {name: "exp2 1", fname: "exp2", value: float64(100), result: math.Exp2(100)}, + {name: "expm1 1", fname: "expm1", value: float64(100), result: math.Expm1(100)}, + {name: "floor 1", fname: "floor", value: float64(0), result: math.Floor(0)}, + {name: "floor 2", fname: "floor", value: float64(0.1), result: math.Floor(0.1)}, + {name: "floor 3", fname: "floor", value: float64(0.5), result: math.Floor(0.5)}, + {name: "floor 4", fname: "floor", value: float64(0.9), result: math.Floor(0.9)}, + {name: "floor 5", fname: "floor", value: float64(100), result: math.Floor(100)}, + {name: "gamma 1", fname: "gamma", value: float64(100), result: math.Gamma(100)}, + {name: "j0 1", fname: "j0", value: float64(100), result: math.J0(100)}, + {name: "j1 1", fname: "j1", value: float64(100), result: math.J1(100)}, + {name: "log 1", fname: "log", value: float64(100), result: math.Log(100)}, + {name: "log10 1", fname: "log10", value: float64(100), result: math.Log10(100)}, + {name: "log1p 1", fname: "log1p", value: float64(100), result: math.Log1p(100)}, + {name: "log2 1", fname: "log2", value: float64(100), result: math.Log2(100)}, + {name: "logb 1", fname: "logb", value: float64(100), result: math.Logb(100)}, + {name: "round 1", fname: "round", value: float64(0), result: math.Round(0)}, + {name: "round 2", fname: "round", value: float64(0.1), result: math.Round(0.1)}, + {name: "round 3", fname: "round", value: float64(0.5), result: math.Round(0.5)}, + {name: "round 4", fname: "round", value: float64(0.9), result: math.Round(0.9)}, + {name: "round 5", fname: "round", value: float64(100), result: math.Round(100)}, + {name: "roundtoeven 1", fname: "roundtoeven", value: float64(0), result: math.RoundToEven(0)}, + {name: "roundtoeven 2", fname: "roundtoeven", value: float64(0.5), result: math.RoundToEven(0.5)}, + {name: "roundtoeven 3", fname: "roundtoeven", value: float64(0.1), result: math.RoundToEven(0.1)}, + {name: "roundtoeven 4", fname: "roundtoeven", value: float64(0.9), result: math.RoundToEven(0.9)}, + {name: "roundtoeven 5", fname: "roundtoeven", value: float64(1), result: math.RoundToEven(1)}, + {name: "sin 1", fname: "sin", value: float64(100), result: math.Sin(100)}, + {name: "sinh 1", fname: "sinh", value: float64(100), result: math.Sinh(100)}, + {name: "sqrt 1", fname: "sqrt", value: float64(100), result: math.Sqrt(100)}, + {name: "tan 1", fname: "tan", value: float64(100), result: math.Tan(100)}, + {name: "tanh 1", fname: "tanh", value: float64(100), result: math.Tanh(100)}, + {name: "trunc 1", fname: "trunc", value: float64(100), result: math.Trunc(100)}, + {name: "y0 1", fname: "y0", value: float64(100), result: math.Y0(100)}, + {name: "y1 1", fname: "y1", value: float64(100), result: math.Y1(100)}, + + {name: "pow10", fname: "pow10", value: float64(10), result: math.Pow10(10)}, + {name: "factorial", fname: "factorial", value: float64(10), result: 3628800}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + node := NumericNode(test.name, test.value) + expected := NumericNode(test.fname, test.result) + result, err := functions[test.fname](node) + if err != nil { + t.Errorf("Unexpected error: %s", err.Error()) + } else if ok, err := result.Eq(expected); !ok { + if err != nil { + t.Errorf("Unexpected error on comparation: %s", err.Error()) + } + t.Errorf("Wrong value: %v != %v", result.value.Load(), expected.value.Load()) + } + }) + } +} + +func TestFunctions2(t *testing.T) { + _e := valueNode(nil, "", Numeric, "foo") + tests := []struct { + name string + fname string + value *Node + result *Node + fail bool + }{ + {name: "pow10 error", fname: "pow10", value: _e, fail: true}, + {name: "factorial error", fname: "factorial", value: _e, fail: true}, + {name: "abs error 1", fname: "abs", value: _e, fail: true}, + {name: "abs error 2", fname: "abs", value: StringNode("", ""), fail: true}, + + {name: "length", fname: "length", value: ArrayNode("test", []*Node{ + valueNode(nil, "", Numeric, "foo"), + valueNode(nil, "", Numeric, "foo"), + valueNode(nil, "", Numeric, "foo"), + }), result: NumericNode("", 3)}, + {name: "length error", fname: "length", value: _e, fail: true}, + {name: "avg error 1", fname: "avg", value: ArrayNode("test", []*Node{ + valueNode(nil, "", Numeric, "foo"), + valueNode(nil, "", Numeric, "foo"), + valueNode(nil, "", Numeric, "foo"), + }), fail: true}, + {name: "avg error 2", fname: "avg", value: _e, fail: true}, + {name: "avg array 1", fname: "avg", value: ArrayNode("test", []*Node{ + NumericNode("", 1), + NumericNode("", 1), + NumericNode("", 1), + NumericNode("", 1), + }), result: NumericNode("", 1)}, + {name: "avg array 2", fname: "avg", value: ArrayNode("test", []*Node{ + NumericNode("", 1), + NumericNode("", 2), + NumericNode("", 3), + }), result: NumericNode("", 2)}, + {name: "avg object", fname: "avg", value: ObjectNode("test", map[string]*Node{ + "q": NumericNode("", 1), + "w": NumericNode("", 2), + "e": NumericNode("", 3), + }), result: NumericNode("", 2)}, + {name: "avg array blank", fname: "avg", value: ArrayNode("test", []*Node{}), result: NumericNode("", 0)}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result, err := functions[test.fname](test.value) + if test.fail { + if err == nil { + t.Error("Expected error: nil given") + } + } else if err != nil { + t.Errorf("Unexpected error: %s", err.Error()) + } else if ok, err := result.Eq(test.result); !ok { + if err != nil { + t.Errorf("Unexpected error on comparation: %s", err.Error()) + } + t.Errorf("Wrong value: %v != %v", result.value.Load(), test.result.value.Load()) + } + }) + } +} + +func TestConstants(t *testing.T) { + tests := []struct { + name string + expected *Node + }{ + {name: "e", expected: NumericNode("e", float64(math.E))}, + {name: "pi", expected: NumericNode("pi", float64(math.Pi))}, + {name: "phi", expected: NumericNode("phi", float64(math.Phi))}, + + {name: "sqrt2", expected: NumericNode("sqrt2", float64(math.Sqrt2))}, + {name: "sqrte", expected: NumericNode("sqrte", float64(math.SqrtE))}, + {name: "sqrtpi", expected: NumericNode("sqrtpi", float64(math.SqrtPi))}, + {name: "sqrtphi", expected: NumericNode("sqrtphi", float64(math.SqrtPhi))}, + + {name: "ln2", expected: NumericNode("ln2", float64(math.Ln2))}, + {name: "log2e", expected: NumericNode("log2e", float64(math.Log2E))}, + {name: "ln10", expected: NumericNode("ln10", float64(math.Ln10))}, + {name: "log10e", expected: NumericNode("log10e", float64(math.Log10E))}, + + {name: "true", expected: BoolNode("true", true)}, + {name: "false", expected: BoolNode("false", false)}, + {name: "null", expected: NullNode("null")}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := constants[test.name] + if ok, err := result.Eq(test.expected); !ok { + if err != nil { + t.Errorf("Unexpected error on comparation: %s", err.Error()) + } + t.Errorf("Wrong value: %v != %v", result.value.Load(), test.expected.value.Load()) + } + }) + } +} diff --git a/node.go b/node.go index 8b8db54..69312f4 100644 --- a/node.go +++ b/node.go @@ -46,17 +46,17 @@ type NodeType int // Object = map[string]*Node // const ( - //Null is reflection of nil.(interface{}) + // Null is reflection of nil.(interface{}) Null NodeType = iota - //Numeric is reflection of float64 + // Numeric is reflection of float64 Numeric - //String is reflection of string + // String is reflection of string String - //Bool is reflection of bool + // Bool is reflection of bool Bool - //Array is reflection of []*Node + // Array is reflection of []*Node Array - //Object is reflection of map[string]*Node + // Object is reflection of map[string]*Node Object ) @@ -179,12 +179,12 @@ func valueNode(parent *Node, key string, _type NodeType, value interface{}) (cur return } -//Parent returns link to the parent of current node, nil for root +// Parent returns link to the parent of current node, nil for root func (n *Node) Parent() *Node { return n.parent } -//Source returns slice of bytes, which was identified to be current node +// Source returns slice of bytes, which was identified to be current node func (n *Node) Source() []byte { if n.ready() { return (*n.data)[n.borders[0]:n.borders[1]] @@ -192,7 +192,7 @@ func (n *Node) Source() []byte { return nil } -//String is implementation of Stringer interface, returns string based on source part +// String is implementation of Stringer interface, returns string based on source part func (n *Node) String() string { if n.ready() { return string(n.Source()) @@ -204,27 +204,27 @@ func (n *Node) String() string { return "null" } -//Type will return type of current node +// Type will return type of current node func (n *Node) Type() NodeType { return n._type } -//Key will return key of current node, please check, that parent of this node has an Object type +// Key will return key of current node, please check, that parent of this node has an Object type func (n *Node) Key() string { return *n.key } -//Index will return index of current node, please check, that parent of this node has an Array type +// Index will return index of current node, please check, that parent of this node has an Array type func (n *Node) Index() int { return *n.index } -//Size will return count of children of current node, please check, that parent of this node has an Array type +// Size will return count of children of current node, please check, that parent of this node has an Array type func (n *Node) Size() int { return len(n.children) } -//Keys will return count all keys of children of current node, please check, that parent of this node has an Object type +// Keys will return count all keys of children of current node, please check, that parent of this node has an Object type func (n *Node) Keys() (result []string) { result = make([]string, 0, len(n.children)) for key := range n.children { @@ -233,37 +233,37 @@ func (n *Node) Keys() (result []string) { return } -//IsArray returns true if current node is Array +// IsArray returns true if current node is Array func (n *Node) IsArray() bool { return n._type == Array } -//IsObject returns true if current node is Object +// IsObject returns true if current node is Object func (n *Node) IsObject() bool { return n._type == Object } -//IsNull returns true if current node is Null +// IsNull returns true if current node is Null func (n *Node) IsNull() bool { return n._type == Null } -//IsNumeric returns true if current node is Numeric +// IsNumeric returns true if current node is Numeric func (n *Node) IsNumeric() bool { return n._type == Numeric } -//IsString returns true if current node is String +// IsString returns true if current node is String func (n *Node) IsString() bool { return n._type == String } -//IsBool returns true if current node is Bool +// IsBool returns true if current node is Bool func (n *Node) IsBool() bool { return n._type == Bool } -//Value is calculating and returns a value of current node. +// Value is calculating and returns a value of current node. // // It returns nil, if current node type is Null. // @@ -319,7 +319,7 @@ func (n *Node) Value() (value interface{}, err error) { return } -//GetNull returns nil, if current type is Null, else: WrongType error +// GetNull returns nil, if current type is Null, else: WrongType error func (n *Node) GetNull() (value interface{}, err error) { if n._type != Null { return value, errorType() @@ -327,7 +327,7 @@ func (n *Node) GetNull() (value interface{}, err error) { return } -//GetNumeric returns float64, if current type is Numeric, else: WrongType error +// GetNumeric returns float64, if current type is Numeric, else: WrongType error func (n *Node) GetNumeric() (value float64, err error) { if n._type != Numeric { return value, errorType() @@ -343,7 +343,7 @@ func (n *Node) GetNumeric() (value float64, err error) { return value, nil } -//GetString returns string, if current type is String, else: WrongType error +// GetString returns string, if current type is String, else: WrongType error func (n *Node) GetString() (value string, err error) { if n._type != String { return value, errorType() @@ -359,7 +359,7 @@ func (n *Node) GetString() (value string, err error) { return value, nil } -//GetBool returns bool, if current type is Bool, else: WrongType error +// GetBool returns bool, if current type is Bool, else: WrongType error func (n *Node) GetBool() (value bool, err error) { if n._type != Bool { return value, errorType() @@ -375,7 +375,7 @@ func (n *Node) GetBool() (value bool, err error) { return value, nil } -//GetArray returns []*Node, if current type is Array, else: WrongType error +// GetArray returns []*Node, if current type is Array, else: WrongType error func (n *Node) GetArray() (value []*Node, err error) { if n._type != Array { return value, errorType() @@ -391,7 +391,7 @@ func (n *Node) GetArray() (value []*Node, err error) { return value, nil } -//GetObject returns map[string]*Node, if current type is Object, else: WrongType error +// GetObject returns map[string]*Node, if current type is Object, else: WrongType error func (n *Node) GetObject() (value map[string]*Node, err error) { if n._type != Object { return value, errorType() @@ -407,7 +407,7 @@ func (n *Node) GetObject() (value map[string]*Node, err error) { return value, nil } -//MustNull returns nil, if current type is Null, else: panic if error happened +// MustNull returns nil, if current type is Null, else: panic if error happened func (n *Node) MustNull() (value interface{}) { value, err := n.GetNull() if err != nil { @@ -416,7 +416,7 @@ func (n *Node) MustNull() (value interface{}) { return } -//MustNumeric returns float64, if current type is Numeric, else: panic if error happened +// MustNumeric returns float64, if current type is Numeric, else: panic if error happened func (n *Node) MustNumeric() (value float64) { value, err := n.GetNumeric() if err != nil { @@ -425,7 +425,7 @@ func (n *Node) MustNumeric() (value float64) { return } -//MustString returns string, if current type is String, else: panic if error happened +// MustString returns string, if current type is String, else: panic if error happened func (n *Node) MustString() (value string) { value, err := n.GetString() if err != nil { @@ -434,7 +434,7 @@ func (n *Node) MustString() (value string) { return } -//MustBool returns bool, if current type is Bool, else: panic if error happened +// MustBool returns bool, if current type is Bool, else: panic if error happened func (n *Node) MustBool() (value bool) { value, err := n.GetBool() if err != nil { @@ -443,7 +443,7 @@ func (n *Node) MustBool() (value bool) { return } -//MustArray returns []*Node, if current type is Array, else: panic if error happened +// MustArray returns []*Node, if current type is Array, else: panic if error happened func (n *Node) MustArray() (value []*Node) { value, err := n.GetArray() if err != nil { @@ -452,7 +452,7 @@ func (n *Node) MustArray() (value []*Node) { return } -//MustObject returns map[string]*Node, if current type is Object, else: panic if error happened +// MustObject returns map[string]*Node, if current type is Object, else: panic if error happened func (n *Node) MustObject() (value map[string]*Node) { value, err := n.GetObject() if err != nil { @@ -461,7 +461,7 @@ func (n *Node) MustObject() (value map[string]*Node) { return } -//Unpack will produce current node to it's interface, recursively with all underlying nodes (in contrast to Node.Value). +// Unpack will produce current node to it's interface, recursively with all underlying nodes (in contrast to Node.Value). func (n *Node) Unpack() (value interface{}, err error) { switch n._type { case Null: @@ -500,7 +500,7 @@ func (n *Node) Unpack() (value interface{}, err error) { return } -//GetIndex will return child node of current array node. If current node is not Array, or index is unavailable, will return error +// GetIndex will return child node of current array node. If current node is not Array, or index is unavailable, will return error func (n *Node) GetIndex(index int) (*Node, error) { if n._type != Array { return nil, errorType() @@ -512,7 +512,7 @@ func (n *Node) GetIndex(index int) (*Node, error) { return child, nil } -//MustIndex will return child node of current array node. If current node is not Array, or index is unavailable, raise a panic +// MustIndex will return child node of current array node. If current node is not Array, or index is unavailable, raise a panic func (n *Node) MustIndex(index int) (value *Node) { value, err := n.GetIndex(index) if err != nil { @@ -521,7 +521,7 @@ func (n *Node) MustIndex(index int) (value *Node) { return } -//GetKey will return child node of current object node. If current node is not Object, or key is unavailable, will return error +// GetKey will return child node of current object node. If current node is not Object, or key is unavailable, will return error func (n *Node) GetKey(key string) (*Node, error) { if n._type != Object { return nil, errorType() @@ -533,7 +533,7 @@ func (n *Node) GetKey(key string) (*Node, error) { return value, nil } -//MustKey will return child node of current object node. If current node is not Object, or key is unavailable, raise a panic +// MustKey will return child node of current object node. If current node is not Object, or key is unavailable, raise a panic func (n *Node) MustKey(key string) (value *Node) { value, err := n.GetKey(key) if err != nil { @@ -542,13 +542,13 @@ func (n *Node) MustKey(key string) (value *Node) { return } -//HasKey will return boolean value, if current object node has custom key +// HasKey will return boolean value, if current object node has custom key func (n *Node) HasKey(key string) bool { _, ok := n.children[key] return ok } -//Empty method check if current container node has no children +// Empty method check if current container node has no children func (n *Node) Empty() bool { return len(n.children) == 0 } diff --git a/node_test.go b/node_test.go index a30049a..6a135d7 100644 --- a/node_test.go +++ b/node_test.go @@ -66,6 +66,7 @@ func TestNode_Unpack(t *testing.T) { root, err := Unmarshal([]byte(test.value)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } unpacked, err := root.Unpack() if err != nil { @@ -93,6 +94,7 @@ func TestNode_Value(t *testing.T) { }`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } iValue, err := root.Value() if err != nil { @@ -119,6 +121,7 @@ func TestNode_Empty(t *testing.T) { }`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } iValue, err := root.Value() if err != nil { @@ -146,6 +149,7 @@ func TestNode_GetArray(t *testing.T) { root, err := Unmarshal([]byte(`[1, 2, 3]`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } array, err := root.GetArray() if err != nil { @@ -166,6 +170,7 @@ func TestNode_MustArray(t *testing.T) { root, err := Unmarshal([]byte(`[1, 2, 3]`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } array := root.MustArray() if len(array) != 3 { @@ -177,6 +182,7 @@ func TestNode_GetBool(t *testing.T) { root, err := Unmarshal([]byte(`true`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value, err := root.GetBool() if err != nil { @@ -197,6 +203,7 @@ func TestNode_MustBool(t *testing.T) { root, err := Unmarshal([]byte(`true`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.MustBool() if !value { @@ -208,10 +215,12 @@ func TestNode_GetIndex(t *testing.T) { root, err := Unmarshal([]byte(`[1, 2, 3]`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value, err := root.GetIndex(1) if err != nil { t.Errorf("Error on root.GetIndex(): %s", err.Error()) + return } if value.MustNumeric() != 2 { t.Errorf("root.GetIndex() is corrupted") @@ -229,6 +238,7 @@ func TestNode_MustIndex(t *testing.T) { root, err := Unmarshal([]byte(`[1, 2, 3]`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.MustIndex(1) if value.MustNumeric() != 2 { @@ -240,10 +250,12 @@ func TestNode_GetKey(t *testing.T) { root, err := Unmarshal([]byte(`{"foo":2,"bar":null}`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value, err := root.GetKey("foo") if err != nil { t.Errorf("Error on root.GetKey(): %s", err.Error()) + return } if value.MustNumeric() != 2 { t.Errorf("root.GetKey() is corrupted") @@ -261,6 +273,7 @@ func TestNode_MustKey(t *testing.T) { root, err := Unmarshal([]byte(`{"foo":2,"bar":null}`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.MustKey("foo") if value.MustNumeric() != 2 { @@ -272,6 +285,7 @@ func TestNode_GetNull(t *testing.T) { root, err := Unmarshal([]byte(`null`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value, err := root.GetNull() if err != nil { @@ -292,6 +306,7 @@ func TestNode_MustNull(t *testing.T) { root, err := Unmarshal([]byte(`null`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.MustNull() if value != nil { @@ -303,6 +318,7 @@ func TestNode_GetNumeric(t *testing.T) { root, err := Unmarshal([]byte(`123`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value, err := root.GetNumeric() if err != nil { @@ -329,6 +345,7 @@ func TestNode_MustNumeric(t *testing.T) { root, err := Unmarshal([]byte(`123`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.MustNumeric() if value != float64(123) { @@ -340,6 +357,7 @@ func TestNode_GetObject(t *testing.T) { root, err := Unmarshal([]byte(`{"foo":true,"bar":null}`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value, err := root.GetObject() if err != nil { @@ -363,6 +381,7 @@ func TestNode_MustObject(t *testing.T) { root, err := Unmarshal([]byte(`{"foo":true,"bar":null}`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.MustObject() if _, ok := value["foo"]; !ok { @@ -377,6 +396,7 @@ func TestNode_GetString(t *testing.T) { root, err := Unmarshal([]byte(`"123"`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value, err := root.GetString() if err != nil { @@ -397,6 +417,7 @@ func TestNode_MustString(t *testing.T) { root, err := Unmarshal([]byte(`"123"`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.MustString() if value != "123" { @@ -408,6 +429,7 @@ func TestNode_Index(t *testing.T) { root, err := Unmarshal([]byte(`[1, 2, 3]`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } array := root.MustArray() for i, node := range array { @@ -421,6 +443,7 @@ func TestNode_Key(t *testing.T) { root, err := Unmarshal([]byte(`{"foo":"bar", "baz":null}`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } object := root.MustObject() for key, node := range object { @@ -434,6 +457,7 @@ func TestNode_IsArray(t *testing.T) { root, err := Unmarshal([]byte(`[1, 2, 3]`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } if !root.IsArray() { t.Errorf("Wrong root.IsArray()") @@ -459,6 +483,7 @@ func TestNode_IsObject(t *testing.T) { root, err := Unmarshal([]byte(`{"foo":null}`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } if root.IsArray() { t.Errorf("Wrong root.IsArray()") @@ -484,6 +509,7 @@ func TestNode_IsString(t *testing.T) { root, err := Unmarshal([]byte(`"123"`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } if root.IsArray() { t.Errorf("Wrong root.IsArray()") @@ -509,6 +535,7 @@ func TestNode_IsNumeric(t *testing.T) { root, err := Unmarshal([]byte(`+1.23e-2`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } if root.IsArray() { t.Errorf("Wrong root.IsArray()") @@ -534,6 +561,7 @@ func TestNode_IsBool(t *testing.T) { root, err := Unmarshal([]byte(`true`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } if root.IsArray() { t.Errorf("Wrong root.IsArray()") @@ -559,6 +587,7 @@ func TestNode_IsNull(t *testing.T) { root, err := Unmarshal([]byte(`null`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } if root.IsArray() { t.Errorf("Wrong root.IsArray()") @@ -584,6 +613,7 @@ func TestNode_Keys(t *testing.T) { root, err := Unmarshal([]byte(`{"foo":true,"bar":null}`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.Keys() if len(value) != 2 { @@ -601,6 +631,7 @@ func TestNode_Size(t *testing.T) { root, err := Unmarshal([]byte(`[1,2,3,4]`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.Size() if value != 4 { @@ -612,6 +643,7 @@ func TestNode_Parent(t *testing.T) { root, err := Unmarshal([]byte(`{"foo":true,"bar":null}`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.Parent() if value != nil { @@ -627,6 +659,7 @@ func TestNode_Source(t *testing.T) { root, err := Unmarshal([]byte(`{"foo":true,"bar":null}`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.Source() if !bytes.Equal(value, []byte(`{"foo":true,"bar":null}`)) { @@ -638,6 +671,7 @@ func TestNode_String(t *testing.T) { root, err := Unmarshal([]byte(`{"foo":true,"bar":null}`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } value := root.String() if value != `{"foo":true,"bar":null}` { @@ -689,6 +723,7 @@ func TestNode_HasKey(t *testing.T) { root, err := Unmarshal([]byte(`{"foo":true,"bar":null}`)) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } if !root.HasKey("foo") { t.Errorf("Wrong root.HasKey('foo')") @@ -719,6 +754,7 @@ func TestNode_Path(t *testing.T) { root, err := Unmarshal(data) if err != nil { t.Errorf("Error on Unmarshal(): %s", err.Error()) + return } if root.Path() != "$" { t.Errorf("Wrong root.Path()") @@ -830,10 +866,12 @@ func TestNode_Eq(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { actual, err := test.left.Eq(test.right) - if err != nil { - if !test.error { - t.Errorf("Error on node.Eq(): %s", err.Error()) + if test.error { + if err == nil { + t.Errorf("Error expected: nil given") } + } else if err != nil { + t.Errorf("Error on node.Eq(): %s", err.Error()) } else if actual != test.expected { t.Errorf("Failed node.Eq()") }