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 package memory | 5 package memory |
6 | 6 |
7 import ( | 7 import ( |
8 "sort" | 8 "sort" |
9 "testing" | 9 "testing" |
10 "time" | 10 "time" |
11 | 11 |
12 ds "github.com/luci/gae/service/datastore" | 12 ds "github.com/luci/gae/service/datastore" |
| 13 "github.com/luci/gae/service/datastore/serialize" |
13 "github.com/luci/gkvlite" | 14 "github.com/luci/gkvlite" |
14 . "github.com/smartystreets/goconvey/convey" | 15 . "github.com/smartystreets/goconvey/convey" |
15 ) | 16 ) |
16 | 17 |
17 var fakeKey = key("parentKind", "sid", "knd", 10) | 18 var fakeKey = key("parentKind", "sid", "knd", 10) |
18 | 19 |
19 func TestCollated(t *testing.T) { | |
20 t.Parallel() | |
21 | |
22 Convey("TestCollated", t, func() { | |
23 Convey("nil list", func() { | |
24 pm := ds.PropertyMap(nil) | |
25 sip := partiallySerialize(fakeKey, pm) | |
26 | |
27 Convey("nil collated", func() { | |
28 Convey("defaultIndexes", func() { | |
29 idxs := defaultIndexes("knd", pm) | |
30 So(len(idxs), ShouldEqual, 1) | |
31 So(idxs[0].String(), ShouldEqual, "B:knd
") | |
32 }) | |
33 Convey("indexEntries", func() { | |
34 s := sip.indexEntries("ns", defaultIndex
es("knd", pm)) | |
35 numItems, _ := s.GetCollection("idx").Ge
tTotals() | |
36 So(numItems, ShouldEqual, 1) | |
37 itm := s.GetCollection("idx").MinItem(fa
lse) | |
38 So(itm.Key, ShouldResemble, cat(indx("kn
d").PrepForIdxTable())) | |
39 numItems, _ = s.GetCollection("idx:ns:"
+ string(itm.Key)).GetTotals() | |
40 So(numItems, ShouldEqual, 1) | |
41 }) | |
42 }) | |
43 }) | |
44 | |
45 Convey("list", func() { | |
46 pm := ds.PropertyMap{ | |
47 "wat": {propNI("thing"), prop("hat"), prop(100)
}, | |
48 "nerd": {prop(103.7)}, | |
49 "spaz": {propNI(false)}, | |
50 } | |
51 sip := partiallySerialize(fakeKey, pm) | |
52 So(len(sip), ShouldEqual, 4) | |
53 | |
54 Convey("single collated", func() { | |
55 Convey("indexableMap", func() { | |
56 So(sip, ShouldResemble, serializedIndexa
blePmap{ | |
57 "wat": { | |
58 cat(prop("hat")), | |
59 cat(prop(100)), | |
60 // 'thing' is skipped, b
ecause it's not NoIndex | |
61 }, | |
62 "nerd": { | |
63 cat(prop(103.7)), | |
64 }, | |
65 "__key__": { | |
66 cat(prop(fakeKey)), | |
67 }, | |
68 "__ancestor__": { | |
69 cat(prop(fakeKey)), | |
70 cat(prop(fakeKey.Parent(
))), | |
71 }, | |
72 }) | |
73 }) | |
74 Convey("defaultIndexes", func() { | |
75 idxs := defaultIndexes("knd", pm) | |
76 So(len(idxs), ShouldEqual, 5) | |
77 So(idxs[0].String(), ShouldEqual, "B:knd
") | |
78 So(idxs[1].String(), ShouldEqual, "B:knd
/nerd") | |
79 So(idxs[2].String(), ShouldEqual, "B:knd
/wat") | |
80 So(idxs[3].String(), ShouldEqual, "B:knd
/-nerd") | |
81 So(idxs[4].String(), ShouldEqual, "B:knd
/-wat") | |
82 }) | |
83 }) | |
84 }) | |
85 }) | |
86 } | |
87 | |
88 var rgenComplexTime = time.Date( | 20 var rgenComplexTime = time.Date( |
89 1986, time.October, 26, 1, 20, 00, 00, time.UTC) | 21 1986, time.October, 26, 1, 20, 00, 00, time.UTC) |
90 var rgenComplexKey = key("kind", "id") | 22 var rgenComplexKey = key("kind", "id") |
91 var rgenComplexTimeIdx = prop(rgenComplexTime).ForIndex() | 23 var rgenComplexTimeIdx = prop(rgenComplexTime).ForIndex() |
92 | 24 |
93 var rowGenTestCases = []struct { | 25 var rowGenTestCases = []struct { |
94 name string | 26 name string |
95 pmap ds.PropertyMap | 27 pmap ds.PropertyMap |
96 withBuiltin bool | 28 withBuiltin bool |
97 idxs []*ds.IndexDefinition | 29 idxs []*ds.IndexDefinition |
98 | 30 |
99 // These are checked in TestIndexRowGen. nil to skip test case. | 31 // These are checked in TestIndexRowGen. nil to skip test case. |
100 » expected []serializedPvals | 32 » expected []serialize.SerializedPslice |
101 | 33 |
102 // just the collections you want to assert. These are checked in | 34 // just the collections you want to assert. These are checked in |
103 // TestIndexEntries. nil to skip test case. | 35 // TestIndexEntries. nil to skip test case. |
104 collections map[string][][]byte | 36 collections map[string][][]byte |
105 }{ | 37 }{ |
106 | 38 |
107 { | 39 { |
108 name: "simple including builtins", | 40 name: "simple including builtins", |
109 pmap: ds.PropertyMap{ | 41 pmap: ds.PropertyMap{ |
110 "wat": {propNI("thing"), prop("hat"), prop(100)}, | 42 "wat": {propNI("thing"), prop("hat"), prop(100)}, |
111 "nerd": {prop(103.7)}, | 43 "nerd": {prop(103.7)}, |
112 "spaz": {propNI(false)}, | 44 "spaz": {propNI(false)}, |
113 }, | 45 }, |
114 withBuiltin: true, | 46 withBuiltin: true, |
115 idxs: []*ds.IndexDefinition{ | 47 idxs: []*ds.IndexDefinition{ |
116 indx("knd", "-wat", "nerd"), | 48 indx("knd", "-wat", "nerd"), |
117 }, | 49 }, |
118 » » expected: []serializedPvals{ | 50 » » expected: []serialize.SerializedPslice{ |
119 {cat(prop(fakeKey))}, // B:knd | 51 {cat(prop(fakeKey))}, // B:knd |
120 {cat(prop(103.7), prop(fakeKey))}, // B:knd/nerd | 52 {cat(prop(103.7), prop(fakeKey))}, // B:knd/nerd |
121 { // B:knd/wat | 53 { // B:knd/wat |
122 cat(prop(100), prop(fakeKey)), | 54 cat(prop(100), prop(fakeKey)), |
123 cat(prop("hat"), prop(fakeKey)), | 55 cat(prop("hat"), prop(fakeKey)), |
124 }, | 56 }, |
125 { // B:knd/-nerd | 57 { // B:knd/-nerd |
126 cat(icat(prop(103.7)), prop(fakeKey)), | 58 cat(icat(prop(103.7)), prop(fakeKey)), |
127 }, | 59 }, |
128 { // B:knd/-wat | 60 { // B:knd/-wat |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
165 "wat": { | 97 "wat": { |
166 prop(rgenComplexTime), | 98 prop(rgenComplexTime), |
167 prop([]byte("value")), | 99 prop([]byte("value")), |
168 prop(rgenComplexKey)}, | 100 prop(rgenComplexKey)}, |
169 "spaz": {prop(nil), prop(false), prop(true)}, | 101 "spaz": {prop(nil), prop(false), prop(true)}, |
170 }, | 102 }, |
171 idxs: []*ds.IndexDefinition{ | 103 idxs: []*ds.IndexDefinition{ |
172 indx("knd", "-wat", "nerd", "spaz"), // doesn't match, s
o empty | 104 indx("knd", "-wat", "nerd", "spaz"), // doesn't match, s
o empty |
173 indx("knd", "yerp", "-wat", "spaz"), | 105 indx("knd", "yerp", "-wat", "spaz"), |
174 }, | 106 }, |
175 » » expected: []serializedPvals{ | 107 » » expected: []serialize.SerializedPslice{ |
176 {}, // C:knd/-wat/nerd/spaz, no match | 108 {}, // C:knd/-wat/nerd/spaz, no match |
177 { // C:knd/yerp/-wat/spaz | 109 { // C:knd/yerp/-wat/spaz |
178 // thank goodness the binary serialization only
happens 1/val in the | 110 // thank goodness the binary serialization only
happens 1/val in the |
179 // real code :). | 111 // real code :). |
180 cat(prop("hat"), icat(prop(rgenComplexKey)), pro
p(nil), prop(fakeKey)), | 112 cat(prop("hat"), icat(prop(rgenComplexKey)), pro
p(nil), prop(fakeKey)), |
181 cat(prop("hat"), icat(prop(rgenComplexKey)), pro
p(false), prop(fakeKey)), | 113 cat(prop("hat"), icat(prop(rgenComplexKey)), pro
p(false), prop(fakeKey)), |
182 cat(prop("hat"), icat(prop(rgenComplexKey)), pro
p(true), prop(fakeKey)), | 114 cat(prop("hat"), icat(prop(rgenComplexKey)), pro
p(true), prop(fakeKey)), |
183 cat(prop("hat"), icat(prop("value")), prop(nil),
prop(fakeKey)), | 115 cat(prop("hat"), icat(prop("value")), prop(nil),
prop(fakeKey)), |
184 cat(prop("hat"), icat(prop("value")), prop(false
), prop(fakeKey)), | 116 cat(prop("hat"), icat(prop("value")), prop(false
), prop(fakeKey)), |
185 cat(prop("hat"), icat(prop("value")), prop(true)
, prop(fakeKey)), | 117 cat(prop("hat"), icat(prop("value")), prop(true)
, prop(fakeKey)), |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 t.Parallel() | 153 t.Parallel() |
222 | 154 |
223 Convey("Test Index Row Generation", t, func() { | 155 Convey("Test Index Row Generation", t, func() { |
224 for _, tc := range rowGenTestCases { | 156 for _, tc := range rowGenTestCases { |
225 if tc.expected == nil { | 157 if tc.expected == nil { |
226 Convey(tc.name, nil) // shows up as 'skipped' | 158 Convey(tc.name, nil) // shows up as 'skipped' |
227 continue | 159 continue |
228 } | 160 } |
229 | 161 |
230 Convey(tc.name, func() { | 162 Convey(tc.name, func() { |
231 » » » » mvals := partiallySerialize(fakeKey, tc.pmap) | 163 » » » » mvals := serialize.PropertyMapPartially(fakeKey,
tc.pmap) |
232 idxs := []*ds.IndexDefinition(nil) | 164 idxs := []*ds.IndexDefinition(nil) |
233 if tc.withBuiltin { | 165 if tc.withBuiltin { |
234 idxs = append(defaultIndexes("coolKind",
tc.pmap), tc.idxs...) | 166 idxs = append(defaultIndexes("coolKind",
tc.pmap), tc.idxs...) |
235 } else { | 167 } else { |
236 idxs = tc.idxs | 168 idxs = tc.idxs |
237 } | 169 } |
238 | 170 |
239 m := matcher{} | 171 m := matcher{} |
240 for i, idx := range idxs { | 172 for i, idx := range idxs { |
241 Convey(idx.String(), func() { | 173 Convey(idx.String(), func() { |
242 iGen, ok := m.match(idx.GetFullS
ortOrder(), mvals) | 174 iGen, ok := m.match(idx.GetFullS
ortOrder(), mvals) |
243 if len(tc.expected[i]) > 0 { | 175 if len(tc.expected[i]) > 0 { |
244 So(ok, ShouldBeTrue) | 176 So(ok, ShouldBeTrue) |
245 » » » » » » » actual := make(serialize
dPvals, 0, len(tc.expected[i])) | 177 » » » » » » » actual := make(serialize
.SerializedPslice, 0, len(tc.expected[i])) |
246 iGen.permute(func(row, _
[]byte) { | 178 iGen.permute(func(row, _
[]byte) { |
247 actual = append(
actual, row) | 179 actual = append(
actual, row) |
248 }) | 180 }) |
249 So(len(actual), ShouldEq
ual, len(tc.expected[i])) | 181 So(len(actual), ShouldEq
ual, len(tc.expected[i])) |
250 sort.Sort(actual) | 182 sort.Sort(actual) |
251 for j, act := range actu
al { | 183 for j, act := range actu
al { |
252 So(act, ShouldRe
semble, tc.expected[i][j]) | 184 So(act, ShouldRe
semble, tc.expected[i][j]) |
253 } | 185 } |
254 } else { | 186 } else { |
255 So(ok, ShouldBeFalse) | 187 So(ok, ShouldBeFalse) |
256 } | 188 } |
257 }) | 189 }) |
258 } | 190 } |
259 }) | 191 }) |
260 } | 192 } |
261 }) | 193 }) |
| 194 |
| 195 Convey("default indexes", t, func() { |
| 196 Convey("nil collated", func() { |
| 197 Convey("defaultIndexes (nil)", func() { |
| 198 idxs := defaultIndexes("knd", ds.PropertyMap(nil
)) |
| 199 So(len(idxs), ShouldEqual, 1) |
| 200 So(idxs[0].String(), ShouldEqual, "B:knd") |
| 201 }) |
| 202 |
| 203 Convey("indexEntries", func() { |
| 204 sip := serialize.PropertyMapPartially(fakeKey, n
il) |
| 205 s := indexEntries(sip, "ns", defaultIndexes("knd
", ds.PropertyMap(nil))) |
| 206 numItems, _ := s.GetCollection("idx").GetTotals(
) |
| 207 So(numItems, ShouldEqual, 1) |
| 208 itm := s.GetCollection("idx").MinItem(false) |
| 209 So(itm.Key, ShouldResemble, cat(indx("knd").Prep
ForIdxTable())) |
| 210 numItems, _ = s.GetCollection("idx:ns:" + string
(itm.Key)).GetTotals() |
| 211 So(numItems, ShouldEqual, 1) |
| 212 }) |
| 213 |
| 214 Convey("defaultIndexes", func() { |
| 215 pm := ds.PropertyMap{ |
| 216 "wat": {propNI("thing"), prop("hat"), p
rop(100)}, |
| 217 "nerd": {prop(103.7)}, |
| 218 "spaz": {propNI(false)}, |
| 219 } |
| 220 idxs := defaultIndexes("knd", pm) |
| 221 So(len(idxs), ShouldEqual, 5) |
| 222 So(idxs[0].String(), ShouldEqual, "B:knd") |
| 223 So(idxs[1].String(), ShouldEqual, "B:knd/nerd") |
| 224 So(idxs[2].String(), ShouldEqual, "B:knd/wat") |
| 225 So(idxs[3].String(), ShouldEqual, "B:knd/-nerd") |
| 226 So(idxs[4].String(), ShouldEqual, "B:knd/-wat") |
| 227 }) |
| 228 |
| 229 }) |
| 230 }) |
262 } | 231 } |
263 | 232 |
264 func TestIndexEntries(t *testing.T) { | 233 func TestIndexEntries(t *testing.T) { |
265 t.Parallel() | 234 t.Parallel() |
266 | 235 |
267 Convey("Test indexEntriesWithBuiltins", t, func() { | 236 Convey("Test indexEntriesWithBuiltins", t, func() { |
268 for _, tc := range rowGenTestCases { | 237 for _, tc := range rowGenTestCases { |
269 if tc.collections == nil { | 238 if tc.collections == nil { |
270 Convey(tc.name, nil) // shows up as 'skipped' | 239 Convey(tc.name, nil) // shows up as 'skipped' |
271 continue | 240 continue |
272 } | 241 } |
273 | 242 |
274 Convey(tc.name, func() { | 243 Convey(tc.name, func() { |
275 store := (*memStore)(nil) | 244 store := (*memStore)(nil) |
276 if tc.withBuiltin { | 245 if tc.withBuiltin { |
277 store = indexEntriesWithBuiltins(fakeKey
, tc.pmap, tc.idxs) | 246 store = indexEntriesWithBuiltins(fakeKey
, tc.pmap, tc.idxs) |
278 } else { | 247 } else { |
279 » » » » » store = partiallySerialize(fakeKey, tc.p
map).indexEntries(fakeKey.Namespace(), tc.idxs) | 248 » » » » » sip := serialize.PropertyMapPartially(fa
keKey, tc.pmap) |
| 249 » » » » » store = indexEntries(sip, fakeKey.Namesp
ace(), tc.idxs) |
280 } | 250 } |
281 for colName, vals := range tc.collections { | 251 for colName, vals := range tc.collections { |
282 i := 0 | 252 i := 0 |
283 coll := store.GetCollection(colName) | 253 coll := store.GetCollection(colName) |
284 numItems, _ := coll.GetTotals() | 254 numItems, _ := coll.GetTotals() |
285 So(numItems, ShouldEqual, len(tc.collect
ions[colName])) | 255 So(numItems, ShouldEqual, len(tc.collect
ions[colName])) |
286 coll.VisitItemsAscend(nil, true, func(it
m *gkvlite.Item) bool { | 256 coll.VisitItemsAscend(nil, true, func(it
m *gkvlite.Item) bool { |
287 So(itm.Key, ShouldResemble, vals
[i]) | 257 So(itm.Key, ShouldResemble, vals
[i]) |
288 i++ | 258 i++ |
289 return true | 259 return true |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
404 So(data[i], ShouldResemble, itm.
Key) | 374 So(data[i], ShouldResemble, itm.
Key) |
405 i++ | 375 i++ |
406 return true | 376 return true |
407 }) | 377 }) |
408 So(i, ShouldEqual, len(data)) | 378 So(i, ShouldEqual, len(data)) |
409 } | 379 } |
410 }) | 380 }) |
411 } | 381 } |
412 }) | 382 }) |
413 } | 383 } |
OLD | NEW |