1package query 2 3import ( 4 "github.com/pelletier/go-toml" 5 "testing" 6) 7 8func testQLFlow(t *testing.T, input string, expectedFlow []token) { 9 ch := lexQuery(input) 10 for idx, expected := range expectedFlow { 11 token := <-ch 12 if token != expected { 13 t.Log("While testing #", idx, ":", input) 14 t.Log("compared (got)", token, "to (expected)", expected) 15 t.Log("\tvalue:", token.val, "<->", expected.val) 16 t.Log("\tvalue as bytes:", []byte(token.val), "<->", []byte(expected.val)) 17 t.Log("\ttype:", token.typ.String(), "<->", expected.typ.String()) 18 t.Log("\tline:", token.Line, "<->", expected.Line) 19 t.Log("\tcolumn:", token.Col, "<->", expected.Col) 20 t.Log("compared", token, "to", expected) 21 t.FailNow() 22 } 23 } 24 25 tok, ok := <-ch 26 if ok { 27 t.Log("channel is not closed!") 28 t.Log(len(ch)+1, "tokens remaining:") 29 30 t.Log("token ->", tok) 31 for token := range ch { 32 t.Log("token ->", token) 33 } 34 t.FailNow() 35 } 36} 37 38func TestLexSpecialChars(t *testing.T) { 39 testQLFlow(t, " .$[]..()?*", []token{ 40 {toml.Position{1, 2}, tokenDot, "."}, 41 {toml.Position{1, 3}, tokenDollar, "$"}, 42 {toml.Position{1, 4}, tokenLeftBracket, "["}, 43 {toml.Position{1, 5}, tokenRightBracket, "]"}, 44 {toml.Position{1, 6}, tokenDotDot, ".."}, 45 {toml.Position{1, 8}, tokenLeftParen, "("}, 46 {toml.Position{1, 9}, tokenRightParen, ")"}, 47 {toml.Position{1, 10}, tokenQuestion, "?"}, 48 {toml.Position{1, 11}, tokenStar, "*"}, 49 {toml.Position{1, 12}, tokenEOF, ""}, 50 }) 51} 52 53func TestLexString(t *testing.T) { 54 testQLFlow(t, "'foo\n'", []token{ 55 {toml.Position{1, 2}, tokenString, "foo\n"}, 56 {toml.Position{2, 2}, tokenEOF, ""}, 57 }) 58} 59 60func TestLexDoubleString(t *testing.T) { 61 testQLFlow(t, `"bar"`, []token{ 62 {toml.Position{1, 2}, tokenString, "bar"}, 63 {toml.Position{1, 6}, tokenEOF, ""}, 64 }) 65} 66 67func TestLexStringEscapes(t *testing.T) { 68 testQLFlow(t, `"foo \" \' \b \f \/ \t \r \\ \u03A9 \U00012345 \n bar"`, []token{ 69 {toml.Position{1, 2}, tokenString, "foo \" ' \b \f / \t \r \\ \u03A9 \U00012345 \n bar"}, 70 {toml.Position{1, 55}, tokenEOF, ""}, 71 }) 72} 73 74func TestLexStringUnfinishedUnicode4(t *testing.T) { 75 testQLFlow(t, `"\u000"`, []token{ 76 {toml.Position{1, 2}, tokenError, "unfinished unicode escape"}, 77 }) 78} 79 80func TestLexStringUnfinishedUnicode8(t *testing.T) { 81 testQLFlow(t, `"\U0000"`, []token{ 82 {toml.Position{1, 2}, tokenError, "unfinished unicode escape"}, 83 }) 84} 85 86func TestLexStringInvalidEscape(t *testing.T) { 87 testQLFlow(t, `"\x"`, []token{ 88 {toml.Position{1, 2}, tokenError, "invalid escape sequence: \\x"}, 89 }) 90} 91 92func TestLexStringUnfinished(t *testing.T) { 93 testQLFlow(t, `"bar`, []token{ 94 {toml.Position{1, 2}, tokenError, "unclosed string"}, 95 }) 96} 97 98func TestLexKey(t *testing.T) { 99 testQLFlow(t, "foo", []token{ 100 {toml.Position{1, 1}, tokenKey, "foo"}, 101 {toml.Position{1, 4}, tokenEOF, ""}, 102 }) 103} 104 105func TestLexRecurse(t *testing.T) { 106 testQLFlow(t, "$..*", []token{ 107 {toml.Position{1, 1}, tokenDollar, "$"}, 108 {toml.Position{1, 2}, tokenDotDot, ".."}, 109 {toml.Position{1, 4}, tokenStar, "*"}, 110 {toml.Position{1, 5}, tokenEOF, ""}, 111 }) 112} 113 114func TestLexBracketKey(t *testing.T) { 115 testQLFlow(t, "$[foo]", []token{ 116 {toml.Position{1, 1}, tokenDollar, "$"}, 117 {toml.Position{1, 2}, tokenLeftBracket, "["}, 118 {toml.Position{1, 3}, tokenKey, "foo"}, 119 {toml.Position{1, 6}, tokenRightBracket, "]"}, 120 {toml.Position{1, 7}, tokenEOF, ""}, 121 }) 122} 123 124func TestLexSpace(t *testing.T) { 125 testQLFlow(t, "foo bar baz", []token{ 126 {toml.Position{1, 1}, tokenKey, "foo"}, 127 {toml.Position{1, 5}, tokenKey, "bar"}, 128 {toml.Position{1, 9}, tokenKey, "baz"}, 129 {toml.Position{1, 12}, tokenEOF, ""}, 130 }) 131} 132 133func TestLexInteger(t *testing.T) { 134 testQLFlow(t, "100 +200 -300", []token{ 135 {toml.Position{1, 1}, tokenInteger, "100"}, 136 {toml.Position{1, 5}, tokenInteger, "+200"}, 137 {toml.Position{1, 10}, tokenInteger, "-300"}, 138 {toml.Position{1, 14}, tokenEOF, ""}, 139 }) 140} 141 142func TestLexFloat(t *testing.T) { 143 testQLFlow(t, "100.0 +200.0 -300.0", []token{ 144 {toml.Position{1, 1}, tokenFloat, "100.0"}, 145 {toml.Position{1, 7}, tokenFloat, "+200.0"}, 146 {toml.Position{1, 14}, tokenFloat, "-300.0"}, 147 {toml.Position{1, 20}, tokenEOF, ""}, 148 }) 149} 150 151func TestLexFloatWithMultipleDots(t *testing.T) { 152 testQLFlow(t, "4.2.", []token{ 153 {toml.Position{1, 1}, tokenError, "cannot have two dots in one float"}, 154 }) 155} 156 157func TestLexFloatLeadingDot(t *testing.T) { 158 testQLFlow(t, "+.1", []token{ 159 {toml.Position{1, 1}, tokenError, "cannot start float with a dot"}, 160 }) 161} 162 163func TestLexFloatWithTrailingDot(t *testing.T) { 164 testQLFlow(t, "42.", []token{ 165 {toml.Position{1, 1}, tokenError, "float cannot end with a dot"}, 166 }) 167} 168 169func TestLexNumberWithoutDigit(t *testing.T) { 170 testQLFlow(t, "+", []token{ 171 {toml.Position{1, 1}, tokenError, "no digit in that number"}, 172 }) 173} 174 175func TestLexUnknown(t *testing.T) { 176 testQLFlow(t, "^", []token{ 177 {toml.Position{1, 1}, tokenError, "unexpected char: '94'"}, 178 }) 179} 180