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  }