Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 <!DOCTYPE html> | |
| 2 <title>IndexedDB v2: object store renaming support</title> | |
| 3 <script src='../../resources/testharness.js'></script> | |
| 4 <link rel="help" | |
| 5 href="https://w3c.github.io/IndexedDB/#dom-idbobjectstore-name"> | |
| 6 <link rel="author" href="pwnall@chromium.org" title="Victor Costan"> | |
| 7 <script src='../../resources/testharnessreport.js'></script> | |
| 8 <script> | |
| 9 | |
| 10 // Returns an IndexedDB database name likely to be unique to the test case. | |
| 11 const databaseName = function(testCase) { | |
| 12 return 'db' + self.location.pathname + '-' + testCase.name; | |
| 13 }; | |
| 14 | |
| 15 // Creates an EventWatcher covering all the events that can be issued by | |
| 16 // IndexedDB requests and transactions. | |
| 17 const requestWatcher = function(testCase, request) { | |
| 18 return new EventWatcher(testCase, request, | |
| 19 ['abort', 'complete', 'error', 'success', 'upgradeneeded']); | |
| 20 }; | |
| 21 | |
| 22 // Creates an IndexedDB database whose name is unique for the test case. | |
| 23 // | |
| 24 // setupCallback will be called during a versionchange transaction, and will be | |
| 25 // given the created database and the versionchange transaction. | |
| 26 // | |
| 27 // Returns a promise that yields an IndexedDB open success event. | |
| 28 const createDatabase = function(testCase, setupCallback) { | |
| 29 const dbName = databaseName(testCase); | |
| 30 const request = indexedDB.deleteDatabase(dbName); | |
| 31 const eventWatcher = requestWatcher(testCase, request); | |
| 32 | |
| 33 return eventWatcher.wait_for('success').then((event) => { | |
| 34 // We cannot use eventWatcher.wait_for('upgradeneeded') here, because | |
| 35 // the versionchange transaction auto-commits before the Promise's then | |
| 36 // callback gets called. | |
| 37 return new Promise((resolve, reject) => { | |
| 38 const request = indexedDB.open(dbName, 1); | |
| 39 request.onupgradeneeded = (event) => { | |
| 40 const eventWatcher = requestWatcher(testCase, request); | |
| 41 const database = event.target.result; | |
| 42 const transaction = event.target.transaction; | |
| 43 setupCallback(database, transaction); | |
| 44 resolve(eventWatcher.wait_for('success')); | |
| 45 }; | |
| 46 request.onerror = | |
| 47 testCase.unreached_func('indexedDB.open issued an error event'); | |
| 48 }); | |
| 49 }); | |
| 50 }; | |
| 51 | |
| 52 // Creates a 'books' object store whose contents closely resembles the first | |
| 53 // example in the IndexedDB specification. | |
| 54 const createBooksStore = function(testCase, database) { | |
| 55 const store = database.createObjectStore('books', | |
| 56 { keyPath: 'isbn', autoIncrement: true }); | |
| 57 const index = store.createIndex('by_author', 'author'); | |
| 58 store.put({title: 'Quarry Memories', author: 'Fred', isbn: 123456}); | |
| 59 store.put({title: 'Water Buffaloes', author: 'Fred', isbn: 234567}); | |
| 60 store.put({title: 'Bedrock Nights', author: 'Barney', isbn: 345678}); | |
| 61 return store; | |
| 62 }; | |
| 63 | |
| 64 // Creates a 'not_books' object store used to test renaming into existing or | |
| 65 // deleted store names. | |
| 66 const createNotBooksStore = function(testCase, database) { | |
|
cmumford
2016/08/26 21:23:52
You need to call this somewhere too right? Also, c
pwnall
2016/08/26 22:23:17
Done.
I wrote createNotBooksStore to test the ren
| |
| 67 const store = database.createObjectStore('not_books'); | |
| 68 return store; | |
| 69 }; | |
| 70 | |
| 71 // Renames the 'books' store into a 'not books' store. | |
| 72 // | |
| 73 // Returns a Promise that yields an IndexedDB open success event. | |
| 74 const renameBooksStore = function(testCase) { | |
| 75 return new Promise((resolve, reject) => { | |
| 76 const request = indexedDB.open(databaseName(testCase), 2); | |
| 77 request.onupgradeneeded = (event) => { | |
| 78 const eventWatcher = requestWatcher(testCase, request); | |
| 79 const database = event.target.result; | |
| 80 const transaction = event.target.transaction; | |
| 81 const store = transaction.objectStore('books'); | |
| 82 store.name = 'renamed_books'; | |
| 83 resolve(eventWatcher.wait_for('success')); | |
| 84 }; | |
| 85 request.onerror = | |
| 86 testCase.unreached_func('indexedDB.open issued an error event'); | |
| 87 }); | |
| 88 }; | |
| 89 | |
| 90 // Verifies that an object store's contents matches the contents used to create | |
| 91 // the books store in the test database's version 1. | |
| 92 const checkStoreContents = function(testCase, store) { | |
| 93 let request = store.get(123456); | |
| 94 let eventWatcher = requestWatcher(testCase, request); | |
| 95 return eventWatcher.wait_for('success').then(() => { | |
| 96 let result = request.result; | |
| 97 testCase.step(() => { | |
| 98 assert_equals(result.isbn, 123456); | |
| 99 assert_equals(result.author, 'Fred'); | |
| 100 assert_equals(result.title, 'Quarry Memories'); | |
| 101 }); | |
| 102 }); | |
| 103 }; | |
| 104 | |
| 105 // Verifies that an object store's index matches the index used to create the | |
| 106 // books store in the test database's version 1. | |
| 107 const checkStoreIndex = function(testCase, store) { | |
| 108 let index = store.index('by_author'); | |
| 109 let request = index.get('Barney'); | |
| 110 let eventWatcher = requestWatcher(testCase, request); | |
| 111 return eventWatcher.wait_for('success').then(() => { | |
| 112 let result = request.result; | |
| 113 testCase.step(() => { | |
| 114 assert_equals(result.isbn, 345678); | |
| 115 assert_equals(result.title, 'Bedrock Nights'); | |
| 116 }); | |
| 117 }); | |
| 118 }; | |
| 119 | |
| 120 // Verifies that an object store's key generator is in the same state as the | |
| 121 // key generator created for the books store in the test database's version 1. | |
| 122 const checkStoreGenerator = function(testCase, store, expectedKey) { | |
| 123 let request = store.put({title: 'Bedrock Nights II', author: 'Barney'}); | |
| 124 let eventWatcher = requestWatcher(testCase, request); | |
| 125 return eventWatcher.wait_for('success').then(() => { | |
| 126 let result = request.result; | |
| 127 testCase.step(() => { | |
| 128 assert_equals(result, expectedKey); | |
| 129 }); | |
| 130 }); | |
| 131 }; | |
| 132 | |
| 133 promise_test(testCase => { | |
| 134 let bookStore = null, bookStore2 = null; | |
| 135 let renamedBookStore = null, renamedBookStore2 = null; | |
| 136 return createDatabase(testCase, (database, transaction) => { | |
| 137 bookStore = createBooksStore(testCase, database); | |
| 138 }).then((event) => { | |
| 139 const database = event.target.result; | |
| 140 testCase.step(() => { | |
| 141 assert_array_equals(database.objectStoreNames, ['books']); | |
| 142 }); | |
| 143 const transaction = database.transaction('books', 'readonly'); | |
| 144 bookStore2 = transaction.objectStore('books'); | |
| 145 // If checkStoreContents fails here, its implementation is incorrect. | |
| 146 return checkStoreContents(testCase, bookStore2).then(() => { | |
| 147 database.close(); | |
| 148 }); | |
| 149 }).then(() => { | |
| 150 return new Promise((resolve, reject) => { | |
| 151 const request = indexedDB.open(databaseName(testCase), 2); | |
| 152 request.onupgradeneeded = (event) => { | |
| 153 const eventWatcher = requestWatcher(testCase, request); | |
| 154 const database = event.target.result; | |
| 155 const transaction = event.target.transaction; | |
| 156 renamedBookStore = transaction.objectStore('books'); | |
| 157 renamedBookStore.name = 'renamed_books'; | |
| 158 testCase.step(() => { | |
| 159 assert_equals(renamedBookStore.name, 'renamed_books'); | |
| 160 }); | |
| 161 resolve(eventWatcher.wait_for('success')); | |
| 162 }; | |
| 163 request.onerror = | |
| 164 testCase.unreached_func('indexedDB.open issued an error event'); | |
| 165 }); | |
| 166 }).then((event) => { | |
| 167 const database = event.target.result; | |
| 168 testCase.step(() => { | |
| 169 assert_array_equals(database.objectStoreNames, ['renamed_books']); | |
| 170 }); | |
| 171 const transaction = database.transaction('renamed_books', 'readonly'); | |
| 172 renamedBookStore2 = transaction.objectStore('renamed_books'); | |
| 173 return checkStoreContents(testCase, renamedBookStore2).then(() => { | |
| 174 database.close(); | |
| 175 }); | |
| 176 }).then(() => { | |
| 177 testCase.step(() => { | |
| 178 assert_equals(bookStore.name, 'books'); | |
| 179 assert_equals(bookStore2.name, 'books'); | |
| 180 assert_equals(renamedBookStore.name, 'renamed_books'); | |
| 181 assert_equals(renamedBookStore2.name, 'renamed_books'); | |
| 182 }); | |
| 183 }); | |
| 184 }, 'IndexedDB object store rename in new transaction', { | |
| 185 assert: 'An object store can be renamed in a new version change transaction' , | |
| 186 }); | |
| 187 | |
| 188 promise_test(testCase => { | |
| 189 let renamedBookStore = null, renamedBookStore2 = null; | |
| 190 return createDatabase(testCase, (database, transaction) => { | |
| 191 renamedBookStore = createBooksStore(testCase, database); | |
| 192 renamedBookStore.name = 'renamed_books'; | |
| 193 testCase.step(() => { | |
| 194 assert_equals(renamedBookStore.name, 'renamed_books'); | |
| 195 }); | |
| 196 }).then((event) => { | |
| 197 const database = event.target.result; | |
| 198 testCase.step(() => { | |
| 199 assert_array_equals(database.objectStoreNames, ['renamed_books']); | |
| 200 }); | |
| 201 const transaction = database.transaction('renamed_books', 'readonly'); | |
| 202 renamedBookStore2 = transaction.objectStore('renamed_books'); | |
| 203 return checkStoreContents(testCase, renamedBookStore2).then(() => { | |
| 204 database.close(); | |
| 205 }); | |
| 206 }).then(() => { | |
| 207 testCase.step(() => { | |
| 208 assert_equals(renamedBookStore.name, 'renamed_books'); | |
| 209 assert_equals(renamedBookStore2.name, 'renamed_books'); | |
| 210 }); | |
| 211 }); | |
| 212 }, 'IndexedDB object store rename in the transaction where it is created', { | |
| 213 assert: | |
| 214 'An object store can be renamed in the transaction where it is created', | |
| 215 }); | |
| 216 | |
| 217 promise_test(testCase => { | |
| 218 return createDatabase(testCase, (database, transaction) => { | |
| 219 createBooksStore(testCase, database); | |
| 220 }).then((event) => { | |
| 221 const database = event.target.result; | |
| 222 const transaction = database.transaction('books', 'readonly'); | |
| 223 const store = transaction.objectStore('books'); | |
| 224 // If checkStoreIndex fails here, its implementation is incorrect. | |
| 225 return checkStoreIndex(testCase, store).then(() => { | |
| 226 database.close(); | |
| 227 }); | |
| 228 }).then(() => { | |
| 229 return renameBooksStore(testCase); | |
| 230 }).then((event) => { | |
| 231 const database = event.target.result; | |
| 232 const transaction = database.transaction('renamed_books', 'readonly'); | |
| 233 const store = transaction.objectStore('renamed_books'); | |
| 234 return checkStoreIndex(testCase, store).then(() => { | |
| 235 database.close(); | |
| 236 }); | |
| 237 }); | |
| 238 }, 'IndexedDB object store rename covers index', { | |
| 239 assert: 'Renaming an object store maintains its indexes', | |
| 240 }); | |
| 241 | |
| 242 promise_test(testCase => { | |
| 243 return createDatabase(testCase, (database, transaction) => { | |
| 244 createBooksStore(testCase, database); | |
| 245 }).then((event) => { | |
| 246 const database = event.target.result; | |
| 247 const transaction = database.transaction('books', 'readwrite'); | |
| 248 // If checkStoreGenerator fails here, its implementation is incorrect. | |
| 249 const store = transaction.objectStore('books'); | |
| 250 return checkStoreGenerator(testCase, store, 345679).then(() => { | |
| 251 database.close(); | |
| 252 }); | |
| 253 }).then(() => { | |
| 254 return renameBooksStore(testCase); | |
| 255 }).then((event) => { | |
| 256 const database = event.target.result; | |
| 257 const transaction = database.transaction('renamed_books', 'readwrite'); | |
| 258 const store = transaction.objectStore('renamed_books'); | |
| 259 return checkStoreGenerator(testCase, store, 345680).then(() => { | |
| 260 database.close(); | |
| 261 }); | |
| 262 }); | |
| 263 }, 'IndexedDB object store rename covers key generator', { | |
| 264 assert: 'Renaming an object store maintains its key generator', | |
| 265 }); | |
| 266 | |
| 267 promise_test(testCase => { | |
| 268 const dbName = databaseName(testCase); | |
| 269 let bookStore = null, bookStore2 = null, bookStore3 = null; | |
| 270 return createDatabase(testCase, (database, transaction) => { | |
| 271 bookStore = createBooksStore(testCase, database); | |
| 272 }).then((event) => { | |
| 273 const database = event.target.result; | |
| 274 database.close(); | |
| 275 }).then((event) => { | |
| 276 return new Promise((resolve, reject) => { | |
| 277 const request = indexedDB.open(dbName, 2); | |
| 278 request.onupgradeneeded = (event) => { | |
| 279 const database = event.target.result; | |
| 280 const transaction = event.target.transaction; | |
| 281 bookStore2 = transaction.objectStore('books'); | |
| 282 bookStore2.name = 'renamed_books'; | |
| 283 testCase.step(() => { | |
| 284 assert_equals(bookStore.name, 'books'); | |
| 285 assert_equals(bookStore2.name, 'renamed_books'); | |
| 286 }); | |
| 287 request.onerror = (event) => { | |
| 288 event.preventDefault(); | |
| 289 resolve(event); | |
| 290 } | |
| 291 transaction.onabort = () => null; | |
| 292 transaction.onerror = () => null; | |
| 293 transaction.abort(); | |
| 294 }; | |
| 295 request.onerror = | |
| 296 testCase.unreached_func('indexedDB.open issued an error event'); | |
| 297 request.onsuccess = testCase.unreached_func( | |
| 298 'indexedDB.open was not supposed to succeed'); | |
| 299 }); | |
| 300 }).then((event) => { | |
| 301 testCase.step(() => { | |
| 302 assert_equals(bookStore.name, 'books', | |
| 303 'object store rename not reverted after transaction abort'); | |
| 304 }); | |
| 305 | |
| 306 const request = indexedDB.open(dbName, 1); | |
| 307 const eventWatcher = requestWatcher(testCase, request); | |
| 308 return eventWatcher.wait_for('success'); | |
| 309 }).then((event) => { | |
| 310 const database = event.target.result; | |
| 311 const transaction = database.transaction('books', 'readonly'); | |
| 312 bookStore3 = transaction.objectStore('books'); | |
| 313 return checkStoreContents(testCase, bookStore3).then(() => { | |
| 314 database.close(); | |
| 315 }); | |
| 316 }).then(() => { | |
| 317 testCase.step(() => { | |
| 318 assert_equals(bookStore.name, 'books'); | |
| 319 assert_equals(bookStore2.name, 'books'); | |
| 320 assert_equals(bookStore3.name, 'books'); | |
| 321 }); | |
| 322 }); | |
| 323 }, 'IndexedDB object store rename in aborted transaction', { | |
| 324 assert: 'An object store rename is correctly reverted', | |
| 325 }); | |
| 326 | |
| 327 </script> | |
| OLD | NEW |