gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/encoding/decoder_test.go (about) 1 package encoding 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "image" 8 "reflect" 9 "testing" 10 ) 11 12 type T struct { 13 X string 14 Y int 15 Z int `rethinkdb:"-"` 16 } 17 18 type U struct { 19 Alphabet string `rethinkdb:"alpha"` 20 } 21 22 type V struct { 23 F1 interface{} 24 F2 int32 25 F3 string 26 } 27 28 type tx struct { 29 x int 30 } 31 32 var txType = reflect.TypeOf((*tx)(nil)).Elem() 33 34 // Test data structures for anonymous fields. 35 36 type Point struct { 37 Z int 38 } 39 40 type Top struct { 41 Level0 int 42 Embed0 43 *Embed0a 44 *Embed0b `rethinkdb:"e,omitempty"` // treated as named 45 Embed0c `rethinkdb:"-"` // ignored 46 Loop 47 Embed0p // has Point with X, Y, used 48 Embed0q // has Point with Z, used 49 } 50 51 type Embed0 struct { 52 Level1a int // overridden by Embed0a's Level1a with tag 53 Level1b int // used because Embed0a's Level1b is renamed 54 Level1c int // used because Embed0a's Level1c is ignored 55 Level1d int // annihilated by Embed0a's Level1d 56 Level1e int `rethinkdb:"x"` // annihilated by Embed0a.Level1e 57 } 58 59 type Embed0a struct { 60 Level1a int `rethinkdb:"Level1a,omitempty"` 61 Level1b int `rethinkdb:"LEVEL1B,omitempty"` 62 Level1c int `rethinkdb:"-"` 63 Level1d int // annihilated by Embed0's Level1d 64 Level1f int `rethinkdb:"x"` // annihilated by Embed0's Level1e 65 } 66 67 type Embed0b Embed0 68 69 type Embed0c Embed0 70 71 type Embed0p struct { 72 image.Point 73 } 74 75 type Embed0q struct { 76 Point 77 } 78 79 type Loop struct { 80 Loop1 int `rethinkdb:",omitempty"` 81 Loop2 int `rethinkdb:",omitempty"` 82 *Loop 83 } 84 85 // From reflect test: 86 // The X in S6 and S7 annihilate, but they also block the X in S8.S9. 87 type S5 struct { 88 S6 89 S7 90 S8 91 } 92 93 type S6 struct { 94 X int 95 } 96 97 type S7 S6 98 99 type S8 struct { 100 S9 101 } 102 103 type S9 struct { 104 X int 105 Y int 106 } 107 108 // From reflect test: 109 // The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. 110 type S10 struct { 111 S11 112 S12 113 S13 114 } 115 116 type S11 struct { 117 S6 118 } 119 120 type S12 struct { 121 S6 122 } 123 124 type S13 struct { 125 S8 126 } 127 128 type PointerBasic struct { 129 X int 130 Y *int 131 } 132 133 type Pointer struct { 134 PPoint *Point 135 Point Point 136 } 137 138 type decodeTest struct { 139 in interface{} 140 ptr interface{} 141 out interface{} 142 err error 143 } 144 145 type Ambig struct { 146 // Given "hello", the first match should win. 147 First int `rethinkdb:"HELLO"` 148 Second int `rethinkdb:"Hello"` 149 } 150 151 type SliceStruct struct { 152 X []string 153 } 154 155 // Decode test helper vars 156 var ( 157 sampleInt = 2 158 ) 159 160 var decodeTests = []decodeTest{ 161 // basic types 162 {in: true, ptr: new(bool), out: true}, 163 {in: 1, ptr: new(int), out: 1}, 164 {in: 1.2, ptr: new(float64), out: 1.2}, 165 {in: -5, ptr: new(int16), out: int16(-5)}, 166 {in: 2, ptr: new(string), out: string("2")}, 167 {in: float64(2.0), ptr: new(interface{}), out: float64(2.0)}, 168 {in: string("2"), ptr: new(interface{}), out: string("2")}, 169 {in: "a\u1234", ptr: new(string), out: "a\u1234"}, 170 {in: []interface{}{}, ptr: new([]string), out: []string{}}, 171 {in: map[string]interface{}{"X": []interface{}{1, 2, 3}, "Y": 4}, ptr: new(T), out: T{}, err: &DecodeTypeError{reflect.TypeOf(""), reflect.TypeOf([]interface{}{}), ""}}, 172 {in: map[string]interface{}{"x": 1}, ptr: new(tx), out: tx{}}, 173 {in: map[string]interface{}{"F1": float64(1), "F2": 2, "F3": 3}, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: string("3")}}, 174 {in: map[string]interface{}{"F1": string("1"), "F2": 2, "F3": 3}, ptr: new(V), out: V{F1: string("1"), F2: int32(2), F3: string("3")}}, 175 { 176 in: map[string]interface{}{"k1": int64(1), "k2": "s", "k3": []interface{}{int64(1), 2.0, 3e-3}, "k4": map[string]interface{}{"kk1": "s", "kk2": int64(2)}}, 177 out: map[string]interface{}{"k1": int64(1), "k2": "s", "k3": []interface{}{int64(1), 2.0, 3e-3}, "k4": map[string]interface{}{"kk1": "s", "kk2": int64(2)}}, 178 ptr: new(interface{}), 179 }, 180 181 // Z has a "-" tag. 182 {in: map[string]interface{}{"Y": 1, "Z": 2}, ptr: new(T), out: T{Y: 1}}, 183 184 {in: map[string]interface{}{"alpha": "abc", "alphabet": "xyz"}, ptr: new(U), out: U{Alphabet: "abc"}}, 185 {in: map[string]interface{}{"alpha": "abc"}, ptr: new(U), out: U{Alphabet: "abc"}}, 186 {in: map[string]interface{}{"alphabet": "xyz"}, ptr: new(U), out: U{}}, 187 188 // array tests 189 {in: []interface{}{1, 2, 3}, ptr: new([3]int), out: [3]int{1, 2, 3}}, 190 {in: []interface{}{1, 2, 3}, ptr: new([1]int), out: [1]int{1}}, 191 {in: []interface{}{1, 2, 3}, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}}, 192 193 // empty array to interface test 194 {in: map[string]interface{}{"T": []interface{}{}}, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": []interface{}{}}}, 195 196 { 197 in: map[string]interface{}{ 198 "Level0": 1, 199 "Level1b": 2, 200 "Level1c": 3, 201 "level1d": 4, 202 "Level1a": 5, 203 "LEVEL1B": 6, 204 "e": map[string]interface{}{ 205 "Level1a": 8, 206 "Level1b": 9, 207 "Level1c": 10, 208 "Level1d": 11, 209 "x": 12, 210 }, 211 "Loop1": 13, 212 "Loop2": 14, 213 "X": 15, 214 "Y": 16, 215 "Z": 17, 216 }, 217 ptr: new(Top), 218 out: Top{ 219 Level0: 1, 220 Embed0: Embed0{ 221 Level1b: 2, 222 Level1c: 3, 223 }, 224 Embed0a: &Embed0a{ 225 Level1a: 5, 226 Level1b: 6, 227 }, 228 Embed0b: &Embed0b{ 229 Level1a: 8, 230 Level1b: 9, 231 Level1c: 10, 232 Level1d: 11, 233 }, 234 Loop: Loop{ 235 Loop1: 13, 236 Loop2: 14, 237 }, 238 Embed0p: Embed0p{ 239 Point: image.Point{X: 15, Y: 16}, 240 }, 241 Embed0q: Embed0q{ 242 Point: Point{Z: 17}, 243 }, 244 }, 245 }, 246 { 247 in: map[string]interface{}{"hello": 1}, 248 ptr: new(Ambig), 249 out: Ambig{First: 1}, 250 }, 251 { 252 in: map[string]interface{}{"X": 1, "Y": 2}, 253 ptr: new(S5), 254 out: S5{S8: S8{S9: S9{Y: 2}}}, 255 }, 256 { 257 in: map[string]interface{}{"X": 1, "Y": 2}, 258 ptr: new(S10), 259 out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}}, 260 }, 261 { 262 in: map[string]interface{}{"PPoint": map[string]interface{}{"Z": 1}, "Point": map[string]interface{}{"Z": 2}}, 263 ptr: new(Pointer), 264 out: Pointer{PPoint: &Point{Z: 1}, Point: Point{Z: 2}}, 265 }, 266 { 267 in: map[string]interface{}{"Point": map[string]interface{}{"Z": 2}}, 268 ptr: new(Pointer), 269 out: Pointer{PPoint: nil, Point: Point{Z: 2}}, 270 }, 271 { 272 in: map[string]interface{}{"x": 2}, 273 ptr: new(PointerBasic), 274 out: PointerBasic{X: 2, Y: nil}, 275 }, 276 { 277 in: map[string]interface{}{"x": 2, "y": 2}, 278 ptr: new(PointerBasic), 279 out: PointerBasic{X: 2, Y: &sampleInt}, 280 }, 281 } 282 283 func TestDecode(t *testing.T) { 284 for i, tt := range decodeTests { 285 if tt.ptr == nil { 286 continue 287 } 288 289 // v = new(right-type) 290 v := reflect.New(reflect.TypeOf(tt.ptr).Elem()) 291 292 err := Decode(v.Interface(), tt.in) 293 if !jsonEqual(err, tt.err) { 294 t.Errorf("#%d: got error %v want %v", i, err, tt.err) 295 continue 296 } 297 298 if tt.err == nil && !jsonEqual(v.Elem().Interface(), tt.out) { 299 t.Errorf("#%d: mismatch\nhave: %+v\nwant: %+v", i, v.Elem().Interface(), tt.out) 300 continue 301 } 302 303 // Check round trip. 304 if tt.err == nil { 305 enc, err := Encode(v.Interface()) 306 if err != nil { 307 t.Errorf("#%d: error re-marshaling: %v", i, err) 308 continue 309 } 310 vv := reflect.New(reflect.TypeOf(tt.ptr).Elem()) 311 312 if err := Decode(vv.Interface(), enc); err != nil { 313 t.Errorf("#%d: error re-decodeing: %v", i, err) 314 continue 315 } 316 if !jsonEqual(v.Elem().Interface(), vv.Elem().Interface()) { 317 t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface()) 318 continue 319 } 320 } 321 } 322 } 323 324 func TestStringKind(t *testing.T) { 325 type aMap map[string]int 326 327 var m1, m2 map[string]int 328 m1 = map[string]int{ 329 "foo": 42, 330 } 331 332 data, err := Encode(m1) 333 if err != nil { 334 t.Errorf("Unexpected error encoding: %v", err) 335 } 336 337 err = Decode(&m2, data) 338 if err != nil { 339 t.Errorf("Unexpected error decoding: %v", err) 340 } 341 342 if !jsonEqual(m1, m2) { 343 t.Error("Items should be equal after encoding and then decoding") 344 } 345 346 } 347 348 // Test handling of unexported fields that should be ignored. 349 type unexportedFields struct { 350 Name string 351 m map[string]interface{} `rethinkdb:"-"` 352 m2 map[string]interface{} `rethinkdb:"abcd"` 353 } 354 355 func TestDecodeUnexported(t *testing.T) { 356 input := map[string]interface{}{ 357 "Name": "Bob", 358 "m": map[string]interface{}{ 359 "x": 123, 360 }, 361 "m2": map[string]interface{}{ 362 "y": 123, 363 }, 364 "abcd": map[string]interface{}{ 365 "z": 789, 366 }, 367 } 368 want := &unexportedFields{Name: "Bob"} 369 370 out := &unexportedFields{} 371 err := Decode(out, input) 372 if err != nil { 373 t.Errorf("got error %v, expected nil", err) 374 } 375 if !jsonEqual(out, want) { 376 t.Errorf("got %q, want %q", out, want) 377 } 378 } 379 380 type Foo struct { 381 FooBar interface{} `rethinkdb:"foobar"` 382 } 383 type Bar struct { 384 Baz int `rethinkdb:"baz"` 385 } 386 387 type UnmarshalerPointer struct { 388 Value *UnmarshalerValue 389 } 390 391 type UnmarshalerValue struct { 392 ValueInt int64 393 ValueString string 394 } 395 396 func (v *UnmarshalerValue) MarshalRQL() (interface{}, error) { 397 if v.ValueInt != int64(0) { 398 return Encode(v.ValueInt) 399 } 400 if v.ValueString != "" { 401 return Encode(v.ValueString) 402 } 403 404 return Encode(nil) 405 } 406 407 func (v *UnmarshalerValue) UnmarshalRQL(b interface{}) (err error) { 408 n, s := int64(0), "" 409 410 if err = Decode(&s, b); err == nil { 411 v.ValueString = s 412 return 413 } 414 if err = Decode(&n, b); err == nil { 415 v.ValueInt = n 416 417 } 418 419 return 420 } 421 422 func TestDecodeUnmarshalerPointer(t *testing.T) { 423 input := map[string]interface{}{ 424 "Value": "abc", 425 } 426 want := &UnmarshalerPointer{ 427 Value: &UnmarshalerValue{ValueString: "abc"}, 428 } 429 430 out := &UnmarshalerPointer{} 431 err := Decode(out, input) 432 if err != nil { 433 t.Errorf("got error %v, expected nil", err) 434 } 435 if !jsonEqual(out, want) { 436 t.Errorf("got %+v, want %+v", out, want) 437 } 438 } 439 440 func TestDecodeMapIntKeys(t *testing.T) { 441 input := map[string]int{"1": 1, "2": 2, "3": 3} 442 want := map[int]int{1: 1, 2: 2, 3: 3} 443 444 out := map[int]int{} 445 err := Decode(&out, input) 446 if err != nil { 447 t.Errorf("got error %v, expected nil", err) 448 } 449 if !jsonEqual(out, want) { 450 t.Errorf("got %q, want %q", out, want) 451 } 452 } 453 454 func TestDecodeCompoundKey(t *testing.T) { 455 input := map[string]interface{}{"id": []string{"1", "2"}, "err_a[]": "3", "err_b[": "4", "err_c]": "5"} 456 want := Compound{"1", "2", "3", "4", "5"} 457 458 out := Compound{} 459 err := Decode(&out, input) 460 if err != nil { 461 t.Errorf("got error %v, expected nil", err) 462 } 463 if !jsonEqual(out, want) { 464 t.Errorf("got %q, want %q", out, want) 465 } 466 } 467 468 func TestDecodeNilSlice(t *testing.T) { 469 input := map[string]interface{}{"X": nil} 470 want := SliceStruct{} 471 472 out := SliceStruct{} 473 err := Decode(&out, input) 474 if err != nil { 475 t.Errorf("got error %v, expected nil", err) 476 } 477 if !jsonEqual(out, want) { 478 t.Errorf("got %q, want %q", out, want) 479 } 480 } 481 482 func jsonEqual(a, b interface{}) bool { 483 // First check using reflect.DeepEqual 484 if reflect.DeepEqual(a, b) { 485 return true 486 } 487 488 // Then use jsonEqual 489 ba, err := json.Marshal(a) 490 if err != nil { 491 panic(err) 492 } 493 bb, err := json.Marshal(b) 494 if err != nil { 495 panic(err) 496 } 497 498 return bytes.Compare(ba, bb) == 0 499 } 500 501 func TestMergeStruct(t *testing.T) { 502 var dst struct { 503 Field string 504 AnotherField string 505 } 506 dst.Field = "change me" 507 dst.AnotherField = "don't blank me" 508 err := Merge(&dst, map[string]interface{}{"Field": "Changed!"}) 509 if err != nil { 510 t.Error("Cannot merge:", err) 511 } 512 if dst.AnotherField == "" { 513 t.Error("Field has been wiped") 514 } 515 } 516 517 func TestMergeMap(t *testing.T) { 518 var dst = make(map[string]string) 519 dst["field"] = "change me" 520 dst["another_field"] = "don't blank me" 521 err := Merge(&dst, map[string]interface{}{"field": "Changed!"}) 522 if err != nil { 523 t.Error("Cannot merge:", err) 524 } 525 if dst["another_field"] == "" { 526 t.Error("Field has been wiped") 527 } 528 } 529 530 func TestDecodeCustomTypeEncodingValue(t *testing.T) { 531 type innerType struct { 532 Val int 533 } 534 type outerType struct { 535 Inner innerType `rethinkdb:"inner"` 536 } 537 538 want := outerType{Inner: innerType{Val: 5}} 539 in := map[string]interface{}{ 540 "inner": map[string]interface{}{ 541 "someval": 5, 542 }, 543 } 544 545 SetTypeEncoding(reflect.TypeOf(innerType{}), 546 nil, func(enc interface{}, val reflect.Value) error { 547 m := enc.(map[string]interface{}) 548 val.Set(reflect.ValueOf(innerType{Val: m["someval"].(int)})) 549 return nil 550 }) 551 552 var out outerType 553 err := Decode(&out, in) 554 if err != nil { 555 t.Errorf("got error %v, expected nil", err) 556 } 557 if !jsonEqual(out, want) { 558 t.Errorf("got %q, want %q", out, want) 559 } 560 } 561 562 func TestDecodeCustomTypeEncodingPointer(t *testing.T) { 563 type innerType struct { 564 Val int 565 } 566 type outerType struct { 567 Inner *innerType `rethinkdb:"inner"` 568 } 569 570 want := outerType{Inner: &innerType{Val: 5}} 571 in := map[string]interface{}{ 572 "inner": map[string]interface{}{ 573 "someval": 5, 574 }, 575 } 576 577 SetTypeEncoding(reflect.TypeOf((*innerType)(nil)), 578 nil, func(enc interface{}, val reflect.Value) error { 579 m := enc.(map[string]interface{}) 580 val.Set(reflect.ValueOf(&innerType{Val: m["someval"].(int)})) 581 return nil 582 }) 583 584 var out outerType 585 err := Decode(&out, in) 586 if err != nil { 587 t.Errorf("got error %v, expected nil", err) 588 } 589 if !jsonEqual(out, want) { 590 t.Errorf("got %+v, want %+v", out, want) 591 } 592 } 593 594 func TestDecodeCustomRootTypeEncodingValue(t *testing.T) { 595 type cType struct { 596 Val int 597 } 598 599 want := cType{Val: 5} 600 in := map[string]interface{}{ 601 "someval": 5, 602 } 603 604 SetTypeEncoding(reflect.TypeOf(cType{}), 605 nil, func(enc interface{}, val reflect.Value) error { 606 m := enc.(map[string]interface{}) 607 val.Set(reflect.ValueOf(cType{Val: m["someval"].(int)})) 608 return nil 609 }) 610 611 var out cType 612 err := Decode(&out, in) 613 if err != nil { 614 t.Errorf("got error %v, expected nil", err) 615 } 616 if !jsonEqual(out, want) { 617 t.Errorf("got %q, want %q", out, want) 618 } 619 } 620 621 func TestDecodeCustomRootTypeEncodingPointer(t *testing.T) { 622 type cType struct { 623 Val int 624 } 625 626 want := cType{Val: 5} 627 in := map[string]interface{}{ 628 "someval": 5, 629 } 630 631 SetTypeEncoding(reflect.TypeOf((*cType)(nil)), 632 nil, func(enc interface{}, val reflect.Value) error { 633 m := enc.(map[string]interface{}) 634 val.Set(reflect.ValueOf(&cType{Val: m["someval"].(int)})) 635 return nil 636 }) 637 638 var out *cType 639 err := Decode(&out, in) 640 if err != nil { 641 t.Errorf("got error %v, expected nil", err) 642 } 643 if !jsonEqual(out, want) { 644 t.Errorf("got %q, want %q", out, want) 645 } 646 } 647 648 func TestDecodeCustomTypeEncodingError(t *testing.T) { 649 type cType struct { 650 Val int 651 } 652 653 in := map[string]interface{}{ 654 "val": 5, 655 } 656 657 cerr := errors.New("decode error") 658 SetTypeEncoding(reflect.TypeOf(cType{}), 659 nil, func(enc interface{}, val reflect.Value) error { 660 return cerr 661 }) 662 663 var out cType 664 err := Decode(&out, in) 665 if err == nil { 666 t.Errorf("got no error, expected %v", cerr) 667 } 668 if err != cerr { 669 t.Errorf("got %v, want %v", err, cerr) 670 } 671 }