Skip to content

Commit

Permalink
IndexedDB: Speed up and refactor IDBObjectStore get all WPTs
Browse files Browse the repository at this point in the history
Updates test setup to only create the object store used by the test.
Prior to this change, test setup always created all object stores, but
each test only used a single object store.

The `IDBObjectStore::getAllRecords()` change
(https://chromium-review.googlesource.com/c/chromium/src/+/5911615)
introduced a large value test, which caused timeouts when running in
slow test environments.  Skipping setup of the large value object store
improves test runtimes.

The change also adds `META: timeout=long` since the `IDBObjectStore`
get all  WPTs include many test cases. This should also help prevent
future timeouts.

The change refactors the `IDBObjectStore` get all WPTs to run the same
test code.  The change introduces the `get_all_test()` helper function
that includes a `getAllFunctionName` argument to select which
get all function to run: `getAllKeys()`, `getAll()` or
`getAllRecords()`.  `getAllFunctionName` also selects which output
verification to perform, which either verifies an array of keys,
values, or `IDBRecords`.  This refactoring eliminates code duplicated
across the `IDBObjectStore` get all WPTs.

Bug: 378869818,383706488,382575666,380653805
Change-Id: I8c3351f6d19ac1bf016c7f478d64dbe4b415c7b8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6098322
Reviewed-by: Evan Stade <estade@chromium.org>
Reviewed-by: Rahul Singh <rahsin@microsoft.com>
Commit-Queue: Steve Becker <stevebe@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1397443}
  • Loading branch information
SteveBeckerMSFT authored and chromium-wpt-export-bot committed Dec 17, 2024
1 parent 81fc365 commit 810cb30
Show file tree
Hide file tree
Showing 4 changed files with 386 additions and 433 deletions.
283 changes: 93 additions & 190 deletions IndexedDB/idbobjectstore_getAll.any.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,196 +4,99 @@
// META: script=resources/support.js
// META: script=resources/support-get-all.js
// META: script=resources/support-promises.js
// META: timeout=long

'use strict';

function createGetAllRequest(t, storeName, connection, keyRange, maxCount) {
const transaction = connection.transaction(storeName, 'readonly');
const store = transaction.objectStore(storeName);
const req = store.getAll(keyRange, maxCount);
req.onerror = t.unreached_func('getAll request should succeed');
return req;
}

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'out-of-line', connection, 'c');
req.onsuccess = t.step_func(evt => {
assert_array_equals(evt.target.result, ['value-c']);
t.done();
});
}, 'Single item get');

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'generated', connection, 3);
req.onsuccess = t.step_func(evt => {
const data = evt.target.result;
assert_true(Array.isArray(data));
assert_equals(data.length, 1);
assert_equals(data[0].id, 3);
assert_equals(data[0].ch, 'c');
t.done();
});
}, 'Single item get (generated key)');

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'empty', connection);
req.onsuccess = t.step_func(evt => {
assert_array_equals(evt.target.result, [],
'getAll() on empty object store should return an empty array');
t.done();
});
}, 'getAll on empty object store');

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'out-of-line', connection);
req.onsuccess = t.step_func(evt => {
assert_array_equals(evt.target.result, alphabet.map(c => `value-${c}`));
t.done();
});
}, 'Get all values');

object_store_get_all_test((test, connection) => {
const request = createGetAllRequest(test, 'large-values', connection);
request.onsuccess = test.step_func(event => {
const actualResults = event.target.result;
assert_true(Array.isArray(actualResults), 'The results must be an array');

const expectedRecords = expectedObjectStoreRecords['large-values'];
assert_equals(
actualResults.length, expectedRecords.length,
'The results array must contain the expected number of records');

// Verify each large value.
for (let i = 0; i < expectedRecords.length; i++) {
assert_large_array_equals(
actualResults[i], expectedRecords[i].value,
'The record must have the expected value');
}
test.done();
});
}, 'Get all with large values');

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'out-of-line', connection, undefined,
10);
req.onsuccess = t.step_func(evt => {
assert_array_equals(evt.target.result, 'abcdefghij'.split('').map(c => `value-${c}`));
t.done();
});
}, 'Test maxCount');

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'out-of-line', connection,
IDBKeyRange.bound('g', 'm'));
req.onsuccess = t.step_func(evt => {
assert_array_equals(evt.target.result, 'ghijklm'.split('').map(c => `value-${c}`));
t.done();
});
}, 'Get bound range');

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'out-of-line', connection,
IDBKeyRange.bound('g', 'm'), 3);
req.onsuccess = t.step_func(evt => {
assert_array_equals(evt.target.result, ['g', 'h', 'i'].map(c => `value-${c}`));
t.done();
});
}, 'Get bound range with maxCount');

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'out-of-line', connection,
IDBKeyRange.bound('g', 'k', false, true));
req.onsuccess = t.step_func(evt => {
assert_array_equals(evt.target.result, ['g', 'h', 'i', 'j'].map(c => `value-${c}`));
t.done();
});
}, 'Get upper excluded');

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'out-of-line', connection,
IDBKeyRange.bound('g', 'k', true, false));
req.onsuccess = t.step_func(evt => {
assert_array_equals(evt.target.result, ['h', 'i', 'j', 'k'].map(c => `value-${c}`));
t.done();
});
}, 'Get lower excluded');

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'generated', connection,
IDBKeyRange.bound(4, 15), 3);
req.onsuccess = t.step_func(evt => {
const data = evt.target.result;
assert_true(Array.isArray(data));
assert_array_equals(data.map(e => e.ch), ['d', 'e', 'f']);
assert_array_equals(data.map(e => e.id), [4, 5, 6]);
t.done();
});
}, 'Get bound range (generated) with maxCount');

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'out-of-line', connection,
"Doesn't exist");
req.onsuccess = t.step_func(evt => {
assert_array_equals(evt.target.result, [],
'getAll() using a nonexistent key should return an empty array');
t.done();
});
req.onerror = t.unreached_func('getAll request should succeed');
}, 'Non existent key');

object_store_get_all_test((t, connection) => {
const req = createGetAllRequest(t, 'out-of-line', connection, undefined, 0);
req.onsuccess = t.step_func(evt => {
assert_array_equals(evt.target.result, alphabet.map(c => `value-${c}`));
t.done();
});
}, 'zero maxCount');

object_store_get_all_test((test, connection) => {
const request = createGetAllRequest(
test, 'out-of-line', connection, /*query=*/ undefined,
/*count=*/ 4294967295);
request.onsuccess = test.step_func(event => {
assert_array_equals(event.target.result, alphabet.map(c => `value-${c}`));
test.done();
});
}, 'Max value count');

object_store_get_all_test((test, connection) => {
const request = createGetAllRequest(
test, /*storeName=*/ 'out-of-line', connection,
IDBKeyRange.upperBound('0'));
request.onsuccess = test.step_func((event) => {
assert_array_equals(
event.target.result, /*expectedResults=*/[],
'getAll() with an empty query range must return an empty array');
test.done();
});
}, 'Query with empty range where first key < upperBound');

object_store_get_all_test((test, connection) => {
const request = createGetAllRequest(
test, /*storeName=*/ 'out-of-line', connection,
IDBKeyRange.lowerBound('zz'));
request.onsuccess = test.step_func((event) => {
assert_array_equals(
event.target.result, /*expectedResults=*/[],
'getAll() with an empty query range must return an empty array');
test.done();
});
}, 'Query with empty range where lowerBound < last key');

object_store_get_all_test((t, connection) => {
const transaction = connection.transaction('out-of-line', 'readonly');
const store = transaction.objectStore('out-of-line');
const req = store.getAll();
transaction.commit();
transaction.oncomplete =
t.unreached_func('transaction completed before request succeeded');

req.onerror = t.unreached_func('getAll request should succeed');
req.onsuccess = t.step_func(evt => {
assert_array_equals(evt.target.result, alphabet.map(c => `value-${c}`));
transaction.oncomplete = t.step_func_done();
});
}, 'Get all values with transaction.commit()');
object_store_get_all_values_test(
/*storeName=*/ 'out-of-line', /*options=*/ {query: 'c'}, 'Single item get');

object_store_get_all_values_test(
/*storeName=*/ 'generated', /*options=*/ {query: 3},
'Single item get (generated key)');

object_store_get_all_values_test(
/*storeName=*/ 'empty', /*options=*/ undefined,
'getAll on empty object store');

object_store_get_all_values_test(
/*storeName=*/ 'out-of-line', /*options=*/ undefined, 'Get all values');

object_store_get_all_values_test(
/*storeName=*/ 'large-values', /*options=*/ undefined,
'Get all with large values');

object_store_get_all_values_test(
/*storeName=*/ 'out-of-line', /*options=*/ {count: 10}, 'Test maxCount');

object_store_get_all_values_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.bound('g', 'm')}, 'Get bound range');

object_store_get_all_values_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.bound('g', 'm'), count: 3},
'Get bound range with maxCount');

object_store_get_all_values_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {
query:
IDBKeyRange.bound('g', 'k', /*lowerOpen=*/ false, /*upperOpen=*/ true)
},
'Get upper excluded');

object_store_get_all_values_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {
query:
IDBKeyRange.bound('g', 'k', /*lowerOpen=*/ true, /*upperOpen=*/ false)
},
'Get lower excluded');

object_store_get_all_values_test(
/*storeName=*/ 'generated',
/*options=*/ {query: IDBKeyRange.bound(4, 15), count: 3},
'Get bound range (generated) with maxCount');

object_store_get_all_values_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: 'Doesn\'t exist'}, 'Non existent key');

object_store_get_all_values_test(
/*storeName=*/ 'out-of-line', /*options=*/ {count: 0}, 'zero maxCount');

object_store_get_all_values_test(
/*storeName=*/ 'out-of-line', /*options=*/ {count: 4294967295},
'Max value count');

object_store_get_all_values_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.upperBound('0')},
'Query with empty range where first key < upperBound');

object_store_get_all_values_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.lowerBound('zz')},
'Query with empty range where lowerBound < last key');

object_store_get_all_test_setup(
/*storeName=*/ 'out-of-line', (test, connection, expectedRecords) => {
const transaction = connection.transaction('out-of-line', 'readonly');
const store = transaction.objectStore('out-of-line');
const request = store.getAll();
transaction.commit();
transaction.oncomplete =
test.unreached_func('transaction completed before request succeeded');

request.onerror = test.unreached_func('getAll request should succeed');
request.onsuccess = test.step_func(event => {
// Convert the expected array of records to an array of IDB values.
const expectedResults =
calculateExpectedGetAllResults('getAll', expectedRecords);

const actualResults = event.target.result;
verifyGetAllResults('getAll', actualResults, expectedResults);
transaction.oncomplete = test.step_func_done();
});
}, 'Get all values with transaction.commit()');
Loading

0 comments on commit 810cb30

Please sign in to comment.