package attl import "testing" import "reflect" type testCase struct { ParseFunc input string index int expectedIndex int expectError bool expectedValue Value } func (tc *testCase) Run(t *testing.T) { t.Logf("Test case input: %s", tc.input) res, err := tc.ParseFunc([]rune(tc.input), &tc.index) if tc.expectError { if err == nil { t.Errorf("expected parse error") return } } else { if err != nil { t.Errorf("error: unexpected parse error at %d: %v", tc.index, err) return } } if tc.index != tc.expectedIndex { if err == nil { t.Errorf("error: index not correct: %d <> %d", tc.index, tc.expectedIndex) } } if tc.expectedValue == nil { if res != nil { t.Errorf("error: expected nil value, got %v", res) } else { t.Logf("Test case value: nil") } } else { if res == nil { t.Errorf("error: expected value %v was nil", tc.expectedValue) } else { t.Logf("Test case value: %v", res) se := tc.expectedValue.String() so := res.String() if so != se { t.Errorf("error: value is not as expected: %s <-> %s", so, se) } else { if !reflect.DeepEqual(tc.expectedValue, res) { t.Errorf("error: values are not deeply equal: %s <-> %s", so, se) } } } } rs := []rune(tc.input) b := string(rs[0:tc.index]) a := string(rs[tc.index:len(rs)]) t.Logf("Test case index: %s|%s", b, a) } func TestParseComment(t *testing.T) { tcs := []testCase{ testCase{ParseComment, "# comment\nnot comment", 0, 9, false, Comment("# comment")}, testCase{ParseComment, "not comment\n#comment\n", 0, 0, false, nil}, testCase{ParseComment, "# comment\nnot comment", 9, 9, false, nil}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseWs(t *testing.T) { tcs := []testCase{ testCase{ParseWs, "# comment ", 0, 0, false, nil}, testCase{ParseWs, "1234567890 ", 0, 0, false, nil}, testCase{ParseWs, "-1234567890 ", 0, 0, false, nil}, testCase{ParseWs, "+1234567890 ", 0, 0, false, nil}, testCase{ParseWs, "`string` ", 0, 0, false, nil}, testCase{ParseWs, `"string" `, 0, 0, false, nil}, testCase{ParseWs, "word ", 0, 0, false, nil}, testCase{ParseWs, " \tword ", 0, 2, false, String(" \t")}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseGetter(t *testing.T) { tcs := []testCase{ testCase{ParseGetter, "not a getter ", 0, 0, false, nil}, testCase{ParseGetter, "$# comment ", 0, 0, true, nil}, testCase{ParseGetter, "$ bad space ", 0, 0, true, nil}, testCase{ParseGetter, "$1234567890 ", 0, 11, false, Getter{Int(+1234567890)}}, testCase{ParseGetter, "$-1234567890 ", 0, 12, false, Getter{Int(-1234567890)}}, testCase{ParseGetter, "$+1234567890 ", 0, 12, false, Getter{Int(+1234567890)}}, testCase{ParseGetter, "$`string` ", 0, 9, false, Getter{String("string")}}, testCase{ParseGetter, `$"string" `, 0, 9, false, Getter{String("string")}}, testCase{ParseGetter, "$word ", 0, 5, false, Getter{Word("word")}}, testCase{ParseGetter, "$µ_rd ", 0, 5, false, Getter{Word("µ_rd")}}, testCase{ParseGetter, "$wo09 ", 0, 5, false, Getter{Word("wo09")}}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseInteger(t *testing.T) { tcs := []testCase{ testCase{ParseInteger, "01234567890 ", 0, 11, false, Int(1234567890)}, testCase{ParseInteger, "-1234567890 ", 0, 11, false, Int(-1234567890)}, testCase{ParseInteger, "+1234567890 ", 0, 11, false, Int(1234567890)}, testCase{ParseInteger, "+1234567890", 0, 11, true, nil}, testCase{ParseInteger, "not an integer", 0, 0, false, nil}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseString(t *testing.T) { tcs := []testCase{ testCase{ParseString, `"string"`, 0, 8, false, String("string")}, testCase{ParseString, `"string\"\n"`, 0, 12, false, String("string\"\n")}, testCase{ParseString, `"bad `, 0, 12, true, nil}, testCase{ParseString, `not a string`, 0, 0, false, nil}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseRawString(t *testing.T) { tcs := []testCase{ testCase{ParseRawString, "`string` ", 0, 8, false, String("string")}, testCase{ParseRawString, "`string\"quote` ", 0, 14, false, String("string\"quote")}, testCase{ParseRawString, "`bad ", 0, 11, true, nil}, testCase{ParseRawString, "not a string` ", 0, 0, false, nil}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseWord(t *testing.T) { tcs := []testCase{ testCase{ParseWord, "word ", 0, 4, false, Word("word")}, testCase{ParseWord, "word\n", 0, 4, false, Word("word")}, testCase{ParseWord, "word; ", 0, 4, false, Word("word")}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseLiteral(t *testing.T) { tcs := []testCase{ testCase{ParseLiteral, "# comment ", 0, 0, false, nil}, testCase{ParseLiteral, " \t ", 0, 0, false, nil}, testCase{ParseLiteral, "1234567890 ", 0, 10, false, Int(1234567890)}, testCase{ParseLiteral, "-1234567890 ", 0, 11, false, Int(-1234567890)}, testCase{ParseLiteral, "+1234567890 ", 0, 11, false, Int(1234567890)}, testCase{ParseLiteral, "`string` ", 0, 8, false, String("string")}, testCase{ParseLiteral, `"string" `, 0, 8, false, String("string")}, testCase{ParseLiteral, "word ", 0, 4, false, Word("word")}, testCase{ParseLiteral, "µ_rd ", 0, 4, false, Word("µ_rd")}, testCase{ParseLiteral, "wo09 ", 0, 4, false, Word("wo09")}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseParameters(t *testing.T) { tcs := []testCase{ testCase{ParseParameters, " world\n", 0, 6, false, List{Word("world")}}, testCase{ParseParameters, ` world 7 "foo"` + "`\n", 0, 14, false, List{Word("world"), Int(7), String("foo")}, }, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseOrder(t *testing.T) { tcs := []testCase{ testCase{ParseOrder, "hello ", 0, 5, false, Word("hello")}, testCase{ParseOrder, "1 ", 0, 1, false, Int(1)}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseCommand(t *testing.T) { tcs := []testCase{ testCase{ParseCommand, "hello world\n", 0, 11, false, Command{Word("hello"), List{Word("world")}}, }, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseEvaluation(t *testing.T) { tcs := []testCase{ testCase{ParseEvaluation, "[hello world]", 0, 13, false, Evaluation{Command{Word("hello"), List{Word("world")}}}, }, testCase{ParseEvaluation, "[hello 123 ]", 0, 12, false, Evaluation{Command{Word("hello"), List{Int(123)}}}, }, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseAStatement(t *testing.T) { tcs := []testCase{ testCase{ParseStatement, " hello world\n", 0, 12, false, Command{Word("hello"), List{Word("world")}}, }, testCase{ParseStatement, " hello;world;", 0, 6, false, Command{Word("hello"), List{}}, }, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseStatements(t *testing.T) { tcs := []testCase{ testCase{ParseStatements, "\n\n\n", 0, 3, false, List{}, }, testCase{ParseStatements, "\n\n \n", 0, 4, false, List{}, }, testCase{ParseStatements, "hello world\n", 0, 12, false, List{Command{Word("hello"), List{Word("world")}}}, }, testCase{ParseStatements, "hello;world;", 0, 12, false, List{Command{Word("hello"), List{}}, Command{Word("world"), List{}}}, }, testCase{ParseStatements, "hello \n world \n\n", 0, 16, false, List{Command{Word("hello"), List{}}, Command{Word("world"), List{}}}, }, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseBlock(t *testing.T) { tcs := []testCase{ testCase{ParseBlock, "{hello world}", 0, 13, false, Block{List{Command{Word("hello"), List{Word("world")}}}}, }, testCase{ParseBlock, "{hello;world}", 0, 13, false, Block{List{Command{Word("hello"), List{}}, Command{Word("world"), List{}}}}, }, testCase{ParseBlock, "{hello\nworld}", 0, 13, false, Block{List{Command{Word("hello"), List{}}, Command{Word("world"), List{}}}}, }, testCase{ParseBlock, "{ hello\n world\n}", 0, 16, false, Block{List{Command{Word("hello"), List{}}, Command{Word("world"), List{}}}}, }, testCase{ParseBlock, "{ #Comment\n hello\n world\n}", 0, 26, false, Block{List{Comment("#Comment"), Command{Word("hello"), List{}}, Command{Word("world"), List{}}}}}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParse(t *testing.T) { script1 := ` # Comment print "Hello world!" ` res1 := Block{List{Comment("# Comment"), Command{Word("print"), List{String("Hello world!")}}}} script2 := ` # Comment print "Hello world!" ` res2 := Block{List{Comment("# Comment"), Command{Word("print"), List{String("Hello world!")}}}} tcs := []testCase{ testCase{ParseScript, script1, 0, len(script1), false, res1}, testCase{ParseScript, script2, 0, len(script1), false, res2}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t) } } func TestParseAndRun(t *testing.T) { script1 := ` # Comment print "Hello world!" ` parsed, err := Parse(script1) if err != nil { t.Errorf("Parse error: %v", err) return } if parsed == nil { t.Errorf("No parse results error: %v", parsed) return } env := &Environment{} env.Push() env.Define("print", Proc(func(e *Environment, args ...Value) (Value, Effect) { var msg string Args(args, &msg) print(msg, "\n") return nil, nil }), 0) parsed.Eval(env) } type iTestCase struct { in string ex string expectError bool args []Value } func (tc *iTestCase) Run(t *testing.T, e Environment) { t.Logf("Test case input: %s", tc.in) res := e.Interpolate(tc.in, tc.args...) t.Logf("Test case result: %v", res) if res != tc.ex { t.Errorf("error: value not expected: %s <-> %s", res, tc.ex) } } func TestInterpolate(t *testing.T) { e := Environment{} e.Push() e.Define("foo", String("{world}"), 0) tcs := []iTestCase{ iTestCase{`hello {world}`, `hello {world}`, false, []Value{}}, iTestCase{`hello ${foo}`, `hello {world}`, false, []Value{}}, iTestCase{`hello $${foo}`, `hello ${foo}`, false, []Value{}}, iTestCase{`hello ${1}`, `hello {world}`, false, []Value{String("{world}")}}, } for i, tc := range tcs { t.Logf("Case: %d", i+1) tc.Run(t, e) } }