OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // adapted from github.com/golang/appengine/datastore | 5 // adapted from github.com/golang/appengine/datastore |
6 | 6 |
7 package datastore | 7 package datastore |
8 | 8 |
9 import ( | 9 import ( |
10 "bytes" | 10 "bytes" |
11 "encoding/json" | 11 "encoding/json" |
12 "fmt" | 12 "fmt" |
13 "math" | 13 "math" |
14 "reflect" | 14 "reflect" |
15 "strconv" | 15 "strconv" |
16 "strings" | 16 "strings" |
17 "testing" | 17 "testing" |
18 "time" | 18 "time" |
19 | 19 |
20 "github.com/luci/gae/service/blobstore" | 20 "github.com/luci/gae/service/blobstore" |
| 21 . "github.com/luci/luci-go/common/testing/assertions" |
21 . "github.com/smartystreets/goconvey/convey" | 22 . "github.com/smartystreets/goconvey/convey" |
22 ) | 23 ) |
23 | 24 |
24 var ( | 25 var ( |
25 mp = MkProperty | 26 mp = MkProperty |
26 mpNI = MkPropertyNI | 27 mpNI = MkPropertyNI |
27 ) | 28 ) |
28 | 29 |
29 const testAppID = "testApp" | 30 const testAppID = "testApp" |
30 | 31 |
(...skipping 489 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
520 *c = Complex(complex(gval.Lat, gval.Lng)) | 521 *c = Complex(complex(gval.Lat, gval.Lng)) |
521 return nil | 522 return nil |
522 } | 523 } |
523 return fmt.Errorf("nope") | 524 return fmt.Errorf("nope") |
524 } | 525 } |
525 | 526 |
526 type Impossible4 struct { | 527 type Impossible4 struct { |
527 Values []Complex | 528 Values []Complex |
528 } | 529 } |
529 | 530 |
| 531 // TODO(riannucci): see if there's a way to NOT have this be a duplicate of |
| 532 // key.Generic. I couldn't figure out the package interdependency, and this |
| 533 // allows things to be in separate packages. |
| 534 type genericKey struct { |
| 535 kind string |
| 536 sid string |
| 537 iid int64 |
| 538 |
| 539 aid string |
| 540 ns string |
| 541 |
| 542 parent *genericKey |
| 543 } |
| 544 |
| 545 func (g *genericKey) Kind() string { return g.kind } |
| 546 func (g *genericKey) StringID() string { return g.sid } |
| 547 func (g *genericKey) IntID() int64 { return g.iid } |
| 548 func (g *genericKey) AppID() string { return g.aid } |
| 549 func (g *genericKey) Namespace() string { return g.ns } |
| 550 func (g *genericKey) Parent() Key { return g.parent } |
| 551 |
| 552 func marshalDSKey(b *bytes.Buffer, k *genericKey) { |
| 553 if k.parent != nil { |
| 554 marshalDSKey(b, k.parent) |
| 555 } |
| 556 b.WriteByte('/') |
| 557 b.WriteString(k.kind) |
| 558 b.WriteByte(',') |
| 559 if k.sid != "" { |
| 560 b.WriteString(k.sid) |
| 561 } else { |
| 562 b.WriteString(strconv.FormatInt(k.iid, 10)) |
| 563 } |
| 564 } |
| 565 |
| 566 func (g *genericKey) String() string { |
| 567 if g == nil { |
| 568 return "" |
| 569 } |
| 570 b := bytes.NewBuffer(make([]byte, 0, 512)) |
| 571 marshalDSKey(b, g) |
| 572 return b.String() |
| 573 } |
| 574 |
| 575 func (g *genericKey) Incomplete() bool { |
| 576 return g.iid == 0 && g.sid == "" |
| 577 } |
| 578 |
| 579 func (g *genericKey) Valid(allowSpecial bool, aid, ns string) bool { |
| 580 if g == nil { |
| 581 return false |
| 582 } |
| 583 if aid != g.AppID() || ns != g.Namespace() { |
| 584 return false |
| 585 } |
| 586 for ; g != nil; g = g.parent { |
| 587 if !allowSpecial && len(g.Kind()) >= 2 && g.Kind()[:2] == "__" { |
| 588 return false |
| 589 } |
| 590 if g.Kind() == "" || g.AppID() == "" { |
| 591 return false |
| 592 } |
| 593 if g.StringID() != "" && g.IntID() != 0 { |
| 594 return false |
| 595 } |
| 596 if g.parent != nil { |
| 597 if g.parent.Incomplete() { |
| 598 return false |
| 599 } |
| 600 if g.parent.AppID() != g.AppID() || g.parent.Namespace()
!= g.Namespace() { |
| 601 return false |
| 602 } |
| 603 } |
| 604 } |
| 605 return true |
| 606 } |
| 607 |
| 608 func (g *genericKey) PartialValid(aid, ns string) bool { |
| 609 if g.Incomplete() { |
| 610 g = mkKey(g.AppID(), g.Namespace(), g.Kind(), 1, g.Parent()).(*g
enericKey) |
| 611 } |
| 612 return g.Valid(false, aid, ns) |
| 613 } |
| 614 |
| 615 var _ Key = (*genericKey)(nil) |
| 616 |
| 617 func mkKey(aid, ns string, pairs ...interface{}) Key { |
| 618 ret := (*genericKey)(nil) |
| 619 if len(pairs)%2 != 0 { |
| 620 ret, _ = pairs[len(pairs)-1].(*genericKey) |
| 621 pairs = pairs[:len(pairs)-1] |
| 622 } |
| 623 for i := 0; i < len(pairs); i += 2 { |
| 624 kind := pairs[i].(string) |
| 625 id := pairs[i+1] |
| 626 ret = &genericKey{ |
| 627 kind: kind, |
| 628 aid: aid, |
| 629 ns: ns, |
| 630 parent: ret, |
| 631 } |
| 632 ret.sid, _ = id.(string) |
| 633 iid, ok := id.(int) |
| 634 if ok { |
| 635 ret.iid = int64(iid) |
| 636 } else { |
| 637 ret.iid, _ = id.(int64) |
| 638 } |
| 639 } |
| 640 return ret |
| 641 } |
| 642 |
530 type DerivedKey struct { | 643 type DerivedKey struct { |
531 » K *GenericKey | 644 » K *genericKey |
532 } | 645 } |
533 | 646 |
534 type IfaceKey struct { | 647 type IfaceKey struct { |
535 K Key | 648 K Key |
536 } | 649 } |
537 | 650 |
538 type testCase struct { | 651 type testCase struct { |
539 desc string | 652 desc string |
540 src interface{} | 653 src interface{} |
541 want interface{} | 654 want interface{} |
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
792 }, | 905 }, |
793 }, | 906 }, |
794 { | 907 { |
795 desc: "convertable complex slice (bad load)", | 908 desc: "convertable complex slice (bad load)", |
796 src: PropertyMap{"Values": {mp("hello")}}, | 909 src: PropertyMap{"Values": {mp("hello")}}, |
797 want: &Impossible4{[]Complex(nil)}, | 910 want: &Impossible4{[]Complex(nil)}, |
798 loadErr: "nope", | 911 loadErr: "nope", |
799 }, | 912 }, |
800 { | 913 { |
801 desc: "allow concrete Key implementors (save)", | 914 desc: "allow concrete Key implementors (save)", |
802 » » src: &DerivedKey{testKey2a.(*GenericKey)}, | 915 » » src: &DerivedKey{testKey2a.(*genericKey)}, |
803 want: &IfaceKey{testKey2b}, | 916 want: &IfaceKey{testKey2b}, |
804 }, | 917 }, |
805 { | 918 { |
806 desc: "allow concrete Key implementors (load)", | 919 desc: "allow concrete Key implementors (load)", |
807 src: &IfaceKey{testKey2b}, | 920 src: &IfaceKey{testKey2b}, |
808 » » want: &DerivedKey{testKey2a.(*GenericKey)}, | 921 » » want: &DerivedKey{testKey2a.(*genericKey)}, |
809 }, | 922 }, |
810 { | 923 { |
811 desc: "save []float64 load []int64", | 924 desc: "save []float64 load []int64", |
812 src: &Y0{B: true, F: []float64{7, 8, 9}}, | 925 src: &Y0{B: true, F: []float64{7, 8, 9}}, |
813 want: &Y2{B: true}, | 926 want: &Y2{B: true}, |
814 loadErr: "type mismatch", | 927 loadErr: "type mismatch", |
815 }, | 928 }, |
816 { | 929 { |
817 desc: "single slice is too long", | 930 desc: "single slice is too long", |
818 src: &Y0{F: make([]float64, maxIndexedProperties+1)}, | 931 src: &Y0{F: make([]float64, maxIndexedProperties+1)}, |
(...skipping 637 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1456 got := err.Error() | 1569 got := err.Error() |
1457 if want == "" || strings.Index(got, want) == -1 { | 1570 if want == "" || strings.Index(got, want) == -1 { |
1458 return got | 1571 return got |
1459 } | 1572 } |
1460 } else if want != "" { | 1573 } else if want != "" { |
1461 return fmt.Sprintf("want error %q", want) | 1574 return fmt.Sprintf("want error %q", want) |
1462 } | 1575 } |
1463 return "" | 1576 return "" |
1464 } | 1577 } |
1465 | 1578 |
1466 func ShouldErrLike(actual interface{}, expected ...interface{}) string { | |
1467 e2s := func(o interface{}) (string, bool) { | |
1468 switch x := o.(type) { | |
1469 case nil: | |
1470 return "", true | |
1471 case string: | |
1472 return x, true | |
1473 case error: | |
1474 if x != nil { | |
1475 return x.Error(), true | |
1476 } | |
1477 return "", true | |
1478 } | |
1479 return fmt.Sprintf("unknown argument type %T, expected string, e
rror or nil", actual), false | |
1480 } | |
1481 | |
1482 as, ok := e2s(actual) | |
1483 if !ok { | |
1484 return as | |
1485 } | |
1486 | |
1487 if len(expected) != 1 { | |
1488 return fmt.Sprintf("Assertion requires 1 expected value, got %d"
, len(expected)) | |
1489 } | |
1490 | |
1491 err, ok := e2s(expected[0]) | |
1492 if !ok { | |
1493 return err | |
1494 } | |
1495 return ShouldContainSubstring(as, err) | |
1496 } | |
1497 | |
1498 func TestRoundTrip(t *testing.T) { | 1579 func TestRoundTrip(t *testing.T) { |
1499 t.Parallel() | 1580 t.Parallel() |
1500 | 1581 |
1501 checkErr := func(actual interface{}, expected string) bool { | 1582 checkErr := func(actual interface{}, expected string) bool { |
1502 » » So(actual, ShouldErrLike, expected) | 1583 » » if expected == "" { |
| 1584 » » » So(actual, ShouldErrLike, nil) |
| 1585 » » } else { |
| 1586 » » » So(actual, ShouldErrLike, expected) |
| 1587 » » } |
1503 return expected != "" | 1588 return expected != "" |
1504 } | 1589 } |
1505 | 1590 |
1506 Convey("Test round-trip", t, func() { | 1591 Convey("Test round-trip", t, func() { |
1507 for _, tc := range testCases { | 1592 for _, tc := range testCases { |
1508 tc := tc | 1593 tc := tc |
1509 Convey(tc.desc, func() { | 1594 Convey(tc.desc, func() { |
1510 pls, ok := tc.src.(PropertyLoadSaver) | 1595 pls, ok := tc.src.(PropertyLoadSaver) |
1511 if !ok { | 1596 if !ok { |
1512 pls = GetPLS(tc.src) | 1597 pls = GetPLS(tc.src) |
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1756 | 1841 |
1757 Convey("Bad default meta type", func() { | 1842 Convey("Bad default meta type", func() { |
1758 type BadDefault struct { | 1843 type BadDefault struct { |
1759 Val time.Time `gae:"$meta,tomorrow"` | 1844 Val time.Time `gae:"$meta,tomorrow"` |
1760 } | 1845 } |
1761 pls := GetPLS(&BadDefault{}) | 1846 pls := GetPLS(&BadDefault{}) |
1762 So(pls.Problem().Error(), ShouldContainSubstring, "bad t
ype") | 1847 So(pls.Problem().Error(), ShouldContainSubstring, "bad t
ype") |
1763 }) | 1848 }) |
1764 }) | 1849 }) |
1765 } | 1850 } |
OLD | NEW |