From f62fd2bfc9f82200b5dfa9bbe3b7e592a5236b81 Mon Sep 17 00:00:00 2001 From: Matt Layher Date: Wed, 16 Jan 2019 16:35:45 -0500 Subject: [PATCH] promql: use subtests in TestLexer Signed-off-by: Matt Layher --- promql/lex_test.go | 1183 +++++++++++++++++++++++--------------------- 1 file changed, 626 insertions(+), 557 deletions(-) diff --git a/promql/lex_test.go b/promql/lex_test.go index b4b9280d9..86eb3fdf3 100644 --- a/promql/lex_test.go +++ b/promql/lex_test.go @@ -19,617 +19,686 @@ import ( "testing" ) -var tests = []struct { +type testCase struct { input string expected []item fail bool seriesDesc bool // Whether to lex a series description. +} + +var tests = []struct { + name string + tests []testCase }{ - // Test common stuff. { - input: ",", - expected: []item{{itemComma, 0, ","}}, - }, { - input: "()", - expected: []item{{itemLeftParen, 0, `(`}, {itemRightParen, 1, `)`}}, - }, { - input: "{}", - expected: []item{{itemLeftBrace, 0, `{`}, {itemRightBrace, 1, `}`}}, - }, { - input: "[5m]", - expected: []item{ - {itemLeftBracket, 0, `[`}, - {itemDuration, 1, `5m`}, - {itemRightBracket, 3, `]`}, + name: "common", + tests: []testCase{ + { + input: ",", + expected: []item{{itemComma, 0, ","}}, + }, { + input: "()", + expected: []item{{itemLeftParen, 0, `(`}, {itemRightParen, 1, `)`}}, + }, { + input: "{}", + expected: []item{{itemLeftBrace, 0, `{`}, {itemRightBrace, 1, `}`}}, + }, { + input: "[5m]", + expected: []item{ + {itemLeftBracket, 0, `[`}, + {itemDuration, 1, `5m`}, + {itemRightBracket, 3, `]`}, + }, + }, { + input: "\r\n\r", + expected: []item{}, + }, }, - }, { - input: "\r\n\r", - expected: []item{}, }, - // Test numbers. { - input: "1", - expected: []item{{itemNumber, 0, "1"}}, - }, { - input: "4.23", - expected: []item{{itemNumber, 0, "4.23"}}, - }, { - input: ".3", - expected: []item{{itemNumber, 0, ".3"}}, - }, { - input: "5.", - expected: []item{{itemNumber, 0, "5."}}, - }, { - input: "NaN", - expected: []item{{itemNumber, 0, "NaN"}}, - }, { - input: "nAN", - expected: []item{{itemNumber, 0, "nAN"}}, - }, { - input: "NaN 123", - expected: []item{{itemNumber, 0, "NaN"}, {itemNumber, 4, "123"}}, - }, { - input: "NaN123", - expected: []item{{itemIdentifier, 0, "NaN123"}}, - }, { - input: "iNf", - expected: []item{{itemNumber, 0, "iNf"}}, - }, { - input: "Inf", - expected: []item{{itemNumber, 0, "Inf"}}, - }, { - input: "+Inf", - expected: []item{{itemADD, 0, "+"}, {itemNumber, 1, "Inf"}}, - }, { - input: "+Inf 123", - expected: []item{{itemADD, 0, "+"}, {itemNumber, 1, "Inf"}, {itemNumber, 5, "123"}}, - }, { - input: "-Inf", - expected: []item{{itemSUB, 0, "-"}, {itemNumber, 1, "Inf"}}, - }, { - input: "Infoo", - expected: []item{{itemIdentifier, 0, "Infoo"}}, - }, { - input: "-Infoo", - expected: []item{{itemSUB, 0, "-"}, {itemIdentifier, 1, "Infoo"}}, - }, { - input: "-Inf 123", - expected: []item{{itemSUB, 0, "-"}, {itemNumber, 1, "Inf"}, {itemNumber, 5, "123"}}, - }, { - input: "0x123", - expected: []item{{itemNumber, 0, "0x123"}}, - }, - // Test strings. - { - input: "\"test\\tsequence\"", - expected: []item{{itemString, 0, `"test\tsequence"`}}, - }, - { - input: "\"test\\\\.expression\"", - expected: []item{{itemString, 0, `"test\\.expression"`}}, - }, - { - input: "\"test\\.expression\"", - expected: []item{ - {itemError, 0, "unknown escape sequence U+002E '.'"}, - {itemString, 0, `"test\.expression"`}, + name: "numbers", + tests: []testCase{ + { + input: "1", + expected: []item{{itemNumber, 0, "1"}}, + }, { + input: "4.23", + expected: []item{{itemNumber, 0, "4.23"}}, + }, { + input: ".3", + expected: []item{{itemNumber, 0, ".3"}}, + }, { + input: "5.", + expected: []item{{itemNumber, 0, "5."}}, + }, { + input: "NaN", + expected: []item{{itemNumber, 0, "NaN"}}, + }, { + input: "nAN", + expected: []item{{itemNumber, 0, "nAN"}}, + }, { + input: "NaN 123", + expected: []item{{itemNumber, 0, "NaN"}, {itemNumber, 4, "123"}}, + }, { + input: "NaN123", + expected: []item{{itemIdentifier, 0, "NaN123"}}, + }, { + input: "iNf", + expected: []item{{itemNumber, 0, "iNf"}}, + }, { + input: "Inf", + expected: []item{{itemNumber, 0, "Inf"}}, + }, { + input: "+Inf", + expected: []item{{itemADD, 0, "+"}, {itemNumber, 1, "Inf"}}, + }, { + input: "+Inf 123", + expected: []item{{itemADD, 0, "+"}, {itemNumber, 1, "Inf"}, {itemNumber, 5, "123"}}, + }, { + input: "-Inf", + expected: []item{{itemSUB, 0, "-"}, {itemNumber, 1, "Inf"}}, + }, { + input: "Infoo", + expected: []item{{itemIdentifier, 0, "Infoo"}}, + }, { + input: "-Infoo", + expected: []item{{itemSUB, 0, "-"}, {itemIdentifier, 1, "Infoo"}}, + }, { + input: "-Inf 123", + expected: []item{{itemSUB, 0, "-"}, {itemNumber, 1, "Inf"}, {itemNumber, 5, "123"}}, + }, { + input: "0x123", + expected: []item{{itemNumber, 0, "0x123"}}, + }, }, }, { - input: "`test\\.expression`", - expected: []item{{itemString, 0, "`test\\.expression`"}}, - }, - { - // See https://github.com/prometheus/prometheus/issues/939. - input: ".٩", - fail: true, - }, - // Test duration. - { - input: "5s", - expected: []item{{itemDuration, 0, "5s"}}, - }, { - input: "123m", - expected: []item{{itemDuration, 0, "123m"}}, - }, { - input: "1h", - expected: []item{{itemDuration, 0, "1h"}}, - }, { - input: "3w", - expected: []item{{itemDuration, 0, "3w"}}, - }, { - input: "1y", - expected: []item{{itemDuration, 0, "1y"}}, - }, - // Test identifiers. - { - input: "abc", - expected: []item{{itemIdentifier, 0, "abc"}}, - }, { - input: "a:bc", - expected: []item{{itemMetricIdentifier, 0, "a:bc"}}, - }, { - input: "abc d", - expected: []item{{itemIdentifier, 0, "abc"}, {itemIdentifier, 4, "d"}}, - }, { - input: ":bc", - expected: []item{{itemMetricIdentifier, 0, ":bc"}}, - }, { - input: "0a:bc", - fail: true, - }, - // Test comments. - { - input: "# some comment", - expected: []item{{itemComment, 0, "# some comment"}}, - }, { - input: "5 # 1+1\n5", - expected: []item{ - {itemNumber, 0, "5"}, - {itemComment, 2, "# 1+1"}, - {itemNumber, 8, "5"}, + name: "strings", + tests: []testCase{ + { + input: "\"test\\tsequence\"", + expected: []item{{itemString, 0, `"test\tsequence"`}}, + }, + { + input: "\"test\\\\.expression\"", + expected: []item{{itemString, 0, `"test\\.expression"`}}, + }, + { + input: "\"test\\.expression\"", + expected: []item{ + {itemError, 0, "unknown escape sequence U+002E '.'"}, + {itemString, 0, `"test\.expression"`}, + }, + }, + { + input: "`test\\.expression`", + expected: []item{{itemString, 0, "`test\\.expression`"}}, + }, + { + // See https://github.com/prometheus/prometheus/issues/939. + input: ".٩", + fail: true, + }, }, }, - // Test operators. - { - input: `=`, - expected: []item{{itemAssign, 0, `=`}}, - }, { - // Inside braces equality is a single '=' character. - input: `{=}`, - expected: []item{{itemLeftBrace, 0, `{`}, {itemEQL, 1, `=`}, {itemRightBrace, 2, `}`}}, - }, { - input: `==`, - expected: []item{{itemEQL, 0, `==`}}, - }, { - input: `!=`, - expected: []item{{itemNEQ, 0, `!=`}}, - }, { - input: `<`, - expected: []item{{itemLSS, 0, `<`}}, - }, { - input: `>`, - expected: []item{{itemGTR, 0, `>`}}, - }, { - input: `>=`, - expected: []item{{itemGTE, 0, `>=`}}, - }, { - input: `<=`, - expected: []item{{itemLTE, 0, `<=`}}, - }, { - input: `+`, - expected: []item{{itemADD, 0, `+`}}, - }, { - input: `-`, - expected: []item{{itemSUB, 0, `-`}}, - }, { - input: `*`, - expected: []item{{itemMUL, 0, `*`}}, - }, { - input: `/`, - expected: []item{{itemDIV, 0, `/`}}, - }, { - input: `^`, - expected: []item{{itemPOW, 0, `^`}}, - }, { - input: `%`, - expected: []item{{itemMOD, 0, `%`}}, - }, { - input: `AND`, - expected: []item{{itemLAND, 0, `AND`}}, - }, { - input: `or`, - expected: []item{{itemLOR, 0, `or`}}, - }, { - input: `unless`, - expected: []item{{itemLUnless, 0, `unless`}}, - }, - // Test aggregators. - { - input: `sum`, - expected: []item{{itemSum, 0, `sum`}}, - }, { - input: `AVG`, - expected: []item{{itemAvg, 0, `AVG`}}, - }, { - input: `MAX`, - expected: []item{{itemMax, 0, `MAX`}}, - }, { - input: `min`, - expected: []item{{itemMin, 0, `min`}}, - }, { - input: `count`, - expected: []item{{itemCount, 0, `count`}}, - }, { - input: `stdvar`, - expected: []item{{itemStdvar, 0, `stdvar`}}, - }, { - input: `stddev`, - expected: []item{{itemStddev, 0, `stddev`}}, - }, - // Test keywords. { - input: "offset", - expected: []item{{itemOffset, 0, "offset"}}, - }, { - input: "by", - expected: []item{{itemBy, 0, "by"}}, - }, { - input: "without", - expected: []item{{itemWithout, 0, "without"}}, - }, { - input: "on", - expected: []item{{itemOn, 0, "on"}}, - }, { - input: "ignoring", - expected: []item{{itemIgnoring, 0, "ignoring"}}, - }, { - input: "group_left", - expected: []item{{itemGroupLeft, 0, "group_left"}}, - }, { - input: "group_right", - expected: []item{{itemGroupRight, 0, "group_right"}}, - }, { - input: "bool", - expected: []item{{itemBool, 0, "bool"}}, + name: "durations", + tests: []testCase{ + { + input: "5s", + expected: []item{{itemDuration, 0, "5s"}}, + }, { + input: "123m", + expected: []item{{itemDuration, 0, "123m"}}, + }, { + input: "1h", + expected: []item{{itemDuration, 0, "1h"}}, + }, { + input: "3w", + expected: []item{{itemDuration, 0, "3w"}}, + }, { + input: "1y", + expected: []item{{itemDuration, 0, "1y"}}, + }, + }, }, - // Test Selector. { - input: `台北`, - fail: true, - }, { - input: `{台北='a'}`, - fail: true, - }, { - input: `{0a='a'}`, - fail: true, - }, { - input: `{foo='bar'}`, - expected: []item{ - {itemLeftBrace, 0, `{`}, - {itemIdentifier, 1, `foo`}, - {itemEQL, 4, `=`}, - {itemString, 5, `'bar'`}, - {itemRightBrace, 10, `}`}, - }, - }, { - input: `{foo="bar"}`, - expected: []item{ - {itemLeftBrace, 0, `{`}, - {itemIdentifier, 1, `foo`}, - {itemEQL, 4, `=`}, - {itemString, 5, `"bar"`}, - {itemRightBrace, 10, `}`}, - }, - }, { - input: `{foo="bar\"bar"}`, - expected: []item{ - {itemLeftBrace, 0, `{`}, - {itemIdentifier, 1, `foo`}, - {itemEQL, 4, `=`}, - {itemString, 5, `"bar\"bar"`}, - {itemRightBrace, 15, `}`}, + name: "identifiers", + tests: []testCase{ + { + input: "abc", + expected: []item{{itemIdentifier, 0, "abc"}}, + }, { + input: "a:bc", + expected: []item{{itemMetricIdentifier, 0, "a:bc"}}, + }, { + input: "abc d", + expected: []item{{itemIdentifier, 0, "abc"}, {itemIdentifier, 4, "d"}}, + }, { + input: ":bc", + expected: []item{{itemMetricIdentifier, 0, ":bc"}}, + }, { + input: "0a:bc", + fail: true, + }, }, - }, { - input: `{NaN != "bar" }`, - expected: []item{ - {itemLeftBrace, 0, `{`}, - {itemIdentifier, 1, `NaN`}, - {itemNEQ, 5, `!=`}, - {itemString, 8, `"bar"`}, - {itemRightBrace, 14, `}`}, - }, - }, { - input: `{alert=~"bar" }`, - expected: []item{ - {itemLeftBrace, 0, `{`}, - {itemIdentifier, 1, `alert`}, - {itemEQLRegex, 6, `=~`}, - {itemString, 8, `"bar"`}, - {itemRightBrace, 14, `}`}, - }, - }, { - input: `{on!~"bar"}`, - expected: []item{ - {itemLeftBrace, 0, `{`}, - {itemIdentifier, 1, `on`}, - {itemNEQRegex, 3, `!~`}, - {itemString, 5, `"bar"`}, - {itemRightBrace, 10, `}`}, - }, - }, { - input: `{alert!#"bar"}`, fail: true, - }, { - input: `{foo:a="bar"}`, fail: true, }, - // Test common errors. { - input: `=~`, fail: true, - }, { - input: `!~`, fail: true, - }, { - input: `!(`, fail: true, - }, { - input: "1a", fail: true, + name: "comments", + tests: []testCase{ + { + input: "# some comment", + expected: []item{{itemComment, 0, "# some comment"}}, + }, { + input: "5 # 1+1\n5", + expected: []item{ + {itemNumber, 0, "5"}, + {itemComment, 2, "# 1+1"}, + {itemNumber, 8, "5"}, + }, + }, + }, }, - // Test mismatched parens. { - input: `(`, fail: true, - }, { - input: `())`, fail: true, - }, { - input: `(()`, fail: true, - }, { - input: `{`, fail: true, - }, { - input: `}`, fail: true, - }, { - input: "{{", fail: true, - }, { - input: "{{}}", fail: true, - }, { - input: `[`, fail: true, - }, { - input: `[[`, fail: true, - }, { - input: `[]]`, fail: true, - }, { - input: `[[]]`, fail: true, - }, { - input: `]`, fail: true, + name: "operators", + tests: []testCase{ + { + input: `=`, + expected: []item{{itemAssign, 0, `=`}}, + }, { + // Inside braces equality is a single '=' character. + input: `{=}`, + expected: []item{{itemLeftBrace, 0, `{`}, {itemEQL, 1, `=`}, {itemRightBrace, 2, `}`}}, + }, { + input: `==`, + expected: []item{{itemEQL, 0, `==`}}, + }, { + input: `!=`, + expected: []item{{itemNEQ, 0, `!=`}}, + }, { + input: `<`, + expected: []item{{itemLSS, 0, `<`}}, + }, { + input: `>`, + expected: []item{{itemGTR, 0, `>`}}, + }, { + input: `>=`, + expected: []item{{itemGTE, 0, `>=`}}, + }, { + input: `<=`, + expected: []item{{itemLTE, 0, `<=`}}, + }, { + input: `+`, + expected: []item{{itemADD, 0, `+`}}, + }, { + input: `-`, + expected: []item{{itemSUB, 0, `-`}}, + }, { + input: `*`, + expected: []item{{itemMUL, 0, `*`}}, + }, { + input: `/`, + expected: []item{{itemDIV, 0, `/`}}, + }, { + input: `^`, + expected: []item{{itemPOW, 0, `^`}}, + }, { + input: `%`, + expected: []item{{itemMOD, 0, `%`}}, + }, { + input: `AND`, + expected: []item{{itemLAND, 0, `AND`}}, + }, { + input: `or`, + expected: []item{{itemLOR, 0, `or`}}, + }, { + input: `unless`, + expected: []item{{itemLUnless, 0, `unless`}}, + }, + }, }, - // Test encoding issues. { - input: "\"\xff\"", fail: true, + name: "aggregators", + tests: []testCase{ + { + input: `sum`, + expected: []item{{itemSum, 0, `sum`}}, + }, { + input: `AVG`, + expected: []item{{itemAvg, 0, `AVG`}}, + }, { + input: `MAX`, + expected: []item{{itemMax, 0, `MAX`}}, + }, { + input: `min`, + expected: []item{{itemMin, 0, `min`}}, + }, { + input: `count`, + expected: []item{{itemCount, 0, `count`}}, + }, { + input: `stdvar`, + expected: []item{{itemStdvar, 0, `stdvar`}}, + }, { + input: `stddev`, + expected: []item{{itemStddev, 0, `stddev`}}, + }, + }, }, { - input: "`\xff`", fail: true, + name: "keywords", + tests: []testCase{ + { + input: "offset", + expected: []item{{itemOffset, 0, "offset"}}, + }, { + input: "by", + expected: []item{{itemBy, 0, "by"}}, + }, { + input: "without", + expected: []item{{itemWithout, 0, "without"}}, + }, { + input: "on", + expected: []item{{itemOn, 0, "on"}}, + }, { + input: "ignoring", + expected: []item{{itemIgnoring, 0, "ignoring"}}, + }, { + input: "group_left", + expected: []item{{itemGroupLeft, 0, "group_left"}}, + }, { + input: "group_right", + expected: []item{{itemGroupRight, 0, "group_right"}}, + }, { + input: "bool", + expected: []item{{itemBool, 0, "bool"}}, + }, + }, }, - // Test series description. { - input: `{} _ 1 x .3`, - expected: []item{ - {itemLeftBrace, 0, `{`}, - {itemRightBrace, 1, `}`}, - {itemSpace, 2, ` `}, - {itemBlank, 3, `_`}, - {itemSpace, 4, ` `}, - {itemNumber, 5, `1`}, - {itemSpace, 6, ` `}, - {itemTimes, 7, `x`}, - {itemSpace, 8, ` `}, - {itemNumber, 9, `.3`}, + name: "selectors", + tests: []testCase{ + { + input: `台北`, + fail: true, + }, { + input: `{台北='a'}`, + fail: true, + }, { + input: `{0a='a'}`, + fail: true, + }, { + input: `{foo='bar'}`, + expected: []item{ + {itemLeftBrace, 0, `{`}, + {itemIdentifier, 1, `foo`}, + {itemEQL, 4, `=`}, + {itemString, 5, `'bar'`}, + {itemRightBrace, 10, `}`}, + }, + }, { + input: `{foo="bar"}`, + expected: []item{ + {itemLeftBrace, 0, `{`}, + {itemIdentifier, 1, `foo`}, + {itemEQL, 4, `=`}, + {itemString, 5, `"bar"`}, + {itemRightBrace, 10, `}`}, + }, + }, { + input: `{foo="bar\"bar"}`, + expected: []item{ + {itemLeftBrace, 0, `{`}, + {itemIdentifier, 1, `foo`}, + {itemEQL, 4, `=`}, + {itemString, 5, `"bar\"bar"`}, + {itemRightBrace, 15, `}`}, + }, + }, { + input: `{NaN != "bar" }`, + expected: []item{ + {itemLeftBrace, 0, `{`}, + {itemIdentifier, 1, `NaN`}, + {itemNEQ, 5, `!=`}, + {itemString, 8, `"bar"`}, + {itemRightBrace, 14, `}`}, + }, + }, { + input: `{alert=~"bar" }`, + expected: []item{ + {itemLeftBrace, 0, `{`}, + {itemIdentifier, 1, `alert`}, + {itemEQLRegex, 6, `=~`}, + {itemString, 8, `"bar"`}, + {itemRightBrace, 14, `}`}, + }, + }, { + input: `{on!~"bar"}`, + expected: []item{ + {itemLeftBrace, 0, `{`}, + {itemIdentifier, 1, `on`}, + {itemNEQRegex, 3, `!~`}, + {itemString, 5, `"bar"`}, + {itemRightBrace, 10, `}`}, + }, + }, { + input: `{alert!#"bar"}`, fail: true, + }, { + input: `{foo:a="bar"}`, fail: true, + }, }, - seriesDesc: true, }, { - input: `metric +Inf Inf NaN`, - expected: []item{ - {itemIdentifier, 0, `metric`}, - {itemSpace, 6, ` `}, - {itemADD, 7, `+`}, - {itemNumber, 8, `Inf`}, - {itemSpace, 11, ` `}, - {itemNumber, 12, `Inf`}, - {itemSpace, 15, ` `}, - {itemNumber, 16, `NaN`}, + name: "common errors", + tests: []testCase{ + { + input: `=~`, fail: true, + }, { + input: `!~`, fail: true, + }, { + input: `!(`, fail: true, + }, { + input: "1a", fail: true, + }, }, - seriesDesc: true, }, { - input: `metric 1+1x4`, - expected: []item{ - {itemIdentifier, 0, `metric`}, - {itemSpace, 6, ` `}, - {itemNumber, 7, `1`}, - {itemADD, 8, `+`}, - {itemNumber, 9, `1`}, - {itemTimes, 10, `x`}, - {itemNumber, 11, `4`}, + name: "mismatched parentheses", + tests: []testCase{ + { + input: `(`, fail: true, + }, { + input: `())`, fail: true, + }, { + input: `(()`, fail: true, + }, { + input: `{`, fail: true, + }, { + input: `}`, fail: true, + }, { + input: "{{", fail: true, + }, { + input: "{{}}", fail: true, + }, { + input: `[`, fail: true, + }, { + input: `[[`, fail: true, + }, { + input: `[]]`, fail: true, + }, { + input: `[[]]`, fail: true, + }, { + input: `]`, fail: true, + }, }, - seriesDesc: true, }, - // Test subquery. { - input: `test_name{on!~"bar"}[4m:4s]`, - expected: []item{ - {itemIdentifier, 0, `test_name`}, - {itemLeftBrace, 9, `{`}, - {itemIdentifier, 10, `on`}, - {itemNEQRegex, 12, `!~`}, - {itemString, 14, `"bar"`}, - {itemRightBrace, 19, `}`}, - {itemLeftBracket, 20, `[`}, - {itemDuration, 21, `4m`}, - {itemColon, 23, `:`}, - {itemDuration, 24, `4s`}, - {itemRightBracket, 26, `]`}, + name: "encoding issues", + tests: []testCase{ + { + input: "\"\xff\"", fail: true, + }, + { + input: "`\xff`", fail: true, + }, }, }, { - input: `test:name{on!~"bar"}[4m:4s]`, - expected: []item{ - {itemMetricIdentifier, 0, `test:name`}, - {itemLeftBrace, 9, `{`}, - {itemIdentifier, 10, `on`}, - {itemNEQRegex, 12, `!~`}, - {itemString, 14, `"bar"`}, - {itemRightBrace, 19, `}`}, - {itemLeftBracket, 20, `[`}, - {itemDuration, 21, `4m`}, - {itemColon, 23, `:`}, - {itemDuration, 24, `4s`}, - {itemRightBracket, 26, `]`}, - }, - }, { - input: `test:name{on!~"b:ar"}[4m:4s]`, - expected: []item{ - {itemMetricIdentifier, 0, `test:name`}, - {itemLeftBrace, 9, `{`}, - {itemIdentifier, 10, `on`}, - {itemNEQRegex, 12, `!~`}, - {itemString, 14, `"b:ar"`}, - {itemRightBrace, 20, `}`}, - {itemLeftBracket, 21, `[`}, - {itemDuration, 22, `4m`}, - {itemColon, 24, `:`}, - {itemDuration, 25, `4s`}, - {itemRightBracket, 27, `]`}, - }, - }, { - input: `test:name{on!~"b:ar"}[4m:]`, - expected: []item{ - {itemMetricIdentifier, 0, `test:name`}, - {itemLeftBrace, 9, `{`}, - {itemIdentifier, 10, `on`}, - {itemNEQRegex, 12, `!~`}, - {itemString, 14, `"b:ar"`}, - {itemRightBrace, 20, `}`}, - {itemLeftBracket, 21, `[`}, - {itemDuration, 22, `4m`}, - {itemColon, 24, `:`}, - {itemRightBracket, 25, `]`}, - }, - }, { // Nested Subquery. - input: `min_over_time(rate(foo{bar="baz"}[2s])[5m:])[4m:3s]`, - expected: []item{ - - {itemIdentifier, 0, `min_over_time`}, - {itemLeftParen, 13, `(`}, - {itemIdentifier, 14, `rate`}, - {itemLeftParen, 18, `(`}, - {itemIdentifier, 19, `foo`}, - {itemLeftBrace, 22, `{`}, - {itemIdentifier, 23, `bar`}, - {itemEQL, 26, `=`}, - {itemString, 27, `"baz"`}, - {itemRightBrace, 32, `}`}, - {itemLeftBracket, 33, `[`}, - {itemDuration, 34, `2s`}, - {itemRightBracket, 36, `]`}, - {itemRightParen, 37, `)`}, - {itemLeftBracket, 38, `[`}, - {itemDuration, 39, `5m`}, - {itemColon, 41, `:`}, - {itemRightBracket, 42, `]`}, - {itemRightParen, 43, `)`}, - {itemLeftBracket, 44, `[`}, - {itemDuration, 45, `4m`}, - {itemColon, 47, `:`}, - {itemDuration, 48, `3s`}, - {itemRightBracket, 50, `]`}, + name: "series descriptions", + tests: []testCase{ + { + input: `{} _ 1 x .3`, + expected: []item{ + {itemLeftBrace, 0, `{`}, + {itemRightBrace, 1, `}`}, + {itemSpace, 2, ` `}, + {itemBlank, 3, `_`}, + {itemSpace, 4, ` `}, + {itemNumber, 5, `1`}, + {itemSpace, 6, ` `}, + {itemTimes, 7, `x`}, + {itemSpace, 8, ` `}, + {itemNumber, 9, `.3`}, + }, + seriesDesc: true, + }, + { + input: `metric +Inf Inf NaN`, + expected: []item{ + {itemIdentifier, 0, `metric`}, + {itemSpace, 6, ` `}, + {itemADD, 7, `+`}, + {itemNumber, 8, `Inf`}, + {itemSpace, 11, ` `}, + {itemNumber, 12, `Inf`}, + {itemSpace, 15, ` `}, + {itemNumber, 16, `NaN`}, + }, + seriesDesc: true, + }, + { + input: `metric 1+1x4`, + expected: []item{ + {itemIdentifier, 0, `metric`}, + {itemSpace, 6, ` `}, + {itemNumber, 7, `1`}, + {itemADD, 8, `+`}, + {itemNumber, 9, `1`}, + {itemTimes, 10, `x`}, + {itemNumber, 11, `4`}, + }, + seriesDesc: true, + }, }, }, - // Subquery with offset. { - input: `test:name{on!~"b:ar"}[4m:4s] offset 10m`, - expected: []item{ - {itemMetricIdentifier, 0, `test:name`}, - {itemLeftBrace, 9, `{`}, - {itemIdentifier, 10, `on`}, - {itemNEQRegex, 12, `!~`}, - {itemString, 14, `"b:ar"`}, - {itemRightBrace, 20, `}`}, - {itemLeftBracket, 21, `[`}, - {itemDuration, 22, `4m`}, - {itemColon, 24, `:`}, - {itemDuration, 25, `4s`}, - {itemRightBracket, 27, `]`}, - {itemOffset, 29, "offset"}, - {itemDuration, 36, "10m"}, - }, - }, { - input: `min_over_time(rate(foo{bar="baz"}[2s])[5m:] offset 6m)[4m:3s]`, - expected: []item{ + name: "subqueries", + tests: []testCase{ + { + input: `test_name{on!~"bar"}[4m:4s]`, + expected: []item{ + {itemIdentifier, 0, `test_name`}, + {itemLeftBrace, 9, `{`}, + {itemIdentifier, 10, `on`}, + {itemNEQRegex, 12, `!~`}, + {itemString, 14, `"bar"`}, + {itemRightBrace, 19, `}`}, + {itemLeftBracket, 20, `[`}, + {itemDuration, 21, `4m`}, + {itemColon, 23, `:`}, + {itemDuration, 24, `4s`}, + {itemRightBracket, 26, `]`}, + }, + }, + { + input: `test:name{on!~"bar"}[4m:4s]`, + expected: []item{ + {itemMetricIdentifier, 0, `test:name`}, + {itemLeftBrace, 9, `{`}, + {itemIdentifier, 10, `on`}, + {itemNEQRegex, 12, `!~`}, + {itemString, 14, `"bar"`}, + {itemRightBrace, 19, `}`}, + {itemLeftBracket, 20, `[`}, + {itemDuration, 21, `4m`}, + {itemColon, 23, `:`}, + {itemDuration, 24, `4s`}, + {itemRightBracket, 26, `]`}, + }, + }, { + input: `test:name{on!~"b:ar"}[4m:4s]`, + expected: []item{ + {itemMetricIdentifier, 0, `test:name`}, + {itemLeftBrace, 9, `{`}, + {itemIdentifier, 10, `on`}, + {itemNEQRegex, 12, `!~`}, + {itemString, 14, `"b:ar"`}, + {itemRightBrace, 20, `}`}, + {itemLeftBracket, 21, `[`}, + {itemDuration, 22, `4m`}, + {itemColon, 24, `:`}, + {itemDuration, 25, `4s`}, + {itemRightBracket, 27, `]`}, + }, + }, { + input: `test:name{on!~"b:ar"}[4m:]`, + expected: []item{ + {itemMetricIdentifier, 0, `test:name`}, + {itemLeftBrace, 9, `{`}, + {itemIdentifier, 10, `on`}, + {itemNEQRegex, 12, `!~`}, + {itemString, 14, `"b:ar"`}, + {itemRightBrace, 20, `}`}, + {itemLeftBracket, 21, `[`}, + {itemDuration, 22, `4m`}, + {itemColon, 24, `:`}, + {itemRightBracket, 25, `]`}, + }, + }, { // Nested Subquery. + input: `min_over_time(rate(foo{bar="baz"}[2s])[5m:])[4m:3s]`, + expected: []item{ + + {itemIdentifier, 0, `min_over_time`}, + {itemLeftParen, 13, `(`}, + {itemIdentifier, 14, `rate`}, + {itemLeftParen, 18, `(`}, + {itemIdentifier, 19, `foo`}, + {itemLeftBrace, 22, `{`}, + {itemIdentifier, 23, `bar`}, + {itemEQL, 26, `=`}, + {itemString, 27, `"baz"`}, + {itemRightBrace, 32, `}`}, + {itemLeftBracket, 33, `[`}, + {itemDuration, 34, `2s`}, + {itemRightBracket, 36, `]`}, + {itemRightParen, 37, `)`}, + {itemLeftBracket, 38, `[`}, + {itemDuration, 39, `5m`}, + {itemColon, 41, `:`}, + {itemRightBracket, 42, `]`}, + {itemRightParen, 43, `)`}, + {itemLeftBracket, 44, `[`}, + {itemDuration, 45, `4m`}, + {itemColon, 47, `:`}, + {itemDuration, 48, `3s`}, + {itemRightBracket, 50, `]`}, + }, + }, + // Subquery with offset. + { + input: `test:name{on!~"b:ar"}[4m:4s] offset 10m`, + expected: []item{ + {itemMetricIdentifier, 0, `test:name`}, + {itemLeftBrace, 9, `{`}, + {itemIdentifier, 10, `on`}, + {itemNEQRegex, 12, `!~`}, + {itemString, 14, `"b:ar"`}, + {itemRightBrace, 20, `}`}, + {itemLeftBracket, 21, `[`}, + {itemDuration, 22, `4m`}, + {itemColon, 24, `:`}, + {itemDuration, 25, `4s`}, + {itemRightBracket, 27, `]`}, + {itemOffset, 29, "offset"}, + {itemDuration, 36, "10m"}, + }, + }, { + input: `min_over_time(rate(foo{bar="baz"}[2s])[5m:] offset 6m)[4m:3s]`, + expected: []item{ - {itemIdentifier, 0, `min_over_time`}, - {itemLeftParen, 13, `(`}, - {itemIdentifier, 14, `rate`}, - {itemLeftParen, 18, `(`}, - {itemIdentifier, 19, `foo`}, - {itemLeftBrace, 22, `{`}, - {itemIdentifier, 23, `bar`}, - {itemEQL, 26, `=`}, - {itemString, 27, `"baz"`}, - {itemRightBrace, 32, `}`}, - {itemLeftBracket, 33, `[`}, - {itemDuration, 34, `2s`}, - {itemRightBracket, 36, `]`}, - {itemRightParen, 37, `)`}, - {itemLeftBracket, 38, `[`}, - {itemDuration, 39, `5m`}, - {itemColon, 41, `:`}, - {itemRightBracket, 42, `]`}, - {itemOffset, 44, `offset`}, - {itemDuration, 51, `6m`}, - {itemRightParen, 53, `)`}, - {itemLeftBracket, 54, `[`}, - {itemDuration, 55, `4m`}, - {itemColon, 57, `:`}, - {itemDuration, 58, `3s`}, - {itemRightBracket, 60, `]`}, + {itemIdentifier, 0, `min_over_time`}, + {itemLeftParen, 13, `(`}, + {itemIdentifier, 14, `rate`}, + {itemLeftParen, 18, `(`}, + {itemIdentifier, 19, `foo`}, + {itemLeftBrace, 22, `{`}, + {itemIdentifier, 23, `bar`}, + {itemEQL, 26, `=`}, + {itemString, 27, `"baz"`}, + {itemRightBrace, 32, `}`}, + {itemLeftBracket, 33, `[`}, + {itemDuration, 34, `2s`}, + {itemRightBracket, 36, `]`}, + {itemRightParen, 37, `)`}, + {itemLeftBracket, 38, `[`}, + {itemDuration, 39, `5m`}, + {itemColon, 41, `:`}, + {itemRightBracket, 42, `]`}, + {itemOffset, 44, `offset`}, + {itemDuration, 51, `6m`}, + {itemRightParen, 53, `)`}, + {itemLeftBracket, 54, `[`}, + {itemDuration, 55, `4m`}, + {itemColon, 57, `:`}, + {itemDuration, 58, `3s`}, + {itemRightBracket, 60, `]`}, + }, + }, + { + input: `test:name{o:n!~"bar"}[4m:4s]`, + fail: true, + }, + { + input: `test:name{on!~"bar"}[4m:4s:4h]`, + fail: true, + }, + { + input: `test:name{on!~"bar"}[4m:4s:]`, + fail: true, + }, + { + input: `test:name{on!~"bar"}[4m::]`, + fail: true, + }, + { + input: `test:name{on!~"bar"}[:4s]`, + fail: true, + }, }, }, - { - input: `test:name{o:n!~"bar"}[4m:4s]`, - fail: true, - }, - { - input: `test:name{on!~"bar"}[4m:4s:4h]`, - fail: true, - }, - { - input: `test:name{on!~"bar"}[4m:4s:]`, - fail: true, - }, - { - input: `test:name{on!~"bar"}[4m::]`, - fail: true, - }, - { - input: `test:name{on!~"bar"}[:4s]`, - fail: true, - }, } // TestLexer tests basic functionality of the lexer. More elaborate tests are implemented // for the parser to avoid duplicated effort. func TestLexer(t *testing.T) { - for i, test := range tests { - l := &lexer{ - input: test.input, - items: make(chan item), - seriesDesc: test.seriesDesc, - } - go l.run() + for _, typ := range tests { + t.Run(typ.name, func(t *testing.T) { + for i, test := range typ.tests { + l := &lexer{ + input: test.input, + items: make(chan item), + seriesDesc: test.seriesDesc, + } + go l.run() - out := []item{} - for it := range l.items { - out = append(out, it) - } + out := []item{} + for it := range l.items { + out = append(out, it) + } - lastItem := out[len(out)-1] - if test.fail { - if lastItem.typ != itemError { - t.Logf("%d: input %q", i, test.input) - t.Fatalf("expected lexing error but did not fail") - } - continue - } - if lastItem.typ == itemError { - t.Logf("%d: input %q", i, test.input) - t.Fatalf("unexpected lexing error at position %d: %s", lastItem.pos, lastItem) - } + lastItem := out[len(out)-1] + if test.fail { + if lastItem.typ != itemError { + t.Logf("%d: input %q", i, test.input) + t.Fatalf("expected lexing error but did not fail") + } + continue + } + if lastItem.typ == itemError { + t.Logf("%d: input %q", i, test.input) + t.Fatalf("unexpected lexing error at position %d: %s", lastItem.pos, lastItem) + } - if !reflect.DeepEqual(lastItem, item{itemEOF, Pos(len(test.input)), ""}) { - t.Logf("%d: input %q", i, test.input) - t.Fatalf("lexing error: expected output to end with EOF item.\ngot:\n%s", expectedList(out)) - } - out = out[:len(out)-1] - if !reflect.DeepEqual(out, test.expected) { - t.Logf("%d: input %q", i, test.input) - t.Fatalf("lexing mismatch:\nexpected:\n%s\ngot:\n%s", expectedList(test.expected), expectedList(out)) - } + if !reflect.DeepEqual(lastItem, item{itemEOF, Pos(len(test.input)), ""}) { + t.Logf("%d: input %q", i, test.input) + t.Fatalf("lexing error: expected output to end with EOF item.\ngot:\n%s", expectedList(out)) + } + out = out[:len(out)-1] + if !reflect.DeepEqual(out, test.expected) { + t.Logf("%d: input %q", i, test.input) + t.Fatalf("lexing mismatch:\nexpected:\n%s\ngot:\n%s", expectedList(test.expected), expectedList(out)) + } + } + }) } }