Bug 1332024 - Add a method to explicitly finalize `JSONFile` objects. r?MattN
MozReview-Commit-ID: 4VjIyeMNXOJ
--- a/services/sync/tests/unit/test_telemetry.js
+++ b/services/sync/tests/unit/test_telemetry.js
@@ -58,16 +58,22 @@ function BogusEngine(service) {
BogusEngine.prototype = Object.create(SteamEngine.prototype);
async function cleanAndGo(engine, server) {
Svc.Prefs.resetBranch("");
Svc.Prefs.set("log.logger.engine.rotary", "Trace");
Service.recordManager.clearCache();
engine._tracker.clearChangedIDs();
+ // Bug 1325523: we create new engine and tracker instances for each test.
+ // Since the tracker persists in the background, it's possible for items
+ // tracked in one test to survive until the next test. This causes
+ // intermittent failures, so we explicitly finalize storage between tests
+ // to make sure all our changes are persisted.
+ await engine._tracker._storage.finalize();
await promiseStopServer(server);
}
// Avoid addon manager complaining about not being initialized
Service.engineManager.unregister("addons");
add_identity_test(this, async function test_basic() {
let helper = track_collections_helper();
@@ -431,16 +437,17 @@ add_task(async function test_initial_syn
}
greaterOrEqual(e.took, 1);
ok(!!e.outgoing)
equal(e.outgoing.length, 1);
notEqual(e.outgoing[0].sent, undefined);
equal(e.outgoing[0].failed, undefined);
}
} finally {
+ Service.engineManager.unregister(engine);
await cleanAndGo(engine, server);
}
});
add_task(async function test_nserror() {
Service.engineManager.register(SteamEngine);
let engine = Service.engineManager.get("steam");
engine.enabled = true;
--- a/toolkit/modules/JSONFile.jsm
+++ b/toolkit/modules/JSONFile.jsm
@@ -98,17 +98,17 @@ function JSONFile(config) {
}
if (config.saveDelayMs === undefined) {
config.saveDelayMs = kSaveDelayMs;
}
this._saver = new DeferredTask(() => this._save(), config.saveDelayMs);
AsyncShutdown.profileBeforeChange.addBlocker("JSON store: writing data",
- () => this._saver.finalize());
+ () => this.finalize());
}
JSONFile.prototype = {
/**
* String containing the file path where data should be saved.
*/
path: "",
@@ -277,9 +277,26 @@ JSONFile.prototype = {
}),
/**
* Synchronously work on the data just loaded into memory.
*/
_processLoadedData(data) {
this.data = this._dataPostProcessor ? this._dataPostProcessor(data) : data;
},
+
+ /**
+ * Ensures that all data is persisted to disk, and prevents future calls to
+ * `saveSoon`. This is called automatically on shutdown, but can also be
+ * called explicitly when the object is no longer needed. Finalizing the
+ * object multiple times is a no-op.
+ */
+ finalize: Task.async(function* () {
+ if (this._finalized) {
+ return;
+ }
+ this._finalized = true;
+ yield this._saver.finalize();
+ this._data = null;
+ this.dataReady = false;
+ }),
+ _finalized: false,
};