commit e2afb0c982dae5a77f6c7025a626bf57e0b68c3d
parent 5622001e5bf0f47823188963bfcd2b82e4d6cf4b
Author: Rolf Rando <rrando@mozilla.com>
Date: Wed, 10 Dec 2025 18:55:23 +0000
Bug 2005108 - fix logic bug on randomization r=home-newtab-reviewers,nbarrett
Differential Revision: https://phabricator.services.mozilla.com/D275715
Diffstat:
3 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/browser/extensions/newtab/lib/NewTabContentPing.sys.mjs b/browser/extensions/newtab/lib/NewTabContentPing.sys.mjs
@@ -350,7 +350,8 @@ export class NewTabContentPing {
* Returns true or false with a certain proability specified
*
* @param {number} prob Probability
- * @returns {boolean} Random boolean result of probability prob
+ * @returns {boolean} Random boolean result of probability prob. A higher prob
+ * increases the chance of true being returned.
*/
static decideWithProbability(prob) {
if (prob <= 0) {
diff --git a/browser/extensions/newtab/lib/TelemetryFeed.sys.mjs b/browser/extensions/newtab/lib/TelemetryFeed.sys.mjs
@@ -883,7 +883,7 @@ export class TelemetryFeed {
const { p } =
this._privateRandomContentTelemetryProbablityValues[cache_key];
- if (!lazy.NewTabContentPing.decideWithProbability(p)) {
+ if (lazy.NewTabContentPing.decideWithProbability(p)) {
return item;
}
const allRecs = this.getAllRecommendations(); // Number of recommendations has changed
@@ -905,7 +905,11 @@ export class TelemetryFeed {
};
// If we're replacing a non top stories item, then assign the appropriate
// section to the item
- if (resultItem.section !== TOP_STORIES_SECTION_NAME && randomItem.section) {
+ if (
+ resultItem.section &&
+ resultItem.section !== TOP_STORIES_SECTION_NAME &&
+ randomItem.section
+ ) {
resultItem.section = randomItem.section;
resultItem.section_position = randomItem.section_position;
}
diff --git a/browser/extensions/newtab/test/xpcshell/test_TelemetryFeed.js b/browser/extensions/newtab/test/xpcshell/test_TelemetryFeed.js
@@ -17,6 +17,7 @@ ChromeUtils.defineESModuleGetters(this, {
HomePage: "resource:///modules/HomePage.sys.mjs",
JsonSchemaValidator:
"resource://gre/modules/components-utils/JsonSchemaValidator.sys.mjs",
+ NewTabContentPing: "resource://newtab/lib/NewTabContentPing.sys.mjs",
sinon: "resource://testing-common/Sinon.sys.mjs",
TelemetryController: "resource://gre/modules/TelemetryController.sys.mjs",
TelemetryFeed: "resource://newtab/lib/TelemetryFeed.sys.mjs",
@@ -2711,3 +2712,41 @@ add_task(async function test_handleBlockUrl_no_record_dismiss_on_no_session() {
sandbox.restore();
});
+
+add_task(function test_randomizeOrganicContentEvent() {
+ info(
+ "TelemetryFeed._randomizeOrganicContentEvent should return true or false" +
+ " based on the given probability"
+ );
+ let sandbox = sinon.createSandbox();
+ let instance = new TelemetryFeed();
+
+ const computeRec = id => ({
+ corpus_item_id: `item-${id}`,
+ topic: "a",
+ is_sponsored: false,
+ section_id: "section",
+ section_position: 3,
+ });
+ const allRecs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(computeRec);
+ sandbox.stub(instance, "getRecommendationCount").returns(allRecs.length);
+ sandbox.stub(instance, "getAllRecommendations").returns(allRecs);
+ instance._privateRandomContentTelemetryProbablityValues = { epsilon: 30 };
+ let decideStub = sandbox.stub(NewTabContentPing, "decideWithProbability");
+ decideStub.returns(true);
+ let result = instance.randomizeOrganicContentEvent(allRecs[0]);
+ Assert.equal(result, allRecs[0]);
+ Assert.ok(decideStub.calledOnce, "decideWithProbability was called once");
+ const [probUsed] = decideStub.firstCall.args;
+ Assert.greater(probUsed, 0.9); // Epsilon 30 is very high probability
+ Assert.less(probUsed, 1.01);
+
+ // Run again - randomization kicks in
+ decideStub.returns(false);
+ sandbox.stub(NewTabContentPing, "secureRandIntInRange").returns(3);
+ result = instance.randomizeOrganicContentEvent(allRecs[0]);
+ Assert.equal(probUsed, decideStub.lastCall.args[0]);
+ Assert.deepEqual(result, allRecs[3]);
+
+ sandbox.restore();
+});