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  }