Source code
Revision control
Copy as Markdown
Other Tools
/* Any copyright is dedicated to the Public Domain.
"use strict";
/* import-globals-from head.js */
// Helpers for the remote debugging backward compatibility tests, which connect
// this Firefox to a second Firefox provisioned by ./mach devtools-compat-test.
// Documentation in /documentation/TESTS_BACKWARD_COMPAT.md
const COMPAT_CONFIG_ENV_VAR = "DEVTOOLS_COMPAT_CONFIG";
// The fixture pages are served by the regular mochitest HTTP server, but the
// server Firefox runs with its own profile and has no proxy configured, so it
// cannot use the example.com aliases the other URL_ROOT constants rely on.
// 127.0.0.1 is served directly (it is registered in build/pgo/server-locations.txt)
// and, unlike mochi.test, it is a secure context, which the service worker
// fixture needs. The port matches the one declared in server-locations.txt.
const FIXTURE_ROOT =
CHROME_URL_ROOT.replace(
) + "resources/backward-compat/";
/**
* Read the configuration written by ./mach devtools-compat-test.
*
* @returns {object|null}
* The parsed configuration, or null when no server was provisioned,
* which is the case on regular pushes and for a plain `mach mochitest`
* run. The configuration holds the name of the `server` recipe, the
* `host` of the server as a "host:port" string, the `control` channel
* base URL, and a `runtime` object describing the server application
* with its brandName, version and channel.
*/
async function getCompatConfig() {
const path = Services.env.get(COMPAT_CONFIG_ENV_VAR);
if (!path) {
return null;
}
info(`Load backward compatibility configuration from ${path}`);
const buffer = await IOUtils.read(path);
return JSON.parse(new TextDecoder().decode(buffer));
}
/* exported getCompatConfig */
function getCurrentVersion() {
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
return AppConstants.MOZ_APP_VERSION;
}
// When testing against the release channel close to a new release, Nightly
// might already have been bumped while release is still lagging behind and
// now has a gap of 3 versions against Nightly.
function isReleaseVersionTooOld(compatConfig) {
if (compatConfig.runtime.channel !== "release") {
// Only relevant when testing release channel.
return false;
}
const getMajor = version => Number.parseInt(version.match(/\d+/)[0], 10);
return (
getMajor(getCurrentVersion()) === getMajor(compatConfig.runtime.version) + 3
);
}
/**
* Register a test which only runs when a server has been provisioned. Without
* one the task reports a single passing assertion and returns immediately.
*
* @param {Function} taskFn
* Async test function, called with the configuration returned by
* getCompatConfig as its only argument.
*/
function addCompatTask(taskFn) {
add_task(async function () {
const config = await getCompatConfig();
if (!config) {
ok(
true,
`No ${COMPAT_CONFIG_ENV_VAR} in the environment, skipping. ` +
"Run these tests with `./mach devtools-compat-test`"
);
return;
}
const { brandName, version, channel } = config.runtime;
info(
`Testing against ${brandName} ${version} (${channel}) on ${config.host}`
);
/**
* @backward-compat { version 154 }
* the backward compatibility window to allow for a gap of 3 versions.
* Since the backward compatibility code for v154 was already removed, we
* should wait until the beginning of cycle 157 to remove this.
*
* When Firefox 154 is no longer on release, remove the if block below as
* well as the `isReleaseVersionTooOld` helper.
*/
if (isReleaseVersionTooOld(config)) {
ok(
true,
`The release version (${version}) is too old to be tested against the current version (${getCurrentVersion()}). ` +
"This should only happen for a few days around release time"
);
return;
}
await taskFn(config);
});
}
/* exported addCompatTask */
/**
* Register the provisioned server as an about:debugging network location and
* open about:debugging.
*
* @param {object} config
* The configuration returned by getCompatConfig.
* @param {object} options
* Options forwarded to openAboutDebugging.
* @returns {object} An object with the properties { tab, document, window }
* describing the create about:debugging tab.
*/
async function openAboutDebuggingWithCompatServer(config, options = {}) {
await pushPref(
"devtools.aboutdebugging.network-locations",
JSON.stringify([config.host])
);
return openAboutDebugging(options);
}
/* exported openAboutDebuggingWithCompatServer */
/**
* Run a command on the server Firefox, through the harness control channel.
*
* @param {object} config
* The configuration returned by getCompatConfig.
* @param {string} name
* Name of the command, see backward_compat_test_server/control.py.
* @param {object} args
* Arguments for the command.
* @returns {object} The result of the command.
*/
async function runDevToolsServerCommand(config, name, args = {}) {
info(`Run the '${name}' command on the server`);
const response = await fetch(`${config.control}/command`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, args }),
});
const body = await response.json();
if (!response.ok) {
throw new Error(`Command '${name}' failed: ${body.error}`);
}
return body.result;
}
/* exported runDevToolsServerCommand */
/**
* Open a tab on the server Firefox for one of the fixture pages, and return
* its window handle.
*/
async function openDevToolsServerTab(config, fixture) {
const { handle } = await runDevToolsServerCommand(config, "open-tab", {
url: getCompatFixtureUrl(fixture),
});
return handle;
}
/* exported openDevToolsServerTab */
/**
* Close a tab previously opened with openDevToolsServerTab.
*/
async function closeDevToolsServerTab(config, handle) {
await runDevToolsServerCommand(config, "close-tab", { handle });
}
/* exported closeDevToolsServerTab */
/**
* URL of a fixture page, as the server Firefox will load it.
*/
function getCompatFixtureUrl(fixture) {
return FIXTURE_ROOT + fixture;
}
/* exported getCompatFixtureUrl */
/**
* Open a fixture page on the server, then open a toolbox on it from the runtime
* page of the server. This is the setup shared by all the tests inspecting a
* remote target.
*
* @param {object} config
* The configuration returned by getCompatConfig.
* @param {string} fixture
* Name of the fixture page to open on the server.
* @returns {object}
* The about:debugging document, tab and window, the toolbox and its tab,
* and the window handle of the page opened on the server.
*/
async function openCompatToolbox(config, fixture) {
const serverHandle = await openDevToolsServerTab(config, fixture);
const { document, tab, window } =
await openAboutDebuggingWithCompatServer(config);
await connectToRuntime(config.host, document);
await waitForRuntimePage(config.runtime.brandName, document);
const { devtoolsTab, devtoolsWindow } = await openAboutDevtoolsToolbox(
document,
tab,
window,
getCompatFixtureUrl(fixture)
);
return {
document,
tab,
window,
devtoolsTab,
devtoolsWindow,
toolbox: getToolbox(devtoolsWindow),
serverHandle,
};
}
/* exported openCompatToolbox */
/**
* Tear down what openCompatToolbox created.
*/
async function closeCompatToolbox(config, setup) {
const { document, tab, window, devtoolsTab, serverHandle } = setup;
await closeAboutDevtoolsToolbox(document, devtoolsTab, window);
await closeDevToolsServerTab(config, serverHandle);
await removeTab(tab);
}
/* exported closeCompatToolbox */