Bug 1168606 - Added test case for IDBObjectStore.delete while iterating a cursor. r=asuth
authorSimon Giesecke <sgiesecke@mozilla.com>
Thu, 14 Nov 2019 15:51:16 +0000
changeset 501977 fc34f807986e87758aa1433c228497fd9eb1c974
parent 501976 fe3a07722fee5e181b33b93240634bdb7639dde2
child 501978 1ca7e5b24181b20b3a63c3b11227ba46afabdb94
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1168606
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1168606 - Added test case for IDBObjectStore.delete while iterating a cursor. r=asuth Differential Revision: https://phabricator.services.mozilla.com/D48504
testing/web-platform/tests/IndexedDB/idbcursor_continue_delete_objectstore.htm
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/IndexedDB/idbcursor_continue_delete_objectstore.htm
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<title>IDBObjectStore.delete() and IDBCursor.continue() - object store - remove a record from the object store while iterating cursor</title>
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support.js"></script>
+
+<script>
+    /* The goal here is to test that any prefetching of cursor values performs
+     * correct invalidation of prefetched data.  This test is motivated by the
+     * particularities of the Firefox implementation of preloading, and is
+     * specifically motivated by an edge case when prefetching prefetches at
+     * least 2 extra records and at most determines whether a mutation is
+     * potentially relevant based on current cursor position and direction and
+     * does not test for key equivalence.  Future implementations may want to
+     * help refine this test if their cursors are more clever.
+     *
+     * Step-wise we:
+     * - Open a cursor, returning key 0.
+     * - When the cursor request completes, without yielding control:
+     *   - Issue a delete() call that won't actually delete anything but looks
+     *     relevant.  This should purge prefetched records 1 and 2.
+     *   - Issue a continue() which should result in record 1 being fetched
+     *     again and record 2 being prefetched again.
+     *   - Delete record 2.  Unless there's a synchronously available source
+     *     of truth, the data from continue() above will not be present and
+     *     we'll expect the implementation to need to set a flag to invalidate
+     *     the prefetched data when it arrives.
+     * - When the cursor request completes, validate we got record 1 and issue
+     *   a continue.
+     * - When the request completes, we should have a null cursor result value
+     *   because 2 was deleted.
+     */
+    var db,
+      count = 0,
+      t = async_test(),
+      records = [ { pKey: "primaryKey_0" },
+                  { pKey: "primaryKey_1" },
+                  { pKey: "primaryKey_2" } ];
+
+    // This is a key that is not present in the database, but that is known to
+    // be relevant to a forward iteration of the above keys by comparing to be
+    // greater than all of them.
+    var plausibleFutureKey = "primaryKey_9";
+
+    var open_rq = createdb(t);
+    open_rq.onupgradeneeded = function(e) {
+        db = e.target.result;
+
+        var objStore = db.createObjectStore("test", { keyPath: "pKey" });
+
+        for (var i = 0; i < records.length; i++)
+            objStore.add(records[i]);
+    };
+
+    open_rq.onsuccess = t.step_func(CursorDeleteRecord);
+
+
+    function CursorDeleteRecord(e) {
+        var txn = db.transaction("test", "readwrite"),
+          object_store = txn.objectStore("test"),
+          cursor_rq = object_store.openCursor();
+
+        var iteration = 0;
+
+        cursor_rq.onsuccess = t.step_func(function(e) {
+            var cursor = e.target.result;
+
+            switch (iteration) {
+            case 0:
+                object_store.delete(plausibleFutureKey);
+                assert_true(cursor != null, "cursor valid");
+                assert_equals(cursor.value.pKey, records[iteration].pKey);
+                cursor.continue();
+                object_store.delete(records[2].pKey);
+                break;
+            case 1:
+                assert_true(cursor != null, "cursor valid");
+                assert_equals(cursor.value.pKey, records[iteration].pKey);
+                cursor.continue();
+                break;
+            case 2:
+                assert_equals(cursor, null, "cursor no longer valid");
+                break;
+            };
+            iteration++;
+        });
+
+        txn.oncomplete = t.step_func(VerifyRecordWasDeleted);
+    }
+
+
+    function VerifyRecordWasDeleted(e) {
+        var cursor_rq = db.transaction("test")
+                          .objectStore("test")
+                          .openCursor();
+
+        cursor_rq.onsuccess = t.step_func(function(e) {
+            var cursor = e.target.result;
+
+            if (!cursor) {
+                assert_equals(count, 2, 'count');
+                t.done();
+            }
+
+            assert_equals(cursor.value.pKey, records[count].pKey);
+            count++;
+            cursor.continue();
+        });
+    }
+
+</script>
+
+<div id="log"></div>