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 }