Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
"use strict";
/**
* @fileOverview Checks that the MLBF updating logic works reasonably.
*/
Services.prefs.setBoolPref("extensions.blocklist.useMLBF", true);
const ExtensionBlocklistMLBF = getExtensionBlocklistMLBF();
// This test needs to interact with the RemoteSettings client.
ExtensionBlocklistMLBF.ensureInitialized();
// Multiple internal calls to update should be coalesced and end up with the
// MLBF attachment from the last update call.
add_task(async function collapse_multiple_pending_update_requests() {
const observed = [];
// The first step of starting an update is to read from the RemoteSettings
// collection. When a non-forced update is requested while another update is
// pending, the non-forced update should return/await the previous call
// instead of starting a new read/fetch from the RemoteSettings collection.
// Add a spy to the RemoteSettings client, so we can verify that the number
// of RemoteSettings accesses matches with what we expect.
const originalClientGet = ExtensionBlocklistMLBF._client.get;
const spyClientGet = tag => {
ExtensionBlocklistMLBF._client.get = async function () {
// Record the method call.
observed.push(tag);
// Clone a valid record and tag it so we can identify it below.
let dummyRecord = JSON.parse(JSON.stringify(MLBF_RECORD));
dummyRecord.tagged = tag;
return [dummyRecord];
};
};
// Another significant part of updating is fetching the MLBF attachment.
// Add a spy too, so we can check which attachment is being requested.
const originalFetchMLBF = ExtensionBlocklistMLBF._fetchMLBF;
ExtensionBlocklistMLBF._fetchMLBF = async function (record) {
observed.push(`fetchMLBF:${record.tagged}`);
throw new Error(`Deliberately ignoring call to MLBF:${record.tagged}`);
};
spyClientGet("initial"); // Very first call = read RS.
let update1 = ExtensionBlocklistMLBF._updateMLBF(false);
spyClientGet("unexpected update2"); // Non-forced update = reuse update1.
let update2 = ExtensionBlocklistMLBF._updateMLBF(false);
spyClientGet("forced1"); // forceUpdate=true = supersede previous update.
let forcedUpdate1 = ExtensionBlocklistMLBF._updateMLBF(true);
spyClientGet("forced2"); // forceUpdate=true = supersede previous update.
let forcedUpdate2 = ExtensionBlocklistMLBF._updateMLBF(true);
let res = await Promise.all([update1, update2, forcedUpdate1, forcedUpdate2]);
Assert.equal(observed.length, 4, "expected number of observed events");
Assert.equal(observed[0], "initial", "First update should request records");
Assert.equal(observed[1], "forced1", "Forced update supersedes initial");
Assert.equal(observed[2], "forced2", "Forced update supersedes forced1");
// We call the _updateMLBF methods immediately after each other. Every update
// request starts with an asynchronous operation (looking up the RS records),
// so the implementation should return early for all update requests except
// for the last one. So we should only observe a fetch for the last request.
Assert.equal(observed[3], "fetchMLBF:forced2", "expected fetch result");
// All update requests should end up with the same result.
Assert.equal(res[0], res[1], "update1 == update2");
Assert.equal(res[1], res[2], "update2 == forcedUpdate1");
Assert.equal(res[2], res[3], "forcedUpdate1 == forcedUpdate2");
ExtensionBlocklistMLBF._client.get = originalClientGet;
ExtensionBlocklistMLBF._fetchMLBF = originalFetchMLBF;
});