1
0
treerack/errors_test.go

383 lines
8.4 KiB
Go
Raw Permalink Normal View History

2017-11-25 17:37:05 +01:00
package treerack
import (
"bytes"
"reflect"
2017-11-25 17:37:05 +01:00
"testing"
)
2017-12-29 18:20:06 +01:00
type errorTestItem struct {
title string
syntax string
doc string
perr ParseError
}
func testParseErrorItem(s *Syntax, test errorTestItem) func(t *testing.T) {
return func(t *testing.T) {
_, err := s.Parse(bytes.NewBufferString(test.doc))
if err == nil {
t.Fatal("failed to fail")
}
perr, ok := err.(*ParseError)
if !ok {
t.Fatal("invalid error type returned")
}
perr.Input = ""
2026-06-06 21:19:04 +02:00
perr.inputContent = nil
if len(test.perr.Trace) == 0 {
perr.Trace = nil
}
if test.perr.UnexpectedInputLine == 0 && test.perr.UnexpectedInputCol == 0 &&
perr.UnexpectedInputLine == -1 && perr.UnexpectedInputCol == -1 {
test.perr.UnexpectedInputLine = -1
test.perr.UnexpectedInputCol = -1
}
2017-12-29 18:20:06 +01:00
2017-12-31 17:34:10 +01:00
if !reflect.DeepEqual(*perr, test.perr) {
2017-12-29 18:20:06 +01:00
t.Error("invalid error returned")
t.Log("got: ", *perr)
t.Log("expected:", test.perr)
}
}
}
func testParseError(t *testing.T, syntax string, tests []errorTestItem) {
var s *Syntax
if syntax != "" {
var err error
s, err = openSyntaxString(syntax)
if err != nil {
t.Fatal(err)
}
2017-11-25 17:37:05 +01:00
}
2017-12-29 18:20:06 +01:00
for _, test := range tests {
ts := s
if test.syntax != "" {
var err error
ts, err = openSyntaxString(test.syntax)
if err != nil {
t.Fatal(err)
}
}
if ts == nil {
t.Fatal("no syntax defined")
}
t.Run(test.title, testParseErrorItem(ts, test))
}
}
func TestError(t *testing.T) {
testParseError(t, "", []errorTestItem{{
title: "single def, empty text",
syntax: `a = "a"`,
perr: ParseError{
Definition: "a",
},
2017-11-25 17:37:05 +01:00
}, {
title: "single def, wrong text",
syntax: `a = "a"`,
2017-12-29 18:20:06 +01:00
doc: "b",
perr: ParseError{
Definition: "a",
},
2017-11-25 17:37:05 +01:00
}, {
title: "single optional def, wrong text",
syntax: `a = "a"?`,
2017-12-29 18:20:06 +01:00
doc: "b",
perr: ParseError{
Definition: "a",
},
2017-11-25 17:37:05 +01:00
}, {
title: "error on second line, second column",
syntax: `a = [a\n]*`,
2017-12-29 18:20:06 +01:00
doc: "aa\nabaa\naa",
perr: ParseError{
2026-06-06 21:19:04 +02:00
Offset: 4,
Line: 1,
Column: 1,
Definition: "a",
UnexpectedInputLine: 1,
UnexpectedInputCol: 1,
},
2017-11-25 17:37:05 +01:00
}, {
title: "multiple definitions",
syntax: `a = "aa"; A:root = a`,
2017-12-29 18:20:06 +01:00
doc: "ab",
perr: ParseError{
Offset: 1,
Column: 1,
Definition: "a",
},
2026-06-10 00:43:27 +02:00
// }, {
// title: "choice, options succeed",
// syntax: `a = "12"; b = "1"; c:root = a | b`,
// doc: "123",
// perr: ParseError{
// Offset: 2,
// Column: 2,
// Definition: "c",
// },
2017-11-25 17:37:05 +01:00
}, {
title: "choice succeeds, document fails",
syntax: `a = "12"; b = "1"; c:root = a | b`,
2017-12-29 18:20:06 +01:00
doc: "13",
perr: ParseError{
2026-06-06 21:19:04 +02:00
Offset: 1,
Column: 1,
Definition: "c",
UnexpectedInputCol: 1,
},
2017-11-25 17:37:05 +01:00
}, {
title: "choice fails",
syntax: `a = "12"; b = "2"; c:root = a | b`,
2017-12-29 18:20:06 +01:00
doc: "13",
perr: ParseError{
Offset: 1,
Column: 1,
Definition: "a",
},
2017-11-25 22:01:02 +01:00
}, {
title: "choice fails, longer option reported",
syntax: `a = "12"; b = "134"; c:root = a | b`,
2017-12-29 18:20:06 +01:00
doc: "135",
perr: ParseError{
Offset: 2,
Column: 2,
Definition: "b",
},
2017-11-26 18:48:56 +01:00
}, {
title: "failing choice on the failing branch",
syntax: `a = "123"; b:root = a | "13"`,
2017-12-29 18:20:06 +01:00
doc: "124",
perr: ParseError{
Offset: 2,
Column: 2,
Definition: "a",
},
2017-11-26 18:48:56 +01:00
}, {
title: "failing choice on a shorter branch",
syntax: `a = "13"; b:root = "123" | a`,
2017-12-29 18:20:06 +01:00
doc: "124",
perr: ParseError{
Offset: 2,
Column: 2,
Definition: "b",
},
2017-11-26 18:48:56 +01:00
}, {
title: "longer failure on a later pass",
syntax: `a = "12"; b = "34"; c = "1" b; d:root = a | c`,
2017-12-29 18:20:06 +01:00
doc: "135",
perr: ParseError{
Offset: 2,
Column: 2,
Definition: "b",
},
2017-11-26 18:48:56 +01:00
}, {
title: "char as a choice option",
syntax: `a = "12"; b = [a] | [b]; c = a b`,
2017-12-29 18:20:06 +01:00
doc: "12c",
perr: ParseError{
Offset: 2,
Column: 2,
Definition: "b",
},
2017-12-29 18:20:06 +01:00
}})
2017-11-25 17:37:05 +01:00
}
2017-11-26 02:34:34 +01:00
2017-11-26 18:48:56 +01:00
func TestErrorRecursive(t *testing.T) {
const syntax = `
ws:ws = " ";
symbol = [a-z]+;
function-application = expression "(" expression? ")";
expression = function-application | symbol;
doc:root = (expression (";" expression)*)+;
`
2017-11-26 18:48:56 +01:00
2017-12-29 18:20:06 +01:00
testParseError(t, syntax, []errorTestItem{{
title: "simple, open",
doc: "a(",
perr: ParseError{
2026-06-06 21:19:04 +02:00
Offset: 2,
Column: 2,
Definition: "function-application",
UnexpectedInputCol: 1,
},
}, {
title: "simple, close",
doc: "a)",
perr: ParseError{
2026-06-06 21:19:04 +02:00
Offset: 1,
Column: 1,
Definition: "function-application",
UnexpectedInputCol: 1,
},
}, {
title: "inner, open",
doc: "a(b()",
perr: ParseError{
2026-06-06 21:19:04 +02:00
Offset: 5,
Column: 5,
Definition: "function-application",
UnexpectedInputCol: 1,
},
}, {
title: "inner, close",
doc: "a(b))",
perr: ParseError{
2026-06-06 21:19:04 +02:00
Offset: 4,
Column: 4,
Definition: "function-application",
UnexpectedInputCol: 4,
},
}, {
title: "outer, open",
doc: "a()b(",
perr: ParseError{
2026-06-06 21:19:04 +02:00
Offset: 5,
Column: 5,
Definition: "function-application",
UnexpectedInputCol: 4,
},
}, {
title: "outer, close",
doc: "a()b)",
perr: ParseError{
2026-06-06 21:19:04 +02:00
Offset: 4,
Column: 4,
Definition: "function-application",
UnexpectedInputCol: 4,
},
2017-12-29 18:20:06 +01:00
}})
2017-11-26 18:48:56 +01:00
}
2017-11-26 02:34:34 +01:00
func TestErrorMessage(t *testing.T) {
2026-06-06 21:19:04 +02:00
const expected = "foo:4:10:parse failed, parsing: bar, at 4:10"
2017-11-26 02:34:34 +01:00
perr := &ParseError{
2026-06-06 21:19:04 +02:00
Input: "foo",
Offset: 42,
Line: 3,
Column: 9,
Definition: "bar",
UnexpectedInputLine: -1,
UnexpectedInputCol: -1,
2017-11-26 02:34:34 +01:00
}
message := perr.Error()
if message != expected {
t.Error("failed to return the right error message")
t.Log("got: ", message)
t.Log("expected:", expected)
}
}
2017-12-29 18:20:06 +01:00
func TestLongestFail(t *testing.T) {
const syntax = `
whitespace:ws = [ \t];
number:nows = [0-9]+;
symbol:nows = [a-z]+;
list-separator = [,\n];
function-application = expression "(" (expression (list-separator+ expression)*)? ")";
expression = number | symbol | function-application;
statement-separator = [;\n];
doc:root = (expression (statement-separator+ expression)*)?
`
2017-12-29 18:36:58 +01:00
const doc = `f(a b c)`
2017-12-29 18:20:06 +01:00
testParseError(t, syntax, []errorTestItem{{
2017-12-29 18:36:58 +01:00
title: "fail on longest failing parser",
doc: doc,
perr: ParseError{
2026-06-06 21:19:04 +02:00
Offset: 4,
Line: 0,
Column: 4,
Definition: "function-application",
UnexpectedInputCol: 1,
2017-12-29 18:36:58 +01:00
},
}})
}
func TestFailPass(t *testing.T) {
t.Run("basic", func(t *testing.T) {
const syntax = `
space:ws = " ";
symbol:nows = [a-z]+;
list-separator:failpass = ",";
argument-list:failpass = (symbol (list-separator+ symbol)*);
function-application = symbol "(" argument-list? ")";
`
2017-12-29 18:36:58 +01:00
const doc = `f(a b c)`
2017-12-29 18:36:58 +01:00
testParseError(t, syntax, []errorTestItem{{
title: "fail in outer definition",
doc: doc,
perr: ParseError{
Offset: 4,
Line: 0,
Column: 4,
Definition: "function-application",
},
}})
})
2017-12-31 16:14:56 +01:00
t.Run("root", func(t *testing.T) {
const syntax = `foo:failpass = "foo"`
_, err := openSyntaxString(syntax)
if err == nil {
t.Error("failed to fail")
}
})
t.Run("default alias", func(t *testing.T) {
const syntax = `
space:ws = " ";
symbol:nows = [a-z]+;
list-separator:alias = ",";
argument-list:failpass = (symbol (list-separator+ symbol)*);
function-application = symbol "(" argument-list? ")";
`
const doc = `f(a b c)`
testParseError(t, syntax, []errorTestItem{{
title: "fail in outer definition",
doc: doc,
perr: ParseError{
Offset: 4,
Line: 0,
Column: 4,
Definition: "function-application",
},
}})
})
2026-06-06 05:23:32 +02:00
t.Run("force fail for alias", func(t *testing.T) {
const syntax = `
space:ws = " ";
symbol:nows = [a-z]+;
list-separator:alias:nofailpass = ",";
argument-list:failpass = (symbol (list-separator+ symbol)*);
function-application = symbol "(" argument-list? ")";
`
const doc = `f(a b c)`
testParseError(t, syntax, []errorTestItem{{
title: "fail in outer definition",
doc: doc,
perr: ParseError{
Offset: 4,
Line: 0,
Column: 4,
Definition: "list-separator",
},
}})
})
2017-12-31 16:14:56 +01:00
}