Skip to content

Commit 8cb700e

Browse files
committed
tests; now handle arrays as set
1 parent 9212f25 commit 8cb700e

11 files changed

Lines changed: 98 additions & 32 deletions

dfl/After_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ package dfl
1010
import (
1111
"reflect"
1212
"testing"
13-
"time"
13+
"time"
1414
)
1515

1616
import (
@@ -20,9 +20,9 @@ import (
2020
func TestAfter(t *testing.T) {
2121

2222
ctx := Context{
23-
"a": time.Date(2018, time.April, 2, 3, 28, 56, 0, time.UTC),
24-
"b": time.Date(2018, time.May, 2, 3, 28, 56, 0, time.UTC),
25-
}
23+
"a": time.Date(2018, time.April, 2, 3, 28, 56, 0, time.UTC),
24+
"b": time.Date(2018, time.May, 2, 3, 28, 56, 0, time.UTC),
25+
}
2626

2727
testCases := []TestCase{
2828
NewTestCase("2017-01-01 after 2018-01-01", ctx, false),

dfl/Before_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ package dfl
1010
import (
1111
"reflect"
1212
"testing"
13-
"time"
13+
"time"
1414
)
1515

1616
import (
@@ -20,9 +20,9 @@ import (
2020
func TestBefore(t *testing.T) {
2121

2222
ctx := Context{
23-
"a": time.Date(2018, time.April, 2, 3, 28, 56, 0, time.UTC),
24-
"b": time.Date(2018, time.May, 2, 3, 28, 56, 0, time.UTC),
25-
}
23+
"a": time.Date(2018, time.April, 2, 3, 28, 56, 0, time.UTC),
24+
"b": time.Date(2018, time.May, 2, 3, 28, 56, 0, time.UTC),
25+
}
2626

2727
testCases := []TestCase{
2828
NewTestCase("2017-01-01 before 2018-01-01", ctx, true),

dfl/ILike_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ func TestILike(t *testing.T) {
2525
NewTestCase("@a ilike cafe", ctx, false),
2626
NewTestCase("bar ilike @b", ctx, true),
2727
NewTestCase("@a ilike @b", ctx, false),
28-
NewTestCase("@a ilike cafe%", ctx, true),
29-
NewTestCase("@a ilike %bar", ctx, true),
30-
NewTestCase("@a ilike %and%", ctx, true),
28+
NewTestCase("@a ilike cafe%", ctx, true),
29+
NewTestCase("@a ilike %bar", ctx, true),
30+
NewTestCase("@a ilike %and%", ctx, true),
3131
}
3232

3333
for _, testCase := range testCases {

dfl/In.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ func (i In) Evaluate(ctx Context, funcs FunctionMap) (interface{}, error) {
6363
return strings.Contains(fmt.Sprint(rv), lvs), nil
6464
case float64:
6565
return strings.Contains(strconv.FormatFloat(rv.(float64), 'f', 6, 64), lvs), nil
66+
case map[string]struct{}:
67+
_, ok := rv.(map[string]struct{})[lvs]
68+
return ok, nil
69+
case []string:
70+
for _, x := range rv.([]string) {
71+
if lvs == x {
72+
return true, nil
73+
}
74+
}
75+
return false, nil
6676
case []interface{}:
6777
for _, x := range rv.([]interface{}) {
6878
if lvs == fmt.Sprint(x) {

dfl/In_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// =================================================================
2+
//
3+
// Copyright (C) 2018 Spatial Current, Inc. - All Rights Reserved
4+
// Released as open source under the MIT License. See LICENSE file.
5+
//
6+
// =================================================================
7+
8+
package dfl
9+
10+
import (
11+
"reflect"
12+
"testing"
13+
)
14+
15+
import (
16+
"github.com/pkg/errors"
17+
)
18+
19+
func TestIn(t *testing.T) {
20+
21+
ctx := Context{"a": "cafe", "b": []string{"bar", "cafe"}}
22+
23+
testCases := []TestCase{
24+
NewTestCase("bar in @b", ctx, true),
25+
NewTestCase("@a in @b", ctx, true),
26+
NewTestCase("bar in [bar, cafe]", ctx, true),
27+
NewTestCase("fast_food in [bar, cafe]", ctx, false),
28+
NewTestCase("fast_food in @b", ctx, false),
29+
}
30+
31+
for _, testCase := range testCases {
32+
node, err := Parse(testCase.Expression)
33+
if err != nil {
34+
t.Errorf(errors.Wrap(err, "Error parsing expression \""+testCase.Expression+"\"").Error())
35+
continue
36+
}
37+
node = node.Compile()
38+
got, err := node.Evaluate(testCase.Context, FunctionMap{})
39+
if err != nil {
40+
t.Errorf(errors.Wrap(err, "Error evaluating expression \""+testCase.Expression+"\"").Error())
41+
} else if got != testCase.Result {
42+
t.Errorf("TestILike(%q) == %v (%q), want %v (%q)", testCase.Expression, got, reflect.TypeOf(got), testCase.Result, reflect.TypeOf(testCase.Result))
43+
}
44+
}
45+
46+
}

dfl/Like_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ func TestLike(t *testing.T) {
2525
NewTestCase("@a like cafe", ctx, false),
2626
NewTestCase("bar like @b", ctx, true),
2727
NewTestCase("@a like @b", ctx, false),
28-
NewTestCase("@a like cafe%", ctx, true),
29-
NewTestCase("@a like %bar", ctx, true),
30-
NewTestCase("@a like %and%", ctx, true),
28+
NewTestCase("@a like cafe%", ctx, true),
29+
NewTestCase("@a like %bar", ctx, true),
30+
NewTestCase("@a like %and%", ctx, true),
3131
}
3232

3333
for _, testCase := range testCases {

dfl/Parse.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func Parse(in string) (Node, error) {
6767
if len(s) >= 2 && ((strings.HasPrefix(s, "'") && strings.HasSuffix(s, "'")) || (strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\""))) {
6868
return ParseLiteral(s[1:len(s)-1], remainder)
6969
} else if len(s) >= 2 && strings.HasPrefix(s, "[") && strings.HasSuffix(s, "]") {
70-
return ParseArray(s[1:len(s)-1], remainder)
70+
return ParseSet(s[1:len(s)-1], remainder)
7171
} else if len(s) >= 2 && strings.HasPrefix(s, "(") && strings.HasSuffix(s, ")") {
7272
return ParseSub(s[1:len(s)-1], remainder)
7373
} else if s_lc == "and" {

dfl/ParseArray.go renamed to dfl/ParseSet.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ import (
1717
"github.com/pkg/errors"
1818
)
1919

20-
// ParseArray parses an Array Node and recursively any remainder.
20+
// ParseSet parses a Set Node and recursively any remainder.
2121
// If parameter "in" is gramatically a child node, then return the parent node.
22-
// DFL arrays can include Attribute or Literal Nodes.
22+
// DFL sets can include Attribute or Literal Nodes.
2323
// As all attribute references must start with an "@" character, parantheses are optional for literals except if a comma exists.
2424
// Below are some example inputs
2525
//
2626
// [bank, bureau_de_change, atm]
2727
// [1, 2, @target]
2828
// [Taco, Tacos, Burrito, Burritos, "Mexican Food", @example]
29-
func ParseArray(in string, remainder string) (Node, error) {
29+
func ParseSet(in string, remainder string) (Node, error) {
3030

3131
nodes := make([]Node, 0)
3232
singlequotes := 0
@@ -69,10 +69,10 @@ func ParseArray(in string, remainder string) (Node, error) {
6969
}
7070

7171
if len(remainder) == 0 {
72-
return &Array{Nodes: nodes}, nil
72+
return &Set{Nodes: nodes}, nil
7373
}
7474

75-
left := &Array{Nodes: nodes}
75+
left := &Set{Nodes: nodes}
7676
root, err := Parse(remainder)
7777
if err != nil {
7878
return root, err

dfl/Array.go renamed to dfl/Set.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ import (
1111
"reflect"
1212
)
1313

14-
// Array is a Node representing an array (aka slice in Go) of values, which can be either a Literal or Attribute.
15-
type Array struct {
14+
// Set is a Node representing a set of values, which can be either a Literal or Attribute.
15+
type Set struct {
1616
Nodes []Node
1717
}
1818

19-
func (a Array) Dfl() string {
19+
func (a Set) Dfl() string {
2020
str := "["
2121
for i, x := range a.Nodes {
2222
if i > 0 {
@@ -28,15 +28,16 @@ func (a Array) Dfl() string {
2828
return str
2929
}
3030

31-
func (a Array) Map() map[string]interface{} {
31+
func (a Set) Map() map[string]interface{} {
3232
return map[string]interface{}{
3333
"nodes": a.Nodes,
3434
}
3535
}
3636

3737
// Compile returns a compiled version of this node.
38-
// If all the values of an array are literals, returns a single Literal with the corresponding array/slice as its value.
39-
func (a Array) Compile() Node {
38+
// If all the values of an Set are literals, returns a single Literal with the corresponding Set/slice as its value.
39+
// Otherwise returns the original node..
40+
func (a Set) Compile() Node {
4041
values := make([]interface{}, len(a.Nodes))
4142
nodes := reflect.ValueOf(a.Nodes)
4243
for i := 0; i < nodes.Len(); i++ {
@@ -48,10 +49,19 @@ func (a Array) Compile() Node {
4849
return a
4950
}
5051
}
51-
return Literal{Value: values}
52+
set := make(map[string]struct{}, len(values))
53+
for _, v := range values {
54+
switch v.(type) {
55+
case string:
56+
set[v.(string)] = struct{}{}
57+
default:
58+
return Literal{Value: values}
59+
}
60+
}
61+
return Literal{Value: set}
5262
}
5363

54-
func (a Array) Evaluate(ctx Context, funcs FunctionMap) (interface{}, error) {
64+
func (a Set) Evaluate(ctx Context, funcs FunctionMap) (interface{}, error) {
5565
values := make([]interface{}, len(a.Nodes))
5666
for i, n := range a.Nodes {
5767
v, err := n.Evaluate(ctx, funcs)
@@ -63,7 +73,7 @@ func (a Array) Evaluate(ctx Context, funcs FunctionMap) (interface{}, error) {
6373
return values, nil
6474
}
6575

66-
func (a Array) Attributes() []string {
76+
func (a Set) Attributes() []string {
6777
set := make(map[string]struct{})
6878
for _, n := range a.Nodes {
6979
for _, x := range n.Attributes() {

dfl/TestCase.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ package dfl
99

1010
// TestCase is a struct containing the variables for a unit test of expression evaluation.
1111
type TestCase struct {
12-
Expression string // the DFL expression
13-
Context Context // the Context to use for evaluation
12+
Expression string // the DFL expression
13+
Context Context // the Context to use for evaluation
1414
Result interface{} // The result of the evaluation
1515
}
1616

0 commit comments

Comments
 (0)