Chromium Code Reviews| Index: third_party/WebKit/LayoutTests/storage/indexeddb/rename-index.html |
| diff --git a/third_party/WebKit/LayoutTests/storage/indexeddb/rename-index.html b/third_party/WebKit/LayoutTests/storage/indexeddb/rename-index.html |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d8ce8da47b9faf7a1007572fc3de86b7bfcd85f2 |
| --- /dev/null |
| +++ b/third_party/WebKit/LayoutTests/storage/indexeddb/rename-index.html |
| @@ -0,0 +1,537 @@ |
| +<!DOCTYPE html> |
| +<title>IndexedDB: index renaming support</title> |
| +<script src='../../resources/testharness.js'></script> |
| +<link rel="help" |
| + href="https://w3c.github.io/IndexedDB/#dom-idbindex-name"> |
| +<link rel="author" href="pwnall@chromium.org" title="Victor Costan"> |
| +<script src='../../resources/testharnessreport.js'></script> |
| +<script src='resources/rename-common.js'></script> |
| +<script> |
| + |
| +promise_test(testCase => { |
| + let authorIndex = null, authorIndex2 = null; |
| + let renamedAuthorIndex = null, renamedAuthorIndex2 = null; |
| + return createDatabase(testCase, (database, transaction) => { |
| + const store = createBooksStore(testCase, database); |
| + authorIndex = store.index('by_author'); |
| + }).then(database => { |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + assert_array_equals( |
| + store.indexNames, ['by_author', 'by_title'], |
| + 'Test setup should have created two indexes'); |
| + authorIndex2 = store.index('by_author'); |
| + return checkAuthorIndexContents( |
| + testCase, authorIndex2, |
| + 'The index should have the expected contents before any renaming'). |
| + then(() => database.close()); |
| + }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + renamedAuthorIndex = store.index('by_author'); |
| + renamedAuthorIndex.name = 'renamed_by_author'; |
| + |
| + assert_equals( |
| + renamedAuthorIndex.name, 'renamed_by_author', |
| + 'IDBIndex name should change immediately after a rename'); |
| + assert_array_equals( |
| + store.indexNames, ['by_title', 'renamed_by_author'], |
| + 'IDBObjectStore.indexNames should immediately reflect the rename'); |
| + assert_equals( |
| + store.index('renamed_by_author'), renamedAuthorIndex, |
| + 'IDBObjectStore.index should return the renamed index store when ' + |
| + 'queried using the new name immediately after the rename'); |
| + assert_throws( |
| + 'NotFoundError', () => store.index('by_author'), |
| + 'IDBObjectStore.index should throw when queried using the renamed' + |
|
jsbell
2016/09/07 17:16:14
need a space before the closing '
pwnall
2016/09/07 22:43:52
Done.
|
| + "index's old name immediately after the rename"); |
| + })).then(database => { |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + assert_array_equals( |
| + store.indexNames, ['by_title', 'renamed_by_author'], |
| + 'IDBObjectStore.indexNames should still reflect the rename after ' + |
| + 'the versionchange transaction commits'); |
| + renamedAuthorIndex2 = store.index('renamed_by_author'); |
| + return checkAuthorIndexContents( |
| + testCase, renamedAuthorIndex2, |
| + 'Renaming an index should not change its contents').then( |
| + () => database.close()); |
| + }).then(() => { |
| + assert_equals( |
| + authorIndex.name, 'by_author', |
| + 'IDBIndex obtained before the rename transaction should not ' + |
| + 'reflect the rename'); |
| + assert_equals( |
| + authorIndex2.name, 'by_author', |
| + 'IDBIndex obtained before the rename transaction should not ' + |
| + 'reflect the rename'); |
| + assert_equals( |
| + renamedAuthorIndex.name, 'renamed_by_author', |
| + 'IDBIndex used in the rename transaction should keep reflecting ' + |
| + 'the new name after the transaction is committed'); |
| + assert_equals( |
| + renamedAuthorIndex2.name, 'renamed_by_author', |
| + 'IDBIndex obtained after the rename transaction should reflect ' + |
| + 'the new name'); |
| + }); |
| +}, 'IndexedDB index rename in new transaction'); |
| + |
| +promise_test(testCase => { |
| + let renamedAuthorIndex = null, renamedAuthorIndex2 = null; |
| + return createDatabase(testCase, (database, transaction) => { |
| + const store = createBooksStore(testCase, database); |
| + renamedAuthorIndex = store.index('by_author'); |
| + renamedAuthorIndex.name = 'renamed_by_author'; |
| + |
| + assert_equals( |
| + renamedAuthorIndex.name, 'renamed_by_author', |
| + 'IDBIndex name should change immediately after a rename'); |
| + assert_array_equals( |
| + store.indexNames, ['by_title', 'renamed_by_author'], |
| + 'IDBObjectStore.indexNames should immediately reflect the rename'); |
| + assert_equals( |
| + store.index('renamed_by_author'), renamedAuthorIndex, |
| + 'IDBObjectStore.index should return the renamed index store when ' + |
| + 'queried using the new name immediately after the rename'); |
| + assert_throws( |
| + 'NotFoundError', () => store.index('by_author'), |
| + 'IDBObjectStore.index should throw when queried using the renamed' + |
|
jsbell
2016/09/07 17:16:14
need a space before the closing '
pwnall
2016/09/07 22:43:52
Done.
|
| + "index's old name immediately after the rename"); |
| + }).then(database => { |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + assert_array_equals( |
| + store.indexNames, ['by_title', 'renamed_by_author'], |
| + 'IDBObjectStore.indexNames should still reflect the rename after ' + |
| + 'the versionchange transaction commits'); |
| + renamedAuthorIndex2 = store.index('renamed_by_author'); |
| + return checkAuthorIndexContents( |
| + testCase, renamedAuthorIndex2, |
| + 'Renaming an index should not change its contents').then( |
| + () => database.close()); |
| + }).then(() => { |
| + assert_equals( |
| + renamedAuthorIndex.name, 'renamed_by_author', |
| + 'IDBIndex used in the rename transaction should keep reflecting ' + |
| + 'the new name after the transaction is committed'); |
| + assert_equals( |
| + renamedAuthorIndex2.name, 'renamed_by_author', |
| + 'IDBIndex obtained after the rename transaction should reflect ' + |
| + 'the new name'); |
| + }); |
| +}, 'IndexedDB index rename in the transaction where it is created'); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then(database => { |
| + database.close(); |
| + }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + const index = store.index('by_author'); |
| + store.deleteIndex('by_author'); |
| + assert_throws( |
| + 'InvalidStateError', () => index.name = 'renamed_by_author'); |
| + })).then(database => database.close()); |
| +}, 'IndexedDB deleted index rename throws'); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then(database => { |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + const index = store.index('by_author'); |
| + |
| + assert_throws( |
| + 'InvalidStateError', () => index.name = 'renamed_by_author'); |
| + database.close(); |
| + }); |
| +}, 'IndexedDB index rename throws in a readonly transaction'); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then(database => { |
| + const transaction = database.transaction('books', 'readwrite'); |
| + const store = transaction.objectStore('books'); |
| + const index = store.index('by_author'); |
| + |
| + assert_throws( |
| + 'InvalidStateError', () => index.name = 'renamed_by_author'); |
| + database.close(); |
| + }); |
| +}, 'IndexedDB index rename throws in a readwrite transaction'); |
| + |
| +promise_test(testCase => { |
| + let authorIndex = null; |
| + return createDatabase(testCase, (database, transaction) => { |
| + const store = createBooksStore(testCase, database); |
| + authorIndex = store.index('by_author'); |
| + }).then(database => { |
| + assert_throws( |
| + 'TransactionInactiveError', |
| + () => authorIndex.name = 'renamed_by_author'); |
| + database.close(); |
| + }); |
| +}, 'IndexedDB index rename throws in an inactive transaction'); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then(database => { |
| + database.close(); |
| + }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + const index = store.index('by_author'); |
| + index.name = 'by_author'; |
| + assert_array_equals( |
| + store.indexNames, ['by_author', 'by_title'], |
| + 'Renaming an index to the same name should not change the ' + |
| + "index's IDBObjectStore.indexNames"); |
| + })).then(database => { |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + assert_array_equals( |
| + store.indexNames, ['by_author', 'by_title'], |
| + 'Committing a transaction that renames a store to the same name ' + |
| + "should not change the index's IDBObjectStore.indexNames"); |
| + const index = store.index('by_author'); |
| + return checkAuthorIndexContents( |
| + testCase, index, |
| + 'Committing a transaction that renames an index to the same name ' + |
| + "should not change the index's contents").then( |
| + () => database.close()); |
| + }); |
| +}, 'IndexedDB index rename to the same name succeeds'); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then(database => { |
| + database.close(); |
| + }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + const index = store.index('by_author'); |
| + |
| + assert_throws('ConstraintError', () => index.name = 'by_title'); |
| + assert_array_equals( |
| + store.indexNames, ['by_author', 'by_title'], |
| + 'An index rename that throws an exception should not change the ' + |
| + "index's IDBObjectStore.indexNames"); |
| + })).then(database => { |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + assert_array_equals( |
| + store.indexNames, ['by_author', 'by_title'], |
| + 'Committing a transaction with a failed store rename attempt ' + |
| + "should not change the index's IDBObjectStore.indexNames"); |
| + const index = store.index('by_author'); |
| + return checkAuthorIndexContents( |
| + testCase, index, |
| + 'Committing a transaction with a failed rename attempt should not' + |
|
jsbell
2016/09/07 17:16:14
need a space before the closing '
pwnall
2016/09/07 22:43:52
Done.
|
| + "change the index's contents").then(() => database.close()); |
| + }); |
| +}, 'IndexedDB index rename to the name of another index throws'); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then(database => { |
| + database.close(); |
| + }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + const index = store.index('by_author'); |
| + store.deleteIndex('by_title'); |
| + index.name = 'by_title'; |
| + assert_array_equals( |
| + store.indexNames, ['by_title'], |
| + 'IDBObjectStore.indexNames should immediately reflect the rename'); |
| + })).then(database => { |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + assert_array_equals( |
| + store.indexNames, ['by_title'], |
| + 'IDBObjectStore.indexNames should still reflect the rename after ' + |
| + 'the versionchange transaction commits'); |
| + const index = store.index('by_title'); |
| + return checkAuthorIndexContents( |
| + testCase, index, |
| + 'Renaming an index should not change its contents').then( |
| + () => database.close()); |
| + }); |
| +}, 'IndexedDB index rename to the name of a deleted index succeeds'); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then(database => { |
| + database.close(); |
| + }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + store.index('by_author').name = 'tmp'; |
| + store.index('by_title').name = 'by_author'; |
| + store.index('tmp').name = 'by_title'; |
| + assert_array_equals( |
| + store.indexNames, ['by_author', 'by_title'], |
| + 'IDBObjectStore.indexNames should reflect the swap immediately ' + |
| + 'after the renames'); |
| + return checkTitleIndexContents( |
| + testCase, store.index('by_author'), |
| + 'Renaming an index should not change its contents'); |
| + })).then(database => { |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + assert_array_equals( |
| + store.indexNames, ['by_author', 'by_title'], |
| + 'IDBObjectStore.indexNames should still reflect the swap after ' + |
| + 'the versionchange transaction commits'); |
| + const index = store.index('by_title'); |
| + return checkAuthorIndexContents( |
| + testCase, index, |
| + 'Renaming an index should not change its contents').then( |
| + () => database.close()); |
| + }); |
| +}, 'IndexedDB index swapping via renames succeeds'); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then(database => { |
| + database.close(); |
| + }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + const index = store.index('by_author'); |
| + |
| + index.name = 42; |
| + assert_equals(index.name, '42', |
| + 'IDBIndex name should change immediately after a rename to a ' + |
| + 'number'); |
| + assert_array_equals( |
| + store.indexNames, ['42', 'by_title'], |
| + 'IDBObjectStore.indexNames should immediately reflect the ' + |
| + 'stringifying rename'); |
| + |
| + index.name = true; |
| + assert_equals(index.name, 'true', |
| + 'IDBIndex name should change immediately after a rename to a ' + |
| + 'boolean'); |
| + |
| + index.name = {}; |
| + assert_equals(index.name, '[object Object]', |
| + 'IDBIndex name should change immediately after a rename to an ' + |
| + 'object'); |
| + |
| + index.name = () => null; |
| + assert_equals(index.name, '() => null', |
| + 'IDBIndex name should change immediately after a rename to a ' + |
| + 'function'); |
| + |
| + index.name = undefined; |
| + assert_equals(index.name, 'undefined', |
| + 'IDBIndex name should change immediately after a rename to ' + |
| + 'undefined'); |
| + })).then(database => { |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + assert_array_equals( |
| + store.indexNames, ['by_title', 'undefined'], |
| + 'IDBObjectStore.indexNames should reflect the last rename ' + |
| + 'after the versionchange transaction commits'); |
| + const index = store.index('undefined'); |
| + return checkAuthorIndexContents( |
| + testCase, index, |
| + 'Renaming an index should not change its contents').then( |
| + () => database.close()); |
| + }); |
| +}, 'IndexedDB index rename stringifies non-string names'); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then(database => { |
| + database.close(); |
| + }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + const index = store.index('by_author'); |
| + |
| + assert_throws( |
| + { name: 'Custom stringifying error'}, |
| + () => { |
| + index.name = { |
| + toString: () => { throw { name: 'Custom stringifying error'}; } |
| + }; |
| + }, 'IDBObjectStore rename should re-raise toString() exception'); |
| + assert_array_equals( |
| + store.indexNames, ['by_author', 'by_title'], |
| + 'An index rename that throws an exception should not change the ' + |
| + "index's IDBObjectStore.indexNames"); |
| + })).then(database => { |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + assert_array_equals( |
| + store.indexNames, ['by_author', 'by_title'], |
| + 'Committing a transaction with a failed store rename attempt ' + |
| + "should not change the index's IDBObjectStore.indexNames"); |
| + const index = store.index('by_author'); |
| + return checkAuthorIndexContents( |
| + testCase, index, |
| + 'Committing a transaction with a failed rename attempt should not' + |
|
jsbell
2016/09/07 17:16:14
need a space before the closing '
pwnall
2016/09/07 22:43:52
Done.
I also used a regexp to find any other inst
|
| + "change the index's contents").then(() => database.close()); |
| + }); |
| +}, 'IndexedDB index rename handles exceptions when stringifying names'); |
| + |
| +for (let escapedName of ['', '\\u0000', '\\uDC00\\uD800']) ((escapedName) => { |
| + const name = JSON.parse('"' + escapedName + '"'); |
| + promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then(database => { |
| + database.close(); |
| + }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + const index = store.index('by_author'); |
| + |
| + index.name = name; |
| + assert_equals(index.name, name, |
| + 'IDBIndex name should change immediately after the rename'); |
| + assert_array_equals( |
| + store.indexNames, [name, 'by_title'].sort(), |
| + 'IDBObjectStore.indexNames should immediately reflect the rename'); |
| + })).then(database => { |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + assert_array_equals( |
| + store.indexNames, [name, 'by_title'].sort(), |
| + 'IDBObjectStore.indexNames should reflect the rename ' + |
| + 'after the versionchange transaction commits'); |
| + const index = store.index(name); |
| + return checkAuthorIndexContents( |
| + testCase, index, |
| + 'Renaming an index should not change its contents').then( |
| + () => database.close()); |
| + }); |
| + }, 'IndexedDB index can be renamed to "' + escapedName + '"'); |
| +})(escapedName); |
| + |
| +promise_test(testCase => { |
| + const dbName = databaseName(testCase); |
| + let authorIndex = null, authorIndex2 = null; |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then(database => { |
| + database.close(); |
| + }).then(() => new Promise((resolve, reject) => { |
| + const request = indexedDB.open(dbName, 2); |
| + request.onupgradeneeded = event => { |
| + const database = event.target.result; |
| + const transaction = event.target.transaction; |
| + const store = transaction.objectStore('books'); |
| + authorIndex = store.index('by_author'); |
| + authorIndex.name = 'renamed_by_author'; |
| + request.onerror = event => { |
| + event.preventDefault(); |
| + resolve(event); |
| + } |
| + transaction.abort(); |
| + |
| + assert_equals( |
| + authorIndex.name, 'by_author', |
| + 'IDBIndex.name should not reflect the rename anymore ' + |
|
jsbell
2016/09/07 17:16:14
spelling: "any more" (here and below)
pwnall
2016/09/07 22:43:52
Done.
|
| + 'immediately after transaction.abort() returns'); |
| + assert_array_equals( |
| + store.indexNames, ['by_author', 'by_title'], |
| + 'IDBObjectStore.indexNames should not reflect the rename ' + |
| + 'anymore immediately after transaction.abort() returns'); |
| + }; |
| + request.onerror = event => reject(event.target.error); |
| + request.onsuccess = () => reject(new Error( |
| + 'indexedDB.open was not supposed to succeed')); |
| + })).then(event => { |
| + assert_equals(authorIndex.name, 'by_author', |
| + 'IDBIndex.name should not reflect the rename anymore ' + |
| + 'after the versionchange transaction is aborted'); |
| + |
| + const request = indexedDB.open(dbName, 1); |
| + return requestWatcher(testCase, request).wait_for('success'); |
| + }).then(event => { |
| + const database = event.target.result; |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + assert_array_equals( |
| + store.indexNames, ['by_author', 'by_title'], |
| + 'IDBDatabase.objectStoreNames should not reflect the rename ' + |
| + 'after the versionchange transaction is aborted'); |
| + |
| + authorIndex2 = store.index('by_author'); |
| + return checkAuthorIndexContents( |
| + testCase, authorIndex2, |
| + 'Aborting an index rename transaction should not change the ' + |
| + "index's records").then(() => database.close()); |
| + }).then(() => { |
| + assert_equals( |
| + authorIndex.name, 'by_author', |
| + 'IDBIndex used in aborted rename transaction should not reflect ' + |
| + 'the rename after the transaction is aborted'); |
| + assert_equals(authorIndex2.name, 'by_author', |
| + 'IDBIndex obtained after an aborted rename transaction should ' + |
| + 'not reflect the rename'); |
| + }); |
| +}, 'IndexedDB index rename in aborted transaction'); |
| + |
| +promise_test(testCase => { |
| + const dbName = databaseName(testCase); |
| + let authorIndex = null; |
| + return createDatabase(testCase, (database, transaction) => { |
| + createNotBooksStore(testCase, database); |
| + }).then(database => { |
| + database.close(); |
| + }).then(() => new Promise((resolve, reject) => { |
| + const request = indexedDB.open(dbName, 2); |
| + request.onupgradeneeded = event => { |
| + const database = event.target.result; |
| + const transaction = event.target.transaction; |
| + const store = transaction.objectStore('not_books'); |
| + authorIndex = store.createIndex('by_author', 'author'); |
| + authorIndex.name = 'by_author_renamed'; |
| + authorIndex.name = 'by_author_renamed_again'; |
| + |
| + request.onerror = event => { |
| + event.preventDefault(); |
| + resolve(event); |
| + } |
| + transaction.abort(); |
| + |
| + assert_equals( |
| + authorIndex.name, 'by_author_renamed_again', |
| + 'IDBIndex.name should reflect the last rename immediately ' + |
| + 'after transaction.abort() returns'); |
| + assert_array_equals( |
| + store.indexNames, [], |
| + 'IDBObjectStore.indexNames should not reflect the creation ' + |
| + 'or the rename immediately after transaction.abort() returns'); |
| + }; |
| + request.onerror = event => reject(event.target.error); |
| + request.onsuccess = () => reject(new Error( |
| + 'indexedDB.open was not supposed to succeed')); |
| + })).then(event => { |
| + assert_equals(authorIndex.name, 'by_author_renamed_again', |
| + 'IDBIndex.name should reflect the last rename after the ' + |
| + 'versionchange transaction is aborted'); |
| + |
| + const request = indexedDB.open(dbName, 1); |
| + return requestWatcher(testCase, request).wait_for('success'); |
| + }).then(event => { |
| + const database = event.target.result; |
| + const transaction = database.transaction('not_books', 'readonly'); |
| + const store = transaction.objectStore('not_books'); |
| + assert_array_equals( |
| + store.indexNames, [], |
| + 'IDBDatabase.objectStoreNames should not reflect the creation or ' + |
| + 'the rename after the versionchange transaction is aborted'); |
| + |
| + database.close(); |
| + }); |
| +}, 'IndexedDB index creation and rename in an aborted transaction'); |
| + |
| +</script> |