vsphere-influxdb-go/vendor/github.com/influxdata/influxdb/influxql/scanner_test.go

291 lines
10 KiB
Go

package influxql_test
import (
"reflect"
"strings"
"testing"
"github.com/influxdata/influxdb/influxql"
)
// Ensure the scanner can scan tokens correctly.
func TestScanner_Scan(t *testing.T) {
var tests = []struct {
s string
tok influxql.Token
lit string
pos influxql.Pos
}{
// Special tokens (EOF, ILLEGAL, WS)
{s: ``, tok: influxql.EOF},
{s: `#`, tok: influxql.ILLEGAL, lit: `#`},
{s: ` `, tok: influxql.WS, lit: " "},
{s: "\t", tok: influxql.WS, lit: "\t"},
{s: "\n", tok: influxql.WS, lit: "\n"},
{s: "\r", tok: influxql.WS, lit: "\n"},
{s: "\r\n", tok: influxql.WS, lit: "\n"},
{s: "\rX", tok: influxql.WS, lit: "\n"},
{s: "\n\r", tok: influxql.WS, lit: "\n\n"},
{s: " \n\t \r\n\t", tok: influxql.WS, lit: " \n\t \n\t"},
{s: " foo", tok: influxql.WS, lit: " "},
// Numeric operators
{s: `+`, tok: influxql.ADD},
{s: `-`, tok: influxql.SUB},
{s: `*`, tok: influxql.MUL},
{s: `/`, tok: influxql.DIV},
{s: `%`, tok: influxql.MOD},
// Logical operators
{s: `AND`, tok: influxql.AND},
{s: `and`, tok: influxql.AND},
{s: `OR`, tok: influxql.OR},
{s: `or`, tok: influxql.OR},
{s: `=`, tok: influxql.EQ},
{s: `<>`, tok: influxql.NEQ},
{s: `! `, tok: influxql.ILLEGAL, lit: "!"},
{s: `<`, tok: influxql.LT},
{s: `<=`, tok: influxql.LTE},
{s: `>`, tok: influxql.GT},
{s: `>=`, tok: influxql.GTE},
// Misc tokens
{s: `(`, tok: influxql.LPAREN},
{s: `)`, tok: influxql.RPAREN},
{s: `,`, tok: influxql.COMMA},
{s: `;`, tok: influxql.SEMICOLON},
{s: `.`, tok: influxql.DOT},
{s: `=~`, tok: influxql.EQREGEX},
{s: `!~`, tok: influxql.NEQREGEX},
{s: `:`, tok: influxql.COLON},
{s: `::`, tok: influxql.DOUBLECOLON},
// Identifiers
{s: `foo`, tok: influxql.IDENT, lit: `foo`},
{s: `_foo`, tok: influxql.IDENT, lit: `_foo`},
{s: `Zx12_3U_-`, tok: influxql.IDENT, lit: `Zx12_3U_`},
{s: `"foo"`, tok: influxql.IDENT, lit: `foo`},
{s: `"foo\\bar"`, tok: influxql.IDENT, lit: `foo\bar`},
{s: `"foo\bar"`, tok: influxql.BADESCAPE, lit: `\b`, pos: influxql.Pos{Line: 0, Char: 5}},
{s: `"foo\"bar\""`, tok: influxql.IDENT, lit: `foo"bar"`},
{s: `test"`, tok: influxql.BADSTRING, lit: "", pos: influxql.Pos{Line: 0, Char: 3}},
{s: `"test`, tok: influxql.BADSTRING, lit: `test`},
{s: `$host`, tok: influxql.BOUNDPARAM, lit: `$host`},
{s: `$"host param"`, tok: influxql.BOUNDPARAM, lit: `$host param`},
{s: `true`, tok: influxql.TRUE},
{s: `false`, tok: influxql.FALSE},
// Strings
{s: `'testing 123!'`, tok: influxql.STRING, lit: `testing 123!`},
{s: `'foo\nbar'`, tok: influxql.STRING, lit: "foo\nbar"},
{s: `'foo\\bar'`, tok: influxql.STRING, lit: "foo\\bar"},
{s: `'test`, tok: influxql.BADSTRING, lit: `test`},
{s: "'test\nfoo", tok: influxql.BADSTRING, lit: `test`},
{s: `'test\g'`, tok: influxql.BADESCAPE, lit: `\g`, pos: influxql.Pos{Line: 0, Char: 6}},
// Numbers
{s: `100`, tok: influxql.INTEGER, lit: `100`},
{s: `100.23`, tok: influxql.NUMBER, lit: `100.23`},
{s: `.23`, tok: influxql.NUMBER, lit: `.23`},
//{s: `.`, tok: influxql.ILLEGAL, lit: `.`},
{s: `10.3s`, tok: influxql.NUMBER, lit: `10.3`},
// Durations
{s: `10u`, tok: influxql.DURATIONVAL, lit: `10u`},
{s: `10µ`, tok: influxql.DURATIONVAL, lit: `10µ`},
{s: `10ms`, tok: influxql.DURATIONVAL, lit: `10ms`},
{s: `1s`, tok: influxql.DURATIONVAL, lit: `1s`},
{s: `10m`, tok: influxql.DURATIONVAL, lit: `10m`},
{s: `10h`, tok: influxql.DURATIONVAL, lit: `10h`},
{s: `10d`, tok: influxql.DURATIONVAL, lit: `10d`},
{s: `10w`, tok: influxql.DURATIONVAL, lit: `10w`},
{s: `10x`, tok: influxql.DURATIONVAL, lit: `10x`}, // non-duration unit, but scanned as a duration value
// Keywords
{s: `ALL`, tok: influxql.ALL},
{s: `ALTER`, tok: influxql.ALTER},
{s: `AS`, tok: influxql.AS},
{s: `ASC`, tok: influxql.ASC},
{s: `BEGIN`, tok: influxql.BEGIN},
{s: `BY`, tok: influxql.BY},
{s: `CREATE`, tok: influxql.CREATE},
{s: `CONTINUOUS`, tok: influxql.CONTINUOUS},
{s: `DATABASE`, tok: influxql.DATABASE},
{s: `DATABASES`, tok: influxql.DATABASES},
{s: `DEFAULT`, tok: influxql.DEFAULT},
{s: `DELETE`, tok: influxql.DELETE},
{s: `DESC`, tok: influxql.DESC},
{s: `DROP`, tok: influxql.DROP},
{s: `DURATION`, tok: influxql.DURATION},
{s: `END`, tok: influxql.END},
{s: `EVERY`, tok: influxql.EVERY},
{s: `EXPLAIN`, tok: influxql.EXPLAIN},
{s: `FIELD`, tok: influxql.FIELD},
{s: `FROM`, tok: influxql.FROM},
{s: `GRANT`, tok: influxql.GRANT},
{s: `GROUP`, tok: influxql.GROUP},
{s: `GROUPS`, tok: influxql.GROUPS},
{s: `INSERT`, tok: influxql.INSERT},
{s: `INTO`, tok: influxql.INTO},
{s: `KEY`, tok: influxql.KEY},
{s: `KEYS`, tok: influxql.KEYS},
{s: `KILL`, tok: influxql.KILL},
{s: `LIMIT`, tok: influxql.LIMIT},
{s: `SHOW`, tok: influxql.SHOW},
{s: `SHARD`, tok: influxql.SHARD},
{s: `SHARDS`, tok: influxql.SHARDS},
{s: `MEASUREMENT`, tok: influxql.MEASUREMENT},
{s: `MEASUREMENTS`, tok: influxql.MEASUREMENTS},
{s: `OFFSET`, tok: influxql.OFFSET},
{s: `ON`, tok: influxql.ON},
{s: `ORDER`, tok: influxql.ORDER},
{s: `PASSWORD`, tok: influxql.PASSWORD},
{s: `POLICY`, tok: influxql.POLICY},
{s: `POLICIES`, tok: influxql.POLICIES},
{s: `PRIVILEGES`, tok: influxql.PRIVILEGES},
{s: `QUERIES`, tok: influxql.QUERIES},
{s: `QUERY`, tok: influxql.QUERY},
{s: `READ`, tok: influxql.READ},
{s: `REPLICATION`, tok: influxql.REPLICATION},
{s: `RESAMPLE`, tok: influxql.RESAMPLE},
{s: `RETENTION`, tok: influxql.RETENTION},
{s: `REVOKE`, tok: influxql.REVOKE},
{s: `SELECT`, tok: influxql.SELECT},
{s: `SERIES`, tok: influxql.SERIES},
{s: `TAG`, tok: influxql.TAG},
{s: `TO`, tok: influxql.TO},
{s: `USER`, tok: influxql.USER},
{s: `USERS`, tok: influxql.USERS},
{s: `VALUES`, tok: influxql.VALUES},
{s: `WHERE`, tok: influxql.WHERE},
{s: `WITH`, tok: influxql.WITH},
{s: `WRITE`, tok: influxql.WRITE},
{s: `explain`, tok: influxql.EXPLAIN}, // case insensitive
{s: `seLECT`, tok: influxql.SELECT}, // case insensitive
}
for i, tt := range tests {
s := influxql.NewScanner(strings.NewReader(tt.s))
tok, pos, lit := s.Scan()
if tt.tok != tok {
t.Errorf("%d. %q token mismatch: exp=%q got=%q <%q>", i, tt.s, tt.tok, tok, lit)
} else if tt.pos.Line != pos.Line || tt.pos.Char != pos.Char {
t.Errorf("%d. %q pos mismatch: exp=%#v got=%#v", i, tt.s, tt.pos, pos)
} else if tt.lit != lit {
t.Errorf("%d. %q literal mismatch: exp=%q got=%q", i, tt.s, tt.lit, lit)
}
}
}
// Ensure the scanner can scan a series of tokens correctly.
func TestScanner_Scan_Multi(t *testing.T) {
type result struct {
tok influxql.Token
pos influxql.Pos
lit string
}
exp := []result{
{tok: influxql.SELECT, pos: influxql.Pos{Line: 0, Char: 0}, lit: ""},
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 6}, lit: " "},
{tok: influxql.IDENT, pos: influxql.Pos{Line: 0, Char: 7}, lit: "value"},
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 12}, lit: " "},
{tok: influxql.FROM, pos: influxql.Pos{Line: 0, Char: 13}, lit: ""},
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 17}, lit: " "},
{tok: influxql.IDENT, pos: influxql.Pos{Line: 0, Char: 18}, lit: "myseries"},
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 26}, lit: " "},
{tok: influxql.WHERE, pos: influxql.Pos{Line: 0, Char: 27}, lit: ""},
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 32}, lit: " "},
{tok: influxql.IDENT, pos: influxql.Pos{Line: 0, Char: 33}, lit: "a"},
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 34}, lit: " "},
{tok: influxql.EQ, pos: influxql.Pos{Line: 0, Char: 35}, lit: ""},
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 36}, lit: " "},
{tok: influxql.STRING, pos: influxql.Pos{Line: 0, Char: 36}, lit: "b"},
{tok: influxql.EOF, pos: influxql.Pos{Line: 0, Char: 40}, lit: ""},
}
// Create a scanner.
v := `SELECT value from myseries WHERE a = 'b'`
s := influxql.NewScanner(strings.NewReader(v))
// Continually scan until we reach the end.
var act []result
for {
tok, pos, lit := s.Scan()
act = append(act, result{tok, pos, lit})
if tok == influxql.EOF {
break
}
}
// Verify the token counts match.
if len(exp) != len(act) {
t.Fatalf("token count mismatch: exp=%d, got=%d", len(exp), len(act))
}
// Verify each token matches.
for i := range exp {
if !reflect.DeepEqual(exp[i], act[i]) {
t.Fatalf("%d. token mismatch:\n\nexp=%#v\n\ngot=%#v", i, exp[i], act[i])
}
}
}
// Ensure the library can correctly scan strings.
func TestScanString(t *testing.T) {
var tests = []struct {
in string
out string
err string
}{
{in: `""`, out: ``},
{in: `"foo bar"`, out: `foo bar`},
{in: `'foo bar'`, out: `foo bar`},
{in: `"foo\nbar"`, out: "foo\nbar"},
{in: `"foo\\bar"`, out: `foo\bar`},
{in: `"foo\"bar"`, out: `foo"bar`},
{in: `'foo\'bar'`, out: `foo'bar`},
{in: `"foo` + "\n", out: `foo`, err: "bad string"}, // newline in string
{in: `"foo`, out: `foo`, err: "bad string"}, // unclosed quotes
{in: `"foo\xbar"`, out: `\x`, err: "bad escape"}, // invalid escape
}
for i, tt := range tests {
out, err := influxql.ScanString(strings.NewReader(tt.in))
if tt.err != errstring(err) {
t.Errorf("%d. %s: error: exp=%s, got=%s", i, tt.in, tt.err, err)
} else if tt.out != out {
t.Errorf("%d. %s: out: exp=%s, got=%s", i, tt.in, tt.out, out)
}
}
}
// Test scanning regex
func TestScanRegex(t *testing.T) {
var tests = []struct {
in string
tok influxql.Token
lit string
err string
}{
{in: `/^payments\./`, tok: influxql.REGEX, lit: `^payments\.`},
{in: `/foo\/bar/`, tok: influxql.REGEX, lit: `foo/bar`},
{in: `/foo\\/bar/`, tok: influxql.REGEX, lit: `foo\/bar`},
{in: `/foo\\bar/`, tok: influxql.REGEX, lit: `foo\\bar`},
{in: `/http\:\/\/www\.example\.com/`, tok: influxql.REGEX, lit: `http\://www\.example\.com`},
}
for i, tt := range tests {
s := influxql.NewScanner(strings.NewReader(tt.in))
tok, _, lit := s.ScanRegex()
if tok != tt.tok {
t.Errorf("%d. %s: error:\n\texp=%s\n\tgot=%s\n", i, tt.in, tt.tok.String(), tok.String())
}
if lit != tt.lit {
t.Errorf("%d. %s: error:\n\texp=%s\n\tgot=%s\n", i, tt.in, tt.lit, lit)
}
}
}