Files
tubestation/toolkit/components/backgroundtasks/BackgroundTasksTestUtils.jsm
Nick Alexander 770d26e84e Bug 1776514 - Allow background tasks to enable Nimbus and Firefox Messaging System. r=barret,bytesized
This commit does several things:

1.  It configures a new Nimbus feature and corresponding message group
    `backgroundTaskMessage`.

2.  It configures `Firefox Messaging Experiments` to use the new
    Nimbus feature in background tasks.  The existing Remote Settings
    collection `nimbus-desktop-experiments` continues to be used.

    These configurations are achieved by setting preferences in
    `backgroundtasks_browser.js`, which is applied on top of
    `firefox.js` prefs by the preference service.  These preferences
    apply to every background task.

3.  It implements functions for enabling Nimbus and the Firefox
    Messaging System (and Messaging Experiments) that can be used by
    arbitrary background tasks.

    It is assumed (but not enforced here) that such tasks will use
    non-ephemeral (persistent) profiles, so that Remote Settings
    incremental sync, Nimbus bucketing, and Messaging System message
    limits, function as expected.

4.  It adds a new `message` background task specifically for testing
    background task messages.  Invoke the testing task with command
    lines like `firefox --backgroundtask message ...`.

    To ease testing, the framework accepts `--url about:studies?...`
    arguments from the Experimenter Web UI to explicitly opt-in to
    specific experiment branches.

    This task is complicated because it is intended both for QA to
    manually invoke, but also to be used by automated tests.

Eventually the existing `backgroundupdate` task will use the new
functions, just as the testing `message` task does.

Differential Revision: https://phabricator.services.mozilla.com/D150521
2022-07-22 04:42:13 +00:00

142 lines
4.2 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
var EXPORTED_SYMBOLS = ["BackgroundTasksTestUtils"];
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
const { Subprocess } = ChromeUtils.import(
"resource://gre/modules/Subprocess.jsm"
);
function getFirefoxExecutableFilename() {
if (AppConstants.platform === "win") {
return AppConstants.MOZ_APP_NAME + ".exe";
}
if (AppConstants.platform == "linux") {
return AppConstants.MOZ_APP_NAME + "-bin";
}
return AppConstants.MOZ_APP_NAME;
}
// Returns a nsIFile to the firefox.exe (really, application) executable file.
function getFirefoxExecutableFile() {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file = Services.dirsvc.get("GreBinD", Ci.nsIFile);
file.append(getFirefoxExecutableFilename());
return file;
}
var BackgroundTasksTestUtils = {
init(scope) {
this.testScope = scope;
},
async do_backgroundtask(
task,
options = { extraArgs: [], extraEnv: {}, onStdoutLine: null }
) {
options = Object.assign({}, options);
options.extraArgs = options.extraArgs || [];
options.extraEnv = options.extraEnv || {};
let command = getFirefoxExecutableFile().path;
let args = ["--backgroundtask", task];
args.push(...options.extraArgs);
// Ensure `resource://testing-common` gets mapped.
let protocolHandler = Services.io
.getProtocolHandler("resource")
.QueryInterface(Ci.nsIResProtocolHandler);
let uri = protocolHandler.getSubstitution("testing-common");
const { Assert } = this.testScope;
Assert.ok(!!uri, "resource://testing-common is not substituted");
// The equivalent of _TESTING_MODULES_DIR in xpcshell.
options.extraEnv.XPCSHELL_TESTING_MODULES_URI = uri.spec;
// Now we can actually invoke the process.
console.info(`launching background task`, {
command,
args,
extraEnv: options.extraEnv,
});
let { proc, readPromise } = await Subprocess.call({
command,
arguments: args,
environment: options.extraEnv,
environmentAppend: true,
stderr: "stdout",
}).then(p => {
p.stdin.close();
const dumpPipe = async pipe => {
// We must assemble all of the string fragments from stdout.
let leftover = "";
let data = await pipe.readString();
while (data) {
data = leftover + data;
// When the string is empty and the separator is not empty,
// split() returns an array containing one empty string,
// rather than an empty array, i.e., we always have
// `lines.length > 0`.
let lines = data.split(/\r\n|\r|\n/);
for (let line of lines.slice(0, -1)) {
dump(`${p.pid}> ${line}\n`);
if (options.onStdoutLine) {
options.onStdoutLine(line, p);
}
}
leftover = lines[lines.length - 1];
data = await pipe.readString();
}
if (leftover.length) {
dump(`${p.pid}> ${leftover}\n`);
if (options.onStdoutLine) {
options.onStdoutLine(leftover, p);
}
}
};
let readPromise = dumpPipe(p.stdout);
return { proc: p, readPromise };
});
let { exitCode } = await proc.wait();
try {
// Read from the output pipe.
await readPromise;
} catch (e) {
if (e.message !== "File closed") {
throw e;
}
}
return exitCode;
},
// Setup that allows to use the profile service in xpcshell tests,
// lifted from `toolkit/profile/xpcshell/head.js`.
setupProfileService() {
let gProfD = this.testScope.do_get_profile();
let gDataHome = gProfD.clone();
gDataHome.append("data");
gDataHome.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
let gDataHomeLocal = gProfD.clone();
gDataHomeLocal.append("local");
gDataHomeLocal.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
let xreDirProvider = Cc["@mozilla.org/xre/directory-provider;1"].getService(
Ci.nsIXREDirProvider
);
xreDirProvider.setUserDataDirectory(gDataHome, false);
xreDirProvider.setUserDataDirectory(gDataHomeLocal, true);
},
};