From 43a97878ce14b72f0981164f87f2e35e14151312 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:22:09 +0200 Subject: Adding upstream version 110.0.1. Signed-off-by: Daniel Baumann --- .../tests/IndexedDB/idb-explicit-commit.any.js | 288 +++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 testing/web-platform/tests/IndexedDB/idb-explicit-commit.any.js (limited to 'testing/web-platform/tests/IndexedDB/idb-explicit-commit.any.js') diff --git a/testing/web-platform/tests/IndexedDB/idb-explicit-commit.any.js b/testing/web-platform/tests/IndexedDB/idb-explicit-commit.any.js new file mode 100644 index 0000000000..898d461d7b --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/idb-explicit-commit.any.js @@ -0,0 +1,288 @@ +// META: script=resources/support-promises.js + +promise_test(async testCase => { + const db = await createDatabase(testCase, db => { + createBooksStore(testCase, db); + }); + const txn = db.transaction(['books'], 'readwrite'); + const objectStore = txn.objectStore('books'); + objectStore.put({isbn: 'one', title: 'title1'}); + objectStore.put({isbn: 'two', title: 'title2'}); + objectStore.put({isbn: 'three', title: 'title3'}); + txn.commit(); + await promiseForTransaction(testCase, txn); + + const txn2 = db.transaction(['books'], 'readonly'); + const objectStore2 = txn2.objectStore('books'); + const getRequestitle1 = objectStore2.get('one'); + const getRequestitle2 = objectStore2.get('two'); + const getRequestitle3 = objectStore2.get('three'); + txn2.commit(); + await promiseForTransaction(testCase, txn2); + assert_array_equals( + [getRequestitle1.result.title, + getRequestitle2.result.title, + getRequestitle3.result.title], + ['title1', 'title2', 'title3'], + 'All three retrieved titles should match those that were put.'); + db.close(); +}, 'Explicitly committed data can be read back out.'); + + +promise_test(async testCase => { + let db = await createDatabase(testCase, () => {}); + assert_equals(1, db.version, 'A database should be created as version 1'); + db.close(); + + // Upgrade the versionDB database and explicitly commit its versionchange + // transaction. + db = await migrateDatabase(testCase, 2, (db, txn) => { + txn.commit(); + }); + assert_equals(2, db.version, + 'The database version should have been incremented regardless of ' + + 'whether the versionchange transaction was explicitly or implicitly ' + + 'committed.'); + db.close(); +}, 'commit() on a version change transaction does not cause errors.'); + + +promise_test(async testCase => { + const db = await createDatabase(testCase, db => { + createBooksStore(testCase, db); + }); + const txn = db.transaction(['books'], 'readwrite'); + const objectStore = txn.objectStore('books'); + txn.commit(); + assert_throws_dom('TransactionInactiveError', + () => { objectStore.put({isbn: 'one', title: 'title1'}); }, + 'After commit is called, the transaction should be inactive.'); + db.close(); +}, 'A committed transaction becomes inactive immediately.'); + + +promise_test(async testCase => { + const db = await createDatabase(testCase, db => { + createBooksStore(testCase, db); + }); + const txn = db.transaction(['books'], 'readwrite'); + const objectStore = txn.objectStore('books'); + const putRequest = objectStore.put({isbn: 'one', title: 'title1'}); + putRequest.onsuccess = testCase.step_func(() => { + assert_throws_dom('TransactionInactiveError', + () => { objectStore.put({isbn:'two', title:'title2'}); }, + 'The transaction should not be active in the callback of a request after ' + + 'commit() is called.'); + }); + txn.commit(); + await promiseForTransaction(testCase, txn); + db.close(); +}, 'A committed transaction is inactive in future request callbacks.'); + + +promise_test(async testCase => { + const db = await createDatabase(testCase, db => { + createBooksStore(testCase, db); + }); + const txn = db.transaction(['books'], 'readwrite'); + const objectStore = txn.objectStore('books'); + txn.commit(); + + assert_throws_dom('TransactionInactiveError', + () => { objectStore.put({isbn:'one', title:'title1'}); }, + 'After commit is called, the transaction should be inactive.'); + + const txn2 = db.transaction(['books'], 'readonly'); + const objectStore2 = txn2.objectStore('books'); + const getRequest = objectStore2.get('one'); + await promiseForTransaction(testCase, txn2); + assert_equals(getRequest.result, undefined); + + db.close(); +}, 'Puts issued after commit are not fulfilled.'); + + +promise_test(async testCase => { + const db = await createDatabase(testCase, db => { + createBooksStore(testCase, db); + }); + const txn = db.transaction(['books'], 'readwrite'); + const objectStore = txn.objectStore('books'); + txn.abort(); + assert_throws_dom('InvalidStateError', + () => { txn.commit(); }, + 'The transaction should have been aborted.'); + db.close(); +}, 'Calling commit on an aborted transaction throws.'); + + +promise_test(async testCase => { + const db = await createDatabase(testCase, db => { + createBooksStore(testCase, db); + }); + const txn = db.transaction(['books'], 'readwrite'); + const objectStore = txn.objectStore('books'); + txn.commit(); + assert_throws_dom('InvalidStateError', + () => { txn.commit(); }, + 'The transaction should have already committed.'); + db.close(); +}, 'Calling commit on a committed transaction throws.'); + + +promise_test(async testCase => { + const db = await createDatabase(testCase, db => { + createBooksStore(testCase, db); + }); + const txn = db.transaction(['books'], 'readwrite'); + const objectStore = txn.objectStore('books'); + const putRequest = objectStore.put({isbn:'one', title:'title1'}); + txn.commit(); + assert_throws_dom('InvalidStateError', + () => { txn.abort(); }, + 'The transaction should already have committed.'); + const txn2 = db.transaction(['books'], 'readwrite'); + const objectStore2 = txn2.objectStore('books'); + const getRequest = objectStore2.get('one'); + await promiseForTransaction(testCase, txn2); + assert_equals( + getRequest.result.title, + 'title1', + 'Explicitly committed data should be gettable.'); + db.close(); +}, 'Calling abort on a committed transaction throws and does not prevent ' + + 'persisting the data.'); + + +promise_test(async testCase => { + const db = await createDatabase(testCase, db => { + createBooksStore(testCase, db); + }); + const txn = db.transaction(['books'], 'readwrite'); + const objectStore = txn.objectStore('books'); + const releaseTxnFunction = keepAlive(testCase, txn, 'books'); + + // Break up the scope of execution to force the transaction into an inactive + // state. + await timeoutPromise(0); + + assert_throws_dom('InvalidStateError', + () => { txn.commit(); }, + 'The transaction should be inactive so calling commit should throw.'); + releaseTxnFunction(); + db.close(); +}, 'Calling txn.commit() when txn is inactive should throw.'); + + +promise_test(async testCase => { + const db = await createDatabase(testCase, db => { + createBooksStore(testCase, db); + createNotBooksStore(testCase, db); + }); + // Txn1 should commit before txn2, even though txn2 uses commit(). + const txn1 = db.transaction(['books'], 'readwrite'); + txn1.objectStore('books').put({isbn: 'one', title: 'title1'}); + const releaseTxnFunction = keepAlive(testCase, txn1, 'books'); + + const txn2 = db.transaction(['books'], 'readwrite'); + txn2.objectStore('books').put({isbn:'one', title:'title2'}); + txn2.commit(); + + // Exercise the IndexedDB transaction ordering by executing one with a + // different scope. A readonly transaction is used here because + // implementations are not required to run non-overlapping readwrite + // transactions in parallel, and some implementations (ex: Firefox) + // will not. + const txn3 = db.transaction(['not_books'], 'readonly'); + txn3.objectStore('not_books').getAllKeys(); + txn3.oncomplete = function() { + releaseTxnFunction(); + } + await Promise.all([promiseForTransaction(testCase, txn1), + promiseForTransaction(testCase, txn2)]); + + // Read the data back to verify that txn2 executed last. + const txn4 = db.transaction(['books'], 'readonly'); + const getRequest4 = txn4.objectStore('books').get('one'); + await promiseForTransaction(testCase, txn4); + assert_equals(getRequest4.result.title, 'title2'); + db.close(); +}, 'Transactions with same scope should stay in program order, even if one ' + + 'calls commit.'); + + +promise_test(async testCase => { + const db = await createDatabase(testCase, db => { + createBooksStore(testCase, db); + }); + // Txn1 creates the book 'one' so the 'add()' below fails. + const txn1 = db.transaction(['books'], 'readwrite'); + txn1.objectStore('books').add({isbn:'one', title:'title1'}); + txn1.commit(); + await promiseForTransaction(testCase, txn1); + + // Txn2 should abort, because the 'add' call is invalid, and commit() was + // called. + const txn2 = db.transaction(['books'], 'readwrite'); + const objectStore2 = txn2.objectStore('books'); + objectStore2.put({isbn:'two', title:'title2'}); + const addRequest = objectStore2.add({isbn:'one', title:'title2'}); + txn2.commit(); + txn2.oncomplete = () => { assert_unreached( + 'Transaction with invalid "add" call should not be completed.'); }; + + // Wait for the transaction to complete. We have to explicitly wait for the + // error signal on the transaction because of the nature of the test tooling. + await Promise.all([ + requestWatcher(testCase, addRequest).wait_for('error'), + transactionWatcher(testCase, txn2).wait_for(['error', 'abort']) + ]); + + // Read the data back to verify that txn2 was aborted. + const txn3 = db.transaction(['books'], 'readonly'); + const objectStore3 = txn3.objectStore('books'); + const getRequest1 = objectStore3.get('one'); + const getRequest2 = objectStore3.count('two'); + await promiseForTransaction(testCase, txn3); + assert_equals(getRequest1.result.title, 'title1'); + assert_equals(getRequest2.result, 0); + db.close(); +}, 'Transactions that explicitly commit and have errors should abort.'); + + +promise_test(async testCase => { + const db = await createDatabase(testCase, db => { + createBooksStore(testCase, db); + }); + const txn1 = db.transaction(['books'], 'readwrite'); + txn1.objectStore('books').add({isbn: 'one', title: 'title1'}); + txn1.commit(); + await promiseForTransaction(testCase, txn1); + + // The second add request will throw an error, but the onerror handler will + // appropriately catch the error allowing the valid put request on the + // transaction to commit. + const txn2 = db.transaction(['books'], 'readwrite'); + const objectStore2 = txn2.objectStore('books'); + objectStore2.put({isbn: 'two', title:'title2'}); + const addRequest = objectStore2.add({isbn: 'one', title:'unreached_title'}); + addRequest.onerror = (event) => { + event.preventDefault(); + addRequest.transaction.commit(); + }; + + // Wait for the transaction to complete. We have to explicitly wait for the + // error signal on the transaction because of the nature of the test tooling. + await transactionWatcher(testCase,txn2).wait_for(['error', 'complete']) + + // Read the data back to verify that txn2 was committed. + const txn3 = db.transaction(['books'], 'readonly'); + const objectStore3 = txn3.objectStore('books'); + const getRequest1 = objectStore3.get('one'); + const getRequest2 = objectStore3.get('two'); + await promiseForTransaction(testCase, txn3); + assert_equals(getRequest1.result.title, 'title1'); + assert_equals(getRequest2.result.title, 'title2'); + db.close(); +}, 'Transactions that handle all errors properly should behave as ' + + 'expected when an explicit commit is called in an onerror handler.'); -- cgit v1.2.3