Skip to content
This repository has been archived by the owner on Nov 7, 2018. It is now read-only.

Commit

Permalink
Bug 1495792: Add a profile first-run time to the telemetry environmen…
Browse files Browse the repository at this point in the history
…t. r=janerik

Differential Revision: https://phabricator.services.mozilla.com/D8464
  • Loading branch information
Mossop committed Oct 15, 2018
1 parent fd1aad3 commit 61c64ca
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 4 deletions.
5 changes: 5 additions & 0 deletions toolkit/components/telemetry/app/TelemetryEnvironment.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -1447,13 +1447,18 @@ EnvironmentCache.prototype = {

let creationDate = await profileAccessor.created;
let resetDate = await profileAccessor.reset;
let firstUseDate = await profileAccessor.firstUse;

this._currentEnvironment.profile.creationDate =
Utils.millisecondsToDays(creationDate);
if (resetDate) {
this._currentEnvironment.profile.resetDate =
Utils.millisecondsToDays(resetDate);
}
if (firstUseDate) {
this._currentEnvironment.profile.firstUseDate =
Utils.millisecondsToDays(firstUseDate);
}
},

/**
Expand Down
15 changes: 15 additions & 0 deletions toolkit/components/telemetry/docs/data/environment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Structure:
profile: {
creationDate: <integer>, // integer days since UNIX epoch, e.g. 16446
resetDate: <integer>, // integer days since UNIX epoch, e.g. 16446 - optional
firstUseDate: <integer>, // integer days since UNIX epoch, e.g. 16446 - optional
},
partner: { // This section may not be immediately available on startup
distributionId: <string>, // pref "distribution.id", null on failure
Expand Down Expand Up @@ -376,6 +377,20 @@ It's read from a file-stored timestamp from the client's profile directory.
The oldest creation or modification date of the scanned files is then taken to be the profile creation date.
This has been shown to sometimes be inaccurate (`bug 1449739 <https://bugzilla.mozilla.org/show_bug.cgi?id=1449739>`_).

resetDate
~~~~~~~~~~~~

The time of the last reset time for the profile. If the profile has never been
reset this field will not be present.
It's read from a file-stored timestamp from the client's profile directory.

firstUseDate
~~~~~~~~~~~~

The time of the first use of profile. If this is an old profile where we can't
determine this this field will not be present.
It's read from a file-stored timestamp from the client's profile directory.

partner
-------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ const GFX_DEVICE_ID = "0x1234";
// The profile reset date, in milliseconds (Today)
const PROFILE_RESET_DATE_MS = Date.now();
// The profile creation date, in milliseconds (Yesterday).
const PROFILE_CREATION_DATE_MS = PROFILE_RESET_DATE_MS - MILLISECONDS_PER_DAY;
const PROFILE_FIRST_USE_MS = PROFILE_RESET_DATE_MS - MILLISECONDS_PER_DAY;
const PROFILE_CREATION_DATE_MS = PROFILE_FIRST_USE_MS - MILLISECONDS_PER_DAY;

const FLASH_PLUGIN_NAME = "Shockwave Flash";
const FLASH_PLUGIN_DESC = "A mock flash plugin";
Expand Down Expand Up @@ -298,6 +299,7 @@ function spoofProfileReset() {
return CommonUtils.writeJSON({
created: PROFILE_CREATION_DATE_MS,
reset: PROFILE_RESET_DATE_MS,
firstUse: PROFILE_FIRST_USE_MS,
}, OS.Path.join(OS.Constants.Path.profileDir, "times.json"));
}

Expand Down Expand Up @@ -453,6 +455,7 @@ function checkProfileSection(data) {
Assert.ok("profile" in data, "There must be a profile section in Environment.");
Assert.equal(data.profile.creationDate, truncateToDays(PROFILE_CREATION_DATE_MS));
Assert.equal(data.profile.resetDate, truncateToDays(PROFILE_RESET_DATE_MS));
Assert.equal(data.profile.firstUseDate, truncateToDays(PROFILE_FIRST_USE_MS));
}

function checkPartnerSection(data, isInitial) {
Expand Down
22 changes: 21 additions & 1 deletion toolkit/modules/ProfileAge.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ class ProfileAgeImpl {
this.profilePath = profile || OS.Constants.Path.profileDir;
this._times = times;
this._log = Log.repository.getLogger("Toolkit.ProfileAge");

if ("firstUse" in this._times && this._times.firstUse === null) {
// Indicates that this is a new profile that needs a first use timestamp.
this._times.firstUse = Date.now();
this.writeTimes();
}
}

/**
Expand Down Expand Up @@ -122,6 +128,17 @@ class ProfileAgeImpl {
return this._created;
}

/**
* Returns a promise to the time of first use of the profile. This may be
* undefined if the first use time is unknown.
*/
get firstUse() {
if ("firstUse" in this._times) {
return Promise.resolve(this._times.firstUse);
}
return Promise.resolve(undefined);
}

/**
* Return a promise representing the writing the current times to the profile.
*/
Expand Down Expand Up @@ -176,7 +193,10 @@ async function initProfileAge(profile) {
let times = await CommonUtils.readJSON(timesPath);
return new ProfileAgeImpl(profile, times || {});
} catch (e) {
return new ProfileAgeImpl(profile, {});
// Indicates that the file was missing or broken. In this case we want to
// record the first use time as now. The constructor will set this and write
// times.json
return new ProfileAgeImpl(profile, { firstUse: null });
}
}

Expand Down
18 changes: 17 additions & 1 deletion toolkit/modules/tests/xpcshell/test_ProfileAge.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ ChromeUtils.import("resource://gre/modules/osfile.jsm");
ChromeUtils.import("resource://services-common/utils.js");

const gProfD = do_get_profile();
let ID = 0;

// Creates a unique profile directory to use for a test.
function withDummyProfile(task) {
return async () => {
let profile = OS.Path.join(gProfD.path, "" + Math.floor(Math.random() * 100));
let profile = OS.Path.join(gProfD.path, "" + ID++);
await OS.File.makeDir(profile);
await task(profile);
await OS.File.removeDir(profile);
Expand All @@ -18,6 +19,7 @@ add_task(withDummyProfile(async (profile) => {
let times = await ProfileAge(profile);
Assert.ok((await times.created) > 0, "We can't really say what this will be, just assume if it is a number it's ok.");
Assert.equal(await times.reset, undefined, "Reset time is undefined in a new profile");
Assert.ok((await times.firstUse) <= Date.now(), "Should have initialised a first use time.");
}));

add_task(withDummyProfile(async (profile) => {
Expand All @@ -30,6 +32,7 @@ add_task(withDummyProfile(async (profile) => {

let times = await ProfileAge(profile);
Assert.equal((await times.created), CREATED_TIME, "Should have seen the right profile time.");
Assert.equal((await times.firstUse), undefined, "Should be no first use time.");

let times2 = await ProfileAge(profile);
Assert.equal(times, times2, "Should have got the same instance.");
Expand Down Expand Up @@ -57,7 +60,20 @@ add_task(withDummyProfile(async (profile) => {
]);

let results = await CommonUtils.readJSON(OS.Path.join(profile, "times.json"));
delete results.firstUse;
Assert.deepEqual(results, {
reset: RESET_TIME2,
}, "Should have seen the right results.");
}));

add_task(withDummyProfile(async (profile) => {
const CREATED_TIME = Date.now() - 1000;

CommonUtils.writeJSON({
created: CREATED_TIME,
firstUse: null,
}, OS.Path.join(profile, "times.json"));

let times = await ProfileAge(profile);
Assert.ok((await times.firstUse) <= Date.now(), "Should have initialised a first use time.");
}));
2 changes: 1 addition & 1 deletion toolkit/profile/nsToolkitProfileService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ nsToolkitProfileService::CreateTimesInternal(nsIFile* aProfileDir)
rv = creationLog->OpenNSPRFileDesc(PR_WRONLY, 0700, &writeFile);
NS_ENSURE_SUCCESS(rv, rv);

PR_fprintf(writeFile, "{\n\"created\": %lld\n}\n", msec);
PR_fprintf(writeFile, "{\n\"created\": %lld,\n\"firstUse\": null\n}\n", msec);
PR_Close(writeFile);
return NS_OK;
}
Expand Down

0 comments on commit 61c64ca

Please sign in to comment.