tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_InstallationTelemetry.js (6463B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/
      3 */
      4 "use strict";
      5 
      6 const { AppConstants } = ChromeUtils.importESModule(
      7  "resource://gre/modules/AppConstants.sys.mjs"
      8 );
      9 const { BrowserUsageTelemetry } = ChromeUtils.importESModule(
     10  "resource:///modules/BrowserUsageTelemetry.sys.mjs"
     11 );
     12 const { TelemetryTestUtils } = ChromeUtils.importESModule(
     13  "resource://testing-common/TelemetryTestUtils.sys.mjs"
     14 );
     15 ChromeUtils.defineESModuleGetters(this, {
     16  FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
     17 });
     18 
     19 const TIMESTAMP_PREF = "app.installation.timestamp";
     20 
     21 function encodeUtf16(str) {
     22  const buf = new ArrayBuffer(str.length * 2);
     23  const utf16 = new Uint16Array(buf);
     24  for (let i = 0; i < str.length; i++) {
     25    utf16[i] = str.charCodeAt(i);
     26  }
     27  return new Uint8Array(buf);
     28 }
     29 
     30 // Returns Promise
     31 function writeJsonUtf16(fileName, obj) {
     32  const str = JSON.stringify(obj);
     33  return IOUtils.write(fileName, encodeUtf16(str));
     34 }
     35 
     36 async function runReport(
     37  dataFile,
     38  installType,
     39  { clearTS, setTS, assertRejects, expectExtra, expectTS, msixPrefixes }
     40 ) {
     41  // Setup timestamp
     42  if (clearTS) {
     43    Services.prefs.clearUserPref(TIMESTAMP_PREF);
     44  }
     45  if (typeof setTS == "string") {
     46    Services.prefs.setStringPref(TIMESTAMP_PREF, setTS);
     47  }
     48 
     49  // Init events
     50  Services.telemetry.clearEvents();
     51 
     52  // Exercise reportInstallationTelemetry
     53  if (typeof assertRejects != "undefined") {
     54    await Assert.rejects(
     55      BrowserUsageTelemetry.reportInstallationTelemetry(dataFile),
     56      assertRejects
     57    );
     58  } else if (!msixPrefixes) {
     59    await BrowserUsageTelemetry.reportInstallationTelemetry(dataFile);
     60  } else {
     61    await BrowserUsageTelemetry.reportInstallationTelemetry(
     62      dataFile,
     63      msixPrefixes
     64    );
     65  }
     66 
     67  // Check events
     68  TelemetryTestUtils.assertEvents(
     69    expectExtra
     70      ? [{ object: installType, value: null, extra: expectExtra }]
     71      : [],
     72    { category: "installation", method: "first_seen" }
     73  );
     74 
     75  // Check timestamp
     76  if (typeof expectTS == "string") {
     77    Assert.equal(expectTS, Services.prefs.getStringPref(TIMESTAMP_PREF));
     78  }
     79 }
     80 
     81 let condition = {
     82  skip_if: () =>
     83    AppConstants.platform !== "win" ||
     84    !Services.sysinfo.getProperty("hasWinPackageId"),
     85 };
     86 add_task(condition, async function testInstallationTelemetryMSIX() {
     87  // Unfortunately, we have no way to inject different installation ping data
     88  // into the system in a way that doesn't just completely override the code
     89  // under test - so other than a basic test of the happy path, there's
     90  // nothing we can do here.
     91  let msixExtra = {
     92    version: AppConstants.MOZ_APP_VERSION,
     93    build_id: AppConstants.MOZ_BULIDID,
     94    admin_user: "false",
     95    from_msi: "false",
     96    silent: "false",
     97    default_path: "true",
     98    install_existed: "false",
     99    other_inst: "false",
    100    other_msix_inst: "false",
    101    profdir_existed: "false",
    102  };
    103 
    104  await runReport("fake", "msix", {
    105    expectExtra: msixExtra,
    106  });
    107 });
    108 condition = {
    109  skip_if: () =>
    110    AppConstants.platform === "win" &&
    111    Services.sysinfo.getProperty("hasWinPackageId"),
    112 };
    113 add_task(condition, async function testInstallationTelemetry() {
    114  let dataFilePath = await IOUtils.createUniqueFile(
    115    Services.dirsvc.get("TmpD", Ci.nsIFile).path,
    116    "installation-telemetry-test-data" + Math.random() + ".json"
    117  );
    118  let dataFile = new FileUtils.File(dataFilePath);
    119 
    120  registerCleanupFunction(async () => {
    121    try {
    122      await IOUtils.remove(dataFilePath);
    123    } catch (ex) {
    124      // Ignore remove failure, file may not exist by now
    125    }
    126 
    127    Services.prefs.clearUserPref(TIMESTAMP_PREF);
    128  });
    129 
    130  // Test with normal stub data
    131  let stubData = {
    132    version: "99.0abc",
    133    build_id: "123",
    134    installer_type: "stub",
    135    admin_user: true,
    136    install_existed: false,
    137    profdir_existed: false,
    138    install_timestamp: "0",
    139  };
    140  let stubExtra = {
    141    version: "99.0abc",
    142    build_id: "123",
    143    admin_user: "true",
    144    install_existed: "false",
    145    other_inst: "false",
    146    other_msix_inst: "false",
    147    profdir_existed: "false",
    148  };
    149 
    150  await writeJsonUtf16(dataFilePath, stubData);
    151  await runReport(dataFile, "stub", {
    152    clearTS: true,
    153    expectExtra: stubExtra,
    154    expectTS: "0",
    155  });
    156 
    157  // Check that it doesn't generate another event when the timestamp is unchanged
    158  await runReport(dataFile, "stub", { expectTS: "0" });
    159 
    160  // New timestamp
    161  stubData.install_timestamp = "1";
    162  await writeJsonUtf16(dataFilePath, stubData);
    163  await runReport(dataFile, "stub", {
    164    expectExtra: stubExtra,
    165    expectTS: "1",
    166  });
    167 
    168  // Test with normal full data
    169  let fullData = {
    170    version: "99.0abc",
    171    build_id: "123",
    172    installer_type: "full",
    173    admin_user: false,
    174    install_existed: true,
    175    profdir_existed: true,
    176    silent: false,
    177    from_msi: false,
    178    default_path: true,
    179 
    180    install_timestamp: "1",
    181  };
    182  let fullExtra = {
    183    version: "99.0abc",
    184    build_id: "123",
    185    admin_user: "false",
    186    install_existed: "true",
    187    other_inst: "false",
    188    other_msix_inst: "false",
    189    profdir_existed: "true",
    190    silent: "false",
    191    from_msi: "false",
    192    default_path: "true",
    193  };
    194 
    195  await writeJsonUtf16(dataFilePath, fullData);
    196  await runReport(dataFile, "full", {
    197    clearTS: true,
    198    expectExtra: fullExtra,
    199    expectTS: "1",
    200  });
    201 
    202  // Check that it doesn't generate another event when the timestamp is unchanged
    203  await runReport(dataFile, "full", { expectTS: "1" });
    204 
    205  // New timestamp and a check to make sure we can find installed MSIX packages
    206  // by overriding the prefixes a bit further down.
    207  fullData.install_timestamp = "2";
    208  // This check only works on Windows
    209  if (AppConstants.platform == "win") {
    210    fullExtra.other_msix_inst = "true";
    211  }
    212  await writeJsonUtf16(dataFilePath, fullData);
    213  await runReport(dataFile, "full", {
    214    expectExtra: fullExtra,
    215    expectTS: "2",
    216    msixPrefixes: ["Microsoft"],
    217  });
    218 
    219  // Missing field
    220  delete fullData.install_existed;
    221  fullData.install_timestamp = "3";
    222  await writeJsonUtf16(dataFilePath, fullData);
    223  await runReport(dataFile, "full", { assertRejects: /install_existed/ });
    224 
    225  // Malformed JSON
    226  await IOUtils.write(dataFilePath, encodeUtf16("hello"));
    227  await runReport(dataFile, "stub", {
    228    assertRejects: /unexpected character/,
    229  });
    230 
    231  // Missing file, should return with no exception
    232  await IOUtils.remove(dataFilePath);
    233  await runReport(dataFile, "stub", { setTS: "3", expectTS: "3" });
    234 });