mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-22 16:36:27 +01:00
402 lines
9.5 KiB
Go
402 lines
9.5 KiB
Go
package metricsql
|
||
|
||
import (
|
||
"reflect"
|
||
"testing"
|
||
)
|
||
|
||
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()
|
||
result := appendEscapedIdent(nil, s)
|
||
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\`)
|
||
}
|
||
|
||
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")
|
||
}
|
||
}
|
||
|
||
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)
|
||
}
|
||
}
|
||
|
||
// Integer durations
|
||
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)
|
||
|
||
// Float durations
|
||
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)
|
||
}
|
||
|
||
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)
|
||
}
|
||
}
|
||
f("")
|
||
f("foo")
|
||
f("m")
|
||
f("12")
|
||
f("1.23")
|
||
f("1.23mm")
|
||
f("123q")
|
||
f("-123s")
|
||
}
|
||
|
||
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)
|
||
}
|
||
}
|
||
|
||
// 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)
|
||
|
||
// 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)
|
||
}
|
||
|
||
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)
|
||
}
|
||
}
|
||
f("")
|
||
f("foo")
|
||
f("m")
|
||
f("12")
|
||
f("1.23")
|
||
f("1.23mm")
|
||
f("123q")
|
||
}
|