/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; 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"; 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" ); // 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(); Services.prefs.setBoolPref( "toolkit.telemetry.testing.overrideProductsCheck", true ); run_next_test(); } 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"], ]; // Clear the scalar snapshot from previous tests. Services.telemetry.getSnapshotForScalars("main", true); // If there is no DRS file, and no cached id, we should get a new client ID. await ClientID._reset(); Services.prefs.clearUserPref(PREF_CACHED_CLIENTID); await OS.File.remove(drsPath, { ignoreAbsent: true }); let clientID = await ClientID.getClientID(); Assert.equal(typeof clientID, "string"); Assert.ok(uuidRegex.test(clientID)); let snapshot = Services.telemetry.getSnapshotForScalars("main", true).parent; Assert.equal(snapshot["telemetry.generated_new_client_id"], true); // No file to read means no value to mismatch with pref. Assert.ok(!("telemetry.loaded_client_id_doesnt_match_pref" in snapshot)); Assert.equal(snapshot["telemetry.state_file_read_errors"], 1); Assert.ok(!("telemetry.using_pref_client_id" in snapshot)); // We should be guarded against invalid DRS json. await ClientID._reset(); Services.prefs.clearUserPref(PREF_CACHED_CLIENTID); 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)); snapshot = Services.telemetry.getSnapshotForScalars("main", true).parent; Assert.equal(snapshot["telemetry.generated_new_client_id"], true); // Invalid file means no value to mismatch with pref. Assert.ok(!("telemetry.loaded_client_id_doesnt_match_pref" in snapshot)); Assert.equal(snapshot["telemetry.state_file_read_errors"], 1); Assert.ok(!("telemetry.using_pref_client_id" in snapshot)); // If the DRS data is broken, we should end up with the cached ID. let oldClientID = clientID; for (let [invalidID] of invalidIDs) { await ClientID._reset(); await CommonUtils.writeJSON({ clientID: invalidID }, drsPath); clientID = await ClientID.getClientID(); Assert.equal(clientID, oldClientID); snapshot = Services.telemetry.getSnapshotForScalars("main", true).parent; Assert.ok(!("telemetry.generated_new_client_id" in snapshot)); Assert.equal(snapshot["telemetry.loaded_client_id_doesnt_match_pref"], 1); Assert.ok(!("telemetry.state_file_read_errors" in snapshot)); Assert.equal(snapshot["telemetry.using_pref_client_id"], 1); } // Test that valid DRS actually works. const validClientID = "5afebd62-a33c-416c-b519-5c60fb988e8e"; await ClientID._reset(); await CommonUtils.writeJSON({ clientID: validClientID }, drsPath); clientID = await ClientID.getClientID(); Assert.equal(clientID, validClientID); snapshot = Services.telemetry.getSnapshotForScalars("main", true).parent; Assert.ok(!("telemetry.generated_new_client_id" in snapshot)); Assert.equal(snapshot["telemetry.loaded_client_id_doesnt_match_pref"], 1); Assert.ok(!("telemetry.state_file_read_errors" in snapshot)); Assert.ok(!("telemetry.using_pref_client_id" in snapshot)); // Test that reloading a valid DRS works. await ClientID._reset(); Services.prefs.clearUserPref(PREF_CACHED_CLIENTID); clientID = await ClientID.getClientID(); Assert.equal(clientID, validClientID); // snapshot may be empty if no other scalars are recorded. snapshot = Services.telemetry.getSnapshotForScalars("main", true).parent || {}; Assert.ok(!("telemetry.generated_new_client_id" in snapshot)); Assert.ok(!("telemetry.loaded_client_id_doesnt_match_pref" in snapshot)); Assert.ok(!("telemetry.state_file_read_errors" in snapshot)); Assert.ok(!("telemetry.using_pref_client_id" in snapshot)); // 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_setCanaryClientID() { const KNOWN_UUID = "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0"; await ClientID._reset(); // We should be able to set a valid UUID await ClientID.setCanaryClientID(); let clientID = await ClientID.getClientID(); Assert.equal(KNOWN_UUID, clientID); }); add_task(async function test_removeParallelGet() { // We should get a valid UUID after reset await ClientID.removeClientID(); let firstClientID = await ClientID.getClientID(); // We should get the same ID twice when requesting it in parallel to a reset. let promiseRemoveClientID = ClientID.removeClientID(); let p = ClientID.getClientID(); let newClientID = await ClientID.getClientID(); await promiseRemoveClientID; 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." ); });