Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

const PAGE = "about:logging";
function clearLoggingPrefs() {
for (let pref of Services.prefs.getBranch("logging.").getChildList("")) {
info(`Clearing: ${pref}`);
Services.prefs.clearUserPref("logging." + pref);
}
}
// Before running, save any MOZ_LOG environment variable that might be preset,
// and restore them at the end of this test.
add_setup(async function saveRestoreLogModules() {
let savedLogModules = Services.env.get("MOZ_LOG");
Services.env.set("MOZ_LOG", "");
registerCleanupFunction(() => {
clearLoggingPrefs();
info(" -- Restoring log modules: " + savedLogModules);
for (let pref of savedLogModules.split(",")) {
let [logModule, level] = pref.split(":");
Services.prefs.setIntPref("logging." + logModule, parseInt(level));
}
// Removing this line causes a sandboxxing error in nsTraceRefCnt.cpp (!).
Services.env.set("MOZ_LOG", savedLogModules);
});
});
// Test that some UI elements are disabled in some cirumstances.
add_task(async function testElementsDisabled() {
// This test needs a MOZ_LOG env var set.
Services.env.set("MOZ_LOG", "example:4");
await BrowserTestUtils.withNewTab(PAGE, async browser => {
await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
$("#set-log-modules-button").disabled,
"Because a MOZ_LOG env var is set by the harness, it should be impossible to set new log modules."
);
});
});
Services.env.set("MOZ_LOG", "");
await BrowserTestUtils.withNewTab(
PAGE + "?modules=example:5&output=profiler",
async browser => {
await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
!$("#some-elements-unavailable").hidden,
"If a log modules are configured via URL params, a warning should be visible."
);
Assert.ok(
$("#set-log-modules-button").disabled,
"If a log modules are configured via URL params, some in-page elements should be disabled (button)."
);
Assert.ok(
$("#log-modules").disabled,
"If a log modules are configured via URL params, some in-page elements should be disabled (input)."
);
Assert.ok(
$("#logging-preset-dropdown").disabled,
"If a log modules are configured via URL params, some in-page elements should be disabled (dropdown)."
);
Assert.ok(
$("#radio-logging-profiler").disabled &&
$("#radio-logging-file").disabled,
"If the ouptut type is configured via URL param, the radio buttons should be disabled."
);
});
}
);
await BrowserTestUtils.withNewTab(
PAGE + "?preset=media-playback",
async browser => {
await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
!$("#some-elements-unavailable").hidden,
"If a preset is selected via URL, a warning should be displayed."
);
Assert.ok(
$("#set-log-modules-button").disabled,
"If a preset is selected via URL, some in-page elements should be disabled (button)."
);
Assert.ok(
$("#log-modules").disabled,
"If a preset is selected via URL, some in-page elements should be disabled (input)."
);
Assert.ok(
$("#logging-preset-dropdown").disabled,
"If a preset is selected via URL, some in-page elements should be disabled (dropdown)."
);
});
}
);
clearLoggingPrefs();
});
// Test URL parameters
const modulesInURL = "example:4,otherexample:5";
const presetInURL = "media-playback";
const threadsInURL = "example,otherexample";
const profilerPresetInURL = "media";
add_task(async function testURLParameters() {
await BrowserTestUtils.withNewTab(
PAGE + "?modules=" + modulesInURL,
async browser => {
await SpecialPowers.spawn(browser, [modulesInURL], async modulesInURL => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
!$("#some-elements-unavailable").hidden,
"If modules are selected via URL, a warning should be displayed."
);
var inInputSorted = $("#log-modules").value.split(",").sort().join(",");
var modulesSorted = modulesInURL.split(",").sort().join(",");
Assert.equal(
modulesSorted,
inInputSorted,
"When selecting modules via URL params, the log modules aren't immediately set"
);
});
}
);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: PAGE + "?preset=" + presetInURL,
},
async browser => {
await SpecialPowers.spawn(browser, [presetInURL], async presetInURL => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
!$("#some-elements-unavailable").hidden,
"If a preset is selected via URL, a warning should be displayed."
);
var inInputSorted = $("#log-modules").value.split(",").sort().join(",");
var presetSorted = content
.presets()
[presetInURL].modules.split(",")
.sort()
.join(",");
Assert.equal(
inInputSorted,
presetSorted,
"When selecting a preset via URL params, the correct log modules are reflected in the input."
);
});
}
);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: PAGE + "?profiler-preset=" + profilerPresetInURL,
},
async browser => {
await SpecialPowers.spawn(browser, [profilerPresetInURL], async inURL => {
let $ = content.document.querySelector.bind(content.document);
// Threads override doesn't have a UI element, the warning shouldn't
// be displayed.
Assert.ok(
$("#some-elements-unavailable").hidden,
"When overriding the profiler preset, no warning is displayed on the page."
);
var inSettings = content.settings().profilerPreset;
Assert.equal(
inSettings,
inURL,
"When overriding the profiler preset via URL param, the correct preset is set in the logging manager settings."
);
});
}
);
await BrowserTestUtils.withNewTab(PAGE + "?profilerstacks", async browser => {
await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
!$("#some-elements-unavailable").hidden,
"If the profiler stacks config is set via URL, a warning should be displayed."
);
Assert.ok(
$("#with-profiler-stacks-checkbox").disabled,
"If the profiler stacks config is set via URL, its checkbox should be disabled."
);
Assert.ok(
Services.prefs.getBoolPref("logging.config.profilerstacks"),
"The preference for profiler stacks is set initially, as a result of parsing the URL parameter"
);
$("#radio-logging-file").click();
$("#radio-logging-profiler").click();
Assert.ok(
$("#with-profiler-stacks-checkbox").disabled,
"If the profiler stacks config is set via URL, its checkbox should be disabled even after clicking around."
);
});
});
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: PAGE + "?invalid-param",
},
async browser => {
await SpecialPowers.spawn(browser, [profilerPresetInURL], async () => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
!$("#error").hidden,
"When an invalid URL param is passed in, the page displays a warning."
);
});
}
);
clearLoggingPrefs();
});
// Test various things related to presets: that it's populated correctly, that
// setting presets work in terms of UI, but also that it sets the logging.*
// prefs correctly.
add_task(async function testAboutLoggingPresets() {
await BrowserTestUtils.withNewTab(PAGE, async browser => {
await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
let presetsDropdown = $("#logging-preset-dropdown");
Assert.equal(
Object.keys(content.presets()).length,
presetsDropdown.childNodes.length,
"Presets populated."
);
Assert.equal(presetsDropdown.value, "networking");
$("#set-log-modules-button").click();
Assert.ok(
$("#no-log-modules").hidden && !$("#current-log-modules").hidden,
"When log modules are set, they are visible."
);
var lengthModuleListNetworking = $("#log-modules").value.length;
var lengthCurrentModuleListNetworking = $("#current-log-modules")
.innerText.length;
Assert.notEqual(
lengthModuleListNetworking,
0,
"When setting a profiler preset, the module string is non-empty (input)."
);
Assert.notEqual(
lengthCurrentModuleListNetworking,
0,
"When setting a profiler preset, the module string is non-empty (selected modules)."
);
// Change preset
presetsDropdown.value = "media-playback";
presetsDropdown.dispatchEvent(new content.Event("change"));
// Check the following after "onchange".
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => content.setTimeout(resolve, 0));
Assert.equal(
presetsDropdown.value,
"media-playback",
"Selecting another preset is reflected in the page"
);
$("#set-log-modules-button").click();
Assert.ok(
$("#no-log-modules").hidden && !$("#current-log-modules").hidden,
"When other log modules are set, they are still visible"
);
Assert.notEqual(
$("#log-modules").value.length,
0,
"When setting a profiler preset, the module string is non-empty (input)."
);
Assert.notEqual(
$("#current-log-modules").innerText.length,
0,
"When setting a profiler preset, the module string is non-empty (selected modules)."
);
Assert.notEqual(
$("#log-modules").value.length,
lengthModuleListNetworking,
"When setting another profiler preset, the module string changes (input)."
);
let currentLogModulesString = $("#current-log-modules").innerText;
Assert.notEqual(
currentLogModulesString.length,
lengthCurrentModuleListNetworking,
"When setting another profiler preset, the module string changes (selected modules)."
);
// After setting some log modules via the preset dropdown, verify
// that they have been reflected to logging.* preferences.
var activeLogModules = [];
let children = Services.prefs.getBranch("logging.").getChildList("");
for (let pref of children) {
if (pref.startsWith("config.")) {
continue;
}
try {
let value = Services.prefs.getIntPref(`logging.${pref}`);
activeLogModules.push(`${pref}:${value}`);
} catch (e) {
console.error(e);
}
}
let mod;
while ((mod = activeLogModules.pop())) {
Assert.ok(
currentLogModulesString.includes(mod),
`${mod} was effectively set`
);
}
});
});
clearLoggingPrefs();
});
// Test various things around the profiler stacks feature
add_task(async function testProfilerStacks() {
// Check the initial state before changing anything.
Assert.ok(
!Services.prefs.getBoolPref("logging.config.profilerstacks", false),
"The preference for profiler stacks isn't set initially"
);
await BrowserTestUtils.withNewTab(PAGE, async browser => {
await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
const checkbox = $("#with-profiler-stacks-checkbox");
Assert.ok(
!checkbox.checked,
"The profiler stacks checkbox isn't checked at load time."
);
checkbox.checked = true;
checkbox.dispatchEvent(new content.Event("change"));
Assert.ok(
Services.prefs.getBoolPref("logging.config.profilerstacks"),
"The preference for profiler stacks is now set to true"
);
checkbox.checked = false;
checkbox.dispatchEvent(new content.Event("change"));
Assert.ok(
!Services.prefs.getBoolPref("logging.config.profilerstacks"),
"The preference for profiler stacks is now back to false"
);
$("#radio-logging-file").click();
Assert.ok(
checkbox.disabled,
"The profiler stacks checkbox is disabled when the output type is 'file'"
);
$("#radio-logging-profiler").click();
Assert.ok(
!checkbox.disabled,
"The profiler stacks checkbox is enabled when the output type is 'profiler'"
);
});
});
clearLoggingPrefs();
});
// Here we test that starting and stopping log collection to the Firefox
// Profiler opens a new tab. We don't actually check the content of the profile.
add_task(async function testProfilerOpens() {
await BrowserTestUtils.withNewTab(PAGE, async browser => {
let profilerOpenedPromise = BrowserTestUtils.waitForNewTab(
gBrowser,
false
);
SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
// Override the URL the profiler uses to avoid hitting external
// resources (and crash).
await SpecialPowers.pushPrefEnv({
set: [
["devtools.performance.recording.ui-base-url", "https://example.com"],
["devtools.performance.recording.ui-base-url-path", "/"],
],
});
$("#radio-logging-file").click();
$("#radio-logging-profiler").click();
$("#logging-preset-dropdown").value = "networking";
$("#logging-preset-dropdown").dispatchEvent(new content.Event("change"));
$("#set-log-modules-button").click();
$("#toggle-logging-button").click();
// Wait for the profiler to start. This can be very slow.
await content.profilerPromise();
// Wait for some time for good measure while the profiler collects some
// data. We don't really care about the data itself.
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => content.setTimeout(resolve, 1000));
$("#toggle-logging-button").click();
});
let tab = await profilerOpenedPromise;
Assert.ok(true, "Profiler tab opened after profiling");
await BrowserTestUtils.removeTab(tab);
});
clearLoggingPrefs();
});
// Same test, outputing to a file, with network logging, while opening and
// closing a tab. We only check that the file exists and has a non-zero size.
add_task(async function testLogFileFound() {
await BrowserTestUtils.withNewTab(PAGE, async browser => {
await SpecialPowers.spawn(browser, [], async () => {
// Clear any previous log file.
let $ = content.document.querySelector.bind(content.document);
$("#radio-logging-file").click();
$("#log-file").value = "";
$("#log-file").dispatchEvent(new content.Event("change"));
$("#set-log-file-button").click();
Assert.ok(
!$("#no-log-file").hidden,
"When a log file hasn't been set, it's indicated as such."
);
});
});
await BrowserTestUtils.withNewTab(PAGE, async browser => {
let logPath = await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
$("#radio-logging-file").click();
// Set the log file (use the default path)
$("#set-log-file-button").click();
var logPath = $("#current-log-file").innerText;
// Set log modules for networking
$("#logging-preset-dropdown").value = "networking";
$("#logging-preset-dropdown").dispatchEvent(new content.Event("change"));
$("#set-log-modules-button").click();
return logPath;
});
// No need to start or stop logging when logging to a file. Just open
// a tab, any URL will do. Wait for this tab to be loaded so we're sure
// something (anything) has happened in necko.
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
true /* waitForLoad */
);
await BrowserTestUtils.removeTab(tab);
let logDirectory = PathUtils.parent(logPath);
let logBasename = PathUtils.filename(logPath);
let entries = await IOUtils.getChildren(logDirectory);
let foundNonEmptyLogFile = false;
for (let entry of entries) {
if (entry.includes(logBasename)) {
info("-- Log file found: " + entry);
let fileinfo = await IOUtils.stat(entry);
foundNonEmptyLogFile |= fileinfo.size > 0;
}
}
Assert.ok(foundNonEmptyLogFile, "Found at least one non-empty log file.");
});
clearLoggingPrefs();
});