2019-12-25 19:35:47 +00:00
|
|
|
|
package metricsql
|
2019-05-22 21:16:55 +00:00
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"reflect"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
2019-05-31 14:35:15 +00:00
|
|
|
|
func TestUnescapeIdent(t *testing.T) {
|
|
|
|
|
f := func(s, resultExpected string) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
result := unescapeIdent(s)
|
|
|
|
|
if result != resultExpected {
|
|
|
|
|
t.Fatalf("unexpected result for unescapeIdent(%q); got %q; want %q", s, result, resultExpected)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
f("", "")
|
|
|
|
|
f("a", "a")
|
|
|
|
|
f("\\", "")
|
|
|
|
|
f(`\\`, `\`)
|
|
|
|
|
f(`\foo\-bar`, `foo-bar`)
|
|
|
|
|
f(`a\\\\b\"c\d`, `a\\b"cd`)
|
|
|
|
|
f(`foo.bar:baz_123`, `foo.bar:baz_123`)
|
|
|
|
|
f(`foo\ bar`, `foo bar`)
|
|
|
|
|
f(`\x21`, `!`)
|
|
|
|
|
f(`\xeDfoo\x2Fbar\-\xqw\x`, "\xedfoo\x2fbar-xqwx")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAppendEscapedIdent(t *testing.T) {
|
|
|
|
|
f := func(s, resultExpected string) {
|
|
|
|
|
t.Helper()
|
2019-12-25 19:35:47 +00:00
|
|
|
|
result := appendEscapedIdent(nil, s)
|
2019-05-31 14:35:15 +00:00
|
|
|
|
if string(result) != resultExpected {
|
|
|
|
|
t.Fatalf("unexpected result for appendEscapedIdent(%q); got %q; want %q", s, result, resultExpected)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
f(`a`, `a`)
|
|
|
|
|
f(`a.b:c_23`, `a.b:c_23`)
|
|
|
|
|
f(`a b-cd+dd\`, `a\ b\-cd\+dd\\`)
|
|
|
|
|
f("a\x1E\x20\xee", `a\x1e\ \xee`)
|
|
|
|
|
f("\x2e\x2e", `\x2e.`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestScanIdent(t *testing.T) {
|
|
|
|
|
f := func(s, resultExpected string) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
result := scanIdent(s)
|
|
|
|
|
if result != resultExpected {
|
|
|
|
|
t.Fatalf("unexpected result for scanIdent(%q): got %q; want %q", s, result, resultExpected)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
f("a", "a")
|
|
|
|
|
f("foo.bar:baz_123", "foo.bar:baz_123")
|
|
|
|
|
f("a+b", "a")
|
|
|
|
|
f("foo()", "foo")
|
|
|
|
|
f(`a\-b+c`, `a\-b`)
|
|
|
|
|
f(`a\ b\\\ c\`, `a\ b\\\ c\`)
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-22 21:16:55 +00:00
|
|
|
|
func TestLexerNextPrev(t *testing.T) {
|
|
|
|
|
var lex lexer
|
|
|
|
|
lex.Init("foo bar baz")
|
|
|
|
|
if lex.Token != "" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "")
|
|
|
|
|
}
|
|
|
|
|
if err := lex.Next(); err != nil {
|
|
|
|
|
t.Fatalf("unexpeted error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if lex.Token != "foo" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "foo")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rewind before the first item.
|
|
|
|
|
lex.Prev()
|
|
|
|
|
if lex.Token != "" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "")
|
|
|
|
|
}
|
|
|
|
|
if err := lex.Next(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if lex.Token != "foo" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "foo")
|
|
|
|
|
}
|
|
|
|
|
if err := lex.Next(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if lex.Token != "bar" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "bar")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rewind to the first item.
|
|
|
|
|
lex.Prev()
|
|
|
|
|
if lex.Token != "foo" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "foo")
|
|
|
|
|
}
|
|
|
|
|
if err := lex.Next(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if lex.Token != "bar" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "bar")
|
|
|
|
|
}
|
|
|
|
|
if err := lex.Next(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if lex.Token != "baz" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "baz")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Go beyond the token stream.
|
|
|
|
|
if err := lex.Next(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if lex.Token != "" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "")
|
|
|
|
|
}
|
|
|
|
|
if !isEOF(lex.Token) {
|
|
|
|
|
t.Fatalf("expecting eof")
|
|
|
|
|
}
|
|
|
|
|
lex.Prev()
|
|
|
|
|
if lex.Token != "baz" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "baz")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Go multiple times lex.Next() beyond token stream.
|
|
|
|
|
if err := lex.Next(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if lex.Token != "" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "")
|
|
|
|
|
}
|
|
|
|
|
if !isEOF(lex.Token) {
|
|
|
|
|
t.Fatalf("expecting eof")
|
|
|
|
|
}
|
|
|
|
|
if err := lex.Next(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if lex.Token != "" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "")
|
|
|
|
|
}
|
|
|
|
|
if !isEOF(lex.Token) {
|
|
|
|
|
t.Fatalf("expecting eof")
|
|
|
|
|
}
|
|
|
|
|
lex.Prev()
|
|
|
|
|
if lex.Token != "" {
|
|
|
|
|
t.Fatalf("unexpected token got: %q; want %q", lex.Token, "")
|
|
|
|
|
}
|
|
|
|
|
if !isEOF(lex.Token) {
|
|
|
|
|
t.Fatalf("expecting eof")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLexerSuccess(t *testing.T) {
|
|
|
|
|
var s string
|
|
|
|
|
var expectedTokens []string
|
|
|
|
|
|
|
|
|
|
// An empty string
|
|
|
|
|
s = ""
|
|
|
|
|
expectedTokens = nil
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
// String with whitespace
|
|
|
|
|
s = " \n\t\r "
|
|
|
|
|
expectedTokens = nil
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
// Just metric name
|
|
|
|
|
s = "metric"
|
|
|
|
|
expectedTokens = []string{"metric"}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
// Metric name with spec chars
|
|
|
|
|
s = ":foo.bar_"
|
|
|
|
|
expectedTokens = []string{":foo.bar_"}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
// Metric name with window
|
|
|
|
|
s = "metric[5m] "
|
|
|
|
|
expectedTokens = []string{"metric", "[", "5m", "]"}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
// Metric name with tag filters
|
|
|
|
|
s = ` metric:12.34{a="foo", b != "bar", c=~ "x.+y", d !~ "zzz"}`
|
|
|
|
|
expectedTokens = []string{`metric:12.34`, `{`, `a`, `=`, `"foo"`, `,`, `b`, `!=`, `"bar"`, `,`, `c`, `=~`, `"x.+y"`, `,`, `d`, `!~`, `"zzz"`, `}`}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
// Metric name with offset
|
|
|
|
|
s = ` metric offset 10d `
|
|
|
|
|
expectedTokens = []string{`metric`, `offset`, `10d`}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
// Func call
|
|
|
|
|
s = `sum ( metric{x="y" } [5m] offset 10h)`
|
|
|
|
|
expectedTokens = []string{`sum`, `(`, `metric`, `{`, `x`, `=`, `"y"`, `}`, `[`, `5m`, `]`, `offset`, `10h`, `)`}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
// Binary op
|
|
|
|
|
s = `a+b or c % d and e unless f`
|
|
|
|
|
expectedTokens = []string{`a`, `+`, `b`, `or`, `c`, `%`, `d`, `and`, `e`, `unless`, `f`}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
// Numbers
|
|
|
|
|
s = `3+1.2-.23+4.5e5-78e-6+1.24e+45-NaN+Inf`
|
|
|
|
|
expectedTokens = []string{`3`, `+`, `1.2`, `-`, `.23`, `+`, `4.5e5`, `-`, `78e-6`, `+`, `1.24e+45`, `-`, `NaN`, `+`, `Inf`}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
s = `12.34`
|
|
|
|
|
expectedTokens = []string{`12.34`}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
// Strings
|
|
|
|
|
s = `""''` + "``" + `"\\" '\\' "\"" '\''"\\\"\\"`
|
|
|
|
|
expectedTokens = []string{`""`, `''`, "``", `"\\"`, `'\\'`, `"\""`, `'\''`, `"\\\"\\"`}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
s = " `foo\\\\\\`бар` "
|
|
|
|
|
expectedTokens = []string{"`foo\\\\\\`бар`"}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
|
|
|
|
|
s = `# comment # sdf
|
|
|
|
|
foobar # comment
|
|
|
|
|
baz
|
|
|
|
|
# yet another comment`
|
|
|
|
|
expectedTokens = []string{"foobar", "baz"}
|
|
|
|
|
testLexerSuccess(t, s, expectedTokens)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testLexerSuccess(t *testing.T, s string, expectedTokens []string) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
|
|
var lex lexer
|
|
|
|
|
lex.Init(s)
|
|
|
|
|
|
|
|
|
|
var tokens []string
|
|
|
|
|
for {
|
|
|
|
|
if err := lex.Next(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if isEOF(lex.Token) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
tokens = append(tokens, lex.Token)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(tokens, expectedTokens) {
|
|
|
|
|
t.Fatalf("unexected tokens\ngot\n%q\nwant\n%q", tokens, expectedTokens)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLexerError(t *testing.T) {
|
|
|
|
|
// Invalid identifier
|
|
|
|
|
testLexerError(t, ".foo")
|
|
|
|
|
|
|
|
|
|
// Incomplete string
|
|
|
|
|
testLexerError(t, `"foobar`)
|
|
|
|
|
testLexerError(t, `'`)
|
|
|
|
|
testLexerError(t, "`")
|
|
|
|
|
|
|
|
|
|
// Unrecognized char
|
|
|
|
|
testLexerError(t, "тест")
|
|
|
|
|
|
|
|
|
|
// Invalid numbers
|
|
|
|
|
testLexerError(t, `.`)
|
|
|
|
|
testLexerError(t, `123.`)
|
|
|
|
|
testLexerError(t, `12e`)
|
|
|
|
|
testLexerError(t, `1.2e`)
|
|
|
|
|
testLexerError(t, `1.2E+`)
|
|
|
|
|
testLexerError(t, `1.2E-`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testLexerError(t *testing.T, s string) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
|
|
var lex lexer
|
|
|
|
|
lex.Init(s)
|
|
|
|
|
for {
|
|
|
|
|
if err := lex.Next(); err != nil {
|
|
|
|
|
// Expected error
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if isEOF(lex.Token) {
|
|
|
|
|
t.Fatalf("expecting error during parse")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try calling Next again. It must return error.
|
|
|
|
|
if err := lex.Next(); err == nil {
|
|
|
|
|
t.Fatalf("expecting non-nil error")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-10 21:41:11 +00:00
|
|
|
|
func TestPositiveDurationSuccess(t *testing.T) {
|
|
|
|
|
f := func(s string, step, expectedD int64) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
d, err := PositiveDurationValue(s, step)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if d != expectedD {
|
|
|
|
|
t.Fatalf("unexpected duration; got %d; want %d", d, expectedD)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-22 21:16:55 +00:00
|
|
|
|
// Integer durations
|
2019-12-10 21:41:11 +00:00
|
|
|
|
f("123s", 42, 123*1000)
|
|
|
|
|
f("123m", 42, 123*60*1000)
|
|
|
|
|
f("1h", 42, 1*60*60*1000)
|
|
|
|
|
f("2d", 42, 2*24*60*60*1000)
|
|
|
|
|
f("3w", 42, 3*7*24*60*60*1000)
|
|
|
|
|
f("4y", 42, 4*365*24*60*60*1000)
|
|
|
|
|
f("1i", 42*1000, 42*1000)
|
|
|
|
|
f("3i", 42, 3*42)
|
2019-05-22 21:16:55 +00:00
|
|
|
|
|
|
|
|
|
// Float durations
|
2019-12-10 21:41:11 +00:00
|
|
|
|
f("0.234s", 42, 234)
|
|
|
|
|
f("1.5s", 42, 1.5*1000)
|
|
|
|
|
f("1.5m", 42, 1.5*60*1000)
|
|
|
|
|
f("1.2h", 42, 1.2*60*60*1000)
|
|
|
|
|
f("1.1d", 42, 1.1*24*60*60*1000)
|
|
|
|
|
f("1.1w", 42, 1.1*7*24*60*60*1000)
|
|
|
|
|
f("1.3y", 42, 1.3*365*24*60*60*1000)
|
|
|
|
|
f("0.1i", 12340, 0.1*12340)
|
2019-05-22 21:16:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-10 21:41:11 +00:00
|
|
|
|
func TestPositiveDurationError(t *testing.T) {
|
|
|
|
|
f := func(s string) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
if isPositiveDuration(s) {
|
|
|
|
|
t.Fatalf("unexpected valid duration %q", s)
|
|
|
|
|
}
|
|
|
|
|
d, err := PositiveDurationValue(s, 42)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("expecting non-nil error for duration %q", s)
|
|
|
|
|
}
|
|
|
|
|
if d != 0 {
|
|
|
|
|
t.Fatalf("expecting zero duration; got %d", d)
|
|
|
|
|
}
|
2019-05-22 21:16:55 +00:00
|
|
|
|
}
|
2019-12-10 21:41:11 +00:00
|
|
|
|
f("")
|
|
|
|
|
f("foo")
|
|
|
|
|
f("m")
|
|
|
|
|
f("12")
|
|
|
|
|
f("1.23")
|
|
|
|
|
f("1.23mm")
|
|
|
|
|
f("123q")
|
|
|
|
|
f("-123s")
|
2019-05-22 21:16:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-10 21:41:11 +00:00
|
|
|
|
func TestDurationSuccess(t *testing.T) {
|
|
|
|
|
f := func(s string, step, expectedD int64) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
d, err := DurationValue(s, step)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if d != expectedD {
|
|
|
|
|
t.Fatalf("unexpected duration; got %d; want %d", d, expectedD)
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-22 21:16:55 +00:00
|
|
|
|
|
2019-12-10 21:41:11 +00:00
|
|
|
|
// Integer durations
|
|
|
|
|
f("123s", 42, 123*1000)
|
|
|
|
|
f("-123s", 42, -123*1000)
|
|
|
|
|
f("123m", 42, 123*60*1000)
|
|
|
|
|
f("1h", 42, 1*60*60*1000)
|
|
|
|
|
f("2d", 42, 2*24*60*60*1000)
|
|
|
|
|
f("3w", 42, 3*7*24*60*60*1000)
|
|
|
|
|
f("4y", 42, 4*365*24*60*60*1000)
|
|
|
|
|
f("1i", 42*1000, 42*1000)
|
|
|
|
|
f("3i", 42, 3*42)
|
|
|
|
|
f("-3i", 42, -3*42)
|
2019-05-22 21:16:55 +00:00
|
|
|
|
|
2019-12-10 21:41:11 +00:00
|
|
|
|
// Float durations
|
|
|
|
|
f("0.234s", 42, 234)
|
|
|
|
|
f("-0.234s", 42, -234)
|
|
|
|
|
f("1.5s", 42, 1.5*1000)
|
|
|
|
|
f("1.5m", 42, 1.5*60*1000)
|
|
|
|
|
f("1.2h", 42, 1.2*60*60*1000)
|
|
|
|
|
f("1.1d", 42, 1.1*24*60*60*1000)
|
|
|
|
|
f("1.1w", 42, 1.1*7*24*60*60*1000)
|
|
|
|
|
f("1.3y", 42, 1.3*365*24*60*60*1000)
|
|
|
|
|
f("-1.3y", 42, -1.3*365*24*60*60*1000)
|
|
|
|
|
f("0.1i", 12340, 0.1*12340)
|
|
|
|
|
}
|
2019-05-22 21:16:55 +00:00
|
|
|
|
|
2019-12-10 21:41:11 +00:00
|
|
|
|
func TestDurationError(t *testing.T) {
|
|
|
|
|
f := func(s string) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
d, err := DurationValue(s, 42)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("expecting non-nil error for duration %q", s)
|
|
|
|
|
}
|
|
|
|
|
if d != 0 {
|
|
|
|
|
t.Fatalf("expecting zero duration; got %d", d)
|
|
|
|
|
}
|
2019-05-22 21:16:55 +00:00
|
|
|
|
}
|
2019-12-10 21:41:11 +00:00
|
|
|
|
f("")
|
|
|
|
|
f("foo")
|
|
|
|
|
f("m")
|
|
|
|
|
f("12")
|
|
|
|
|
f("1.23")
|
|
|
|
|
f("1.23mm")
|
|
|
|
|
f("123q")
|
2019-05-22 21:16:55 +00:00
|
|
|
|
}
|