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.
5 var overallTestStartTime
= window
.performance
.now();
7 var kDontUseIndex
= false;
8 var kReadKeysOnly
= true;
9 var kReadDataToo
= false;
11 var kDontWrite
= false;
12 var kWriteSameStore
= true;
13 var kWriteDifferentStore
= false;
14 var kPlaceholderArg
= false;
15 var kDontRead
= false;
16 var kAlternateWithReads
= true;
19 // Create a single small item in a single object store, then delete everything.
20 [testCreateAndDeleteDatabase
, 1, 1, 1],
21 // Create many small items in a single object store, then delete everything.
22 [testCreateAndDeleteDatabase
, 1000, 1, 1],
23 // Create a single small item in many object stores, then delete everything.
24 [testCreateAndDeleteDatabase
, 1, 1000, 1],
25 // Create many large items in a single object store, then delete everything.
26 [testCreateAndDeleteDatabase
, 1000, 1, 10000],
27 // Create a single small item in a single object store.
28 [testCreateKeysInStores
, 1, 1, 1],
29 // Create many small items in a single object store.
30 [testCreateKeysInStores
, 1000, 1, 1],
31 // Create a single small item in many object stores.
32 [testCreateKeysInStores
, 1, 1000, 1],
33 // Create many large items in a single object store.
34 [testCreateKeysInStores
, 1000, 1, 10000],
35 // Read one item per transaction.
36 [testRandomReadsAndWrites
, 1000, 1, 0, 1000, kDontUseIndex
],
37 // Read a few random items in each of many transactions.
38 [testRandomReadsAndWrites
, 1000, 5, 0, 100, kDontUseIndex
],
39 // Read many random items in each of a few transactions.
40 [testRandomReadsAndWrites
, 1000, 500, 0, 5, kDontUseIndex
],
41 // Read many random items in each of a few transactions, in a large store.
42 [testRandomReadsAndWrites
, 10000, 500, 0, 5, kDontUseIndex
],
43 // Read a few random items from an index, in each of many transactions.
44 [testRandomReadsAndWrites
, 1000, 5, 0, 100, kUseIndex
],
45 // Read many random items from an index, in each of a few transactions.
46 [testRandomReadsAndWrites
, 1000, 500, 0, 5, kUseIndex
],
47 // Read many random items from an index, in each of a few transactions, in a
49 [testRandomReadsAndWrites
, 10000, 500, 0, 5, kUseIndex
],
50 // Read and write a few random items in each of many transactions.
51 [testRandomReadsAndWrites
, 1000, 5, 5, 50, kDontUseIndex
],
52 // Read and write a few random items, reading from an index, in each of many
54 [testRandomReadsAndWrites
, 1000, 5, 5, 50, kUseIndex
],
55 // Read a long, contiguous sequence of an object store via a cursor.
56 [testCursorReadsAndRandomWrites
, kReadDataToo
, kDontUseIndex
, kDontWrite
,
58 // Read a sequence of an object store via a cursor, writing
59 // transformed values into another.
60 [testCursorReadsAndRandomWrites
, kReadDataToo
, kDontUseIndex
, kWriteToo
,
61 kWriteDifferentStore
],
62 // Read a sequence of an object store via a cursor, writing
63 // transformed values into another.
64 [testCursorReadsAndRandomWrites
, kReadDataToo
, kDontUseIndex
, kWriteToo
,
66 // Read a sequence of an index into an object store via a cursor.
67 [testCursorReadsAndRandomWrites
, kReadDataToo
, kUseIndex
, kDontWrite
,
69 // Read a sequence of an index into an object store via a key cursor.
70 [testCursorReadsAndRandomWrites
, kReadKeysOnly
, kUseIndex
, kDontWrite
,
72 // Make a small bunch of batches of reads of the same keys from an object store.
73 [testReadCache
, 10, kDontUseIndex
],
74 // Make a bunch of batches of reads of the same keys from an index.
75 [testReadCache
, 50, kUseIndex
],
76 // Make a small bunch of batches of reads of the same keys from an object store.
77 [testReadCache
, 10, kDontUseIndex
],
78 // Make a bunch of batches of reads of the same keys from an index.
79 [testReadCache
, 50, kUseIndex
],
80 // Create and delete an index on a store that already contains data [produces
81 // a timing result for each of creation and deletion].
82 [testCreateAndDeleteIndex
, 5000],
83 // Walk through multiple cursors into the same object store, round-robin, until
84 // you've reached the end of each of them.
85 [testWalkingMultipleCursors
, 5],
86 // Walk through many cursors into the same object store, round-robin, until
87 // you've reached the end of each of them.
88 [testWalkingMultipleCursors
, 50],
89 // Open an object store cursor, then continue(key) to the last value.
90 [testCursorSeeks
, 2000, 10, 4, kDontUseIndex
],
91 // Open an index key cursor, then continue(key) to the last value.
92 [testCursorSeeks
, 2000, 10, 4, kUseIndex
],
101 function runNextTest() {
102 var filter
= window
.location
.hash
.slice(1);
104 while (currentTest
< tests
.length
) {
105 test
= tests
[currentTest
];
107 if (!filter
|| f
.name
== filter
)
112 if (currentTest
< tests
.length
) {
113 test
.push(runNextTest
);
117 onAllTestsComplete();
121 function onAllTestsComplete() {
122 var overallDuration
= window
.performance
.now() - overallTestStartTime
;
123 automation
.addResult("OverallTestDuration", overallDuration
);
124 automation
.setDone();
127 // This is the only test that includes database creation and deletion in its
128 // results; the others just test specific operations. To see only the
129 // creation/deletion without the specific operations used to build up the data
130 // in the object stores here, subtract off the results of
131 // testCreateKeysInStores.
132 function testCreateAndDeleteDatabase(
133 numKeys
, numStores
, payloadLength
, onTestComplete
) {
134 var testName
= getDisplayName(arguments
);
135 assert(numKeys
>= 0);
136 assert(numStores
>= 1);
137 var objectStoreNames
= [];
138 for (var i
=0; i
< numStores
; ++i
) {
139 objectStoreNames
.push("store " + i
);
141 var value
= stringOfLength(payloadLength
);
142 function getValue() {
146 automation
.setStatus("Creating database.");
147 var startTime
= window
.performance
.now();
149 createDatabase(testName
, objectStoreNames
, onCreated
, onError
);
151 function onCreated(db
) {
152 automation
.setStatus("Constructing transaction.");
154 getTransaction(db
, objectStoreNames
, "readwrite",
155 function() { onValuesWritten(db
); });
156 putLinearValues(transaction
, objectStoreNames
, numKeys
, null, getValue
);
159 function onValuesWritten(db
) {
160 automation
.setStatus("Deleting database.");
162 deleteDatabase(testName
, onDeleted
);
165 function onDeleted() {
166 var duration
= window
.performance
.now() - startTime
;
167 automation
.addResult(testName
, duration
);
168 automation
.setStatus("Deleted database.");
173 function testCreateKeysInStores(
174 numKeys
, numStores
, payloadLength
, onTestComplete
) {
175 var testName
= getDisplayName(arguments
);
176 assert(numKeys
>= 0);
177 assert(numStores
>= 1);
178 var objectStoreNames
= [];
179 for (var i
=0; i
< numStores
; ++i
) {
180 objectStoreNames
.push("store " + i
);
182 var value
= stringOfLength(payloadLength
);
183 function getValue() {
187 automation
.setStatus("Creating database.");
188 createDatabase(testName
, objectStoreNames
, onCreated
, onError
);
190 function onCreated(db
) {
191 automation
.setStatus("Constructing transaction.");
193 getCompletionFunc(db
, testName
, window
.performance
.now(),
196 getTransaction(db
, objectStoreNames
, "readwrite", completionFunc
);
197 putLinearValues(transaction
, objectStoreNames
, numKeys
, null, getValue
);
201 function testRandomReadsAndWrites(
202 numKeys
, numReadsPerTransaction
, numWritesPerTransaction
, numTransactions
,
203 useIndexForReads
, onTestComplete
) {
205 if (useIndexForReads
)
207 var testName
= getDisplayName(arguments
);
208 var objectStoreNames
= ["store"];
209 var getKey
= getSimpleKey
;
210 var getValue
= useIndexForReads
? getIndexableValue
: getSimpleValue
;
212 automation
.setStatus("Creating database.");
214 if (useIndexForReads
) {
216 indexName
: indexName
,
218 indexIsUnique
: false,
219 indexIsMultiEntry
: false,
222 createDatabase(testName
, objectStoreNames
, onCreated
, onError
, options
);
224 function onCreated(db
) {
225 automation
.setStatus("Setting up test database.");
226 var transaction
= getTransaction(db
, objectStoreNames
, "readwrite",
227 function() { onSetupComplete(db
); });
228 putLinearValues(transaction
, objectStoreNames
, numKeys
, null,
229 function() { return "test value"; });
232 function onSetupComplete(db
) {
233 automation
.setStatus("Setup complete.");
235 getCompletionFunc(db
, testName
, window
.performance
.now(),
237 var mode
= "readonly";
238 if (numWritesPerTransaction
)
240 runTransactionBatch(db
, numTransactions
, batchFunc
, objectStoreNames
, mode
,
244 function batchFunc(transaction
) {
245 getRandomValues(transaction
, objectStoreNames
, numReadsPerTransaction
,
246 numKeys
, indexName
, getKey
);
247 putRandomValues(transaction
, objectStoreNames
, numWritesPerTransaction
,
248 numKeys
, getKey
, getValue
);
252 function testReadCache(numTransactions
, useIndexForReads
, onTestComplete
) {
254 var numReadsPerTransaction
= 50;
255 var numTransactionsLeft
= numTransactions
;
257 if (useIndexForReads
)
259 var testName
= getDisplayName(arguments
);
260 var objectStoreNames
= ["store"];
261 var getKey
= getSimpleKey
;
262 var getValue
= useIndexForReads
? getIndexableValue
: getSimpleValue
;
265 for (var i
=0; i
< numReadsPerTransaction
; ++i
) {
266 keys
.push(getKey(Math
.floor(random() * numKeys
)));
269 automation
.setStatus("Creating database.");
271 if (useIndexForReads
) {
273 indexName
: indexName
,
275 indexIsUnique
: false,
276 indexIsMultiEntry
: false,
279 createDatabase(testName
, objectStoreNames
, onCreated
, onError
, options
);
281 function onCreated(db
) {
282 automation
.setStatus("Setting up test database.");
283 var transaction
= getTransaction(db
, objectStoreNames
, "readwrite",
284 function() { onSetupComplete(db
); });
285 putLinearValues(transaction
, objectStoreNames
, numKeys
, getKey
,
290 function onSetupComplete(db
) {
291 automation
.setStatus("Setup complete.");
293 getCompletionFunc(db
, testName
, window
.performance
.now(),
295 runTransactionBatch(db
, numTransactions
, batchFunc
, objectStoreNames
,
296 "readonly", completionFunc
);
299 function batchFunc(transaction
) {
300 getSpecificValues(transaction
, objectStoreNames
, indexName
, keys
);
304 function testCreateAndDeleteIndex(numKeys
, onTestComplete
) {
305 var testName
= getDisplayName(arguments
);
306 var objectStoreNames
= ["store"];
308 automation
.setStatus("Creating database.");
309 createDatabase(testName
, objectStoreNames
, onCreated
, onError
);
312 function onCreated(db
) {
313 automation
.setStatus("Initializing data.");
314 var transaction
= getTransaction(db
, objectStoreNames
, "readwrite",
315 function() { onPopulated(db
); });
316 putLinearValues(transaction
, objectStoreNames
, numKeys
, null, getValue
);
319 function getValue(i
) {
320 return { firstName
: i
+ " first name", lastName
: i
+ " last name" };
323 function onPopulated(db
) {
325 automation
.setStatus("Building index.");
326 startTime
= window
.performance
.now();
327 var f = function(objectStore
) {
328 objectStore
.createIndex("index", "firstName", {unique
: true});
330 alterObjectStores(testName
, objectStoreNames
, f
, onIndexCreated
, onError
);
333 var indexCreationCompleteTime
;
334 function onIndexCreated(db
) {
336 indexCreationCompleteTime
= window
.performance
.now();
337 automation
.addResult("testCreateIndex",
338 indexCreationCompleteTime
- startTime
);
339 var f = function(objectStore
) {
340 objectStore
.deleteIndex("index");
342 automation
.setStatus("Deleting index.");
343 alterObjectStores(testName
, objectStoreNames
, f
, onIndexDeleted
, onError
);
346 function onIndexDeleted(db
) {
347 var duration
= window
.performance
.now() - indexCreationCompleteTime
;
348 // Ignore the cleanup time for this test.
349 automation
.addResult("testDeleteIndex", duration
);
350 automation
.setStatus("Deleting database.");
352 deleteDatabase(testName
, onDeleted
);
355 function onDeleted() {
356 automation
.setStatus("Deleted database.");
361 function testCursorReadsAndRandomWrites(
362 readKeysOnly
, useIndexForReads
, writeAlso
, sameStoreForWrites
,
364 // There's no key cursor unless you're reading from an index.
365 assert(useIndexForReads
|| !readKeysOnly
);
366 // If we're writing to another store, having an index would constrain our
367 // writes, as we create both object stores with the same configurations.
368 // We could do that if needed, but it's simpler not to.
369 assert(!useIndexForReads
|| !writeAlso
);
371 var numReadsPerTransaction
= 1000;
372 var testName
= getDisplayName(arguments
);
373 var objectStoreNames
= ["input store"];
376 if (sameStoreForWrites
) {
377 outputStoreName
= objectStoreNames
[0];
379 outputStoreName
= "output store";
380 objectStoreNames
.push(outputStoreName
);
383 var getKeyForRead
= getSimpleKey
;
385 if (useIndexForReads
) {
387 getKeyForRead = function(i
) {
388 // This depends on the implementations of getValuesFromCursor and
389 // getObjectValue. We reverse the order of the iteration here so that
390 // setting up bounds from k to k+n with n>0 works. Without this reversal,
391 // the upper bound is below the lower bound.
392 return getBackwardIndexKey(numKeys
- i
);
396 automation
.setStatus("Creating database.");
398 if (useIndexForReads
) {
400 indexName
: indexName
,
401 indexKeyPath
: "lastName", // depends on getBackwardIndexKey()
403 indexIsMultiEntry
: false,
406 createDatabase(testName
, objectStoreNames
, onCreated
, onError
, options
);
408 function onCreated(db
) {
409 automation
.setStatus("Setting up test database.");
410 var transaction
= getTransaction(db
, objectStoreNames
, "readwrite",
411 function() { onSetupComplete(db
); });
412 putLinearValues(transaction
, objectStoreNames
, numKeys
, getSimpleKey
,
415 function onSetupComplete(db
) {
416 automation
.setStatus("Setup complete.");
418 getCompletionFunc(db
, testName
, window
.performance
.now(),
420 var mode
= "readonly";
424 getTransaction(db
, objectStoreNames
, mode
, completionFunc
);
427 transaction
, objectStoreNames
[0], numReadsPerTransaction
, numKeys
,
428 indexName
, getKeyForRead
, readKeysOnly
, outputStoreName
);
432 function testWalkingMultipleCursors(numCursors
, onTestComplete
) {
434 var numHitsPerKey
= 10;
435 var testName
= getDisplayName(arguments
);
436 var objectStoreNames
= ["input store"];
437 var indexName
= "index name";
438 var getKey
= getSimpleKey
;
439 var getValue
= getIndexableValue
;
441 automation
.setStatus("Creating database.");
443 indexName
: indexName
,
445 indexIsUnique
: false,
446 indexIsMultiEntry
: false,
448 createDatabase(testName
, objectStoreNames
, onCreated
, onError
, options
);
450 function onCreated(db
) {
451 automation
.setStatus("Setting up test database.");
452 var transaction
= getTransaction(db
, objectStoreNames
, "readwrite",
453 function() { onSetupComplete(db
); });
454 // This loop adds the same value numHitsPerKey times for each key.
455 for (var i
= 0; i
< numHitsPerKey
; ++i
) {
456 putLinearValues(transaction
, objectStoreNames
, numKeys
, getKeyFunc(i
),
460 // While the value is the same each time through the putLinearValues loop, we
461 // want the key to keep increaasing for each copy.
462 function getKeyFunc(k
) {
464 return getKey(k
* numKeys
+ i
);
468 function onSetupComplete(db
) {
469 automation
.setStatus("Setup complete.");
471 getCompletionFunc(db
, testName
, window
.performance
.now(),
474 getTransaction(db
, objectStoreNames
, "readonly", verifyComplete
);
476 walkSeveralCursors(transaction
, numKeys
);
478 var responseCounts
= [];
479 var cursorsRunning
= numCursors
;
480 function walkSeveralCursors(transaction
, numKeys
) {
481 var source
= transaction
.objectStore(objectStoreNames
[0]).index(indexName
);
483 var continueCursorIndex
= 0;
484 for (var i
= 0; i
< numCursors
; ++i
) {
485 var rand
= Math
.floor(random() * numKeys
);
486 // Since we have numHitsPerKey copies of each value in the database,
487 // IDBKeyRange.only will return numHitsPerKey results, each referring to a
488 // different key with the matching value.
489 var request
= source
.openCursor(IDBKeyRange
.only(getSimpleValue(rand
)));
490 responseCounts
.push(0);
491 request
.onerror
= onError
;
492 request
.onsuccess = function(event
) {
493 assert(cursorsRunning
);
494 var request
= event
.target
;
495 if (!("requestIndex" in request
)) {
496 assert(requests
.length
< numCursors
);
497 request
.requestIndex
= requests
.length
;
498 requests
.push(request
);
500 var cursor
= event
.target
.result
;
502 assert(responseCounts
[request
.requestIndex
] < numHitsPerKey
);
503 ++responseCounts
[request
.requestIndex
];
505 assert(responseCounts
[request
.requestIndex
] == numHitsPerKey
);
508 if (cursorsRunning
) {
509 if (requests
.length
== numCursors
) {
510 requests
[continueCursorIndex
++].result
.continue();
511 continueCursorIndex
%= numCursors
;
517 function verifyComplete() {
518 assert(!cursorsRunning
);
523 function testCursorSeeks(
524 numKeys
, numSeeksPerTransaction
, numTransactions
, useIndexForReads
,
526 var testName
= getDisplayName(arguments
);
527 var objectStoreNames
= ["store"];
528 var getKey
= useIndexForReads
? getForwardIndexKey
: getSimpleKey
;
530 if (useIndexForReads
) {
534 automation
.setStatus("Creating database.");
536 if (useIndexForReads
) {
538 indexName
: indexName
,
539 indexKeyPath
: "firstName",
541 indexIsMultiEntry
: false,
544 createDatabase(testName
, objectStoreNames
, onCreated
, onError
, options
);
546 function onCreated(db
) {
547 automation
.setStatus("Setting up test database.");
548 var transaction
= getTransaction(db
, objectStoreNames
, "readwrite",
549 function() { onSetupComplete(db
); });
550 putLinearValues(transaction
, objectStoreNames
, numKeys
, getSimpleKey
,
554 function onSetupComplete(db
) {
555 automation
.setStatus("Setup complete.");
557 getCompletionFunc(db
, testName
, window
.performance
.now(),
559 var mode
= "readonly";
560 runTransactionBatch(db
, numTransactions
, batchFunc
, objectStoreNames
, mode
,
564 function batchFunc(transaction
) {
565 for (var i
in objectStoreNames
) {
566 var source
= transaction
.objectStore(objectStoreNames
[i
]);
567 if (useIndexForReads
)
568 source
= source
.index(indexName
);
569 for (var j
= 0; j
< numSeeksPerTransaction
; ++j
) {
575 function randomSeek(source
) {
576 var request
= useIndexForReads
? source
.openKeyCursor()
577 : source
.openCursor();
579 request
.onerror
= onError
;
580 request
.onsuccess = function() {
581 var cursor
= request
.result
;
582 if (cursor
&& first
) {
584 cursor
.continue(getKey(numKeys
- 1));