Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(248)

Side by Side Diff: impl/memory/datastore_query_execution_test.go

Issue 1302813003: impl/memory: Implement Queries (Closed) Base URL: https://github.com/luci/gae.git@add_multi_iterator
Patch Set: stringSet everywhere Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « impl/memory/datastore_query_execution.go ('k') | impl/memory/datastore_query_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 "fmt"
9 "testing"
10
11 ds "github.com/luci/gae/service/datastore"
12 "github.com/luci/gae/service/info"
13 . "github.com/luci/luci-go/common/testing/assertions"
14 . "github.com/smartystreets/goconvey/convey"
15 "golang.org/x/net/context"
16 )
17
18 type qExpect struct {
19 q ds.Query
20 inTxn bool
21
22 get []ds.PropertyMap
23 keys []ds.Key
24 }
25
26 type qExStage struct {
27 addIdxs []*ds.IndexDefinition
28 putEnts []ds.PropertyMap
29 delEnts []ds.Key
30
31 expect []qExpect
32
33 extraFns []func(context.Context)
34 }
35
36 type qExTest struct {
37 name string
38 test []qExStage
39 }
40
41 var stage1Data = []ds.PropertyMap{
42 pmap("$key", key("Kind", 1), NEXT,
43 "Val", 1, 2, 3, NEXT,
44 "Extra", "hello",
45 ),
46 pmap("$key", key("Kind", 2), NEXT,
47 "Val", 6, 8, 7, NEXT,
48 "Extra", "zebra",
49 ),
50 pmap("$key", key("Kind", 3), NEXT,
51 "Val", 1, 2, 2, 100, NEXT,
52 "Extra", "waffle",
53 ),
54 pmap("$key", key("Kind", 6), NEXT,
55 "Val", 5, NEXT,
56 "Extra", "waffle",
57 ),
58 pmap("$key", key("Child", "seven", key("Kind", 3)), NEXT,
59 "Interesting", 28, NEXT,
60 "Extra", "hello",
61 ),
62 pmap("$key", key("Unique", 1), NEXT,
63 "Derp", 39,
64 ),
65 }
66
67 var stage2Data = []ds.PropertyMap{
68 pmap("$key", key("Kind", 1, key("Kind", 3)), NEXT,
69 "Val", 2, 4, 28, NEXT,
70 "Extra", "hello", "waffle",
71 ),
72 pmap("$key", key("Kind", 2, key("Kind", 3)), NEXT,
73 "Val", 3, 4, NEXT,
74 "Extra", "hello", "waffle",
75 ),
76 pmap("$key", key("Kind", 3, key("Kind", 3)), NEXT,
77 "Val", 3, 4, 2, 1, NEXT,
78 "Extra", "nuts",
79 ),
80 }
81
82 var queryExecutionTests = []qExTest{
83 {"basic", []qExStage{
84 {
85 addIdxs: []*ds.IndexDefinition{
86 indx("Unrelated", "-thing", "bob", "-__key__"),
87 indx("Wat", "deep", "opt", "other"),
88 indx("Wat", "meep", "opt", "other"),
89 },
90 },
91
92 {
93 expect: []qExpect{
94 // tests the case where the query has indexes to fulfill it, but there
95 // are no actual entities in the datastore.
96 {q: nq("Wat").Filter("meep =", 1).Filter("deep = ", 2).Order("opt").Order("other"),
97 get: []ds.PropertyMap{}},
98 },
99 },
100
101 {
102 putEnts: stage1Data,
103 expect: []qExpect{
104 {q: nq("Kind"), get: []ds.PropertyMap{}},
105 {q: nq("Child").Ancestor(key("Kind", 3)), keys: []ds.Key{
106 key("Child", "seven", key("Kind", 3)),
107 }},
108 },
109 },
110
111 {
112 putEnts: stage2Data,
113 delEnts: []ds.Key{key("Unique", 1)},
114 addIdxs: []*ds.IndexDefinition{
115 indx("Kind!", "-Extra", "-Val"),
116 indx("Kind!", "-Extra", "-Val", "-__key__"),
117 indx("Kind!", "Bogus", "Extra", "-Val"),
118 },
119 expect: []qExpect{
120 {q: nq("Kind"), get: stage1Data[:4]},
121
122 {q: nq("Kind").Offset(2).Limit(1), get: []ds.Pro pertyMap{
123 stage1Data[2],
124 }},
125
126 {q: nq("Missing"), get: []ds.PropertyMap{}},
127
128 {q: nq("Missing").Filter("Id <", 2).Filter("Id > ", 2), get: []ds.PropertyMap{}},
129
130 {q: nq("Missing").Filter("Bogus =", 3), get: []d s.PropertyMap{}},
131
132 {q: nq("Kind").Filter("Extra =", "waffle"), get: []ds.PropertyMap{
133 stage1Data[2], stage1Data[3],
134 }},
135
136 // get ziggy with it
137 {q: nq("Kind").Filter("Extra =", "waffle").Filte r("Val =", 100), get: []ds.PropertyMap{
138 stage1Data[2],
139 }},
140 {q: nq("Child").Filter("Interesting =", 28).Filt er("Extra =", "hello"), get: []ds.PropertyMap{
141 stage1Data[4],
142 }},
143
144 {q: (nq("Kind").Ancestor(key("Kind", 3)).Order(" Val").
145 Start(curs("Val", 1, "__key__", key("Kin d", 3))).
146 End(curs("Val", 90, "__key__", key("Zeta ", "woot", key("Kind", 3))))), keys: []ds.Key{},
147 },
148
149 {q: nq("Kind").Filter("Val >", 2).Filter("Val <= ", 5), get: []ds.PropertyMap{
150 stage1Data[0], stage1Data[3],
151 }},
152
153 {q: nq("Kind").Filter("Val >", 2).Filter("Val <= ", 5).Order("-Val"), get: []ds.PropertyMap{
154 stage1Data[3], stage1Data[0],
155 }},
156
157 {q: nq("").Filter("__key__ >", key("Kind", 2)), get: []ds.PropertyMap{
158 // TODO(riannucci): determine if the rea l datastore shows metadata
159 // during kindless queries. The document ation seems to imply so, but
160 // I'd like to be sure.
161 pmap("$key", key("__entity_group__", 1, key("Kind", 2)), NEXT,
162 "__version__", 1),
163 stage1Data[2],
164 stage1Data[4],
165 // this is 5 because the value is retrie ved from HEAD and not from
166 // the index snapshot!
167 pmap("$key", key("__entity_group__", 1, key("Kind", 3)), NEXT,
168 "__version__", 5),
169 stage1Data[3],
170 pmap("$key", key("__entity_group__", 1, key("Kind", 6)), NEXT,
171 "__version__", 1),
172 pmap("$key", key("__entity_group__", 1, key("Unique", 1)), NEXT,
173 "__version__", 2),
174 }},
175
176 {q: (nq("Kind").
177 Filter("Val >", 2).Filter("Extra =", "wa ffle").
178 Order("-Val").
179 Ancestor(key("Kind", 3))),
180 get: []ds.PropertyMap{
181 stage1Data[2],
182 stage2Data[0],
183 stage2Data[1],
184 }},
185
186 {q: (nq("Kind").
187 Filter("Val >", 2).Filter("Extra =", "wa ffle").
188 Order("-Val").Order("-__key__").
189 Ancestor(key("Kind", 3))),
190 get: []ds.PropertyMap{
191 stage1Data[2],
192 stage2Data[0],
193 stage2Data[1],
194 }},
195
196 {q: (nq("Kind").
197 Filter("Val >", 2).Filter("Extra =", "wa ffle").
198 Order("-Val").
199 Ancestor(key("Kind", 3)).Project("Val")) ,
200 get: []ds.PropertyMap{
201 pmap("$key", key("Kind", 3), NEX T,
202 "Val", 100),
203 pmap("$key", key("Kind", 1, key( "Kind", 3)), NEXT,
204 "Val", 28),
205 pmap("$key", key("Kind", 1, key( "Kind", 3)), NEXT,
206 "Val", 4),
207 pmap("$key", key("Kind", 2, key( "Kind", 3)), NEXT,
208 "Val", 4),
209 pmap("$key", key("Kind", 2, key( "Kind", 3)), NEXT,
210 "Val", 3),
211 }},
212
213 {q: (nq("Kind").
214 Filter("Val >", 2).Filter("Extra =", "wa ffle").
215 Order("-Val").
216 Ancestor(key("Kind", 3)).Project("Val"). Distinct()),
217 get: []ds.PropertyMap{
218 pmap("$key", key("Kind", 3), NEX T,
219 "Val", 100),
220 pmap("$key", key("Kind", 1, key( "Kind", 3)), NEXT,
221 "Val", 28),
222 pmap("$key", key("Kind", 1, key( "Kind", 3)), NEXT,
223 "Val", 4),
224 pmap("$key", key("Kind", 2, key( "Kind", 3)), NEXT,
225 "Val", 3),
226 }},
227 },
228
229 extraFns: []func(context.Context){
230 func(c context.Context) {
231 data := ds.Get(c)
232 curs := ds.Cursor(nil)
233
234 q := nq("").Filter("__key__ >", key("Kin d", 2))
235
236 err := data.Run(q, func(pm ds.PropertyMa p, gc ds.CursorCB) bool {
237 So(pm, ShouldResemble, pmap(
238 "$key", key("__entity_gr oup__", 1, key("Kind", 2)), NEXT,
239 "__version__", 1))
240
241 err := error(nil)
242 curs, err = gc()
243 So(err, ShouldBeNil)
244 return false
245 })
246 So(err, ShouldBeNil)
247
248 err = data.Run(q.Start(curs), func(pm ds .PropertyMap, gc ds.CursorCB) bool {
249 So(pm, ShouldResemble, stage1Dat a[2])
250 return false
251 })
252 So(err, ShouldBeNil)
253 },
254
255 func(c context.Context) {
256 data := ds.Get(c)
257 q := nq("Something").Filter("Does =", 2) .Order("Not").Order("Work")
258 So(data.Run(q, func(ds.Key, ds.CursorCB) bool {
259 return true
260 }), ShouldErrLike, "Try adding:\n C:Som ething/Does/Not/Work")
261 },
262 },
263 },
264
265 {
266 expect: []qExpect{
267 // eventual consistency; Unique/1 is deleted at HEAD. Keysonly finds it,
268 // but 'normal' doesn't.
269 {q: nq("Unique").Filter("__key__ >", key("AKind" , 5)).Filter("__key__ <=", key("Zeta", "prime")),
270 keys: []ds.Key{key("Unique", 1)},
271 get: []ds.PropertyMap{}},
272
273 {q: nq("Kind").Filter("Val =", 1).Filter("Val =" , 3), get: []ds.PropertyMap{
274 stage1Data[0], stage2Data[2],
275 }},
276 },
277 },
278 }},
279 }
280
281 func TestQueryExecution(t *testing.T) {
282 t.Parallel()
283
284 Convey("Test query execution", t, func() {
285 c, err := info.Get(Use(context.Background())).Namespace("ns")
286 if err != nil {
287 panic(err)
288 }
289
290 data := ds.Get(c)
291 testing := data.Raw().Testable()
292
293 for _, tc := range queryExecutionTests {
294 Convey(tc.name, func() {
295 for i, stage := range tc.test {
296 // outside of Convey, since these must a lways happen
297 testing.CatchupIndexes()
298
299 testing.AddIndexes(stage.addIdxs...)
300 if err := data.PutMulti(stage.putEnts); err != nil {
301 // prevent Convey from thinking this assertion should show up in
302 // every test loop.
303 panic(err)
304 }
305
306 if err := data.DeleteMulti(stage.delEnts ); err != nil {
307 panic(err)
308 }
309
310 Convey(fmt.Sprintf("stage %d", i), func( ) {
311 for j, expect := range stage.exp ect {
312 runner := func(f func(ic context.Context) error, _ *ds.TransactionOptions) error {
313 return f(c)
314 }
315 if expect.inTxn {
316 runner = data.Ru nInTransaction
317 }
318
319 if expect.keys != nil {
320 runner(func(c co ntext.Context) error {
321 data := ds.Get(c)
322 Convey(f mt.Sprintf("expect %d (keys)", j), func() {
323 rslt := []ds.Key(nil)
324 So(data.GetAll(expect.q, &rslt), ShouldBeNil)
325 So(len(rslt), ShouldEqual, len(expect.keys))
326 for i, r := range rslt {
327 So(r, ShouldResemble, expect.keys[i])
328 }
329 })
330 return n il
331 }, &ds.Transacti onOptions{XG: true})
332 }
333
334 if expect.get != nil {
335 Convey(fmt.Sprin tf("expect %d (data)", j), func() {
336 runner(f unc(c context.Context) error {
337 rslt := []ds.PropertyMap(nil)
338 So(data.GetAll(expect.q, &rslt), ShouldBeNil)
339 So(len(rslt), ShouldEqual, len(expect.get))
340 for i, r := range rslt {
341 So(r, ShouldResemble, expect.get[i])
342 }
343 return nil
344 }, &ds.T ransactionOptions{XG: true})
345 })
346 }
347 }
348
349 for j, fn := range stage.extraFn s {
350 Convey(fmt.Sprintf("extr aFn %d", j), func() {
351 fn(c)
352 })
353 }
354 })
355 }
356 })
357 }
358 })
359 }
OLDNEW
« no previous file with comments | « impl/memory/datastore_query_execution.go ('k') | impl/memory/datastore_query_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698