OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 package memory | |
6 | |
7 import ( | |
8 "testing" | |
9 "time" | |
10 | |
11 ds "github.com/luci/gae/service/datastore" | |
12 "github.com/luci/gkvlite" | |
13 . "github.com/smartystreets/goconvey/convey" | |
14 ) | |
15 | |
16 func init() { | |
17 indexCreationDeterministic = true | |
18 } | |
19 | |
20 var fakeKey = key("knd", 10, key("parentKind", "sid")) | |
21 | |
22 func TestCollated(t *testing.T) { | |
23 t.Parallel() | |
24 | |
25 Convey("TestCollated", t, func() { | |
26 Convey("nil list", func() { | |
27 pm := (ds.PropertyMap)(nil) | |
28 sip := partiallySerialize(pm) | |
29 So(sip, ShouldBeNil) | |
30 | |
31 Convey("nil collated", func() { | |
32 Convey("defaultIndicies", func() { | |
33 idxs := defaultIndicies("knd", pm) | |
34 So(len(idxs), ShouldEqual, 1) | |
35 So(idxs[0].String(), ShouldEqual, "B:knd
") | |
36 }) | |
37 Convey("indexEntries", func() { | |
38 s := sip.indexEntries(fakeKey, defaultIn
dicies("knd", pm)) | |
39 numItems, _ := s.GetCollection("idx").Ge
tTotals() | |
40 So(numItems, ShouldEqual, 1) | |
41 itm := s.GetCollection("idx").MinItem(fa
lse) | |
42 So(itm.Key, ShouldResemble, cat(indx("kn
d"))) | |
43 numItems, _ = s.GetCollection("idx:ns:"
+ string(itm.Key)).GetTotals() | |
44 So(numItems, ShouldEqual, 1) | |
45 }) | |
46 }) | |
47 }) | |
48 | |
49 Convey("list", func() { | |
50 pm := ds.PropertyMap{ | |
51 "wat": {propNI("thing"), prop("hat"), prop(100)
}, | |
52 "nerd": {prop(103.7)}, | |
53 "spaz": {propNI(false)}, | |
54 } | |
55 sip := partiallySerialize(pm) | |
56 So(len(sip), ShouldEqual, 2) | |
57 | |
58 Convey("single collated", func() { | |
59 Convey("indexableMap", func() { | |
60 So(sip, ShouldResemble, serializedIndexa
blePmap{ | |
61 "wat": { | |
62 cat(ds.PTInt, 100), | |
63 cat(ds.PTString, "hat"), | |
64 // 'thing' is skipped, b
ecause it's not NoIndex | |
65 }, | |
66 "nerd": { | |
67 cat(ds.PTFloat, 103.7), | |
68 }, | |
69 }) | |
70 }) | |
71 Convey("defaultIndicies", func() { | |
72 idxs := defaultIndicies("knd", pm) | |
73 So(len(idxs), ShouldEqual, 5) | |
74 So(idxs[0].String(), ShouldEqual, "B:knd
") | |
75 So(idxs[1].String(), ShouldEqual, "B:knd
/-nerd") | |
76 So(idxs[2].String(), ShouldEqual, "B:knd
/-wat") | |
77 So(idxs[3].String(), ShouldEqual, "B:knd
/nerd") | |
78 So(idxs[4].String(), ShouldEqual, "B:knd
/wat") | |
79 }) | |
80 }) | |
81 }) | |
82 }) | |
83 } | |
84 | |
85 var rgenComplexTime = time.Date( | |
86 1986, time.October, 26, 1, 20, 00, 00, time.UTC) | |
87 var rgenComplexKey = key("kind", "id") | |
88 | |
89 var rowGenTestCases = []struct { | |
90 name string | |
91 pmap ds.PropertyMap | |
92 withBuiltin bool | |
93 idxs []*qIndex | |
94 | |
95 // These are checked in TestIndexRowGen. nil to skip test case. | |
96 expected []serializedPvals | |
97 | |
98 // just the collections you want to assert. These are checked in | |
99 // TestIndexEntries. nil to skip test case. | |
100 collections map[string][]kv | |
101 }{ | |
102 { | |
103 name: "simple including builtins", | |
104 pmap: ds.PropertyMap{ | |
105 "wat": {propNI("thing"), prop("hat"), prop(100)}, | |
106 "nerd": {prop(103.7)}, | |
107 "spaz": {propNI(false)}, | |
108 }, | |
109 withBuiltin: true, | |
110 idxs: []*qIndex{ | |
111 indx("knd", "-wat", "nerd"), | |
112 }, | |
113 expected: []serializedPvals{ | |
114 {{}}, // B:knd | |
115 {icat(ds.PTFloat, 103.7)}, // B:kn
d/-nerd | |
116 {icat(ds.PTString, "hat"), icat(ds.PTInt, 100)}, // B:kn
d/-wat | |
117 {cat(ds.PTFloat, 103.7)}, // B:kn
d/nerd | |
118 {cat(ds.PTInt, 100), cat(ds.PTString, "hat")}, // B:kn
d/wat | |
119 { // B:knd/-wat/nerd | |
120 cat(icat(ds.PTString, "hat"), cat(ds.PTFloat, 10
3.7)), | |
121 cat(icat(ds.PTInt, 100), cat(ds.PTFloat, 103.7))
, | |
122 }, | |
123 }, | |
124 collections: map[string][]kv{ | |
125 "idx": { | |
126 // 0 == builtin, 1 == complex | |
127 {cat(byte(0), "knd", byte(1), 0), []byte{}}, | |
128 {cat(byte(0), "knd", byte(1), 1, byte(0), "nerd"
), []byte{}}, | |
129 {cat(byte(0), "knd", byte(1), 1, byte(0), "wat")
, []byte{}}, | |
130 {cat(byte(0), "knd", byte(1), 1, byte(1), "nerd"
), []byte{}}, | |
131 {cat(byte(0), "knd", byte(1), 1, byte(1), "wat")
, []byte{}}, | |
132 {cat(byte(1), "knd", byte(1), 2, byte(1), "wat",
byte(0), "nerd"), []byte{}}, | |
133 }, | |
134 "idx:ns:" + sat(indx("knd")): { | |
135 {cat(fakeKey), []byte{}}, | |
136 }, | |
137 "idx:ns:" + sat(indx("knd", "wat")): { | |
138 {cat(ds.PTInt, 100, fakeKey), []byte{}}, | |
139 {cat(ds.PTString, "hat", fakeKey), cat(ds.PTInt,
100)}, | |
140 }, | |
141 "idx:ns:" + sat(indx("knd", "-wat")): { | |
142 {cat(icat(ds.PTString, "hat"), fakeKey), []byte{
}}, | |
143 {cat(icat(ds.PTInt, 100), fakeKey), icat(ds.PTSt
ring, "hat")}, | |
144 }, | |
145 }, | |
146 }, | |
147 { | |
148 name: "complex", | |
149 pmap: ds.PropertyMap{ | |
150 "yerp": {prop("hat"), prop(73.9)}, | |
151 "wat": { | |
152 prop(rgenComplexTime), | |
153 prop(ds.ByteString("value")), | |
154 prop(rgenComplexKey)}, | |
155 "spaz": {prop(nil), prop(false), prop(true)}, | |
156 }, | |
157 idxs: []*qIndex{ | |
158 indx("knd", "-wat", "nerd", "spaz"), // doesn't match, s
o empty | |
159 indx("knd", "yerp", "-wat", "spaz"), | |
160 }, | |
161 expected: []serializedPvals{ | |
162 {}, // C:knd/-wat/nerd/spaz, no match | |
163 { // C:knd/yerp/-wat/spaz | |
164 // thank goodness the binary serialization only
happens 1/val in the | |
165 // real code :). | |
166 cat(cat(ds.PTString, "hat"), icat(ds.PTKey, rgen
ComplexKey), cat(ds.PTNull)), | |
167 cat(cat(ds.PTString, "hat"), icat(ds.PTKey, rgen
ComplexKey), cat(ds.PTBoolFalse)), | |
168 cat(cat(ds.PTString, "hat"), icat(ds.PTKey, rgen
ComplexKey), cat(ds.PTBoolTrue)), | |
169 cat(cat(ds.PTString, "hat"), icat(ds.PTBytes, "v
alue"), cat(ds.PTNull)), | |
170 cat(cat(ds.PTString, "hat"), icat(ds.PTBytes, "v
alue"), cat(ds.PTBoolFalse)), | |
171 cat(cat(ds.PTString, "hat"), icat(ds.PTBytes, "v
alue"), cat(ds.PTBoolTrue)), | |
172 cat(cat(ds.PTString, "hat"), icat(ds.PTTime, rge
nComplexTime), cat(ds.PTNull)), | |
173 cat(cat(ds.PTString, "hat"), icat(ds.PTTime, rge
nComplexTime), cat(ds.PTBoolFalse)), | |
174 cat(cat(ds.PTString, "hat"), icat(ds.PTTime, rge
nComplexTime), cat(ds.PTBoolTrue)), | |
175 | |
176 cat(cat(ds.PTFloat, 73.9), icat(ds.PTKey, rgenCo
mplexKey), cat(ds.PTNull)), | |
177 cat(cat(ds.PTFloat, 73.9), icat(ds.PTKey, rgenCo
mplexKey), cat(ds.PTBoolFalse)), | |
178 cat(cat(ds.PTFloat, 73.9), icat(ds.PTKey, rgenCo
mplexKey), cat(ds.PTBoolTrue)), | |
179 cat(cat(ds.PTFloat, 73.9), icat(ds.PTBytes, "val
ue"), cat(ds.PTNull)), | |
180 cat(cat(ds.PTFloat, 73.9), icat(ds.PTBytes, "val
ue"), cat(ds.PTBoolFalse)), | |
181 cat(cat(ds.PTFloat, 73.9), icat(ds.PTBytes, "val
ue"), cat(ds.PTBoolTrue)), | |
182 cat(cat(ds.PTFloat, 73.9), icat(ds.PTTime, rgenC
omplexTime), cat(ds.PTNull)), | |
183 cat(cat(ds.PTFloat, 73.9), icat(ds.PTTime, rgenC
omplexTime), cat(ds.PTBoolFalse)), | |
184 cat(cat(ds.PTFloat, 73.9), icat(ds.PTTime, rgenC
omplexTime), cat(ds.PTBoolTrue)), | |
185 }, | |
186 }, | |
187 }, | |
188 { | |
189 name: "ancestor", | |
190 pmap: ds.PropertyMap{ | |
191 "wat": {prop("sup")}, | |
192 }, | |
193 idxs: []*qIndex{ | |
194 indx("knd!", "wat"), | |
195 }, | |
196 collections: map[string][]kv{ | |
197 "idx:ns:" + sat(indx("knd!", "wat")): { | |
198 {cat(fakeKey.Parent(), ds.PTString, "sup", fakeK
ey), []byte{}}, | |
199 {cat(fakeKey, ds.PTString, "sup", fakeKey), []by
te{}}, | |
200 }, | |
201 }, | |
202 }, | |
203 } | |
204 | |
205 func TestIndexRowGen(t *testing.T) { | |
206 t.Parallel() | |
207 | |
208 Convey("Test Index Row Generation", t, func() { | |
209 for _, tc := range rowGenTestCases { | |
210 if tc.expected == nil { | |
211 Convey(tc.name, nil) // shows up as 'skipped' | |
212 continue | |
213 } | |
214 | |
215 Convey(tc.name, func() { | |
216 mvals := partiallySerialize(tc.pmap) | |
217 idxs := []*qIndex(nil) | |
218 if tc.withBuiltin { | |
219 idxs = append(defaultIndicies("coolKind"
, tc.pmap), tc.idxs...) | |
220 } else { | |
221 idxs = tc.idxs | |
222 } | |
223 | |
224 m := matcher{} | |
225 for i, idx := range idxs { | |
226 Convey(idx.String(), func() { | |
227 iGen, ok := m.match(idx, mvals) | |
228 if len(tc.expected[i]) > 0 { | |
229 So(ok, ShouldBeTrue) | |
230 j := 0 | |
231 iGen.permute(func(row []
byte) { | |
232 So([]byte(row),
ShouldResemble, tc.expected[i][j]) | |
233 j++ | |
234 }) | |
235 So(j, ShouldEqual, len(t
c.expected[i])) | |
236 } else { | |
237 So(ok, ShouldBeFalse) | |
238 } | |
239 }) | |
240 } | |
241 }) | |
242 } | |
243 }) | |
244 } | |
245 | |
246 func TestIndexEntries(t *testing.T) { | |
247 t.Parallel() | |
248 | |
249 Convey("Test indexEntriesWithBuiltins", t, func() { | |
250 for _, tc := range rowGenTestCases { | |
251 if tc.collections == nil { | |
252 Convey(tc.name, nil) // shows up as 'skipped' | |
253 continue | |
254 } | |
255 | |
256 Convey(tc.name, func() { | |
257 store := (*memStore)(nil) | |
258 if tc.withBuiltin { | |
259 store = indexEntriesWithBuiltins(fakeKey
, tc.pmap, tc.idxs) | |
260 } else { | |
261 store = partiallySerialize(tc.pmap).inde
xEntries(fakeKey, tc.idxs) | |
262 } | |
263 for colName, vals := range tc.collections { | |
264 i := 0 | |
265 store.GetCollection(colName).VisitItemsA
scend(nil, true, func(itm *gkvlite.Item) bool { | |
266 So(itm.Key, ShouldResemble, vals
[i].k) | |
267 So(itm.Val, ShouldResemble, vals
[i].v) | |
268 i++ | |
269 return true | |
270 }) | |
271 So(i, ShouldEqual, len(vals)) | |
272 } | |
273 }) | |
274 } | |
275 }) | |
276 } | |
277 | |
278 type dumbItem struct { | |
279 key ds.Key | |
280 props ds.PropertyMap | |
281 } | |
282 | |
283 var updateIndiciesTests = []struct { | |
284 name string | |
285 idxs []*qIndex | |
286 data []dumbItem | |
287 expected map[string][][]byte | |
288 }{ | |
289 { | |
290 name: "basic", | |
291 data: []dumbItem{ | |
292 {key("knd", 1), ds.PropertyMap{ | |
293 "wat": {prop(10)}, | |
294 "yerp": {prop(10)}}, | |
295 }, | |
296 {key("knd", 10), ds.PropertyMap{ | |
297 "wat": {prop(1)}, | |
298 "yerp": {prop(200)}}, | |
299 }, | |
300 {key("knd", 1), ds.PropertyMap{ | |
301 "wat": {prop(10)}, | |
302 "yerp": {prop(202)}}, | |
303 }, | |
304 }, | |
305 expected: map[string][][]byte{ | |
306 "idx:ns:" + sat(indx("knd", "wat")): { | |
307 cat(ds.PTInt, 1, key("knd", 10)), | |
308 cat(ds.PTInt, 10, key("knd", 1)), | |
309 }, | |
310 "idx:ns:" + sat(indx("knd", "-wat")): { | |
311 cat(icat(ds.PTInt, 10), key("knd", 1)), | |
312 cat(icat(ds.PTInt, 1), key("knd", 10)), | |
313 }, | |
314 "idx:ns:" + sat(indx("knd", "yerp")): { | |
315 cat(ds.PTInt, 200, key("knd", 10)), | |
316 cat(ds.PTInt, 202, key("knd", 1)), | |
317 }, | |
318 }, | |
319 }, | |
320 { | |
321 name: "compound", | |
322 idxs: []*qIndex{indx("knd", "yerp", "-wat")}, | |
323 data: []dumbItem{ | |
324 {key("knd", 1), ds.PropertyMap{ | |
325 "wat": {prop(10)}, | |
326 "yerp": {prop(100)}}, | |
327 }, | |
328 {key("knd", 10), ds.PropertyMap{ | |
329 "wat": {prop(1)}, | |
330 "yerp": {prop(200)}}, | |
331 }, | |
332 {key("knd", 11), ds.PropertyMap{ | |
333 "wat": {prop(20)}, | |
334 "yerp": {prop(200)}}, | |
335 }, | |
336 {key("knd", 14), ds.PropertyMap{ | |
337 "wat": {prop(20)}, | |
338 "yerp": {prop(200)}}, | |
339 }, | |
340 {key("knd", 1), ds.PropertyMap{ | |
341 "wat": {prop(10)}, | |
342 "yerp": {prop(202)}}, | |
343 }, | |
344 }, | |
345 expected: map[string][][]byte{ | |
346 "idx:ns:" + sat(indx("knd", "yerp", "-wat")): { | |
347 cat(ds.PTInt, 200, icat(ds.PTInt, 20), key("knd"
, 11)), | |
348 cat(ds.PTInt, 200, icat(ds.PTInt, 20), key("knd"
, 14)), | |
349 cat(ds.PTInt, 200, icat(ds.PTInt, 1), key("knd",
10)), | |
350 cat(ds.PTInt, 202, icat(ds.PTInt, 10), key("knd"
, 1)), | |
351 }, | |
352 }, | |
353 }, | |
354 } | |
355 | |
356 func TestUpdateIndicies(t *testing.T) { | |
357 t.Parallel() | |
358 | |
359 Convey("Test updateIndicies", t, func() { | |
360 for _, tc := range updateIndiciesTests { | |
361 Convey(tc.name, func() { | |
362 store := newMemStore() | |
363 idxColl := store.SetCollection("idx", nil) | |
364 for _, i := range tc.idxs { | |
365 idxColl.Set(cat(i), []byte{}) | |
366 } | |
367 | |
368 tmpLoader := map[string]ds.PropertyMap{} | |
369 for _, itm := range tc.data { | |
370 ks := itm.key.String() | |
371 prev := tmpLoader[ks] | |
372 updateIndicies(store, itm.key, prev, itm
.props) | |
373 tmpLoader[ks] = itm.props | |
374 } | |
375 tmpLoader = nil | |
376 | |
377 for colName, data := range tc.expected { | |
378 coll := store.GetCollection(colName) | |
379 So(coll, ShouldNotBeNil) | |
380 i := 0 | |
381 coll.VisitItemsAscend(nil, false, func(i
tm *gkvlite.Item) bool { | |
382 So(data[i], ShouldResemble, itm.
Key) | |
383 i++ | |
384 return true | |
385 }) | |
386 So(i, ShouldEqual, len(data)) | |
387 } | |
388 }) | |
389 } | |
390 }) | |
391 } | |
OLD | NEW |