package toml import ( "fmt" "sort" "strings" "testing" ) type queryTestNode struct { value interface{} position Position } func valueString(root interface{}) string { result := "" //fmt.Sprintf("%T:", root) switch node := root.(type) { case *tomlValue: return valueString(node.value) case *QueryResult: items := []string{} for i, v := range node.Values() { items = append(items, fmt.Sprintf("%s:%s", node.Positions()[i].String(), valueString(v))) } sort.Strings(items) result = "[" + strings.Join(items, ", ") + "]" case queryTestNode: result = fmt.Sprintf("%s:%s", node.position.String(), valueString(node.value)) case []interface{}: items := []string{} for _, v := range node { items = append(items, valueString(v)) } sort.Strings(items) result = "[" + strings.Join(items, ", ") + "]" case *TomlTree: // workaround for unreliable map key ordering items := []string{} for _, k := range node.Keys() { v := node.GetPath([]string{k}) items = append(items, k+":"+valueString(v)) } sort.Strings(items) result = "{" + strings.Join(items, ", ") + "}" case map[string]interface{}: // workaround for unreliable map key ordering items := []string{} for k, v := range node { items = append(items, k+":"+valueString(v)) } sort.Strings(items) result = "{" + strings.Join(items, ", ") + "}" case int64: result += fmt.Sprintf("%d", node) case string: result += "'" + node + "'" } return result } func assertValue(t *testing.T, result, ref interface{}) { pathStr := valueString(result) refStr := valueString(ref) if pathStr != refStr { t.Errorf("values do not match") t.Log("test:", pathStr) t.Log("ref: ", refStr) } } func assertQueryPositions(t *testing.T, toml, query string, ref []interface{}) { tree, err := Load(toml) if err != nil { t.Errorf("Non-nil toml parse error: %v", err) return } q, err := Compile(query) if err != nil { t.Error(err) return } results := q.Execute(tree) assertValue(t, results, ref) } func TestQueryRoot(t *testing.T) { assertQueryPositions(t, "a = 42", "$", []interface{}{ queryTestNode{ map[string]interface{}{ "a": int64(42), }, Position{1, 1}, }, }) } func TestQueryKey(t *testing.T) { assertQueryPositions(t, "[foo]\na = 42", "$.foo.a", []interface{}{ queryTestNode{ int64(42), Position{2, 1}, }, }) } func TestQueryKeyString(t *testing.T) { assertQueryPositions(t, "[foo]\na = 42", "$.foo['a']", []interface{}{ queryTestNode{ int64(42), Position{2, 1}, }, }) } func TestQueryIndex(t *testing.T) { assertQueryPositions(t, "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", "$.foo.a[5]", []interface{}{ queryTestNode{ int64(6), Position{2, 1}, }, }) } func TestQuerySliceRange(t *testing.T) { assertQueryPositions(t, "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", "$.foo.a[0:5]", []interface{}{ queryTestNode{ int64(1), Position{2, 1}, }, queryTestNode{ int64(2), Position{2, 1}, }, queryTestNode{ int64(3), Position{2, 1}, }, queryTestNode{ int64(4), Position{2, 1}, }, queryTestNode{ int64(5), Position{2, 1}, }, }) } func TestQuerySliceStep(t *testing.T) { assertQueryPositions(t, "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", "$.foo.a[0:5:2]", []interface{}{ queryTestNode{ int64(1), Position{2, 1}, }, queryTestNode{ int64(3), Position{2, 1}, }, queryTestNode{ int64(5), Position{2, 1}, }, }) } func TestQueryAny(t *testing.T) { assertQueryPositions(t, "[foo.bar]\na=1\nb=2\n[foo.baz]\na=3\nb=4", "$.foo.*", []interface{}{ queryTestNode{ map[string]interface{}{ "a": int64(1), "b": int64(2), }, Position{1, 1}, }, queryTestNode{ map[string]interface{}{ "a": int64(3), "b": int64(4), }, Position{4, 1}, }, }) } func TestQueryUnionSimple(t *testing.T) { assertQueryPositions(t, "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", "$.*[bar,foo]", []interface{}{ queryTestNode{ map[string]interface{}{ "a": int64(1), "b": int64(2), }, Position{1, 1}, }, queryTestNode{ map[string]interface{}{ "a": int64(3), "b": int64(4), }, Position{4, 1}, }, queryTestNode{ map[string]interface{}{ "a": int64(5), "b": int64(6), }, Position{7, 1}, }, }) } func TestQueryRecursionAll(t *testing.T) { assertQueryPositions(t, "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", "$..*", []interface{}{ queryTestNode{ map[string]interface{}{ "bar": map[string]interface{}{ "a": int64(1), "b": int64(2), }, }, Position{1, 1}, }, queryTestNode{ map[string]interface{}{ "a": int64(1), "b": int64(2), }, Position{1, 1}, }, queryTestNode{ int64(1), Position{2, 1}, }, queryTestNode{ int64(2), Position{3, 1}, }, queryTestNode{ map[string]interface{}{ "foo": map[string]interface{}{ "a": int64(3), "b": int64(4), }, }, Position{4, 1}, }, queryTestNode{ map[string]interface{}{ "a": int64(3), "b": int64(4), }, Position{4, 1}, }, queryTestNode{ int64(3), Position{5, 1}, }, queryTestNode{ int64(4), Position{6, 1}, }, queryTestNode{ map[string]interface{}{ "foo": map[string]interface{}{ "a": int64(5), "b": int64(6), }, }, Position{7, 1}, }, queryTestNode{ map[string]interface{}{ "a": int64(5), "b": int64(6), }, Position{7, 1}, }, queryTestNode{ int64(5), Position{8, 1}, }, queryTestNode{ int64(6), Position{9, 1}, }, }) } func TestQueryRecursionUnionSimple(t *testing.T) { assertQueryPositions(t, "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", "$..['foo','bar']", []interface{}{ queryTestNode{ map[string]interface{}{ "a": int64(1), "b": int64(2), }, Position{1, 1}, }, queryTestNode{ map[string]interface{}{ "a": int64(3), "b": int64(4), }, Position{4, 1}, }, queryTestNode{ map[string]interface{}{ "a": int64(5), "b": int64(6), }, Position{7, 1}, }, }) } func TestQueryScriptFnLast(t *testing.T) { assertQueryPositions(t, "[foo]\na = [0,1,2,3,4,5,6,7,8,9]", "$.foo.a[(last)]", []interface{}{ queryTestNode{ int64(9), Position{2, 1}, }, }) } func TestQueryFilterFnOdd(t *testing.T) { assertQueryPositions(t, "[foo]\na = [0,1,2,3,4,5,6,7,8,9]", "$.foo.a[?(odd)]", []interface{}{ queryTestNode{ int64(1), Position{2, 1}, }, queryTestNode{ int64(3), Position{2, 1}, }, queryTestNode{ int64(5), Position{2, 1}, }, queryTestNode{ int64(7), Position{2, 1}, }, queryTestNode{ int64(9), Position{2, 1}, }, }) } func TestQueryFilterFnEven(t *testing.T) { assertQueryPositions(t, "[foo]\na = [0,1,2,3,4,5,6,7,8,9]", "$.foo.a[?(even)]", []interface{}{ queryTestNode{ int64(0), Position{2, 1}, }, queryTestNode{ int64(2), Position{2, 1}, }, queryTestNode{ int64(4), Position{2, 1}, }, queryTestNode{ int64(6), Position{2, 1}, }, queryTestNode{ int64(8), Position{2, 1}, }, }) }