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" |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 Other string | 147 Other string |
148 } | 148 } |
149 | 149 |
150 type N2 struct { | 150 type N2 struct { |
151 N1 `gae:"red"` | 151 N1 `gae:"red"` |
152 Green N1 `gae:"green"` | 152 Green N1 `gae:"green"` |
153 Blue N1 | 153 Blue N1 |
154 White N1 `gae:"-"` | 154 White N1 `gae:"-"` |
155 } | 155 } |
156 | 156 |
| 157 type N3 struct { |
| 158 ID uint32 `gae:"$id,200"` |
| 159 } |
| 160 |
157 type O0 struct { | 161 type O0 struct { |
158 I int64 | 162 I int64 |
159 } | 163 } |
160 | 164 |
161 type O1 struct { | 165 type O1 struct { |
162 I int32 | 166 I int32 |
163 } | 167 } |
164 | 168 |
165 type U0 struct { | 169 type U0 struct { |
166 » U uint | 170 » U uint32 |
167 } | 171 } |
168 | 172 |
169 type U1 struct { | 173 type U1 struct { |
170 » U string | 174 » U byte |
| 175 } |
| 176 |
| 177 type U2 struct { |
| 178 » U int64 |
171 } | 179 } |
172 | 180 |
173 type T struct { | 181 type T struct { |
174 T time.Time | 182 T time.Time |
175 } | 183 } |
176 | 184 |
177 type X0 struct { | 185 type X0 struct { |
178 S string | 186 S string |
179 I int | 187 I int |
180 i int | 188 i int |
(...skipping 599 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
780 want: &T{T: time.Unix(1e9, 0).UTC()}, | 788 want: &T{T: time.Unix(1e9, 0).UTC()}, |
781 }, | 789 }, |
782 { | 790 { |
783 desc: "time as props", | 791 desc: "time as props", |
784 src: &T{T: time.Unix(1e9, 0).UTC()}, | 792 src: &T{T: time.Unix(1e9, 0).UTC()}, |
785 want: PropertyMap{ | 793 want: PropertyMap{ |
786 "T": {mp(time.Unix(1e9, 0).UTC())}, | 794 "T": {mp(time.Unix(1e9, 0).UTC())}, |
787 }, | 795 }, |
788 }, | 796 }, |
789 { | 797 { |
790 » » desc: "uint save", | 798 » » desc: "uint32 save", |
791 » » src: &U0{U: 1}, | 799 » » src: &U0{U: 1}, |
792 » » plsErr: `field "U" has invalid type: uint`, | 800 » » want: PropertyMap{ |
| 801 » » » "U": {mp(1)}, |
| 802 » » }, |
793 }, | 803 }, |
794 { | 804 { |
795 » » desc: "uint load", | 805 » » desc: "uint32 load", |
796 » » src: &U1{U: "not a uint"}, | 806 » » src: &U2{U: 100}, |
797 » » want: &U0{}, | 807 » » want: &U0{U: 100}, |
798 » » plsLoadErr: `field "U" has invalid type: uint`, | 808 » }, |
| 809 » { |
| 810 » » desc: "uint32 load oob (neg)", |
| 811 » » src: &U2{U: -1}, |
| 812 » » want: &U0{}, |
| 813 » » loadErr: "overflow", |
| 814 » }, |
| 815 » { |
| 816 » » desc: "uint32 load oob (huge)", |
| 817 » » src: &U2{U: math.MaxInt64}, |
| 818 » » want: &U0{}, |
| 819 » » loadErr: "overflow", |
| 820 » }, |
| 821 » { |
| 822 » » desc: "byte save", |
| 823 » » src: &U1{U: 1}, |
| 824 » » want: PropertyMap{ |
| 825 » » » "U": {mp(1)}, |
| 826 » » }, |
| 827 » }, |
| 828 » { |
| 829 » » desc: "byte load", |
| 830 » » src: &U2{U: 100}, |
| 831 » » want: &U1{U: 100}, |
| 832 » }, |
| 833 » { |
| 834 » » desc: "byte load oob (neg)", |
| 835 » » src: &U2{U: -1}, |
| 836 » » want: &U1{}, |
| 837 » » loadErr: "overflow", |
| 838 » }, |
| 839 » { |
| 840 » » desc: "byte load oob (huge)", |
| 841 » » src: &U2{U: math.MaxInt64}, |
| 842 » » want: &U1{}, |
| 843 » » loadErr: "overflow", |
799 }, | 844 }, |
800 { | 845 { |
801 desc: "zero", | 846 desc: "zero", |
802 src: &X0{}, | 847 src: &X0{}, |
803 want: &X0{}, | 848 want: &X0{}, |
804 }, | 849 }, |
805 { | 850 { |
806 desc: "basic", | 851 desc: "basic", |
807 src: &X0{S: "one", I: 2, i: 3}, | 852 src: &X0{S: "one", I: 2, i: 3}, |
808 want: &X0{S: "one", I: 2}, | 853 want: &X0{S: "one", I: 2}, |
(...skipping 785 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1594 desc: "json.RawMessage to myBlob", | 1639 desc: "json.RawMessage to myBlob", |
1595 src: &struct { | 1640 src: &struct { |
1596 B json.RawMessage | 1641 B json.RawMessage |
1597 }{ | 1642 }{ |
1598 B: json.RawMessage("rawr"), | 1643 B: json.RawMessage("rawr"), |
1599 }, | 1644 }, |
1600 want: &B2{B: myBlob("rawr")}, | 1645 want: &B2{B: myBlob("rawr")}, |
1601 }, | 1646 }, |
1602 } | 1647 } |
1603 | 1648 |
1604 // checkErr returns the empty string if either both want and err are zero, | |
1605 // or if want is a non-empty substring of err's string representation. | |
1606 func checkErr(want string, err error) string { | |
1607 if err != nil { | |
1608 got := err.Error() | |
1609 if want == "" || strings.Index(got, want) == -1 { | |
1610 return got | |
1611 } | |
1612 } else if want != "" { | |
1613 return fmt.Sprintf("want error %q", want) | |
1614 } | |
1615 return "" | |
1616 } | |
1617 | |
1618 func TestRoundTrip(t *testing.T) { | 1649 func TestRoundTrip(t *testing.T) { |
1619 t.Parallel() | 1650 t.Parallel() |
1620 | 1651 |
1621 checkErr := func(actual interface{}, expected string) bool { | |
1622 if expected == "" { | |
1623 So(actual, ShouldErrLike, nil) | |
1624 } else { | |
1625 So(actual, ShouldErrLike, expected) | |
1626 } | |
1627 return expected != "" | |
1628 } | |
1629 | |
1630 getPLSErr := func(obj interface{}) (pls PropertyLoadSaver, err error) { | 1652 getPLSErr := func(obj interface{}) (pls PropertyLoadSaver, err error) { |
1631 defer func() { | 1653 defer func() { |
1632 if v := recover(); v != nil { | 1654 if v := recover(); v != nil { |
1633 err = v.(error) | 1655 err = v.(error) |
1634 } | 1656 } |
1635 }() | 1657 }() |
1636 pls = GetPLS(obj) | 1658 pls = GetPLS(obj) |
1637 return | 1659 return |
1638 } | 1660 } |
1639 | 1661 |
1640 Convey("Test round-trip", t, func() { | 1662 Convey("Test round-trip", t, func() { |
1641 for _, tc := range testCases { | 1663 for _, tc := range testCases { |
1642 tc := tc | 1664 tc := tc |
1643 Convey(tc.desc, func() { | 1665 Convey(tc.desc, func() { |
1644 pls, ok := tc.src.(PropertyLoadSaver) | 1666 pls, ok := tc.src.(PropertyLoadSaver) |
1645 if !ok { | 1667 if !ok { |
1646 var err error | 1668 var err error |
1647 pls, err = getPLSErr(tc.src) | 1669 pls, err = getPLSErr(tc.src) |
1648 » » » » » if checkErr(err, tc.plsErr) { | 1670 » » » » » if tc.plsErr != "" { |
| 1671 » » » » » » So(err, ShouldErrLike, tc.plsErr
) |
1649 return | 1672 return |
1650 } | 1673 } |
1651 } | 1674 } |
1652 So(pls, ShouldNotBeNil) | 1675 So(pls, ShouldNotBeNil) |
1653 | 1676 |
1654 savedProps, err := pls.Save(false) | 1677 savedProps, err := pls.Save(false) |
1655 » » » » if checkErr(err, tc.saveErr) { | 1678 » » » » if tc.saveErr != "" { |
| 1679 » » » » » So(err, ShouldErrLike, tc.saveErr) |
1656 return | 1680 return |
1657 } | 1681 } |
1658 So(savedProps, ShouldNotBeNil) | 1682 So(savedProps, ShouldNotBeNil) |
1659 | 1683 |
1660 var got interface{} | 1684 var got interface{} |
1661 if _, ok := tc.want.(PropertyMap); ok { | 1685 if _, ok := tc.want.(PropertyMap); ok { |
1662 pls = PropertyMap{} | 1686 pls = PropertyMap{} |
1663 got = pls | 1687 got = pls |
1664 } else { | 1688 } else { |
1665 got = reflect.New(reflect.TypeOf(tc.want
).Elem()).Interface() | 1689 got = reflect.New(reflect.TypeOf(tc.want
).Elem()).Interface() |
1666 if pls, ok = got.(PropertyLoadSaver); !o
k { | 1690 if pls, ok = got.(PropertyLoadSaver); !o
k { |
1667 var err error | 1691 var err error |
1668 pls, err = getPLSErr(got) | 1692 pls, err = getPLSErr(got) |
1669 » » » » » » if checkErr(err, tc.plsLoadErr)
{ | 1693 » » » » » » if tc.plsLoadErr != "" { |
| 1694 » » » » » » » So(err, ShouldErrLike, t
c.plsLoadErr) |
1670 return | 1695 return |
1671 } | 1696 } |
1672 } | 1697 } |
1673 } | 1698 } |
1674 | 1699 |
1675 So(pls, ShouldNotBeNil) | 1700 So(pls, ShouldNotBeNil) |
1676 | 1701 |
1677 err = pls.Load(savedProps) | 1702 err = pls.Load(savedProps) |
1678 » » » » if checkErr(err, tc.loadErr) { | 1703 » » » » if tc.loadErr != "" { |
| 1704 » » » » » So(err, ShouldErrLike, tc.loadErr) |
1679 return | 1705 return |
1680 } | 1706 } |
1681 if tc.want == nil { | 1707 if tc.want == nil { |
1682 return | 1708 return |
1683 } | 1709 } |
1684 | 1710 |
1685 if gotT, ok := got.(*T); ok { | 1711 if gotT, ok := got.(*T); ok { |
1686 // Round tripping a time.Time can result
in a different time.Location: Local instead of UTC. | 1712 // Round tripping a time.Time can result
in a different time.Location: Local instead of UTC. |
1687 // We therefore test equality explicitly
, instead of relying on reflect.DeepEqual. | 1713 // We therefore test equality explicitly
, instead of relying on reflect.DeepEqual. |
1688 So(gotT.T.Equal(tc.want.(*T).T), ShouldB
eTrue) | 1714 So(gotT.T.Equal(tc.want.(*T).T), ShouldB
eTrue) |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1738 So(mgs.SetMeta("id", int64(200)), ShouldBeTrue) | 1764 So(mgs.SetMeta("id", int64(200)), ShouldBeTrue) |
1739 So(o.ID, ShouldEqual, 200) | 1765 So(o.ID, ShouldEqual, 200) |
1740 }) | 1766 }) |
1741 | 1767 |
1742 Convey("assigning to unsassiagnable fields returns !ok", func()
{ | 1768 Convey("assigning to unsassiagnable fields returns !ok", func()
{ |
1743 o := &N0{ID: 100} | 1769 o := &N0{ID: 100} |
1744 mgs := getMGS(o) | 1770 mgs := getMGS(o) |
1745 So(mgs.SetMeta("kind", "hi"), ShouldBeFalse) | 1771 So(mgs.SetMeta("kind", "hi"), ShouldBeFalse) |
1746 So(mgs.SetMeta("noob", "hi"), ShouldBeFalse) | 1772 So(mgs.SetMeta("noob", "hi"), ShouldBeFalse) |
1747 }) | 1773 }) |
| 1774 |
| 1775 Convey("unsigned int meta fields work", func() { |
| 1776 o := &N3{} |
| 1777 mgs := getMGS(o) |
| 1778 v, ok := mgs.GetMeta("id") |
| 1779 So(v, ShouldEqual, int64(200)) |
| 1780 So(ok, ShouldBeTrue) |
| 1781 |
| 1782 So(mgs.SetMeta("id", 20), ShouldBeTrue) |
| 1783 So(o.ID, ShouldEqual, 20) |
| 1784 |
| 1785 So(mgs.SetMeta("id", math.MaxInt64), ShouldBeFalse) |
| 1786 So(o.ID, ShouldEqual, 20) |
| 1787 |
| 1788 So(mgs.SetMeta("id", math.MaxUint32), ShouldBeTrue) |
| 1789 So(o.ID, ShouldEqual, math.MaxUint32) |
| 1790 }) |
1748 }) | 1791 }) |
1749 | 1792 |
1750 Convey("StructPLS Miscellaneous", t, func() { | 1793 Convey("StructPLS Miscellaneous", t, func() { |
1751 Convey("a simple struct has a default $kind", func() { | 1794 Convey("a simple struct has a default $kind", func() { |
1752 So(GetPLS(&Simple{}).GetAllMeta(), ShouldResemble, Prope
rtyMap{ | 1795 So(GetPLS(&Simple{}).GetAllMeta(), ShouldResemble, Prope
rtyMap{ |
1753 "$kind": []Property{mpNI("Simple")}, | 1796 "$kind": []Property{mpNI("Simple")}, |
1754 }) | 1797 }) |
1755 }) | 1798 }) |
1756 | 1799 |
1757 Convey("multiple overlapping fields is an error", func() { | 1800 Convey("multiple overlapping fields is an error", func() { |
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1953 So(pls.SetMeta("id", "sup|1337"), ShouldBeTrue) | 1996 So(pls.SetMeta("id", "sup|1337"), ShouldBeTrue) |
1954 So(ide.EmbeddedID, ShouldResemble, EmbeddedID{"sup", 133
7}) | 1997 So(ide.EmbeddedID, ShouldResemble, EmbeddedID{"sup", 133
7}) |
1955 | 1998 |
1956 So(pls.GetAllMeta(), ShouldResembleV, PropertyMap{ | 1999 So(pls.GetAllMeta(), ShouldResembleV, PropertyMap{ |
1957 "$id": {mpNI("sup|1337")}, | 2000 "$id": {mpNI("sup|1337")}, |
1958 "$kind": {mpNI("IDEmbedder")}, | 2001 "$kind": {mpNI("IDEmbedder")}, |
1959 }) | 2002 }) |
1960 }) | 2003 }) |
1961 }) | 2004 }) |
1962 } | 2005 } |
OLD | NEW |