Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
"use strict";
const { MockRegistrar } = ChromeUtils.importESModule(
);
const { sinon } = ChromeUtils.importESModule(
);
const { CustomIconManager } = ChromeUtils.importESModule(
"moz-src:///browser/components/shell/CustomIconManager.sys.mjs"
);
const PREF_ICON_ID = "browser.shell.customIcon.id";
const TEST_AUMID = "Test.Firefox.AUMID";
const TEST_SHORTCUTS = ["C:\\fake\\Desktop\\Nightly.lnk"];
const RETRO_RESOURCE_ID = CustomIconManager.list().retro.iconResourceId;
// CustomIconManager.apply() refuses to run on MSIX (packaged) builds, so on the
// MSIX CI job every task except the MSIX-specific one (which fakes the
// condition itself and runs everywhere) is skipped.
const ON_MSIX = Services.sysinfo.getProperty("hasWinPackageId");
// add_task() mutates the options object it is handed (tagging it isTask), so
// each call needs its own fresh object rather than a shared one.
function skipOnMsix() {
return { skip_if: () => ON_MSIX };
}
function exePath() {
return Services.dirsvc.get("XREExeF", Ci.nsIFile).path;
}
let shellServiceMock = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIWindowsShellService]),
enumerateInstallShortcuts: sinon.stub(),
setShortcutsIcon: sinon.stub(),
};
let winTaskbarMock = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIWinTaskbar]),
setAllWindowIcons: sinon.stub(),
get defaultGroupId() {
return TEST_AUMID;
},
};
// Reset stub history + default behaviour and clear the pref before each task.
function resetMocks() {
shellServiceMock.enumerateInstallShortcuts.reset();
shellServiceMock.enumerateInstallShortcuts.resolves(TEST_SHORTCUTS.slice());
shellServiceMock.setShortcutsIcon.reset();
shellServiceMock.setShortcutsIcon.resolves();
winTaskbarMock.setAllWindowIcons.reset();
Services.prefs.clearUserPref(PREF_ICON_ID);
}
add_setup(function () {
let shellCid = MockRegistrar.register(
"@mozilla.org/browser/shell-service;1",
shellServiceMock
);
let taskbarCid = MockRegistrar.register(
"@mozilla.org/windows-taskbar;1",
winTaskbarMock
);
registerCleanupFunction(() => {
MockRegistrar.unregister(taskbarCid);
MockRegistrar.unregister(shellCid);
Services.prefs.clearUserPref(PREF_ICON_ID);
});
});
/**
* This test verifies that apply() enumerates shortcuts by the default AUMID,
* writes the catalog resource ID (positive, un-negated) and the executable
* path to the matching shortcuts, sets the runtime window icon, and records
* the pref.
*/
add_task(
skipOnMsix(),
async function test_apply_updates_shortcuts_pref_and_runtime() {
resetMocks();
await CustomIconManager.apply("retro");
Assert.ok(
shellServiceMock.enumerateInstallShortcuts.calledOnceWithExactly(
TEST_AUMID
),
"enumerateInstallShortcuts called once with the default AUMID"
);
Assert.ok(
shellServiceMock.setShortcutsIcon.calledOnce,
"setShortcutsIcon called once"
);
let [shortcuts, iconPath, resourceId] =
shellServiceMock.setShortcutsIcon.getCall(0).args;
Assert.deepEqual(
shortcuts,
TEST_SHORTCUTS,
"passed the enumerated shortcuts through"
);
Assert.equal(iconPath, exePath(), "icon source is the running executable");
Assert.equal(
resourceId,
RETRO_RESOURCE_ID,
"passed the catalog resource ID as-is (negation happens in C++, not JS)"
);
Assert.ok(
winTaskbarMock.setAllWindowIcons.calledOnceWithExactly(RETRO_RESOURCE_ID),
"runtime window icon set to the retro resource ID"
);
Assert.equal(
Services.prefs.getStringPref(PREF_ICON_ID, ""),
"retro",
"pref records the applied id"
);
}
);
/**
* This test verifies that apply() rejects when given an id absent from the
* catalog, without touching any shortcut or runtime state or the pref.
*/
add_task(skipOnMsix(), async function test_apply_unknown_id_throws() {
resetMocks();
await Assert.rejects(
CustomIconManager.apply("does-not-exist"),
/Unknown icon id/,
"apply rejects for an unknown catalog id"
);
Assert.ok(
shellServiceMock.setShortcutsIcon.notCalled,
"no shortcut work attempted for an unknown id"
);
Assert.ok(
winTaskbarMock.setAllWindowIcons.notCalled,
"no runtime work attempted for an unknown id"
);
Assert.equal(
Services.prefs.getStringPref(PREF_ICON_ID, ""),
"",
"pref left untouched"
);
});
/**
* This test verifies that apply() throws on MSIX (packaged) builds, where the
* feature is unsupported, without touching shortcuts, the runtime icon, or the
* pref.
*/
add_task(async function test_apply_throws_on_msix() {
resetMocks();
// Fake an MSIX build by flipping the sysinfo property the manager checks.
// nsSystemInfo is a writable property bag, so set it directly and restore it.
let bag = Services.sysinfo.QueryInterface(Ci.nsIWritablePropertyBag2);
let original = bag.getProperty("hasWinPackageId");
bag.setPropertyAsBool("hasWinPackageId", true);
try {
await Assert.rejects(
CustomIconManager.apply("retro"),
/MSIX/,
"apply rejects on an MSIX build"
);
Assert.ok(
shellServiceMock.setShortcutsIcon.notCalled,
"no shortcut work attempted on MSIX"
);
Assert.ok(
winTaskbarMock.setAllWindowIcons.notCalled,
"no runtime work attempted on MSIX"
);
Assert.equal(
Services.prefs.getStringPref(PREF_ICON_ID, ""),
"",
"pref left untouched on MSIX"
);
} finally {
bag.setPropertyAsBool("hasWinPackageId", original);
}
});
/**
* This test verifies that revert() resets matching shortcuts to the
* executable's default icon (resource ID 0), clears the runtime override, and
* clears the pref.
*/
add_task(
skipOnMsix(),
async function test_revert_resets_shortcuts_pref_and_runtime() {
resetMocks();
Services.prefs.setStringPref(PREF_ICON_ID, "retro");
await CustomIconManager.revert();
Assert.ok(
shellServiceMock.setShortcutsIcon.calledOnce,
"setShortcutsIcon called once"
);
let [, iconPath, resourceId] =
shellServiceMock.setShortcutsIcon.getCall(0).args;
Assert.equal(iconPath, exePath(), "reverts using the executable path");
Assert.equal(
resourceId,
0,
"resource ID 0 selects the executable's default icon"
);
Assert.ok(
winTaskbarMock.setAllWindowIcons.calledOnceWithExactly(0),
"runtime window icon cleared (0)"
);
Assert.ok(!Services.prefs.prefHasUserValue(PREF_ICON_ID), "pref cleared");
}
);
/**
* This test verifies that when enumeration matches no shortcuts, apply() skips
* setShortcutsIcon but still applies the runtime icon and records the pref, so
* the running window updates even though no shortcut could be changed.
*/
add_task(skipOnMsix(), async function test_apply_no_matching_shortcuts() {
resetMocks();
shellServiceMock.enumerateInstallShortcuts.resolves([]);
// Must not throw even though nothing matched.
await CustomIconManager.apply("retro");
Assert.ok(
shellServiceMock.setShortcutsIcon.notCalled,
"setShortcutsIcon not called when enumeration matched nothing"
);
Assert.ok(
winTaskbarMock.setAllWindowIcons.calledOnceWithExactly(RETRO_RESOURCE_ID),
"runtime icon still applied even though no shortcut changed"
);
Assert.equal(
Services.prefs.getStringPref(PREF_ICON_ID, ""),
"retro",
"pref still recorded"
);
});
/**
* This test verifies that when setShortcutsIcon rejects, apply() logs and
* swallows the failure rather than throwing, and still applies the runtime
* icon and pref.
*/
add_task(
skipOnMsix(),
async function test_apply_shortcut_write_failure_is_swallowed() {
resetMocks();
shellServiceMock.setShortcutsIcon.rejects(
Components.Exception(
"mock setShortcutsIcon failure",
Cr.NS_ERROR_NOT_AVAILABLE
)
);
// A shortcut-write failure is logged, not thrown.
await CustomIconManager.apply("retro");
Assert.ok(
shellServiceMock.setShortcutsIcon.calledOnce,
"setShortcutsIcon was attempted"
);
Assert.ok(
winTaskbarMock.setAllWindowIcons.calledOnceWithExactly(RETRO_RESOURCE_ID),
"runtime icon still applied despite the shortcut-write failure"
);
Assert.equal(
Services.prefs.getStringPref(PREF_ICON_ID, ""),
"retro",
"pref still recorded"
);
}
);
/**
* This test verifies that ensureAppliedOrRevert() with a pref naming a known
* catalog id re-applies the runtime icon only, without rewriting shortcuts,
* and keeps the pref.
*/
add_task(
skipOnMsix(),
async function test_ensureAppliedOrRevert_applies_known_id() {
resetMocks();
Services.prefs.setStringPref(PREF_ICON_ID, "retro");
await CustomIconManager.ensureAppliedOrRevert();
Assert.ok(
winTaskbarMock.setAllWindowIcons.calledOnceWithExactly(RETRO_RESOURCE_ID),
"runtime icon applied for a known id"
);
Assert.ok(
shellServiceMock.setShortcutsIcon.notCalled,
"ensureAppliedOrRevert does not rewrite shortcuts for a known id"
);
Assert.equal(
Services.prefs.getStringPref(PREF_ICON_ID, ""),
"retro",
"pref retained"
);
}
);
/**
* This test verifies that ensureAppliedOrRevert() with a pref naming an id
* absent from the catalog (e.g. a newer build's icon, or one since retired)
* reverts the shortcuts and runtime icon to default and clears the pref.
*/
add_task(
skipOnMsix(),
async function test_ensureAppliedOrRevert_reverts_unknown_id() {
resetMocks();
Services.prefs.setStringPref(PREF_ICON_ID, "icon-from-a-newer-build");
await CustomIconManager.ensureAppliedOrRevert();
// Unknown id -> revert: shortcuts reset to default, runtime cleared, pref
// cleared.
Assert.ok(
shellServiceMock.setShortcutsIcon.calledOnce,
"revert rewrote shortcuts"
);
Assert.equal(
shellServiceMock.setShortcutsIcon.getCall(0).args[2],
0,
"shortcuts reset to the default icon"
);
Assert.ok(
winTaskbarMock.setAllWindowIcons.calledOnceWithExactly(0),
"runtime icon cleared"
);
Assert.ok(!Services.prefs.prefHasUserValue(PREF_ICON_ID), "pref cleared");
}
);
/**
* This test verifies that ensureAppliedOrRevert() does nothing when no custom
* icon pref is set.
*/
add_task(
skipOnMsix(),
async function test_ensureAppliedOrRevert_noop_without_pref() {
resetMocks();
await CustomIconManager.ensureAppliedOrRevert();
Assert.ok(
shellServiceMock.setShortcutsIcon.notCalled,
"no shortcut work when no custom icon is recorded"
);
Assert.ok(
winTaskbarMock.setAllWindowIcons.notCalled,
"no runtime work when no custom icon is recorded"
);
}
);