github.com/bytedance/go-tagexpr@v2.7.5-0.20210114074101-de5b8743ad85+incompatible/expr_test.go (about) 1 // Copyright 2019 Bytedance Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tagexpr 16 17 import ( 18 "math" 19 "reflect" 20 "testing" 21 ) 22 23 func TestExpr(t *testing.T) { 24 var cases = []struct { 25 expr string 26 val interface{} 27 }{ 28 // Simple string 29 {expr: "'a'", val: "a"}, 30 {expr: "('a')", val: "a"}, 31 // Simple digital 32 {expr: " 10 ", val: 10.0}, 33 {expr: "(10)", val: 10.0}, 34 // Simple bool 35 {expr: "true", val: true}, 36 {expr: "!true", val: false}, 37 {expr: "!!true", val: true}, 38 {expr: "false", val: false}, 39 {expr: "!false", val: true}, 40 {expr: "!!false", val: false}, 41 {expr: "(false)", val: false}, 42 {expr: "(!false)", val: true}, 43 {expr: "(!!false)", val: false}, 44 {expr: "!!(!false)", val: true}, 45 {expr: "!(!false)", val: false}, 46 // Join string 47 {expr: "'true '+('a')", val: "true a"}, 48 {expr: "'a'+('b'+'c')+'d'", val: "abcd"}, 49 // Arithmetic operator 50 {expr: "1+7+2", val: 10.0}, 51 {expr: "1+(7)+(2)", val: 10.0}, 52 {expr: "1.1+ 2", val: 3.1}, 53 {expr: "-1.1+4", val: 2.9}, 54 {expr: "10-7-2", val: 1.0}, 55 {expr: "20/2", val: 10.0}, 56 {expr: "1/0", val: math.NaN()}, 57 {expr: "20%2", val: 0.0}, 58 {expr: "6 % 5", val: 1.0}, 59 {expr: "20%7 %5", val: 1.0}, 60 {expr: "1*2+7+2.2", val: 11.2}, 61 {expr: "-20/2+1+2", val: -7.0}, 62 {expr: "20/2+1-2-1", val: 8.0}, 63 {expr: "30/(2+1)/5-2-1", val: -1.0}, 64 {expr: "100/(( 2+8)*5 )-(1 +1- 0)", val: 0.0}, 65 {expr: "(2*3)+(4*2)", val: 14.0}, 66 {expr: "1+(2*(3+4))", val: 15.0}, 67 {expr: "20%(7%5)", val: 0.0}, 68 // Relational operator 69 {expr: "50 == 5", val: false}, 70 {expr: "'50'==50", val: false}, 71 {expr: "'50'=='50'", val: true}, 72 {expr: "'50' =='5' == true", val: false}, 73 {expr: "50== 50 == false", val: false}, 74 {expr: "50== 50 == true ==true==true", val: true}, 75 {expr: "50 != 5", val: true}, 76 {expr: "'50'!=50", val: true}, 77 {expr: "'50'!= '50'", val: false}, 78 {expr: "'50' !='5' != true", val: false}, 79 {expr: "50!= 50 == false", val: true}, 80 {expr: "50== 50 != true ==true!=true", val: true}, 81 {expr: "50 > 5", val: true}, 82 {expr: "50.1 > 50.1", val: false}, 83 {expr: "3.2 > 2.1", val: true}, 84 {expr: "'3.2' > '2.1'", val: true}, 85 {expr: "'13.2'>'2.1'", val: false}, 86 {expr: "3.2 >= 2.1", val: true}, 87 {expr: "2.1 >= 2.1", val: true}, 88 {expr: "2.05 >= 2.1", val: false}, 89 {expr: "'2.05'>='2.1'", val: false}, 90 {expr: "'12.05'>='2.1'", val: false}, 91 {expr: "50 < 5", val: false}, 92 {expr: "50.1 < 50.1", val: false}, 93 {expr: "3 <12.11", val: true}, 94 {expr: "3.2 < 2.1", val: false}, 95 {expr: "'3.2' < '2.1'", val: false}, 96 {expr: "'13.2' < '2.1'", val: true}, 97 {expr: "3.2 <= 2.1", val: false}, 98 {expr: "2.1 <= 2.1", val: true}, 99 {expr: "2.05 <= 2.1", val: true}, 100 {expr: "'2.05'<='2.1'", val: true}, 101 {expr: "'12.05'<='2.1'", val: true}, 102 // Logical operator 103 {expr: "!('13.2' < '2.1')", val: false}, 104 {expr: "(3.2 <= 2.1) &&true", val: false}, 105 {expr: "true&&(2.1<=2.1)", val: true}, 106 {expr: "(2.05<=2.1)&&false", val: false}, 107 {expr: "true&&!true&&false", val: false}, 108 {expr: "true&&true&&true", val: true}, 109 {expr: "true&&true&&false", val: false}, 110 {expr: "false&&true&&true", val: false}, 111 {expr: "true && false && true", val: false}, 112 {expr: "true||false", val: true}, 113 {expr: "false ||true", val: true}, 114 {expr: "true&&true || false", val: true}, 115 {expr: "true&&false || false", val: false}, 116 {expr: "true && false || true ", val: true}, 117 } 118 for _, c := range cases { 119 t.Log(c.expr) 120 vm, err := parseExpr(c.expr) 121 if err != nil { 122 t.Fatal(err) 123 } 124 val := vm.run("", nil) 125 if !reflect.DeepEqual(val, c.val) { 126 if f, ok := c.val.(float64); ok && math.IsNaN(f) && math.IsNaN(val.(float64)) { 127 continue 128 } 129 t.Fatalf("expr: %q, got: %v, expect: %v", c.expr, val, c.val) 130 } 131 } 132 } 133 134 func TestPriority(t *testing.T) { 135 var cases = []struct { 136 expr string 137 val interface{} 138 }{ 139 {expr: "false||true&&8==8", val: true}, 140 {expr: "1+2>5-4", val: true}, 141 {expr: "1+2*4/2", val: 5.0}, 142 {expr: "(true||false)&&false||false", val: false}, 143 {expr: "true||false&&false||false", val: true}, 144 {expr: "true||1<0&&'a'!='a'||0!=0", val: true}, 145 } 146 for _, c := range cases { 147 t.Log(c.expr) 148 vm, err := parseExpr(c.expr) 149 if err != nil { 150 t.Fatal(err) 151 } 152 val := vm.run("", nil) 153 if !reflect.DeepEqual(val, c.val) { 154 if f, ok := c.val.(float64); ok && math.IsNaN(f) && math.IsNaN(val.(float64)) { 155 continue 156 } 157 t.Fatalf("expr: %q, got: %v, expect: %v", c.expr, val, c.val) 158 } 159 } 160 } 161 162 func TestBuiltInFunc(t *testing.T) { 163 var cases = []struct { 164 expr string 165 val interface{} 166 }{ 167 {expr: "len('abc')", val: 3.0}, 168 {expr: "len('abc')+2*2/len('cd')", val: 5.0}, 169 {expr: "len(0)", val: 0}, 170 171 {expr: "regexp('a\\d','a0')", val: true}, 172 {expr: "regexp('^a\\d$','a0')", val: true}, 173 {expr: "regexp('a\\d','a')", val: false}, 174 {expr: "regexp('^a\\d$','a')", val: false}, 175 176 {expr: "sprintf('test string: %s','a')", val: "test string: a"}, 177 {expr: "sprintf('test string: %s','a'+'b')", val: "test string: ab"}, 178 {expr: "sprintf('test string: %s,%v','a',1)", val: "test string: a,1"}, 179 {expr: "sprintf('')+'a'", val: "a"}, 180 {expr: "sprintf('%v',10+2*2)", val: "14"}, 181 } 182 for _, c := range cases { 183 t.Log(c.expr) 184 vm, err := parseExpr(c.expr) 185 if err != nil { 186 t.Fatal(err) 187 } 188 val := vm.run("", nil) 189 if !reflect.DeepEqual(val, c.val) { 190 if f, ok := c.val.(float64); ok && math.IsNaN(f) && math.IsNaN(val.(float64)) { 191 continue 192 } 193 t.Fatalf("expr: %q, got: %v, expect: %v", c.expr, val, c.val) 194 } 195 } 196 } 197 198 func TestSyntaxIncorrect(t *testing.T) { 199 var cases = []struct { 200 incorrectExpr string 201 }{ 202 {incorrectExpr: "1 + + 'a'"}, 203 {incorrectExpr: "len"}, 204 {incorrectExpr: "regexp"}, 205 {incorrectExpr: "regexp()"}, 206 {incorrectExpr: "regexp('^'+'a','a')"}, 207 {incorrectExpr: "regexp('^a','a','b')"}, 208 {incorrectExpr: "sprintf()"}, 209 {incorrectExpr: "sprintf(0)"}, 210 {incorrectExpr: "sprintf('a'+'b')"}, 211 } 212 for _, c := range cases { 213 _, err := parseExpr(c.incorrectExpr) 214 if err == nil { 215 t.Fatalf("expect syntax incorrect: %s", c.incorrectExpr) 216 } else { 217 t.Log(err) 218 } 219 } 220 }