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 }