Files
palemoon27/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js
T

283 lines
7.9 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
/**
* This test case populates the profile with some fake stored
* pings, and checks that:
*
* 1) Pings that are considered "expired" are deleted and never sent.
* 2) Pings that are considered "overdue" trigger a send of all
* overdue and recent pings.
*/
"use strict"
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://testing-common/httpd.js", this);
Cu.import("resource://gre/modules/Promise.jsm", this);
Cu.import("resource://gre/modules/TelemetryFile.jsm", this);
Cu.import("resource://gre/modules/TelemetryPing.jsm", this);
Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
let {OS: {File, Path, Constants}} = Cu.import("resource://gre/modules/osfile.jsm", {});
XPCOMUtils.defineLazyGetter(this, "gDatareportingService",
() => Cc["@mozilla.org/datareporting/service;1"]
.getService(Ci.nsISupports)
.wrappedJSObject);
// We increment TelemetryFile's MAX_PING_FILE_AGE and
// OVERDUE_PING_FILE_AGE by 1 minute so that our test pings exceed
// those points in time, even taking into account file system imprecision.
const ONE_MINUTE_MS = 60 * 1000;
const EXPIRED_PING_FILE_AGE = TelemetryFile.MAX_PING_FILE_AGE + ONE_MINUTE_MS;
const OVERDUE_PING_FILE_AGE = TelemetryFile.OVERDUE_PING_FILE_AGE + ONE_MINUTE_MS;
const PING_SAVE_FOLDER = "saved-telemetry-pings";
const PING_TIMEOUT_LENGTH = 5000;
const EXPIRED_PINGS = 5;
const OVERDUE_PINGS = 6;
const RECENT_PINGS = 4;
const LRU_PINGS = TelemetryFile.MAX_LRU_PINGS;
const TOTAL_EXPECTED_PINGS = OVERDUE_PINGS + RECENT_PINGS;
let gHttpServer = new HttpServer();
let gCreatedPings = 0;
let gSeenPings = 0;
/**
* Creates some TelemetrySession pings for the current session and
* saves them to disk. Each ping gets a unique ID slug based on
* an incrementor.
*
* @param aNum the number of pings to create.
* @param aAge the age in milliseconds to offset from now. A value
* of 10 would make the ping 10ms older than now, for
* example.
* @returns Promise
* @resolve an Array with the created pings.
*/
function createSavedPings(aNum, aAge) {
return Task.spawn(function*(){
let pings = [];
let age = Date.now() - aAge;
for (let i = 0; i < aNum; ++i) {
let payload = TelemetrySession.getPayload();
let ping = { slug: "test-ping-" + gCreatedPings, reason: "test", payload: payload };
yield TelemetryFile.savePing(ping);
if (aAge) {
// savePing writes to the file synchronously, so we're good to
// modify the lastModifedTime now.
let file = getSavePathForPing(ping);
yield File.setDates(file, null, age);
}
gCreatedPings++;
pings.push(ping);
}
return pings;
});
}
/**
* Deletes locally saved pings in aPings if they
* exist.
*
* @param aPings an Array of pings to delete.
* @returns Promise
*/
function clearPings(aPings) {
return Task.spawn(function*() {
for (let ping of aPings) {
let path = getSavePathForPing(ping);
yield File.remove(path);
}
});
}
/**
* Returns a handle for the file that aPing should be
* stored in locally.
*
* @returns path
*/
function getSavePathForPing(aPing) {
return Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, aPing.slug);
}
/**
* Check if the number of TelemetrySession pings received by the
* HttpServer is not equal to aExpectedNum.
*
* @param aExpectedNum the number of pings we expect to receive.
*/
function assertReceivedPings(aExpectedNum) {
do_check_eq(gSeenPings, aExpectedNum);
}
/**
* Throws if any pings in aPings is saved locally.
*
* @param aPings an Array of pings to check.
* @returns Promise
*/
function assertNotSaved(aPings) {
return Task.spawn(function*() {
let saved = 0;
for (let ping of aPings) {
let file = getSavePathForPing(ping);
if (yield File.exists()) {
saved++;
}
}
if (saved > 0) {
do_throw("Found " + saved + " unexpected saved pings.");
}
});
}
/**
* Our handler function for the HttpServer that simply
* increments the gSeenPings global when it successfully
* receives and decodes a TelemetrySession payload.
*
* @param aRequest the HTTP request sent from HttpServer.
*/
function pingHandler(aRequest) {
gSeenPings++;
}
/**
* Returns a Promise that resolves when gHttpServer has been
* successfully shut down.
*
* @returns Promise
*/
function stopHttpServer() {
let deferred = Promise.defer();
gHttpServer.stop(function() {
deferred.resolve();
})
return deferred.promise;
}
/**
* Reset Telemetry state.
*/
function resetTelemetry() {
TelemetrySession.uninstall();
// Quick and dirty way to clear TelemetryFile's pendingPings
// collection, and put it back in its initial state.
let gen = TelemetryFile.popPendingPings();
for (let item of gen) {};
}
/**
* Creates and returns a TelemetryPing instance in "testing"
* mode.
*/
function startTelemetry() {
return TelemetryPing.setup();
}
function startTelemetrySession() {
return TelemetrySession.setup();
}
function run_test() {
gHttpServer.registerPrefixHandler("/submit/telemetry/", pingHandler);
gHttpServer.start(-1);
do_get_profile();
// Send the needed startup notifications to the datareporting service
// to ensure that it has been initialized.
gDatareportingService.observe(null, "app-startup", null);
gDatareportingService.observe(null, "profile-after-change", null);
Services.prefs.setBoolPref(TelemetryPing.Constants.PREF_ENABLED, true);
Services.prefs.setCharPref(TelemetryPing.Constants.PREF_SERVER,
"http://localhost:" + gHttpServer.identity.primaryPort);
run_next_test();
}
/**
* Test that pings that are considered too old are just chucked out
* immediately and never sent.
*/
add_task(function* test_expired_pings_are_deleted() {
yield startTelemetrySession();
let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
yield startTelemetry();
assertReceivedPings(0);
yield assertNotSaved(expiredPings);
yield resetTelemetry();
});
/**
* Test that really recent pings are not sent on Telemetry initialization.
*/
add_task(function* test_recent_pings_not_sent() {
yield startTelemetrySession();
let recentPings = yield createSavedPings(RECENT_PINGS);
yield startTelemetry();
assertReceivedPings(0);
yield resetTelemetry();
yield clearPings(recentPings);
});
/**
* Test that only the most recent LRU_PINGS pings are kept at startup.
*/
add_task(function* test_most_recent_pings_kept() {
yield startTelemetrySession();
let head = yield createSavedPings(LRU_PINGS);
let tail = yield createSavedPings(3, ONE_MINUTE_MS);
let pings = head.concat(tail);
yield startTelemetry();
let gen = TelemetryFile.popPendingPings();
for (let item of gen) {
for (let p of tail) {
do_check_neq(p.slug, item.slug);
}
}
assertNotSaved(tail);
yield resetTelemetry();
yield clearPings(pings);
});
/**
* Create some recent, expired and overdue pings. The overdue pings should
* trigger a send of all recent and overdue pings, but the expired pings
* should just be deleted.
*/
add_task(function* test_overdue_pings_trigger_send() {
yield startTelemetrySession();
let recentPings = yield createSavedPings(RECENT_PINGS);
let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
let overduePings = yield createSavedPings(OVERDUE_PINGS, OVERDUE_PING_FILE_AGE);
yield startTelemetry();
assertReceivedPings(TOTAL_EXPECTED_PINGS);
yield assertNotSaved(recentPings);
yield assertNotSaved(expiredPings);
yield assertNotSaved(overduePings);
yield resetTelemetry();
});
add_task(function* teardown() {
yield stopHttpServer();
});