Backed out 3 changesets (bug 1821092) for causing failures in browser_autocomplete_import.js CLOSED TREE

Backed out changeset 142e58fc3ee1 (bug 1821092)
Backed out changeset ff07a826e81d (bug 1821092)
Backed out changeset b70d97b262ed (bug 1821092)
This commit is contained in:
Noemi Erli 2023-04-06 04:09:51 +03:00
parent ff02e04b0e
commit f91604fab5
13 changed files with 123 additions and 1899 deletions

View file

@ -56,12 +56,6 @@ function featuresCompat(branch) {
return features;
}
function getBranchFeature(enrollment, targetFeatureId) {
return featuresCompat(enrollment.branch).find(
({ featureId }) => featureId === targetFeatureId
);
}
const experimentBranchAccessor = {
get: (target, prop) => {
// Offer an API where we can access `branch.feature.*`.
@ -357,32 +351,20 @@ export class _ExperimentFeature {
}
/**
* Lookup feature variables in experiments, rollouts, and fallback prefs.
* Lookup feature variables in experiments, prefs, and remote defaults.
* @param {{defaultValues?: {[variableName: string]: any}}} options
* @returns {{[variableName: string]: any}} The feature value
*/
getAllVariables({ defaultValues = null } = {}) {
let enrollment = null;
try {
enrollment = ExperimentAPI._store.getExperimentForFeature(this.featureId);
} catch (e) {
console.error(e);
}
let featureValue = this._getLocalizedValue(enrollment);
if (typeof featureValue === "undefined") {
try {
enrollment = ExperimentAPI._store.getRolloutForFeature(this.featureId);
} catch (e) {
console.error(e);
}
featureValue = this._getLocalizedValue(enrollment);
}
const branch = ExperimentAPI.getActiveBranch({ featureId: this.featureId });
const featureValue = featuresCompat(branch).find(
({ featureId }) => featureId === this.featureId
)?.value;
return {
...this.prefGetters,
...defaultValues,
...featureValue,
...(featureValue ? featureValue : this.getRollout()?.value),
};
}
@ -397,26 +379,21 @@ export class _ExperimentFeature {
}
// Next, check if an experiment is defined
let enrollment = null;
try {
enrollment = ExperimentAPI._store.getExperimentForFeature(this.featureId);
} catch (e) {
console.error(e);
}
let value = this._getLocalizedValue(enrollment, variable);
if (typeof value !== "undefined") {
return value;
const branch = ExperimentAPI.getActiveBranch({
featureId: this.featureId,
});
const experimentValue = featuresCompat(branch).find(
({ featureId }) => featureId === this.featureId
)?.value?.[variable];
if (typeof experimentValue !== "undefined") {
return experimentValue;
}
// Next, check for a rollout.
try {
enrollment = ExperimentAPI._store.getRolloutForFeature(this.featureId);
} catch (e) {
console.error(e);
}
value = this._getLocalizedValue(enrollment, variable);
if (typeof value !== "undefined") {
return value;
// Next, check remote defaults
const remoteValue = this.getRollout()?.value?.[variable];
if (typeof remoteValue !== "undefined") {
return remoteValue;
}
// Return the default preference value
@ -500,159 +477,8 @@ export class _ExperimentFeature {
rollouts: this.getRollout(),
};
}
/**
* Do recursive locale substitution on the values, if applicable.
*
* If there are no localizations provided, the value will be returned as-is.
*
* If the value is an object containing an $l10n key, its substitution will be
* returned.
*
* Otherwise, the value will be recursively substituted.
*
* @param {unknown} values The values to perform substitutions upon.
* @param {Record<string, string>} localizations The localization
* substitutions for a specific locale.
* @param {Set<string>?} missingIds An optional set to collect all the IDs of
* all missing l10n entries.
*
* @returns {any} The values, potentially locale substituted.
*/
static substituteLocalizations(
values,
localizations,
missingIds = undefined
) {
const result = _ExperimentFeature._substituteLocalizations(
values,
localizations,
missingIds
);
if (missingIds?.size) {
throw new ExperimentLocalizationError("l10n-missing-entry");
}
return result;
}
/**
* The implementation of localization substitution.
*
* @param {unknown} values The values to perform substitutions upon.
* @param {Record<string, string>} localizations The localization
* substitutions for a specific locale.
* @param {Set<string>?} missingIds An optional set to collect all the IDs of
* all missing l10n entries.
*
* @returns {any} The values, potentially locale substituted.
*/
static _substituteLocalizations(values, localizations, missingIds) {
// If the recipe is not localized, we don't need to do anything.
// Likewise, if the value we are attempting to localize is not an object,
// there is nothing to localize.
if (
typeof localizations === "undefined" ||
typeof values !== "object" ||
values === null
) {
return values;
}
if (Array.isArray(values)) {
return values.map(value =>
_ExperimentFeature._substituteLocalizations(
value,
localizations,
missingIds
)
);
}
const substituted = Object.assign({}, values);
for (const [key, value] of Object.entries(values)) {
if (
key === "$l10n" &&
typeof value === "object" &&
value !== null &&
value?.id
) {
if (!Object.hasOwn(localizations, value.id)) {
if (missingIds) {
missingIds.add(value.id);
break;
} else {
throw new ExperimentLocalizationError("l10n-missing-entry");
}
}
return localizations[value.id];
}
substituted[key] = _ExperimentFeature._substituteLocalizations(
value,
localizations,
missingIds
);
}
return substituted;
}
/**
* Return a value (or all values) from an enrollment, potentially localized.
*
* @param {Enrollment} enrollment - The enrollment to query for the value or values.
* @param {string?} variable - The name of the variable to query for. If not
* provided, all variables will be returned.
*
* @returns {any} The value for the variable(s) in question.
*/
_getLocalizedValue(enrollment, variable = undefined) {
if (enrollment) {
const locale = Services.locale.appLocaleAsBCP47;
if (
typeof enrollment.localizations === "object" &&
enrollment.localizations !== null &&
(typeof enrollment.localizations[locale] !== "object" ||
enrollment.localizations[locale] === null)
) {
ExperimentAPI._manager.unenroll(enrollment.slug, "l10n-missing-locale");
return undefined;
}
const allValues = getBranchFeature(enrollment, this.featureId)?.value;
const value =
typeof variable === "undefined" ? allValues : allValues?.[variable];
if (typeof value !== "undefined") {
try {
return _ExperimentFeature.substituteLocalizations(
value,
enrollment.localizations?.[locale]
);
} catch (e) {
// This should never happen.
if (e instanceof ExperimentLocalizationError) {
ExperimentAPI._manager.unenroll(enrollment.slug, e.reason);
} else {
throw e;
}
}
}
}
return undefined;
}
}
XPCOMUtils.defineLazyGetter(ExperimentAPI, "_manager", function() {
return lazy.ExperimentManager;
});
XPCOMUtils.defineLazyGetter(ExperimentAPI, "_store", function() {
return IS_MAIN_PROCESS
? lazy.ExperimentManager.store
@ -662,10 +488,3 @@ XPCOMUtils.defineLazyGetter(ExperimentAPI, "_store", function() {
XPCOMUtils.defineLazyGetter(ExperimentAPI, "_remoteSettingsClient", function() {
return lazy.RemoteSettings(lazy.COLLECTION_ID);
});
class ExperimentLocalizationError extends Error {
constructor(reason) {
super(`Localized experiment error (${reason})`);
this.reason = reason;
}
}

View file

@ -179,9 +179,7 @@ export class _ExperimentManager {
recipeMismatches,
invalidRecipes,
invalidBranches,
invalidFeatures,
missingLocale,
missingL10nIds
invalidFeatures
) {
for (const enrollment of enrollments) {
const { slug, source } = enrollment;
@ -198,10 +196,6 @@ export class _ExperimentManager {
reason = "invalid-recipe";
} else if (invalidBranches.has(slug) || invalidFeatures.has(slug)) {
reason = "invalid-branch";
} else if (missingLocale.includes(slug)) {
reason = "l10n-missing-locale";
} else if (missingL10nIds.has(slug)) {
reason = "l10n-missing-entry";
} else {
reason = "recipe-not-seen";
}
@ -227,16 +221,6 @@ export class _ExperimentManager {
* feature validation.
* @param {Map<string, string[]>} options.invalidFeatures
* The mapping of experiment slugs to a list of invalid feature IDs.
* @param {string[]} options.missingLocale
* The list of experiment slugs missing an entry in the localization
* table for the current locale.
* @param {Map<string, string[]>} options.missingL10nIds
* The mapping of experiment slugs to the IDs of localization entries
* missing from the current locale.
* @param {string | null} options.locale
* The current locale.
* @param {boolean} options.validationEnabled
* Whether or not schema validation was enabled.
*/
onFinalize(
sourceToCheck,
@ -245,9 +229,6 @@ export class _ExperimentManager {
invalidRecipes = [],
invalidBranches = new Map(),
invalidFeatures = new Map(),
missingLocale = [],
missingL10nIds = new Map(),
locale = null,
validationEnabled = true,
} = {}
) {
@ -262,9 +243,7 @@ export class _ExperimentManager {
recipeMismatches,
invalidRecipes,
invalidBranches,
invalidFeatures,
missingLocale,
missingL10nIds
invalidFeatures
);
this._checkUnseenEnrollments(
activeRollouts,
@ -272,13 +251,11 @@ export class _ExperimentManager {
recipeMismatches,
invalidRecipes,
invalidBranches,
invalidFeatures,
missingLocale,
missingL10nIds
invalidFeatures
);
// If schema validation is disabled, then we will never send these
// validation failed telemetry events
// If validation is disabled, then we will never send validation failed
// telemetry.
if (validationEnabled) {
for (const slug of invalidRecipes) {
this.sendValidationFailedTelemetry(slug, "invalid-recipe");
@ -299,21 +276,6 @@ export class _ExperimentManager {
}
}
if (locale) {
for (const slug of missingLocale.values()) {
this.sendValidationFailedTelemetry(slug, "l10n-missing-locale", {
locale,
});
}
for (const [slug, ids] of missingL10nIds.entries()) {
this.sendValidationFailedTelemetry(slug, "l10n-missing-entry", {
l10n_ids: ids.join(","),
locale,
});
}
}
this.sessions.delete(sourceToCheck);
}
@ -393,7 +355,6 @@ export class _ExperimentManager {
userFacingDescription,
featureIds,
isRollout,
localizations,
},
branch,
source,
@ -414,7 +375,6 @@ export class _ExperimentManager {
lastSeen: new Date().toJSON(),
featureIds,
prefs,
localizations,
};
if (typeof isRollout !== "undefined") {

View file

@ -7,7 +7,6 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
_ExperimentFeature: "resource://nimbus/ExperimentAPI.sys.mjs",
ExperimentManager: "resource://nimbus/lib/ExperimentManager.sys.mjs",
JsonSchema: "resource://gre/modules/JsonSchema.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
@ -377,10 +376,6 @@ export class EnrollmentsContext {
this.invalidBranches = new Map();
this.invalidFeatures = new Map();
this.validatorCache = {};
this.missingLocale = [];
this.missingL10nIds = new Map();
this.locale = Services.locale.appLocaleAsBCP47;
}
getResults() {
@ -389,9 +384,6 @@ export class EnrollmentsContext {
invalidRecipes: this.invalidRecipes,
invalidBranches: this.invalidBranches,
invalidFeatures: this.invalidFeatures,
missingLocale: this.missingLocale,
missingL10nIds: this.missingL10nIds,
locale: this.locale,
validationEnabled: this.validationEnabled,
};
}
@ -409,7 +401,7 @@ export class EnrollmentsContext {
return false;
}
const validateFeatureSchemas =
const validateFeatures =
this.validationEnabled && !recipe.featureValidationOptOut;
if (this.validationEnabled) {
@ -474,23 +466,8 @@ export class EnrollmentsContext {
this.matches++;
if (
typeof recipe.localizations === "object" &&
recipe.localizations !== null
) {
if (
typeof recipe.localizations[this.locale] !== "object" ||
recipe.localizations[this.locale] === null
) {
this.missingLocale.push(recipe.slug);
lazy.log.debug(
`${recipe.id} is localized but missing locale ${this.locale}`
);
return false;
}
}
const result = await this._validateBranches(recipe, validateFeatureSchemas);
if (validateFeatures) {
const result = await this._validateBranches(recipe);
if (!result.valid) {
if (result.invalidBranchSlugs.length) {
this.invalidBranches.set(recipe.slug, result.invalidBranchSlugs);
@ -498,12 +475,10 @@ export class EnrollmentsContext {
if (result.invalidFeatureIds.length) {
this.invalidFeatures.set(recipe.slug, result.invalidFeatureIds);
}
if (result.missingL10nIds.length) {
this.missingL10nIds.set(recipe.slug, result.missingL10nIds);
}
lazy.log.debug(`${recipe.id} did not validate`);
return false;
}
}
return true;
}
@ -563,64 +538,39 @@ export class EnrollmentsContext {
}
/**
* Validate the branches of an experiment.
* Validate the branches of an experiment using schemas
*
* @param {object} recipe The recipe object.
* @param {boolean} validateSchema Whether to validate the feature values
* using JSON schemas.
* @param {object} validatorCache A cache of JSON Schema validators keyed by feature
* ID.
*
* @returns {object} The lists of invalid branch slugs and invalid feature
* IDs.
*/
async _validateBranches({ id, branches, localizations }, validateSchema) {
async _validateBranches({ id, branches }) {
const invalidBranchSlugs = [];
const invalidFeatureIds = new Set();
const missingL10nIds = new Set();
if (validateSchema || typeof localizations !== "undefined") {
for (const [branchIdx, branch] of branches.entries()) {
const features = branch.features ?? [branch.feature];
for (const feature of features) {
const { featureId, value } = feature;
if (!lazy.NimbusFeatures[featureId]) {
console.error(
`Experiment ${id} has unknown featureId: ${featureId}`
);
console.error(`Experiment ${id} has unknown featureId: ${featureId}`);
invalidFeatureIds.add(featureId);
continue;
}
let substitutedValue = value;
if (localizations) {
// We already know that we have a localization table for this locale
// because we checked in `checkRecipe`.
try {
substitutedValue = lazy._ExperimentFeature.substituteLocalizations(
value,
localizations[Services.locale.appLocaleAsBCP47],
missingL10nIds
);
} catch (e) {
if (e?.reason === "l10n-missing-entry") {
// Skip validation because it *will* fail.
continue;
}
throw e;
}
}
if (validateSchema) {
let validator;
if (this.validatorCache[featureId]) {
validator = this.validatorCache[featureId];
} else if (lazy.NimbusFeatures[featureId].manifest.schema?.uri) {
const uri = lazy.NimbusFeatures[featureId].manifest.schema.uri;
try {
const schema = await fetch(uri, {
credentials: "omit",
}).then(rsp => rsp.json());
const schema = await fetch(uri, { credentials: "omit" }).then(rsp =>
rsp.json()
);
validator = this.validatorCache[
featureId
@ -639,7 +589,7 @@ export class EnrollmentsContext {
] = new lazy.JsonSchema.Validator(schema);
}
const result = validator.validate(substitutedValue);
const result = validator.validate(value);
if (!result.valid) {
console.error(
`Experiment ${id} branch ${branchIdx} feature ${featureId} does not validate: ${JSON.stringify(
@ -652,17 +602,11 @@ export class EnrollmentsContext {
}
}
}
}
}
return {
invalidBranchSlugs,
invalidFeatureIds: Array.from(invalidFeatureIds),
missingL10nIds: Array.from(missingL10nIds),
valid:
invalidBranchSlugs.length === 0 &&
invalidFeatureIds.size === 0 &&
missingL10nIds.size === 0,
valid: invalidBranchSlugs.length === 0 && invalidFeatureIds.size === 0,
};
}

View file

@ -177,8 +177,8 @@ nimbus_events:
reason:
type: string
description: >
Why validation failed (one of "invalid-recipe", "invalid-branch",
"invalid-reason", "missing-locale", or "missing-l10n-entry").
Why validation failed (one of "invalid-recipe", "invalid-branch", or
"invalid-reason").
branch:
type: string
description: >
@ -186,27 +186,12 @@ nimbus_events:
feature:
type: string
description: If reason == invalid-feature, the invalid feature ID.
locale:
type: string
description: >
If reason == missing-locale, the locale that was missing from the
localization table.
If reason == missing-l10n-entry, the locale that was missing the
localization entries.
l10n_ids:
type: string
description: >
If reason == missing-l10n-entry, a comma-sparated list of missing
localization entries.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1762652
- https://bugzilla.mozilla.org/show_bug.cgi?id=1781953
- https://bugzilla.mozilla.org/show_bug.cgi?id=1821092
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1762652
- https://bugzilla.mozilla.org/show_bug.cgi?id=1781953
- https://bugzilla.mozilla.org/show_bug.cgi?id=1821092
data_sensitivity:
- technical
notification_emails:

View file

@ -314,16 +314,6 @@
"featureValidationOptOut": {
"type": "boolean",
"description": "Opt out of feature schema validation. Only supported on desktop."
},
"localizations": {
"type": "object",
"additionalProperties": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"description": "Per-locale localization substitutions.\n\nThe top level key is the locale (e.g., \"en-US\" or \"fr\"). Each entry is a mapping of string IDs to their localized equivalents.\n\nOnly supported on desktop."
}
},
"required": [

View file

@ -9,8 +9,8 @@ origin:
description: "Shared data and schemas for Project Nimbus"
url: "https://github.com/mozilla/nimbus-shared"
license: "MPL-2.0"
release: "version 2.0.0"
revision: "v2.0.0"
release: "version 1.10.0"
revision: "v1.10.0"
vendoring:
url: "https://github.com/mozilla/nimbus-shared"

View file

@ -9,7 +9,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
ExperimentManager: "resource://nimbus/lib/ExperimentManager.sys.mjs",
JsonSchema: "resource://gre/modules/JsonSchema.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
_ExperimentManager: "resource://nimbus/lib/ExperimentManager.sys.mjs",
@ -186,7 +186,7 @@ export const ExperimentFakes = {
},
async enrollWithRollout(
featureConfig,
{ manager = lazy.ExperimentAPI._manager, source } = {}
{ manager = lazy.ExperimentManager, source } = {}
) {
await manager.store.init();
const rollout = this.rollout(`${featureConfig.featureId}-rollout`, {
@ -223,7 +223,7 @@ export const ExperimentFakes = {
},
async enrollWithFeatureConfig(
featureConfig,
{ manager = lazy.ExperimentAPI._manager, isRollout = false } = {}
{ manager = lazy.ExperimentManager, isRollout = false } = {}
) {
await manager.store.ready();
// Use id passed in featureConfig value to compute experimentId
@ -259,10 +259,7 @@ export const ExperimentFakes = {
return doExperimentCleanup;
},
enrollmentHelper(
recipe,
{ manager = lazy.ExperimentAPI._manager, source = "enrollmentHelper" } = {}
) {
enrollmentHelper(recipe, { manager = lazy.ExperimentManager } = {}) {
if (!recipe?.slug) {
throw new Error("Enrollment helper expects a recipe");
}
@ -296,11 +293,11 @@ export const ExperimentFakes = {
if (!manager.store._isReady) {
throw new Error("Manager store not ready, call `manager.onStartup`");
}
manager.enroll(recipe, source);
manager.enroll(recipe, "enrollmentHelper");
return { enrollmentPromise, doExperimentCleanup };
},
async cleanupAll(slugs, { manager = lazy.ExperimentAPI._manager } = {}) {
async cleanupAll(slugs, { manager = lazy.ExperimentManager } = {}) {
function unenrollCompleted(slug) {
return new Promise(resolve =>
manager.store.on(`update:${slug}`, (event, experiment) => {

View file

@ -7,49 +7,7 @@ const { sinon } = ChromeUtils.importESModule(
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
ChromeUtils.defineModuleGetter(
this,
"ObjectUtils",
"resource://gre/modules/ObjectUtils.jsm"
);
ChromeUtils.defineESModuleGetters(this, {
ExperimentFakes: "resource://testing-common/NimbusTestUtils.sys.mjs",
ExperimentTestUtils: "resource://testing-common/NimbusTestUtils.sys.mjs",
});
// Sinon does not support Set or Map in spy.calledWith()
function onFinalizeCalled(spyOrCallArgs, ...expectedArgs) {
function mapToObject(map) {
return Object.assign(
{},
...Array.from(map.entries()).map(([k, v]) => ({ [k]: v }))
);
}
function toPlainObjects(args) {
return [
args[0],
{
...args[1],
invalidBranches: mapToObject(args[1].invalidBranches),
invalidFeatures: mapToObject(args[1].invalidFeatures),
missingLocale: Array.from(args[1].missingLocale),
missingL10nIds: mapToObject(args[1].missingL10nIds),
},
];
}
const plainExpected = toPlainObjects(expectedArgs);
if (Array.isArray(spyOrCallArgs)) {
return ObjectUtils.deepEqual(toPlainObjects(spyOrCallArgs), plainExpected);
}
for (const args of spyOrCallArgs.args) {
if (ObjectUtils.deepEqual(toPlainObjects(args), plainExpected)) {
return true;
}
}
return false;
}

View file

@ -161,14 +161,11 @@ add_task(async function test_updateRecipes_someMismatch() {
);
ok(loader.manager.onFinalize.calledOnce, "Should call onFinalize.");
ok(
onFinalizeCalled(loader.manager.onFinalize, "rs-loader", {
loader.manager.onFinalize.calledWith("rs-loader", {
recipeMismatches: [FAIL_FILTER_RECIPE.slug],
invalidRecipes: [],
invalidBranches: new Map(),
invalidFeatures: new Map(),
missingL10nIds: new Map(),
missingLocale: [],
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: true,
}),
"should call .onFinalize with the recipes that failed targeting"

View file

@ -195,16 +195,12 @@ add_task(async function test_updateRecipes_invalidRecipeAfterUpdate() {
"should call .onRecipe with argument data"
);
equal(loader.manager.onFinalize.callCount, 1, "should call .onFinalize once");
ok(
onFinalizeCalled(loader.manager.onFinalize, "rs-loader", {
loader.manager.onFinalize.calledWith("rs-loader", {
recipeMismatches: [],
invalidRecipes: [],
invalidBranches: new Map(),
invalidFeatures: new Map(),
missingLocale: [],
missingL10nIds: new Map(),
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: true,
}),
"should call .onFinalize with no mismatches or invalid recipes"
@ -227,14 +223,11 @@ add_task(async function test_updateRecipes_invalidRecipeAfterUpdate() {
);
ok(
onFinalizeCalled(loader.manager.onFinalize.secondCall.args, "rs-loader", {
loader.manager.onFinalize.secondCall.calledWith("rs-loader", {
recipeMismatches: [],
invalidRecipes: ["foo"],
invalidBranches: new Map(),
invalidFeatures: new Map(),
missingLocale: [],
missingL10nIds: new Map(),
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: true,
}),
"should call .onFinalize with an invalid recipe"
@ -310,14 +303,11 @@ add_task(async function test_updateRecipes_invalidBranchAfterUpdate() {
);
equal(loader.manager.onFinalize.callCount, 1, "should call .onFinalize once");
ok(
onFinalizeCalled(loader.manager.onFinalize, "rs-loader", {
loader.manager.onFinalize.calledWith("rs-loader", {
recipeMismatches: [],
invalidRecipes: [],
invalidBranches: new Map(),
invalidFeatures: new Map(),
missingLocale: [],
missingL10nIds: new Map(),
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: true,
}),
"should call .onFinalize with no mismatches or invalid recipes"
@ -340,14 +330,11 @@ add_task(async function test_updateRecipes_invalidBranchAfterUpdate() {
);
ok(
onFinalizeCalled(loader.manager.onFinalize.secondCall.args, "rs-loader", {
loader.manager.onFinalize.secondCall.calledWith("rs-loader", {
recipeMismatches: [],
invalidRecipes: [],
invalidBranches: new Map([["foo", [badRecipe.branches[1].slug]]]),
invalidBranches: new Map([["foo", [badRecipe.branches[0].slug]]]),
invalidFeatures: new Map(),
missingLocale: [],
missingL10nIds: new Map(),
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: true,
}),
"should call .onFinalize with an invalid branch"
@ -419,14 +406,11 @@ add_task(async function test_updateRecipes_simpleFeatureInvalidAfterUpdate() {
);
equal(loader.manager.onFinalize.callCount, 1, "should call .onFinalize once");
ok(
onFinalizeCalled(loader.manager.onFinalize, "rs-loader", {
loader.manager.onFinalize.calledWith("rs-loader", {
recipeMismatches: [],
invalidRecipes: [],
invalidBranches: new Map(),
invalidFeatures: new Map(),
missingLocale: [],
missingL10nIds: new Map(),
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: true,
}),
"should call .onFinalize with nomismatches or invalid recipes"
@ -460,14 +444,11 @@ add_task(async function test_updateRecipes_simpleFeatureInvalidAfterUpdate() {
);
ok(
onFinalizeCalled(loader.manager.onFinalize.secondCall.args, "rs-loader", {
loader.manager.onFinalize.secondCall.calledWith("rs-loader", {
recipeMismatches: [],
invalidRecipes: [],
invalidBranches: new Map([["foo", [badRecipe.branches[0].slug]]]),
invalidFeatures: new Map(),
missingLocale: [],
missingL10nIds: new Map(),
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: true,
}),
"should call .onFinalize with an invalid branch"
@ -657,14 +638,11 @@ add_task(async function test_updateRecipes_validationDisabled() {
"Should not send validation failed telemetry"
);
Assert.ok(
onFinalizeCalled(finalizeStub, "rs-loader", {
finalizeStub.calledWith("rs-loader", {
recipeMismatches: [],
invalidRecipes: [],
invalidBranches: new Map(),
invalidFeatures: new Map(),
missingLocale: [],
missingL10nIds: new Map(),
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: false,
}),
"should call .onFinalize with no validation issues"
@ -704,14 +682,11 @@ add_task(async function test_updateRecipes_appId() {
Assert.equal(manager.onRecipe.callCount, 0, ".onRecipe was never called");
Assert.ok(
onFinalizeCalled(manager.onFinalize, "rs-loader", {
manager.onFinalize.calledWith("rs-loader", {
recipeMismatches: [],
invalidRecipes: [],
invalidBranches: new Map(),
invalidFeatures: new Map(),
missingLocale: [],
missingL10nIds: new Map(),
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: true,
}),
"Should call .onFinalize with no validation issues"
@ -731,14 +706,11 @@ add_task(async function test_updateRecipes_appId() {
);
Assert.ok(
onFinalizeCalled(manager.onFinalize, "rs-loader", {
manager.onFinalize.calledWith("rs-loader", {
recipeMismatches: [],
invalidRecipes: [],
invalidBranches: new Map(),
invalidFeatures: new Map(),
missingLocale: [],
missingL10nIds: new Map(),
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: true,
}),
"Should call .onFinalize with no validation issues"
@ -822,14 +794,11 @@ add_task(async function test_updateRecipes_recipeAppId() {
Assert.equal(manager.onRecipe.callCount, 0, ".onRecipe was never called");
Assert.ok(
onFinalizeCalled(manager.onFinalize, "rs-loader", {
manager.onFinalize.calledWith("rs-loader", {
recipeMismatches: [],
invalidRecipes: [],
invalidBranches: new Map(),
invalidFeatures: new Map(),
missingLocale: [],
missingL10nIds: new Map(),
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: true,
}),
"Should call .onFinalize with no validation issues"
@ -901,17 +870,12 @@ add_task(async function test_updateRecipes_featureValidationOptOut() {
manager.onRecipe.calledOnceWith(optOutRecipe, "rs-loader"),
"should call .onRecipe for the opt-out recipe"
);
ok(
manager.onFinalize.calledOnce &&
onFinalizeCalled(manager.onFinalize, "rs-loader", {
manager.onFinalize.calledOnceWith("rs-loader", {
recipeMismatches: [],
invalidRecipes: [],
invalidBranches: new Map([[invalidRecipe.slug, ["control"]]]),
invalidFeatures: new Map(),
missingLocale: [],
missingL10nIds: new Map(),
locale: Services.locale.appLocaleAsBCP47,
validationEnabled: true,
}),
"should call .onFinalize with only one invalid recipe"

File diff suppressed because it is too large Load diff

View file

@ -9,20 +9,19 @@ skip-if =
appname == "thunderbird"
run-sequentially = very high failure rate in parallel
[test_ExperimentManager_context.js]
[test_ExperimentManager_enroll.js]
[test_ExperimentManager_lifecycle.js]
[test_ExperimentManager_unenroll.js]
[test_ExperimentManager_generateTestIds.js]
[test_ExperimentManager_prefs.js]
[test_ExperimentStore.js]
[test_NimbusTestUtils.js]
[test_SharedDataMap.js]
[test_ExperimentAPI.js]
[test_ExperimentAPI_ExperimentFeature.js]
[test_ExperimentAPI_ExperimentFeature_getAllVariables.js]
[test_ExperimentAPI_ExperimentFeature_getVariable.js]
[test_ExperimentAPI_NimbusFeatures.js]
[test_ExperimentManager_context.js]
[test_ExperimentManager_enroll.js]
[test_ExperimentManager_generateTestIds.js]
[test_ExperimentManager_lifecycle.js]
[test_ExperimentManager_prefs.js]
[test_ExperimentManager_unenroll.js]
[test_ExperimentStore.js]
[test_NimbusTestUtils.js]
[test_RemoteSettingsExperimentLoader.js]
[test_RemoteSettingsExperimentLoader_updateRecipes.js]
[test_SharedDataMap.js]
[test_localization.js]
[test_ExperimentAPI_NimbusFeatures.js]

View file

@ -1034,14 +1034,6 @@ normandy:
reason: Why validation failed (one of "invalid-recipe", "invalid-branch", or "invalid-reason").
branch: If reason == invalid-branch, the branch that failed validation.
feature: If reason == invalid-feature, the invalid feature ID.
locale: >
If reason == missing-locale, the locale that was missing from the
localization table.
If reason == missing-l10n-entry, the locale that was missing the
localization entries.
l10n_ids: >
If reason == missing-l10n-entry, a comma-separated list of missing
localization entries.
browser.launched_to_handle:
system_notification: