Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / test / data / indexeddb / perf_test.js
blob1221e96019022ef37561e6a718f0778f09975b5a
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();
6 var kUseIndex = true;
7 var kDontUseIndex = false;
8 var kReadKeysOnly = true;
9 var kReadDataToo = false;
10 var kWriteToo = true;
11 var kDontWrite = false;
12 var kWriteSameStore = true;
13 var kWriteDifferentStore = false;
14 var kPlaceholderArg = false;
15 var kDontRead = false;
16 var kAlternateWithReads = true;
18 var tests = [
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
48 // large store.
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
53 // transactions.
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,
57 kPlaceholderArg],
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,
65 kWriteSameStore],
66 // Read a sequence of an index into an object store via a cursor.
67 [testCursorReadsAndRandomWrites, kReadDataToo, kUseIndex, kDontWrite,
68 kPlaceholderArg],
69 // Read a sequence of an index into an object store via a key cursor.
70 [testCursorReadsAndRandomWrites, kReadKeysOnly, kUseIndex, kDontWrite,
71 kPlaceholderArg],
72 // Make batches of random writes into a store, triggered by periodic setTimeout
73 // calls.
74 [testSporadicWrites, 5, 0, kDontRead],
75 // Make large batches of random writes into a store, triggered by periodic
76 // setTimeout calls.
77 [testSporadicWrites, 50, 0, kDontRead],
78 // Make batches of random writes into a store with many indices, triggered by
79 // periodic setTimeout calls.
80 [testSporadicWrites, 5, 10, kDontRead],
81 // Make large batches of random writes into a store with many indices, triggered
82 // by periodic setTimeout calls.
83 [testSporadicWrites, 50, 10, kDontRead],
84 // Make batches of random writes into a store, triggered by periodic setTimeout
85 // calls. Intersperse read transactions to test read-write lock conflicts.
86 [testSporadicWrites, 5, 0, kAlternateWithReads],
87 // Make large batches of random writes into a store, triggered by periodic
88 // setTimeout calls. Intersperse read transactions to test read-write lock
89 // conflicts.
90 [testSporadicWrites, 50, 0, kAlternateWithReads],
91 // Make a small bunch of batches of reads of the same keys from an object store.
92 [testReadCache, 10, kDontUseIndex],
93 // Make a bunch of batches of reads of the same keys from an index.
94 [testReadCache, 50, kUseIndex],
95 // Make a small bunch of batches of reads of the same keys from an object store.
96 [testReadCache, 10, kDontUseIndex],
97 // Make a bunch of batches of reads of the same keys from an index.
98 [testReadCache, 50, kUseIndex],
99 // Create and delete an index on a store that already contains data [produces
100 // a timing result for each of creation and deletion].
101 [testCreateAndDeleteIndex, 5000],
102 // Walk through multiple cursors into the same object store, round-robin, until
103 // you've reached the end of each of them.
104 [testWalkingMultipleCursors, 5],
105 // Walk through many cursors into the same object store, round-robin, until
106 // you've reached the end of each of them.
107 [testWalkingMultipleCursors, 50],
108 // Open an object store cursor, then continue(key) to the last value.
109 [testCursorSeeks, 2000, 10, 4, kDontUseIndex],
110 // Open an index key cursor, then continue(key) to the last value.
111 [testCursorSeeks, 2000, 10, 4, kUseIndex],
114 var currentTest = 0;
116 function test() {
117 runNextTest();
120 function runNextTest() {
121 var filter = window.location.hash.slice(1);
122 var test, f;
123 while (currentTest < tests.length) {
124 test = tests[currentTest];
125 f = test.shift();
126 if (!filter || f.name == filter)
127 break;
128 ++currentTest;
131 if (currentTest < tests.length) {
132 test.push(runNextTest);
133 f.apply(null, test);
134 ++currentTest;
135 } else {
136 onAllTestsComplete();
140 function onAllTestsComplete() {
141 var overallDuration = window.performance.now() - overallTestStartTime;
142 automation.addResult("OverallTestDuration", overallDuration);
143 automation.setDone();
146 // This is the only test that includes database creation and deletion in its
147 // results; the others just test specific operations. To see only the
148 // creation/deletion without the specific operations used to build up the data
149 // in the object stores here, subtract off the results of
150 // testCreateKeysInStores.
151 function testCreateAndDeleteDatabase(
152 numKeys, numStores, payloadLength, onTestComplete) {
153 var testName = getDisplayName(arguments);
154 assert(numKeys >= 0);
155 assert(numStores >= 1);
156 var objectStoreNames = [];
157 for (var i=0; i < numStores; ++i) {
158 objectStoreNames.push("store " + i);
160 var value = stringOfLength(payloadLength);
161 function getValue() {
162 return value;
165 automation.setStatus("Creating database.");
166 var startTime = window.performance.now();
168 createDatabase(testName, objectStoreNames, onCreated, onError);
170 function onCreated(db) {
171 automation.setStatus("Constructing transaction.");
172 var transaction =
173 getTransaction(db, objectStoreNames, "readwrite",
174 function() { onValuesWritten(db); });
175 putLinearValues(transaction, objectStoreNames, numKeys, null, getValue);
178 function onValuesWritten(db) {
179 automation.setStatus("Deleting database.");
180 db.close();
181 deleteDatabase(testName, onDeleted);
184 function onDeleted() {
185 var duration = window.performance.now() - startTime;
186 automation.addResult(testName, duration);
187 automation.setStatus("Deleted database.");
188 onTestComplete();
192 function testCreateKeysInStores(
193 numKeys, numStores, payloadLength, onTestComplete) {
194 var testName = getDisplayName(arguments);
195 assert(numKeys >= 0);
196 assert(numStores >= 1);
197 var objectStoreNames = [];
198 for (var i=0; i < numStores; ++i) {
199 objectStoreNames.push("store " + i);
201 var value = stringOfLength(payloadLength);
202 function getValue() {
203 return value;
206 automation.setStatus("Creating database.");
207 createDatabase(testName, objectStoreNames, onCreated, onError);
209 function onCreated(db) {
210 automation.setStatus("Constructing transaction.");
211 var completionFunc =
212 getCompletionFunc(db, testName, window.performance.now(),
213 onTestComplete);
214 var transaction =
215 getTransaction(db, objectStoreNames, "readwrite", completionFunc);
216 putLinearValues(transaction, objectStoreNames, numKeys, null, getValue);
220 function testRandomReadsAndWrites(
221 numKeys, numReadsPerTransaction, numWritesPerTransaction, numTransactions,
222 useIndexForReads, onTestComplete) {
223 var indexName;
224 if (useIndexForReads)
225 indexName = "index";
226 var testName = getDisplayName(arguments);
227 var objectStoreNames = ["store"];
228 var getKey = getSimpleKey;
229 var getValue = useIndexForReads ? getIndexableValue : getSimpleValue;
231 automation.setStatus("Creating database.");
232 var options;
233 if (useIndexForReads) {
234 options = [{
235 indexName: indexName,
236 indexKeyPath: "id",
237 indexIsUnique: false,
238 indexIsMultiEntry: false,
241 createDatabase(testName, objectStoreNames, onCreated, onError, options);
243 function onCreated(db) {
244 automation.setStatus("Setting up test database.");
245 var transaction = getTransaction(db, objectStoreNames, "readwrite",
246 function() { onSetupComplete(db); });
247 putLinearValues(transaction, objectStoreNames, numKeys, null,
248 function() { return "test value"; });
251 function onSetupComplete(db) {
252 automation.setStatus("Setup complete.");
253 var completionFunc =
254 getCompletionFunc(db, testName, window.performance.now(),
255 onTestComplete);
256 var mode = "readonly";
257 if (numWritesPerTransaction)
258 mode = "readwrite";
259 runTransactionBatch(db, numTransactions, batchFunc, objectStoreNames, mode,
260 completionFunc);
263 function batchFunc(transaction) {
264 getRandomValues(transaction, objectStoreNames, numReadsPerTransaction,
265 numKeys, indexName, getKey);
266 putRandomValues(transaction, objectStoreNames, numWritesPerTransaction,
267 numKeys, getKey, getValue);
271 function testReadCache(numTransactions, useIndexForReads, onTestComplete) {
272 var numKeys = 10000;
273 var numReadsPerTransaction = 50;
274 var numTransactionsLeft = numTransactions;
275 var indexName;
276 if (useIndexForReads)
277 indexName = "index";
278 var testName = getDisplayName(arguments);
279 var objectStoreNames = ["store"];
280 var getKey = getSimpleKey;
281 var getValue = useIndexForReads ? getIndexableValue : getSimpleValue;
282 var keys = [];
284 for (var i=0; i < numReadsPerTransaction; ++i) {
285 keys.push(getKey(Math.floor(random() * numKeys)));
288 automation.setStatus("Creating database.");
289 var options;
290 if (useIndexForReads) {
291 options = [{
292 indexName: indexName,
293 indexKeyPath: "id",
294 indexIsUnique: false,
295 indexIsMultiEntry: false,
298 createDatabase(testName, objectStoreNames, onCreated, onError, options);
300 function onCreated(db) {
301 automation.setStatus("Setting up test database.");
302 var transaction = getTransaction(db, objectStoreNames, "readwrite",
303 function() { onSetupComplete(db); });
304 putLinearValues(transaction, objectStoreNames, numKeys, getKey,
305 getValue);
308 var completionFunc;
309 function onSetupComplete(db) {
310 automation.setStatus("Setup complete.");
311 completionFunc =
312 getCompletionFunc(db, testName, window.performance.now(),
313 onTestComplete);
314 runTransactionBatch(db, numTransactions, batchFunc, objectStoreNames,
315 "readonly", completionFunc);
318 function batchFunc(transaction) {
319 getSpecificValues(transaction, objectStoreNames, indexName, keys);
323 function testCreateAndDeleteIndex(numKeys, onTestComplete) {
324 var testName = getDisplayName(arguments);
325 var objectStoreNames = ["store"];
327 automation.setStatus("Creating database.");
328 createDatabase(testName, objectStoreNames, onCreated, onError);
330 var startTime;
331 function onCreated(db) {
332 automation.setStatus("Initializing data.");
333 var transaction = getTransaction(db, objectStoreNames, "readwrite",
334 function() { onPopulated(db); });
335 putLinearValues(transaction, objectStoreNames, numKeys, null, getValue);
338 function getValue(i) {
339 return { firstName: i + " first name", lastName: i + " last name" };
342 function onPopulated(db) {
343 db.close();
344 automation.setStatus("Building index.");
345 startTime = window.performance.now();
346 var f = function(objectStore) {
347 objectStore.createIndex("index", "firstName", {unique: true});
349 alterObjectStores(testName, objectStoreNames, f, onIndexCreated, onError);
352 var indexCreationCompleteTime;
353 function onIndexCreated(db) {
354 db.close();
355 indexCreationCompleteTime = window.performance.now();
356 automation.addResult("testCreateIndex",
357 indexCreationCompleteTime - startTime);
358 var f = function(objectStore) {
359 objectStore.deleteIndex("index");
361 automation.setStatus("Deleting index.");
362 alterObjectStores(testName, objectStoreNames, f, onIndexDeleted, onError);
365 function onIndexDeleted(db) {
366 var duration = window.performance.now() - indexCreationCompleteTime;
367 // Ignore the cleanup time for this test.
368 automation.addResult("testDeleteIndex", duration);
369 automation.setStatus("Deleting database.");
370 db.close();
371 deleteDatabase(testName, onDeleted);
374 function onDeleted() {
375 automation.setStatus("Deleted database.");
376 onTestComplete();
380 function testCursorReadsAndRandomWrites(
381 readKeysOnly, useIndexForReads, writeAlso, sameStoreForWrites,
382 onTestComplete) {
383 // There's no key cursor unless you're reading from an index.
384 assert(useIndexForReads || !readKeysOnly);
385 // If we're writing to another store, having an index would constrain our
386 // writes, as we create both object stores with the same configurations.
387 // We could do that if needed, but it's simpler not to.
388 assert(!useIndexForReads || !writeAlso);
389 var numKeys = 10000;
390 var numReadsPerTransaction = 1000;
391 var testName = getDisplayName(arguments);
392 var objectStoreNames = ["input store"];
393 var outputStoreName;
394 if (writeAlso) {
395 if (sameStoreForWrites) {
396 outputStoreName = objectStoreNames[0];
397 } else {
398 outputStoreName = "output store";
399 objectStoreNames.push(outputStoreName);
402 var getKeyForRead = getSimpleKey;
403 var indexName;
404 if (useIndexForReads) {
405 indexName = "index";
406 getKeyForRead = function(i) {
407 // This depends on the implementations of getValuesFromCursor and
408 // getObjectValue. We reverse the order of the iteration here so that
409 // setting up bounds from k to k+n with n>0 works. Without this reversal,
410 // the upper bound is below the lower bound.
411 return getBackwardIndexKey(numKeys - i);
415 automation.setStatus("Creating database.");
416 var options;
417 if (useIndexForReads) {
418 options = [{
419 indexName: indexName,
420 indexKeyPath: "lastName", // depends on getBackwardIndexKey()
421 indexIsUnique: true,
422 indexIsMultiEntry: false,
425 createDatabase(testName, objectStoreNames, onCreated, onError, options);
427 function onCreated(db) {
428 automation.setStatus("Setting up test database.");
429 var transaction = getTransaction(db, objectStoreNames, "readwrite",
430 function() { onSetupComplete(db); });
431 putLinearValues(transaction, objectStoreNames, numKeys, getSimpleKey,
432 getObjectValue);
434 function onSetupComplete(db) {
435 automation.setStatus("Setup complete.");
436 var completionFunc =
437 getCompletionFunc(db, testName, window.performance.now(),
438 onTestComplete);
439 var mode = "readonly";
440 if (writeAlso)
441 mode = "readwrite";
442 var transaction =
443 getTransaction(db, objectStoreNames, mode, completionFunc);
445 getValuesFromCursor(
446 transaction, objectStoreNames[0], numReadsPerTransaction, numKeys,
447 indexName, getKeyForRead, readKeysOnly, outputStoreName);
451 function testSporadicWrites(
452 numOperationsPerTransaction, numIndices, alternateWithReads,
453 onTestComplete) {
454 var numKeys = 1000;
455 // With 30 transactions, spaced 50ms apart, we'll need at least 1.5s.
456 var numTransactions = 30;
457 if (alternateWithReads)
458 numTransactions *= 2;
459 var delayBetweenBatches = 50;
460 var indexName;
461 var testName = getDisplayName(arguments);
462 var numTransactionsLeft = numTransactions;
463 var objectStoreNames = ["store"];
464 var numTransactionsRunning = 0;
466 var getValue = getSimpleValue;
467 if (numIndices)
468 getValue = function (i) { return getNFieldObjectValue(i, numIndices); };
470 automation.setStatus("Creating database.");
471 var options = [];
472 for (var i=0; i < numIndices; ++i) {
473 var o = {};
474 o.indexName = "index " + i;
475 o.indexKeyPath = getNFieldName(i);
476 o.indexIsUnique = false;
477 o.indexIsMultiEntry = false;
478 options.push(o);
480 createDatabase(testName, objectStoreNames, onCreated, onError, options);
482 function onCreated(db) {
483 automation.setStatus("Setting up test database.");
484 var transaction = getTransaction(db, objectStoreNames, "readwrite",
485 function() { onSetupComplete(db); });
486 putLinearValues(transaction, objectStoreNames, numKeys, getSimpleKey,
487 getValue);
489 var completionFunc;
490 function onSetupComplete(db) {
491 automation.setStatus("Setup complete.");
492 completionFunc =
493 getCompletionFunc(db, testName, window.performance.now(),
494 onTestComplete);
495 runOneBatch(db);
498 function runOneBatch(db) {
499 assert(numTransactionsLeft);
500 if (--numTransactionsLeft) {
501 setTimeout(function () { runOneBatch(db); }, delayBetweenBatches);
504 var mode, transaction;
505 if (alternateWithReads) {
506 ++numTransactionsRunning;
507 transaction =
508 getTransaction(db, objectStoreNames, "readonly", batchComplete);
509 getRandomValues(transaction, objectStoreNames,
510 numOperationsPerTransaction, numKeys);
512 ++numTransactionsRunning;
513 transaction =
514 getTransaction(db, objectStoreNames, "readwrite", batchComplete);
515 putRandomValues(transaction, objectStoreNames, numOperationsPerTransaction,
516 numKeys);
518 function batchComplete() {
519 assert(numTransactionsRunning);
520 if (!--numTransactionsRunning && !numTransactionsLeft)
521 completionFunc();
526 function testWalkingMultipleCursors(numCursors, onTestComplete) {
527 var numKeys = 1000;
528 var numHitsPerKey = 10;
529 var testName = getDisplayName(arguments);
530 var objectStoreNames = ["input store"];
531 var indexName = "index name";
532 var getKey = getSimpleKey;
533 var getValue = getIndexableValue;
535 automation.setStatus("Creating database.");
536 var options = [{
537 indexName: indexName,
538 indexKeyPath: "id",
539 indexIsUnique: false,
540 indexIsMultiEntry: false,
542 createDatabase(testName, objectStoreNames, onCreated, onError, options);
544 function onCreated(db) {
545 automation.setStatus("Setting up test database.");
546 var transaction = getTransaction(db, objectStoreNames, "readwrite",
547 function() { onSetupComplete(db); });
548 // This loop adds the same value numHitsPerKey times for each key.
549 for (var i = 0; i < numHitsPerKey; ++i) {
550 putLinearValues(transaction, objectStoreNames, numKeys, getKeyFunc(i),
551 getValue);
554 // While the value is the same each time through the putLinearValues loop, we
555 // want the key to keep increaasing for each copy.
556 function getKeyFunc(k) {
557 return function(i) {
558 return getKey(k * numKeys + i);
561 var completionFunc;
562 function onSetupComplete(db) {
563 automation.setStatus("Setup complete.");
564 completionFunc =
565 getCompletionFunc(db, testName, window.performance.now(),
566 onTestComplete);
567 var transaction =
568 getTransaction(db, objectStoreNames, "readonly", verifyComplete);
570 walkSeveralCursors(transaction, numKeys);
572 var responseCounts = [];
573 var cursorsRunning = numCursors;
574 function walkSeveralCursors(transaction, numKeys) {
575 var source = transaction.objectStore(objectStoreNames[0]).index(indexName);
576 var requests = [];
577 var continueCursorIndex = 0;
578 for (var i = 0; i < numCursors; ++i) {
579 var rand = Math.floor(random() * numKeys);
580 // Since we have numHitsPerKey copies of each value in the database,
581 // IDBKeyRange.only will return numHitsPerKey results, each referring to a
582 // different key with the matching value.
583 var request = source.openCursor(IDBKeyRange.only(getSimpleValue(rand)));
584 responseCounts.push(0);
585 request.onerror = onError;
586 request.onsuccess = function(event) {
587 assert(cursorsRunning);
588 var request = event.target;
589 if (!("requestIndex" in request)) {
590 assert(requests.length < numCursors);
591 request.requestIndex = requests.length;
592 requests.push(request);
594 var cursor = event.target.result;
595 if (cursor) {
596 assert(responseCounts[request.requestIndex] < numHitsPerKey);
597 ++responseCounts[request.requestIndex];
598 } else {
599 assert(responseCounts[request.requestIndex] == numHitsPerKey);
600 --cursorsRunning;
602 if (cursorsRunning) {
603 if (requests.length == numCursors) {
604 requests[continueCursorIndex++].result.continue();
605 continueCursorIndex %= numCursors;
611 function verifyComplete() {
612 assert(!cursorsRunning);
613 completionFunc();
617 function testCursorSeeks(
618 numKeys, numSeeksPerTransaction, numTransactions, useIndexForReads,
619 onTestComplete) {
620 var testName = getDisplayName(arguments);
621 var objectStoreNames = ["store"];
622 var getKey = useIndexForReads ? getForwardIndexKey : getSimpleKey;
623 var indexName;
624 if (useIndexForReads) {
625 indexName = "index";
628 automation.setStatus("Creating database.");
629 var options;
630 if (useIndexForReads) {
631 options = [{
632 indexName: indexName,
633 indexKeyPath: "firstName",
634 indexIsUnique: true,
635 indexIsMultiEntry: false,
638 createDatabase(testName, objectStoreNames, onCreated, onError, options);
640 function onCreated(db) {
641 automation.setStatus("Setting up test database.");
642 var transaction = getTransaction(db, objectStoreNames, "readwrite",
643 function() { onSetupComplete(db); });
644 putLinearValues(transaction, objectStoreNames, numKeys, getSimpleKey,
645 getObjectValue);
648 function onSetupComplete(db) {
649 automation.setStatus("Setup complete.");
650 var completionFunc =
651 getCompletionFunc(db, testName, window.performance.now(),
652 onTestComplete);
653 var mode = "readonly";
654 runTransactionBatch(db, numTransactions, batchFunc, objectStoreNames, mode,
655 completionFunc);
658 function batchFunc(transaction) {
659 for (var i in objectStoreNames) {
660 var source = transaction.objectStore(objectStoreNames[i]);
661 if (useIndexForReads)
662 source = source.index(indexName);
663 for (var j = 0; j < numSeeksPerTransaction; ++j) {
664 randomSeek(source);
669 function randomSeek(source) {
670 var request = useIndexForReads ? source.openKeyCursor()
671 : source.openCursor();
672 var first = true;
673 request.onerror = onError;
674 request.onsuccess = function() {
675 var cursor = request.result;
676 if (cursor && first) {
677 first = false;
678 cursor.continue(getKey(numKeys - 1));