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 587 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
768 src: &K1{[]*Key{testKey1a, nil, testKey2a}}, | 776 src: &K1{[]*Key{testKey1a, nil, testKey2a}}, |
769 want: &K1{[]*Key{testKey1b, nil, testKey2b}}, | 777 want: &K1{[]*Key{testKey1b, nil, testKey2b}}, |
770 }, | 778 }, |
771 { | 779 { |
772 desc: "overflow", | 780 desc: "overflow", |
773 src: &O0{I: 1 << 48}, | 781 src: &O0{I: 1 << 48}, |
774 want: &O1{}, | 782 want: &O1{}, |
775 loadErr: "overflow", | 783 loadErr: "overflow", |
776 }, | 784 }, |
777 { | 785 { |
| 786 desc: "underflow", |
| 787 src: &O0{I: math.MaxInt64}, |
| 788 want: &O1{}, |
| 789 loadErr: "overflow", |
| 790 }, |
| 791 { |
778 desc: "time", | 792 desc: "time", |
779 src: &T{T: time.Unix(1e9, 0).UTC()}, | 793 src: &T{T: time.Unix(1e9, 0).UTC()}, |
780 want: &T{T: time.Unix(1e9, 0).UTC()}, | 794 want: &T{T: time.Unix(1e9, 0).UTC()}, |
781 }, | 795 }, |
782 { | 796 { |
783 desc: "time as props", | 797 desc: "time as props", |
784 src: &T{T: time.Unix(1e9, 0).UTC()}, | 798 src: &T{T: time.Unix(1e9, 0).UTC()}, |
785 want: PropertyMap{ | 799 want: PropertyMap{ |
786 "T": {mp(time.Unix(1e9, 0).UTC())}, | 800 "T": {mp(time.Unix(1e9, 0).UTC())}, |
787 }, | 801 }, |
788 }, | 802 }, |
789 { | 803 { |
790 » » desc: "uint save", | 804 » » desc: "uint32 save", |
791 » » src: &U0{U: 1}, | 805 » » src: &U0{U: 1}, |
792 » » plsErr: `field "U" has invalid type: uint`, | 806 » » want: PropertyMap{ |
| 807 » » » "U": {mp(1)}, |
| 808 » » }, |
793 }, | 809 }, |
794 { | 810 { |
795 » » desc: "uint load", | 811 » » desc: "uint32 load", |
796 » » src: &U1{U: "not a uint"}, | 812 » » src: &U2{U: 100}, |
797 » » want: &U0{}, | 813 » » want: &U0{U: 100}, |
798 » » plsLoadErr: `field "U" has invalid type: uint`, | 814 » }, |
| 815 » { |
| 816 » » desc: "uint32 load oob (neg)", |
| 817 » » src: &U2{U: -1}, |
| 818 » » want: &U0{}, |
| 819 » » loadErr: "overflow", |
| 820 » }, |
| 821 » { |
| 822 » » desc: "uint32 load oob (huge)", |
| 823 » » src: &U2{U: math.MaxInt64}, |
| 824 » » want: &U0{}, |
| 825 » » loadErr: "overflow", |
| 826 » }, |
| 827 » { |
| 828 » » desc: "byte save", |
| 829 » » src: &U1{U: 1}, |
| 830 » » want: PropertyMap{ |
| 831 » » » "U": {mp(1)}, |
| 832 » » }, |
| 833 » }, |
| 834 » { |
| 835 » » desc: "byte load", |
| 836 » » src: &U2{U: 100}, |
| 837 » » want: &U1{U: 100}, |
| 838 » }, |
| 839 » { |
| 840 » » desc: "byte load oob (neg)", |
| 841 » » src: &U2{U: -1}, |
| 842 » » want: &U1{}, |
| 843 » » loadErr: "overflow", |
| 844 » }, |
| 845 » { |
| 846 » » desc: "byte load oob (huge)", |
| 847 » » src: &U2{U: math.MaxInt64}, |
| 848 » » want: &U1{}, |
| 849 » » loadErr: "overflow", |
799 }, | 850 }, |
800 { | 851 { |
801 desc: "zero", | 852 desc: "zero", |
802 src: &X0{}, | 853 src: &X0{}, |
803 want: &X0{}, | 854 want: &X0{}, |
804 }, | 855 }, |
805 { | 856 { |
806 desc: "basic", | 857 desc: "basic", |
807 src: &X0{S: "one", I: 2, i: 3}, | 858 src: &X0{S: "one", I: 2, i: 3}, |
808 want: &X0{S: "one", I: 2}, | 859 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", | 1645 desc: "json.RawMessage to myBlob", |
1595 src: &struct { | 1646 src: &struct { |
1596 B json.RawMessage | 1647 B json.RawMessage |
1597 }{ | 1648 }{ |
1598 B: json.RawMessage("rawr"), | 1649 B: json.RawMessage("rawr"), |
1599 }, | 1650 }, |
1600 want: &B2{B: myBlob("rawr")}, | 1651 want: &B2{B: myBlob("rawr")}, |
1601 }, | 1652 }, |
1602 } | 1653 } |
1603 | 1654 |
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) { | 1655 func TestRoundTrip(t *testing.T) { |
1619 t.Parallel() | 1656 t.Parallel() |
1620 | 1657 |
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) { | 1658 getPLSErr := func(obj interface{}) (pls PropertyLoadSaver, err error) { |
1631 defer func() { | 1659 defer func() { |
1632 if v := recover(); v != nil { | 1660 if v := recover(); v != nil { |
1633 err = v.(error) | 1661 err = v.(error) |
1634 } | 1662 } |
1635 }() | 1663 }() |
1636 pls = GetPLS(obj) | 1664 pls = GetPLS(obj) |
1637 return | 1665 return |
1638 } | 1666 } |
1639 | 1667 |
1640 Convey("Test round-trip", t, func() { | 1668 Convey("Test round-trip", t, func() { |
1641 for _, tc := range testCases { | 1669 for _, tc := range testCases { |
1642 tc := tc | 1670 tc := tc |
1643 Convey(tc.desc, func() { | 1671 Convey(tc.desc, func() { |
1644 pls, ok := tc.src.(PropertyLoadSaver) | 1672 pls, ok := tc.src.(PropertyLoadSaver) |
1645 if !ok { | 1673 if !ok { |
1646 var err error | 1674 var err error |
1647 pls, err = getPLSErr(tc.src) | 1675 pls, err = getPLSErr(tc.src) |
1648 » » » » » if checkErr(err, tc.plsErr) { | 1676 » » » » » if tc.plsErr != "" { |
| 1677 » » » » » » So(err, ShouldErrLike, tc.plsErr
) |
1649 return | 1678 return |
1650 } | 1679 } |
1651 } | 1680 } |
1652 So(pls, ShouldNotBeNil) | 1681 So(pls, ShouldNotBeNil) |
1653 | 1682 |
1654 savedProps, err := pls.Save(false) | 1683 savedProps, err := pls.Save(false) |
1655 » » » » if checkErr(err, tc.saveErr) { | 1684 » » » » if tc.saveErr != "" { |
| 1685 » » » » » So(err, ShouldErrLike, tc.saveErr) |
1656 return | 1686 return |
1657 } | 1687 } |
1658 So(savedProps, ShouldNotBeNil) | 1688 So(savedProps, ShouldNotBeNil) |
1659 | 1689 |
1660 var got interface{} | 1690 var got interface{} |
1661 if _, ok := tc.want.(PropertyMap); ok { | 1691 if _, ok := tc.want.(PropertyMap); ok { |
1662 pls = PropertyMap{} | 1692 pls = PropertyMap{} |
1663 got = pls | 1693 got = pls |
1664 } else { | 1694 } else { |
1665 got = reflect.New(reflect.TypeOf(tc.want
).Elem()).Interface() | 1695 got = reflect.New(reflect.TypeOf(tc.want
).Elem()).Interface() |
1666 if pls, ok = got.(PropertyLoadSaver); !o
k { | 1696 if pls, ok = got.(PropertyLoadSaver); !o
k { |
1667 var err error | 1697 var err error |
1668 pls, err = getPLSErr(got) | 1698 pls, err = getPLSErr(got) |
1669 » » » » » » if checkErr(err, tc.plsLoadErr)
{ | 1699 » » » » » » if tc.plsLoadErr != "" { |
| 1700 » » » » » » » So(err, ShouldErrLike, t
c.plsLoadErr) |
1670 return | 1701 return |
1671 } | 1702 } |
1672 } | 1703 } |
1673 } | 1704 } |
1674 | 1705 |
1675 So(pls, ShouldNotBeNil) | 1706 So(pls, ShouldNotBeNil) |
1676 | 1707 |
1677 err = pls.Load(savedProps) | 1708 err = pls.Load(savedProps) |
1678 » » » » if checkErr(err, tc.loadErr) { | 1709 » » » » if tc.loadErr != "" { |
| 1710 » » » » » So(err, ShouldErrLike, tc.loadErr) |
1679 return | 1711 return |
1680 } | 1712 } |
1681 if tc.want == nil { | 1713 if tc.want == nil { |
1682 return | 1714 return |
1683 } | 1715 } |
1684 | 1716 |
1685 if gotT, ok := got.(*T); ok { | 1717 if gotT, ok := got.(*T); ok { |
1686 // Round tripping a time.Time can result
in a different time.Location: Local instead of UTC. | 1718 // 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. | 1719 // We therefore test equality explicitly
, instead of relying on reflect.DeepEqual. |
1688 So(gotT.T.Equal(tc.want.(*T).T), ShouldB
eTrue) | 1720 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) | 1770 So(mgs.SetMeta("id", int64(200)), ShouldBeTrue) |
1739 So(o.ID, ShouldEqual, 200) | 1771 So(o.ID, ShouldEqual, 200) |
1740 }) | 1772 }) |
1741 | 1773 |
1742 Convey("assigning to unsassiagnable fields returns !ok", func()
{ | 1774 Convey("assigning to unsassiagnable fields returns !ok", func()
{ |
1743 o := &N0{ID: 100} | 1775 o := &N0{ID: 100} |
1744 mgs := getMGS(o) | 1776 mgs := getMGS(o) |
1745 So(mgs.SetMeta("kind", "hi"), ShouldBeFalse) | 1777 So(mgs.SetMeta("kind", "hi"), ShouldBeFalse) |
1746 So(mgs.SetMeta("noob", "hi"), ShouldBeFalse) | 1778 So(mgs.SetMeta("noob", "hi"), ShouldBeFalse) |
1747 }) | 1779 }) |
| 1780 |
| 1781 Convey("unsigned int meta fields work", func() { |
| 1782 o := &N3{} |
| 1783 mgs := getMGS(o) |
| 1784 v, ok := mgs.GetMeta("id") |
| 1785 So(v, ShouldEqual, int64(200)) |
| 1786 So(ok, ShouldBeTrue) |
| 1787 |
| 1788 So(mgs.SetMeta("id", 20), ShouldBeTrue) |
| 1789 So(o.ID, ShouldEqual, 20) |
| 1790 |
| 1791 So(mgs.SetMeta("id", math.MaxInt64), ShouldBeFalse) |
| 1792 So(o.ID, ShouldEqual, 20) |
| 1793 |
| 1794 So(mgs.SetMeta("id", math.MaxUint32), ShouldBeTrue) |
| 1795 So(o.ID, ShouldEqual, math.MaxUint32) |
| 1796 }) |
1748 }) | 1797 }) |
1749 | 1798 |
1750 Convey("StructPLS Miscellaneous", t, func() { | 1799 Convey("StructPLS Miscellaneous", t, func() { |
1751 Convey("a simple struct has a default $kind", func() { | 1800 Convey("a simple struct has a default $kind", func() { |
1752 So(GetPLS(&Simple{}).GetAllMeta(), ShouldResemble, Prope
rtyMap{ | 1801 So(GetPLS(&Simple{}).GetAllMeta(), ShouldResemble, Prope
rtyMap{ |
1753 "$kind": []Property{mpNI("Simple")}, | 1802 "$kind": []Property{mpNI("Simple")}, |
1754 }) | 1803 }) |
1755 }) | 1804 }) |
1756 | 1805 |
1757 Convey("multiple overlapping fields is an error", func() { | 1806 Convey("multiple overlapping fields is an error", func() { |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1855 v, ok := mgs.GetMeta("val") | 1904 v, ok := mgs.GetMeta("val") |
1856 So(ok, ShouldBeTrue) | 1905 So(ok, ShouldBeTrue) |
1857 So(v, ShouldEqual, int64(100)) | 1906 So(v, ShouldEqual, int64(100)) |
1858 | 1907 |
1859 o.Val = 10 | 1908 o.Val = 10 |
1860 v, ok = mgs.GetMeta("val") | 1909 v, ok = mgs.GetMeta("val") |
1861 So(ok, ShouldBeTrue) | 1910 So(ok, ShouldBeTrue) |
1862 So(v, ShouldEqual, int64(10)) | 1911 So(v, ShouldEqual, int64(10)) |
1863 }) | 1912 }) |
1864 | 1913 |
| 1914 Convey("underflow", func() { |
| 1915 type UnderflowMeta struct { |
| 1916 ID int16 `gae:"$id"` |
| 1917 } |
| 1918 um := &UnderflowMeta{} |
| 1919 mgs := getMGS(um) |
| 1920 So(mgs.SetMeta("id", -20), ShouldBeTrue) |
| 1921 So(mgs.SetMeta("id", math.MinInt64), ShouldBeFalse) |
| 1922 }) |
| 1923 |
| 1924 Convey("negative default", func() { |
| 1925 type UnderflowMeta struct { |
| 1926 ID int16 `gae:"$id,-30"` |
| 1927 } |
| 1928 um := &UnderflowMeta{} |
| 1929 mgs := getMGS(um) |
| 1930 val, ok := mgs.GetMeta("id") |
| 1931 So(ok, ShouldBeTrue) |
| 1932 So(val, ShouldEqual, -30) |
| 1933 }) |
| 1934 |
1865 Convey("Derived metadata fields", func() { | 1935 Convey("Derived metadata fields", func() { |
1866 type DerivedString string | 1936 type DerivedString string |
1867 type DerivedInt int16 | 1937 type DerivedInt int16 |
1868 type DerivedStruct struct { | 1938 type DerivedStruct struct { |
1869 ID DerivedString `gae:"$id"` | 1939 ID DerivedString `gae:"$id"` |
1870 Foo DerivedInt `gae:"$foo"` | 1940 Foo DerivedInt `gae:"$foo"` |
1871 } | 1941 } |
1872 o := &DerivedStruct{"hello", 10} | 1942 o := &DerivedStruct{"hello", 10} |
1873 mgs := getMGS(o) | 1943 mgs := getMGS(o) |
1874 v, ok := mgs.GetMeta("id") | 1944 v, ok := mgs.GetMeta("id") |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1953 So(pls.SetMeta("id", "sup|1337"), ShouldBeTrue) | 2023 So(pls.SetMeta("id", "sup|1337"), ShouldBeTrue) |
1954 So(ide.EmbeddedID, ShouldResemble, EmbeddedID{"sup", 133
7}) | 2024 So(ide.EmbeddedID, ShouldResemble, EmbeddedID{"sup", 133
7}) |
1955 | 2025 |
1956 So(pls.GetAllMeta(), ShouldResembleV, PropertyMap{ | 2026 So(pls.GetAllMeta(), ShouldResembleV, PropertyMap{ |
1957 "$id": {mpNI("sup|1337")}, | 2027 "$id": {mpNI("sup|1337")}, |
1958 "$kind": {mpNI("IDEmbedder")}, | 2028 "$kind": {mpNI("IDEmbedder")}, |
1959 }) | 2029 }) |
1960 }) | 2030 }) |
1961 }) | 2031 }) |
1962 } | 2032 } |
OLD | NEW |