Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* 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 { HttpServer } = ChromeUtils.importESModule(
);
const { PlacesTestUtils } = ChromeUtils.importESModule(
);
const { NetUtil } = ChromeUtils.importESModule(
"resource://gre/modules/NetUtil.sys.mjs"
);
const { PlacesUtils } = ChromeUtils.importESModule(
"resource://gre/modules/PlacesUtils.sys.mjs"
);
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
const { NimbusTestUtils } = ChromeUtils.importESModule(
);
NimbusTestUtils.init(this);
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
DAPVisitCounter: "resource://gre/modules/DAPVisitCounter.sys.mjs",
});
const BinaryInputStream = Components.Constructor(
"@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream",
"setInputStream"
);
const PREF_LEADER = "toolkit.telemetry.dap.leader.url";
const PREF_HELPER = "toolkit.telemetry.dap.helper.url";
const TRANSITION_TYPED = PlacesUtils.history.TRANSITION_TYPED;
let server;
let server_addr;
// The dummy test server will record report sizes in this list.
let server_requests = [];
const task = {
id: "o-91EcR2kfxfAmkKPPHifXKqiH7Upm0Ilw5joB3L_pE",
vdaf: "histogram",
length: 30,
time_precision: 300,
};
function uploadHandler(request, response) {
Assert.equal(
request.getHeader("Content-Type"),
"application/dap-report",
"Wrong Content-Type header."
);
let body = new BinaryInputStream(request.bodyInputStream);
server_requests.push(body.available());
response.setStatusLine(request.httpVersion, 200);
}
add_setup(async function () {
do_get_profile();
// Set up a mock server to represent the DAP endpoints.
server = new HttpServer();
server.registerPrefixHandler("/leader_endpoint/tasks/", uploadHandler);
server.start(-1);
const i = server.identity;
server_addr = i.primaryScheme + "://" + i.primaryHost + ":" + i.primaryPort;
Services.prefs.setStringPref(PREF_LEADER, server_addr + "/leader_endpoint");
Services.prefs.setStringPref(PREF_HELPER, server_addr + "/helper_endpoint");
registerCleanupFunction(() => {
Services.prefs.clearUserPref(PREF_LEADER);
Services.prefs.clearUserPref(PREF_HELPER);
return new Promise(resolve => {
server.stop(resolve);
});
});
});
function openDatabase() {
return new Promise((resolve, reject) => {
const request = indexedDB.open("SubmissionCap", 1);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
function countRecords(store) {
return new Promise((resolve, reject) => {
const request = store.count();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async function getReportCount(db) {
const tx = db.transaction("reports", "readonly");
const store = tx.objectStore("reports");
const count = await countRecords(store);
await tx.done;
return count;
}
async function getFreqCapCount(db) {
const tx = db.transaction("freq_caps", "readonly");
const store = tx.objectStore("freq_caps");
const count = await countRecords(store);
await tx.done;
return count;
}
add_task(
{
// Requires Normandy.
skip_if: () => !AppConstants.MOZ_NORMANDY,
},
async function testVisitCounterNimbus() {
const { cleanup } = await NimbusTestUtils.setupTest();
await lazy.DAPVisitCounter.startup();
Assert.ok(
lazy.DAPVisitCounter.dapReportContoller === null,
"dapReportContoller should not exist before enrollment"
);
// Enroll experiment
let doExperimentCleanup = await NimbusTestUtils.enrollWithFeatureConfig({
featureId: "dapTelemetry",
value: {
enabled: true,
visitCountingEnabled: true,
visitCountingExperimentList: [
{
task_id: task.id,
urls: ["*://*.mozilla.org/", "*://*.example.com/"],
task_veclen: 30,
bucket: 0,
},
],
},
});
Assert.ok(
lazy.DAPVisitCounter.dapReportContoller !== null,
"dapReportContoller should be active"
);
// Verify there are no pending reports to submit
const db = await openDatabase();
let numRecords = await getReportCount(db);
Assert.ok(numRecords == 0, "Should be no pending reports");
// Visit a URL
let uri = NetUtil.newURI("http://www.mozilla.org/");
let timestamp = Date.now() * 1000;
await PlacesTestUtils.addVisits({
uri,
transition: TRANSITION_TYPED,
visitDate: timestamp,
});
//Verify there is 1 pending report to send
numRecords = await getReportCount(db);
Assert.ok(numRecords == 1, "Should be 1 pending report");
// Trigger submission of the report
await lazy.DAPVisitCounter.dapReportContoller.submit(1000, "unit-test");
// Verify there are 0 pending reports
numRecords = await getReportCount(db);
Assert.ok(numRecords == 0, "Should be 0 pending reports");
// Verify submission capping is active
numRecords = await getFreqCapCount(db);
Assert.ok(numRecords == 1, "Should be 1 cap entry");
// Unenroll experiment
await doExperimentCleanup();
Services.tm.spinEventLoopUntil(
"Wait for DAPVisitCounter to flush",
() => lazy.DAPVisitCounter.counters === null
);
//Verify IndexDB Cleanup after unenrollment
numRecords = await getFreqCapCount(db);
Assert.ok(numRecords == 0, "Should be 0 cap entries");
//Report
numRecords = await getReportCount(db);
Assert.ok(numRecords == 0, "Should be 0 pending reports");
// The 2 server requests are:
// 1. The first request is submitted on enrollment
// 2. The second request is force submitted
// 2. The third is submitted on unenrollment
Assert.deepEqual(
server_requests,
[1126, 1126, 1126],
"Should have one report on enrollment, second for triggered submission, third on unenrollment"
);
Assert.ok(
lazy.DAPVisitCounter.dapReportContoller === null,
"dapReportContoller should not exist after unenrollment"
);
await cleanup();
}
);