Commit 1113f81e authored by Joshua Bell's avatar Joshua Bell Committed by Commit Bot

WPT: Upstream and add some transaction scheduling tests

In service of https://github.com/w3c/IndexedDB/issues/253 move some
transaction scheduling tests from Blink to WPT.

This involved converting them from js-test.js to testharness.js, but
the overall logic of each test was retained.

This also adds one new test which verifies the change described
in https://github.com/w3c/IndexedDB/pull/319 - all browsers implicitly
block R-O transactions behind overlapping R/W transactions.

Change-Id: I596aaa75b79bf3bf3e17a2553abb4e11329d59ab
Bug: 921193
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2081237Reviewed-by: default avatarDaniel Murphy <dmurph@chromium.org>
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Auto-Submit: Joshua Bell <jsbell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746553}
parent 7cd5fd3b
......@@ -101,6 +101,16 @@ function assert_key_equals(actual, expected, description) {
assert_equals(indexedDB.cmp(actual, expected), 0, description);
}
// Usage:
// indexeddb_test(
// (test_object, db_connection, upgrade_tx, open_request) => {
// // Database creation logic.
// },
// (test_object, db_connection, open_request) => {
// // Test logic.
// test_object.done();
// },
// 'Test case 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;
};
}
// Returns a new function. After it is called |count| times, |func|
// will be called.
function barrier_func(count, func) {
let n = 0;
return () => {
if (++n === count)
func();
};
}
// META: script=support.js
indexeddb_test(
(t, db) => {
const store = db.createObjectStore('store');
},
(t, db1) => {
// Open a second connection to the same database.
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;
// Keep transaction1 alive for a while and ensure transaction2
// doesn't start.
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.");
// META: script=support.js
indexeddb_test(
(t, db) => {
const store = db.createObjectStore('store');
},
(t, db1) => {
// Open a second database.
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);
// Keep both transactions alive until each has reported at least one
// successful operation.
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.");
// META: script=support.js
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);
});
// transaction2 overlaps with transaction1, so must wait until transaction1
// completes.
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);
});
// transaction3 overlaps with transaction2, so must wait until transaction2
// completes even though it does not overlap with transaction1.
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.");
// META: script=support.js
indexeddb_test(
(t, db) => {
const store = db.createObjectStore('store');
},
(t, db) => {
// Create in order tx1, tx2.
const tx1 = db.transaction('store', 'readwrite');
const tx2 = db.transaction('store', 'readwrite');
// Use in order tx2, tx1.
tx2.objectStore('store').get(0);
tx1.objectStore('store').get(0);
const order = [];
const done = barrier_func(2, t.step_func_done(() => {
// IndexedDB Spec:
// https://w3c.github.io/IndexedDB/#transaction-scheduling
//
// If multiple "readwrite" transactions are attempting to
// access the same object store (i.e. if they have overlapping
// scope), the transaction that was created first must be the
// transaction which gets access to the object store first.
//
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");
// META: script=support.js
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");
// META: script=support.js
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);
});
// transaction2 overlaps with transaction1, so must wait until transaction1
// completes.
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);
});
// transaction3 overlaps with transaction2, so must wait until transaction2
// completes even though it does not overlap with transaction1.
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.");
// META: script=support.js
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);
// Keep both transactions alive until each has reported at least one
// successful operation
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() {
// NOTE: No logging since execution order is not deterministic.
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.');
if (this.importScripts) {
importScripts('../../../resources/js-test.js');
importScripts('shared.js');
}
description("Check that transactions in different databases can run in parallel.");
function test()
{
setDBNameFromPath();
evalAndLog("dbname1 = dbname + '1'");
evalAndLog("dbname2 = dbname + '2'");
deleteDatabase1();
}
function deleteDatabase1()
{
preamble();
request = evalAndLog("indexedDB.deleteDatabase(dbname1)");
request.onerror = unexpectedBlockedCallback;
request.onblocked = unexpectedBlockedCallback;
request.onsuccess = deleteDatabase2;
}
function deleteDatabase2()
{
preamble();
request = evalAndLog("indexedDB.deleteDatabase(dbname2)");
request.onerror = unexpectedBlockedCallback;
request.onblocked = unexpectedBlockedCallback;
request.onsuccess = openDatabase1;
}
function openDatabase1()
{
preamble();
request = evalAndLog("indexedDB.open(dbname1, 1)");
request.onerror = unexpectedBlockedCallback;
request.onblocked = unexpectedBlockedCallback;
request.onupgradeneeded = function openOnUpgradeNeeded1(evt) {
preamble(evt);
evalAndLog("db1 = event.target.result");
evalAndLog("store1 = db1.createObjectStore('store')");
evalAndLog("store1.put(0, 0)");
};
request.onsuccess = function openOnSuccess1(evt) {
preamble(evt);
evalAndLog("db1 = event.target.result");
openDatabase2();
};
}
function openDatabase2()
{
preamble();
request = evalAndLog("indexedDB.open(dbname2, 1)");
request.onerror = unexpectedBlockedCallback;
request.onblocked = unexpectedBlockedCallback;
request.onupgradeneeded = function onUpgradeNeeded2(evt) {
preamble(evt);
evalAndLog("db2 = event.target.result");
evalAndLog("store2 = db2.createObjectStore('store')");
};
request.onsuccess = function openOnSuccess2(evt) {
preamble(evt);
evalAndLog("db2 = event.target.result");
startWork();
};
}
function startWork()
{
preamble();
evalAndLog("transaction1 = db1.transaction('store', 'readwrite')");
transaction1.onabort = unexpectedAbortCallback;
transaction1.oncomplete = onTransactionComplete;
evalAndLog("transaction2 = db2.transaction('store', 'readwrite')");
transaction1.onabort = unexpectedAbortCallback;
transaction2.oncomplete = onTransactionComplete;
evalAndLog("transaction1PutSuccess = false");
evalAndLog("transaction2PutSuccess = false");
debug("Keep both transactions alive until each has reported at least one successful operation");
function doTransaction1Put() {
// NOTE: No logging since execution order is not deterministic.
request = transaction1.objectStore('store').put(0, 0);
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
transaction1PutSuccess = true;
if (!transaction2PutSuccess)
doTransaction1Put();
};
}
function doTransaction2Put() {
// NOTE: No logging since execution order is not deterministic.
request = transaction2.objectStore('store').put(0, 0);
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
transaction2PutSuccess = true;
if (!transaction1PutSuccess)
doTransaction2Put();
};
}
doTransaction1Put();
doTransaction2Put();
}
transactionCompletionCount = 0;
function onTransactionComplete(evt)
{
preamble(evt);
++transactionCompletionCount;
if (transactionCompletionCount < 2) {
debug("first transaction complete, still waiting...");
return;
}
shouldBeTrue("transaction1PutSuccess");
shouldBeTrue("transaction2PutSuccess");
evalAndLog("db1.close()");
evalAndLog("db2.close()");
finishJSTest();
}
test();
if (this.importScripts) {
importScripts('../../../resources/js-test.js');
importScripts('shared.js');
}
description("readonly transaction should see the result of a previous readwrite transaction");
indexedDBTest(prepareDatabase, runTransactions);
function prepareDatabase(evt)
{
preamble(evt);
evalAndLog("db = event.target.result");
evalAndLog("store = db.createObjectStore('store')");
evalAndLog("store.put('original value', 'key')");
}
function runTransactions(evt)
{
preamble(evt);
evalAndLog("db = event.target.result");
evalAndLog("transaction1 = db.transaction('store', 'readwrite')");
transaction1.onabort = unexpectedAbortCallback;
evalAndLog("transaction2 = db.transaction('store', 'readonly')");
transaction2.onabort = unexpectedAbortCallback;
evalAndLog("request = transaction1.objectStore('store').put('new value', 'key')");
request.onerror = unexpectedErrorCallback;
evalAndLog("request2 = transaction2.objectStore('store').get('key')");
request2.onerror = unexpectedErrorCallback;
request2.onsuccess = function checkResult(evt) {
preamble(evt);
shouldBeEqualToString('request2.result', 'new value');
db.close();
finishJSTest();
};
}
if (this.importScripts) {
importScripts('../../../resources/js-test.js');
importScripts('shared.js');
}
description("Check that read-only transactions within a database can run in parallel.");
indexedDBTest(prepareDatabase, runParallelTransactions);
function prepareDatabase(evt)
{
preamble(evt);
evalAndLog("db = event.target.result");
evalAndLog("store = db.createObjectStore('store')");
evalAndLog("store.put('value', 'key')");
}
function runParallelTransactions(evt)
{
preamble(evt);
evalAndLog("db = event.target.result");
debug("");
evalAndLog("transaction1 = db.transaction('store', 'readonly')");
transaction1.onabort = unexpectedAbortCallback;
transaction1.oncomplete = onTransactionComplete;
evalAndLog("transaction2 = db.transaction('store', 'readonly')");
transaction1.onabort = unexpectedAbortCallback;
transaction2.oncomplete = onTransactionComplete;
evalAndLog("transaction1GetSuccess = false");
evalAndLog("transaction2GetSuccess = false");
debug("Keep both transactions alive until each has reported at least one successful operation");
function doTransaction1Get() {
// NOTE: No logging since execution order is not deterministic.
request = transaction1.objectStore('store').get('key');
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
transaction1GetSuccess = true;
if (!transaction2GetSuccess)
doTransaction1Get();
};
}
function doTransaction2Get() {
// NOTE: No logging since execution order is not deterministic.
request = transaction2.objectStore('store').get('key');
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
transaction2GetSuccess = true;
if (!transaction1GetSuccess)
doTransaction2Get();
};
}
doTransaction1Get();
doTransaction2Get();
}
transactionCompletionCount = 0;
function onTransactionComplete(evt)
{
preamble(evt);
++transactionCompletionCount;
if (transactionCompletionCount < 2) {
debug("first transaction complete, still waiting...");
return;
}
shouldBeTrue("transaction1GetSuccess");
shouldBeTrue("transaction2GetSuccess");
evalAndLog("db.close()");
finishJSTest();
}
if (this.importScripts) {
importScripts('../../../resources/js-test.js');
importScripts('shared.js');
}
description("Check that readwrite transactions with overlapping scopes do not run in parallel.");
function test()
{
setDBNameFromPath();
request = evalAndLog("indexedDB.deleteDatabase(dbname)");
request.onerror = unexpectedBlockedCallback;
request.onblocked = unexpectedBlockedCallback;
request.onsuccess = openConnection1;
}
function openConnection1()
{
preamble();
request = evalAndLog("indexedDB.open(dbname, 1)");
request.onerror = unexpectedBlockedCallback;
request.onblocked = unexpectedBlockedCallback;
request.onupgradeneeded = function openOnUpgradeNeeded1(evt) {
preamble(evt);
evalAndLog("db = event.target.result");
evalAndLog("store = db.createObjectStore('store')");
};
request.onsuccess = function openOnSuccess(evt) {
preamble(evt);
evalAndLog("db1 = event.target.result");
openConnection2();
};
}
function openConnection2()
{
preamble();
request = evalAndLog("indexedDB.open(dbname, 1)");
request.onerror = unexpectedBlockedCallback;
request.onblocked = unexpectedBlockedCallback;
request.onupgradeneeded = unexpectedUpgradeNeededCallback;
request.onsuccess = function openOnSuccess2(evt) {
preamble(evt);
evalAndLog("db2 = event.target.result");
startWork();
};
}
function startWork()
{
preamble();
evalAndLog("transaction1 = db1.transaction('store', 'readwrite')");
transaction1.onabort = unexpectedAbortCallback;
evalAndLog("transaction2 = db2.transaction('store', 'readwrite')");
transaction1.onabort = unexpectedAbortCallback;
evalAndLog("transaction1PutSuccess = false");
evalAndLog("transaction1Complete = false");
evalAndLog("transaction2PutSuccess = false");
evalAndLog("transaction2Complete = false");
debug("");
debug("Keep transaction1 alive for a while and ensure transaction2 doesn't start");
evalAndLog("count = 0");
(function doTransaction1Put() {
request = evalAndLog("transaction1.objectStore('store').put(1, count++)");
request.onerror = unexpectedErrorCallback;
request.onsuccess = function put1OnSuccess(evt) {
preamble(evt);
evalAndLog("transaction1PutSuccess = true");
if (count < 5) {
doTransaction1Put();
}
};
}());
transaction1.oncomplete = function onTransaction1Complete(evt) {
preamble(evt);
evalAndLog("transaction1Complete = true");
shouldBeFalse("transaction2PutSuccess");
shouldBeFalse("transaction2Complete");
};
request = evalAndLog("transaction2.objectStore('store').put(2, 0)");
request.onerror = unexpectedErrorCallback;
request.onsuccess = function put2OnSuccess(evt) {
preamble(evt);
evalAndLog("transaction2PutSuccess = true");
shouldBeTrue("transaction1Complete");
};
transaction2.oncomplete = function onTransaction2Complete(evt) {
preamble(evt);
evalAndLog("transaction2Complete = true");
shouldBeTrue("transaction1PutSuccess");
shouldBeTrue("transaction1Complete");
shouldBeTrue("transaction2PutSuccess");
finishJSTest();
};
}
test();
if (this.importScripts) {
importScripts('../../../resources/js-test.js');
importScripts('shared.js');
}
description("Check that scope restrictions on read-write transactions are enforced.");
indexedDBTest(prepareDatabase, runTransactions);
function prepareDatabase(evt)
{
preamble(evt);
evalAndLog("db = event.target.result");
evalAndLog("db.createObjectStore('a')");
evalAndLog("db.createObjectStore('b')");
evalAndLog("db.createObjectStore('c')");
}
function runTransactions(evt)
{
preamble(evt);
evalAndLog("db = event.target.result");
debug("");
evalAndLog("transaction1 = db.transaction(['a'], 'readwrite')");
evalAndLog("transaction1Started = false");
evalAndLog("transaction1Complete = false");
request = evalAndLog("transaction1.objectStore('a').get(0)");
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
debug("");
evalAndLog("transaction1Started = true");
};
transaction1.onabort = unexpectedAbortCallback;
transaction1.oncomplete = function() {
debug("");
evalAndLog("transaction1Complete = true");
shouldBeFalse("transaction2Started");
shouldBeFalse("transaction3Started");
};
debug("");
debug("transaction2 overlaps with transaction1, so must wait until transaction1 completes");
evalAndLog("transaction2 = db.transaction(['a', 'b'], 'readwrite')");
evalAndLog("transaction2Started = false");
evalAndLog("transaction2Complete = false");
request = evalAndLog("transaction2.objectStore('a').get(0)");
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
debug("");
shouldBeTrue("transaction1Complete");
evalAndLog("transaction2Started = true");
};
transaction2.onabort = unexpectedAbortCallback;
transaction2.oncomplete = function() {
debug("");
evalAndLog("transaction2Complete = true");
shouldBeFalse("transaction3Started");
};
debug("");
debug("transaction3 overlaps with transaction2, so must wait until transaction2 completes");
debug("even though it does not overlap with transaction1");
evalAndLog("transaction3 = db.transaction(['b', 'c'], 'readwrite')");
evalAndLog("transaction3Started = false");
evalAndLog("transaction3Complete = false");
request = evalAndLog("transaction3.objectStore('b').get(0)");
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
debug("");
shouldBeTrue("transaction1Complete");
shouldBeTrue("transaction2Complete");
evalAndLog("transaction3Started = true");
};
transaction3.onabort = unexpectedAbortCallback;
transaction3.oncomplete = function() {
debug("");
evalAndLog("transaction3Complete = true");
finishJSTest();
};
}
Check that transactions in different databases can run in parallel.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
dbname = "transaction-coordination-across-databases.html"
dbname1 = dbname + '1'
dbname2 = dbname + '2'
deleteDatabase1():
indexedDB.deleteDatabase(dbname1)
deleteDatabase2():
indexedDB.deleteDatabase(dbname2)
openDatabase1():
indexedDB.open(dbname1, 1)
openOnUpgradeNeeded1():
db1 = event.target.result
store1 = db1.createObjectStore('store')
store1.put(0, 0)
openOnSuccess1():
db1 = event.target.result
openDatabase2():
indexedDB.open(dbname2, 1)
onUpgradeNeeded2():
db2 = event.target.result
store2 = db2.createObjectStore('store')
openOnSuccess2():
db2 = event.target.result
startWork():
transaction1 = db1.transaction('store', 'readwrite')
transaction2 = db2.transaction('store', 'readwrite')
transaction1PutSuccess = false
transaction2PutSuccess = false
Keep both transactions alive until each has reported at least one successful operation
onTransactionComplete():
first transaction complete, still waiting...
onTransactionComplete():
PASS transaction1PutSuccess is true
PASS transaction2PutSuccess is true
db1.close()
db2.close()
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<script src="../../resources/js-test.js"></script>
<script src="resources/shared.js"></script>
</head>
<body>
<script src="resources/transaction-coordination-across-databases.js"></script>
</body>
</html>
readonly transaction should see the result of a previous readwrite transaction
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
dbname = "transaction-coordination-ro-waits-for-rw.html"
indexedDB.deleteDatabase(dbname)
indexedDB.open(dbname)
prepareDatabase():
db = event.target.result
store = db.createObjectStore('store')
store.put('original value', 'key')
runTransactions():
db = event.target.result
transaction1 = db.transaction('store', 'readwrite')
transaction2 = db.transaction('store', 'readonly')
request = transaction1.objectStore('store').put('new value', 'key')
request2 = transaction2.objectStore('store').get('key')
checkResult():
PASS request2.result is "new value"
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<script src="../../resources/js-test.js"></script>
<script src="resources/shared.js"></script>
</head>
<body>
<script src="resources/transaction-coordination-ro-waits-for-rw.js"></script>
</body>
</html>
Check that read-only transactions within a database can run in parallel.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
dbname = "transaction-coordination-within-database.html"
indexedDB.deleteDatabase(dbname)
indexedDB.open(dbname)
prepareDatabase():
db = event.target.result
store = db.createObjectStore('store')
store.put('value', 'key')
runParallelTransactions():
db = event.target.result
transaction1 = db.transaction('store', 'readonly')
transaction2 = db.transaction('store', 'readonly')
transaction1GetSuccess = false
transaction2GetSuccess = false
Keep both transactions alive until each has reported at least one successful operation
onTransactionComplete():
first transaction complete, still waiting...
onTransactionComplete():
PASS transaction1GetSuccess is true
PASS transaction2GetSuccess is true
db.close()
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<script src="../../resources/js-test.js"></script>
<script src="resources/shared.js"></script>
</head>
<body>
<script src="resources/transaction-coordination-within-database.js"></script>
</body>
</html>
Verify Indexed DB transactions are ordered per spec
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
dbname = "transaction-ordering.html"
indexedDB.deleteDatabase(dbname)
indexedDB.open(dbname)
prepareDatabase():
db = event.target.result
store = db.createObjectStore('store')
onOpen():
db = event.target.result
Create in order tx1, tx2
tx1 = db.transaction('store', 'readwrite')
tx2 = db.transaction('store', 'readwrite')
Use in order tx2, tx1
tx2.objectStore('store').get(0)
tx1.objectStore('store').get(0)
order = []
tx1 complete
tx1 complete
done():
PASS areArraysEqual(order, [ 1, 2 ]) is true
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<script src="../../resources/js-test.js"></script>
<script src="resources/shared.js"></script>
<script>
description("Verify Indexed DB transactions are ordered per spec");
indexedDBTest(prepareDatabase, onOpen);
function prepareDatabase(evt)
{
preamble(evt);
evalAndLog("db = event.target.result");
evalAndLog("store = db.createObjectStore('store')");
}
function onOpen(evt)
{
preamble(evt);
evalAndLog("db = event.target.result");
debug("");
debug("Create in order tx1, tx2");
evalAndLog("tx1 = db.transaction('store', 'readwrite')");
evalAndLog("tx2 = db.transaction('store', 'readwrite')");
debug("");
debug("Use in order tx2, tx1");
evalAndLog("tx2.objectStore('store').get(0)");
evalAndLog("tx1.objectStore('store').get(0)");
debug("");
evalAndLog("order = []");
tx1.oncomplete = function(evt) {
debug("tx1 complete");
order.push(1);
if (order.length === 2) done();
};
tx2.oncomplete = function(evt) {
debug("tx1 complete");
order.push(2);
if (order.length === 2) done();
};
function done() {
preamble();
// IndexedDB Spec:
// https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#transaction-concept
//
// If multiple "readwrite" transactions are attempting to
// access the same object store (i.e. if they have overlapping
// scope), the transaction that was created first must be the
// transaction which gets access to the object store first.
//
shouldBeTrue("areArraysEqual(order, [ 1, 2 ])");
finishJSTest();
}
}
</script>
Check that readwrite transactions with overlapping scopes do not run in parallel.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
dbname = "transaction-readwrite-exclusive.html"
indexedDB.deleteDatabase(dbname)
openConnection1():
indexedDB.open(dbname, 1)
openOnUpgradeNeeded1():
db = event.target.result
store = db.createObjectStore('store')
openOnSuccess():
db1 = event.target.result
openConnection2():
indexedDB.open(dbname, 1)
openOnSuccess2():
db2 = event.target.result
startWork():
transaction1 = db1.transaction('store', 'readwrite')
transaction2 = db2.transaction('store', 'readwrite')
transaction1PutSuccess = false
transaction1Complete = false
transaction2PutSuccess = false
transaction2Complete = false
Keep transaction1 alive for a while and ensure transaction2 doesn't start
count = 0
transaction1.objectStore('store').put(1, count++)
transaction2.objectStore('store').put(2, 0)
put1OnSuccess():
transaction1PutSuccess = true
transaction1.objectStore('store').put(1, count++)
put1OnSuccess():
transaction1PutSuccess = true
transaction1.objectStore('store').put(1, count++)
put1OnSuccess():
transaction1PutSuccess = true
transaction1.objectStore('store').put(1, count++)
put1OnSuccess():
transaction1PutSuccess = true
transaction1.objectStore('store').put(1, count++)
put1OnSuccess():
transaction1PutSuccess = true
onTransaction1Complete():
transaction1Complete = true
PASS transaction2PutSuccess is false
PASS transaction2Complete is false
put2OnSuccess():
transaction2PutSuccess = true
PASS transaction1Complete is true
onTransaction2Complete():
transaction2Complete = true
PASS transaction1PutSuccess is true
PASS transaction1Complete is true
PASS transaction2PutSuccess is true
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<script src="../../resources/js-test.js"></script>
<script src="resources/shared.js"></script>
</head>
<body>
<script src="resources/transaction-readwrite-exclusive.js"></script>
</body>
</html>
Check that scope restrictions on read-write transactions are enforced.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
dbname = "transaction-scope-sequencing.html"
indexedDB.deleteDatabase(dbname)
indexedDB.open(dbname)
prepareDatabase():
db = event.target.result
db.createObjectStore('a')
db.createObjectStore('b')
db.createObjectStore('c')
runTransactions():
db = event.target.result
transaction1 = db.transaction(['a'], 'readwrite')
transaction1Started = false
transaction1Complete = false
transaction1.objectStore('a').get(0)
transaction2 overlaps with transaction1, so must wait until transaction1 completes
transaction2 = db.transaction(['a', 'b'], 'readwrite')
transaction2Started = false
transaction2Complete = false
transaction2.objectStore('a').get(0)
transaction3 overlaps with transaction2, so must wait until transaction2 completes
even though it does not overlap with transaction1
transaction3 = db.transaction(['b', 'c'], 'readwrite')
transaction3Started = false
transaction3Complete = false
transaction3.objectStore('b').get(0)
transaction1Started = true
transaction1Complete = true
PASS transaction2Started is false
PASS transaction3Started is false
PASS transaction1Complete is true
transaction2Started = true
transaction2Complete = true
PASS transaction3Started is false
PASS transaction1Complete is true
PASS transaction2Complete is true
transaction3Started = true
transaction3Complete = true
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<script src="../../resources/js-test.js"></script>
<script src="resources/shared.js"></script>
</head>
<body>
<script src="resources/transaction-scope-sequencing.js"></script>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment