зеркало из https://github.com/mozilla/pjs.git
Bug 704642 - Rewrite TPS add-ons functionality; style improvements to TPS module; r=rnewman
This commit is contained in:
Родитель
9c819e6c6d
Коммит
25b3d10526
|
@ -21,7 +21,11 @@
|
|||
"test_privbrw_tabs.js",
|
||||
"test_bookmarks_in_same_named_folder.js",
|
||||
"test_client_wipe.js",
|
||||
"test_special_tabs.js"
|
||||
"test_special_tabs.js",
|
||||
"test_addon_sanity.js",
|
||||
"test_addon_restartless_xpi.js",
|
||||
"test_addon_nonrestartless_xpi.js",
|
||||
"test_addon_reconciling.js"
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<searchresults total_results="1">
|
||||
<addon id="5617">
|
||||
<name>Restartless Test XPI</name>
|
||||
<type id="1">Extension</type>
|
||||
<guid>restartless-xpi@tests.mozilla.org</guid>
|
||||
<slug>restartless-xpi</slug>
|
||||
<version>1.0</version>
|
||||
|
||||
<compatible_applications><application>
|
||||
<name>Firefox</name>
|
||||
<application_id>1</application_id>
|
||||
<min_version>3.6</min_version>
|
||||
<max_version>*</max_version>
|
||||
<appID>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</appID>
|
||||
</application></compatible_applications>
|
||||
<all_compatible_os><os>ALL</os></all_compatible_os>
|
||||
|
||||
<install os="ALL" size="485">http://127.0.0.1:4567/restartless.xpi</install>
|
||||
<created epoch="1252903662">
|
||||
2009-09-14T04:47:42Z
|
||||
</created>
|
||||
<last_updated epoch="1315255329">
|
||||
2011-09-05T20:42:09Z
|
||||
</last_updated>
|
||||
</addon>
|
||||
</searchresults>
|
|
@ -7,44 +7,23 @@
|
|||
* testrunner (no single quotes, extra comma's, etc).
|
||||
*/
|
||||
|
||||
var phases = { "phase1": "profile1",
|
||||
"phase2": "profile1",
|
||||
"phase3": "profile1",
|
||||
"phase4": "profile1",
|
||||
"phase5": "profile1" };
|
||||
EnableEngines(["addons"]);
|
||||
|
||||
/*
|
||||
* Test phases
|
||||
*/
|
||||
let phases = { "phase1": "profile1",
|
||||
"phase2": "profile1" };
|
||||
|
||||
Phase('phase1', [
|
||||
[Addons.install, ['unsigned-1.0.xml']],
|
||||
[Addons.verify, ['unsigned-xpi@tests.mozilla.org'], STATE_DISABLED],
|
||||
[Sync, SYNC_WIPE_SERVER],
|
||||
const id = "unsigned-xpi@tests.mozilla.org";
|
||||
|
||||
Phase("phase1", [
|
||||
[Addons.install, [id]],
|
||||
// Non-restartless add-on shouldn't be found after install.
|
||||
[Addons.verifyNot, [id]],
|
||||
|
||||
// But it should be marked for Sync.
|
||||
[Sync]
|
||||
]);
|
||||
|
||||
Phase('phase2', [
|
||||
[Sync],
|
||||
[Addons.verify, ['unsigned-xpi@tests.mozilla.org'], STATE_ENABLED],
|
||||
[Addons.setState, ['unsigned-xpi@tests.mozilla.org'], STATE_DISABLED],
|
||||
[Sync],
|
||||
]);
|
||||
|
||||
Phase('phase3', [
|
||||
[Sync],
|
||||
[Addons.verify, ['unsigned-xpi@tests.mozilla.org'], STATE_DISABLED],
|
||||
[Addons.setState, ['unsigned-xpi@tests.mozilla.org'], STATE_ENABLED],
|
||||
[Sync],
|
||||
]);
|
||||
|
||||
Phase('phase4', [
|
||||
[Sync],
|
||||
[Addons.verify, ['unsigned-xpi@tests.mozilla.org'], STATE_ENABLED],
|
||||
[Addons.uninstall, ['unsigned-xpi@tests.mozilla.org']],
|
||||
[Sync],
|
||||
]);
|
||||
|
||||
Phase('phase5', [
|
||||
[Sync],
|
||||
[Addons.verifyNot, ['unsigned-xpi@tests.mozilla.org']],
|
||||
Phase("phase2", [
|
||||
// Add-on should be present after restart
|
||||
[Addons.verify, [id], STATE_ENABLED]
|
||||
]);
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<searchresults total_results="1">
|
||||
<addon id="5612">
|
||||
<name>Unsigned Test XPI</name>
|
||||
<type id="1">Extension</type>
|
||||
<guid>unsigned-xpi@tests.mozilla.org</guid>
|
||||
<slug>unsigned-xpi</slug>
|
||||
<version>1.0</version>
|
||||
|
||||
<compatible_applications><application>
|
||||
<name>Firefox</name>
|
||||
<application_id>1</application_id>
|
||||
<min_version>3.6</min_version>
|
||||
<max_version>*</max_version>
|
||||
<appID>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</appID>
|
||||
</application></compatible_applications>
|
||||
<all_compatible_os><os>ALL</os></all_compatible_os>
|
||||
|
||||
<install os="ALL" size="452">http://127.0.0.1:4567/unsigned-1.0.xpi</install>
|
||||
<created epoch="1252903662">
|
||||
2009-09-14T04:47:42Z
|
||||
</created>
|
||||
<last_updated epoch="1315255329">
|
||||
2011-09-05T20:42:09Z
|
||||
</last_updated>
|
||||
</addon>
|
||||
</searchresults>
|
|
@ -33,21 +33,19 @@
|
|||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["Addon", "STATE_ENABLED", "STATE_DISABLED"];
|
||||
let EXPORTED_SYMBOLS = ["Addon", "STATE_ENABLED", "STATE_DISABLED"];
|
||||
|
||||
const CC = Components.classes;
|
||||
const CI = Components.interfaces;
|
||||
const CU = Components.utils;
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
CU.import("resource://gre/modules/AddonManager.jsm");
|
||||
CU.import("resource://gre/modules/AddonRepository.jsm");
|
||||
CU.import("resource://gre/modules/Services.jsm");
|
||||
CU.import("resource://services-sync/async.js");
|
||||
CU.import("resource://services-sync/util.js");
|
||||
CU.import("resource://tps/logger.jsm");
|
||||
var XPIProvider = CU.import("resource://gre/modules/XPIProvider.jsm")
|
||||
.XPIProvider;
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
Cu.import("resource://gre/modules/AddonRepository.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://services-sync/async.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://tps/logger.jsm");
|
||||
|
||||
const ADDONSGETURL = 'http://127.0.0.1:4567/';
|
||||
const STATE_ENABLED = 1;
|
||||
|
@ -57,14 +55,14 @@ function GetFileAsText(file)
|
|||
{
|
||||
let channel = Services.io.newChannel(file, null, null);
|
||||
let inputStream = channel.open();
|
||||
if (channel instanceof CI.nsIHttpChannel &&
|
||||
if (channel instanceof Ci.nsIHttpChannel &&
|
||||
channel.responseStatus != 200) {
|
||||
return "";
|
||||
}
|
||||
|
||||
let streamBuf = "";
|
||||
let sis = CC["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(CI.nsIScriptableInputStream);
|
||||
let sis = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Ci.nsIScriptableInputStream);
|
||||
sis.init(inputStream);
|
||||
|
||||
let available;
|
||||
|
@ -82,171 +80,84 @@ function Addon(TPS, id) {
|
|||
}
|
||||
|
||||
Addon.prototype = {
|
||||
_addons_requiring_restart: [],
|
||||
_addons_pending_install: [],
|
||||
addon: null,
|
||||
|
||||
Delete: function() {
|
||||
uninstall: function uninstall() {
|
||||
// find our addon locally
|
||||
let cb = Async.makeSyncCallback();
|
||||
XPIProvider.getAddonsByTypes(null, cb);
|
||||
let results = Async.waitForSyncCallback(cb);
|
||||
var addon;
|
||||
var id = this.id;
|
||||
results.forEach(function(result) {
|
||||
if (result.id == id) {
|
||||
addon = result;
|
||||
}
|
||||
});
|
||||
AddonManager.getAddonByID(this.id, cb);
|
||||
let addon = Async.waitForSyncCallback(cb);
|
||||
|
||||
Logger.AssertTrue(!!addon, 'could not find addon ' + this.id + ' to uninstall');
|
||||
addon.uninstall();
|
||||
|
||||
cb = Async.makeSpinningCallback();
|
||||
let store = Engines.get("addons")._store;
|
||||
store.uninstallAddon(addon, cb);
|
||||
cb.wait();
|
||||
},
|
||||
|
||||
Find: function(state) {
|
||||
find: function find(state) {
|
||||
let cb = Async.makeSyncCallback();
|
||||
let addon_found = false;
|
||||
var that = this;
|
||||
AddonManager.getAddonByID(this.id, cb);
|
||||
let addon = Async.waitForSyncCallback(cb);
|
||||
|
||||
var log_addon = function(addon) {
|
||||
that.addon = addon;
|
||||
Logger.logInfo('addon ' + addon.id + ' found, isActive: ' + addon.isActive);
|
||||
if (state == STATE_ENABLED || state == STATE_DISABLED) {
|
||||
Logger.AssertEqual(addon.isActive,
|
||||
state == STATE_ENABLED ? true : false,
|
||||
"addon " + that.id + " has an incorrect enabled state");
|
||||
}
|
||||
};
|
||||
|
||||
// first look in the list of all addons
|
||||
XPIProvider.getAddonsByTypes(null, cb);
|
||||
let addonlist = Async.waitForSyncCallback(cb);
|
||||
addonlist.forEach(function(addon) {
|
||||
if (addon.id == that.id) {
|
||||
addon_found = true;
|
||||
log_addon.call(that, addon);
|
||||
}
|
||||
});
|
||||
|
||||
if (!addon_found) {
|
||||
// then look in the list of recent installs
|
||||
cb = Async.makeSyncCallback();
|
||||
XPIProvider.getInstallsByTypes(null, cb);
|
||||
addonlist = Async.waitForSyncCallback(cb);
|
||||
for (var i in addonlist) {
|
||||
if (addonlist[i].addon && addonlist[i].addon.id == that.id &&
|
||||
addonlist[i].state == AddonManager.STATE_INSTALLED) {
|
||||
addon_found = true;
|
||||
log_addon.call(that, addonlist[i].addon);
|
||||
}
|
||||
}
|
||||
if (!addon) {
|
||||
Logger.logInfo("Could not find add-on with ID: " + this.id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return addon_found;
|
||||
this.addon = addon;
|
||||
|
||||
Logger.logInfo("add-on found: " + addon.id + ", enabled: " +
|
||||
!addon.userDisabled);
|
||||
if (state == STATE_ENABLED) {
|
||||
Logger.AssertFalse(addon.userDisabled, "add-on is disabled: " + addon.id);
|
||||
return true;
|
||||
} else if (state == STATE_DISABLED) {
|
||||
Logger.AssertTrue(addon.userDisabled, "add-on is enabled: " + addon.id);
|
||||
return true;
|
||||
} else if (state) {
|
||||
throw Error("Don't know how to handle state: " + state);
|
||||
} else {
|
||||
// No state, so just checking that it exists.
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
Install: function() {
|
||||
install: function install() {
|
||||
// For Install, the id parameter initially passed is really the filename
|
||||
// for the addon's install .xml; we'll read the actual id from the .xml.
|
||||
let url = this.id;
|
||||
|
||||
// set the url used by getAddonsByIDs
|
||||
var prefs = CC["@mozilla.org/preferences-service;1"]
|
||||
.getService(CI.nsIPrefBranch);
|
||||
prefs.setCharPref('extensions.getAddons.get.url', ADDONSGETURL + url);
|
||||
let cb = Async.makeSpinningCallback();
|
||||
// We call the store's APIs for installation because it is simpler. If that
|
||||
// API is broken, it should ideally be caught by an xpcshell test. But, if
|
||||
// TPS tests fail, it's all the same: a genuite reported error.
|
||||
let store = Engines.get("addons")._store;
|
||||
store.installAddonsFromIDs([this.id], cb);
|
||||
let result = cb.wait();
|
||||
|
||||
// read the XML and find the addon id
|
||||
xml = GetFileAsText(ADDONSGETURL + url);
|
||||
Logger.AssertTrue(xml.indexOf("<guid>") > -1, 'guid not found in ' + url);
|
||||
this.id = xml.substring(xml.indexOf("<guid>") + 6, xml.indexOf("</guid"));
|
||||
Logger.logInfo('addon XML = ' + this.id);
|
||||
Logger.AssertEqual(1, result.installedIDs.length, "Exactly 1 add-on was installed.");
|
||||
Logger.AssertEqual(this.id, result.installedIDs[0],
|
||||
"Add-on was installed successfully: " + this.id);
|
||||
},
|
||||
|
||||
// find our addon on 'AMO'
|
||||
let cb = Async.makeSyncCallback();
|
||||
AddonRepository.getAddonsByIDs([this.id], {
|
||||
searchSucceeded: cb,
|
||||
searchFailed: cb
|
||||
}, false);
|
||||
setEnabled: function setEnabled(flag) {
|
||||
Logger.AssertTrue(this.find(), "Add-on is available.");
|
||||
|
||||
// Result will be array of addons on searchSucceeded or undefined on
|
||||
// searchFailed.
|
||||
let install_addons = Async.waitForSyncCallback(cb);
|
||||
|
||||
Logger.AssertTrue(install_addons,
|
||||
"no addons found for id " + this.id);
|
||||
Logger.AssertEqual(install_addons.length,
|
||||
1,
|
||||
"multiple addons found for id " + this.id);
|
||||
|
||||
let addon = install_addons[0];
|
||||
Logger.logInfo(JSON.stringify(addon), null, ' ');
|
||||
if (XPIProvider.installRequiresRestart(addon)) {
|
||||
this._addons_requiring_restart.push(addon.id);
|
||||
let userDisabled;
|
||||
if (flag == STATE_ENABLED) {
|
||||
userDisabled = false;
|
||||
} else if (flag == STATE_DISABLED) {
|
||||
userDisabled = true;
|
||||
} else {
|
||||
throw new Error("Unknown flag to setEnabled: " + flag);
|
||||
}
|
||||
|
||||
// Start installing the addon asynchronously; finish up in
|
||||
// onInstallEnded(), onInstallFailed(), or onDownloadFailed().
|
||||
this._addons_pending_install.push(addon.id);
|
||||
this.TPS.StartAsyncOperation();
|
||||
|
||||
Utils.nextTick(function() {
|
||||
let callback = function(aInstall) {
|
||||
addon.install = aInstall;
|
||||
Logger.logInfo("addon install: " + addon.install);
|
||||
Logger.AssertTrue(addon.install,
|
||||
"could not get install object for id " + this.id);
|
||||
addon.install.addListener(this);
|
||||
addon.install.install();
|
||||
};
|
||||
|
||||
AddonManager.getInstallForURL(addon.sourceURI.spec,
|
||||
callback.bind(this),
|
||||
"application/x-xpinstall");
|
||||
}, this);
|
||||
},
|
||||
|
||||
SetState: function(state) {
|
||||
if (!this.Find())
|
||||
return false;
|
||||
this.addon.userDisabled = state == STATE_ENABLED ? false : true;
|
||||
return true;
|
||||
},
|
||||
|
||||
// addon installation callbacks
|
||||
onInstallEnded: function(addon) {
|
||||
try {
|
||||
Logger.logInfo('--------- event observed: addon onInstallEnded');
|
||||
Logger.AssertTrue(addon.addon,
|
||||
"No addon object in addon instance passed to onInstallEnded");
|
||||
Logger.AssertTrue(this._addons_pending_install.indexOf(addon.addon.id) > -1,
|
||||
"onInstallEnded received for unexpected addon " + addon.addon.id);
|
||||
this._addons_pending_install.splice(
|
||||
this._addons_pending_install.indexOf(addon.addon.id),
|
||||
1);
|
||||
}
|
||||
catch(e) {
|
||||
// We can't throw during a callback, as it will just get eaten by
|
||||
// the callback's caller.
|
||||
Utils.nextTick(function() {
|
||||
this.DumpError(e);
|
||||
}, this);
|
||||
return;
|
||||
}
|
||||
this.TPS.FinishAsyncOperation();
|
||||
},
|
||||
|
||||
onInstallFailed: function(addon) {
|
||||
Logger.logInfo('--------- event observed: addon onInstallFailed');
|
||||
Utils.nextTick(function() {
|
||||
this.DumpError('Installation failed for addon ' +
|
||||
(addon.addon && addon.addon.id ? addon.addon.id : 'unknown'));
|
||||
}, this);
|
||||
},
|
||||
|
||||
onDownloadFailed: function(addon) {
|
||||
Logger.logInfo('--------- event observed: addon onDownloadFailed');
|
||||
Utils.nextTick(function() {
|
||||
this.DumpError('Download failed for addon ' +
|
||||
(addon.addon && addon.addon.id ? addon.addon.id : 'unknown'));
|
||||
}, this);
|
||||
},
|
||||
let store = Engines.get("addons")._store;
|
||||
let cb = Async.makeSpinningCallback();
|
||||
store.updateUserDisabled(this.addon, userDisabled, cb);
|
||||
cb.wait();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -341,26 +341,28 @@ let TPS =
|
|||
},
|
||||
|
||||
HandleAddons: function (addons, action, state) {
|
||||
for (var i in addons) {
|
||||
for each (let entry in addons) {
|
||||
Logger.logInfo("executing action " + action.toUpperCase() +
|
||||
" on addon " + JSON.stringify(addons[i]));
|
||||
var addon = new Addon(this, addons[i]);
|
||||
" on addon " + JSON.stringify(entry));
|
||||
let addon = new Addon(this, entry);
|
||||
switch(action) {
|
||||
case ACTION_ADD:
|
||||
addon.Install();
|
||||
addon.install();
|
||||
break;
|
||||
case ACTION_DELETE:
|
||||
addon.Delete();
|
||||
addon.uninstall();
|
||||
break;
|
||||
case ACTION_VERIFY:
|
||||
Logger.AssertTrue(addon.Find(state), 'addon ' + addon.id + ' not found');
|
||||
Logger.AssertTrue(addon.find(state), 'addon ' + addon.id + ' not found');
|
||||
break;
|
||||
case ACTION_VERIFY_NOT:
|
||||
Logger.AssertTrue(!addon.Find(state), 'addon ' + addon.id + " is present, but it shouldn't be");
|
||||
Logger.AssertFalse(addon.find(state), 'addon ' + addon.id + " is present, but it shouldn't be");
|
||||
break;
|
||||
case ACTION_SETSTATE:
|
||||
Logger.AssertTrue(addon.SetState(state), 'addon ' + addon.id + ' not found');
|
||||
case ACTION_SET_ENABLED:
|
||||
Logger.AssertTrue(addon.setEnabled(state), 'addon ' + addon.id + ' not found');
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown action for add-on: " + action);
|
||||
}
|
||||
}
|
||||
Logger.logPass("executing action " + action.toUpperCase() +
|
||||
|
|
|
@ -96,9 +96,11 @@ class TPSTestRunner(object):
|
|||
'browser.tabs.warnOnClose' : False,
|
||||
'browser.warnOnQuit': False,
|
||||
'browser.sessionstore.resume_from_crash': False,
|
||||
'services.sync.addons.ignoreRepositoryChecking': True,
|
||||
'services.sync.firstSync': 'notReady',
|
||||
'services.sync.lastversion': '1.0',
|
||||
'services.sync.log.rootLogger': 'Trace',
|
||||
'services.sync.log.logger.engine.addons': 'Trace',
|
||||
'services.sync.log.logger.service.main': 'Trace',
|
||||
'services.sync.log.logger.engine.bookmarks': 'Trace',
|
||||
'services.sync.log.appender.console': 'Trace',
|
||||
|
|
Загрузка…
Ссылка в новой задаче