1// Copyright (c) 2012 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// These constants should match the ones in renderer_webidbcursor_impl.h
6// to make sure the test hits the right code paths.
7var kPrefetchThreshold = 2;
8var kMinPrefetchAmount = 5;
9
10var kNumberOfItems = 200;
11
12function test() {
13  indexedDBTest(setVersionSuccess, fillObjectStore);
14}
15
16function setVersionSuccess() {
17  debug("setVersionSuccess():");
18  window.db = event.target.result;
19  window.trans = event.target.transaction;
20  shouldBeTrue("trans !== null");
21  var store = db.createObjectStore('store');
22  store.createIndex('index', '');
23}
24
25function fillObjectStore() {
26  debug("fillObjectStore()");
27  var trans = db.transaction(['store'], 'readwrite');
28  trans.onabort = unexpectedAbortCallback;
29  trans.oncomplete = firstTest;
30
31  var store = trans.objectStore('store');
32  debug("Storing " + kNumberOfItems + " object in the object store.");
33  for (var i = 0; i < kNumberOfItems; ++i) {
34    var req = store.put(i, i);
35    req.onerror = unexpectedErrorCallback;
36  }
37
38  // Let the transaction finish.
39}
40
41function firstTest() {
42  debug("firstTest()");
43
44  // Test iterating straight through the object store.
45
46  var trans = db.transaction(['store'], 'readwrite');
47  trans.onabort = unexpectedAbortCallback;
48  trans.oncomplete = secondTest;
49
50  var store = trans.objectStore('store');
51  var cursorReq = store.openCursor();
52  cursorReq.onerror = unexpectedErrorCallback;
53
54  count = 0;
55  cursorReq.onsuccess = function() {
56    cursor = event.target.result;
57    if (cursor === null) {
58      shouldBe("count", "kNumberOfItems");
59      return; // Let the transaction finish.
60    }
61
62    if (cursor.key !== count)
63      shouldBe("cursor.key", "count");
64    if (cursor.value !== count)
65      shouldBe("cursor.value", "count");
66
67    ++count;
68
69    cursor.continue();
70  };
71}
72
73function secondTest() {
74  debug("secondTest()");
75
76  // Test iterating through the object store, intermixed with
77  // continue calls to specific keys.
78
79  var trans = db.transaction(['store'], 'readwrite');
80  trans.onabort = unexpectedAbortCallback;
81  trans.oncomplete = thirdTest;
82
83  var store = trans.objectStore('store');
84  var cursorReq = store.openCursor();
85  cursorReq.onerror = unexpectedErrorCallback;
86
87  var jumpTable = [{from: 5,   to: 17},
88                   {from: 25,  to: 30},
89                   {from: 31,  to: 35},
90                   {from: 70,  to: 80},
91                   {from: 98,  to: 99}];
92
93  count = 0;
94  expectedKey = 0;
95
96  cursorReq.onsuccess = function() {
97    cursor = event.target.result;
98    if (cursor === null) {
99      debug("Finished iterating after " + count + " steps.");
100      return; // Let the transaction finish.
101    }
102
103    if (cursor.key !== expectedKey)
104      shouldBe("cursor.key", "expectedKey");
105    if (cursor.value !== expectedKey)
106      shouldBe("cursor.value", "expectedKey");
107
108    ++count;
109
110    for (var i = 0; i < jumpTable.length; ++i) {
111      if (jumpTable[i].from === cursor.key) {
112        expectedKey = jumpTable[i].to;
113        debug("Jumping from "+ cursor.key + " to " + expectedKey);
114        cursor.continue(expectedKey);
115        return;
116      }
117    }
118
119    ++expectedKey;
120    cursor.continue();
121  };
122}
123
124function thirdTest() {
125  debug("thirdTest()");
126
127  // Test iterating straight through the object store in reverse.
128
129  var trans = db.transaction(['store'], 'readwrite');
130  trans.onabort = unexpectedAbortCallback;
131  trans.oncomplete = fourthTest;
132
133  var store = trans.objectStore('store');
134  var cursorReq = store.openCursor(
135      IDBKeyRange.upperBound(kNumberOfItems-1), 'prev');
136  cursorReq.onerror = unexpectedErrorCallback;
137
138  count = 0;
139  cursorReq.onsuccess = function() {
140    cursor = event.target.result;
141    if (cursor === null) {
142      shouldBe("count", "kNumberOfItems");
143      return; // Let the transaction finish.
144    }
145
146    expectedKey = kNumberOfItems - count - 1;
147
148    if (cursor.key !== expectedKey)
149      shouldBe("cursor.key", "expectedKey");
150    if (cursor.value !== expectedKey)
151      shouldBe("cursor.value", "expectedKey");
152
153    ++count;
154
155    cursor.continue();
156  };
157}
158
159function fourthTest() {
160  debug("fourthTest()");
161
162  // Test iterating, and then stopping before reaching the end.
163  // Make sure transaction terminates anyway.
164
165  var trans = db.transaction(['store'], 'readwrite');
166  trans.onabort = unexpectedAbortCallback;
167  trans.oncomplete = function() {
168    debug("fourthTest() transaction completed");
169    fifthTest();
170  };
171
172  var store = trans.objectStore('store');
173  var cursorReq = store.openCursor();
174  cursorReq.onerror = unexpectedErrorCallback;
175
176  count = 0;
177  cursorReq.onsuccess = function() {
178    cursor = event.target.result;
179
180    if (cursor.key !== count)
181      shouldBe("cursor.key", "count");
182    if (cursor.value !== count)
183      shouldBe("cursor.value", "count");
184
185    ++count;
186
187    if (count === 25) {
188      // Schedule some other request.
189      var otherReq = store.get(42);
190      otherReq.onerror = unexpectedErrorCallback;
191      otherReq.onsuccess = function() {
192        if (count === 25) {
193          debug("Other request fired before continue, as expected.");
194        } else {
195          debug("Other request fired out-of-order!");
196          fail();
197        }
198      };
199
200      cursor.continue();
201      return;
202    }
203
204    if (count === 30) {
205      // Do a continue first, then another request.
206      cursor.continue();
207
208      var otherReq = store.get(42);
209      otherReq.onerror = unexpectedErrorCallback;
210      otherReq.onsuccess = function() {
211        if (count === 31) {
212          debug("Other request fired right after continue as expected.");
213        } else {
214          debug("Other request didn't fire right after continue as expected.");
215          fail();
216        }
217      };
218
219      return;
220    }
221
222    if (count === 75) {
223      return;  // Sudden stop.
224    }
225
226    cursor.continue();
227  };
228}
229
230function fifthTest() {
231  debug("fifthTest()");
232
233  // Test iterating over the pre-fetch threshold, but make sure the
234  // cursor is positioned so that it is actually at the last element
235  // in the range when pre-fetch fires, and make sure a null cursor
236  // is the result as expected.
237
238  var trans = db.transaction(['store'], 'readwrite');
239  trans.onabort = unexpectedAbortCallback;
240  trans.oncomplete = sixthTest;
241
242  var store = trans.objectStore('store');
243
244  var startKey = kNumberOfItems - 1 - kPrefetchThreshold;
245  var cursorReq = store.openCursor(IDBKeyRange.lowerBound(startKey));
246  cursorReq.onerror = unexpectedErrorCallback;
247
248  count = 0;
249  cursorReq.onsuccess = function() {
250    cursor = event.target.result;
251
252    if (cursor === null) {
253      debug("cursor is null");
254      shouldBe("count", "kPrefetchThreshold + 1");
255      return;
256    }
257
258    debug("count: " + count);
259    ++count;
260    cursor.continue();
261  };
262}
263
264function sixthTest() {
265  debug("sixthTest()");
266
267  // Test stepping two cursors simultaneously. First cursor1 steps
268  // for a while, then cursor2, then back to cursor1, etc.
269
270  var trans = db.transaction(['store'], 'readwrite');
271  trans.onabort = unexpectedAbortCallback;
272  trans.oncomplete = seventhTest;
273  var store = trans.objectStore('store');
274
275  cursor1 = null;
276  cursor2 = null;
277
278  count1 = 0;
279  count2 = 0;
280
281  var cursor1func = function() {
282    var cursor = event.target.result;
283    if (cursor === null) {
284      shouldBe("count1", "kNumberOfItems");
285      cursor2.continue();
286      return;
287    }
288
289    if (cursor1 === null) {
290      cursor1 = cursor;
291    }
292
293    if (cursor1.key !== count1)
294      shouldBe("cursor1.key", "count1");
295    if (cursor1.value !== count1)
296      shouldBe("cursor1.value", "count1");
297
298    ++count1;
299
300    if (count1 % 20 === 0) {
301      if (cursor2 !== null) {
302        cursor2.continue();
303      } else {
304        var req = store.openCursor();
305        req.onerror = unexpectedErrorCallback;
306        req.onsuccess = cursor2func;
307      }
308    } else {
309      cursor1.continue();
310    }
311  };
312
313  var cursor2func = function() {
314    var cursor = event.target.result;
315    if (cursor === null) {
316      shouldBe("count2", "kNumberOfItems");
317      return;
318    }
319
320    if (cursor2 === null) {
321      cursor2 = cursor;
322    }
323
324    if (cursor2.key !== count2)
325      shouldBe("cursor2.key", "count2");
326    if (cursor2.value !== count2)
327      shouldBe("cursor2.value", "count2");
328
329    ++count2;
330
331    if (count2 % 20 === 0) {
332      cursor1.continue();
333    } else {
334      cursor2.continue();
335    }
336  };
337
338  var req = store.openCursor();
339  req.onerror = unexpectedErrorCallback;
340  req.onsuccess = cursor1func;
341}
342
343function seventhTest() {
344  debug("seventhTest()");
345
346  // Test iterating straight through an index.
347
348  var trans = db.transaction(['store'], 'readwrite');
349  trans.onabort = unexpectedAbortCallback;
350  trans.oncomplete = eighthTest;
351
352  var store = trans.objectStore('store');
353  var index = store.index('index');
354
355  var cursorReq = index.openCursor();
356  cursorReq.onerror = unexpectedErrorCallback;
357  count = 0;
358
359  cursorReq.onsuccess = function() {
360    cursor = event.target.result;
361    if (cursor === null) {
362      shouldBe("count", "kNumberOfItems");
363      return;
364    }
365
366    if (cursor.key !== count)
367      shouldBe("cursor.key", "count");
368    if (cursor.primaryKey !== count)
369      shouldBe("cursor.primaryKey", "count");
370    if (cursor.value !== count)
371      shouldBe("cursor.value", "count");
372
373    ++count;
374    cursor.continue();
375  };
376}
377
378function eighthTest() {
379  debug("eighthTest()");
380
381  // Run a key cursor over an index.
382
383  var trans = db.transaction(['store'], 'readwrite');
384  trans.onabort = unexpectedAbortCallback;
385  trans.oncomplete = done;
386
387  var store = trans.objectStore('store');
388  var index = store.index('index');
389
390  var cursorReq = index.openKeyCursor();
391  cursorReq.onerror = unexpectedErrorCallback;
392  count = 0;
393
394  cursorReq.onsuccess = function() {
395    cursor = event.target.result;
396    if (cursor === null) {
397      shouldBe("count", "kNumberOfItems");
398      return;
399    }
400
401    if (cursor.key !== count)
402      shouldBe("cursor.key", "count");
403    if (cursor.primaryKey !== count)
404      shouldBe("cursor.primaryKey", "count");
405
406    ++count;
407    cursor.continue();
408  };
409}
410