Index: third_party/WebKit/LayoutTests/external/wpt/IndexedDB/request-event-ordering.html |
diff --git a/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/request-event-ordering.html b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/request-event-ordering.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a5440fa390fd809b4fd2630dc90fd6535dcb9aac |
--- /dev/null |
+++ b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/request-event-ordering.html |
@@ -0,0 +1,351 @@ |
+<!doctype html> |
+<meta charset="utf8"> |
+<meta name="timeout" content="long"> |
+<title>IndexedDB: request result events are delivered in order</title> |
+<link rel="help" href="https://w3c.github.io/IndexedDB/#abort-transaction"> |
+<link rel="author" href="pwnall@chromium.org" title="Victor Costan"> |
+<script src="/resources/testharness.js"></script> |
+<script src="/resources/testharnessreport.js"></script> |
+<script src="support-promises.js"></script> |
+<script> |
+'use strict'; |
+ |
+// Should be large enough to trigger large value handling in the IndexedDB |
+// engines that have special code paths for large values. |
+const wrapThreshold = 128 * 1024; |
+ |
+function populateStore(store) { |
+ store.put({id: 1, key: 'k1', value: largeValue(wrapThreshold, 1) }); |
+ store.put({id: 2, key: 'k2', value: ['small-2'] }); |
+ store.put({id: 3, key: 'k3', value: largeValue(wrapThreshold, 3) }); |
+ store.put({id: 4, key: 'k4', value: ['small-4'] }); |
+} |
+ |
+// Assigns cursor indexes for operations that require open cursors. |
+// |
+// Returns the number of open cursors required to perform all operations. |
+function assignCursors(operations) { |
+ return cursorCount; |
+} |
+ |
+// Opens index cursors for operations that require open cursors. |
+// |
+// onsuccess is called if all cursors are opened successfully. Otherwise, |
+// onerror will be called at least once. |
+function openCursors(testCase, index, operations, onerror, onsuccess) { |
+ let pendingCursors = 0; |
+ |
+ for (let operation of operations) { |
+ const opcode = operation[0]; |
+ const primaryKey = operation[1]; |
+ let request; |
+ switch (opcode) { |
+ case 'continue': |
+ request = index.openCursor( |
+ IDBKeyRange.lowerBound(`k${primaryKey - 1}`)); |
+ break; |
+ case 'continue-empty': |
+ // k4 is the last key in the data set, so calling continue() will get |
+ // the cursor past the end of the store. |
+ request = index.openCursor(IDBKeyRange.lowerBound('k4')); |
+ break; |
+ default: |
+ continue; |
+ } |
+ |
+ operation[2] = request; |
+ ++pendingCursors; |
+ |
+ request.onsuccess = testCase.step_func(() => { |
+ --pendingCursors; |
+ if (!pendingCursors) |
+ onsuccess(); |
+ }); |
+ request.onerror = testCase.step_func(onerror); |
+ } |
+ |
+ if (!pendingCursors) |
+ onsuccess(); |
+} |
+ |
+function doOperation(testCase, store, index, operation, requestId, results) { |
+ const opcode = operation[0]; |
+ const primaryKey = operation[1]; |
+ const cursor = operation[2]; |
+ |
+ return new Promise((resolve, reject) => { |
+ let request; |
+ switch (opcode) { |
+ case 'put': // Tests returning a primary key. |
+ request = store.put( |
+ { key: `k${primaryKey}`, value: [`small-${primaryKey}`] }); |
+ break; |
+ case 'get': // Tests returning a value. |
+ case 'get-empty': // Tests returning undefined. |
+ request = store.get(primaryKey); |
+ break; |
+ case 'getall': // Tests returning an array of values. |
+ request = store.getAll(); |
+ break; |
+ case 'error': // Tests returning an error. |
+ request = store.put( |
+ { key: `k${primaryKey}`, value: [`small-${primaryKey}`] }); |
+ request.onerror = testCase.step_func(event => { |
+ event.preventDefault(); |
+ results.push([requestId, request.error]); |
+ resolve(); |
+ }); |
+ request.onsuccess = testCase.step_func(() => { |
+ reject(new Error('put with duplicate primary key succeded')); |
+ }); |
+ break; |
+ case 'continue': // Tests returning a key, primary key, and value. |
+ request = cursor; |
+ cursor.result.continue(); |
+ request.onsuccess = testCase.step_func(() => { |
+ const result = request.result; |
+ results.push( |
+ [requestId, result.key, result.primaryKey, result.value]); |
+ resolve(); |
+ }); |
+ request.onerror = null; |
+ break; |
+ case 'open': // Tests returning a cursor, key, primary key, and value. |
+ request = index.openCursor(IDBKeyRange.lowerBound(`k${primaryKey}`)); |
+ request.onsuccess = testCase.step_func(() => { |
+ const result = request.result; |
+ results.push( |
+ [requestId, result.key, result.primaryKey, result.value]); |
+ resolve(); |
+ }); |
+ break; |
+ case 'continue-empty': // Tests returning a null result. |
+ request = cursor; |
+ cursor.result.continue(); |
+ request.onsuccess = testCase.step_func(() => { |
+ results.push([requestId, request.result]); |
+ resolve(); |
+ }); |
+ request.onerror = null; |
+ break; |
+ case 'open-empty': // Tests returning a null cursor. |
+ request = index.openCursor(IDBKeyRange.lowerBound(`k${primaryKey}`)); |
+ request.onsuccess = testCase.step_func(() => { |
+ const result = request.result; |
+ results.push([requestId, request.result]); |
+ resolve(); |
+ }); |
+ break; |
+ case 'count': // Tests returning a numeric result. |
+ request = index.count(); |
+ request.onsuccess = testCase.step_func(() => { |
+ results.push([requestId, request.result]); |
+ resolve(); |
+ }); |
+ break; |
+ }; |
+ |
+ if (!request.onsuccess) { |
+ request.onsuccess = testCase.step_func(() => { |
+ results.push([requestId, request.result]); |
+ resolve(); |
+ }); |
+ } |
+ if (!request.onerror) |
+ request.onerror = testCase.step_func(event => { |
+ event.preventDefault(); |
+ reject(request.error); |
+ }); |
+ }); |
+} |
+ |
+function checkOperationResult(operation, result, requestId) { |
+ const opcode = operation[0]; |
+ const primaryKey = operation[1]; |
+ |
+ const expectedValue = (primaryKey == 1 || primaryKey == 3) ? |
+ largeValue(wrapThreshold, primaryKey) : [`small-${primaryKey}`]; |
+ |
+ const requestIndex = result[0]; |
+ assert_equals( |
+ requestIndex, requestId, 'result event order should match request order'); |
+ switch (opcode) { |
+ case 'put': |
+ assert_equals( |
+ result[1], primaryKey, |
+ "put result should be the new object's primary key"); |
+ break; |
+ case 'get': |
+ assert_equals( |
+ result[1].id, primaryKey, |
+ 'get result should match put value (primary key)'); |
+ assert_equals( |
+ result[1].key, `k${primaryKey}`, |
+ 'get result should match put value (key)'); |
+ assert_equals( |
+ result[1].value.join(','), expectedValue.join(','), |
+ 'get result should match put value (nested value)'); |
+ break; |
+ case 'getall': |
+ assert_equals( |
+ result[1].length, primaryKey, |
+ 'getAll should return all the objects in the store'); |
+ for (let i = 0; i < primaryKey; ++i) { |
+ const object = result[1][i]; |
+ assert_equals( |
+ object.id, i + 1, |
+ `getAll result ${i + 1} should match put value (primary key)`); |
+ assert_equals( |
+ object.key, `k${i + 1}`, |
+ `get result ${i + 1} should match put value (key)`); |
+ |
+ const expectedValue = (i == 0 || i == 2) ? |
+ largeValue(wrapThreshold, i + 1) : [`small-${i + 1}`]; |
+ assert_equals( |
+ object.value.join(','), object.value.join(','), |
+ `get result ${i + 1} should match put value (nested value)`); |
+ } |
+ break; |
+ case 'get-empty': |
+ assert_equals( |
+ result[1], undefined, 'get-empty result should be undefined'); |
+ break; |
+ case 'error': |
+ assert_equals( |
+ result[1].name, 'ConstraintError', |
+ 'incorrect error from put with duplicate primary key'); |
+ break; |
+ case 'continue': |
+ case 'open': |
+ assert_equals( |
+ result[1], `k${primaryKey}`, |
+ `${opcode} key should match the key in the put value`); |
+ assert_equals( |
+ result[2], primaryKey, |
+ `${opcode} primary key should match the put value's primary key`); |
+ assert_equals( |
+ result[3].id, primaryKey, |
+ `${opcode} value should match put value (primary key)`); |
+ assert_equals( |
+ result[3].key, `k${primaryKey}`, |
+ `${opcode} value should match put value (key)`); |
+ assert_equals( |
+ result[3].value.join(','), expectedValue.join(','), |
+ `${opcode} value should match put value (nested value)`); |
+ break; |
+ case 'continue-empty': |
+ case 'open-empty': |
+ assert_equals(result[1], null, `${opcode} result should be null`); |
+ break; |
+ } |
+} |
+ |
+function eventsTest(label, operations) { |
+ promise_test(testCase => { |
+ return createDatabase(testCase, (database, transaction) => { |
+ const store = database.createObjectStore( |
+ 'test-store', { autoIncrement: true, keyPath: 'id' }); |
+ store.createIndex('test-index', 'key', { unique: true }); |
+ populateStore(store); |
+ }).then(database => { |
+ const transaction = database.transaction(['test-store'], 'readwrite'); |
+ const store = transaction.objectStore('test-store'); |
+ const index = store.index('test-index'); |
+ return new Promise((resolve, reject) => { |
+ openCursors(testCase, index, operations, reject, () => { |
+ const results = []; |
+ const promises = []; |
+ for (let i = 0; i < operations.length; ++i) { |
+ const promise = doOperation( |
+ testCase, store, index, operations[i], i, results); |
+ promises.push(promise); |
+ }; |
+ resolve(Promise.all(promises).then(() => results)); |
+ }); |
+ }); |
+ }).then(results => { |
+ assert_equals( |
+ results.length, operations.length, |
+ 'Promise.all should resolve after all sub-promises resolve'); |
+ for (let i = 0; i < operations.length; ++i) |
+ checkOperationResult(operations[i], results[i], i); |
+ }); |
+ }, label); |
+} |
+ |
+eventsTest('small values', [ |
+ ['get', 2], |
+ ['count', 4], |
+ ['continue-empty', null], |
+ ['get-empty', 5], |
+ ['put', 5], |
+ ['open', 2], |
+ ['continue', 2], |
+ ['get', 4], |
+ ['get-empty', 6], |
+ ['count', 5], |
+ ['put', 6], |
+ ['error', 3], |
+ ['continue', 4], |
+ ['count', 6], |
+ ['get-empty', 7], |
+ ['open', 4], |
+ ['open-empty', 7], |
+]); |
+ |
+eventsTest('large values', [ |
+ ['open', 1], |
+ ['get', 1], |
+ ['getall', 4], |
+ ['get', 3], |
+ ['continue', 3], |
+ ['open', 3], |
+]); |
+ |
+eventsTest('large value followed by small values', [ |
+ ['get', 1], |
+ ['getall', 4], |
+ ['open', 2], |
+ ['continue-empty', null], |
+ ['get', 2], |
+ ['get-empty', 5], |
+ ['count', 4], |
+ ['continue-empty', null], |
+ ['open-empty', 5], |
+ ['put', 5], |
+ ['error', 1], |
+ ['continue', 2], |
+ ['get-empty', 6], |
+]); |
+ |
+eventsTest('large values mixed with small values', [ |
+ ['get', 1], |
+ ['get', 2], |
+ ['get-empty', 5], |
+ ['count', 4], |
+ ['continue-empty', null], |
+ ['open', 1], |
+ ['continue', 2], |
+ ['open-empty', 5], |
+ ['getall', 4], |
+ ['open', 2], |
+ ['continue-empty', null], |
+ ['put', 5], |
+ ['get', 3], |
+ ['count', 5], |
+ ['get-empty', 6], |
+ ['getall', 5], |
+ ['continue', 3], |
+ ['open-empty', 6], |
+ ['put', 6], |
+ ['error', 1], |
+ ['continue', 2], |
+ ['open', 4], |
+ ['get-empty', 7], |
+ ['continue', 3], |
+ ['getall', 6], |
+ ['error', 3], |
+ ['count', 6], |
+]); |
+ |
+</script> |