Skip to content

Commit

Permalink
Finish unit tests for parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
jbeda committed Mar 8, 2016
1 parent 964a6e8 commit d8dc42a
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 10 deletions.
2 changes: 1 addition & 1 deletion parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (p *parser) parseIdentifierList(elementKind string) (identifiers, bool, err
for _, n := range exprs {
v, ok := n.(*astVar)
if !ok {
return identifiers{}, false, makeStaticError(fmt.Sprintf("Not an identifier: %v", n), *n.Loc())
return identifiers{}, false, makeStaticError(fmt.Sprintf("Expected simple identifier but got a complex expression."), *n.Loc())
}
ids = append(ids, v.id)
}
Expand Down
131 changes: 122 additions & 9 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@ limitations under the License.
*/
package jsonnet

import (
"fmt"
"testing"

"github.com/kr/pretty"
)
import "testing"

var tests = []string{
`true`,
Expand All @@ -42,6 +37,7 @@ var tests = []string{
|||`,

`foo(bar)`,
`foo(bar) tailstrict`,
`foo.bar`,
`foo[bar]`,

Expand All @@ -58,6 +54,7 @@ var tests = []string{

`{ ["foo" + "bar"]: 3 }`,
`{ ["field" + x]: x for x in [1, 2, 3] }`,
`{ local y = x, ["field" + x]: x for x in [1, 2, 3] }`,
`{ ["field" + x]: x for x in [1, 2, 3] if x <= 2 }`,
`{ ["field" + x + y]: x + y for x in [1, 2, 3] if x <= 2 for y in [4, 5, 6]}`,

Expand Down Expand Up @@ -100,12 +97,128 @@ func TestParser(t *testing.T) {
t.Errorf("Unexpected lex error\n input: %v\n error: %v", s, err)
continue
}
ast, err := parse(tokens)
_, err = parse(tokens)
if err != nil {
t.Errorf("Unexpected parse error\n input: %v\n error: %v", s, err)
}
if false {
fmt.Printf("input: %v\nast: %# v\n\n", s, pretty.Formatter(ast))
}
}

type testError struct {
input string
err string
}

var errorTests = []testError{
{`function(a, b c)`, `test:1:15-16 Expected a comma before next function parameter.`},
{`function(a, 1)`, `test:1:13-14 Expected simple identifier but got a complex expression.`},
{`a b`, `test:1:3-4 Did not expect: (IDENTIFIER, "b")`},
{`foo(a, bar(a b))`, `test:1:14-15 Expected a comma before next function argument.`},

{`local`, `test:1:6 Expected token IDENTIFIER but got end of file`},
{`local foo = 1, foo = 2; true`, `test:1:16-19 Duplicate local var: foo`},
{`local foo(a b) = a; true`, `test:1:13-14 Expected a comma before next function parameter.`},
{`local foo(a): a; true`, `test:1:13-14 Expected operator = but got ":"`},
{`local foo(a) = bar(a b); true`, `test:1:22-23 Expected a comma before next function argument.`},
{`local foo: 1; true`, `test:1:10-11 Expected operator = but got ":"`},
{`local foo = bar(a b); true`, `test:1:19-20 Expected a comma before next function argument.`},

{`{a b}`, `test:1:4-5 Expected token OPERATOR but got (IDENTIFIER, "b")`},
{`{a = b}`, `test:1:4-5 Expected one of :, ::, :::, +:, +::, +:::, got: =`},
{`{a :::: b}`, `test:1:4-8 Expected one of :, ::, :::, +:, +::, +:::, got: ::::`},

{`{assert x for x in [1, 2, 3]}`, `test:1:11-14 Object comprehension cannot have asserts.`},
{`{['foo' + x]: true, [x]: x for x in [1, 2, 3]}`, `test:1:28-31 Object comprehension can only have one field.`},
{`{foo: x for x in [1, 2, 3]}`, `test:1:9-12 Object comprehensions can only have [e] fields.`},
{`{[x]:: true for x in [1, 2, 3]}`, `test:1:13-16 Object comprehensions cannot have hidden fields.`},
{`{[x]: true for 1 in [1, 2, 3]}`, `test:1:16-17 Expected token IDENTIFIER but got (NUMBER, "1")`},
{`{[x]: true for x at [1, 2, 3]}`, `test:1:18-20 Expected token in but got (IDENTIFIER, "at")`},
{`{[x]: true for x in [1, 2 3]}`, `test:1:27-28 Expected a comma before next array element.`},
{`{[x]: true for x in [1, 2, 3] if (a b)}`, `test:1:37-38 Expected token ")" but got (IDENTIFIER, "b")`},
{`{[x]: true for x in [1, 2, 3] if a b}`, `test:1:36-37 Expected for, if or "}" after for clause, got: (IDENTIFIER, "b")`},

{`{a: b c:d}`, `test:1:7-8 Expected a comma before next field.`},

{`{[(x y)]: z}`, `test:1:6-7 Expected token ")" but got (IDENTIFIER, "y")`},
{`{[x y]: z}`, `test:1:5-6 Expected token "]" but got (IDENTIFIER, "y")`},

{`{foo(x y): z}`, `test:1:8-9 Expected a comma before next method parameter.`},
{`{foo(x)+: z}`, `test:1:2-5 Cannot use +: syntax sugar in a method: foo`},
{`{foo: 1, foo: 2}`, `test:1:10-13 Duplicate field: foo`},
{`{foo: (1 2)}`, `test:1:10-11 Expected token ")" but got (NUMBER, "2")`},

{`{local 1 = 3, true}`, `test:1:8-9 Expected token IDENTIFIER but got (NUMBER, "1")`},
{`{local foo = 1, local foo = 2, true}`, `test:1:23-26 Duplicate local var: foo`},
{`{local foo(a b) = 1, a: true}`, `test:1:14-15 Expected a comma before next function parameter.`},
{`{local foo(a): 1, a: true}`, `test:1:14-15 Expected operator = but got ":"`},
{`{local foo(a) = (a b), a: true}`, `test:1:20-21 Expected token ")" but got (IDENTIFIER, "b")`},

{`{assert (a b), a: true}`, `test:1:12-13 Expected token ")" but got (IDENTIFIER, "b")`},
{`{assert a: (a b), a: true}`, `test:1:15-16 Expected token ")" but got (IDENTIFIER, "b")`},

{`{function(a, b) a+b: true}`, `test:1:2-10 Unexpected: (function, "function") while parsing field definition`},

{`[(a b), 2, 3]`, `test:1:5-6 Expected token ")" but got (IDENTIFIER, "b")`},
{`[1, (a b), 2, 3]`, `test:1:8-9 Expected token ")" but got (IDENTIFIER, "b")`},
{`[a for b in [1 2 3]]`, `test:1:16-17 Expected a comma before next array element.`},

{`for`, `test:1:1-4 Unexpected: (for, "for") while parsing terminal`},
{``, `test:1:1 Unexpected end of file.`},
{`((a b))`, `test:1:5-6 Expected token ")" but got (IDENTIFIER, "b")`},
{`a.1`, `test:1:3-4 Expected token IDENTIFIER but got (NUMBER, "1")`},
{`super.1`, `test:1:7-8 Expected token IDENTIFIER but got (NUMBER, "1")`},
{`super[(a b)]`, `test:1:10-11 Expected token ")" but got (IDENTIFIER, "b")`},
{`super[a b]`, `test:1:9-10 Expected token "]" but got (IDENTIFIER, "b")`},
{`super`, `test:1:1-6 Expected . or [ after super.`},

{`assert (a b); true`, `test:1:11-12 Expected token ")" but got (IDENTIFIER, "b")`},
{`assert a: (a b); true`, `test:1:14-15 Expected token ")" but got (IDENTIFIER, "b")`},
{`assert a: 'foo', true`, `test:1:16-17 Expected token ";" but got (",", ",")`},
{`assert a: 'foo'; (a b)`, `test:1:21-22 Expected token ")" but got (IDENTIFIER, "b")`},

{`error (a b)`, `test:1:10-11 Expected token ")" but got (IDENTIFIER, "b")`},

{`if (a b) then c`, `test:1:7-8 Expected token ")" but got (IDENTIFIER, "b")`},
{`if a b c`, `test:1:6-7 Expected token then but got (IDENTIFIER, "b")`},
{`if a then (b c)`, `test:1:14-15 Expected token ")" but got (IDENTIFIER, "c")`},
{`if a then b else (c d)`, `test:1:21-22 Expected token ")" but got (IDENTIFIER, "d")`},

{`function(a) (a b)`, `test:1:16-17 Expected token ")" but got (IDENTIFIER, "b")`},
{`function a a`, `test:1:10-11 Expected ( but got (IDENTIFIER, "a")`},

{`import (a b)`, `test:1:11-12 Expected token ")" but got (IDENTIFIER, "b")`},
{`import (a+b)`, `test:1:9-12 Computed imports are not allowed`},
{`importstr (a b)`, `test:1:14-15 Expected token ")" but got (IDENTIFIER, "b")`},
{`importstr (a+b)`, `test:1:12-15 Computed imports are not allowed`},

{`local a = b ()`, `test:1:15 Expected , or ; but got end of file`},
{`local a = b; (a b)`, `test:1:17-18 Expected token ")" but got (IDENTIFIER, "b")`},

{`1+ <<`, `test:1:4-6 Not a unary operator: <<`},
{`-(a b)`, `test:1:5-6 Expected token ")" but got (IDENTIFIER, "b")`},
{`1~2`, `test:1:2-3 Not a binary operator: ~`},

{`a[(b c)]`, `test:1:6-7 Expected token ")" but got (IDENTIFIER, "c")`},
{`a[b c]`, `test:1:5-6 Expected token "]" but got (IDENTIFIER, "c")`},

{`a{b c}`, `test:1:5-6 Expected token OPERATOR but got (IDENTIFIER, "c")`},
}

func TestParserErrors(t *testing.T) {
for _, s := range errorTests {
tokens, err := lex("test", s.input)
if err != nil {
t.Errorf("Unexpected lex error\n input: %v\n error: %v", s.input, err)
continue
}
_, err = parse(tokens)
if err == nil {
t.Errorf("Expected parse error but got success\n input: %v", s.input)
continue
}
if err.Error() != s.err {
t.Errorf("Error string not as expected\n input: %v\n expected error: %v\n actual error: %v", s.input, s.err, err.Error())
}
}

}

0 comments on commit d8dc42a

Please sign in to comment.