summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/quicksuggest/unit/test_rust_ingest.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/quicksuggest/unit/test_rust_ingest.js')
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_rust_ingest.js244
1 files changed, 244 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_rust_ingest.js b/browser/components/urlbar/tests/quicksuggest/unit/test_rust_ingest.js
new file mode 100644
index 0000000000..e6ec61bcd4
--- /dev/null
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_rust_ingest.js
@@ -0,0 +1,244 @@
+/* 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/. */
+
+// Tests ingest in the Rust backend.
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ setTimeout: "resource://gre/modules/Timer.sys.mjs",
+});
+
+// These consts are copied from the update timer manager test. See
+// `initUpdateTimerManager()`.
+const PREF_APP_UPDATE_TIMERMINIMUMDELAY = "app.update.timerMinimumDelay";
+const PREF_APP_UPDATE_TIMERFIRSTINTERVAL = "app.update.timerFirstInterval";
+const MAIN_TIMER_INTERVAL = 1000; // milliseconds
+const CATEGORY_UPDATE_TIMER = "update-timer";
+
+const REMOTE_SETTINGS_SUGGESTION = {
+ id: 1,
+ url: "http://example.com/amp",
+ title: "AMP Suggestion",
+ keywords: ["amp"],
+ click_url: "http://example.com/amp-click",
+ impression_url: "http://example.com/amp-impression",
+ advertiser: "Amp",
+ iab_category: "22 - Shopping",
+ icon: "1234",
+};
+
+add_setup(async function () {
+ initUpdateTimerManager();
+
+ await QuickSuggestTestUtils.ensureQuickSuggestInit({
+ remoteSettingsRecords: [
+ {
+ type: "data",
+ attachment: [REMOTE_SETTINGS_SUGGESTION],
+ },
+ ],
+ prefs: [
+ ["suggest.quicksuggest.sponsored", true],
+ ["quicksuggest.rustEnabled", false],
+ ],
+ });
+});
+
+// IMPORTANT: This task must run first!
+//
+// This simulates the first time the Rust backend is enabled in a profile. The
+// backend should perform ingestion immediately.
+add_task(async function firstRun() {
+ Assert.ok(
+ !UrlbarPrefs.get("quicksuggest.rustEnabled"),
+ "rustEnabled pref is initially false (this task must run first!)"
+ );
+ Assert.strictEqual(
+ QuickSuggest.rustBackend.isEnabled,
+ false,
+ "Rust backend is initially disabled (this task must run first!)"
+ );
+ Assert.ok(
+ !QuickSuggest.rustBackend.ingestPromise,
+ "No ingest has been performed yet (this task must run first!)"
+ );
+
+ info("Enabling the Rust backend");
+ UrlbarPrefs.set("quicksuggest.rustEnabled", true);
+ Assert.ok(QuickSuggest.rustBackend.isEnabled, "Rust backend is now enabled");
+
+ // An ingest should start.
+ let { ingestPromise } = await waitForIngestStart(null);
+
+ info("Awaiting ingest promise");
+ await ingestPromise;
+ info("Done awaiting ingest promise");
+
+ await checkSuggestions();
+
+ // Disable and re-enable the backend. No new ingestion should start
+ // immediately since this isn't the first time the backend has been enabled.
+ UrlbarPrefs.set("quicksuggest.rustEnabled", false);
+ UrlbarPrefs.set("quicksuggest.rustEnabled", true);
+ await assertNoNewIngestStarted(ingestPromise);
+
+ await checkSuggestions();
+
+ UrlbarPrefs.set("quicksuggest.rustEnabled", false);
+});
+
+// Ingestion should be performed according to the defined interval.
+add_task(async function interval() {
+ let { ingestPromise } = QuickSuggest.rustBackend;
+ Assert.ok(
+ ingestPromise,
+ "Sanity check: An ingest has already been performed"
+ );
+ Assert.ok(
+ !UrlbarPrefs.get("quicksuggest.rustEnabled"),
+ "Sanity check: Rust backend is initially disabled"
+ );
+
+ // Set a small interval and enable the backend. No new ingestion should start
+ // immediately since this isn't the first time the backend has been enabled.
+ let intervalSecs = 1;
+ UrlbarPrefs.set("quicksuggest.rustIngestIntervalSeconds", intervalSecs);
+ UrlbarPrefs.set("quicksuggest.rustEnabled", true);
+ await assertNoNewIngestStarted(ingestPromise);
+
+ // Wait for a few ingests to happen.
+ for (let i = 0; i < 3; i++) {
+ info("Preparing for ingest at index " + i);
+
+ // Set a new suggestion so we can make sure ingest really happened.
+ let suggestion = {
+ ...REMOTE_SETTINGS_SUGGESTION,
+ url: REMOTE_SETTINGS_SUGGESTION.url + "/" + i,
+ };
+ await QuickSuggestTestUtils.setRemoteSettingsRecords(
+ [
+ {
+ type: "data",
+ attachment: [suggestion],
+ },
+ ],
+ // Don't force sync since the whole point here is to make sure the backend
+ // ingests on its own!
+ { forceSync: false }
+ );
+
+ // Wait for ingest to start and finish.
+ info("Waiting for ingest to start at index " + i);
+ ({ ingestPromise } = await waitForIngestStart(ingestPromise));
+ info("Waiting for ingest to finish at index " + i);
+ await ingestPromise;
+ await checkSuggestions([suggestion]);
+ }
+
+ // In the loop above, there was one additional async call after awaiting the
+ // ingest promise, to `checkSuggestions()`. It's possible, though unlikely,
+ // that call took so long that another ingest has started. To be sure, wait
+ // for one final ingest to start before continuing.
+ ({ ingestPromise } = await waitForIngestStart(ingestPromise));
+
+ // Now immediately disable the backend. New ingests should not start, but the
+ // final one will still be ongoing.
+ info("Disabling the backend");
+ UrlbarPrefs.set("quicksuggest.rustEnabled", false);
+
+ info("Awaiting final ingest promise");
+ await ingestPromise;
+
+ // Wait a few seconds.
+ let waitSecs = 3 * intervalSecs;
+ info(`Waiting ${waitSecs}s...`);
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(r => setTimeout(r, 1000 * waitSecs));
+
+ // No new ingests should have started.
+ Assert.equal(
+ QuickSuggest.rustBackend.ingestPromise,
+ ingestPromise,
+ "No new ingest started after disabling the backend"
+ );
+
+ UrlbarPrefs.clear("quicksuggest.rustIngestIntervalSeconds");
+});
+
+async function waitForIngestStart(oldIngestPromise) {
+ let newIngestPromise;
+ await TestUtils.waitForCondition(() => {
+ let { ingestPromise } = QuickSuggest.rustBackend;
+ if (
+ (oldIngestPromise && ingestPromise != oldIngestPromise) ||
+ (!oldIngestPromise && ingestPromise)
+ ) {
+ newIngestPromise = ingestPromise;
+ return true;
+ }
+ return false;
+ }, "Waiting for a new ingest to start");
+
+ Assert.equal(
+ QuickSuggest.rustBackend.ingestPromise,
+ newIngestPromise,
+ "Sanity check: ingestPromise hasn't changed since waitForCondition returned"
+ );
+
+ // A bare promise can't be returned because it will cause the awaiting caller
+ // to await that promise! We're simply trying to return the promise, which the
+ // caller can later await.
+ return { ingestPromise: newIngestPromise };
+}
+
+async function assertNoNewIngestStarted(oldIngestPromise) {
+ for (let i = 0; i < 3; i++) {
+ await TestUtils.waitForTick();
+ }
+ Assert.equal(
+ QuickSuggest.rustBackend.ingestPromise,
+ oldIngestPromise,
+ "No new ingest started"
+ );
+}
+
+async function checkSuggestions(expected = [REMOTE_SETTINGS_SUGGESTION]) {
+ let actual = await QuickSuggest.rustBackend.query("amp");
+ Assert.deepEqual(
+ actual.map(s => s.url),
+ expected.map(s => s.url),
+ "Backend should be serving the expected suggestions"
+ );
+}
+
+/**
+ * Sets up the update timer manager for testing: makes it fire more often,
+ * removes all existing timers, and initializes it for testing. The body of this
+ * function is copied from:
+ * https://searchfox.org/mozilla-central/source/toolkit/components/timermanager/tests/unit/consumerNotifications.js
+ */
+function initUpdateTimerManager() {
+ // Set the timer to fire every second
+ Services.prefs.setIntPref(
+ PREF_APP_UPDATE_TIMERMINIMUMDELAY,
+ MAIN_TIMER_INTERVAL / 1000
+ );
+ Services.prefs.setIntPref(
+ PREF_APP_UPDATE_TIMERFIRSTINTERVAL,
+ MAIN_TIMER_INTERVAL
+ );
+
+ // Remove existing update timers to prevent them from being notified
+ for (let { data: entry } of Services.catMan.enumerateCategory(
+ CATEGORY_UPDATE_TIMER
+ )) {
+ Services.catMan.deleteCategoryEntry(CATEGORY_UPDATE_TIMER, entry, false);
+ }
+
+ Cc["@mozilla.org/updates/timer-manager;1"]
+ .getService(Ci.nsIUpdateTimerManager)
+ .QueryInterface(Ci.nsIObserver)
+ .observe(null, "utm-test-init", "");
+}