Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chrome / test / data / indexeddb / perf_test.js
blob853fbbfdf9e897fd26384b5b0dcebc7b09068c0e
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 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],
95 var currentTest = 0;
97 function test() {
98   runNextTest();
101 function runNextTest() {
102   var filter = window.location.hash.slice(1);
103   var test, f;
104   while (currentTest < tests.length) {
105     test = tests[currentTest];
106     f = test.shift();
107     if (!filter || f.name == filter)
108       break;
109     ++currentTest;
110   }
112   if (currentTest < tests.length) {
113     test.push(runNextTest);
114     f.apply(null, test);
115     ++currentTest;
116   } else {
117     onAllTestsComplete();
118   }
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);
140   }
141   var value = stringOfLength(payloadLength);
142   function getValue() {
143     return value;
144   }
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.");
153     var transaction =
154         getTransaction(db, objectStoreNames, "readwrite",
155             function() { onValuesWritten(db); });
156     putLinearValues(transaction, objectStoreNames, numKeys, null, getValue);
157   }
159   function onValuesWritten(db) {
160     automation.setStatus("Deleting database.");
161     db.close();
162     deleteDatabase(testName, onDeleted);
163   }
165   function onDeleted() {
166     var duration = window.performance.now() - startTime;
167     automation.addResult(testName, duration);
168     automation.setStatus("Deleted database.");
169     onTestComplete();
170   }
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);
181   }
182   var value = stringOfLength(payloadLength);
183   function getValue() {
184     return value;
185   }
187   automation.setStatus("Creating database.");
188   createDatabase(testName, objectStoreNames, onCreated, onError);
190   function onCreated(db) {
191     automation.setStatus("Constructing transaction.");
192     var completionFunc =
193         getCompletionFunc(db, testName, window.performance.now(),
194             onTestComplete);
195     var transaction =
196         getTransaction(db, objectStoreNames, "readwrite", completionFunc);
197     putLinearValues(transaction, objectStoreNames, numKeys, null, getValue);
198   }
201 function testRandomReadsAndWrites(
202     numKeys, numReadsPerTransaction, numWritesPerTransaction, numTransactions,
203     useIndexForReads, onTestComplete) {
204   var indexName;
205   if (useIndexForReads)
206     indexName = "index";
207   var testName = getDisplayName(arguments);
208   var objectStoreNames = ["store"];
209   var getKey = getSimpleKey;
210   var getValue = useIndexForReads ? getIndexableValue : getSimpleValue;
212   automation.setStatus("Creating database.");
213   var options;
214   if (useIndexForReads) {
215     options = [{
216       indexName: indexName,
217       indexKeyPath: "id",
218       indexIsUnique: false,
219       indexIsMultiEntry: false,
220     }];
221   }
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"; });
230   }
232   function onSetupComplete(db) {
233     automation.setStatus("Setup complete.");
234     var completionFunc =
235         getCompletionFunc(db, testName, window.performance.now(),
236             onTestComplete);
237     var mode = "readonly";
238     if (numWritesPerTransaction)
239       mode = "readwrite";
240     runTransactionBatch(db, numTransactions, batchFunc, objectStoreNames, mode,
241         completionFunc);
242   }
244   function batchFunc(transaction) {
245     getRandomValues(transaction, objectStoreNames, numReadsPerTransaction,
246         numKeys, indexName, getKey);
247     putRandomValues(transaction, objectStoreNames, numWritesPerTransaction,
248         numKeys, getKey, getValue);
249   }
252 function testReadCache(numTransactions, useIndexForReads, onTestComplete) {
253   var numKeys = 10000;
254   var numReadsPerTransaction = 50;
255   var numTransactionsLeft = numTransactions;
256   var indexName;
257   if (useIndexForReads)
258     indexName = "index";
259   var testName = getDisplayName(arguments);
260   var objectStoreNames = ["store"];
261   var getKey = getSimpleKey;
262   var getValue = useIndexForReads ? getIndexableValue : getSimpleValue;
263   var keys = [];
265   for (var i=0; i < numReadsPerTransaction; ++i) {
266     keys.push(getKey(Math.floor(random() * numKeys)));
267   }
269   automation.setStatus("Creating database.");
270   var options;
271   if (useIndexForReads) {
272     options = [{
273       indexName: indexName,
274       indexKeyPath: "id",
275       indexIsUnique: false,
276       indexIsMultiEntry: false,
277     }];
278   }
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,
286         getValue);
287   }
289   var completionFunc;
290   function onSetupComplete(db) {
291     automation.setStatus("Setup complete.");
292     completionFunc =
293         getCompletionFunc(db, testName, window.performance.now(),
294             onTestComplete);
295     runTransactionBatch(db, numTransactions, batchFunc, objectStoreNames,
296         "readonly", completionFunc);
297   }
299   function batchFunc(transaction) {
300     getSpecificValues(transaction, objectStoreNames, indexName, keys);
301   }
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);
311   var startTime;
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);
317   }
319   function getValue(i) {
320     return { firstName: i + " first name", lastName: i + " last name" };
321   }
323   function onPopulated(db) {
324     db.close();
325     automation.setStatus("Building index.");
326     startTime = window.performance.now();
327     var f = function(objectStore) {
328       objectStore.createIndex("index", "firstName", {unique: true});
329     };
330     alterObjectStores(testName, objectStoreNames, f, onIndexCreated, onError);
331   }
333   var indexCreationCompleteTime;
334   function onIndexCreated(db) {
335     db.close();
336     indexCreationCompleteTime = window.performance.now();
337     automation.addResult("testCreateIndex",
338         indexCreationCompleteTime - startTime);
339     var f = function(objectStore) {
340       objectStore.deleteIndex("index");
341     };
342     automation.setStatus("Deleting index.");
343     alterObjectStores(testName, objectStoreNames, f, onIndexDeleted, onError);
344   }
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.");
351     db.close();
352     deleteDatabase(testName, onDeleted);
353   }
355   function onDeleted() {
356     automation.setStatus("Deleted database.");
357     onTestComplete();
358   }
361 function testCursorReadsAndRandomWrites(
362     readKeysOnly, useIndexForReads, writeAlso, sameStoreForWrites,
363     onTestComplete) {
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);
370   var numKeys = 10000;
371   var numReadsPerTransaction = 1000;
372   var testName = getDisplayName(arguments);
373   var objectStoreNames = ["input store"];
374   var outputStoreName;
375   if (writeAlso) {
376     if (sameStoreForWrites) {
377       outputStoreName = objectStoreNames[0];
378     } else {
379       outputStoreName = "output store";
380       objectStoreNames.push(outputStoreName);
381     }
382   }
383   var getKeyForRead = getSimpleKey;
384   var indexName;
385   if (useIndexForReads) {
386     indexName = "index";
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);
393     };
394   }
396   automation.setStatus("Creating database.");
397   var options;
398   if (useIndexForReads) {
399     options = [{
400       indexName: indexName,
401       indexKeyPath: "lastName", // depends on getBackwardIndexKey()
402       indexIsUnique: true,
403       indexIsMultiEntry: false,
404     }];
405   }
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,
413         getObjectValue);
414   }
415   function onSetupComplete(db) {
416     automation.setStatus("Setup complete.");
417     var completionFunc =
418         getCompletionFunc(db, testName, window.performance.now(),
419             onTestComplete);
420     var mode = "readonly";
421     if (writeAlso)
422       mode = "readwrite";
423     var transaction =
424         getTransaction(db, objectStoreNames, mode, completionFunc);
426     getValuesFromCursor(
427         transaction, objectStoreNames[0], numReadsPerTransaction, numKeys,
428         indexName, getKeyForRead, readKeysOnly, outputStoreName);
429   }
432 function testWalkingMultipleCursors(numCursors, onTestComplete) {
433   var numKeys = 1000;
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.");
442   var options = [{
443     indexName: indexName,
444     indexKeyPath: "id",
445     indexIsUnique: false,
446     indexIsMultiEntry: false,
447   }];
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),
457           getValue);
458     }
459   }
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) {
463     return function(i) {
464       return getKey(k * numKeys + i);
465     };
466   }
467   var completionFunc;
468   function onSetupComplete(db) {
469     automation.setStatus("Setup complete.");
470     completionFunc =
471         getCompletionFunc(db, testName, window.performance.now(),
472             onTestComplete);
473     var transaction =
474         getTransaction(db, objectStoreNames, "readonly", verifyComplete);
476     walkSeveralCursors(transaction, numKeys);
477   }
478   var responseCounts = [];
479   var cursorsRunning = numCursors;
480   function walkSeveralCursors(transaction, numKeys) {
481     var source = transaction.objectStore(objectStoreNames[0]).index(indexName);
482     var requests = [];
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);
499         }
500         var cursor = event.target.result;
501         if (cursor) {
502           assert(responseCounts[request.requestIndex] < numHitsPerKey);
503           ++responseCounts[request.requestIndex];
504         } else {
505           assert(responseCounts[request.requestIndex] == numHitsPerKey);
506           --cursorsRunning;
507         }
508         if (cursorsRunning) {
509           if (requests.length == numCursors) {
510             requests[continueCursorIndex++].result.continue();
511             continueCursorIndex %= numCursors;
512           }
513         }
514       };
515     }
516   }
517   function verifyComplete() {
518     assert(!cursorsRunning);
519     completionFunc();
520   }
523 function testCursorSeeks(
524     numKeys, numSeeksPerTransaction, numTransactions, useIndexForReads,
525     onTestComplete) {
526   var testName = getDisplayName(arguments);
527   var objectStoreNames = ["store"];
528   var getKey = useIndexForReads ? getForwardIndexKey : getSimpleKey;
529   var indexName;
530   if (useIndexForReads) {
531     indexName = "index";
532   }
534   automation.setStatus("Creating database.");
535   var options;
536   if (useIndexForReads) {
537     options = [{
538       indexName: indexName,
539       indexKeyPath: "firstName",
540       indexIsUnique: true,
541       indexIsMultiEntry: false,
542     }];
543   }
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,
551         getObjectValue);
552   }
554   function onSetupComplete(db) {
555     automation.setStatus("Setup complete.");
556     var completionFunc =
557           getCompletionFunc(db, testName, window.performance.now(),
558               onTestComplete);
559     var mode = "readonly";
560     runTransactionBatch(db, numTransactions, batchFunc, objectStoreNames, mode,
561         completionFunc);
562   }
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) {
570         randomSeek(source);
571       }
572     }
573   }
575   function randomSeek(source) {
576     var request = useIndexForReads ? source.openKeyCursor()
577           : source.openCursor();
578     var first = true;
579     request.onerror = onError;
580     request.onsuccess = function() {
581       var cursor = request.result;
582       if (cursor && first) {
583         first = false;
584         cursor.continue(getKey(numKeys - 1));
585       }
586     };
587   }