diff --git a/testing/web-platform/tests/IndexedDB/support.js b/testing/web-platform/tests/IndexedDB/support.js index c6f626e54e25..791fc2a51c0f 100644 --- a/testing/web-platform/tests/IndexedDB/support.js +++ b/testing/web-platform/tests/IndexedDB/support.js @@ -101,6 +101,16 @@ function assert_key_equals(actual, expected, description) { assert_equals(indexedDB.cmp(actual, expected), 0, description); } + + + + + + + + + + function indexeddb_test(upgrade_func, open_func, description, options) { async_test(function(t) { options = Object.assign({upgrade_will_abort: false}, options); @@ -189,3 +199,13 @@ function keep_alive(tx, store_name) { keepSpinning = false; }; } + + + +function barrier_func(count, func) { + let n = 0; + return () => { + if (++n === count) + func(); + }; +} diff --git a/testing/web-platform/tests/IndexedDB/transaction-scheduling-across-connections.any.js b/testing/web-platform/tests/IndexedDB/transaction-scheduling-across-connections.any.js new file mode 100644 index 000000000000..b31aadf2c7be --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/transaction-scheduling-across-connections.any.js @@ -0,0 +1,72 @@ + + +indexeddb_test( + (t, db) => { + const store = db.createObjectStore('store'); + }, + + (t, db1) => { + + const open_request = indexedDB.open(db1.name); + open_request.onerror = t.unreached_func('open() should succeed'); + open_request.onupgradeneeded = + t.unreached_func('second connection should not upgrade'); + open_request.onsuccess = t.step_func(() => { + const db2 = open_request.result; + t.add_cleanup(() => { db2.close(); }); + + const transaction1 = db1.transaction('store', 'readwrite'); + transaction1.onabort = t.unreached_func('transaction1 should complete'); + + const transaction2 = db2.transaction('store', 'readwrite'); + transaction2.onabort = t.unreached_func('transaction2 should complete'); + + let transaction1PutSuccess = false; + let transaction1Complete = false; + let transaction2PutSuccess = false; + + + + + let count = 0; + (function doTransaction1Put() { + const request = transaction1.objectStore('store').put(1, count++); + request.onerror = t.unreached_func('request should succeed'); + request.onsuccess = t.step_func(evt => { + transaction1PutSuccess = true; + if (count < 5) { + doTransaction1Put(); + } + }); + }()); + + transaction1.oncomplete = t.step_func(evt => { + transaction1Complete = true; + assert_false( + transaction2PutSuccess, + 'transaction1 should complete before transaction2 put succeeds'); + }); + + const request = transaction2.objectStore('store').put(2, 0); + request.onerror = t.unreached_func('request should succeed'); + request.onsuccess = t.step_func(evt => { + transaction2PutSuccess = true; + assert_true( + transaction1Complete, + 'transaction2 put should not succeed before transaction1 completes'); + }); + + transaction2.oncomplete = t.step_func_done(evt => { + assert_true( + transaction1PutSuccess, + 'transaction1 put should succeed before transaction2 runs'); + assert_true( + transaction1Complete, + 'transaction1 should complete before transaction2 runs'); + assert_true( + transaction2PutSuccess, + 'transaction2 put should succeed before transaction2 completes'); + }); + }); + }, + "Check that readwrite transactions with overlapping scopes do not run in parallel."); diff --git a/testing/web-platform/tests/IndexedDB/transaction-scheduling-across-databases.any.js b/testing/web-platform/tests/IndexedDB/transaction-scheduling-across-databases.any.js new file mode 100644 index 000000000000..7e003e142fc5 --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/transaction-scheduling-across-databases.any.js @@ -0,0 +1,72 @@ + + +indexeddb_test( + (t, db) => { + const store = db.createObjectStore('store'); + }, + + (t, db1) => { + + const db2name = db1.name + '-2'; + const delete_request = indexedDB.deleteDatabase(db2name); + delete_request.onerror = t.unreached_func('deleteDatabase() should succeed'); + const open_request = indexedDB.open(db2name, 1); + open_request.onerror = t.unreached_func('open() should succeed'); + open_request.onupgradeneeded = t.step_func(() => { + const db2 = open_request.result; + const store = db2.createObjectStore('store'); + }); + open_request.onsuccess = t.step_func(() => { + const db2 = open_request.result; + t.add_cleanup(() => { + db2.close(); + indexedDB.deleteDatabase(db2.name); + }); + + let transaction1PutSuccess = false; + let transaction2PutSuccess = false; + + const onTransactionComplete = barrier_func(2, t.step_func_done(() => { + assert_true(transaction1PutSuccess, + 'transaction1 should have executed at least one request'); + assert_true(transaction2PutSuccess, + 'transaction1 should have executed at least one request'); + })); + + + const transaction1 = db1.transaction('store', 'readwrite'); + transaction1.onabort = t.unreached_func('transaction1 should complete'); + transaction1.oncomplete = t.step_func(onTransactionComplete); + + const transaction2 = db2.transaction('store', 'readwrite'); + transaction2.onabort = t.unreached_func('transaction2 should complete'); + transaction2.oncomplete = t.step_func(onTransactionComplete); + + + + + function doTransaction1Put() { + const request = transaction1.objectStore('store').put(0, 0); + request.onerror = t.unreached_func('put request should succeed'); + request.onsuccess = t.step_func(() => { + transaction1PutSuccess = true; + if (!transaction2PutSuccess) + doTransaction1Put(); + }); + } + + function doTransaction2Put() { + const request = transaction2.objectStore('store').put(0, 0); + request.onerror = t.unreached_func('put request should succeed'); + request.onsuccess = t.step_func(() => { + transaction2PutSuccess = true; + if (!transaction1PutSuccess) + doTransaction2Put(); + }); + } + + doTransaction1Put(); + doTransaction2Put(); + }); + }, + "Check that transactions in different databases can run in parallel."); diff --git a/testing/web-platform/tests/IndexedDB/transaction-scheduling-mixed-scopes.any.js b/testing/web-platform/tests/IndexedDB/transaction-scheduling-mixed-scopes.any.js new file mode 100644 index 000000000000..774b04ab56ca --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/transaction-scheduling-mixed-scopes.any.js @@ -0,0 +1,63 @@ + + +indexeddb_test( + (t, db) => { + const store = db.createObjectStore('store'); + db.createObjectStore('a'); + db.createObjectStore('b'); + db.createObjectStore('c'); + }, + + (t, db) => { + let transaction1Started = false; + let transaction1Complete = false; + let transaction2Started = false; + let transaction2Complete = false; + let transaction3Started = false; + let transaction3Complete = false; + + const transaction1 = db.transaction(['a'], 'readonly'); + let request = transaction1.objectStore('a').get(0); + request.onerror = t.unreached_func('request should succeed'); + request.onsuccess = t.step_func(() => { + transaction1Started = true; + }); + transaction1.onabort = t.unreached_func('transaction1 should complete'); + transaction1.oncomplete = t.step_func(() => { + transaction1Complete = true; + assert_false(transaction2Started); + assert_false(transaction3Started); + }); + + + + + const transaction2 = db.transaction(['a', 'b'], 'readwrite'); + request = transaction2.objectStore('a').get(0); + request.onerror = t.unreached_func('request should succeed'); + request.onsuccess = t.step_func(() => { + assert_true(transaction1Complete); + transaction2Started = true; + }); + transaction2.onabort = t.unreached_func('transaction2 should complete'); + transaction2.oncomplete = t.step_func(() => { + transaction2Complete = true; + assert_false(transaction3Started); + }); + + + + const transaction3 = db.transaction(['b', 'c'], 'readonly'); + request = transaction3.objectStore('b').get(0); + request.onerror = t.unreached_func('request should succeed'); + request.onsuccess = t.step_func(() => { + assert_true(transaction1Complete); + assert_true(transaction2Complete); + transaction3Started = true; + }); + transaction3.onabort = t.unreached_func('transaction3 should complete'); + transaction3.oncomplete = t.step_func_done(() => { + transaction3Complete = true; + }); + }, + "Check that scope restrictions on mixed transactions are enforced."); diff --git a/testing/web-platform/tests/IndexedDB/transaction-scheduling-ordering.any.js b/testing/web-platform/tests/IndexedDB/transaction-scheduling-ordering.any.js new file mode 100644 index 000000000000..15e0e0d05a31 --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/transaction-scheduling-ordering.any.js @@ -0,0 +1,40 @@ + + +indexeddb_test( + (t, db) => { + const store = db.createObjectStore('store'); + }, + + (t, db) => { + + const tx1 = db.transaction('store', 'readwrite'); + const tx2 = db.transaction('store', 'readwrite'); + + + tx2.objectStore('store').get(0); + tx1.objectStore('store').get(0); + + const order = []; + const done = barrier_func(2, t.step_func_done(() => { + + + + + + + + + assert_array_equals(order, [1, 2]); + })); + + tx1.oncomplete = t.step_func(e => { + order.push(1); + done(); + }); + + tx2.oncomplete = t.step_func(e => { + order.push(2); + done(); + }); + }, + "Verify Indexed DB transactions are ordered per spec"); diff --git a/testing/web-platform/tests/IndexedDB/transaction-scheduling-ro-waits-for-rw.any.js b/testing/web-platform/tests/IndexedDB/transaction-scheduling-ro-waits-for-rw.any.js new file mode 100644 index 000000000000..106d97794cce --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/transaction-scheduling-ro-waits-for-rw.any.js @@ -0,0 +1,26 @@ + + +indexeddb_test( + (t, db) => { + const store = db.createObjectStore('store'); + store.put('value', 'key'); + }, + + (t, db) => { + const transaction1 = db.transaction('store', 'readwrite'); + transaction1.onabort = t.unreached_func('transaction1 should not abort'); + + const transaction2 = db.transaction('store', 'readonly'); + transaction2.onabort = t.unreached_func('transaction2 should not abort'); + + const request = transaction1.objectStore('store').put('new value', 'key'); + request.onerror = t.unreached_func('request should not fail'); + + const request2 = transaction2.objectStore('store').get('key'); + request2.onerror = t.unreached_func('request2 should not fail'); + request2.onsuccess = t.step_func_done(evt => { + assert_equals(request2.result, 'new value', + 'Request should see new value.'); + }); + }, + "readonly transaction should see the result of a previous readwrite transaction"); diff --git a/testing/web-platform/tests/IndexedDB/transaction-scheduling-rw-scopes.any.js b/testing/web-platform/tests/IndexedDB/transaction-scheduling-rw-scopes.any.js new file mode 100644 index 000000000000..a21cf3d33035 --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/transaction-scheduling-rw-scopes.any.js @@ -0,0 +1,63 @@ + + +indexeddb_test( + (t, db) => { + const store = db.createObjectStore('store'); + db.createObjectStore('a'); + db.createObjectStore('b'); + db.createObjectStore('c'); + }, + + (t, db) => { + let transaction1Started = false; + let transaction1Complete = false; + let transaction2Started = false; + let transaction2Complete = false; + let transaction3Started = false; + let transaction3Complete = false; + + const transaction1 = db.transaction(['a'], 'readwrite'); + let request = transaction1.objectStore('a').get(0); + request.onerror = t.unreached_func('request should succeed'); + request.onsuccess = t.step_func(() => { + transaction1Started = true; + }); + transaction1.onabort = t.unreached_func('transaction1 should complete'); + transaction1.oncomplete = t.step_func(() => { + transaction1Complete = true; + assert_false(transaction2Started); + assert_false(transaction3Started); + }); + + + + + const transaction2 = db.transaction(['a', 'b'], 'readwrite'); + request = transaction2.objectStore('a').get(0); + request.onerror = t.unreached_func('request should succeed'); + request.onsuccess = t.step_func(() => { + assert_true(transaction1Complete); + transaction2Started = true; + }); + transaction2.onabort = t.unreached_func('transaction2 should complete'); + transaction2.oncomplete = t.step_func(() => { + transaction2Complete = true; + assert_false(transaction3Started); + }); + + + + const transaction3 = db.transaction(['b', 'c'], 'readwrite'); + request = transaction3.objectStore('b').get(0); + request.onerror = t.unreached_func('request should succeed'); + request.onsuccess = t.step_func(() => { + assert_true(transaction1Complete); + assert_true(transaction2Complete); + transaction3Started = true; + }); + transaction3.onabort = t.unreached_func('transaction3 should complete'); + transaction3.oncomplete = t.step_func_done(() => { + transaction3Complete = true; + }); + }, + "Check that scope restrictions on read-write transactions are enforced."); diff --git a/testing/web-platform/tests/IndexedDB/transaction-scheduling-within-database.any.js b/testing/web-platform/tests/IndexedDB/transaction-scheduling-within-database.any.js new file mode 100644 index 000000000000..1c0cb99c9ee9 --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/transaction-scheduling-within-database.any.js @@ -0,0 +1,55 @@ + + +indexeddb_test( + (t, db) => { + const store = db.createObjectStore('store'); + store.put('value', 'key'); + }, + + (t, db) => { + let transaction1GetSuccess = false; + let transaction2GetSuccess = false; + + const onTransactionComplete = barrier_func(2, t.step_func_done(() => { + assert_true(transaction1GetSuccess, + 'transaction1 should have executed at least one request'); + assert_true(transaction2GetSuccess, + 'transaction1 should have executed at least one request'); + })); + + const transaction1 = db.transaction('store', 'readonly'); + transaction1.onabort = t.unreached_func('transaction1 should not abort'); + transaction1.oncomplete = t.step_func(onTransactionComplete); + + const transaction2 = db.transaction('store', 'readonly'); + transaction2.onabort = t.unreached_func('transaction2 should not abort'); + transaction2.oncomplete = t.step_func(onTransactionComplete); + + + + + function doTransaction1Get() { + const request = transaction1.objectStore('store').get('key'); + request.onerror = t.unreached_func('request should not fail'); + request.onsuccess = t.step_func(() => { + transaction1GetSuccess = true; + if (!transaction2GetSuccess) + doTransaction1Get(); + }); + } + + function doTransaction2Get() { + + const request = transaction2.objectStore('store').get('key'); + request.onerror = t.unreached_func('request should not fail'); + request.onsuccess = t.step_func(() => { + transaction2GetSuccess = true; + if (!transaction1GetSuccess) + doTransaction2Get(); + }); + } + + doTransaction1Get(); + doTransaction2Get(); + }, + 'Check that read-only transactions within a database can run in parallel.');