| if (this.importScripts) { |
| importScripts('../../../resources/js-test.js'); |
| importScripts('shared.js'); |
| } |
| |
| description("Test IndexedDB's key generator behavior."); |
| |
| function test() |
| { |
| removeVendorPrefixes(); |
| |
| runTests(); |
| } |
| |
| var tests = []; |
| function defineTest(description, verchange, optional) { |
| tests.push( |
| { |
| description: description, |
| verchange: verchange, |
| optional: optional |
| } |
| ); |
| } |
| |
| function runTests() { |
| |
| function nextTest() { |
| if (!tests.length) { |
| testAcrossConnections(); |
| return; |
| } |
| |
| var test = tests.shift(); |
| debug(""); |
| debug(test.description); |
| |
| indexedDBTest(prepareDatabase, onSuccess); |
| function prepareDatabase() |
| { |
| db = event.target.result; |
| trans = event.target.transaction; |
| trans.onabort = unexpectedAbortCallback; |
| test.verchange(db, trans); |
| } |
| function onSuccess() { |
| db = event.target.result; |
| |
| function finishTest() { |
| evalAndLog("db.close()"); |
| nextTest(); |
| } |
| |
| if (test.optional) { |
| test.optional(db, finishTest); |
| } else { |
| finishTest(); |
| } |
| }; |
| } |
| |
| nextTest(); |
| } |
| |
| function check(store, key, expected) { |
| self.store = store; |
| request = evalAndLog("request = store.get(" + JSON.stringify(key) + ")"); |
| request.onerror = unexpectedErrorCallback; |
| request.onsuccess = function (e) { |
| self.expected = expected; |
| if (JSON.stringify(event.target.result) === JSON.stringify(expected)) { |
| testPassed("Got " + JSON.stringify(event.target.result) + " for key: " + JSON.stringify(key)); |
| } else { |
| testFailed("Got " + JSON.stringify(event.target.result) + " for key: " + JSON.stringify(key) + |
| " expected: " + JSON.stringify(expected)); |
| } |
| }; |
| } |
| |
| defineTest( |
| 'Verify that each object store has an independent key generator.', |
| function (db, trans) { |
| evalAndLog("store1 = db.createObjectStore('store1', { autoIncrement: true })"); |
| evalAndLog("store1.put('a')"); |
| check(store1, 1, 'a'); |
| evalAndLog("store2 = db.createObjectStore('store2', { autoIncrement: true })"); |
| evalAndLog("store2.put('a')"); |
| check(store2, 1, 'a'); |
| evalAndLog("store1.put('b')"); |
| check(store1, 2, 'b'); |
| evalAndLog("store2.put('b')"); |
| check(store2, 2, 'b'); |
| } |
| ); |
| |
| defineTest( |
| 'Verify that the key generator is not updated if insertion fails', |
| function (db, trans) { |
| trans.onerror = function(e) { e.preventDefault() }; |
| evalAndLog("store = db.createObjectStore('store1', { autoIncrement: true })"); |
| evalAndLog("index = store.createIndex('index1', 'ix', { unique: true })"); |
| evalAndLog("store.put({ ix: 'a'})"); |
| check(store, 1, {ix: 'a'}); |
| evalAndLog("store.put({ ix: 'a'})"); |
| evalAndLog("store.put({ ix: 'b'})"); |
| check(store, 2, {ix: 'b'}); |
| } |
| ); |
| |
| defineTest( |
| 'Verify that the key generator is not affected by item removal (delete or clear).', |
| function (db, trans) { |
| evalAndLog("store = db.createObjectStore('store1', { autoIncrement: true })"); |
| evalAndLog("store.put('a')"); |
| check(store, 1, 'a'); |
| evalAndLog("store.delete(1)"); |
| evalAndLog("store.put('b')"); |
| check(store, 2, 'b'); |
| evalAndLog("store.clear()"); |
| evalAndLog("store.put('c')"); |
| check(store, 3, 'c'); |
| evalAndLog("store.delete(IDBKeyRange.lowerBound(0))"); |
| evalAndLog("store.put('d')"); |
| check(store, 4, 'd'); |
| } |
| ); |
| |
| defineTest( |
| 'Verify that the key generator is only set if and only if a numeric key greater than the last generated key is used.', |
| function (db, trans) { |
| evalAndLog("store = db.createObjectStore('store1', { autoIncrement: true })"); |
| evalAndLog("store.put('a')"); |
| check(store, 1, 'a'); |
| evalAndLog("store.put('b', 3)"); |
| check(store, 3, 'b'); |
| evalAndLog("store.put('c')"); |
| check(store, 4, 'c'); |
| evalAndLog("store.put('d', -10)"); |
| check(store, -10, 'd'); |
| evalAndLog("store.put('e')"); |
| check(store, 5, 'e'); |
| evalAndLog("store.put('f', 6.00001)"); |
| check(store, 6.00001, 'f'); |
| evalAndLog("store.put('g')"); |
| check(store, 7, 'g'); |
| evalAndLog("store.put('f', 8.9999)"); |
| check(store, 8.9999, 'f'); |
| evalAndLog("store.put('g')"); |
| check(store, 9, 'g'); |
| evalAndLog("store.put('h', 'foo')"); |
| check(store, 'foo', 'h'); |
| evalAndLog("store.put('i')"); |
| check(store, 10, 'i'); |
| evalAndLog("store.put('j', [1000])"); |
| check(store, [1000], 'j'); |
| evalAndLog("store.put('k')"); |
| check(store, 11, 'k'); |
| |
| // FIXME: Repeat this test, but with a keyPath and inline key. |
| } |
| ); |
| |
| defineTest( |
| 'Verify that aborting a transaction resets the key generator state.', |
| function (db, trans) { |
| db.createObjectStore('store', { autoIncrement: true }); |
| }, |
| |
| function (db, callback) { |
| evalAndLog("trans1 = db.transaction(['store'], 'readwrite')"); |
| evalAndLog("store_t1 = trans1.objectStore('store')"); |
| evalAndLog("store_t1.put('a')"); |
| check(store_t1, 1, 'a'); |
| evalAndLog("store_t1.put('b')"); |
| check(store_t1, 2, 'b'); |
| |
| // Schedule the abort as a task (not run it synchronously) |
| store_t1.get(0).onsuccess = function () { |
| debug('aborting...'); |
| evalAndLog("trans1.abort()"); |
| trans1.onabort = function () { |
| debug('aborted!'); |
| |
| evalAndLog("trans2 = db.transaction(['store'], 'readwrite')"); |
| evalAndLog("store_t2 = trans2.objectStore('store')"); |
| evalAndLog("store_t2.put('c')"); |
| check(store_t2, 1, 'c'); |
| evalAndLog("store_t2.put('d')"); |
| check(store_t2, 2, 'd'); |
| |
| trans2.oncomplete = callback; |
| }; |
| }; |
| } |
| ); |
| |
| defineTest( |
| 'Verify that keys above 2^53 result in errors.', |
| function (db, trans) { |
| db.createObjectStore('store', { autoIncrement: true }); |
| }, |
| |
| function (db, callback) { |
| evalAndLog("trans1 = db.transaction(['store'], 'readwrite')"); |
| evalAndLog("store_t1 = trans1.objectStore('store')"); |
| evalAndLog("store_t1.put('a')"); |
| check(store_t1, 1, 'a'); |
| evalAndLog("store_t1.put('b', 9007199254740992)"); |
| check(store_t1, 9007199254740992, 'b'); |
| request = evalAndLog("store_t1.put('c')"); |
| request.onsuccess = unexpectedSuccessCallback; |
| request.onerror = function () { |
| debug("Error event fired auto-incrementing past 2^53 (as expected)"); |
| shouldBe("event.target.error.name", "'ConstraintError'"); |
| evalAndLog("event.preventDefault()"); |
| }; |
| evalAndLog("store_t1.put('d', 2)"); |
| check(store_t1, 2, 'd'); |
| |
| trans1.oncomplete = callback; |
| } |
| ); |
| |
| defineTest( |
| 'Verify that keys above 2^64 result in errors.', |
| function (db, trans) { |
| db.createObjectStore('store', { autoIncrement: true }); |
| }, |
| |
| function (db, callback) { |
| evalAndLog("trans1 = db.transaction(['store'], 'readwrite')"); |
| evalAndLog("store_t1 = trans1.objectStore('store')"); |
| evalAndLog("store_t1.put('a')"); |
| check(store_t1, 1, 'a'); |
| evalAndLog("store_t1.put('b', Math.pow(2, 64))"); |
| check(store_t1, Math.pow(2, 64), 'b'); |
| request = evalAndLog("store_t1.put('c')"); |
| request.onsuccess = unexpectedSuccessCallback; |
| request.onerror = function () { |
| debug("Error event fired auto-incrementing past 2^64 (as expected)"); |
| shouldBe("event.target.error.name", "'ConstraintError'"); |
| evalAndLog("event.preventDefault()"); |
| }; |
| evalAndLog("store_t1.put('d', 2)"); |
| check(store_t1, 2, 'd'); |
| |
| trans1.oncomplete = callback; |
| } |
| ); |
| |
| function testAcrossConnections() |
| { |
| debug(""); |
| debug("Ensure key generator state is maintained across connections:"); |
| indexedDBTest(prepareDatabase, doFirstWrite); |
| function prepareDatabase() |
| { |
| db = event.target.result; |
| evalAndLog("db.createObjectStore('store', {autoIncrement: true})"); |
| }; |
| |
| function doFirstWrite() { |
| debug(""); |
| evalAndLog("trans = db.transaction('store', 'readwrite')"); |
| trans.onabort = unexpectedAbortCallback; |
| evalAndLog("request = trans.objectStore('store').put('value1')"); |
| request.onerror = unexpectedErrorCallback; |
| request.onsuccess = function() { |
| shouldBe("request.result", "1"); |
| evalAndLog("trans.objectStore('store').clear()"); |
| }; |
| trans.oncomplete = closeAndReopen; |
| } |
| |
| function closeAndReopen() { |
| evalAndLog("db.close()"); |
| debug(""); |
| evalAndLog("request = indexedDB.open(dbname)"); |
| request.onsuccess = function () { |
| evalAndLog("db = request.result"); |
| doSecondWrite(); |
| }; |
| } |
| |
| function doSecondWrite() { |
| evalAndLog("trans = db.transaction('store', 'readwrite')"); |
| trans.onabort = unexpectedAbortCallback; |
| evalAndLog("request = trans.objectStore('store').put('value2')"); |
| request.onerror = unexpectedErrorCallback; |
| request.onsuccess = function() { |
| shouldBe("request.result", "2"); |
| }; |
| trans.oncomplete = function() { |
| debug(""); |
| finishJSTest(); |
| }; |
| }; |
| } |
| |
| test(); |