github.com/bytedance/go-tagexpr@v2.7.5-0.20210114074101-de5b8743ad85+incompatible/tagexpr_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  	"reflect"
    19  	"strconv"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  )
    25  
    26  func BenchmarkTagExpr(b *testing.B) {
    27  	type T struct {
    28  		a int `bench:"$%3"`
    29  	}
    30  	vm := New("bench")
    31  	vm.MustRun(new(T)) // warm up
    32  	b.ReportAllocs()
    33  	b.ResetTimer()
    34  	var t = &T{10}
    35  	for i := 0; i < b.N; i++ {
    36  		tagExpr, err := vm.Run(t)
    37  		if err != nil {
    38  			b.FailNow()
    39  		}
    40  		if tagExpr.EvalFloat("a") != 1 {
    41  			b.FailNow()
    42  		}
    43  	}
    44  }
    45  
    46  func BenchmarkReflect(b *testing.B) {
    47  	type T struct {
    48  		a int `remainder:"3"`
    49  	}
    50  	b.ReportAllocs()
    51  	b.ResetTimer()
    52  	var t = &T{1}
    53  	for i := 0; i < b.N; i++ {
    54  		v := reflect.ValueOf(t).Elem()
    55  		ft, ok := v.Type().FieldByName("a")
    56  		if !ok {
    57  			b.FailNow()
    58  		}
    59  		x, err := strconv.ParseInt(ft.Tag.Get("remainder"), 10, 64)
    60  		if err != nil {
    61  			b.FailNow()
    62  		}
    63  		fv := v.FieldByName("a")
    64  		if fv.Int()%x != 1 {
    65  			b.FailNow()
    66  		}
    67  	}
    68  }
    69  
    70  func Test(t *testing.T) {
    71  	g := &struct {
    72  		_ int
    73  		h string `tagexpr:"$"`
    74  		s []string
    75  		m map[string][]string
    76  	}{
    77  		h: "haha",
    78  		s: []string{"1"},
    79  		m: map[string][]string{"0": {"2"}},
    80  	}
    81  	d := "ddd"
    82  	e := new(int)
    83  	*e = 3
    84  	type iface interface{}
    85  	var cases = []struct {
    86  		tagName   string
    87  		structure interface{}
    88  		tests     map[string]interface{}
    89  	}{
    90  		{
    91  			tagName: "tagexpr",
    92  			structure: &struct {
    93  				A     int              `tagexpr:"$>0&&$<10&&!''&&!!!0&&!nil&&$"`
    94  				A2    int              `tagexpr:"@:$>0&&$<10"`
    95  				b     string           `tagexpr:"is:$=='test';msg:sprintf('expect: test, but got: %s',$)"`
    96  				c     float32          `tagexpr:"(A)$+$"`
    97  				d     *string          `tagexpr:"$"`
    98  				e     **int            `tagexpr:"$"`
    99  				f     *[3]int          `tagexpr:"x:len($)"`
   100  				g     string           `tagexpr:"x:!regexp('xxx',$);y:regexp('g\\d{3}$')"`
   101  				h     []string         `tagexpr:"x:$[1];y:$[10]"`
   102  				i     map[string]int   `tagexpr:"x:$['a'];y:$[0];z:$==nil"`
   103  				i2    *map[string]int  `tagexpr:"x:$['a'];y:$[0];z:$"`
   104  				j, j2 iface            `tagexpr:"@:$==1;y:$"`
   105  				k     *iface           `tagexpr:"$==nil"`
   106  				m     *struct{ i int } `tagexpr:"@:$;x:$['a']['x']"`
   107  			}{
   108  				A:  5.0,
   109  				A2: 5.0,
   110  				b:  "x",
   111  				c:  1,
   112  				d:  &d,
   113  				e:  &e,
   114  				f:  new([3]int),
   115  				g:  "g123",
   116  				h:  []string{"", "hehe"},
   117  				i:  map[string]int{"a": 7},
   118  				j2: iface(1),
   119  				m:  &struct{ i int }{1},
   120  			},
   121  			tests: map[string]interface{}{
   122  				"A":     true,
   123  				"A2":    true,
   124  				"b@is":  false,
   125  				"b@msg": "expect: test, but got: x",
   126  				"c":     6.0,
   127  				"d":     d,
   128  				"e":     float64(*e),
   129  				"f@x":   float64(3),
   130  				"g@x":   true,
   131  				"g@y":   true,
   132  				"h@x":   "hehe",
   133  				"h@y":   nil,
   134  				"i@x":   7.0,
   135  				"i@y":   nil,
   136  				"i@z":   false,
   137  				"i2@x":  nil,
   138  				"i2@y":  nil,
   139  				"i2@z":  nil,
   140  				"j":     false,
   141  				"j@y":   nil,
   142  				"j2":    true,
   143  				"j2@y":  1.0,
   144  				"k":     true,
   145  				"m":     &struct{ i int }{1},
   146  				"m@x":   nil,
   147  			},
   148  		},
   149  		{
   150  			tagName: "tagexpr",
   151  			structure: &struct {
   152  				A int    `tagexpr:"$>0&&$<10"`
   153  				b string `tagexpr:"is:$=='test';msg:sprintf('expect: test, but got: %s',$)"`
   154  				c struct {
   155  					_ int
   156  					d bool `tagexpr:"$"`
   157  				}
   158  				e *struct {
   159  					_ int
   160  					f bool `tagexpr:"$"`
   161  				}
   162  				g **struct {
   163  					_ int
   164  					h string `tagexpr:"$"`
   165  					s []string
   166  					m map[string][]string
   167  				} `tagexpr:"$['h']"`
   168  				i string  `tagexpr:"(g.s)$[0]+(g.m)$['0'][0]==$"`
   169  				j bool    `tagexpr:"!$"`
   170  				k int     `tagexpr:"!$"`
   171  				m *int    `tagexpr:"$==nil"`
   172  				n *bool   `tagexpr:"$==nil"`
   173  				p *string `tagexpr:"$"`
   174  			}{
   175  				A: 5,
   176  				b: "x",
   177  				c: struct {
   178  					_ int
   179  					d bool `tagexpr:"$"`
   180  				}{d: true},
   181  				e: &struct {
   182  					_ int
   183  					f bool `tagexpr:"$"`
   184  				}{f: true},
   185  				g: &g,
   186  				i: "12",
   187  			},
   188  			tests: map[string]interface{}{
   189  				"A":     true,
   190  				"b@is":  false,
   191  				"b@msg": "expect: test, but got: x",
   192  				"c.d":   true,
   193  				"e.f":   true,
   194  				"g":     "haha",
   195  				"g.h":   "haha",
   196  				"i":     true,
   197  				"j":     true,
   198  				"k":     true,
   199  				"m":     true,
   200  				"n":     true,
   201  				"p":     nil,
   202  			},
   203  		},
   204  		{
   205  			tagName: "p",
   206  			structure: &struct {
   207  				q *struct {
   208  					x int
   209  				} `p:"(q.x)$"`
   210  			}{},
   211  			tests: map[string]interface{}{
   212  				"q": nil,
   213  			},
   214  		},
   215  	}
   216  	for i, c := range cases {
   217  		vm := New(c.tagName)
   218  		// vm.WarmUp(c.structure)
   219  		tagExpr, err := vm.Run(c.structure)
   220  		if err != nil {
   221  			t.Fatal(err)
   222  		}
   223  		for selector, value := range c.tests {
   224  			val := tagExpr.Eval(selector)
   225  			if !reflect.DeepEqual(val, value) {
   226  				t.Fatalf("Eval Serial: %d, selector: %q, got: %v, expect: %v", i, selector, val, value)
   227  			}
   228  		}
   229  		tagExpr.Range(func(eh *ExprHandler) error {
   230  			es := eh.ExprSelector()
   231  			t.Logf("Range selector: %s, field: %q exprName: %q", es, es.Field(), es.Name())
   232  			value := c.tests[es.String()]
   233  			val := eh.Eval()
   234  			if !reflect.DeepEqual(val, value) {
   235  				t.Fatalf("Range NO: %d, selector: %q, got: %v, expect: %v", i, es, val, value)
   236  			}
   237  			return nil
   238  		})
   239  	}
   240  }
   241  
   242  func TestFieldNotInit(t *testing.T) {
   243  	g := &struct {
   244  		_ int
   245  		h string
   246  		s []string
   247  		m map[string][]string
   248  	}{
   249  		h: "haha",
   250  		s: []string{"1"},
   251  		m: map[string][]string{"0": {"2"}},
   252  	}
   253  	structure := &struct {
   254  		A int
   255  		b string
   256  		c struct {
   257  			_ int
   258  			d *bool `expr:"test:nil"`
   259  		}
   260  		e *struct {
   261  			_ int
   262  			f bool
   263  		}
   264  		g **struct {
   265  			_ int
   266  			h string
   267  			s []string
   268  			m map[string][]string
   269  		}
   270  		i string
   271  		j bool
   272  		k int
   273  		m *int
   274  		n *bool
   275  		p *string
   276  	}{
   277  		A: 5,
   278  		b: "x",
   279  		e: &struct {
   280  			_ int
   281  			f bool
   282  		}{f: true},
   283  		g: &g,
   284  		i: "12",
   285  	}
   286  	vm := New("expr")
   287  	e, err := vm.Run(structure)
   288  	if err != nil {
   289  		t.Fatal(err)
   290  	}
   291  	cases := []struct {
   292  		fieldSelector string
   293  		value         interface{}
   294  	}{
   295  		{"A", structure.A},
   296  		{"b", structure.b},
   297  		{"c", structure.c},
   298  		{"c._", 0},
   299  		{"c.d", structure.c.d},
   300  		{"e", structure.e},
   301  		{"e._", 0},
   302  		{"e.f", structure.e.f},
   303  		{"g", structure.g},
   304  		{"g._", 0},
   305  		{"g.h", (*structure.g).h},
   306  		{"g.s", (*structure.g).s},
   307  		{"g.m", (*structure.g).m},
   308  		{"i", structure.i},
   309  		{"j", structure.j},
   310  		{"k", structure.k},
   311  		{"m", structure.m},
   312  		{"n", structure.n},
   313  		{"p", structure.p},
   314  	}
   315  	for _, c := range cases {
   316  		fh, _ := e.Field(c.fieldSelector)
   317  		val := fh.Value(false).Interface()
   318  		assert.Equal(t, c.value, val, c.fieldSelector)
   319  	}
   320  	var i int
   321  	e.RangeFields(func(fh *FieldHandler) bool {
   322  		val := fh.Value(false).Interface()
   323  		if fh.StringSelector() == "c.d" {
   324  			assert.NotNil(t, fh.EvalFuncs()["c.d@test"])
   325  		}
   326  		assert.Equal(t, cases[i].value, val, fh.StringSelector())
   327  		i++
   328  		return true
   329  	})
   330  	var wall uint64 = 1024
   331  	unix := time.Unix(1549186325, int64(wall))
   332  	e, err = vm.Run(&unix)
   333  	if err != nil {
   334  		t.Fatal(err)
   335  	}
   336  	fh, _ := e.Field("wall")
   337  	val := fh.Value(false).Interface()
   338  	if !reflect.DeepEqual(val, wall) {
   339  		t.Fatalf("Time.wall: got: %v(%[1]T), expect: %v(%[2]T)", val, wall)
   340  	}
   341  }
   342  
   343  func TestFieldInitZero(t *testing.T) {
   344  	g := &struct {
   345  		_ int
   346  		h string
   347  		s []string
   348  		m map[string][]string
   349  	}{
   350  		h: "haha",
   351  		s: []string{"1"},
   352  		m: map[string][]string{"0": {"2"}},
   353  	}
   354  
   355  	structure := &struct {
   356  		A int
   357  		b string
   358  		c struct {
   359  			_ int
   360  			d *bool
   361  		}
   362  		e *struct {
   363  			_ int
   364  			f bool
   365  		}
   366  		g **struct {
   367  			_ int
   368  			h string
   369  			s []string
   370  			m map[string][]string
   371  		}
   372  		g2 ****struct {
   373  			_ int
   374  			h string
   375  			s []string
   376  			m map[string][]string
   377  		}
   378  		i string
   379  		j bool
   380  		k int
   381  		m *int
   382  		n *bool
   383  		p *string
   384  	}{
   385  		A: 5,
   386  		b: "x",
   387  		e: &struct {
   388  			_ int
   389  			f bool
   390  		}{f: true},
   391  		g: &g,
   392  		i: "12",
   393  	}
   394  
   395  	vm := New("")
   396  	e, err := vm.Run(structure)
   397  	if err != nil {
   398  		t.Fatal(err)
   399  	}
   400  
   401  	cases := []struct {
   402  		fieldSelector string
   403  		value         interface{}
   404  	}{
   405  		{"A", structure.A},
   406  		{"b", structure.b},
   407  		{"c", struct {
   408  			_ int
   409  			d *bool
   410  		}{}},
   411  		{"c._", 0},
   412  		{"c.d", new(bool)},
   413  		{"e", structure.e},
   414  		{"e._", 0},
   415  		{"e.f", structure.e.f},
   416  		{"g", structure.g},
   417  		{"g._", 0},
   418  		{"g.h", (*structure.g).h},
   419  		{"g.s", (*structure.g).s},
   420  		{"g.m", (*structure.g).m},
   421  		{"g2.m", (map[string][]string)(nil)},
   422  		{"i", structure.i},
   423  		{"j", structure.j},
   424  		{"k", structure.k},
   425  		{"m", new(int)},
   426  		{"n", new(bool)},
   427  		{"p", new(string)},
   428  	}
   429  	for _, c := range cases {
   430  		fh, _ := e.Field(c.fieldSelector)
   431  		val := fh.Value(true).Interface()
   432  		assert.Equal(t, c.value, val, c.fieldSelector)
   433  	}
   434  }
   435  
   436  func TestOperator(t *testing.T) {
   437  
   438  	type Tmp1 struct {
   439  		A string `tagexpr:$=="1"||$=="2"||$="3"`
   440  		B []int  `tagexpr:len($)>=10&&$[0]<10`
   441  		C interface{}
   442  	}
   443  
   444  	type Tmp2 struct {
   445  		A *Tmp1
   446  		B interface{}
   447  	}
   448  
   449  	type Target struct {
   450  		A int             `tagexpr:"-$+$<=10"`
   451  		B int             `tagexpr:"+$-$<=10"`
   452  		C int             `tagexpr:"-$+(M)$*(N)$/$%(D.B)$[2]+$==1"`
   453  		D *Tmp1           `tagexpr:"(D.A)$!=nil"`
   454  		E string          `tagexpr:"((D.A)$=='1'&&len($)>1)||((D.A)$=='2'&&len($)>2)||((D.A)$=='3'&&len($)>3)"`
   455  		F map[string]int  `tagexpr:"x:len($);y:$['a']>10&&$['b']>1"`
   456  		G *map[string]int `tagexpr:"x:$['a']+(F)$['a']>20"`
   457  		H []string        `tagexpr:"len($)>=1&&len($)<10&&$[0]=='123'&&$[1]!='456'"`
   458  		I interface{}     `tagexpr:"$!=nil"`
   459  		K *string         `tagexpr:"len((D.A)$)+len($)<10&&len((D.A)$+$)<10"`
   460  		L **string        `tagexpr:"false"`
   461  		M float64         `tagexpr:"$/2>10&&$%2==0"`
   462  		N *float64        `tagexpr:"($+$*$-$/$+1)/$==$+1"`
   463  		O *[3]float64     `tagexpr:"$[0]>10&&$[0]<20||$[0]>20&&$[0]<30"`
   464  		P *Tmp2           `tagexpr:"x:$!=nil;y:len((P.A.A)$)<=1&&(P.A.B)$[0]==1;z:$['A']['C']==nil;w:$['A']['B'][0]==1;r:$[0][1][2]==3;s1:$[2]==nil;s2:$[0][3]==nil;s3:(ZZ)$;s4:(P.B)$!=nil"`
   465  		Q *Tmp2           `tagexpr:"s1:$['A']['B']!=nil;s2:(Q.A)$['B']!=nil;s3:$['A']['C']==nil;s4:(Q.A)$['C']==nil;s5:(Q.A)$['B'][0]==1;s6:$['X']['Z']==nil"`
   466  	}
   467  
   468  	k := "123456"
   469  	n := float64(-12.5)
   470  	o := [3]float64{15, 9, 9}
   471  	var cases = []struct {
   472  		tagName   string
   473  		structure interface{}
   474  		tests     map[string]interface{}
   475  	}{
   476  		{
   477  			tagName: "tagexpr",
   478  			structure: &Target{
   479  				A: 5,
   480  				B: 10,
   481  				C: -10,
   482  				D: &Tmp1{A: "3", B: []int{1, 2, 3}},
   483  				E: "1234",
   484  				F: map[string]int{"a": 11, "b": 9},
   485  				G: &map[string]int{"a": 11},
   486  				H: []string{"123", "45"},
   487  				I: struct{}{},
   488  				K: &k,
   489  				L: nil,
   490  				M: float64(30),
   491  				N: &n,
   492  				O: &o,
   493  				P: &Tmp2{A: &Tmp1{A: "3", B: []int{1, 2, 3}}, B: struct{}{}},
   494  				Q: &Tmp2{A: &Tmp1{A: "3", B: []int{1, 2, 3}}, B: struct{}{}},
   495  			},
   496  			tests: map[string]interface{}{
   497  				"A":   true,
   498  				"B":   true,
   499  				"C":   true,
   500  				"D":   true,
   501  				"E":   true,
   502  				"F@x": float64(2),
   503  				"F@y": true,
   504  				"G@x": true,
   505  				"H":   true,
   506  				"I":   true,
   507  				"K":   true,
   508  				"L":   false,
   509  				"M":   true,
   510  				"N":   true,
   511  				"O":   true,
   512  
   513  				"P@x":  true,
   514  				"P@y":  true,
   515  				"P@z":  true,
   516  				"P@w":  true,
   517  				"P@r":  true,
   518  				"P@s1": true,
   519  				"P@s2": true,
   520  				"P@s3": nil,
   521  				"P@s4": true,
   522  
   523  				"Q@s1": true,
   524  				"Q@s2": true,
   525  				"Q@s3": true,
   526  				"Q@s4": true,
   527  				"Q@s5": true,
   528  				"Q@s6": true,
   529  			},
   530  		},
   531  	}
   532  
   533  	for i, c := range cases {
   534  		vm := New(c.tagName)
   535  		// vm.WarmUp(c.structure)
   536  		tagExpr, err := vm.Run(c.structure)
   537  		if err != nil {
   538  			t.Fatal(err)
   539  		}
   540  		for selector, value := range c.tests {
   541  			val := tagExpr.Eval(selector)
   542  			if !reflect.DeepEqual(val, value) {
   543  				t.Fatalf("Eval NO: %d, selector: %q, got: %v, expect: %v", i, selector, val, value)
   544  			}
   545  		}
   546  		tagExpr.Range(func(eh *ExprHandler) error {
   547  			es := eh.ExprSelector()
   548  			t.Logf("Range selector: %s, field: %q exprName: %q", es, es.Field(), es.Name())
   549  			value := c.tests[es.String()]
   550  			val := eh.Eval()
   551  			if !reflect.DeepEqual(val, value) {
   552  				t.Fatalf("Range NO: %d, selector: %q, got: %v, expect: %v", i, es, val, value)
   553  			}
   554  			return nil
   555  		})
   556  	}
   557  
   558  }
   559  
   560  func TestStruct(t *testing.T) {
   561  	type A struct {
   562  		B struct {
   563  			C struct {
   564  				D struct {
   565  					X string `vd:"$"`
   566  				}
   567  			} `vd:"@:$['D']['X']"`
   568  			C2 string `vd:"@:(C)$['D']['X']"`
   569  			C3 string `vd:"@:(C.D.X)$"`
   570  		}
   571  	}
   572  	a := new(A)
   573  	a.B.C.D.X = "xxx"
   574  	vm := New("vd")
   575  	expr := vm.MustRun(a)
   576  	assert.Equal(t, "xxx", expr.EvalString("B.C2"))
   577  	assert.Equal(t, "xxx", expr.EvalString("B.C3"))
   578  	assert.Equal(t, "xxx", expr.EvalString("B.C"))
   579  	assert.Equal(t, "xxx", expr.EvalString("B.C.D.X"))
   580  	expr.Range(func(eh *ExprHandler) error {
   581  		es := eh.ExprSelector()
   582  		t.Logf("Range selector: %s, field: %q exprName: %q", es, es.Field(), es.Name())
   583  		if eh.Eval().(string) != "xxx" {
   584  			t.FailNow()
   585  		}
   586  		return nil
   587  	})
   588  }
   589  
   590  func TestStruct2(t *testing.T) {
   591  	type IframeBlock struct {
   592  		XBlock struct {
   593  			BlockType string `vd:"$"`
   594  		}
   595  		Props struct {
   596  			Data struct {
   597  				DataType string `vd:"$"`
   598  			}
   599  		}
   600  	}
   601  	b := new(IframeBlock)
   602  	b.XBlock.BlockType = "BlockType"
   603  	b.Props.Data.DataType = "DataType"
   604  	vm := New("vd")
   605  	expr := vm.MustRun(b)
   606  	if expr.EvalString("XBlock.BlockType") != "BlockType" {
   607  		t.Fatal(expr.EvalString("XBlock.BlockType"))
   608  	}
   609  	if expr.EvalString("Props.Data.DataType") != "DataType" {
   610  		t.Fatal(expr.EvalString("Props.Data.DataType"))
   611  	}
   612  }
   613  
   614  func TestStruct3(t *testing.T) {
   615  	type Data struct {
   616  		DataType string `vd:"$"`
   617  	}
   618  	type Prop struct {
   619  		PropType string       `vd:"$"`
   620  		Datas    []*Data      `vd:"$"`
   621  		Datas2   []*Data      `vd:"$"`
   622  		DataMap  map[int]Data `vd:"$"`
   623  		DataMap2 map[int]Data `vd:"$"`
   624  	}
   625  	type IframeBlock struct {
   626  		XBlock struct {
   627  			BlockType string `vd:"$"`
   628  		}
   629  		Props    []Prop        `vd:"$"`
   630  		Props1   [2]Prop       `vd:"$"`
   631  		Props2   []Prop        `vd:"$"`
   632  		PropMap  map[int]*Prop `vd:"$"`
   633  		PropMap2 map[int]*Prop `vd:"$"`
   634  	}
   635  
   636  	b := new(IframeBlock)
   637  	b.XBlock.BlockType = "BlockType"
   638  	p1 := Prop{
   639  		PropType: "p1",
   640  		Datas: []*Data{
   641  			{"p1s1"},
   642  			{"p1s2"},
   643  			nil,
   644  		},
   645  		DataMap: map[int]Data{
   646  			1: {"p1m1"},
   647  			2: {"p1m2"},
   648  			0: Data{},
   649  		},
   650  	}
   651  	b.Props = []Prop{p1}
   652  	p2 := &Prop{
   653  		PropType: "p2",
   654  		Datas: []*Data{
   655  			{"p2s1"},
   656  			{"p2s2"},
   657  			nil,
   658  		},
   659  		DataMap: map[int]Data{
   660  			1: {"p2m1"},
   661  			2: {"p2m2"},
   662  			0: Data{},
   663  		},
   664  	}
   665  	b.Props1 = [2]Prop{p1, Prop{}}
   666  	b.PropMap = map[int]*Prop{
   667  		9: p2,
   668  	}
   669  
   670  	vm := New("vd")
   671  	expr := vm.MustRun(b)
   672  	if expr.EvalString("XBlock.BlockType") != "BlockType" {
   673  		t.Fatal(expr.EvalString("XBlock.BlockType"))
   674  	}
   675  	err := expr.Range(func(eh *ExprHandler) error {
   676  		es := eh.ExprSelector()
   677  		t.Logf("Range selector: %s, field: %q exprName: %q, eval: %v", eh.Path(), es.Field(), es.Name(), eh.Eval())
   678  		return nil
   679  	})
   680  	assert.NoError(t, err)
   681  }
   682  
   683  func TestNilField(t *testing.T) {
   684  	type P struct {
   685  		X **struct {
   686  			A *[]uint16 `tagexpr:"$"`
   687  		} `tagexpr:"$"`
   688  		Y **struct{} `tagexpr:"$"`
   689  	}
   690  	vm := New("tagexpr")
   691  	te := vm.MustRun(P{})
   692  	te.Range(func(eh *ExprHandler) error {
   693  		r := eh.Eval()
   694  		if r != nil {
   695  			t.Fatal(eh.Path(), r)
   696  		}
   697  		return nil
   698  	})
   699  
   700  	type G struct {
   701  		Nil1 *int `tagexpr:"nil!=$"`
   702  		Nil2 *int `tagexpr:"$!=nil"`
   703  	}
   704  	g := G{
   705  		Nil1: new(int),
   706  		Nil2: new(int),
   707  	}
   708  	vm.MustRun(g).Range(func(eh *ExprHandler) error {
   709  		r, ok := eh.Eval().(bool)
   710  		if !ok || !r {
   711  			t.Fatal(eh.Path(), r)
   712  		}
   713  		return nil
   714  	})
   715  
   716  	type (
   717  		N struct {
   718  			X  string                 `tagexpr:"len($)>0"`
   719  			S  []*N                   `tagexpr:"?"`
   720  			M  map[string]*N          `tagexpr:"?"`
   721  			I  interface{}            `tagexpr:"-"`
   722  			MI map[string]interface{} `tagexpr:"?"`
   723  			SI []interface{}
   724  			*N `tagexpr:"?"`
   725  			N2 *N `tagexpr:"?"`
   726  		}
   727  		M struct {
   728  			X string `tagexpr:"len($)>0"`
   729  		}
   730  	)
   731  	n := &N{
   732  		X:  "n",
   733  		S:  []*N{nil},
   734  		M:  map[string]*N{"": nil},
   735  		I:  new(N),
   736  		MI: map[string]interface{}{"": (*M)(nil)},
   737  		SI: []interface{}{&M{X: "nn"}},
   738  	}
   739  	var cnt int
   740  	vm.MustRun(n).Range(func(eh *ExprHandler) error {
   741  		r, ok := eh.Eval().(bool)
   742  		if !ok || !r {
   743  			t.Fatal(eh.Path(), r)
   744  		}
   745  		t.Log("path:", eh.Path(), "es:", eh.ExprSelector(), "val:", r)
   746  		cnt++
   747  		return nil
   748  	})
   749  	if cnt != 2 {
   750  		t.FailNow()
   751  	}
   752  }
   753  
   754  func TestDeepNested(t *testing.T) {
   755  	type testInner struct {
   756  		Address string `tagexpr:"name:$"`
   757  	}
   758  	type struct1 struct {
   759  		I *testInner
   760  		A []*testInner
   761  		X interface{}
   762  	}
   763  	type struct2 struct {
   764  		S *struct1
   765  	}
   766  	type Data struct {
   767  		S1 *struct2
   768  		S2 *struct2
   769  	}
   770  	data := &Data{
   771  		S1: &struct2{
   772  			S: &struct1{
   773  				I: &testInner{Address: "I:address"},
   774  				A: []*testInner{{Address: "A:address"}},
   775  				X: []*testInner{{Address: "X:address"}},
   776  			},
   777  		},
   778  		S2: &struct2{
   779  			S: &struct1{
   780  				A: []*testInner{nil},
   781  			},
   782  		},
   783  	}
   784  	expectKey := [...]interface{}{"S1.S.I.Address@name", "S2.S.I.Address@name", "S1.S.A[0].Address@name", "S2.S.A[0].Address@name", "S1.S.X[0].Address@name"}
   785  	expectValue := [...]interface{}{"I:address", nil, "A:address", nil, "X:address"}
   786  	var i int
   787  	vm := New("tagexpr")
   788  	vm.MustRun(data).Range(func(eh *ExprHandler) error {
   789  		assert.Equal(t, expectKey[i], eh.Path())
   790  		assert.Equal(t, expectValue[i], eh.Eval())
   791  		i++
   792  		t.Log(eh.Path(), eh.ExprSelector(), eh.Eval())
   793  		return nil
   794  	})
   795  	assert.Equal(t, 5, i)
   796  }
   797  
   798  func TestIssue3(t *testing.T) {
   799  	type C struct {
   800  		Id    string
   801  		Index int32 `vd:"$"`
   802  		P     *int  `vd:"$!=nil"`
   803  	}
   804  	type A struct {
   805  		F1 *C
   806  		F2 *C
   807  	}
   808  	a := &A{
   809  		F1: &C{
   810  			Id:    "test",
   811  			Index: 1,
   812  			P:     new(int),
   813  		},
   814  	}
   815  	vm := New("vd")
   816  	err := vm.MustRun(a).Range(func(eh *ExprHandler) error {
   817  		switch eh.Path() {
   818  		case "F1.Index":
   819  			assert.Equal(t, float64(1), eh.Eval(), eh.Path())
   820  		case "F2.Index":
   821  			assert.Equal(t, nil, eh.Eval(), eh.Path())
   822  		case "F1.P":
   823  			assert.Equal(t, true, eh.Eval(), eh.Path())
   824  		case "F2.P":
   825  			assert.Equal(t, false, eh.Eval(), eh.Path())
   826  		}
   827  		return nil
   828  	})
   829  	assert.NoError(t, err)
   830  }
   831  
   832  func TestIssue4(t *testing.T) {
   833  	type T struct {
   834  		A *string `te:"len($)+mblen($)"`
   835  		B *string `te:"len($)+mblen($)"`
   836  		C *string `te:"len($)+mblen($)"`
   837  	}
   838  	c := "c"
   839  	v := &T{
   840  		B: new(string),
   841  		C: &c,
   842  	}
   843  	vm := New("te")
   844  	err := vm.MustRun(v).Range(func(eh *ExprHandler) error {
   845  		t.Logf("eval:%v, path:%s", eh.EvalFloat(), eh.Path())
   846  		return nil
   847  	})
   848  	assert.NoError(t, err)
   849  }