github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/configs/legacy_promql/lex_test.go (about) 1 // Copyright 2015 The Prometheus Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package promql 15 16 import ( 17 "fmt" 18 "reflect" 19 "testing" 20 ) 21 22 var tests = []struct { 23 input string 24 expected []item 25 fail bool 26 seriesDesc bool // Whether to lex a series description. 27 }{ 28 // Test common stuff. 29 { 30 input: ",", 31 expected: []item{{itemComma, 0, ","}}, 32 }, { 33 input: "()", 34 expected: []item{{itemLeftParen, 0, `(`}, {itemRightParen, 1, `)`}}, 35 }, { 36 input: "{}", 37 expected: []item{{itemLeftBrace, 0, `{`}, {itemRightBrace, 1, `}`}}, 38 }, { 39 input: "[5m]", 40 expected: []item{ 41 {itemLeftBracket, 0, `[`}, 42 {itemDuration, 1, `5m`}, 43 {itemRightBracket, 3, `]`}, 44 }, 45 }, { 46 input: "\r\n\r", 47 expected: []item{}, 48 }, 49 // Test numbers. 50 { 51 input: "1", 52 expected: []item{{itemNumber, 0, "1"}}, 53 }, { 54 input: "4.23", 55 expected: []item{{itemNumber, 0, "4.23"}}, 56 }, { 57 input: ".3", 58 expected: []item{{itemNumber, 0, ".3"}}, 59 }, { 60 input: "5.", 61 expected: []item{{itemNumber, 0, "5."}}, 62 }, { 63 input: "NaN", 64 expected: []item{{itemNumber, 0, "NaN"}}, 65 }, { 66 input: "nAN", 67 expected: []item{{itemNumber, 0, "nAN"}}, 68 }, { 69 input: "NaN 123", 70 expected: []item{{itemNumber, 0, "NaN"}, {itemNumber, 4, "123"}}, 71 }, { 72 input: "NaN123", 73 expected: []item{{itemIdentifier, 0, "NaN123"}}, 74 }, { 75 input: "iNf", 76 expected: []item{{itemNumber, 0, "iNf"}}, 77 }, { 78 input: "Inf", 79 expected: []item{{itemNumber, 0, "Inf"}}, 80 }, { 81 input: "+Inf", 82 expected: []item{{itemADD, 0, "+"}, {itemNumber, 1, "Inf"}}, 83 }, { 84 input: "+Inf 123", 85 expected: []item{{itemADD, 0, "+"}, {itemNumber, 1, "Inf"}, {itemNumber, 5, "123"}}, 86 }, { 87 input: "-Inf", 88 expected: []item{{itemSUB, 0, "-"}, {itemNumber, 1, "Inf"}}, 89 }, { 90 input: "Infoo", 91 expected: []item{{itemIdentifier, 0, "Infoo"}}, 92 }, { 93 input: "-Infoo", 94 expected: []item{{itemSUB, 0, "-"}, {itemIdentifier, 1, "Infoo"}}, 95 }, { 96 input: "-Inf 123", 97 expected: []item{{itemSUB, 0, "-"}, {itemNumber, 1, "Inf"}, {itemNumber, 5, "123"}}, 98 }, { 99 input: "0x123", 100 expected: []item{{itemNumber, 0, "0x123"}}, 101 }, 102 // Test strings. 103 { 104 input: "\"test\\tsequence\"", 105 expected: []item{{itemString, 0, `"test\tsequence"`}}, 106 }, 107 { 108 input: "\"test\\\\.expression\"", 109 expected: []item{{itemString, 0, `"test\\.expression"`}}, 110 }, 111 { 112 input: "\"test\\.expression\"", 113 expected: []item{ 114 {itemError, 0, "unknown escape sequence U+002E '.'"}, 115 {itemString, 0, `"test\.expression"`}, 116 }, 117 }, 118 { 119 input: "`test\\.expression`", 120 expected: []item{{itemString, 0, "`test\\.expression`"}}, 121 }, 122 { 123 // See https://github.com/prometheus/prometheus/issues/939. 124 input: ".٩", 125 fail: true, 126 }, 127 // Test duration. 128 { 129 input: "5s", 130 expected: []item{{itemDuration, 0, "5s"}}, 131 }, { 132 input: "123m", 133 expected: []item{{itemDuration, 0, "123m"}}, 134 }, { 135 input: "1h", 136 expected: []item{{itemDuration, 0, "1h"}}, 137 }, { 138 input: "3w", 139 expected: []item{{itemDuration, 0, "3w"}}, 140 }, { 141 input: "1y", 142 expected: []item{{itemDuration, 0, "1y"}}, 143 }, 144 // Test identifiers. 145 { 146 input: "abc", 147 expected: []item{{itemIdentifier, 0, "abc"}}, 148 }, { 149 input: "a:bc", 150 expected: []item{{itemMetricIdentifier, 0, "a:bc"}}, 151 }, { 152 input: "abc d", 153 expected: []item{{itemIdentifier, 0, "abc"}, {itemIdentifier, 4, "d"}}, 154 }, { 155 input: ":bc", 156 expected: []item{{itemMetricIdentifier, 0, ":bc"}}, 157 }, { 158 input: "0a:bc", 159 fail: true, 160 }, 161 // Test comments. 162 { 163 input: "# some comment", 164 expected: []item{{itemComment, 0, "# some comment"}}, 165 }, { 166 input: "5 # 1+1\n5", 167 expected: []item{ 168 {itemNumber, 0, "5"}, 169 {itemComment, 2, "# 1+1"}, 170 {itemNumber, 8, "5"}, 171 }, 172 }, 173 // Test operators. 174 { 175 input: `=`, 176 expected: []item{{itemAssign, 0, `=`}}, 177 }, { 178 // Inside braces equality is a single '=' character. 179 input: `{=}`, 180 expected: []item{{itemLeftBrace, 0, `{`}, {itemEQL, 1, `=`}, {itemRightBrace, 2, `}`}}, 181 }, { 182 input: `==`, 183 expected: []item{{itemEQL, 0, `==`}}, 184 }, { 185 input: `!=`, 186 expected: []item{{itemNEQ, 0, `!=`}}, 187 }, { 188 input: `<`, 189 expected: []item{{itemLSS, 0, `<`}}, 190 }, { 191 input: `>`, 192 expected: []item{{itemGTR, 0, `>`}}, 193 }, { 194 input: `>=`, 195 expected: []item{{itemGTE, 0, `>=`}}, 196 }, { 197 input: `<=`, 198 expected: []item{{itemLTE, 0, `<=`}}, 199 }, { 200 input: `+`, 201 expected: []item{{itemADD, 0, `+`}}, 202 }, { 203 input: `-`, 204 expected: []item{{itemSUB, 0, `-`}}, 205 }, { 206 input: `*`, 207 expected: []item{{itemMUL, 0, `*`}}, 208 }, { 209 input: `/`, 210 expected: []item{{itemDIV, 0, `/`}}, 211 }, { 212 input: `^`, 213 expected: []item{{itemPOW, 0, `^`}}, 214 }, { 215 input: `%`, 216 expected: []item{{itemMOD, 0, `%`}}, 217 }, { 218 input: `AND`, 219 expected: []item{{itemLAND, 0, `AND`}}, 220 }, { 221 input: `or`, 222 expected: []item{{itemLOR, 0, `or`}}, 223 }, { 224 input: `unless`, 225 expected: []item{{itemLUnless, 0, `unless`}}, 226 }, 227 // Test aggregators. 228 { 229 input: `sum`, 230 expected: []item{{itemSum, 0, `sum`}}, 231 }, { 232 input: `AVG`, 233 expected: []item{{itemAvg, 0, `AVG`}}, 234 }, { 235 input: `MAX`, 236 expected: []item{{itemMax, 0, `MAX`}}, 237 }, { 238 input: `min`, 239 expected: []item{{itemMin, 0, `min`}}, 240 }, { 241 input: `count`, 242 expected: []item{{itemCount, 0, `count`}}, 243 }, { 244 input: `stdvar`, 245 expected: []item{{itemStdvar, 0, `stdvar`}}, 246 }, { 247 input: `stddev`, 248 expected: []item{{itemStddev, 0, `stddev`}}, 249 }, 250 // Test keywords. 251 { 252 input: "alert", 253 expected: []item{{itemAlert, 0, "alert"}}, 254 }, { 255 input: "if", 256 expected: []item{{itemIf, 0, "if"}}, 257 }, { 258 input: "for", 259 expected: []item{{itemFor, 0, "for"}}, 260 }, { 261 input: "labels", 262 expected: []item{{itemLabels, 0, "labels"}}, 263 }, { 264 input: "annotations", 265 expected: []item{{itemAnnotations, 0, "annotations"}}, 266 }, { 267 input: "offset", 268 expected: []item{{itemOffset, 0, "offset"}}, 269 }, { 270 input: "by", 271 expected: []item{{itemBy, 0, "by"}}, 272 }, { 273 input: "without", 274 expected: []item{{itemWithout, 0, "without"}}, 275 }, { 276 input: "on", 277 expected: []item{{itemOn, 0, "on"}}, 278 }, { 279 input: "ignoring", 280 expected: []item{{itemIgnoring, 0, "ignoring"}}, 281 }, { 282 input: "group_left", 283 expected: []item{{itemGroupLeft, 0, "group_left"}}, 284 }, { 285 input: "group_right", 286 expected: []item{{itemGroupRight, 0, "group_right"}}, 287 }, { 288 input: "bool", 289 expected: []item{{itemBool, 0, "bool"}}, 290 }, 291 // Test Selector. 292 { 293 input: `台北`, 294 fail: true, 295 }, { 296 input: `{台北='a'}`, 297 fail: true, 298 }, { 299 input: `{0a='a'}`, 300 fail: true, 301 }, { 302 input: `{foo='bar'}`, 303 expected: []item{ 304 {itemLeftBrace, 0, `{`}, 305 {itemIdentifier, 1, `foo`}, 306 {itemEQL, 4, `=`}, 307 {itemString, 5, `'bar'`}, 308 {itemRightBrace, 10, `}`}, 309 }, 310 }, { 311 input: `{foo="bar"}`, 312 expected: []item{ 313 {itemLeftBrace, 0, `{`}, 314 {itemIdentifier, 1, `foo`}, 315 {itemEQL, 4, `=`}, 316 {itemString, 5, `"bar"`}, 317 {itemRightBrace, 10, `}`}, 318 }, 319 }, { 320 input: `{foo="bar\"bar"}`, 321 expected: []item{ 322 {itemLeftBrace, 0, `{`}, 323 {itemIdentifier, 1, `foo`}, 324 {itemEQL, 4, `=`}, 325 {itemString, 5, `"bar\"bar"`}, 326 {itemRightBrace, 15, `}`}, 327 }, 328 }, { 329 input: `{NaN != "bar" }`, 330 expected: []item{ 331 {itemLeftBrace, 0, `{`}, 332 {itemIdentifier, 1, `NaN`}, 333 {itemNEQ, 5, `!=`}, 334 {itemString, 8, `"bar"`}, 335 {itemRightBrace, 14, `}`}, 336 }, 337 }, { 338 input: `{alert=~"bar" }`, 339 expected: []item{ 340 {itemLeftBrace, 0, `{`}, 341 {itemIdentifier, 1, `alert`}, 342 {itemEQLRegex, 6, `=~`}, 343 {itemString, 8, `"bar"`}, 344 {itemRightBrace, 14, `}`}, 345 }, 346 }, { 347 input: `{on!~"bar"}`, 348 expected: []item{ 349 {itemLeftBrace, 0, `{`}, 350 {itemIdentifier, 1, `on`}, 351 {itemNEQRegex, 3, `!~`}, 352 {itemString, 5, `"bar"`}, 353 {itemRightBrace, 10, `}`}, 354 }, 355 }, { 356 input: `{alert!#"bar"}`, fail: true, 357 }, { 358 input: `{foo:a="bar"}`, fail: true, 359 }, 360 // Test common errors. 361 { 362 input: `=~`, fail: true, 363 }, { 364 input: `!~`, fail: true, 365 }, { 366 input: `!(`, fail: true, 367 }, { 368 input: "1a", fail: true, 369 }, 370 // Test mismatched parens. 371 { 372 input: `(`, fail: true, 373 }, { 374 input: `())`, fail: true, 375 }, { 376 input: `(()`, fail: true, 377 }, { 378 input: `{`, fail: true, 379 }, { 380 input: `}`, fail: true, 381 }, { 382 input: "{{", fail: true, 383 }, { 384 input: "{{}}", fail: true, 385 }, { 386 input: `[`, fail: true, 387 }, { 388 input: `[[`, fail: true, 389 }, { 390 input: `[]]`, fail: true, 391 }, { 392 input: `[[]]`, fail: true, 393 }, { 394 input: `]`, fail: true, 395 }, 396 // Test encoding issues. 397 { 398 input: "\"\xff\"", fail: true, 399 }, 400 { 401 input: "`\xff`", fail: true, 402 }, 403 // Test series description. 404 { 405 input: `{} _ 1 x .3`, 406 expected: []item{ 407 {itemLeftBrace, 0, `{`}, 408 {itemRightBrace, 1, `}`}, 409 {itemBlank, 3, `_`}, 410 {itemNumber, 5, `1`}, 411 {itemTimes, 7, `x`}, 412 {itemNumber, 9, `.3`}, 413 }, 414 seriesDesc: true, 415 }, 416 { 417 input: `metric +Inf Inf NaN`, 418 expected: []item{ 419 {itemIdentifier, 0, `metric`}, 420 {itemADD, 7, `+`}, 421 {itemNumber, 8, `Inf`}, 422 {itemNumber, 12, `Inf`}, 423 {itemNumber, 16, `NaN`}, 424 }, 425 seriesDesc: true, 426 }, 427 { 428 input: `metric 1+1x4`, 429 expected: []item{ 430 {itemIdentifier, 0, `metric`}, 431 {itemNumber, 7, `1`}, 432 {itemADD, 8, `+`}, 433 {itemNumber, 9, `1`}, 434 {itemTimes, 10, `x`}, 435 {itemNumber, 11, `4`}, 436 }, 437 seriesDesc: true, 438 }, 439 } 440 441 // TestLexer tests basic functionality of the lexer. More elaborate tests are implemented 442 // for the parser to avoid duplicated effort. 443 func TestLexer(t *testing.T) { 444 for i, test := range tests { 445 l := &lexer{ 446 input: test.input, 447 items: make(chan item), 448 seriesDesc: test.seriesDesc, 449 } 450 go l.run() 451 452 out := []item{} 453 for it := range l.items { 454 out = append(out, it) 455 } 456 457 lastItem := out[len(out)-1] 458 if test.fail { 459 if lastItem.typ != itemError { 460 t.Logf("%d: input %q", i, test.input) 461 t.Fatalf("expected lexing error but did not fail") 462 } 463 continue 464 } 465 if lastItem.typ == itemError { 466 t.Logf("%d: input %q", i, test.input) 467 t.Fatalf("unexpected lexing error at position %d: %s", lastItem.pos, lastItem) 468 } 469 470 if !reflect.DeepEqual(lastItem, item{itemEOF, Pos(len(test.input)), ""}) { 471 t.Logf("%d: input %q", i, test.input) 472 t.Fatalf("lexing error: expected output to end with EOF item.\ngot:\n%s", expectedList(out)) 473 } 474 out = out[:len(out)-1] 475 if !reflect.DeepEqual(out, test.expected) { 476 t.Logf("%d: input %q", i, test.input) 477 t.Fatalf("lexing mismatch:\nexpected:\n%s\ngot:\n%s", expectedList(test.expected), expectedList(out)) 478 } 479 } 480 } 481 482 func expectedList(exp []item) string { 483 s := "" 484 for _, it := range exp { 485 s += fmt.Sprintf("\t%#v\n", it) 486 } 487 return s 488 }