github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/query/expr/scanner_test.go (about) 1 // Modifications Copyright (c) 2017-2018 Uber Technologies, Inc. 2 // Copyright (c) 2013-2016 Errplane Inc. 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy of 5 // this software and associated documentation files (the "Software"), to deal in 6 // the Software without restriction, including without limitation the rights to 7 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 // the Software, and to permit persons to whom the Software is furnished to do so, 9 // subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in all 12 // copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21 package expr_test 22 23 import ( 24 "reflect" 25 "strings" 26 "testing" 27 28 "github.com/uber/aresdb/query/expr" 29 ) 30 31 // Ensure the scanner can scan tokens correctly. 32 func TestScanner_Scan(t *testing.T) { 33 var tests = []struct { 34 s string 35 tok expr.Token 36 lit string 37 pos expr.Pos 38 }{ 39 // Special tokens (EOF, ILLEGAL, WS) 40 {s: ``, tok: expr.EOF}, 41 {s: `#`, tok: expr.ILLEGAL, lit: `#`}, 42 {s: ` `, tok: expr.WS, lit: " "}, 43 {s: "\t", tok: expr.WS, lit: "\t"}, 44 {s: "\n", tok: expr.WS, lit: "\n"}, 45 {s: "\r", tok: expr.WS, lit: "\n"}, 46 {s: "\r\n", tok: expr.WS, lit: "\n"}, 47 {s: "\rX", tok: expr.WS, lit: "\n"}, 48 {s: "\n\r", tok: expr.WS, lit: "\n\n"}, 49 {s: " \n\t \r\n\t", tok: expr.WS, lit: " \n\t \n\t"}, 50 {s: " foo", tok: expr.WS, lit: " "}, 51 52 // Numeric operators 53 {s: `*`, tok: expr.MUL}, 54 {s: `/`, tok: expr.DIV}, 55 56 // Logical operators 57 {s: `AND`, tok: expr.AND}, 58 {s: `and`, tok: expr.AND}, 59 {s: `OR`, tok: expr.OR}, 60 {s: `or`, tok: expr.OR}, 61 62 {s: `=`, tok: expr.EQ}, 63 {s: `<>`, tok: expr.NEQ}, 64 {s: `! `, tok: expr.EXCLAMATION}, 65 {s: `<`, tok: expr.LT}, 66 {s: `<=`, tok: expr.LTE}, 67 {s: `>`, tok: expr.GT}, 68 {s: `>=`, tok: expr.GTE}, 69 70 // Misc tokens 71 {s: `(`, tok: expr.LPAREN}, 72 {s: `)`, tok: expr.RPAREN}, 73 {s: `,`, tok: expr.COMMA}, 74 {s: `.`, tok: expr.DOT}, 75 76 // Identifiers 77 {s: `foo`, tok: expr.IDENT, lit: `foo`}, 78 {s: `_foo`, tok: expr.IDENT, lit: `_foo`}, 79 {s: `Zx12_3U_-`, tok: expr.IDENT, lit: `Zx12_3U_`}, 80 {s: "`foo`", tok: expr.IDENT, lit: `foo`}, 81 {s: "`foo\\\\bar`", tok: expr.IDENT, lit: `foo\bar`}, 82 {s: "`foo\\bar`", tok: expr.BADESCAPE, lit: `\b`, pos: expr.Pos{Line: 0, Char: 5}}, 83 {s: "`foo\"bar\"`", tok: expr.IDENT, lit: `foo"bar"`}, 84 {s: "test`", tok: expr.BADSTRING, lit: "", pos: expr.Pos{Line: 0, Char: 3}}, 85 {s: "`test", tok: expr.BADSTRING, lit: `test`}, 86 87 {s: `true`, tok: expr.TRUE}, 88 {s: `false`, tok: expr.FALSE}, 89 90 // Strings 91 {s: `'testing 123!'`, tok: expr.STRING, lit: `testing 123!`}, 92 {s: `"testing 123!"`, tok: expr.STRING, lit: `testing 123!`}, 93 {s: `'foo\nbar'`, tok: expr.STRING, lit: "foo\nbar"}, 94 {s: `'foo\\bar'`, tok: expr.STRING, lit: "foo\\bar"}, 95 {s: `'test`, tok: expr.BADSTRING, lit: `test`}, 96 {s: "'test\nfoo", tok: expr.BADSTRING, lit: `test`}, 97 {s: `'test\g'`, tok: expr.BADESCAPE, lit: `\g`, pos: expr.Pos{Line: 0, Char: 6}}, 98 99 // Numbers 100 {s: `100`, tok: expr.NUMBER, lit: `100`}, 101 {s: `100.23`, tok: expr.NUMBER, lit: `100.23`}, 102 {s: `+100.23`, tok: expr.NUMBER, lit: `+100.23`}, 103 {s: `-100.23`, tok: expr.NUMBER, lit: `-100.23`}, 104 {s: `10.3s`, tok: expr.NUMBER, lit: `10.3`}, 105 106 // Keywords 107 {s: `ALL`, tok: expr.ALL}, 108 {s: `AS`, tok: expr.AS}, 109 {s: `ASC`, tok: expr.ASC}, 110 {s: `BEGIN`, tok: expr.BEGIN}, 111 {s: `BY`, tok: expr.BY}, 112 {s: `DEFAULT`, tok: expr.DEFAULT}, 113 {s: `DELETE`, tok: expr.DELETE}, 114 {s: `DESC`, tok: expr.DESC}, 115 {s: `DROP`, tok: expr.DROP}, 116 {s: `END`, tok: expr.END}, 117 {s: `EXISTS`, tok: expr.EXISTS}, 118 {s: `FIELD`, tok: expr.FIELD}, 119 {s: `FROM`, tok: expr.FROM}, 120 {s: `GROUP`, tok: expr.GROUP}, 121 {s: `IF`, tok: expr.IF}, 122 {s: `INNER`, tok: expr.INNER}, 123 {s: `INSERT`, tok: expr.INSERT}, 124 {s: `KEY`, tok: expr.KEY}, 125 {s: `KEYS`, tok: expr.KEYS}, 126 {s: `LIMIT`, tok: expr.LIMIT}, 127 {s: `NOT`, tok: expr.NOT}, 128 {s: `OFFSET`, tok: expr.OFFSET}, 129 {s: `ON`, tok: expr.ON}, 130 {s: `ORDER`, tok: expr.ORDER}, 131 {s: `SELECT`, tok: expr.SELECT}, 132 {s: `TO`, tok: expr.TO}, 133 {s: `VALUES`, tok: expr.VALUES}, 134 {s: `WHERE`, tok: expr.WHERE}, 135 {s: `WITH`, tok: expr.WITH}, 136 {s: `seLECT`, tok: expr.SELECT}, // case insensitive 137 } 138 139 for i, tt := range tests { 140 s := expr.NewScanner(strings.NewReader(tt.s)) 141 tok, pos, lit := s.Scan() 142 if tt.tok != tok { 143 t.Errorf("%d. %q token mismatch: exp=%q got=%q <%q>", i, tt.s, tt.tok, tok, lit) 144 } else if tt.pos.Line != pos.Line || tt.pos.Char != pos.Char { 145 t.Errorf("%d. %q pos mismatch: exp=%#v got=%#v", i, tt.s, tt.pos, pos) 146 } else if tt.lit != lit { 147 t.Errorf("%d. %q literal mismatch: exp=%q got=%q", i, tt.s, tt.lit, lit) 148 } 149 } 150 } 151 152 // Ensure the scanner can scan a series of tokens correctly. 153 func TestScanner_Scan_Multi(t *testing.T) { 154 type result struct { 155 tok expr.Token 156 pos expr.Pos 157 lit string 158 } 159 exp := []result{ 160 {tok: expr.SELECT, pos: expr.Pos{Line: 0, Char: 0}, lit: ""}, 161 {tok: expr.WS, pos: expr.Pos{Line: 0, Char: 6}, lit: " "}, 162 {tok: expr.IDENT, pos: expr.Pos{Line: 0, Char: 7}, lit: "value"}, 163 {tok: expr.WS, pos: expr.Pos{Line: 0, Char: 12}, lit: " "}, 164 {tok: expr.FROM, pos: expr.Pos{Line: 0, Char: 13}, lit: ""}, 165 {tok: expr.WS, pos: expr.Pos{Line: 0, Char: 17}, lit: " "}, 166 {tok: expr.IDENT, pos: expr.Pos{Line: 0, Char: 18}, lit: "myseries"}, 167 {tok: expr.WS, pos: expr.Pos{Line: 0, Char: 26}, lit: " "}, 168 {tok: expr.WHERE, pos: expr.Pos{Line: 0, Char: 27}, lit: ""}, 169 {tok: expr.WS, pos: expr.Pos{Line: 0, Char: 32}, lit: " "}, 170 {tok: expr.IDENT, pos: expr.Pos{Line: 0, Char: 33}, lit: "a"}, 171 {tok: expr.WS, pos: expr.Pos{Line: 0, Char: 34}, lit: " "}, 172 {tok: expr.EQ, pos: expr.Pos{Line: 0, Char: 35}, lit: ""}, 173 {tok: expr.WS, pos: expr.Pos{Line: 0, Char: 36}, lit: " "}, 174 {tok: expr.STRING, pos: expr.Pos{Line: 0, Char: 36}, lit: "b"}, 175 {tok: expr.EOF, pos: expr.Pos{Line: 0, Char: 40}, lit: ""}, 176 } 177 178 // Create a scanner. 179 v := `SELECT value from myseries WHERE a = 'b'` 180 s := expr.NewScanner(strings.NewReader(v)) 181 182 // Continually scan until we reach the end. 183 var act []result 184 for { 185 tok, pos, lit := s.Scan() 186 act = append(act, result{tok, pos, lit}) 187 if tok == expr.EOF { 188 break 189 } 190 } 191 192 // Verify the token counts match. 193 if len(exp) != len(act) { 194 t.Fatalf("token count mismatch: exp=%d, got=%d", len(exp), len(act)) 195 } 196 197 // Verify each token matches. 198 for i := range exp { 199 if !reflect.DeepEqual(exp[i], act[i]) { 200 t.Fatalf("%d. token mismatch:\n\nexp=%#v\n\ngot=%#v", i, exp[i], act[i]) 201 } 202 } 203 } 204 205 // Ensure the library can correctly scan strings. 206 func TestScanString(t *testing.T) { 207 var tests = []struct { 208 in string 209 out string 210 err string 211 }{ 212 {in: `""`, out: ``}, 213 {in: `"foo bar"`, out: `foo bar`}, 214 {in: `'foo bar'`, out: `foo bar`}, 215 {in: `"foo\nbar"`, out: "foo\nbar"}, 216 {in: `"foo\\bar"`, out: `foo\bar`}, 217 {in: `"foo\"bar"`, out: `foo"bar`}, 218 {in: `'foo\'bar'`, out: `foo'bar`}, 219 220 {in: `"foo` + "\n", out: `foo`, err: "bad string"}, // newline in string 221 {in: `"foo`, out: `foo`, err: "bad string"}, // unclosed quotes 222 {in: `"foo\xbar"`, out: `\x`, err: "bad escape"}, // invalid escape 223 } 224 225 for i, tt := range tests { 226 out, err := expr.ScanString(strings.NewReader(tt.in)) 227 if tt.err != errstring(err) { 228 t.Errorf("%d. %s: error: exp=%s, got=%s", i, tt.in, tt.err, err) 229 } else if tt.out != out { 230 t.Errorf("%d. %s: out: exp=%s, got=%s", i, tt.in, tt.out, out) 231 } 232 } 233 }