Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial version of an install and uninstall hook #778

Merged
merged 12 commits into from
Jun 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 33 additions & 0 deletions recipe-client-addon/lib/NormandyDriver.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,39 @@ this.NormandyDriver = function(sandboxManager) {
sandboxManager.removeHold(`setTimeout-${token}`);
},

/*
* Return a promise that resolves to an Addon ID if installation is successful.
*/
installAddon: sandboxManager.wrapAsync(async function installAddon(installUrl) {
const installObj = await AddonManager.getInstallForURL(installUrl, null, "application/x-xpinstall");
const result = new Promise((resolve, reject) => installObj.addListener({
onInstallEnded(addonInstall, addon) {
resolve(addon.id);
},
onInstallFailed(addonInstall) {
reject(`AddonInstall error code: [${addonInstall.error}]`);
},
onDownloadFailed() {
reject(`Download failed: [${installUrl}]`);
},
}));
installObj.install();
return result;
}),

/*
* Return a promise that resolves to a success messsage if
* addon uninstall is successful.
*/
uninstallAddon: sandboxManager.wrapAsync(async function uninstallAddon(addonId) {
const addon = await AddonManager.getAddonByID(addonId);
if (addon === null) {
throw new Error(`No addon with ID [${addonId}] found.`);
}
addon.uninstall();
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to bother returning anything here.

}),

// Sampling
ratioSample: sandboxManager.wrapAsync(Sampling.ratioSample),

Expand Down
5 changes: 3 additions & 2 deletions recipe-client-addon/test/browser/browser.ini
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
[DEFAULT]
support-files =
action_server.sjs
fixtures/normandy.xpi
head = head.js
[browser_NormandyDriver.js]
[browser_FilterExpressions.js]
[browser_EventEmitter.js]
[browser_Storage.js]
[browser_Heartbeat.js]
[browser_RecipeRunner.js]
support-files =
action_server.sjs
[browser_LogManager.js]
[browser_ClientEnvironment.js]
[browser_ShieldRecipeClient.js]
Expand Down
40 changes: 40 additions & 0 deletions recipe-client-addon/test/browser/browser_NormandyDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,46 @@ add_task(withDriver(Assert, async function uuids(driver) {
isnot(uuid1, uuid2, "uuids are unique");
}));

add_task(withDriver(Assert, async function installXpi(driver) {
// Test that we can install an XPI from any URL
const dir = getChromeDir(getResolvedURI(gTestPath));
dir.append("fixtures");
dir.append("normandy.xpi");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check with someone more up-to-date with this stuff (than me) - maybe @Mossop - how this works with add-on signature checks?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If normandy.xpi is unsigned then this will only work in automation in Firefox 55 and higher.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mossop normandy.xpi was indeed unsigned.. I've updated the xpi so that it's signed now.

const xpiUrl = Services.io.newFileURI(dir).spec;
var addonId = await driver.installAddon(xpiUrl);
is(addonId, "normandydriver@example.com", "Expected test addon was installed");
isnot(addonId, null, "Addon install was successful");

// Installing kicks off an asychronous process which tries to read the manifest.json
// to startup the addon. Note that onInstallEnded is triggered too early
// which is why we need this delay.
await new Promise(resolve => SimpleTest.executeSoon(resolve));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we wait for something specific, e.g. an observer notification generated by the test add-on once install has actually happened? Otherwise this feels like it's waiting to go intermittent on infra...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More generally, it feels odd that the addon manager doesn't provide something 'better' to wait for, where install is 'properly' finished, instead of only sort of... Maybe @Mossop can suggest something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is really a problem with extensions/internal/XPIInstall.jsm and the way that checkPrompot fires off an async function right away. Using the observer doesn't work right now because the onInstallEnded listeners are triggered prior to the install actually finishing up.


const uninstallMsg = await driver.uninstallAddon(addonId);
is(uninstallMsg, null, `Uninstall returned an unexpected message [${uninstallMsg}]`);
}));

add_task(withDriver(Assert, async function uninstallInvalidAddonId(driver) {
const invalidAddonId = "not_a_valid_xpi_id@foo.bar";
try {
await driver.uninstallAddon(invalidAddonId);
ok(false, `Uninstalling an invalid XPI should fail. uninstallAddon resolved successfully though.`);
} catch (e) {
ok(true, `This is the expected failure`);
}
}));


add_task(withDriver(Assert, async function installXpiBadURL(driver) {
const xpiUrl = "file:///tmp/invalid_xpi.xpi";
try {
await driver.installAddon(xpiUrl);
ok(false, "Installation succeeded on an XPI that doesn't exist");
} catch (reason) {
ok(true, `Installation was rejected: [${reason}]`);
}
}));

add_task(withDriver(Assert, async function userId(driver) {
// Test that userId is a UUID
ok(UUID_REGEX.test(driver.userId), "userId is a uuid");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"manifest_version": 2,
"name": "normandy_fixture",
"version": "1.0",
"description": "Dummy test fixture that's a webextension",
"applications": {
"gecko": {
"id": "normandydriver@example.com"
}
}
}
Binary file not shown.