tor-browser

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

test_NewTabGleanUtils.js (11623B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /**
      7 * This test file contains tests for NewTabGleanUtils functionality.
      8 * It tests the registration of metrics and pings for Glean telemetry in the newtab context.
      9 */
     10 
     11 ChromeUtils.defineESModuleGetters(this, {
     12  NewTabGleanUtils: "resource://newtab/lib/NewTabGleanUtils.sys.mjs",
     13  sinon: "resource://testing-common/Sinon.sys.mjs",
     14 });
     15 
     16 const TEST_RESOURCE_URI = "resource://test.json";
     17 
     18 function test_setup() {
     19  const sandbox = sinon.createSandbox();
     20  // bug 1954203 - use a builtin ping name to avoid bug with testResetFOG
     21  Services.fog.testResetFOG();
     22 
     23  sandbox.stub(NewTabGleanUtils, "readJSON").returns("{}");
     24  return sandbox;
     25 }
     26 
     27 /**
     28 * Test case: Empty metrics/pings file
     29 * Verifies that no registration occurs when the metrics file is empty
     30 */
     31 add_task(async function test_registerMetricsAndPings_emptyFile() {
     32  const sandbox = test_setup();
     33  // Test with empty data
     34  let result =
     35    await NewTabGleanUtils.registerMetricsAndPings(TEST_RESOURCE_URI);
     36  Assert.ok(!result, "No metrics or ping registration for empty file");
     37 
     38  sandbox.restore();
     39 });
     40 
     41 /**
     42 * Test case: Invalid metrics/pings format
     43 * Verifies that registration fails when the metrics file contains invalid format
     44 * with missing required metric and pings properties
     45 */
     46 add_task(async function test_registerMetricsAndPings_invalidFormat() {
     47  const sandbox = test_setup();
     48 
     49  // Test with invalid metrics and pings format
     50  NewTabGleanUtils.readJSON.returns({
     51    pings: {
     52      ping1: { include_client_id: true },
     53      ping2: { include_client_id: false },
     54    },
     55    metrics: {
     56      category: {
     57        metric1: { type: "counter" },
     58        metric2: { type: "string" },
     59      },
     60    },
     61  });
     62 
     63  let result =
     64    await NewTabGleanUtils.registerMetricsAndPings(TEST_RESOURCE_URI);
     65  Assert.ok(!result, "Registration failure for invalid metric and ping format");
     66 
     67  sandbox.restore();
     68 });
     69 
     70 /**
     71 * Test case: Valid metrics/pings format
     72 * Verifies successful registration with properly formatted metrics and pings
     73 * Tests with all required fields and correct property names
     74 */
     75 add_task(async function test_registerMetricsAndPings_validFormat() {
     76  const sandbox = test_setup();
     77 
     78  // Test with valid metrics and pings format
     79  NewTabGleanUtils.readJSON.returns({
     80    pings: {
     81      newtab_ping: {
     82        includeClientId: false,
     83        sendIfEmpty: false,
     84        preciseTimestamps: true,
     85        includeInfoSections: true,
     86        enabled: true,
     87        schedulesPings: [],
     88        reasonCodes: [],
     89        followsCollectionEnabled: true,
     90        uploaderCapabilities: [],
     91      },
     92    },
     93    metrics: {
     94      newtab_category: {
     95        metric1: {
     96          type: "text",
     97          description: "test-description",
     98          lifetime: "ping",
     99          pings: ["newtab"],
    100          disabled: false,
    101        },
    102      },
    103    },
    104  });
    105 
    106  let result =
    107    await NewTabGleanUtils.registerMetricsAndPings(TEST_RESOURCE_URI);
    108  Assert.ok(result, "Registration success for valid metric and ping formats");
    109 
    110  sandbox.restore();
    111 });
    112 
    113 /**
    114 * Test case: Handle ping configuration options name from runtime-metrics JSON
    115 * Verifies successful registration of pings with ping config options (such as `include_client_id`)
    116 * in snake case inside runtime-metrics JSON
    117 */
    118 add_task(async function test_registerPings_validSnakeCaseFormat() {
    119  const sandbox = test_setup();
    120  NewTabGleanUtils.readJSON.returns({
    121    pings: {
    122      newtab_ping_1: {
    123        include_client_id: false,
    124        send_if_empty: false,
    125        precise_timestamps: true,
    126        include_info_sections: true,
    127        enabled: true,
    128        schedules_pings: [],
    129        reason_codes: [],
    130        follows_collection_enabled: true,
    131        uploader_capabilities: [],
    132      },
    133    },
    134    metrics: {
    135      newtab_category_1: {
    136        metric1: {
    137          type: "text",
    138          description: "test-description",
    139          lifetime: "ping",
    140          pings: ["newtab"],
    141          disabled: false,
    142        },
    143      },
    144    },
    145  });
    146 
    147  let result =
    148    await NewTabGleanUtils.registerMetricsAndPings(TEST_RESOURCE_URI);
    149  Assert.ok(result, "Registration success for valid metric and ping formats");
    150 
    151  sandbox.restore();
    152 });
    153 
    154 /**
    155 * Test case: Optional metric description
    156 * Verifies that metric registration succeeds even when description is missing
    157 * Tests that description is not a required field for metric registration
    158 */
    159 add_task(
    160  async function test_registerMetricsAndPings_metricDescriptionOptional() {
    161    const sandbox = test_setup();
    162    NewTabGleanUtils.readJSON.returns({
    163      metrics: {
    164        newtab: {
    165          metric2: {
    166            type: "text",
    167            lifetime: "ping",
    168            pings: ["newtab"],
    169            disabled: false,
    170          },
    171        },
    172      },
    173    });
    174 
    175    let result =
    176      await NewTabGleanUtils.registerMetricsAndPings(TEST_RESOURCE_URI);
    177    Assert.ok(
    178      result,
    179      `Registration success for metric2 with missing description.
    180      Description property not required in JSON for metric registration`
    181    );
    182 
    183    sandbox.restore();
    184  }
    185 );
    186 
    187 /**
    188 * Test case: Metric registration telemetry
    189 * Verifies that telemetry is properly recorded for both successful and failed metric registrations
    190 * Tests with valid and invalid metric options
    191 */
    192 add_task(async function test_registerMetricIfNeeded_telemetrySent() {
    193  const validOptions = {
    194    name: "metric1",
    195    type: "text",
    196    category: "test",
    197    pings: ["newtab"],
    198    lifetime: "ping",
    199    disabled: false,
    200  };
    201 
    202  NewTabGleanUtils.registerMetricIfNeeded(validOptions);
    203 
    204  // Check instrumented telemetry records success
    205  Assert.ok(
    206    Glean.newtab.metricRegistered.metric1.testGetValue(),
    207    "Glean metricRegistered telemetry sent with value as true"
    208  );
    209 
    210  const invalidOptions = {
    211    name: "metric2",
    212    type: "text",
    213    category: "test",
    214    pings: ["newtab"],
    215    disabled: false,
    216  };
    217 
    218  // Throws when required lifetime property missing
    219  Assert.throws(
    220    () => NewTabGleanUtils.registerMetricIfNeeded(invalidOptions),
    221    /Failure while registering metrics metric2/,
    222    "Throws when metric registration fails due to missing lifetime param"
    223  );
    224 
    225  // Check instrumented telemetry records failure
    226  Assert.strictEqual(
    227    Glean.newtab.metricRegistered.metric2.testGetValue(),
    228    false,
    229    "Glean metricRegistered telemetry sent with value as false"
    230  );
    231 });
    232 
    233 /**
    234 * Test case: Ping registration telemetry
    235 * Verifies that telemetry is properly recorded for both successful and failed ping registrations
    236 * Tests with valid and invalid ping options
    237 */
    238 add_task(async function test_registerPingIfNeeded_telemetrySent() {
    239  const validOptions = {
    240    name: "ping1",
    241    includeClientId: false,
    242    sendIfEmpty: false,
    243    preciseTimestamps: true,
    244    includeInfoSections: true,
    245    enabled: true,
    246    schedulesPings: [],
    247    reasonCodes: [],
    248    followsCollectionEnabled: true,
    249    uploaderCapabilities: [],
    250  };
    251 
    252  NewTabGleanUtils.registerPingIfNeeded(validOptions);
    253  // Check instrumented telemetry records success for registered ping
    254  Assert.ok(
    255    Glean.newtab.pingRegistered.ping1.testGetValue(),
    256    "Glean pingRegistered telemetry sent with value as true for ping1"
    257  );
    258 
    259  const invalidOptions = {
    260    name: "ping2",
    261    includeClientId: false,
    262    sendIfEmpty: false,
    263    preciseTimestamps: true,
    264    includeInfoSections: true,
    265    enabled: true,
    266    schedulesPings: [],
    267    reasonCodes: [],
    268    followsCollectionEnabled: true,
    269  };
    270 
    271  // And test methods throw appropriately
    272  Assert.throws(
    273    () => NewTabGleanUtils.registerPingIfNeeded(invalidOptions),
    274    /Failure while registering ping ping2/,
    275    "Throws when ping registration fails due to missing uploaderCapabilities param"
    276  );
    277 
    278  // Check instrumented telemetry records failure
    279  Assert.strictEqual(
    280    Glean.newtab.pingRegistered.ping2.testGetValue(),
    281    false,
    282    "Glean pingRegistered telemetry sent with value as false for ping2"
    283  );
    284 });
    285 
    286 /**
    287 * Test case: Event metric registration
    288 * Verifies proper registration and recording of event metrics
    289 * Tests both basic event metrics and those with extra arguments
    290 */
    291 add_task(async function test_registerMetricIfNeeded_eventMetrics() {
    292  const options = {
    293    name: "event1",
    294    category: "test_category",
    295    type: "event",
    296    pings: ["events"],
    297    lifetime: "ping",
    298    disabled: false,
    299  };
    300 
    301  NewTabGleanUtils.registerMetricIfNeeded(options);
    302 
    303  // Check instrumented telemetry records success
    304  Assert.ok(
    305    Glean.newtab.metricRegistered.event1.testGetValue(),
    306    "Glean metricRegistered telemetry sent with value as true"
    307  );
    308 
    309  const optionsWithExtra = {
    310    name: "event2",
    311    category: "test_category",
    312    type: "event",
    313    pings: ["events"],
    314    lifetime: "ping",
    315    disabled: false,
    316    extraArgs: {
    317      allowed_extra_keys: ["extra1", "extra2"],
    318    },
    319  };
    320 
    321  NewTabGleanUtils.registerMetricIfNeeded(optionsWithExtra);
    322 
    323  // Check instrumented telemetry records success
    324  Assert.ok(
    325    Glean.newtab.metricRegistered.event2.testGetValue(),
    326    "Glean metricRegistered telemetry sent with value as true"
    327  );
    328 
    329  let extra = { extra1: "extra1 value", extra2: "extra2 value" };
    330  Glean.testCategory.event2.record(extra);
    331 
    332  let events = Glean.testCategory.event2.testGetValue();
    333  Assert.equal(1, events.length, "Events recorded count");
    334 });
    335 
    336 /**
    337 * Test case: Non-event metric registration
    338 * Verifies that non-event metrics cannot use the record method
    339 * Tests that appropriate errors are thrown when trying to record non-event metrics
    340 */
    341 add_task(async function test_registerMetricIfNeeded_nonEventMetrics() {
    342  const options = {
    343    name: "event3",
    344    type: "text",
    345    category: "test_category1",
    346    pings: ["events"],
    347    lifetime: "ping",
    348    disabled: false,
    349    extraArgs: {
    350      allowed_extra_keys: ["extra1", "extra2"],
    351    },
    352  };
    353 
    354  NewTabGleanUtils.registerMetricIfNeeded(options);
    355 
    356  // Check instrumented telemetry records success
    357  Assert.ok(
    358    Glean.newtab.metricRegistered.event3.testGetValue(),
    359    "Glean metricRegistered telemetry sent with value as true"
    360  );
    361 
    362  let extra = { extra1: "extra1 value", extra2: "extra2 value" };
    363 
    364  // And test methods throw appropriately
    365  Assert.throws(
    366    () => Glean.testCategory1.event3.record(extra),
    367    /TypeError/,
    368    "Throws when using record for non event type"
    369  );
    370 
    371  Glean.testCategory1.event3.set("Test");
    372 
    373  Assert.equal(
    374    "Test",
    375    Glean.testCategory1.event3.testGetValue(),
    376    "Success when using set to send data of type text"
    377  );
    378 });
    379 
    380 /**
    381 * Test case: Memory distribution metric registration
    382 * Verifies proper registration and recording of memory_distribution metrics
    383 * Ensures that extraArgs (memory_unit) are honored
    384 */
    385 add_task(
    386  async function test_registerMetricIfNeeded_memoryDistributionMetrics() {
    387    const optionsWithExtra = {
    388      name: "memdist1",
    389      category: "test_category",
    390      type: "memory_distribution",
    391      pings: ["metrics"],
    392      lifetime: "ping",
    393      disabled: false,
    394      extraArgs: {
    395        memory_unit: "megabyte",
    396      },
    397    };
    398 
    399    NewTabGleanUtils.registerMetricIfNeeded(optionsWithExtra);
    400 
    401    Assert.ok(
    402      Glean.newtab.metricRegistered.memdist1.testGetValue(),
    403      "Glean metricRegistered telemetry sent with value as true"
    404    );
    405 
    406    Glean.testCategory.memdist1.accumulate(7);
    407    Glean.testCategory.memdist1.accumulate(17);
    408 
    409    let data = Glean.testCategory.memdist1.testGetValue();
    410    Assert.equal(2, data.count, "Recorded sample count");
    411    Assert.equal(24 * 1024 * 1024, data.sum, "Sum is in bytes for MB unit");
    412  }
    413 );