зеркало из https://github.com/mozilla/gecko-dev.git
Bug 986040 - Telemetry experiments: Assure that no experiment addon is running yet before starting an experiment. r=Unfocused
--HG-- extra : rebase_source : 55be48f0b9cffe978c22baa8260b161f2cbc0562
This commit is contained in:
Родитель
b4ee0d9c48
Коммит
cd23969961
|
@ -168,6 +168,51 @@ function telemetryEnabled() {
|
|||
gPrefsTelemetry.get(PREF_TELEMETRY_PRERELEASE, false);
|
||||
}
|
||||
|
||||
// Returns a promise that is resolved with the AddonInstall for that URL.
|
||||
function addonInstallForURL(url, hash) {
|
||||
let deferred = Promise.defer();
|
||||
AddonManager.getInstallForURL(url, install => deferred.resolve(install),
|
||||
"application/x-xpinstall", hash);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Returns a promise that is resolved with an Array<Addon> of the installed
|
||||
// experiment addons.
|
||||
function installedExperimentAddons() {
|
||||
let deferred = Promise.defer();
|
||||
AddonManager.getAddonsByTypes(["experiment"],
|
||||
addons => deferred.resolve(addons));
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Takes an Array<Addon> and returns a promise that is resolved when the
|
||||
// addons are uninstalled.
|
||||
function uninstallAddons(addons) {
|
||||
let ids = new Set([a.id for (a of addons)]);
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let listener = {};
|
||||
listener.onUninstalled = addon => {
|
||||
if (!ids.has(addon.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ids.delete(addon.id);
|
||||
if (ids.size == 0) {
|
||||
AddonManager.removeAddonListener(listener);
|
||||
deferred.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
AddonManager.addAddonListener(listener);
|
||||
|
||||
for (let addon of addons) {
|
||||
addon.uninstall();
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* The experiments module.
|
||||
*/
|
||||
|
@ -1300,68 +1345,91 @@ Experiments.ExperimentEntry.prototype = {
|
|||
*/
|
||||
start: function () {
|
||||
gLogger.trace("ExperimentEntry::start() for " + this.id);
|
||||
|
||||
return Task.spawn(function* ExperimentEntry_start_task() {
|
||||
let addons = yield installedExperimentAddons();
|
||||
if (addons.length > 0) {
|
||||
gLogger.error("ExperimentEntry::start() - there are already "
|
||||
+ addons.length + " experiment addons installed");
|
||||
yield uninstallAddons(addons);
|
||||
}
|
||||
|
||||
yield this._installAddon();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// Async install of the addon for this experiment, part of the start task above.
|
||||
_installAddon: function* () {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let installCallback = install => {
|
||||
let failureHandler = (install, handler) => {
|
||||
let message = "AddonInstall " + handler + " for " + this.id + ", state=" +
|
||||
(install.state || "?") + ", error=" + install.error;
|
||||
gLogger.error("ExperimentEntry::start() - " + message);
|
||||
this._failedStart = true;
|
||||
let install = yield addonInstallForURL(this._manifestData.xpiURL,
|
||||
this._manifestData.xpiHash);
|
||||
let failureHandler = (install, handler) => {
|
||||
let message = "AddonInstall " + handler + " for " + this.id + ", state=" +
|
||||
(install.state || "?") + ", error=" + install.error;
|
||||
gLogger.error("ExperimentEntry::_installAddon() - " + message);
|
||||
this._failedStart = true;
|
||||
|
||||
TelemetryLog.log(TELEMETRY_LOG.ACTIVATION_KEY,
|
||||
[TELEMETRY_LOG.ACTIVATION.INSTALL_FAILURE, this.id]);
|
||||
TelemetryLog.log(TELEMETRY_LOG.ACTIVATION_KEY,
|
||||
[TELEMETRY_LOG.ACTIVATION.INSTALL_FAILURE, this.id]);
|
||||
|
||||
deferred.reject(new Error(message));
|
||||
};
|
||||
|
||||
let listener = {
|
||||
onDownloadEnded: install => {
|
||||
gLogger.trace("ExperimentEntry::start() - onDownloadEnded for " + this.id);
|
||||
},
|
||||
|
||||
onInstallStarted: install => {
|
||||
gLogger.trace("ExperimentEntry::start() - onInstallStarted for " + this.id);
|
||||
if (install.addon.type !== "experiment") {
|
||||
gLogger.error("ExperimentEntry::start() - wrong addon type");
|
||||
failureHandler({state: -1, error: -1}, "onInstallStarted");
|
||||
install.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
let addon = install.addon;
|
||||
this._name = addon.name;
|
||||
this._addonId = addon.id;
|
||||
this._description = addon.description || "";
|
||||
this._homepageURL = addon.homepageURL || "";
|
||||
},
|
||||
|
||||
onInstallEnded: install => {
|
||||
gLogger.trace("ExperimentEntry::start() - install ended for " + this.id);
|
||||
this._lastChangedDate = this._policy.now();
|
||||
this._startDate = this._policy.now();
|
||||
this._enabled = true;
|
||||
|
||||
TelemetryLog.log(TELEMETRY_LOG.ACTIVATION_KEY,
|
||||
[TELEMETRY_LOG.ACTIVATION.ACTIVATED, this.id]);
|
||||
|
||||
deferred.resolve();
|
||||
},
|
||||
};
|
||||
|
||||
["onDownloadCancelled", "onDownloadFailed", "onInstallCancelled", "onInstallFailed"]
|
||||
.forEach(what => {
|
||||
listener[what] = install => failureHandler(install, what)
|
||||
});
|
||||
|
||||
install.addListener(listener);
|
||||
install.install();
|
||||
deferred.reject(new Error(message));
|
||||
};
|
||||
|
||||
AddonManager.getInstallForURL(this._manifestData.xpiURL,
|
||||
installCallback,
|
||||
"application/x-xpinstall",
|
||||
this._manifestData.xpiHash);
|
||||
let listener = {
|
||||
onDownloadEnded: install => {
|
||||
gLogger.trace("ExperimentEntry::_installAddon() - onDownloadEnded for " + this.id);
|
||||
|
||||
if (install.existingAddon) {
|
||||
gLogger.warn("ExperimentEntry::_installAddon() - onDownloadEnded, addon already installed");
|
||||
}
|
||||
|
||||
if (install.addon.type !== "experiment") {
|
||||
gLogger.error("ExperimentEntry::_installAddon() - onDownloadEnded, wrong addon type");
|
||||
install.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
onInstallStarted: install => {
|
||||
gLogger.trace("ExperimentEntry::_installAddon() - onInstallStarted for " + this.id);
|
||||
|
||||
if (install.existingAddon) {
|
||||
gLogger.warn("ExperimentEntry::_installAddon() - onInstallStarted, addon already installed");
|
||||
}
|
||||
|
||||
if (install.addon.type !== "experiment") {
|
||||
gLogger.error("ExperimentEntry::_installAddon() - onInstallStarted, wrong addon type");
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
onInstallEnded: install => {
|
||||
gLogger.trace("ExperimentEntry::_installAddon() - install ended for " + this.id);
|
||||
this._lastChangedDate = this._policy.now();
|
||||
this._startDate = this._policy.now();
|
||||
this._enabled = true;
|
||||
|
||||
TelemetryLog.log(TELEMETRY_LOG.ACTIVATION_KEY,
|
||||
[TELEMETRY_LOG.ACTIVATION.ACTIVATED, this.id]);
|
||||
|
||||
let addon = install.addon;
|
||||
this._name = addon.name;
|
||||
this._addonId = addon.id;
|
||||
this._description = addon.description || "";
|
||||
this._homepageURL = addon.homepageURL || "";
|
||||
|
||||
deferred.resolve();
|
||||
},
|
||||
};
|
||||
|
||||
["onDownloadCancelled", "onDownloadFailed", "onInstallCancelled", "onInstallFailed"]
|
||||
.forEach(what => {
|
||||
listener[what] = install => failureHandler(install, what)
|
||||
});
|
||||
|
||||
install.addListener(listener);
|
||||
install.install();
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
|
@ -1396,25 +1464,9 @@ Experiments.ExperimentEntry.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
let listener = {};
|
||||
let handler = addon => {
|
||||
if (addon.id !== this._addonId) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateDates();
|
||||
this._logTermination(terminationKind, terminationReason);
|
||||
|
||||
AddonManager.removeAddonListener(listener);
|
||||
deferred.resolve();
|
||||
};
|
||||
|
||||
listener.onUninstalled = handler;
|
||||
listener.onDisabled = handler;
|
||||
|
||||
AddonManager.addAddonListener(listener);
|
||||
|
||||
addon.uninstall();
|
||||
updateDates();
|
||||
this._logTermination(terminationKind, terminationReason);
|
||||
deferred.resolve(uninstallAddons([addon]));
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
|
|
|
@ -160,11 +160,11 @@ function uninstallAddon(id) {
|
|||
return deferred.promise;
|
||||
}
|
||||
|
||||
function createAppInfo(options) {
|
||||
function createAppInfo(optionsIn) {
|
||||
const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
|
||||
const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
|
||||
|
||||
let options = options || {};
|
||||
let options = optionsIn || {};
|
||||
let id = options.id || "xpcshell@tests.mozilla.org";
|
||||
let name = options.name || "XPCShell";
|
||||
let version = options.version || "1.0";
|
||||
|
|
|
@ -263,6 +263,79 @@ add_task(function* test_getExperiments() {
|
|||
yield removeCacheFile();
|
||||
});
|
||||
|
||||
// Test that we handle the experiments addon already being
|
||||
// installed properly.
|
||||
// We should just pave over them.
|
||||
|
||||
add_task(function* test_addonAlreadyInstalled() {
|
||||
const OBSERVER_TOPIC = "experiments-changed";
|
||||
let observerFireCount = 0;
|
||||
let expectedObserverFireCount = 0;
|
||||
let observer = () => ++observerFireCount;
|
||||
Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
|
||||
|
||||
// Dates the following tests are based on.
|
||||
|
||||
let baseDate = new Date(2014, 5, 1, 12);
|
||||
let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
|
||||
let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
|
||||
|
||||
// The manifest data we test with.
|
||||
|
||||
gManifestObject = {
|
||||
"version": 1,
|
||||
experiments: [
|
||||
{
|
||||
id: EXPERIMENT1_ID,
|
||||
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
|
||||
xpiHash: EXPERIMENT1_XPI_SHA1,
|
||||
startTime: dateToSeconds(startDate),
|
||||
endTime: dateToSeconds(endDate),
|
||||
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
|
||||
appName: ["XPCShell"],
|
||||
channel: ["nightly"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let experiments = new Experiments.Experiments(gPolicy);
|
||||
|
||||
// Trigger update, clock set to before any activation.
|
||||
|
||||
let now = baseDate;
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, 0,
|
||||
"Experiments observer should not have been called yet.");
|
||||
let list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 0, "Experiment list should be empty.");
|
||||
|
||||
// Install conflicting addon.
|
||||
|
||||
let installed = yield installAddon(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1);
|
||||
Assert.ok(installed, "Addon should have been installed.");
|
||||
|
||||
// Trigger update, clock set for the experiment to start.
|
||||
|
||||
now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
|
||||
Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
|
||||
Assert.equal(list[0].active, true, "Experiment 1 should be active.");
|
||||
|
||||
// Cleanup.
|
||||
|
||||
Services.obs.removeObserver(observer, OBSERVER_TOPIC);
|
||||
yield experiments.uninit();
|
||||
yield removeCacheFile();
|
||||
});
|
||||
|
||||
add_task(function* test_lastActiveToday() {
|
||||
let experiments = new Experiments.Experiments(gPolicy);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче