Changed match func strategy; added tests
As it turns out, closures are very hard to validate without running them. Since table-driven tests tend to rely on value types that can be compared directly, using structs that adhere to a generic callback interface is more work, but is more easily tested. * Changed jsonpath match functions to structs with Call() methods * Added tests to verify the generation of jsonpath QueryPath data * Added tests to verify jsonpath lexer * Fixed jsonpath whitespace handling bug * Fixed numerous flaws in jsonpath parser
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
package jpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func pathString(path QueryPath) string {
|
||||
result := "["
|
||||
for _, v := range path {
|
||||
result += fmt.Sprintf("%T:%v, ", v, v)
|
||||
}
|
||||
return result + "]"
|
||||
}
|
||||
|
||||
func assertPathMatch(t *testing.T, path, ref QueryPath) bool {
|
||||
if len(path) != len(ref) {
|
||||
t.Errorf("lengths do not match: %v vs %v",
|
||||
pathString(path), pathString(ref))
|
||||
return false
|
||||
} else {
|
||||
for i, v := range ref {
|
||||
pass := false
|
||||
node := path[i]
|
||||
// compare by value
|
||||
switch refNode := v.(type) {
|
||||
case *matchKeyFn:
|
||||
castNode, ok := node.(*matchKeyFn)
|
||||
pass = ok && (*refNode == *castNode)
|
||||
case *matchIndexFn:
|
||||
castNode, ok := node.(*matchIndexFn)
|
||||
pass = ok && (*refNode == *castNode)
|
||||
case *matchSliceFn:
|
||||
castNode, ok := node.(*matchSliceFn)
|
||||
pass = ok && (*refNode == *castNode)
|
||||
case *matchAnyFn:
|
||||
castNode, ok := node.(*matchAnyFn)
|
||||
pass = ok && (*refNode == *castNode)
|
||||
case *matchUnionFn:
|
||||
castNode, ok := node.(*matchUnionFn)
|
||||
// special case - comapre all contents
|
||||
pass = ok && assertPathMatch(t, castNode.Union, refNode.Union)
|
||||
case *matchRecursiveFn:
|
||||
castNode, ok := node.(*matchRecursiveFn)
|
||||
pass = ok && (*refNode == *castNode)
|
||||
}
|
||||
if !pass {
|
||||
t.Errorf("paths do not match at index %d: %v vs %v",
|
||||
i, pathString(path), pathString(ref))
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func assertPath(t *testing.T, query string, ref QueryPath) {
|
||||
_, flow := lex(query)
|
||||
path := parse(flow)
|
||||
assertPathMatch(t, path, ref)
|
||||
}
|
||||
|
||||
func TestPathRoot(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$",
|
||||
QueryPath{
|
||||
// empty
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathKey(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$.foo",
|
||||
QueryPath{
|
||||
&matchKeyFn{ "foo" },
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathBracketKey(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[foo]",
|
||||
QueryPath{
|
||||
&matchKeyFn{ "foo" },
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathBracketStringKey(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$['foo']",
|
||||
QueryPath{
|
||||
&matchKeyFn{ "foo" },
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathIndex(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123]",
|
||||
QueryPath{
|
||||
&matchIndexFn{ 123 },
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathSliceStart(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123:]",
|
||||
QueryPath{
|
||||
&matchSliceFn{ 123, math.MaxInt64, 1 },
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathSliceStartEnd(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123:456]",
|
||||
QueryPath{
|
||||
&matchSliceFn{ 123, 456, 1 },
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathSliceStartEndColon(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123:456:]",
|
||||
QueryPath{
|
||||
&matchSliceFn{ 123, 456, 1 },
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathSliceStartStep(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123::7]",
|
||||
QueryPath{
|
||||
&matchSliceFn{ 123, math.MaxInt64, 7 },
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathSliceEndStep(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[:456:7]",
|
||||
QueryPath{
|
||||
&matchSliceFn{ 0, 456, 7 },
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathSliceStep(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[::7]",
|
||||
QueryPath{
|
||||
&matchSliceFn{ 0, math.MaxInt64, 7 },
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathSliceAll(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123:456:7]",
|
||||
QueryPath{
|
||||
&matchSliceFn{ 123, 456, 7 },
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathAny(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$.*",
|
||||
QueryPath{
|
||||
&matchAnyFn{},
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathUnion(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[foo, bar, baz]",
|
||||
QueryPath{
|
||||
&matchUnionFn{ []PathFn {
|
||||
&matchKeyFn{ "foo" },
|
||||
&matchKeyFn{ "bar" },
|
||||
&matchKeyFn{ "baz" },
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
func TestPathRecurse(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$..*",
|
||||
QueryPath{
|
||||
&matchRecursiveFn{},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user