bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/expr/parse/parse_test.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package parse
     6  
     7  import (
     8  	"flag"
     9  	"fmt"
    10  	"testing"
    11  
    12  	"bosun.org/models"
    13  )
    14  
    15  var debug = flag.Bool("debug", false, "show the errors produced by the main tests")
    16  
    17  type numberTest struct {
    18  	text    string
    19  	isInt   bool
    20  	isUint  bool
    21  	isFloat bool
    22  	int64
    23  	uint64
    24  	float64
    25  }
    26  
    27  var numberTests = []numberTest{
    28  	// basics
    29  	{"0", true, true, true, 0, 0, 0},
    30  	{"73", true, true, true, 73, 73, 73},
    31  	{"073", true, true, true, 073, 073, 073},
    32  	{"0x73", true, true, true, 0x73, 0x73, 0x73},
    33  	{"100", true, true, true, 100, 100, 100},
    34  	{"1e9", true, true, true, 1e9, 1e9, 1e9},
    35  	{"1e19", false, true, true, 0, 1e19, 1e19},
    36  	// funny bases
    37  	{"0123", true, true, true, 0123, 0123, 0123},
    38  	{"0xdeadbeef", true, true, true, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef},
    39  	// some broken syntax
    40  	{text: "+-2"},
    41  	{text: "0x123."},
    42  	{text: "1e."},
    43  	{text: "'x"},
    44  	{text: "'xx'"},
    45  }
    46  
    47  func TestNumberParse(t *testing.T) {
    48  	for _, test := range numberTests {
    49  		n, err := newNumber(0, test.text)
    50  		ok := test.isInt || test.isUint || test.isFloat
    51  		if ok && err != nil {
    52  			t.Errorf("unexpected error for %q: %s", test.text, err)
    53  			continue
    54  		}
    55  		if !ok && err == nil {
    56  			t.Errorf("expected error for %q", test.text)
    57  			continue
    58  		}
    59  		if !ok {
    60  			if *debug {
    61  				fmt.Printf("%s\n\t%s\n", test.text, err)
    62  			}
    63  			continue
    64  		}
    65  		if test.isUint {
    66  			if !n.IsUint {
    67  				t.Errorf("expected unsigned integer for %q", test.text)
    68  			}
    69  			if n.Uint64 != test.uint64 {
    70  				t.Errorf("uint64 for %q should be %d Is %d", test.text, test.uint64, n.Uint64)
    71  			}
    72  		} else if n.IsUint {
    73  			t.Errorf("did not expect unsigned integer for %q", test.text)
    74  		}
    75  		if test.isFloat {
    76  			if !n.IsFloat {
    77  				t.Errorf("expected float for %q", test.text)
    78  			}
    79  			if n.Float64 != test.float64 {
    80  				t.Errorf("float64 for %q should be %g Is %g", test.text, test.float64, n.Float64)
    81  			}
    82  		} else if n.IsFloat {
    83  			t.Errorf("did not expect float for %q", test.text)
    84  		}
    85  	}
    86  }
    87  
    88  type parseTest struct {
    89  	name   string
    90  	input  string
    91  	ok     bool
    92  	result string // what the user would see in an error message.
    93  }
    94  
    95  const (
    96  	noError  = true
    97  	hasError = false
    98  )
    99  
   100  var parseTests = []parseTest{
   101  	{"number", "1", noError, "1"},
   102  	{"function", `avg(q("test", "1m"))`, noError, `avg(q("test", "1m"))`},
   103  	{"addition", "1+2", noError, "1 + 2"},
   104  	{"expression", "1+2*3/4-5 && !2|| -4", noError, "1 + 2 * 3 / 4 - 5 && !2 || -4"},
   105  	{"expression with func", `avg(q("q", "1m"))>=0.7&&avg(q("q", "1m"))!=3-0x8`, noError,
   106  		`avg(q("q", "1m")) >= 0.7 && avg(q("q", "1m")) != 3 - 0x8`},
   107  	{"func types", `avg(q("q", "1m"))>avg(q("q", "1m"))+avg(q("q", "1m"))`, noError, `avg(q("q", "1m")) > avg(q("q", "1m")) + avg(q("q", "1m"))`},
   108  	{"series compare", `q("q", "1m")>0`, noError, `q("q", "1m") > 0`},
   109  	{"unary series", `!q("q", "1m")`, noError, `!q("q", "1m")`},
   110  	{"expr in func", `forecastlr(q("q", "1m"), -1)`, noError, `forecastlr(q("q", "1m"), -1)`},
   111  	{"nested func expr", `avg(q("q","1m")>0)`, noError, `avg(q("q", "1m") > 0)`},
   112  	// Errors.
   113  	{"empty", "", hasError, ""},
   114  	{"unclosed function", "avg(", hasError, ""},
   115  	{"bad function", "bad(1)", hasError, ""},
   116  	{"bad type", `band("q", "1h", "1m", "8")`, hasError, ""},
   117  	{"wrong number args", `avg(q("q", "1m"), "1m", 1)`, hasError, ""},
   118  	{"2 series math", `band(q("q", "1m"))+band(q("q", "1m"))`, hasError, ""},
   119  }
   120  
   121  func TestParse(t *testing.T) {
   122  	textFormat = "%q"
   123  	defer func() { textFormat = "%s" }()
   124  	for _, test := range parseTests {
   125  		tmpl := New(nil)
   126  		err := tmpl.Parse(test.input, builtins)
   127  		switch {
   128  		case err == nil && !test.ok:
   129  			t.Errorf("%q: expected error; got none", test.name)
   130  			continue
   131  		case err != nil && test.ok:
   132  			t.Errorf("%q: unexpected error: %v", test.name, err)
   133  			continue
   134  		case err != nil && !test.ok:
   135  			// expected error, got one
   136  			if *debug {
   137  				fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
   138  			}
   139  			continue
   140  		}
   141  		var result string
   142  		result = tmpl.Root.String()
   143  		if result != test.result {
   144  			t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result)
   145  		}
   146  	}
   147  }
   148  
   149  func tagNil(args []Node) (Tags, error) {
   150  	return nil, nil
   151  }
   152  
   153  var builtins = map[string]Func{
   154  	"avg": {
   155  		[]models.FuncType{models.TypeSeriesSet},
   156  		models.TypeNumberSet,
   157  		tagNil,
   158  		nil,
   159  		false,
   160  		0,
   161  		false,
   162  		false,
   163  		false,
   164  		false,
   165  		false,
   166  		nil,
   167  	},
   168  	"band": {
   169  		[]models.FuncType{models.TypeString, models.TypeString, models.TypeString, models.TypeScalar},
   170  		models.TypeSeriesSet,
   171  		tagNil,
   172  		nil,
   173  		false,
   174  		0,
   175  		false,
   176  		false,
   177  		false,
   178  		false,
   179  		false,
   180  		nil,
   181  	},
   182  	"q": {
   183  		[]models.FuncType{models.TypeString, models.TypeString},
   184  		models.TypeSeriesSet,
   185  		tagNil,
   186  		nil,
   187  		false,
   188  		0,
   189  		false,
   190  		false,
   191  		false,
   192  		false,
   193  		false,
   194  		nil,
   195  	},
   196  	"forecastlr": {
   197  		[]models.FuncType{models.TypeSeriesSet, models.TypeScalar},
   198  		models.TypeNumberSet,
   199  		tagNil,
   200  		nil,
   201  		false,
   202  		0,
   203  		false,
   204  		false,
   205  		false,
   206  		false,
   207  		false,
   208  		nil,
   209  	},
   210  }