зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1569330 - Disable telemetry check in Normandy recipe runner r=mythmon,nalexander
Differential Revision: https://phabricator.services.mozilla.com/D39576 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
9f979c6057
Коммит
43acd674ea
|
@ -1237,7 +1237,9 @@ BrowserGlue.prototype = {
|
|||
"resource:///modules/themes/dark/"
|
||||
);
|
||||
|
||||
Normandy.init();
|
||||
if (AppConstants.MOZ_NORMANDY) {
|
||||
Normandy.init();
|
||||
}
|
||||
|
||||
SaveToPocket.init();
|
||||
Services.obs.notifyObservers(null, "browser-ui-startup-complete");
|
||||
|
|
|
@ -7,17 +7,41 @@ add_task(async function test_policy_disable_shield() {
|
|||
const { RecipeRunner } = ChromeUtils.import(
|
||||
"resource://normandy/lib/RecipeRunner.jsm"
|
||||
);
|
||||
const { BaseAction } = ChromeUtils.import(
|
||||
"resource://normandy/actions/BaseAction.jsm"
|
||||
);
|
||||
const { BaseStudyAction } = ChromeUtils.import(
|
||||
"resource://normandy/actions/BaseStudyAction.jsm"
|
||||
);
|
||||
|
||||
const baseAction = new BaseAction();
|
||||
const baseStudyAction = new BaseStudyAction();
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["app.normandy.api_url", "https://localhost/selfsupport-dummy/"],
|
||||
["datareporting.healthreport.uploadEnabled", true],
|
||||
["app.shield.optoutstudies.enabled", true],
|
||||
],
|
||||
});
|
||||
|
||||
ok(RecipeRunner, "RecipeRunner exists");
|
||||
|
||||
RecipeRunner.checkPrefs();
|
||||
is(RecipeRunner.enabled, true, "RecipeRunner is enabled");
|
||||
ok(RecipeRunner.enabled, "RecipeRunner is enabled");
|
||||
|
||||
baseAction._preExecution();
|
||||
is(
|
||||
baseAction.state,
|
||||
BaseAction.STATE_PREPARING,
|
||||
"Base action is not disabled"
|
||||
);
|
||||
|
||||
baseStudyAction._preExecution();
|
||||
is(
|
||||
baseStudyAction.state,
|
||||
BaseAction.STATE_PREPARING,
|
||||
"Base study action is not disabled"
|
||||
);
|
||||
|
||||
await setupPolicyEngineWithJson({
|
||||
policies: {
|
||||
|
@ -26,5 +50,19 @@ add_task(async function test_policy_disable_shield() {
|
|||
});
|
||||
|
||||
RecipeRunner.checkPrefs();
|
||||
is(RecipeRunner.enabled, false, "RecipeRunner is disabled");
|
||||
ok(RecipeRunner.enabled, "RecipeRunner is still enabled");
|
||||
|
||||
baseAction._preExecution();
|
||||
is(
|
||||
baseAction.state,
|
||||
BaseAction.STATE_PREPARING,
|
||||
"Base action is not disabled"
|
||||
);
|
||||
|
||||
baseStudyAction._preExecution();
|
||||
is(
|
||||
baseStudyAction.state,
|
||||
BaseAction.STATE_DISABLED,
|
||||
"Base study action is disabled"
|
||||
);
|
||||
});
|
||||
|
|
|
@ -828,17 +828,6 @@
|
|||
class="learnMore" is="text-link"
|
||||
data-l10n-id="collection-health-report-link"/>
|
||||
<vbox class="indent">
|
||||
<hbox align="center">
|
||||
<checkbox id="optOutStudiesEnabled"
|
||||
class="tail-with-learn-more"
|
||||
data-l10n-id="collection-studies"/>
|
||||
<label id="viewShieldStudies"
|
||||
href="about:studies"
|
||||
useoriginprincipal="true"
|
||||
class="learnMore" is="text-link"
|
||||
data-l10n-id="collection-studies-link"/>
|
||||
</hbox>
|
||||
|
||||
<hbox align="center">
|
||||
<checkbox id="addonRecommendationEnabled"
|
||||
class="tail-with-learn-more"
|
||||
|
@ -855,6 +844,19 @@
|
|||
data-l10n-id="collection-health-report-disabled"/>
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_NORMANDY
|
||||
<hbox align="center">
|
||||
<checkbox id="optOutStudiesEnabled"
|
||||
class="tail-with-learn-more"
|
||||
data-l10n-id="collection-studies"/>
|
||||
<label id="viewShieldStudies"
|
||||
href="about:studies"
|
||||
useoriginprincipal="true"
|
||||
class="learnMore" is="text-link"
|
||||
data-l10n-id="collection-studies-link"/>
|
||||
</hbox>
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
<hbox align="center">
|
||||
<checkbox id="automaticallySubmitCrashesBox"
|
||||
|
|
|
@ -604,7 +604,9 @@ var gPrivacyPane = {
|
|||
"command",
|
||||
gPrivacyPane.updateSubmitHealthReport
|
||||
);
|
||||
this.initOptOutStudyCheckbox();
|
||||
if (AppConstants.MOZ_NORMANDY) {
|
||||
this.initOptOutStudyCheckbox();
|
||||
}
|
||||
this.initAddonRecommendationsCheckbox();
|
||||
}
|
||||
this._initA11yState();
|
||||
|
@ -2140,14 +2142,11 @@ var gPrivacyPane = {
|
|||
* handles events coming from the UI for it.
|
||||
*/
|
||||
initOptOutStudyCheckbox(doc) {
|
||||
const allowedByPolicy = Services.policies.isAllowed("Shield");
|
||||
|
||||
// The checkbox should be disabled if any of the below are true. This
|
||||
// prevents the user from changing the value in the box.
|
||||
//
|
||||
// * the policy forbids shield
|
||||
// * the Shield Study preference is locked
|
||||
// * the FHR pref is false
|
||||
// * Normandy is disabled
|
||||
//
|
||||
// The checkbox should match the value of the preference only if all of
|
||||
// these are true. Otherwise, the checkbox should remain unchecked. This
|
||||
|
@ -2155,16 +2154,27 @@ var gPrivacyPane = {
|
|||
// so showing a checkbox would be confusing.
|
||||
//
|
||||
// * the policy allows Shield
|
||||
// * the FHR pref is true
|
||||
// * Normandy is enabled
|
||||
dataCollectionCheckboxHandler({
|
||||
checkbox: document.getElementById("optOutStudiesEnabled"),
|
||||
matchPref: () =>
|
||||
allowedByPolicy &&
|
||||
Services.prefs.getBoolPref(PREF_NORMANDY_ENABLED, false),
|
||||
isDisabled: () => !allowedByPolicy,
|
||||
pref: PREF_OPT_OUT_STUDIES_ENABLED,
|
||||
});
|
||||
|
||||
const allowedByPolicy = Services.policies.isAllowed("Shield");
|
||||
const checkbox = document.getElementById("optOutStudiesEnabled");
|
||||
|
||||
if (
|
||||
allowedByPolicy &&
|
||||
Services.prefs.getBoolPref(PREF_NORMANDY_ENABLED, false)
|
||||
) {
|
||||
if (Services.prefs.getBoolPref(PREF_OPT_OUT_STUDIES_ENABLED, false)) {
|
||||
checkbox.setAttribute("checked", "true");
|
||||
} else {
|
||||
checkbox.removeAttribute("checked");
|
||||
}
|
||||
checkbox.setAttribute("preference", PREF_OPT_OUT_STUDIES_ENABLED);
|
||||
checkbox.removeAttribute("disabled");
|
||||
} else {
|
||||
checkbox.removeAttribute("preference");
|
||||
checkbox.removeAttribute("checked");
|
||||
checkbox.setAttribute("disabled", "true");
|
||||
}
|
||||
},
|
||||
|
||||
initAddonRecommendationsCheckbox() {
|
||||
|
|
|
@ -9,6 +9,7 @@ imply_option('MOZ_SERVICES_HEALTHREPORT', True)
|
|||
imply_option('MOZ_SERVICES_SYNC', True)
|
||||
imply_option('MOZ_DEDICATED_PROFILES', True)
|
||||
imply_option('MOZ_BLOCK_PROFILE_DOWNGRADE', True)
|
||||
imply_option('MOZ_NORMANDY', True)
|
||||
|
||||
with only_when(target_is_linux & compile_environment):
|
||||
option(env='MOZ_NO_PIE_COMPAT',
|
||||
|
|
|
@ -121,7 +121,7 @@ set_define('FENNEC_NIGHTLY', depends_if('FENNEC_NIGHTLY')(lambda _: True))
|
|||
def fennec_nightly(nightly):
|
||||
return bool(nightly)
|
||||
|
||||
|
||||
imply_option('MOZ_NORMANDY', False)
|
||||
imply_option('MOZ_SERVICES_HEALTHREPORT', True)
|
||||
imply_option('MOZ_ANDROID_HISTORY', True)
|
||||
imply_option('--enable-small-chunk-size', True)
|
||||
|
|
|
@ -3085,7 +3085,7 @@ AC_SUBST(MOZ_INCLUDE_SOURCE_INFO)
|
|||
dnl If we have any service that uploads data (and requires data submission
|
||||
dnl policy alert), set MOZ_DATA_REPORTING.
|
||||
dnl We need SUBST for build system and DEFINE for xul preprocessor.
|
||||
if test -n "$MOZ_TELEMETRY_REPORTING" || test -n "$MOZ_SERVICES_HEALTHREPORT" || test -n "$MOZ_CRASHREPORTER"; then
|
||||
if test -n "$MOZ_TELEMETRY_REPORTING" || test -n "$MOZ_SERVICES_HEALTHREPORT" || test -n "$MOZ_CRASHREPORTER" || test -n "$MOZ_NORMANDY"; then
|
||||
MOZ_DATA_REPORTING=1
|
||||
AC_DEFINE(MOZ_DATA_REPORTING)
|
||||
AC_SUBST(MOZ_DATA_REPORTING)
|
||||
|
|
|
@ -78,6 +78,7 @@ def build_dict(config, env=os.environ):
|
|||
d['devedition'] = substs.get('MOZ_DEV_EDITION') == '1'
|
||||
d['pgo'] = substs.get('MOZ_PGO') == '1'
|
||||
d['crashreporter'] = bool(substs.get('MOZ_CRASHREPORTER'))
|
||||
d['normandy'] = substs.get('MOZ_NORMANDY') == '1'
|
||||
d['datareporting'] = bool(substs.get('MOZ_DATA_REPORTING'))
|
||||
d['healthreport'] = substs.get('MOZ_SERVICES_HEALTHREPORT') == '1'
|
||||
d['sync'] = substs.get('MOZ_SERVICES_SYNC') == '1'
|
||||
|
|
|
@ -14,6 +14,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
AddonStudies: "resource://normandy/lib/AddonStudies.jsm",
|
||||
CleanupManager: "resource://normandy/lib/CleanupManager.jsm",
|
||||
LogManager: "resource://normandy/lib/LogManager.jsm",
|
||||
NormandyMigrations: "resource://normandy/NormandyMigrations.jsm",
|
||||
PreferenceExperiments: "resource://normandy/lib/PreferenceExperiments.jsm",
|
||||
PreferenceRollouts: "resource://normandy/lib/PreferenceRollouts.jsm",
|
||||
RecipeRunner: "resource://normandy/lib/RecipeRunner.jsm",
|
||||
|
@ -28,7 +29,6 @@ const BOOTSTRAP_LOGGER_NAME = "app.normandy.bootstrap";
|
|||
const SHIELD_INIT_NOTIFICATION = "shield-init-complete";
|
||||
|
||||
const PREF_PREFIX = "app.normandy";
|
||||
const LEGACY_PREF_PREFIX = "extensions.shield-recipe-client";
|
||||
const STARTUP_EXPERIMENT_PREFS_BRANCH = `${PREF_PREFIX}.startupExperimentPrefs.`;
|
||||
const STARTUP_ROLLOUT_PREFS_BRANCH = `${PREF_PREFIX}.startupRolloutPrefs.`;
|
||||
const PREF_LOGGING_LEVEL = `${PREF_PREFIX}.logging.level`;
|
||||
|
@ -44,7 +44,7 @@ var Normandy = {
|
|||
|
||||
init() {
|
||||
// Initialization that needs to happen before the first paint on startup.
|
||||
this.migrateShieldPrefs();
|
||||
NormandyMigrations.applyAll();
|
||||
this.rolloutPrefsChanged = this.applyStartupPrefs(
|
||||
STARTUP_ROLLOUT_PREFS_BRANCH
|
||||
);
|
||||
|
@ -129,57 +129,6 @@ var Normandy = {
|
|||
}
|
||||
},
|
||||
|
||||
migrateShieldPrefs() {
|
||||
const legacyBranch = Services.prefs.getBranch(LEGACY_PREF_PREFIX + ".");
|
||||
const newBranch = Services.prefs.getBranch(PREF_PREFIX + ".");
|
||||
|
||||
for (const prefName of legacyBranch.getChildList("")) {
|
||||
const legacyPrefType = legacyBranch.getPrefType(prefName);
|
||||
const newPrefType = newBranch.getPrefType(prefName);
|
||||
|
||||
// If new preference exists and is not the same as the legacy pref, skip it
|
||||
if (
|
||||
newPrefType !== Services.prefs.PREF_INVALID &&
|
||||
newPrefType !== legacyPrefType
|
||||
) {
|
||||
log.error(
|
||||
`Error migrating normandy pref ${prefName}; pref type does not match.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now move the value over. If it matches the default, this will be a no-op
|
||||
switch (legacyPrefType) {
|
||||
case Services.prefs.PREF_STRING:
|
||||
newBranch.setCharPref(prefName, legacyBranch.getCharPref(prefName));
|
||||
break;
|
||||
|
||||
case Services.prefs.PREF_INT:
|
||||
newBranch.setIntPref(prefName, legacyBranch.getIntPref(prefName));
|
||||
break;
|
||||
|
||||
case Services.prefs.PREF_BOOL:
|
||||
newBranch.setBoolPref(prefName, legacyBranch.getBoolPref(prefName));
|
||||
break;
|
||||
|
||||
case Services.prefs.PREF_INVALID:
|
||||
// This should never happen.
|
||||
log.error(
|
||||
`Error migrating pref ${prefName}; pref type is invalid (${legacyPrefType}).`
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
// This should never happen either.
|
||||
log.error(
|
||||
`Error getting startup pref ${prefName}; unknown value type ${legacyPrefType}.`
|
||||
);
|
||||
}
|
||||
|
||||
legacyBranch.clearUserPref(prefName);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy a preference subtree from one branch to another, being careful about
|
||||
* types, and return the values the target branch originally had. Prefs will
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var EXPORTED_SYMBOLS = ["NormandyMigrations"];
|
||||
|
||||
const BOOTSTRAP_LOGGER_NAME = "app.normandy.bootstrap";
|
||||
|
||||
const PREF_PREFIX = "app.normandy";
|
||||
const LEGACY_PREF_PREFIX = "extensions.shield-recipe-client";
|
||||
const PREF_LOGGING_LEVEL = `${PREF_PREFIX}.logging.level`;
|
||||
const PREF_MIGRATIONS_APPLIED = `${PREF_PREFIX}.migrationsApplied`;
|
||||
const PREF_OPTOUTSTUDIES_ENABLED = "app.shield.optoutstudies.enabled";
|
||||
|
||||
// Logging
|
||||
const log = Log.repository.getLogger(BOOTSTRAP_LOGGER_NAME);
|
||||
log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
|
||||
log.level = Services.prefs.getIntPref(PREF_LOGGING_LEVEL, Log.Level.Warn);
|
||||
|
||||
const NormandyMigrations = {
|
||||
applyAll() {
|
||||
let migrationsApplied = Services.prefs.getIntPref(
|
||||
PREF_MIGRATIONS_APPLIED,
|
||||
0
|
||||
);
|
||||
|
||||
for (let i = migrationsApplied; i < this.migrations.length; i++) {
|
||||
this.applyOne(i);
|
||||
migrationsApplied++;
|
||||
}
|
||||
|
||||
Services.prefs.setIntPref(PREF_MIGRATIONS_APPLIED, migrationsApplied);
|
||||
},
|
||||
|
||||
applyOne(id) {
|
||||
this.migrations[id]();
|
||||
},
|
||||
|
||||
migrations: [
|
||||
// Migration 0
|
||||
migrateShieldPrefs,
|
||||
|
||||
// Migration 1
|
||||
migrateStudiesEnabledWithoutHealthReporting,
|
||||
],
|
||||
};
|
||||
|
||||
function migrateShieldPrefs() {
|
||||
const legacyBranch = Services.prefs.getBranch(LEGACY_PREF_PREFIX + ".");
|
||||
const newBranch = Services.prefs.getBranch(PREF_PREFIX + ".");
|
||||
|
||||
for (const prefName of legacyBranch.getChildList("")) {
|
||||
const legacyPrefType = legacyBranch.getPrefType(prefName);
|
||||
const newPrefType = newBranch.getPrefType(prefName);
|
||||
|
||||
// If new preference exists and is not the same as the legacy pref, skip it
|
||||
if (
|
||||
newPrefType !== Services.prefs.PREF_INVALID &&
|
||||
newPrefType !== legacyPrefType
|
||||
) {
|
||||
log.error(
|
||||
`Error migrating normandy pref ${prefName}; pref type does not match.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now move the value over. If it matches the default, this will be a no-op
|
||||
switch (legacyPrefType) {
|
||||
case Services.prefs.PREF_STRING:
|
||||
newBranch.setCharPref(prefName, legacyBranch.getCharPref(prefName));
|
||||
break;
|
||||
|
||||
case Services.prefs.PREF_INT:
|
||||
newBranch.setIntPref(prefName, legacyBranch.getIntPref(prefName));
|
||||
break;
|
||||
|
||||
case Services.prefs.PREF_BOOL:
|
||||
newBranch.setBoolPref(prefName, legacyBranch.getBoolPref(prefName));
|
||||
break;
|
||||
|
||||
case Services.prefs.PREF_INVALID:
|
||||
// This should never happen.
|
||||
log.error(
|
||||
`Error migrating pref ${prefName}; pref type is invalid (${legacyPrefType}).`
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
// This should never happen either.
|
||||
log.error(
|
||||
`Error getting startup pref ${prefName}; unknown value type ${legacyPrefType}.`
|
||||
);
|
||||
}
|
||||
|
||||
legacyBranch.clearUserPref(prefName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration to handle moving the studies opt-out pref from under the health
|
||||
* report upload pref to an independent pref.
|
||||
*
|
||||
* If the pref was set to true and the health report upload pref was set
|
||||
* to true then the pref should stay true. Otherwise set it to false.
|
||||
*/
|
||||
function migrateStudiesEnabledWithoutHealthReporting() {
|
||||
const optOutStudiesEnabled = Services.prefs.getBoolPref(
|
||||
PREF_OPTOUTSTUDIES_ENABLED,
|
||||
false
|
||||
);
|
||||
const healthReportUploadEnabled = Services.prefs.getBoolPref(
|
||||
"datareporting.healthreport.uploadEnabled",
|
||||
false
|
||||
);
|
||||
Services.prefs.setBoolPref(
|
||||
PREF_OPTOUTSTUDIES_ENABLED,
|
||||
optOutStudiesEnabled && healthReportUploadEnabled
|
||||
);
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { BaseAction } = ChromeUtils.import(
|
||||
"resource://normandy/actions/BaseAction.jsm"
|
||||
);
|
||||
|
||||
var EXPORTED_SYMBOLS = ["BaseStudyAction"];
|
||||
|
||||
const OPT_OUT_STUDIES_ENABLED_PREF = "app.shield.optoutstudies.enabled";
|
||||
|
||||
/**
|
||||
* Base class for local study actions.
|
||||
*
|
||||
* This should be subclassed. Subclasses must implement _run() for
|
||||
* per-recipe behavior, and may implement _finalize for actions to be
|
||||
* taken once after recipes are run.
|
||||
*
|
||||
* For actions that need to be taken once before recipes are run
|
||||
* _preExecution may be overriden but the overridden method must
|
||||
* call the parent method to ensure the appropriate checks occur.
|
||||
*
|
||||
* Other methods should be overridden with care, to maintain the life
|
||||
* cycle events and error reporting implemented by this class.
|
||||
*/
|
||||
class BaseStudyAction extends BaseAction {
|
||||
_preExecution() {
|
||||
if (!Services.policies.isAllowed("Shield")) {
|
||||
this.log.debug("Disabling Shield because it's blocked by policy.");
|
||||
this.disable();
|
||||
}
|
||||
|
||||
if (!Services.prefs.getBoolPref(OPT_OUT_STUDIES_ENABLED_PREF, true)) {
|
||||
this.log.debug(
|
||||
"User has opted-out of opt-out experiments, disabling action."
|
||||
);
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,8 +14,8 @@
|
|||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
const { BaseAction } = ChromeUtils.import(
|
||||
"resource://normandy/actions/BaseAction.jsm"
|
||||
const { BaseStudyAction } = ChromeUtils.import(
|
||||
"resource://normandy/actions/BaseStudyAction.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
|
@ -33,8 +33,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
|
||||
var EXPORTED_SYMBOLS = ["BranchedAddonStudyAction"];
|
||||
|
||||
const OPT_OUT_STUDIES_ENABLED_PREF = "app.shield.optoutstudies.enabled";
|
||||
|
||||
class AddonStudyEnrollError extends Error {
|
||||
/**
|
||||
* @param {string} studyName
|
||||
|
@ -115,7 +113,7 @@ class AddonStudyUpdateError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
class BranchedAddonStudyAction extends BaseAction {
|
||||
class BranchedAddonStudyAction extends BaseStudyAction {
|
||||
get schema() {
|
||||
return ActionSchemas["branched-addon-study"];
|
||||
}
|
||||
|
@ -125,23 +123,6 @@ class BranchedAddonStudyAction extends BaseAction {
|
|||
this.seenRecipeIds = new Set();
|
||||
}
|
||||
|
||||
/**
|
||||
* This hook is executed once before any recipes have been processed, it is
|
||||
* responsible for:
|
||||
*
|
||||
* - Checking if the user has opted out of studies, and if so, it disables the action.
|
||||
* - Setting up tracking of seen recipes, for use in _finalize.
|
||||
*/
|
||||
_preExecution() {
|
||||
// Check opt-out preference
|
||||
if (!Services.prefs.getBoolPref(OPT_OUT_STUDIES_ENABLED_PREF, true)) {
|
||||
this.log.info(
|
||||
"User has opted-out of opt-out experiments, disabling action."
|
||||
);
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This hook is executed once for each recipe that currently applies to this
|
||||
* client. It is responsible for:
|
||||
|
|
|
@ -4,11 +4,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
const { BaseAction } = ChromeUtils.import(
|
||||
"resource://normandy/actions/BaseAction.jsm"
|
||||
const { BaseStudyAction } = ChromeUtils.import(
|
||||
"resource://normandy/actions/BaseStudyAction.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
|
@ -30,13 +27,6 @@ ChromeUtils.defineModuleGetter(
|
|||
"PreferenceExperiments",
|
||||
"resource://normandy/lib/PreferenceExperiments.jsm"
|
||||
);
|
||||
const SHIELD_OPT_OUT_PREF = "app.shield.optoutstudies.enabled";
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
this,
|
||||
"shieldOptOutPref",
|
||||
SHIELD_OPT_OUT_PREF,
|
||||
false
|
||||
);
|
||||
|
||||
var EXPORTED_SYMBOLS = ["PreferenceExperimentAction"];
|
||||
|
||||
|
@ -45,7 +35,7 @@ var EXPORTED_SYMBOLS = ["PreferenceExperimentAction"];
|
|||
* user to an experiment branch and modify a preference temporarily to
|
||||
* measure how it affects Firefox via Telemetry.
|
||||
*/
|
||||
class PreferenceExperimentAction extends BaseAction {
|
||||
class PreferenceExperimentAction extends BaseStudyAction {
|
||||
get schema() {
|
||||
return ActionSchemas["multi-preference-experiment"];
|
||||
}
|
||||
|
@ -55,15 +45,6 @@ class PreferenceExperimentAction extends BaseAction {
|
|||
this.seenExperimentNames = [];
|
||||
}
|
||||
|
||||
_preExecution() {
|
||||
if (!shieldOptOutPref) {
|
||||
this.log.info(
|
||||
"User has opted out of preference experiments. Disabling this action."
|
||||
);
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
||||
async _run(recipe) {
|
||||
const {
|
||||
branches,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
toolkit.jar:
|
||||
% resource normandy %res/normandy/
|
||||
res/normandy/Normandy.jsm (./Normandy.jsm)
|
||||
res/normandy/NormandyMigrations.jsm (./NormandyMigrations.jsm)
|
||||
res/normandy/lib/ (./lib/*)
|
||||
res/normandy/skin/ (./skin/*)
|
||||
res/normandy/actions/ (./actions/*.jsm)
|
||||
|
|
|
@ -39,8 +39,6 @@ const TIMER_NAME = "recipe-client-addon-run";
|
|||
const REMOTE_SETTINGS_COLLECTION = "normandy-recipes";
|
||||
const PREF_CHANGED_TOPIC = "nsPref:changed";
|
||||
|
||||
const TELEMETRY_ENABLED_PREF = "datareporting.healthreport.uploadEnabled";
|
||||
|
||||
const PREF_PREFIX = "app.normandy";
|
||||
const RUN_INTERVAL_PREF = `${PREF_PREFIX}.run_interval_seconds`;
|
||||
const FIRST_RUN_PREF = `${PREF_PREFIX}.first_run`;
|
||||
|
@ -53,12 +51,7 @@ const LAZY_CLASSIFY_PREF = `${PREF_PREFIX}.experiments.lazy_classify`;
|
|||
// see https://searchfox.org/mozilla-central/rev/11cfa0462/toolkit/components/timermanager/UpdateTimerManager.jsm#8
|
||||
const TIMER_LAST_UPDATE_PREF = `app.update.lastUpdateTime.${TIMER_NAME}`;
|
||||
|
||||
const PREFS_TO_WATCH = [
|
||||
RUN_INTERVAL_PREF,
|
||||
TELEMETRY_ENABLED_PREF,
|
||||
SHIELD_ENABLED_PREF,
|
||||
API_URL_PREF,
|
||||
];
|
||||
const PREFS_TO_WATCH = [RUN_INTERVAL_PREF, SHIELD_ENABLED_PREF, API_URL_PREF];
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gRemoteSettingsClient", () => {
|
||||
return RemoteSettings(REMOTE_SETTINGS_COLLECTION, {
|
||||
|
@ -169,7 +162,6 @@ var RecipeRunner = {
|
|||
break;
|
||||
|
||||
// explicit fall-through
|
||||
case TELEMETRY_ENABLED_PREF:
|
||||
case SHIELD_ENABLED_PREF:
|
||||
case API_URL_PREF:
|
||||
this.checkPrefs();
|
||||
|
@ -187,15 +179,6 @@ var RecipeRunner = {
|
|||
},
|
||||
|
||||
checkPrefs() {
|
||||
// Only run if Unified Telemetry is enabled.
|
||||
if (!Services.prefs.getBoolPref(TELEMETRY_ENABLED_PREF)) {
|
||||
log.debug(
|
||||
"Disabling RecipeRunner because Unified Telemetry is disabled."
|
||||
);
|
||||
this.disable();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Services.prefs.getBoolPref(SHIELD_ENABLED_PREF)) {
|
||||
log.debug(
|
||||
`Disabling Shield because ${SHIELD_ENABLED_PREF} is set to false`
|
||||
|
@ -204,12 +187,6 @@ var RecipeRunner = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!Services.policies.isAllowed("Shield")) {
|
||||
log.debug("Disabling Shield because it's blocked by policy.");
|
||||
this.disable();
|
||||
return;
|
||||
}
|
||||
|
||||
const apiUrl = Services.prefs.getCharPref(API_URL_PREF);
|
||||
if (!apiUrl) {
|
||||
log.warn(`Disabling Shield because ${API_URL_PREF} is not set.`);
|
||||
|
|
|
@ -11,8 +11,6 @@ generated-files =
|
|||
addons/normandydriver-a-2.0.xpi
|
||||
head = head.js
|
||||
[browser_about_preferences.js]
|
||||
# Skip this test when FHR/Telemetry aren't available.
|
||||
skip-if = !healthreport || !telemetry
|
||||
[browser_about_studies.js]
|
||||
[browser_actions_AddonStudyAction.js]
|
||||
[browser_actions_BranchedAddonStudyAction.js]
|
||||
|
@ -32,6 +30,7 @@ skip-if = (verify && (os == 'linux'))
|
|||
[browser_Heartbeat.js]
|
||||
[browser_LogManager.js]
|
||||
[browser_Normandy.js]
|
||||
[browser_NormandyMigrations.js]
|
||||
[browser_PreferenceExperiments.js]
|
||||
[browser_PreferenceRollouts.js]
|
||||
[browser_RecipeRunner.js]
|
||||
|
|
|
@ -286,38 +286,3 @@ decorate_task(
|
|||
ok(PreferenceRollouts.init.called, "startup calls PreferenceRollouts.init");
|
||||
}
|
||||
);
|
||||
|
||||
decorate_task(withMockPreferences, async function testPrefMigration(
|
||||
mockPreferences
|
||||
) {
|
||||
const legacyPref = "extensions.shield-recipe-client.test";
|
||||
const migratedPref = "app.normandy.test";
|
||||
mockPreferences.set(legacyPref, 1);
|
||||
|
||||
ok(
|
||||
Services.prefs.prefHasUserValue(legacyPref),
|
||||
"Legacy pref should have a user value before running migration"
|
||||
);
|
||||
ok(
|
||||
!Services.prefs.prefHasUserValue(migratedPref),
|
||||
"Migrated pref should not have a user value before running migration"
|
||||
);
|
||||
|
||||
Normandy.migrateShieldPrefs();
|
||||
|
||||
ok(
|
||||
!Services.prefs.prefHasUserValue(legacyPref),
|
||||
"Legacy pref should not have a user value after running migration"
|
||||
);
|
||||
ok(
|
||||
Services.prefs.prefHasUserValue(migratedPref),
|
||||
"Migrated pref should have a user value after running migration"
|
||||
);
|
||||
is(
|
||||
Services.prefs.getIntPref(migratedPref),
|
||||
1,
|
||||
"Value should have been migrated"
|
||||
);
|
||||
|
||||
Services.prefs.clearUserPref(migratedPref);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
ChromeUtils.import("resource://normandy/NormandyMigrations.jsm", this);
|
||||
|
||||
decorate_task(withMockPreferences, async function testApplyMigrations(
|
||||
mockPreferences
|
||||
) {
|
||||
const migrationsAppliedPref = "app.normandy.migrationsApplied";
|
||||
mockPreferences.set(migrationsAppliedPref, 0);
|
||||
|
||||
NormandyMigrations.applyAll();
|
||||
|
||||
is(
|
||||
Services.prefs.getIntPref(migrationsAppliedPref),
|
||||
NormandyMigrations.migrations.length,
|
||||
"All migrations should have been applied"
|
||||
);
|
||||
});
|
||||
|
||||
decorate_task(withMockPreferences, async function testPrefMigration(
|
||||
mockPreferences
|
||||
) {
|
||||
const legacyPref = "extensions.shield-recipe-client.test";
|
||||
const migratedPref = "app.normandy.test";
|
||||
mockPreferences.set(legacyPref, 1);
|
||||
|
||||
ok(
|
||||
Services.prefs.prefHasUserValue(legacyPref),
|
||||
"Legacy pref should have a user value before running migration"
|
||||
);
|
||||
ok(
|
||||
!Services.prefs.prefHasUserValue(migratedPref),
|
||||
"Migrated pref should not have a user value before running migration"
|
||||
);
|
||||
|
||||
NormandyMigrations.applyOne(0);
|
||||
|
||||
ok(
|
||||
!Services.prefs.prefHasUserValue(legacyPref),
|
||||
"Legacy pref should not have a user value after running migration"
|
||||
);
|
||||
ok(
|
||||
Services.prefs.prefHasUserValue(migratedPref),
|
||||
"Migrated pref should have a user value after running migration"
|
||||
);
|
||||
is(
|
||||
Services.prefs.getIntPref(migratedPref),
|
||||
1,
|
||||
"Value should have been migrated"
|
||||
);
|
||||
|
||||
Services.prefs.clearUserPref(migratedPref);
|
||||
});
|
||||
|
||||
decorate_task(withMockPreferences, async function testMigration0(
|
||||
mockPreferences
|
||||
) {
|
||||
const studiesEnabledPref = "app.shield.optoutstudies.enabled";
|
||||
const healthReportUploadEnabledPref =
|
||||
"datareporting.healthreport.uploadEnabled";
|
||||
|
||||
// Both enabled
|
||||
mockPreferences.set(studiesEnabledPref, true);
|
||||
mockPreferences.set(healthReportUploadEnabledPref, true);
|
||||
NormandyMigrations.applyOne(1);
|
||||
ok(
|
||||
Services.prefs.getBoolPref(studiesEnabledPref),
|
||||
"Studies should be enabled."
|
||||
);
|
||||
|
||||
mockPreferences.cleanup();
|
||||
|
||||
// Telemetry disabled, studies enabled
|
||||
mockPreferences.set(studiesEnabledPref, true);
|
||||
mockPreferences.set(healthReportUploadEnabledPref, false);
|
||||
NormandyMigrations.applyOne(1);
|
||||
ok(
|
||||
!Services.prefs.getBoolPref(studiesEnabledPref),
|
||||
"Studies should be disabled."
|
||||
);
|
||||
|
||||
mockPreferences.cleanup();
|
||||
|
||||
// Telemetry enabled, studies disabled
|
||||
mockPreferences.set(studiesEnabledPref, false);
|
||||
mockPreferences.set(healthReportUploadEnabledPref, true);
|
||||
NormandyMigrations.applyOne(1);
|
||||
ok(
|
||||
!Services.prefs.getBoolPref(studiesEnabledPref),
|
||||
"Studies should be disabled."
|
||||
);
|
||||
|
||||
mockPreferences.cleanup();
|
||||
|
||||
// Both disabled
|
||||
mockPreferences.set(studiesEnabledPref, false);
|
||||
mockPreferences.set(healthReportUploadEnabledPref, false);
|
||||
NormandyMigrations.applyOne(1);
|
||||
ok(
|
||||
!Services.prefs.getBoolPref(studiesEnabledPref),
|
||||
"Studies should be disabled."
|
||||
);
|
||||
});
|
|
@ -590,7 +590,6 @@ decorate_task(
|
|||
decorate_task(
|
||||
withPrefEnv({
|
||||
set: [
|
||||
["datareporting.healthreport.uploadEnabled", true], // telemetry enabled
|
||||
["app.normandy.dev_mode", false],
|
||||
["app.normandy.first_run", false],
|
||||
["app.normandy.enabled", true],
|
||||
|
@ -648,26 +647,6 @@ decorate_task(
|
|||
);
|
||||
is(disableStub.callCount, 2, "Disable should not be called again");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["datareporting.healthreport.uploadEnabled", false]],
|
||||
});
|
||||
is(enableStub.callCount, 3, "Enable should not be called again");
|
||||
is(
|
||||
disableStub.callCount,
|
||||
3,
|
||||
"RecipeRunner should disable when telemetry is disabled"
|
||||
);
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["datareporting.healthreport.uploadEnabled", true]],
|
||||
});
|
||||
is(
|
||||
enableStub.callCount,
|
||||
4,
|
||||
"RecipeRunner should re-enable when telemetry is enabled"
|
||||
);
|
||||
is(disableStub.callCount, 3, "Disable should not be called again");
|
||||
|
||||
is(
|
||||
runStub.callCount,
|
||||
0,
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
ChromeUtils.import("resource://gre/modules/Services.jsm", this);
|
||||
|
||||
const OPT_OUT_PREF = "app.shield.optoutstudies.enabled";
|
||||
const FHR_PREF = "datareporting.healthreport.uploadEnabled";
|
||||
|
||||
function withPrivacyPrefs(testFunc) {
|
||||
return async (...args) =>
|
||||
|
@ -46,48 +45,13 @@ decorate_task(
|
|||
|
||||
decorate_task(
|
||||
withPrefEnv({
|
||||
set: [[FHR_PREF, true]],
|
||||
}),
|
||||
withPrivacyPrefs,
|
||||
async function testEnabledOnLoad(browser) {
|
||||
const checkbox = browser.contentDocument.getElementById(
|
||||
"optOutStudiesEnabled"
|
||||
);
|
||||
ok(
|
||||
!checkbox.disabled,
|
||||
"Opt-out checkbox is enabled on load when the FHR pref is true"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
decorate_task(
|
||||
withPrefEnv({
|
||||
set: [[FHR_PREF, false]],
|
||||
}),
|
||||
withPrivacyPrefs,
|
||||
async function testDisabledOnLoad(browser) {
|
||||
const checkbox = browser.contentDocument.getElementById(
|
||||
"optOutStudiesEnabled"
|
||||
);
|
||||
ok(
|
||||
checkbox.disabled,
|
||||
"Opt-out checkbox is disabled on load when the FHR pref is false"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
decorate_task(
|
||||
withPrefEnv({
|
||||
set: [[FHR_PREF, true], [OPT_OUT_PREF, true]],
|
||||
set: [[OPT_OUT_PREF, true]],
|
||||
}),
|
||||
withPrivacyPrefs,
|
||||
async function testCheckboxes(browser) {
|
||||
const optOutCheckbox = browser.contentDocument.getElementById(
|
||||
"optOutStudiesEnabled"
|
||||
);
|
||||
const fhrCheckbox = browser.contentDocument.getElementById(
|
||||
"submitHealthReportBox"
|
||||
);
|
||||
|
||||
optOutCheckbox.click();
|
||||
ok(
|
||||
|
@ -99,40 +63,12 @@ decorate_task(
|
|||
Services.prefs.getBoolPref(OPT_OUT_PREF),
|
||||
"Checking the opt-out checkbox sets the pref to true."
|
||||
);
|
||||
|
||||
fhrCheckbox.click();
|
||||
ok(
|
||||
!Services.prefs.getBoolPref(OPT_OUT_PREF),
|
||||
"Unchecking the FHR checkbox sets the opt-out pref to false."
|
||||
);
|
||||
ok(
|
||||
optOutCheckbox.disabled,
|
||||
"Unchecking the FHR checkbox disables the opt-out checkbox."
|
||||
);
|
||||
ok(
|
||||
!optOutCheckbox.checked,
|
||||
"Unchecking the FHR checkbox unchecks the opt-out checkbox."
|
||||
);
|
||||
|
||||
fhrCheckbox.click();
|
||||
ok(
|
||||
Services.prefs.getBoolPref(OPT_OUT_PREF),
|
||||
"Checking the FHR checkbox sets the opt-out pref to true."
|
||||
);
|
||||
ok(
|
||||
!optOutCheckbox.disabled,
|
||||
"Checking the FHR checkbox enables the opt-out checkbox."
|
||||
);
|
||||
ok(
|
||||
optOutCheckbox.checked,
|
||||
"Checking the FHR checkbox checks the opt-out checkbox."
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
decorate_task(
|
||||
withPrefEnv({
|
||||
set: [[FHR_PREF, true], [OPT_OUT_PREF, true]],
|
||||
set: [[OPT_OUT_PREF, true]],
|
||||
}),
|
||||
withPrivacyPrefs,
|
||||
async function testPrefWatchers(browser) {
|
||||
|
@ -150,34 +86,6 @@ decorate_task(
|
|||
optOutCheckbox.checked,
|
||||
"Enabling the opt-out pref checks the opt-out checkbox."
|
||||
);
|
||||
|
||||
Services.prefs.setBoolPref(FHR_PREF, false);
|
||||
ok(
|
||||
!Services.prefs.getBoolPref(OPT_OUT_PREF),
|
||||
"Disabling the FHR pref sets the opt-out pref to false."
|
||||
);
|
||||
ok(
|
||||
optOutCheckbox.disabled,
|
||||
"Disabling the FHR pref disables the opt-out checkbox."
|
||||
);
|
||||
ok(
|
||||
!optOutCheckbox.checked,
|
||||
"Disabling the FHR pref unchecks the opt-out checkbox."
|
||||
);
|
||||
|
||||
Services.prefs.setBoolPref(FHR_PREF, true);
|
||||
ok(
|
||||
Services.prefs.getBoolPref(OPT_OUT_PREF),
|
||||
"Enabling the FHR pref sets the opt-out pref to true."
|
||||
);
|
||||
ok(
|
||||
!optOutCheckbox.disabled,
|
||||
"Enabling the FHR pref enables the opt-out checkbox."
|
||||
);
|
||||
ok(
|
||||
optOutCheckbox.checked,
|
||||
"Enabling the FHR pref checks the opt-out checkbox."
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ function addonStudyRecipeFactory(overrides = {}) {
|
|||
// Test that enroll is not called if recipe is already enrolled and update does nothing
|
||||
// if recipe is unchanged
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([addonStudyFactory()]),
|
||||
|
@ -72,6 +73,7 @@ decorate_task(
|
|||
// Test that if the add-on fails to install, the database is cleaned up and the
|
||||
// error is correctly reported.
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -109,6 +111,7 @@ decorate_task(
|
|||
|
||||
// Ensure that the database is clean and error correctly reported if hash check fails
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -149,6 +152,7 @@ decorate_task(
|
|||
|
||||
// Ensure that the database is clean and error correctly reported if there is a metadata mismatch
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -190,6 +194,7 @@ decorate_task(
|
|||
|
||||
// Test that in the case of a study add-on conflicting with a non-study add-on, the study does not enroll
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -244,6 +249,7 @@ decorate_task(
|
|||
|
||||
// Test a successful enrollment
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -333,6 +339,7 @@ decorate_task(
|
|||
|
||||
// Test a successful update
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -412,6 +419,7 @@ decorate_task(
|
|||
|
||||
// Test update fails when addon ID does not match
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -473,6 +481,7 @@ decorate_task(
|
|||
|
||||
// Test update fails when original addon does not exist
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -536,6 +545,7 @@ decorate_task(
|
|||
|
||||
// Test update fails when download fails
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -598,6 +608,7 @@ decorate_task(
|
|||
|
||||
// Test update fails when hash check fails
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -661,6 +672,7 @@ decorate_task(
|
|||
|
||||
// Test update fails on downgrade when study version is greater than extension version
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -723,6 +735,7 @@ decorate_task(
|
|||
|
||||
// Test update fails when there is a version mismatch with metadata
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -799,6 +812,7 @@ decorate_task(
|
|||
|
||||
// Test that unenrolling fails if the study doesn't exist
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
AddonStudies.withStudies(),
|
||||
async function unenrollNonexistent(studies) {
|
||||
|
@ -813,6 +827,7 @@ decorate_task(
|
|||
|
||||
// Test that unenrolling an inactive experiment fails
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
AddonStudies.withStudies([addonStudyFactory({ active: false })]),
|
||||
withSendEventStub,
|
||||
|
@ -829,6 +844,7 @@ decorate_task(
|
|||
// test a successful unenrollment
|
||||
const testStopId = "testStop@example.com";
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
AddonStudies.withStudies([
|
||||
addonStudyFactory({
|
||||
|
@ -870,6 +886,7 @@ decorate_task(
|
|||
|
||||
// If the add-on for a study isn't installed, a warning should be logged, but the action is still successful
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
AddonStudies.withStudies([
|
||||
addonStudyFactory({
|
||||
|
@ -903,6 +920,7 @@ decorate_task(
|
|||
|
||||
// Test that the action respects the study opt-out
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -938,6 +956,7 @@ decorate_task(
|
|||
|
||||
// Test that the action does not enroll paused recipes
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -969,6 +988,7 @@ decorate_task(
|
|||
|
||||
// Test that the action updates paused recipes
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -1034,6 +1054,7 @@ decorate_task(
|
|||
|
||||
// Test that update method works for legacy studies with no hash
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -1117,6 +1138,7 @@ decorate_task(
|
|||
|
||||
// Test that enroll is not called if recipe is already enrolled
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
AddonStudies.withStudies([addonStudyFactory()]),
|
||||
async function enrollTwiceFail([study]) {
|
||||
|
|
|
@ -74,6 +74,7 @@ function recipeFromStudy(study, overrides = {}) {
|
|||
// Test that enroll is not called if recipe is already enrolled and update does nothing
|
||||
// if recipe is unchanged
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([branchedAddonStudyFactory()]),
|
||||
|
@ -107,6 +108,7 @@ decorate_task(
|
|||
// Test that if the add-on fails to install, the database is cleaned up and the
|
||||
// error is correctly reported.
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -147,6 +149,7 @@ decorate_task(
|
|||
|
||||
// Ensure that the database is clean and error correctly reported if hash check fails
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -184,6 +187,7 @@ decorate_task(
|
|||
|
||||
// Ensure that the database is clean and error correctly reported if there is a metadata mismatch
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -221,6 +225,7 @@ decorate_task(
|
|||
|
||||
// Test that in the case of a study add-on conflicting with a non-study add-on, the study does not enroll
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -271,6 +276,7 @@ decorate_task(
|
|||
|
||||
// Test a successful update
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -347,6 +353,7 @@ decorate_task(
|
|||
|
||||
// Test update fails when addon ID does not match
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -400,6 +407,7 @@ decorate_task(
|
|||
|
||||
// Test update fails when original addon does not exist
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -455,6 +463,7 @@ decorate_task(
|
|||
|
||||
// Test update fails when download fails
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -509,6 +518,7 @@ decorate_task(
|
|||
|
||||
// Test update fails when hash check fails
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -564,6 +574,7 @@ decorate_task(
|
|||
|
||||
// Test update fails on downgrade when study version is greater than extension version
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -618,6 +629,7 @@ decorate_task(
|
|||
|
||||
// Test update fails when there is a version mismatch with metadata
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -686,6 +698,7 @@ decorate_task(
|
|||
|
||||
// Test that unenrolling fails if the study doesn't exist
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
AddonStudies.withStudies(),
|
||||
async function unenrollNonexistent(studies) {
|
||||
|
@ -700,6 +713,7 @@ decorate_task(
|
|||
|
||||
// Test that unenrolling an inactive experiment fails
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
AddonStudies.withStudies([branchedAddonStudyFactory({ active: false })]),
|
||||
withSendEventStub,
|
||||
|
@ -716,6 +730,7 @@ decorate_task(
|
|||
// test a successful unenrollment
|
||||
const testStopId = "testStop@example.com";
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
AddonStudies.withStudies([
|
||||
branchedAddonStudyFactory({
|
||||
|
@ -769,6 +784,7 @@ decorate_task(
|
|||
|
||||
// If the add-on for a study isn't installed, a warning should be logged, but the action is still successful
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
AddonStudies.withStudies([
|
||||
branchedAddonStudyFactory({
|
||||
|
@ -801,6 +817,7 @@ decorate_task(
|
|||
|
||||
// Test that the action respects the study opt-out
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -836,6 +853,7 @@ decorate_task(
|
|||
|
||||
// Test that the action does not enroll paused recipes
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -867,6 +885,7 @@ decorate_task(
|
|||
|
||||
// Test that the action updates paused recipes
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([
|
||||
|
@ -924,6 +943,7 @@ decorate_task(
|
|||
|
||||
// Test that unenroll called if the study is no longer sent from the server
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
AddonStudies.withStudies([branchedAddonStudyFactory()]),
|
||||
async function unenroll([study]) {
|
||||
|
@ -942,6 +962,7 @@ decorate_task(
|
|||
// A test function that will be parameterized over the argument "branch" below.
|
||||
// Mocks the branch selector, and then tests that the user correctly gets enrolled in that branch.
|
||||
const successEnrollBranchedTest = decorate(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
@ -1070,6 +1091,7 @@ add_task(() => successEnrollBranchedTest("b"));
|
|||
|
||||
// If the enrolled branch no longer exists, unenroll
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
AddonStudies.withStudies([branchedAddonStudyFactory()]),
|
||||
|
@ -1142,6 +1164,7 @@ decorate_task(
|
|||
|
||||
// Test that branches without an add-on can be enrolled and unenrolled succesfully.
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
ensureAddonCleanup,
|
||||
withMockNormandyApi,
|
||||
withSendEventStub,
|
||||
|
|
|
@ -63,6 +63,7 @@ function preferenceExperimentFactory(args) {
|
|||
}
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(Uptake, "reportRecipe"),
|
||||
async function run_without_errors(reportRecipe) {
|
||||
const action = new PreferenceExperimentAction();
|
||||
|
@ -76,6 +77,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(Uptake, "reportRecipe"),
|
||||
withStub(Uptake, "reportAction"),
|
||||
withPrefEnv({ set: [["app.shield.optoutstudies.enabled", false]] }),
|
||||
|
@ -86,8 +88,9 @@ decorate_task(
|
|||
const recipe = preferenceExperimentFactory();
|
||||
await action.runRecipe(recipe);
|
||||
|
||||
Assert.deepEqual(action.log.info.args, [
|
||||
["User has opted out of preference experiments. Disabling this action."],
|
||||
Assert.ok(action.log.debug.args.length === 1);
|
||||
Assert.deepEqual(action.log.debug.args[0], [
|
||||
"User has opted-out of opt-out experiments, disabling action.",
|
||||
]);
|
||||
Assert.deepEqual(action.log.warn.args, [
|
||||
[
|
||||
|
@ -97,10 +100,9 @@ decorate_task(
|
|||
]);
|
||||
|
||||
await action.finalize();
|
||||
Assert.deepEqual(action.log.debug.args, [
|
||||
[
|
||||
"Skipping post-execution hook for PreferenceExperimentAction because it is disabled.",
|
||||
],
|
||||
Assert.ok(action.log.debug.args.length === 2);
|
||||
Assert.deepEqual(action.log.debug.args[1], [
|
||||
"Skipping post-execution hook for PreferenceExperimentAction because it is disabled.",
|
||||
]);
|
||||
Assert.deepEqual(reportRecipe.args, [
|
||||
[recipe, Uptake.RECIPE_ACTION_DISABLED],
|
||||
|
@ -110,6 +112,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(PreferenceExperiments, "start"),
|
||||
PreferenceExperiments.withMockExperiments([]),
|
||||
async function enroll_user_if_never_been_in_experiment(startStub) {
|
||||
|
@ -172,6 +175,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(PreferenceExperiments, "markLastSeen"),
|
||||
PreferenceExperiments.withMockExperiments([{ name: "test", expired: false }]),
|
||||
async function markSeen_if_experiment_active(markLastSeenStub) {
|
||||
|
@ -188,6 +192,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(PreferenceExperiments, "markLastSeen"),
|
||||
PreferenceExperiments.withMockExperiments([{ name: "test", expired: true }]),
|
||||
async function dont_markSeen_if_experiment_expired(markLastSeenStub) {
|
||||
|
@ -204,6 +209,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(PreferenceExperiments, "start"),
|
||||
async function do_nothing_if_enrollment_paused(startStub) {
|
||||
const action = new PreferenceExperimentAction();
|
||||
|
@ -219,6 +225,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(PreferenceExperiments, "stop"),
|
||||
PreferenceExperiments.withMockExperiments([
|
||||
{ name: "seen", expired: false, actionName: "PreferenceExperimentAction" },
|
||||
|
@ -244,6 +251,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(PreferenceExperiments, "stop"),
|
||||
PreferenceExperiments.withMockExperiments([
|
||||
{
|
||||
|
@ -275,6 +283,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(PreferenceExperiments, "start"),
|
||||
withStub(Uptake, "reportRecipe"),
|
||||
PreferenceExperiments.withMockExperiments([
|
||||
|
@ -317,6 +326,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(PreferenceExperiments, "start"),
|
||||
PreferenceExperiments.withMockExperiments([]),
|
||||
async function experimentType_with_isHighPopulation_false(startStub) {
|
||||
|
@ -333,6 +343,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(PreferenceExperiments, "start"),
|
||||
PreferenceExperiments.withMockExperiments([]),
|
||||
async function experimentType_with_isHighPopulation_true(startStub) {
|
||||
|
@ -349,6 +360,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(Sampling, "ratioSample"),
|
||||
async function chooseBranch_uses_ratioSample(ratioSampleStub) {
|
||||
ratioSampleStub.returns(Promise.resolve(1));
|
||||
|
@ -388,6 +400,7 @@ decorate_task(
|
|||
);
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withMockPreferences,
|
||||
PreferenceExperiments.withMockExperiments([]),
|
||||
async function integration_test_enroll_and_unenroll(prefs) {
|
||||
|
|
|
@ -42,6 +42,7 @@ function preferenceExperimentFactory(args) {
|
|||
}
|
||||
|
||||
decorate_task(
|
||||
withStudiesEnabled,
|
||||
withStub(PreferenceExperimentAction.prototype, "_run"),
|
||||
PreferenceExperiments.withMockExperiments([]),
|
||||
async function enroll_user_if_never_been_in_experiment(runStub) {
|
||||
|
|
|
@ -240,6 +240,19 @@ this.withPrefEnv = function(inPrefs) {
|
|||
};
|
||||
};
|
||||
|
||||
this.withStudiesEnabled = function(testFunc) {
|
||||
return async function inner(...args) {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["app.shield.optoutstudies.enabled", true]],
|
||||
});
|
||||
try {
|
||||
await testFunc(...args);
|
||||
} finally {
|
||||
await SpecialPowers.popPrefEnv();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Combine a list of functions right to left. The rightmost function is passed
|
||||
* to the preceding function as the argument; the result of this is passed to
|
||||
|
|
|
@ -187,6 +187,13 @@ this.AppConstants = Object.freeze({
|
|||
false,
|
||||
#endif
|
||||
|
||||
MOZ_NORMANDY:
|
||||
#ifdef MOZ_NORMANDY
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
|
||||
MOZ_MAINTENANCE_SERVICE:
|
||||
#ifdef MOZ_MAINTENANCE_SERVICE
|
||||
true,
|
||||
|
|
|
@ -617,6 +617,11 @@ project_flag('MOZ_SERVICES_HEALTHREPORT',
|
|||
set_for_old_configure=True,
|
||||
set_as_define=True)
|
||||
|
||||
project_flag('MOZ_NORMANDY',
|
||||
help='Enable Normandy recipe runner',
|
||||
set_for_old_configure=True,
|
||||
set_as_define=True)
|
||||
|
||||
project_flag('MOZ_SERVICES_SYNC',
|
||||
help='Build Sync Services if required')
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ const kIfdefStateForLinting = {
|
|||
MOZ_DATA_REPORTING: true,
|
||||
MOZ_TELEMETRY_REPORTING: true,
|
||||
MOZ_CRASHREPORTER: true,
|
||||
MOZ_NORMANDY: true,
|
||||
MOZ_MAINTENANCE_SERVICE: true,
|
||||
HAVE_SHELL_SERVICE: true,
|
||||
MENUBAR_CAN_AUTOHIDE: true,
|
||||
|
|
Загрузка…
Ссылка в новой задаче