Skip to content

Commit

Permalink
Bug 1120363 - Break up Telemetry sessions on environment changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
rmottola committed Jul 18, 2019
1 parent 93eb9ca commit a7c8d70
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 1 deletion.
14 changes: 13 additions & 1 deletion toolkit/components/telemetry/TelemetryEnvironment.jsm
Expand Up @@ -34,7 +34,7 @@ this.TelemetryEnvironment = {
return;
}

this._log = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, "TelemetryEnvironment::");
this._configureLog();
this._log.trace("init");
this._shutdown = false;
},
Expand All @@ -55,12 +55,22 @@ this.TelemetryEnvironment = {
yield this._collectTask;
}),

_configureLog: function () {
if (this._log) {
return;
}
this._log = Log.repository.getLoggerWithMessagePrefix(
LOGGER_NAME, "TelemetryEnvironment::");
},

/**
* Register a listener for environment changes.
* It's fine to call this on an unitialized TelemetryEnvironment.
* @param name The name of the listener - good for debugging purposes.
* @param listener A JS callback function.
*/
registerChangeListener: function (name, listener) {
this._configureLog();
this._log.trace("registerChangeListener for " + name);
if (this._shutdown) {
this._log.warn("registerChangeListener - already shutdown")
Expand All @@ -71,9 +81,11 @@ this.TelemetryEnvironment = {

/**
* Unregister from listening to environment changes.
* It's fine to call this on an unitialized TelemetryEnvironment.
* @param name The name of the listener to remove.
*/
unregisterChangeListener: function (name) {
this._configureLog();
this._log.trace("unregisterChangeListener for " + name);
if (this._shutdown) {
this._log.warn("registerChangeListener - already shutdown")
Expand Down
22 changes: 22 additions & 0 deletions toolkit/components/telemetry/TelemetrySession.jsm
Expand Up @@ -36,6 +36,9 @@ const REASON_SAVED_SESSION = "saved-session";
const REASON_IDLE_DAILY = "idle-daily";
const REASON_GATHER_PAYLOAD = "gather-payload";
const REASON_TEST_PING = "test-ping";
const REASON_ENVIRONMENT_CHANGE = "environment-change";

const ENVIRONMENT_CHANGE_LISTENER = "TelemetrySession::onEnvironmentChange";

const SEC_IN_ONE_DAY = 24 * 60 * 60;
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
Expand Down Expand Up @@ -114,6 +117,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
"resource://gre/modules/UITelemetry.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
"resource://gre/modules/UpdateChannel.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
"resource://gre/modules/TelemetryEnvironment.jsm");

function generateUUID() {
let str = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
Expand Down Expand Up @@ -1037,6 +1042,9 @@ let Impl = {
Telemetry.asyncFetchTelemetryData(function () {});
this._rescheduleDailyTimer();

TelemetryEnvironment.registerChangeListener(ENVIRONMENT_CHANGE_LISTENER,
() => this._onEnvironmentChange());

deferred.resolve();

}.bind(this), testing ? TELEMETRY_TEST_DELAY : TELEMETRY_DELAY);
Expand Down Expand Up @@ -1305,6 +1313,8 @@ let Impl = {
* can send pings or not, which is used for testing.
*/
shutdown: function(testing = false) {
TelemetryEnvironment.unregisterChangeListener(ENVIRONMENT_CHANGE_LISTENER);

if (this._dailyTimerId) {
Policy.clearDailyTimeout(this._dailyTimerId);
this._dailyTimerId = null;
Expand Down Expand Up @@ -1359,6 +1369,18 @@ let Impl = {
return promise;
},

_onEnvironmentChange: function() {
this._log.trace("_onEnvironmentChange");
let payload = this.getSessionPayload(REASON_ENVIRONMENT_CHANGE, true);

let options = {
retentionDays: RETENTION_DAYS,
addClientId: true,
addEnvironment: true,
};
let promise = TelemetryPing.send(PING_TYPE_MAIN, payload, options);
},

_isClassicReason: function(reason) {
const classicReasons = [
REASON_SAVED_SESSION,
Expand Down
71 changes: 71 additions & 0 deletions toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
Expand Up @@ -20,6 +20,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://gre/modules/TelemetryPing.jsm", this);
Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
Cu.import("resource://gre/modules/TelemetryFile.jsm", this);
Cu.import("resource://gre/modules/TelemetryEnvironment.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
Cu.import("resource://gre/modules/Promise.jsm", this);
Cu.import("resource://gre/modules/Preferences.jsm");
Expand All @@ -31,6 +32,7 @@ const PING_TYPE_MAIN = "main";
const REASON_SAVED_SESSION = "saved-session";
const REASON_TEST_PING = "test-ping";
const REASON_DAILY = "daily";
const REASON_ENVIRONMENT_CHANGE = "environment-change";

const PLATFORM_VERSION = "1.9.2";
const APP_VERSION = "1";
Expand Down Expand Up @@ -112,6 +114,11 @@ function futureDate(date, offset) {
return new Date(date.getTime() + offset);
}

function fakeNow(date) {
let session = Cu.import("resource://gre/modules/TelemetrySession.jsm");
session.Policy.now = () => date;
}

function registerPingHandler(handler) {
gHttpServer.registerPrefixHandler("/submit/telemetry/",
wrapWithExceptionHandler(handler));
Expand Down Expand Up @@ -824,6 +831,70 @@ add_task(function* test_dailyCollection() {
Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["b"].sum, 1);
});

add_task(function* test_environmentChange() {
let now = new Date(2040, 1, 1, 12, 0, 0);
let nowDay = new Date(2040, 1, 1, 0, 0, 0);
let timerCallback = null;
let timerDelay = null;

gRequestIterator = Iterator(new Request());

fakeNow(now);
fakeDailyTimers(() => {}, () => {});

const PREF_TEST = "toolkit.telemetry.test.pref1";
Preferences.reset(PREF_TEST);
let prefsToWatch = {};
prefsToWatch[PREF_TEST] = TelemetryEnvironment.RECORD_PREF_VALUE;

// Setup.
yield TelemetrySession.setup();
TelemetryPing.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
TelemetryEnvironment._watchPreferences(prefsToWatch);

// Set histograms to expected state.
const COUNT_ID = "TELEMETRY_TEST_COUNT";
const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT";
const count = Telemetry.getHistogramById(COUNT_ID);
const keyed = Telemetry.getKeyedHistogramById(KEYED_ID);

count.clear();
keyed.clear();
count.add(1);
keyed.add("a", 1);
keyed.add("b", 1);

// Trigger and collect environment-change ping.
Preferences.set(PREF_TEST, 1);
let request = yield gRequestIterator.next();
Assert.ok(!!request);
let ping = decodeRequestPayload(request);

Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.environment.settings.userPrefs[PREF_TEST], 1);
Assert.equal(ping.payload.info.reason, REASON_ENVIRONMENT_CHANGE);
let subsessionStartDate = new Date(ping.payload.info.subsessionStartDate);
Assert.equal(subsessionStartDate.toISOString(), nowDay.toISOString());

Assert.equal(ping.payload.histograms[COUNT_ID].sum, 1);
Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["a"].sum, 1);

// Trigger and collect another ping. The histograms should be reset.
Preferences.set(PREF_TEST, 2);
request = yield gRequestIterator.next();
Assert.ok(!!request);
ping = decodeRequestPayload(request);

Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.environment.settings.userPrefs[PREF_TEST], 2);
Assert.equal(ping.payload.info.reason, REASON_ENVIRONMENT_CHANGE);
subsessionStartDate = new Date(ping.payload.info.subsessionStartDate);
Assert.equal(subsessionStartDate.toISOString(), nowDay.toISOString());

Assert.equal(ping.payload.histograms[COUNT_ID].sum, 0);
Assert.deepEqual(ping.payload.keyedHistograms[KEYED_ID], {});
});

// Checks that an expired histogram file is deleted when loaded.
add_task(function* test_runOldPingFile() {
let histogramsFile = getSavedPingFile("old-histograms.dat");
Expand Down

0 comments on commit a7c8d70

Please sign in to comment.