diff options
Diffstat (limited to 'toolkit/components/telemetry/tests/unit/test_client_id.js')
-rw-r--r-- | toolkit/components/telemetry/tests/unit/test_client_id.js | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/toolkit/components/telemetry/tests/unit/test_client_id.js b/toolkit/components/telemetry/tests/unit/test_client_id.js new file mode 100644 index 0000000000..96b22546d1 --- /dev/null +++ b/toolkit/components/telemetry/tests/unit/test_client_id.js @@ -0,0 +1,372 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +if (AppConstants.MOZ_GLEAN) { + Cu.importGlobalProperties(["Glean"]); +} +const { ClientID } = ChromeUtils.import("resource://gre/modules/ClientID.jsm"); +const { CommonUtils } = ChromeUtils.import( + "resource://services-common/utils.js" +); +const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); + +const PREF_CACHED_CLIENTID = "toolkit.telemetry.cachedClientID"; + +const SCALAR_DELETION_REQUEST_ECOSYSTEM_CLIENT_ID = + "deletion.request.ecosystem_client_id"; + +var drsPath; + +const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +function run_test() { + do_get_profile(); + drsPath = OS.Path.join( + OS.Constants.Path.profileDir, + "datareporting", + "state.json" + ); + + if (AppConstants.MOZ_GLEAN) { + // We need to ensure FOG is initialized, otherwise operations will be stuck in the pre-init queue. + let FOG = Cc["@mozilla.org/toolkit/glean;1"].createInstance(Ci.nsIFOG); + FOG.initializeFOG(); + } + + run_next_test(); +} + +add_task(async function test_ecosystemClientID() { + await ClientID._reset(); + Assert.ok(!ClientID.getCachedEcosystemClientID()); + let ecosystemClientID = await ClientID.getEcosystemClientID(); + Assert.equal(typeof ecosystemClientID, "string"); + Assert.equal(ClientID.getCachedEcosystemClientID(), ecosystemClientID); + + let clientID = await ClientID.getClientID(); + await ClientID._reset(); + await OS.File.writeAtomic( + drsPath, + JSON.stringify({ + clientID, + }), + { + encoding: "utf-8", + tmpPath: drsPath + ".tmp", + } + ); + + let newClientID = await ClientID.getClientID(); + Assert.equal(newClientID, clientID); + + let newEcosystemClientID = await ClientID.getEcosystemClientID(); + Assert.notEqual(newEcosystemClientID, ecosystemClientID); +}); + +add_task(async function test_client_id() { + const invalidIDs = [ + [-1, "setIntPref"], + [0.5, "setIntPref"], + ["INVALID-UUID", "setStringPref"], + [true, "setBoolPref"], + ["", "setStringPref"], + ["3d1e1560-682a-4043-8cf2-aaaaaaaaaaaZ", "setStringPref"], + ]; + + // If there is no DRS file, we should get a new client ID. + await ClientID._reset(); + let clientID = await ClientID.getClientID(); + Assert.equal(typeof clientID, "string"); + Assert.ok(uuidRegex.test(clientID)); + if (AppConstants.MOZ_GLEAN) { + Assert.equal( + Glean.fogValidation.legacyTelemetryClientId.testGetValue( + "fog-validation" + ), + clientID + ); + } + + // We should be guarded against invalid DRS json. + await ClientID._reset(); + await OS.File.writeAtomic(drsPath, "abcd", { + encoding: "utf-8", + tmpPath: drsPath + ".tmp", + }); + clientID = await ClientID.getClientID(); + Assert.equal(typeof clientID, "string"); + Assert.ok(uuidRegex.test(clientID)); + if (AppConstants.MOZ_GLEAN) { + Assert.equal( + Glean.fogValidation.legacyTelemetryClientId.testGetValue( + "fog-validation" + ), + clientID + ); + } + + // If the DRS data is broken, we should end up with a new client ID. + for (let [invalidID] of invalidIDs) { + await ClientID._reset(); + await CommonUtils.writeJSON({ clientID: invalidID }, drsPath); + clientID = await ClientID.getClientID(); + Assert.equal(typeof clientID, "string"); + Assert.ok(uuidRegex.test(clientID)); + if (AppConstants.MOZ_GLEAN) { + Assert.equal( + Glean.fogValidation.legacyTelemetryClientId.testGetValue( + "fog-validation" + ), + clientID + ); + } + } + + // Assure that cached IDs are being checked for validity. + for (let [invalidID, prefFunc] of invalidIDs) { + await ClientID._reset(); + Services.prefs[prefFunc](PREF_CACHED_CLIENTID, invalidID); + let cachedID = ClientID.getCachedClientID(); + Assert.strictEqual( + cachedID, + null, + "ClientID should ignore invalid cached IDs" + ); + Assert.ok( + !Services.prefs.prefHasUserValue(PREF_CACHED_CLIENTID), + "ClientID should reset invalid cached IDs" + ); + Assert.ok( + Services.prefs.getPrefType(PREF_CACHED_CLIENTID) == + Ci.nsIPrefBranch.PREF_INVALID, + "ClientID should reset invalid cached IDs" + ); + } +}); + +add_task(async function test_setCanaryClientIDs() { + const KNOWN_UUID = "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0"; + + await ClientID._reset(); + + // We should be able to set a valid UUID + await ClientID.setCanaryClientIDs(); + let clientID = await ClientID.getClientID(); + Assert.equal(KNOWN_UUID, clientID); + if (AppConstants.MOZ_GLEAN) { + Assert.equal( + Glean.fogValidation.legacyTelemetryClientId.testGetValue( + "fog-validation" + ), + clientID + ); + } +}); + +add_task(async function test_resetEcosystemClientID() { + await ClientID._reset(); + + let firstClientID = await ClientID.getClientID(); + let firstEcosystemClientID = await ClientID.getEcosystemClientID(); + Assert.ok(firstClientID); + if (AppConstants.MOZ_GLEAN) { + Assert.equal( + Glean.fogValidation.legacyTelemetryClientId.testGetValue( + "fog-validation" + ), + firstClientID + ); + } + Assert.ok(firstEcosystemClientID); + + // We should reset the ecosystem client id, but not the main client id. + await ClientID.resetEcosystemClientID(); + let secondClientID = await ClientID.getClientID(); + let secondEcosystemClientID = await ClientID.getEcosystemClientID(); + Assert.equal(firstClientID, secondClientID); + if (AppConstants.MOZ_GLEAN) { + Assert.equal( + Glean.fogValidation.legacyTelemetryClientId.testGetValue( + "fog-validation" + ), + firstClientID + ); + } + Assert.notEqual(firstEcosystemClientID, secondEcosystemClientID); + + // The new id should have been persisted to disk. + await ClientID._reset(); + let thirdClientID = await ClientID.getClientID(); + let thirdEcosystemClientID = await ClientID.getEcosystemClientID(); + Assert.equal(thirdClientID, secondClientID); + Assert.equal(thirdEcosystemClientID, secondEcosystemClientID); +}); + +add_task(async function test_removeClientIDs() { + // We should get a valid UUID after reset + await ClientID._reset(); + let firstClientID = await ClientID.getClientID(); + let firstEcosystemClientID = await ClientID.getEcosystemClientID(); + Assert.equal(typeof firstClientID, "string"); + Assert.equal(typeof firstEcosystemClientID, "string"); + Assert.ok(uuidRegex.test(firstClientID)); + Assert.ok(uuidRegex.test(firstEcosystemClientID)); + if (AppConstants.MOZ_GLEAN) { + Assert.equal( + Glean.fogValidation.legacyTelemetryClientId.testGetValue( + "fog-validation" + ), + firstClientID + ); + } + + await ClientID.removeClientIDs(); + + if ( + AppConstants.platform != "android" && + AppConstants.MOZ_APP_NAME != "thunderbird" + ) { + // We don't record the old ecosystem client ID on Android or Thunderbird, + // since the FxA and telemetry infrastructure is different there. + let prefClientID = Services.prefs.getStringPref(PREF_CACHED_CLIENTID, null); + let scalarsDeletionRequest = Services.telemetry.getSnapshotForScalars( + "deletion-request" + ); + Assert.ok(!prefClientID); + Assert.ok( + !scalarsDeletionRequest.parent?.[ + SCALAR_DELETION_REQUEST_ECOSYSTEM_CLIENT_ID + ] + ); + } + + // When resetting again we should get a new ID + let nextClientID = await ClientID.getClientID(); + let nextEcosystemClientID = await ClientID.getEcosystemClientID(); + Assert.equal(typeof nextClientID, "string"); + Assert.equal(typeof nextEcosystemClientID, "string"); + Assert.ok(uuidRegex.test(nextClientID)); + Assert.ok(uuidRegex.test(nextEcosystemClientID)); + Assert.notEqual( + firstClientID, + nextClientID, + "After reset client ID should be different." + ); + Assert.notEqual( + firstEcosystemClientID, + nextEcosystemClientID, + "After reset ecosystem client ID should be different." + ); + + let cachedID = ClientID.getCachedClientID(); + Assert.equal(nextClientID, cachedID); + + let cachedEcosystemID = ClientID.getCachedEcosystemClientID(); + Assert.equal(nextEcosystemClientID, cachedEcosystemID); + + let prefClientID = Services.prefs.getStringPref(PREF_CACHED_CLIENTID, null); + Assert.equal(nextClientID, prefClientID); + + if ( + AppConstants.platform != "android" && + AppConstants.MOZ_APP_NAME != "thunderbird" + ) { + let scalarsDeletionRequest = Services.telemetry.getSnapshotForScalars( + "deletion-request" + ); + Assert.equal( + nextEcosystemClientID, + scalarsDeletionRequest.parent[SCALAR_DELETION_REQUEST_ECOSYSTEM_CLIENT_ID] + ); + } +}); + +add_task(async function test_removeParallelGet() { + // We should get a valid UUID after reset + await ClientID.removeClientIDs(); + let firstClientID = await ClientID.getClientID(); + + // We should get the same ID twice when requesting it in parallel to a reset. + let promiseRemoveClientIDs = ClientID.removeClientIDs(); + let p = ClientID.getClientID(); + let newClientID = await ClientID.getClientID(); + await promiseRemoveClientIDs; + let otherClientID = await p; + + Assert.notEqual( + firstClientID, + newClientID, + "After reset client ID should be different." + ); + Assert.equal( + newClientID, + otherClientID, + "Getting the client ID in parallel to a reset should give the same id." + ); + if (AppConstants.MOZ_GLEAN) { + Assert.equal( + Glean.fogValidation.legacyTelemetryClientId.testGetValue( + "fog-validation" + ), + newClientID + ); + } +}); + +add_task( + { + skip_if: () => AppConstants.platform != "android", + }, + async function test_FennecCanaryDetect() { + const KNOWN_UUID = "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0"; + + // We should get a valid UUID after reset + await ClientID.removeClientIDs(); + let firstClientID = await ClientID.getClientID(); + Assert.notEqual(KNOWN_UUID, firstClientID, "Client ID should be random."); + + // Set the canary client ID. + await ClientID.setCanaryClientIDs(); + Assert.equal( + KNOWN_UUID, + await ClientID.getClientID(), + "Client ID should be known canary." + ); + + await ClientID.removeClientIDs(); + let newClientID = await ClientID.getClientID(); + Assert.notEqual( + KNOWN_UUID, + newClientID, + "After reset Client ID should be random." + ); + Assert.notEqual( + firstClientID, + newClientID, + "After reset Client ID should be new." + ); + Assert.ok( + ClientID.wasCanaryClientID(), + "After reset we should have detected a canary client ID" + ); + + await ClientID.removeClientIDs(); + let clientID = await ClientID.getClientID(); + Assert.notEqual( + KNOWN_UUID, + clientID, + "After reset Client ID should be random." + ); + Assert.notEqual( + newClientID, + clientID, + "After reset Client ID should be new." + ); + Assert.ok( + !ClientID.wasCanaryClientID(), + "After reset we should not have detected a canary client ID" + ); + } +); |