aboutsummaryrefslogtreecommitdiff
path: root/tests/unit/spec/test/parser_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit/spec/test/parser_test.go')
-rw-r--r--tests/unit/spec/test/parser_test.go411
1 files changed, 411 insertions, 0 deletions
diff --git a/tests/unit/spec/test/parser_test.go b/tests/unit/spec/test/parser_test.go
new file mode 100644
index 0000000..eddba92
--- /dev/null
+++ b/tests/unit/spec/test/parser_test.go
@@ -0,0 +1,411 @@
+package test
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestTree_Format(t *testing.T) {
+ expected := `(a
+ (b
+ (c))
+ (d)
+ (e))`
+ tree := NewNonTerminalTree("a",
+ NewNonTerminalTree("b",
+ NewNonTerminalTree("c"),
+ ),
+ NewNonTerminalTree("d"),
+ NewNonTerminalTree("e"),
+ )
+ actual := string(tree.Format())
+ if actual != expected {
+ t.Fatalf("unexpected format:\n%v", actual)
+ }
+}
+
+func TestDiffTree(t *testing.T) {
+ tests := []struct {
+ t1 *Tree
+ t2 *Tree
+ different bool
+ }{
+ {
+ t1: NewTerminalNode("a", "a"),
+ t2: NewTerminalNode("a", "a"),
+ },
+ {
+ t1: NewTerminalNode("a", "a"),
+ t2: NewTerminalNode("a", "A"),
+ different: true,
+ },
+ {
+ t1: NewTerminalNode("a", "a"),
+ t2: NewTerminalNode("A", "a"),
+ different: true,
+ },
+ {
+ t1: NewNonTerminalTree("a"),
+ t2: NewNonTerminalTree("a"),
+ },
+ {
+ t1: NewNonTerminalTree("a",
+ NewNonTerminalTree("b"),
+ ),
+ t2: NewNonTerminalTree("a",
+ NewNonTerminalTree("b"),
+ ),
+ },
+ {
+ t1: NewNonTerminalTree("a",
+ NewNonTerminalTree("b"),
+ NewNonTerminalTree("c"),
+ NewNonTerminalTree("d"),
+ ),
+ t2: NewNonTerminalTree("a",
+ NewNonTerminalTree("b"),
+ NewNonTerminalTree("c"),
+ NewNonTerminalTree("d"),
+ ),
+ },
+ {
+ t1: NewNonTerminalTree("a",
+ NewNonTerminalTree("b",
+ NewNonTerminalTree("c"),
+ ),
+ NewNonTerminalTree("d",
+ NewNonTerminalTree("d"),
+ ),
+ ),
+ t2: NewNonTerminalTree("a",
+ NewNonTerminalTree("b",
+ NewNonTerminalTree("c"),
+ ),
+ NewNonTerminalTree("d",
+ NewNonTerminalTree("d"),
+ ),
+ ),
+ },
+ {
+ t1: NewNonTerminalTree("a"),
+ t2: NewNonTerminalTree("b"),
+ different: true,
+ },
+ {
+ t1: NewNonTerminalTree("a",
+ NewNonTerminalTree("b"),
+ ),
+ t2: NewNonTerminalTree("a"),
+ different: true,
+ },
+ {
+ t1: NewNonTerminalTree("a"),
+ t2: NewNonTerminalTree("a",
+ NewNonTerminalTree("b"),
+ ),
+ different: true,
+ },
+ {
+ t1: NewNonTerminalTree("a",
+ NewNonTerminalTree("b"),
+ ),
+ t2: NewNonTerminalTree("a",
+ NewNonTerminalTree("c"),
+ ),
+ different: true,
+ },
+ {
+ t1: NewNonTerminalTree("a",
+ NewNonTerminalTree("b"),
+ NewNonTerminalTree("c"),
+ NewNonTerminalTree("d"),
+ ),
+ t2: NewNonTerminalTree("a",
+ NewNonTerminalTree("b"),
+ NewNonTerminalTree("c"),
+ ),
+ different: true,
+ },
+ {
+ t1: NewNonTerminalTree("a",
+ NewNonTerminalTree("b"),
+ NewNonTerminalTree("c"),
+ ),
+ t2: NewNonTerminalTree("a",
+ NewNonTerminalTree("b"),
+ NewNonTerminalTree("c"),
+ NewNonTerminalTree("d"),
+ ),
+ different: true,
+ },
+ {
+ t1: NewNonTerminalTree("a",
+ NewNonTerminalTree("b",
+ NewNonTerminalTree("c"),
+ ),
+ ),
+ t2: NewNonTerminalTree("a",
+ NewNonTerminalTree("b",
+ NewNonTerminalTree("d"),
+ ),
+ ),
+ different: true,
+ },
+ }
+ for i, tt := range tests {
+ t.Run(fmt.Sprintf("#%v", i), func(t *testing.T) {
+ diffs := DiffTree(tt.t1, tt.t2)
+ if tt.different && len(diffs) == 0 {
+ t.Fatalf("unexpected result")
+ } else if !tt.different && len(diffs) > 0 {
+ t.Fatalf("unexpected result")
+ }
+ })
+ }
+}
+
+func TestParseTestCase(t *testing.T) {
+ tests := []struct {
+ src string
+ tc *TestCase
+ parseErr bool
+ }{
+ {
+ src: `test
+---
+foo
+---
+(foo)
+`,
+ tc: &TestCase{
+ Description: "test",
+ Source: []byte("foo"),
+ Output: NewNonTerminalTree("foo").Fill(),
+ },
+ },
+ {
+ src: `
+test
+
+---
+
+foo
+
+---
+
+(foo)
+
+`,
+ tc: &TestCase{
+ Description: "\ntest\n",
+ Source: []byte("\nfoo\n"),
+ Output: NewNonTerminalTree("foo").Fill(),
+ },
+ },
+ // The length of a part delimiter may be greater than 3.
+ {
+ src: `
+test
+----
+foo
+----
+(foo)
+`,
+ tc: &TestCase{
+ Description: "\ntest",
+ Source: []byte("foo"),
+ Output: NewNonTerminalTree("foo").Fill(),
+ },
+ },
+ // The description part may be empty.
+ {
+ src: `----
+foo
+----
+(foo)
+`,
+ tc: &TestCase{
+ Description: "",
+ Source: []byte("foo"),
+ Output: NewNonTerminalTree("foo").Fill(),
+ },
+ },
+ // The source part may be empty.
+ {
+ src: `test
+---
+---
+(foo)
+`,
+ tc: &TestCase{
+ Description: "test",
+ Source: []byte{},
+ Output: NewNonTerminalTree("foo").Fill(),
+ },
+ },
+ // NOTE: If there is a delimiter at the end of a test case, we really want to make it a syntax error,
+ // but we allow it to simplify the implementation of the parser.
+ {
+ src: `test
+----
+foo
+----
+(foo)
+---
+`,
+ tc: &TestCase{
+ Description: "test",
+ Source: []byte("foo"),
+ Output: NewNonTerminalTree("foo").Fill(),
+ },
+ },
+ {
+ src: ``,
+ parseErr: true,
+ },
+ {
+ src: `test
+---
+`,
+ parseErr: true,
+ },
+ {
+ src: `test
+---
+foo
+`,
+ parseErr: true,
+ },
+ {
+ src: `test
+---
+foo
+---
+`,
+ parseErr: true,
+ },
+ {
+ src: `test
+--
+foo
+--
+(foo)
+`,
+ parseErr: true,
+ },
+ // A node may have just one string node.
+ {
+ src: `test
+----
+foo bar
+----
+(foo (bar 'bar'))
+`,
+ tc: &TestCase{
+ Description: "test",
+ Source: []byte("foo bar"),
+ Output: NewNonTerminalTree("foo",
+ NewTerminalNode("bar", "bar"),
+ ).Fill(),
+ },
+ },
+ // A node may have just one pattern node.
+ {
+ src: `test
+----
+foo bar
+----
+(foo (bar "bar"))
+`,
+ tc: &TestCase{
+ Description: "test",
+ Source: []byte("foo bar"),
+ Output: NewNonTerminalTree("foo",
+ NewTerminalNode("bar", "bar"),
+ ).Fill(),
+ },
+ },
+ // A node may be the error node.
+ {
+ src: `test
+----
+foo x
+----
+(foo (error))
+`,
+ tc: &TestCase{
+ Description: "test",
+ Source: []byte("foo x"),
+ Output: NewNonTerminalTree("foo",
+ NewTerminalNode("error", ""),
+ ).Fill(),
+ },
+ },
+ // The error node cannot have a string node.
+ {
+ src: `test
+----
+foo x
+----
+(foo (error 'x'))
+`,
+ parseErr: true,
+ },
+ // The error node cannot have a pattern node.
+ {
+ src: `test
+----
+foo x
+----
+(foo (error "x"))
+`,
+ parseErr: true,
+ },
+ // The error node cannot have another node.
+ {
+ src: `test
+----
+foo x
+----
+(foo (error (a)))
+`,
+ parseErr: true,
+ },
+ {
+ src: `test
+---
+foo
+---
+?
+`,
+ parseErr: true,
+ },
+ }
+ for i, tt := range tests {
+ t.Run(fmt.Sprintf("#%v", i), func(t *testing.T) {
+ tc, err := ParseTestCase(strings.NewReader(tt.src))
+ if tt.parseErr {
+ if err == nil {
+ t.Fatalf("an expected error didn't occur")
+ }
+ } else {
+ if err != nil {
+ t.Fatal(err)
+ }
+ testTestCase(t, tt.tc, tc)
+ }
+ })
+ }
+}
+
+func testTestCase(t *testing.T, expected, actual *TestCase) {
+ t.Helper()
+
+ if expected.Description != actual.Description ||
+ !reflect.DeepEqual(expected.Source, actual.Source) ||
+ len(DiffTree(expected.Output, actual.Output)) > 0 {
+ t.Fatalf("unexpected test case: want: %#v, got: %#v", expected, actual)
+ }
+}