diff options
Diffstat (limited to 'toolkit/components/places/tests/sync/test_bookmark_abort_merging.js')
-rw-r--r-- | toolkit/components/places/tests/sync/test_bookmark_abort_merging.js | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/sync/test_bookmark_abort_merging.js b/toolkit/components/places/tests/sync/test_bookmark_abort_merging.js new file mode 100644 index 0000000000..877feb99f4 --- /dev/null +++ b/toolkit/components/places/tests/sync/test_bookmark_abort_merging.js @@ -0,0 +1,220 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +var { AsyncShutdown } = ChromeUtils.importESModule( + "resource://gre/modules/AsyncShutdown.sys.mjs" +); + +add_task(async function test_transaction_in_progress() { + let buf = await openMirror("transaction_in_progress"); + + await storeRecords(buf, [ + { + id: "menu", + parentid: "places", + type: "folder", + children: ["bookmarkAAAA"], + }, + { + id: "bookmarkAAAA", + parentid: "menu", + type: "bookmark", + title: "A", + bmkUri: "http://example.com/a", + }, + ]); + + // This transaction should block merging until the transaction is committed. + info("Open transaction on Places connection"); + await buf.db.execute("BEGIN EXCLUSIVE"); + + await Assert.rejects( + buf.apply(), + ex => ex.name == "MergeConflictError", + "Should not merge when a transaction is in progress" + ); + + info("Commit open transaction"); + await buf.db.execute("COMMIT"); + + info("Merging should succeed after committing"); + await buf.apply(); + + await buf.finalize(); + await PlacesUtils.bookmarks.eraseEverything(); + await PlacesSyncUtils.bookmarks.reset(); +}); + +add_task(async function test_abort_store() { + let buf = await openMirror("abort_store"); + + let controller = new AbortController(); + controller.abort(); + await Assert.rejects( + storeRecords( + buf, + [ + { + id: "menu", + parentid: "places", + type: "folder", + children: [], + }, + ], + { signal: controller.signal } + ), + ex => ex.name == "InterruptedError", + "Should abort storing when signaled" + ); + + await buf.finalize(); + await PlacesUtils.bookmarks.eraseEverything(); + await PlacesSyncUtils.bookmarks.reset(); +}); + +add_task(async function test_abort_merging() { + let buf = await openMirror("abort_merging"); + + let controller = new AbortController(); + controller.abort(); + await Assert.rejects( + buf.apply({ signal: controller.signal }), + ex => ex.name == "InterruptedError", + "Should abort merge when signaled" + ); + + // Even though the merger is already finalized on the Rust side, the DB + // connection is still open on the JS side. Finalizing `buf` closes it. + await buf.finalize(); + await PlacesUtils.bookmarks.eraseEverything(); + await PlacesSyncUtils.bookmarks.reset(); +}); + +add_task(async function test_blocker_state() { + let barrier = new AsyncShutdown.Barrier("Test"); + let buf = await SyncedBookmarksMirror.open({ + path: "blocker_state_buf.sqlite", + finalizeAt: barrier.client, + recordStepTelemetry(...args) {}, + recordValidationTelemetry(...args) {}, + }); + await storeRecords(buf, [ + { + id: "menu", + parentid: "places", + type: "folder", + children: ["bookmarkAAAA"], + }, + { + id: "bookmarkAAAA", + parentid: "menu", + type: "bookmark", + title: "A", + bmkUri: "http://example.com/a", + }, + ]); + + await buf.tryApply(buf.finalizeController.signal); + await barrier.wait(); + + let state = buf.progress.fetchState(); + let names = []; + for (let s of state.steps) { + equal(typeof s.at, "number", `Should report timestamp for ${s.step}`); + switch (s.step) { + case "fetchLocalTree": + greaterOrEqual( + s.took, + 0, + "Should report time taken to fetch local tree" + ); + deepEqual( + s.counts, + [ + { name: "items", count: 6 }, + { name: "deletions", count: 0 }, + ], + "Should report number of items in local tree" + ); + break; + + case "fetchRemoteTree": + greaterOrEqual( + s.took, + 0, + "Should report time taken to fetch remote tree" + ); + deepEqual( + s.counts, + [ + { name: "items", count: 6 }, + { name: "deletions", count: 0 }, + ], + "Should report number of items in remote tree" + ); + break; + + case "merge": + greaterOrEqual(s.took, 0, "Should report time taken to merge"); + deepEqual( + s.counts, + [{ name: "items", count: 6 }], + "Should report merge stats" + ); + break; + + case "apply": + greaterOrEqual(s.took, 0, "Should report time taken to apply"); + ok(!("counts" in s), "Should not report counts for applying"); + break; + + case "notifyObservers": + greaterOrEqual( + s.took, + 0, + "Should report time taken to notify observers" + ); + ok(!("counts" in s), "Should not report counts for observers"); + break; + + case "fetchLocalChangeRecords": + greaterOrEqual( + s.took, + 0, + "Should report time taken to fetch records for upload" + ); + deepEqual( + s.counts, + [{ name: "items", count: 4 }], + "Should report number of records to upload" + ); + break; + + case "finalize": + ok(!("took" in s), "Should not report time taken to finalize"); + ok(!("counts" in s), "Should not report counts for finalizing"); + } + names.push(s.step); + } + deepEqual( + names, + [ + "fetchLocalTree", + "fetchRemoteTree", + "merge", + "apply", + "notifyObservers", + "fetchLocalChangeRecords", + "finalize", + ], + "Should report merge progress after waiting on blocker" + ); + ok( + buf.finalizeController.signal.aborted, + "Should abort finalize signal on shutdown" + ); + + await buf.finalize(); + await PlacesUtils.bookmarks.eraseEverything(); + await PlacesSyncUtils.bookmarks.reset(); +}); |