/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /* Test the firefox-on-glean profiler integration. */ "use strict"; const { AppConstants } = ChromeUtils.importESModule( "resource://gre/modules/AppConstants.sys.mjs" ); const { ProfilerTestUtils } = ChromeUtils.importESModule( "resource://testing-common/ProfilerTestUtils.sys.mjs" ); const { setTimeout } = ChromeUtils.importESModule( "resource://gre/modules/Timer.sys.mjs" ); function sleep(ms) { /* eslint-disable mozilla/no-arbitrary-setTimeout */ return new Promise(resolve => setTimeout(resolve, ms)); } add_setup( /* on Android FOG is set up through the global xpcshell head.js */ { skip_if: () => AppConstants.platform == "android" }, function test_setup() { // FOG needs a profile directory to put its data in. do_get_profile(); // We need to initialize it once, otherwise operations will be stuck in the pre-init queue. Services.fog.initializeFOG(); } ); /** * Start the profiler, run `func`, stop the profiler, and get a collection of * markers that were recorded while running `func`. * * @param {string} type The marker payload type, e.g. "Counter" * @param {object} func The function that runs glean code to generate markers * @returns {object} The markers generated during `func`, with the id field * expanded using the string table */ async function runWithProfilerAndGetMarkers(type, func) { await ProfilerTestUtils.startProfiler({ entries: 10000, interval: 10, features: ["nostacksampling"], threads: ["GeckoMain"], }); Assert.ok(Services.profiler.IsActive()); await func(); let profile = await ProfilerTestUtils.stopNowAndGetProfile(); // We assume that we only have one thread being profiled here. Assert.equal( profile.threads.length, 1, "We should only be profiling one thread" ); let markers = ProfilerTestUtils.getPayloadsOfType(profile.threads[0], type); let stringTable = profile.threads[0].stringTable; // We expect that the id, or name, of a marker should be a unique string. // Go through them and look up the values so that we can just write a string // in the test, and not use a numerical id (which may not be stable!) for (let marker of markers) { marker.id = stringTable[marker.id]; if (marker.cat != undefined) { marker.cat = stringTable[marker.cat]; } if (marker.label != undefined) { marker.label = stringTable[marker.label]; } } // Next, filter the markers so that we only return those generated by test // metrics. We need to do this, as some metric types are used by other // parts of Fx, which may be running during the test. As we cannot predict // when they may occur, or what values they may hold, we can't write down // a "gold" value in our tests to compare them to - so we need to remove // them. Some notable existing examples include: // - Memory distribution metrics, "performanceCloneDeserialize." // - Custom distribution metrics (same as memory distribution) // - Timing distribution metrics (for timing of performance // clone/deserialise) // - Counter metrics ("javascriptGc"), which caused intermittents in Bug // 1943425 markers = markers.filter(marker => { // Metric markers with names coming from JS all start with `testOnly`, // `test_only` or `jog_` let name = marker.id.startsWith("testOnly") || marker.id.startsWith("test_only") || marker.id.startsWith("jog_"); // Marker categories all start with test or jog let cat = false; if (marker.cat != undefined) { cat = marker.cat.startsWith("test") || marker.cat.startsWith("jog_"); } // Ping markers are a little more varied, so we enumerate them let ping = ["test-ping", "one-ping-only", "ride-along-ping"].includes( marker.id ); return name || cat || ping; }); // Return selected markers return markers; } add_task(async function test_fog_counter_markers() { let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { Glean.testOnly.badCode.add(31); // Labeled counter submetrics Glean.testOnly.mabelsKitchenCounters.near_the_sink.add(1); Glean.testOnly.mabelsKitchenCounters.with_junk_on_them.add(2); // Invalid label, but we still expect to see a marker Glean.testOnly.mabelsKitchenCounters["1".repeat(72)].add(1); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "test_only", id: "bad_code", val: 31 }, { type: "IntLikeMetric", cat: "test_only", id: "mabels_kitchen_counters", label: "near_the_sink", val: 1, }, { type: "IntLikeMetric", cat: "test_only", id: "mabels_kitchen_counters", label: "with_junk_on_them", val: 2, }, { type: "IntLikeMetric", cat: "test_only", id: "mabels_kitchen_counters", label: "1".repeat(72), val: 1, }, ]); }); add_task(async function test_jog_counter_markers() { Services.fog.testRegisterRuntimeMetric( "counter", "jog_cat", "jog_counter", ["test-ping"], `"ping"`, false ); let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { Glean.jogCat.jogCounter.add(53); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "jog_cat", id: "jog_counter", val: 53 }, ]); }); add_task(async function test_fog_static_labeled_counter_markers() { let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { // Statically labeled counter submetrics Glean.testOnly.mabelsLabeledCounters.next_to_the_fridge.add(1); Glean.testOnly.mabelsLabeledCounters.clean.add(2); Glean.testOnly.mabelsLabeledCounters["1st_counter"].add(2); Glean.testOnlyIpc.aLabeledCounterForCategorical.CommonLabel.add(1); Glean.testOnlyIpc.aLabeledCounterForCategorical.Label4.add(1); Glean.testOnlyIpc.aLabeledCounterForCategorical.Label5.add(1); Glean.testOnlyIpc.aLabeledCounterForCategorical.Label6.add(1); Glean.testOnlyIpc.aLabeledCounterForHgram.false.add(1); Glean.testOnlyIpc.aLabeledCounterForHgram.true.add(1); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "test_only", id: "mabels_labeled_counters", label: "next_to_the_fridge", val: 1, }, { type: "IntLikeMetric", cat: "test_only", id: "mabels_labeled_counters", label: "clean", val: 2, }, { type: "IntLikeMetric", cat: "test_only", id: "mabels_labeled_counters", label: "1st_counter", val: 2, }, { type: "IntLikeMetric", cat: "test_only.ipc", id: "a_labeled_counter_for_categorical", label: "CommonLabel", val: 1, }, { type: "IntLikeMetric", cat: "test_only.ipc", id: "a_labeled_counter_for_categorical", label: "Label4", val: 1, }, { type: "IntLikeMetric", cat: "test_only.ipc", id: "a_labeled_counter_for_categorical", label: "Label5", val: 1, }, { type: "IntLikeMetric", cat: "test_only.ipc", id: "a_labeled_counter_for_categorical", label: "Label6", val: 1, }, { type: "IntLikeMetric", cat: "test_only.ipc", id: "a_labeled_counter_for_hgram", label: "false", val: 1, }, { type: "IntLikeMetric", cat: "test_only.ipc", id: "a_labeled_counter_for_hgram", label: "true", val: 1, }, ]); }); add_task(async function test_jog_labeled_counter_markers() { Services.fog.testRegisterRuntimeMetric( "labeled_counter", "jog_cat", "jog_labeled_counter", ["test-ping"], `"ping"`, false ); let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { Glean.jogCat.jogLabeledCounter.label_1.add(1); Glean.jogCat.jogLabeledCounter.label_2.add(2); Glean.jogCat.jogLabeledCounter["1".repeat(72)].add(1); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "jog_cat", id: "jog_labeled_counter", label: "label_1", val: 1, }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_labeled_counter", label: "label_2", val: 2, }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_labeled_counter", label: "1".repeat(72), val: 1, }, ]); }); add_task(async function test_jog_labeled_counter_with_static_labels_markers() { Services.fog.testRegisterRuntimeMetric( "labeled_counter", "jog_cat", "jog_labeled_counter_with_labels", ["test-ping"], `"ping"`, false, JSON.stringify({ ordered_labels: ["label_1", "label_2"] }) ); let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { Glean.jogCat.jogLabeledCounterWithLabels.label_1.add(1); Glean.jogCat.jogLabeledCounterWithLabels.label_2.add(2); Glean.jogCat.jogLabeledCounterWithLabels["1".repeat(72)].add(1); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "jog_cat", id: "jog_labeled_counter_with_labels", label: "label_1", val: 1, }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_labeled_counter_with_labels", label: "label_2", val: 2, }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_labeled_counter_with_labels", label: "__other__", val: 1, }, ]); }); add_task(async function test_fog_string_markers() { const value = "a cheesy string!"; let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { Glean.testOnly.cheesyString.set(value); Glean.testOnly.cheesyString.set("a".repeat(2048)); // Labeled string submetrics Glean.testOnly.mabelsLabelMaker.singleword.set("portmanteau"); Glean.testOnly.mabelsLabelMaker.snake_case.set("snek"); Glean.testOnly.mabelsLabelMaker["dash-character"].set("Dash Rendar"); Glean.testOnly.mabelsLabelMaker["dot.separated"].set("dot product"); Glean.testOnly.mabelsLabelMaker.camelCase.set("wednesday"); // Invalid label, but we still expect to see a marker const veryLong = "1".repeat(72); Glean.testOnly.mabelsLabelMaker[veryLong].set("seventy-two"); }); // Test that we correctly truncate long strings: const truncatedLongString = "a".repeat(1024); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "test_only", id: "cheesy_string", val: "a cheesy string!", }, { type: "StringLikeMetric", cat: "test_only", id: "cheesy_string", val: truncatedLongString, }, { type: "StringLikeMetric", cat: "test_only", id: "mabels_label_maker", label: "singleword", val: "portmanteau", }, { type: "StringLikeMetric", cat: "test_only", id: "mabels_label_maker", label: "snake_case", val: "snek", }, { type: "StringLikeMetric", cat: "test_only", id: "mabels_label_maker", label: "dash-character", val: "Dash Rendar", }, { type: "StringLikeMetric", cat: "test_only", id: "mabels_label_maker", label: "dot.separated", val: "dot product", }, { type: "StringLikeMetric", cat: "test_only", id: "mabels_label_maker", label: "camelCase", val: "wednesday", }, { type: "StringLikeMetric", cat: "test_only", id: "mabels_label_maker", label: "1".repeat(72), val: "seventy-two", }, ]); }); add_task(async function test_fog_static_labeled_string_markers() { let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { // Labeled string submetrics Glean.testOnly.mabelsBalloonLabels.celebratory.set("hooray"); Glean.testOnly.mabelsBalloonLabels.celebratory_and_snarky.set( "oh yay, hooray" ); }); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "test_only", id: "mabels_balloon_labels", label: "celebratory", val: "hooray", }, { type: "StringLikeMetric", cat: "test_only", id: "mabels_balloon_labels", label: "celebratory_and_snarky", val: "oh yay, hooray", }, ]); }); add_task(async function test_jog_string_markers() { const value = "an active string!"; Services.fog.testRegisterRuntimeMetric( "string", "jog_cat", "jog_string", ["test-ping"], `"ping"`, false ); let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { Glean.jogCat.jogString.set(value); }); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "jog_cat", id: "jog_string", val: "an active string!", }, ]); }); add_task(async function test_jog_labeled_string_markers() { Services.fog.testRegisterRuntimeMetric( "labeled_string", "jog_cat", "jog_labeled_string", ["test-ping"], `"ping"`, false ); let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { Glean.jogCat.jogLabeledString.label_1.set("crimson"); Glean.jogCat.jogLabeledString.label_2.set("various"); Glean.jogCat.jogLabeledString["1".repeat(72)].set("valid"); }); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "jog_cat", id: "jog_labeled_string", label: "label_1", val: "crimson", }, { type: "StringLikeMetric", cat: "jog_cat", id: "jog_labeled_string", label: "label_2", val: "various", }, { type: "StringLikeMetric", cat: "jog_cat", id: "jog_labeled_string", label: "1".repeat(72), val: "valid", }, ]); }); add_task(async function test_jog_labeled_string_with_labels() { Services.fog.testRegisterRuntimeMetric( "labeled_string", "jog_cat", "jog_labeled_string_with_labels", ["test-ping"], `"ping"`, false, JSON.stringify({ ordered_labels: ["label_1", "label_2"] }) ); let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { Glean.jogCat.jogLabeledStringWithLabels.label_1.set("crimson"); Glean.jogCat.jogLabeledStringWithLabels.label_2.set("various"); Glean.jogCat.jogLabeledStringWithLabels["1".repeat(72)].set("valid"); }); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "jog_cat", id: "jog_labeled_string_with_labels", label: "label_1", val: "crimson", }, { type: "StringLikeMetric", cat: "jog_cat", id: "jog_labeled_string_with_labels", label: "label_2", val: "various", }, { type: "StringLikeMetric", cat: "jog_cat", id: "jog_labeled_string_with_labels", label: "__other__", val: "valid", }, ]); }); add_task(async function test_fog_string_list() { const value = "a cheesy string!"; const value2 = "a cheesier string!"; const value3 = "the cheeziest of strings."; const cheeseList = [value, value2]; let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { Glean.testOnly.cheesyStringList.set(cheeseList); Glean.testOnly.cheesyStringList.add(value3); }); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "test_only", id: "cheesy_string_list", // Note: This is a little fragile and will need to be updated if we ever // rearrange the items in the string list. val: `[${value},${value2}]`, }, { type: "StringLikeMetric", cat: "test_only", id: "cheesy_string_list", val: value3, }, ]); }); add_task(async function test_jog_string_list_markers() { const value = "an active string!"; const value2 = "a more active string!"; const value3 = "the most active of strings."; Services.fog.testRegisterRuntimeMetric( "string_list", "jog_cat", "jog_string_list", ["test-ping"], `"ping"`, false ); const jogList = [value, value2]; let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { Glean.jogCat.jogStringList.set(jogList); Glean.jogCat.jogStringList.add(value3); }); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "jog_cat", id: "jog_string_list", val: "[an active string!,a more active string!]", }, { type: "StringLikeMetric", cat: "jog_cat", id: "jog_string_list", val: "the most active of strings.", }, ]); }); add_task(async function test_fog_timespan() { let markers = await runWithProfilerAndGetMarkers( "TimespanMetric", async () => { Glean.testOnly.canWeTimeIt.start(); Glean.testOnly.canWeTimeIt.cancel(); // We start, briefly sleep and then stop. // That guarantees some time to measure. Glean.testOnly.canWeTimeIt.start(); await sleep(10); Glean.testOnly.canWeTimeIt.stop(); // Set a raw value to make sure we get values in markers Glean.testOnly.canWeTimeIt.setRaw(100); } ); Assert.deepEqual(markers, [ { type: "TimespanMetric", cat: "test_only", id: "can_we_time_it" }, { type: "TimespanMetric", cat: "test_only", id: "can_we_time_it" }, { type: "TimespanMetric", cat: "test_only", id: "can_we_time_it" }, { type: "TimespanMetric", cat: "test_only", id: "can_we_time_it" }, { type: "TimespanMetric", cat: "test_only", id: "can_we_time_it", val: 100, }, ]); }); add_task(async function test_jog_timespan_markers() { Services.fog.testRegisterRuntimeMetric( "timespan", "jog_cat", "jog_timespan", ["test-ping"], `"ping"`, false, JSON.stringify({ time_unit: "millisecond" }) ); let markers = await runWithProfilerAndGetMarkers( "TimespanMetric", async () => { Glean.jogCat.jogTimespan.start(); Glean.jogCat.jogTimespan.cancel(); // We start, briefly sleep and then stop. // That guarantees some time to measure. Glean.jogCat.jogTimespan.start(); await sleep(10); Glean.jogCat.jogTimespan.stop(); } ); Assert.deepEqual(markers, [ { type: "TimespanMetric", cat: "jog_cat", id: "jog_timespan" }, { type: "TimespanMetric", cat: "jog_cat", id: "jog_timespan" }, { type: "TimespanMetric", cat: "jog_cat", id: "jog_timespan" }, { type: "TimespanMetric", cat: "jog_cat", id: "jog_timespan" }, ]); }); add_task( async function test_fog_timespan_throws_on_stop_wout_start_but_still_records() { let markers = await runWithProfilerAndGetMarkers("TimespanMetric", () => { // Throws an error inside glean, but we still expect to see a marker Glean.testOnly.canWeTimeIt.stop(); }); Assert.deepEqual(markers, [ { type: "TimespanMetric", cat: "test_only", id: "can_we_time_it" }, ]); } ); add_task(async function test_fog_uuid() { const kTestUuid = "decafdec-afde-cafd-ecaf-decafdecafde"; let generatedUuid; let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { Glean.testOnly.whatIdIt.set(kTestUuid); // We need to compare the generated UUID to what we record in a marker. Since // this won't be stable across test runs (we can't "write it down"), we have // to instead query glean for what it generated, and then check that that // value is in the marker. Glean.testOnly.whatIdIt.generateAndSet(); generatedUuid = Glean.testOnly.whatIdIt.testGetValue("test-ping"); }); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "test_only", id: "what_id_it", val: kTestUuid, }, { type: "StringLikeMetric", cat: "test_only", id: "what_id_it", val: generatedUuid, }, ]); }); add_task(async function test_jog_uuid_markers() { const kTestUuid = "decafdec-afde-cafd-ecaf-decafdecafde"; let generatedUuid; Services.fog.testRegisterRuntimeMetric( "uuid", "jog_cat", "jog_uuid", ["test-ping"], `"ping"`, false ); let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { Glean.jogCat.jogUuid.set(kTestUuid); Glean.jogCat.jogUuid.generateAndSet(); generatedUuid = Glean.jogCat.jogUuid.testGetValue(); }); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "jog_cat", id: "jog_uuid", val: kTestUuid, }, { type: "StringLikeMetric", cat: "jog_cat", id: "jog_uuid", val: generatedUuid, }, ]); }); add_task(async function test_fog_datetime() { const value = new Date(); let markers = await runWithProfilerAndGetMarkers("DatetimeMetric", () => { Glean.testOnly.whatADate.set(value.getTime() * 1000); }); Assert.deepEqual(markers, [ { type: "DatetimeMetric", cat: "test_only", id: "what_a_date", time: value.toISOString(), }, ]); }); add_task(async function test_jog_datetime_markers() { const value = new Date("2020-06-11T12:00:00"); Services.fog.testRegisterRuntimeMetric( "datetime", "jog_cat", "jog_datetime", ["test-ping"], `"ping"`, false, JSON.stringify({ time_unit: "nanosecond" }) ); let markers = await runWithProfilerAndGetMarkers("DatetimeMetric", () => { Glean.jogCat.jogDatetime.set(value.getTime() * 1000); }); Assert.deepEqual(markers, [ { type: "DatetimeMetric", cat: "jog_cat", id: "jog_datetime", time: value.toISOString(), }, ]); }); add_task(async function test_fog_boolean_markers() { let markers = await runWithProfilerAndGetMarkers("BooleanMetric", () => { Glean.testOnly.canWeFlagIt.set(false); Glean.testOnly.canWeFlagIt.set(true); // Labeled boolean submetrics Glean.testOnly.mabelsLikeBalloons.at_parties.set(true); Glean.testOnly.mabelsLikeBalloons.at_funerals.set(false); // Invalid label, but we still expect to see a marker Glean.testOnly.mabelsLikeBalloons["1".repeat(72)].set(true); }); Assert.deepEqual(markers, [ { type: "BooleanMetric", cat: "test_only", id: "can_we_flag_it", val: false, }, { type: "BooleanMetric", cat: "test_only", id: "can_we_flag_it", val: true, }, { type: "BooleanMetric", cat: "test_only", id: "mabels_like_balloons", label: "at_parties", val: true, }, { type: "BooleanMetric", cat: "test_only", id: "mabels_like_balloons", label: "at_funerals", val: false, }, { type: "BooleanMetric", cat: "test_only", id: "mabels_like_balloons", label: "1".repeat(72), val: true, }, ]); }); add_task(async function test_jog_boolean_markers() { Services.fog.testRegisterRuntimeMetric( "boolean", "jog_cat", "jog_bool", ["test-ping"], `"ping"`, false ); let markers = await runWithProfilerAndGetMarkers("BooleanMetric", () => { Glean.jogCat.jogBool.set(false); }); Assert.deepEqual(markers, [ { type: "BooleanMetric", cat: "jog_cat", id: "jog_bool", val: false, }, ]); }); add_task(async function test_fog_static_labeled_boolean_markers() { let markers = await runWithProfilerAndGetMarkers("BooleanMetric", () => { // Labeled boolean submetrics Glean.testOnly.mabelsLikeLabeledBalloons.water.set(true); Glean.testOnly.mabelsLikeLabeledBalloons.birthday_party.set(false); }); Assert.deepEqual(markers, [ { type: "BooleanMetric", cat: "test_only", id: "mabels_like_labeled_balloons", label: "water", val: true, }, { type: "BooleanMetric", cat: "test_only", id: "mabels_like_labeled_balloons", label: "birthday_party", val: false, }, ]); }); add_task(async function test_jog_labeled_boolean_markers() { Services.fog.testRegisterRuntimeMetric( "labeled_boolean", "jog_cat", "jog_labeled_bool", ["test-ping"], `"ping"`, false ); let markers = await runWithProfilerAndGetMarkers("BooleanMetric", () => { Glean.jogCat.jogLabeledBool.label_1.set(true); Glean.jogCat.jogLabeledBool.label_2.set(false); Glean.jogCat.jogLabeledBool.NowValidLabel.set(true); Glean.jogCat.jogLabeledBool["1".repeat(72)].set(true); }); Assert.deepEqual(markers, [ { type: "BooleanMetric", cat: "jog_cat", id: "jog_labeled_bool", label: "label_1", val: true, }, { type: "BooleanMetric", cat: "jog_cat", id: "jog_labeled_bool", label: "label_2", val: false, }, { type: "BooleanMetric", cat: "jog_cat", id: "jog_labeled_bool", label: "NowValidLabel", val: true, }, { type: "BooleanMetric", cat: "jog_cat", id: "jog_labeled_bool", label: "1".repeat(72), val: true, }, ]); }); add_task(async function test_jog_labeled_boolean_with_static_labels_markers() { Services.fog.testRegisterRuntimeMetric( "labeled_boolean", "jog_cat", "jog_labeled_bool_with_labels", ["test-ping"], `"ping"`, false, JSON.stringify({ ordered_labels: ["label_1", "label_2"] }) ); let markers = await runWithProfilerAndGetMarkers("BooleanMetric", () => { Glean.jogCat.jogLabeledBoolWithLabels.label_1.set(true); Glean.jogCat.jogLabeledBoolWithLabels.label_2.set(false); Glean.jogCat.jogLabeledBoolWithLabels.label_3.set(true); }); Assert.deepEqual(markers, [ { type: "BooleanMetric", cat: "jog_cat", id: "jog_labeled_bool_with_labels", label: "label_1", val: true, }, { type: "BooleanMetric", cat: "jog_cat", id: "jog_labeled_bool_with_labels", label: "label_2", val: false, }, { type: "BooleanMetric", cat: "jog_cat", id: "jog_labeled_bool_with_labels", label: "__other__", val: true, }, ]); }); add_task( { // Event metrics don't support profile markers in artifact builds due to // having no centralized metric instance storage. skip_if: () => Services.prefs.getBoolPref("telemetry.fog.artifact_build", false), }, async function test_fog_event_markers() { let markers = await runWithProfilerAndGetMarkers("EventMetric", () => { // Record an event that produces a marker with `extra` undefined: Glean.testOnlyIpc.noExtraEvent.record(); let extra = { extra1: "can set extras", extra2: "passing more data" }; Glean.testOnlyIpc.anEvent.record(extra); // Corner case: Event with extra with `undefined` value. // Should pretend that the extra key (extra1) isn't there. let extraWithUndef = { extra1: undefined, extra2: "defined" }; Glean.testOnlyIpc.anEvent.record(extraWithUndef); let extra2 = { extra1: "can set extras", extra2: 37, extra3_longer_name: false, }; Glean.testOnlyIpc.eventWithExtra.record(extra2); // camelCase extras work. let extra5 = { extra4CamelCase: false, }; Glean.testOnlyIpc.eventWithExtra.record(extra5); // Passing `null` works. Glean.testOnlyIpc.eventWithExtra.record(null); // Invalid extra keys don't crash, the event is not recorded, // but an error and marker are recorded. let extra3 = { extra1_nonexistent_extra: "this does not crash", }; Glean.testOnlyIpc.eventWithExtra.record(extra3); // Supplying extras when there aren't any defined results in the event not // being recorded, but an error is, along with a marker Glean.testOnlyIpc.noExtraEvent.record(extra3); }); let expected_markers = [ { type: "EventMetric", id: "testOnlyIpc.noExtraEvent", }, { type: "EventMetric", id: "testOnlyIpc.anEvent", extra: { extra1: "can set extras", extra2: "passing more data" }, }, { type: "EventMetric", id: "testOnlyIpc.anEvent", extra: { extra2: "defined" }, }, { type: "EventMetric", id: "testOnlyIpc.eventWithExtra", extra: { extra3_longer_name: "false", extra2: "37", extra1: "can set extras", }, }, { type: "EventMetric", id: "testOnlyIpc.eventWithExtra", extra: { extra4CamelCase: "false" }, }, { type: "EventMetric", id: "testOnlyIpc.eventWithExtra", }, // This event throws an error in glean, but we still record a marker { type: "EventMetric", id: "testOnlyIpc.eventWithExtra", extra: { extra1_nonexistent_extra: "this does not crash" }, }, // This event throws an error in glean, but we still record a marker { type: "EventMetric", id: "testOnlyIpc.noExtraEvent", extra: { extra1_nonexistent_extra: "this does not crash" }, }, ]; // Parse the `extra` field of each marker into a JS object so that we can do // a deep equality check, ignoring undefined extras. markers.forEach(m => { if (m.extra !== undefined) { m.extra = JSON.parse(m.extra); } }); Assert.deepEqual(markers, expected_markers); } ); add_task(async function test_fog_memory_distribution() { let markers = await runWithProfilerAndGetMarkers("DistMetric", () => { Glean.testOnly.doYouRemember.accumulate(7); Glean.testOnly.doYouRemember.accumulate(17); // Note, we would like to test something like this, to ensure that we test // the internal `accumulate_samples` marker, but the JS API doesn't support // accumulating multiple samples (or an array of samples). // Glean.testOnly.doYouRemember.accumulate([17, 2134, 543]); // Labeled memory distribution submetrics Glean.testOnly.whatDoYouRemember.twenty_years_ago.accumulate(7); Glean.testOnly.whatDoYouRemember.twenty_years_ago.accumulate(17); }); Assert.deepEqual(markers, [ { type: "DistMetric", cat: "test_only", id: "do_you_remember", sample: "7", }, { type: "DistMetric", cat: "test_only", id: "do_you_remember", sample: "17", }, { type: "DistMetric", cat: "test_only", id: "what_do_you_remember", label: "twenty_years_ago", sample: "7", }, { type: "DistMetric", cat: "test_only", id: "what_do_you_remember", label: "twenty_years_ago", sample: "17", }, ]); }); add_task(async function test_jog_memory_distribution_markers() { Services.fog.testRegisterRuntimeMetric( "memory_distribution", "jog_cat", "jog_memory_dist", ["test-ping"], `"ping"`, false, JSON.stringify({ memory_unit: "megabyte" }) ); let markers = await runWithProfilerAndGetMarkers("DistMetric", () => { Glean.jogCat.jogMemoryDist.accumulate(7); Glean.jogCat.jogMemoryDist.accumulate(17); }); Assert.deepEqual(markers, [ { type: "DistMetric", cat: "jog_cat", id: "jog_memory_dist", sample: "7" }, { type: "DistMetric", cat: "jog_cat", id: "jog_memory_dist", sample: "17" }, ]); }); add_task(async function test_jog_labeled_memory_distribution_markers() { Services.fog.testRegisterRuntimeMetric( "labeled_memory_distribution", "jog_cat", "jog_labeled_memory_dist", ["test-ping"], `"ping"`, false, JSON.stringify({ memory_unit: "megabyte" }) ); let markers = await runWithProfilerAndGetMarkers("DistMetric", () => { Glean.jogCat.jogLabeledMemoryDist.short_term.accumulate(7); Glean.jogCat.jogLabeledMemoryDist.short_term.accumulate(17); }); Assert.deepEqual(markers, [ { type: "DistMetric", cat: "jog_cat", id: "jog_labeled_memory_dist", label: "short_term", sample: "7", }, { type: "DistMetric", cat: "jog_cat", id: "jog_labeled_memory_dist", label: "short_term", sample: "17", }, ]); }); add_task(async function test_fog_custom_distribution() { let markers = await runWithProfilerAndGetMarkers("DistMetric", () => { Glean.testOnlyIpc.aCustomDist.accumulateSingleSample(120); Glean.testOnlyIpc.aCustomDist.accumulateSamples([7, 268435458]); // Negative values will not be recorded, instead an error is recorded. // However, we still expect to see a marker! Glean.testOnlyIpc.aCustomDist.accumulateSamples([-7]); // Labeled custom distribution submetrics Glean.testOnly.mabelsCustomLabelLengths.monospace.accumulateSamples([ 1, 42, ]); Glean.testOnly.mabelsCustomLabelLengths.sanserif.accumulateSingleSample(13); // Invalid label, but we still expect to see a marker Glean.testOnly.mabelsCustomLabelLengths[ "1".repeat(72) ].accumulateSingleSample(3); }); Assert.deepEqual(markers, [ { type: "DistMetric", cat: "test_only.ipc", id: "a_custom_dist", sample: "120", }, { type: "DistMetric", cat: "test_only.ipc", id: "a_custom_dist", samples: "[7,268435458]", }, { type: "DistMetric", cat: "test_only.ipc", id: "a_custom_dist", samples: "[-7]", }, { type: "DistMetric", cat: "test_only", id: "mabels_custom_label_lengths", label: "monospace", samples: "[1,42]", }, { type: "DistMetric", cat: "test_only", id: "mabels_custom_label_lengths", label: "sanserif", sample: "13", }, { type: "DistMetric", cat: "test_only", id: "mabels_custom_label_lengths", label: "1".repeat(72), sample: "3", }, ]); }); add_task(async function test_jog_custom_distribution_markers() { Services.fog.testRegisterRuntimeMetric( "custom_distribution", "jog_cat", "jog_custom_dist", ["test-ping"], `"ping"`, false, JSON.stringify({ range_min: 1, range_max: 2147483646, bucket_count: 10, histogram_type: "linear", }) ); let markers = await runWithProfilerAndGetMarkers("DistMetric", () => { Glean.jogCat.jogCustomDist.accumulateSamples([7, 268435458]); // Negative values will not be recorded, instead an error is recorded. // We still record a marker. Glean.jogCat.jogCustomDist.accumulateSamples([-7]); }); Assert.deepEqual(markers, [ { type: "DistMetric", cat: "jog_cat", id: "jog_custom_dist", samples: "[7,268435458]", }, { type: "DistMetric", cat: "jog_cat", id: "jog_custom_dist", samples: "[-7]", }, ]); }); add_task(async function test_fog_timing_distribution() { let markers = await runWithProfilerAndGetMarkers("TimingDist", async () => { let t1 = Glean.testOnly.whatTimeIsIt.start(); let t2 = Glean.testOnly.whatTimeIsIt.start(); await sleep(5); let t3 = Glean.testOnly.whatTimeIsIt.start(); Glean.testOnly.whatTimeIsIt.cancel(t1); await sleep(5); Glean.testOnly.whatTimeIsIt.stopAndAccumulate(t2); // 10ms Glean.testOnly.whatTimeIsIt.stopAndAccumulate(t3); // 5ms // samples are measured in microseconds, since that's the unit listed in metrics.yaml Glean.testOnly.whatTimeIsIt.accumulateSingleSample(5000); // 5ms Glean.testOnly.whatTimeIsIt.accumulateSamples([2000, 8000]); // 10ms // Labeled timing distribution submetrics let t4 = Glean.testOnly.whereHasTheTimeGone.west.start(); let t5 = Glean.testOnly.whereHasTheTimeGone.west.start(); await sleep(5); let t6 = Glean.testOnly.whereHasTheTimeGone.west.start(); Glean.testOnly.whereHasTheTimeGone.west.cancel(t4); await sleep(5); Glean.testOnly.whereHasTheTimeGone.west.stopAndAccumulate(t5); // 10ms Glean.testOnly.whereHasTheTimeGone.west.stopAndAccumulate(t6); // 5ms Glean.testOnly.whereHasTheTimeGone.west.accumulateSingleSample(5000); // 5ms Glean.testOnly.whereHasTheTimeGone.west.accumulateSamples([2000, 8000]); // 10ms }); Assert.deepEqual(markers, [ { type: "TimingDist", cat: "test_only", id: "what_time_is_it", timer_id: 1, }, { type: "TimingDist", cat: "test_only", id: "what_time_is_it", timer_id: 2, }, { type: "TimingDist", cat: "test_only", id: "what_time_is_it", timer_id: 3, }, { type: "TimingDist", cat: "test_only", id: "what_time_is_it", timer_id: 1, }, { type: "TimingDist", cat: "test_only", id: "what_time_is_it", timer_id: 2, }, { type: "TimingDist", cat: "test_only", id: "what_time_is_it", timer_id: 3, }, { type: "TimingDist", cat: "test_only", id: "what_time_is_it", sample: "5000", }, { type: "TimingDist", cat: "test_only", id: "what_time_is_it", samples: "[2000,8000]", }, { type: "TimingDist", cat: "test_only", id: "where_has_the_time_gone", label: "west", timer_id: 1, }, { type: "TimingDist", cat: "test_only", id: "where_has_the_time_gone", label: "west", timer_id: 2, }, { type: "TimingDist", cat: "test_only", id: "where_has_the_time_gone", label: "west", timer_id: 3, }, { type: "TimingDist", cat: "test_only", id: "where_has_the_time_gone", label: "west", timer_id: 1, }, { type: "TimingDist", cat: "test_only", id: "where_has_the_time_gone", label: "west", timer_id: 2, }, { type: "TimingDist", cat: "test_only", id: "where_has_the_time_gone", label: "west", timer_id: 3, }, { type: "TimingDist", cat: "test_only", id: "where_has_the_time_gone", label: "west", sample: "5000", }, { type: "TimingDist", cat: "test_only", id: "where_has_the_time_gone", label: "west", samples: "[2000,8000]", }, ]); }); add_task(async function test_jog_timing_distribution() { Services.fog.testRegisterRuntimeMetric( "timing_distribution", "jog_cat", "jog_timing_dist", ["test-ping"], `"ping"`, false, JSON.stringify({ time_unit: "microsecond" }) ); let markers = await runWithProfilerAndGetMarkers("TimingDist", async () => { let t1 = Glean.jogCat.jogTimingDist.start(); let t2 = Glean.jogCat.jogTimingDist.start(); await sleep(5); let t3 = Glean.jogCat.jogTimingDist.start(); Glean.jogCat.jogTimingDist.cancel(t1); await sleep(5); Glean.jogCat.jogTimingDist.stopAndAccumulate(t2); // 10ms Glean.jogCat.jogTimingDist.stopAndAccumulate(t3); // 5ms // samples are measured in microseconds, since that's the unit listed in metrics.yaml Glean.jogCat.jogTimingDist.accumulateSingleSample(5000); // 5ms Glean.jogCat.jogTimingDist.accumulateSamples([2000, 8000]); // 10ms }); Assert.deepEqual(markers, [ { type: "TimingDist", cat: "jog_cat", id: "jog_timing_dist", timer_id: 1 }, { type: "TimingDist", cat: "jog_cat", id: "jog_timing_dist", timer_id: 2 }, { type: "TimingDist", cat: "jog_cat", id: "jog_timing_dist", timer_id: 3 }, { type: "TimingDist", cat: "jog_cat", id: "jog_timing_dist", timer_id: 1 }, { type: "TimingDist", cat: "jog_cat", id: "jog_timing_dist", timer_id: 2 }, { type: "TimingDist", cat: "jog_cat", id: "jog_timing_dist", timer_id: 3 }, { type: "TimingDist", cat: "jog_cat", id: "jog_timing_dist", sample: "5000", }, { type: "TimingDist", cat: "jog_cat", id: "jog_timing_dist", samples: "[2000,8000]", }, ]); }); add_task(async function test_jog_labeled_timing_distribution() { Services.fog.testRegisterRuntimeMetric( "labeled_timing_distribution", "jog_cat", "jog_labeled_timing_dist", ["test-ping"], `"ping"`, false, JSON.stringify({ time_unit: "microsecond" }) ); let markers = await runWithProfilerAndGetMarkers("TimingDist", async () => { let t1 = Glean.jogCat.jogLabeledTimingDist.label1.start(); let t2 = Glean.jogCat.jogLabeledTimingDist.label1.start(); await sleep(5); let t3 = Glean.jogCat.jogLabeledTimingDist.label1.start(); Glean.jogCat.jogLabeledTimingDist.label1.cancel(t1); await sleep(5); Glean.jogCat.jogLabeledTimingDist.label1.stopAndAccumulate(t2); // 10ms Glean.jogCat.jogLabeledTimingDist.label1.stopAndAccumulate(t3); // 5ms }); Assert.deepEqual(markers, [ { type: "TimingDist", cat: "jog_cat", id: "jog_labeled_timing_dist", label: "label1", timer_id: 1, }, { type: "TimingDist", cat: "jog_cat", id: "jog_labeled_timing_dist", label: "label1", timer_id: 2, }, { type: "TimingDist", cat: "jog_cat", id: "jog_labeled_timing_dist", label: "label1", timer_id: 3, }, { type: "TimingDist", cat: "jog_cat", id: "jog_labeled_timing_dist", label: "label1", timer_id: 1, }, { type: "TimingDist", cat: "jog_cat", id: "jog_labeled_timing_dist", label: "label1", timer_id: 2, }, { type: "TimingDist", cat: "jog_cat", id: "jog_labeled_timing_dist", label: "label1", timer_id: 3, }, ]); }); add_task(async function test_fog_quantity() { let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { Glean.testOnly.meaningOfLife.set(42); // Labeled quantity submetrics Glean.testOnly.buttonJars.up.set(2); Glean.testOnly.buttonJars.curling.set(0); // Invalid label, but we still expect to see a marker Glean.testOnly.buttonJars["1".repeat(72)].set(0); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "test_only", id: "meaning_of_life", val: 42 }, { type: "IntLikeMetric", cat: "test_only", id: "button_jars", label: "up", val: 2, }, { type: "IntLikeMetric", cat: "test_only", id: "button_jars", label: "curling", val: 0, }, { type: "IntLikeMetric", cat: "test_only", id: "button_jars", label: "1".repeat(72), val: 0, }, ]); }); add_task(async function test_jog_quantity_markers() { Services.fog.testRegisterRuntimeMetric( "quantity", "jog_cat", "jog_quantity", ["test-ping"], `"ping"`, false ); let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { Glean.jogCat.jogQuantity.set(42); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "jog_cat", id: "jog_quantity", val: 42 }, ]); }); add_task(async function test_jog_labeled_quantity_markers() { Services.fog.testRegisterRuntimeMetric( "labeled_quantity", "jog_cat", "jog_labeled_quantity", ["test-ping"], `"ping"`, false ); Assert.equal( undefined, Glean.jogCat.jogLabeledQuantity.label_1.testGetValue(), "New labels with no values should return undefined" ); let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { Glean.jogCat.jogLabeledQuantity.label_1.set(9000); Glean.jogCat.jogLabeledQuantity.label_2.set(0); Glean.jogCat.jogLabeledQuantity.NowValidLabel.set(100); Glean.jogCat.jogLabeledQuantity["1".repeat(72)].set(true); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "jog_cat", id: "jog_labeled_quantity", label: "label_1", val: 9000, }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_labeled_quantity", label: "label_2", val: 0, }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_labeled_quantity", label: "NowValidLabel", val: 100, }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_labeled_quantity", label: "1".repeat(72), val: 1, }, ]); }); add_task(async function test_fog_rate() { let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { // 1) Standard rate with internal denominator Glean.testOnlyIpc.irate.addToNumerator(22); Glean.testOnlyIpc.irate.addToDenominator(7); // 2) Rate with external denominator Glean.testOnlyIpc.anExternalDenominator.add(11); Glean.testOnlyIpc.rateWithExternalDenominator.addToNumerator(121); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "test_only.ipc", id: "irate", val: 22 }, { type: "IntLikeMetric", cat: "test_only.ipc", id: "irate", val: 7 }, { type: "IntLikeMetric", cat: "test_only.ipc", id: "an_external_denominator", val: 11, }, { type: "IntLikeMetric", cat: "test_only.ipc", id: "rate_with_external_denominator", val: 121, }, ]); }); add_task(async function test_jog_rate_markers() { Services.fog.testRegisterRuntimeMetric( "rate", "jog_cat", "jog_rate", ["test-ping"], `"ping"`, false ); Services.fog.testRegisterRuntimeMetric( "denominator", "jog_cat", "jog_denominator", ["test-ping"], `"ping"`, false, JSON.stringify({ numerators: [ { name: "jog_rate_ext", category: "jog_cat", send_in_pings: ["test-ping"], lifetime: "ping", disabled: false, }, ], }) ); Services.fog.testRegisterRuntimeMetric( "rate", "jog_cat", "jog_rate_ext", ["test-ping"], `"ping"`, false ); let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { // 1) Standard rate with internal denominator Glean.jogCat.jogRate.addToNumerator(22); Glean.jogCat.jogRate.addToDenominator(7); // 2) Rate with external denominator Glean.jogCat.jogDenominator.add(11); Glean.jogCat.jogRateExt.addToNumerator(121); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "jog_cat", id: "jog_rate", val: 22 }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_rate", val: 7 }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_denominator", val: 11 }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_rate_ext", val: 121 }, ]); }); add_task(async function test_fog_url() { const value = "https://www.example.com/fog"; let markers = await runWithProfilerAndGetMarkers("UrlMetric", () => { Glean.testOnlyIpc.aUrl.set(value); }); Assert.deepEqual(markers, [ { type: "UrlMetric", cat: "test_only.ipc", id: "a_url", val: "https://www.example.com/fog", }, ]); }); add_task(async function test_fog_text() { const value = "Before the risin' sun, we fly, So many roads to choose, We'll start out walkin' and learn to run, (We've only just begun)"; let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { Glean.testOnlyIpc.aText.set(value); }); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "test_only.ipc", id: "a_text", val: value, }, ]); }); add_task(async function test_jog_text() { const value = "In the heart of the Opéra district in Paris, the Cédric Grolet Opéra bakery-pastry shop is a veritable temple of gourmet delights."; Services.fog.testRegisterRuntimeMetric( "text", "test_only.jog", "a_text", ["test-ping"], `"ping"`, false ); let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { Glean.testOnlyJog.aText.set(value); }); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "test_only.jog", id: "a_text", val: value, }, ]); }); add_task(async function test_fog_text_unusual_character() { const value = "The secret to Dominique Ansel's viennoiserie is the use of Isigny Sainte-Mère butter and Les Grands Moulins de Paris flour"; let markers = await runWithProfilerAndGetMarkers("StringLikeMetric", () => { Glean.testOnlyIpc.aText.set(value); }); Assert.deepEqual(markers, [ { type: "StringLikeMetric", cat: "test_only.ipc", id: "a_text", val: value, }, ]); }); add_task( { // Object metrics don't support profile markers in artifact builds due to // having no centralized metric instance storage. skip_if: () => Services.prefs.getBoolPref("telemetry.fog.artifact_build", false), }, async function test_fog_object_markers() { if (!Glean.testOnly.balloons) { // FIXME(bug 1883857): object metric type not available, e.g. in artifact builds. // Skipping this test. return; } let markers = await runWithProfilerAndGetMarkers("ObjectMetric", () => { let balloons = [ { colour: "red", diameter: 5 }, { colour: "blue", diameter: 7 }, { colour: "orange" }, ]; Glean.testOnly.balloons.set(balloons); // These values are coerced to null or removed. balloons = [ { colour: "inf", diameter: Infinity }, { colour: "negative-inf", diameter: -1 / 0 }, { colour: "nan", diameter: NaN }, { colour: "undef", diameter: undefined }, ]; Glean.testOnly.balloons.set(balloons); // colour != color. // This is invalid, but still produces a marker! let invalid = [{ color: "orange" }, { color: "red", diameter: "small" }]; Glean.testOnly.balloons.set(invalid); Services.fog.testResetFOG(); // set again to ensure it's stored balloons = [ { colour: "red", diameter: 5 }, { colour: "blue", diameter: 7 }, ]; Glean.testOnly.balloons.set(balloons); // Again, invalid, but produces a marker invalid = [{ colour: "red", diameter: 5, extra: "field" }]; Glean.testOnly.balloons.set(invalid); // More complex objects: Glean.testOnly.crashStack.set({}); let stack = { status: "OK", crash_info: { typ: "main", address: "0xf001ba11", crashing_thread: 1, }, main_module: 0, modules: [ { base_addr: "0x00000000", end_addr: "0x00004000", }, ], }; Glean.testOnly.crashStack.set(stack); stack = { status: "OK", modules: [ { base_addr: "0x00000000", end_addr: "0x00004000", }, ], }; Glean.testOnly.crashStack.set(stack); stack = { status: "OK", modules: [], }; Glean.testOnly.crashStack.set(stack); stack = { status: "OK", }; Glean.testOnly.crashStack.set(stack); }); let expected_markers = [ { type: "ObjectMetric", id: "testOnly.balloons", value: [ { colour: "red", diameter: 5 }, { colour: "blue", diameter: 7 }, { colour: "orange" }, ], }, // Check that values are coerced or removed { type: "ObjectMetric", id: "testOnly.balloons", value: [ { colour: "inf", diameter: null }, { colour: "negative-inf", diameter: null }, { colour: "nan", diameter: null }, { colour: "undef" }, ], }, // Invalid glean object, but still produces a marker { type: "ObjectMetric", id: "testOnly.balloons", value: [{ color: "orange" }, { color: "red", diameter: "small" }], }, { type: "ObjectMetric", id: "testOnly.balloons", value: [ { colour: "red", diameter: 5 }, { colour: "blue", diameter: 7 }, ], }, // Invalid glean object, but still produces a marker { type: "ObjectMetric", id: "testOnly.balloons", value: [{ colour: "red", diameter: 5, extra: "field" }], }, { type: "ObjectMetric", id: "testOnly.crashStack", value: {}, }, { type: "ObjectMetric", id: "testOnly.crashStack", value: { status: "OK", crash_info: { typ: "main", address: "0xf001ba11", crashing_thread: 1, }, main_module: 0, modules: [{ base_addr: "0x00000000", end_addr: "0x00004000" }], }, }, { type: "ObjectMetric", id: "testOnly.crashStack", value: { status: "OK", modules: [{ base_addr: "0x00000000", end_addr: "0x00004000" }], }, }, // Modules gets erased within Glean, but it still shows up in a marker { type: "ObjectMetric", id: "testOnly.crashStack", value: { status: "OK", modules: [] }, }, { type: "ObjectMetric", id: "testOnly.crashStack", value: { status: "OK" }, }, ]; // Parse the `value` field of each marker into a JS object so that we can do // a deep equality check, ignoring undefined values. markers.forEach(m => { if (m.value !== undefined) { m.value = JSON.parse(m.value); } }); Assert.deepEqual(markers, expected_markers); } ); add_task( // FIXME(1898464): ride-along pings are not handled correctly in artifact builds. { skip_if: () => Services.prefs.getBoolPref("telemetry.fog.artifact_build", false), }, async function test_fog_ping_markers() { let markers = await runWithProfilerAndGetMarkers("GleanPing", () => { // Do stuff that will generate Ping submissions: // From test_fog_custom_pings: Glean.testOnly.onePingOneBool.set(false); GleanPings.onePingOnly.submit(); // From test_recursive_testBeforeNextSubmit: Glean.testOnly.onePingOneBool.set(false); GleanPings.onePingOnly.submit(); GleanPings.onePingOnly.submit(); GleanPings.onePingOnly.submit(); // From test_fog_ride_along_pings: Glean.testOnly.badCode.add(37); GleanPings.testPing.submit(); }); Assert.deepEqual(markers, [ { type: "GleanPing", id: "one-ping-only" }, { type: "GleanPing", id: "one-ping-only" }, { type: "GleanPing", id: "one-ping-only" }, { type: "GleanPing", id: "one-ping-only" }, { type: "GleanPing", id: "test-ping" }, ]); } ); add_task(async function test_fog_static_dual_labeled_counter_markers() { let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { // Statically labeled counter submetrics Glean.testOnly.keyedCategories.get("rei", "cut").add(1); Glean.testOnly.keyedCategories.get("gong", "lasered").add(4); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "test_only", id: "keyed_categories", label: "rei, cut", val: 1, }, { type: "IntLikeMetric", cat: "test_only", id: "keyed_categories", label: "gong, lasered", val: 4, }, ]); }); add_task(async function test_jog_dual_labeled_counter_markers() { Services.fog.testRegisterRuntimeMetric( "dual_labeled_counter", "jog_cat", "jog_dlc", ["test-ping"], `"ping"`, false ); let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { Glean.jogCat.jogDlc.get("saunter", "thisaway").add(8); Glean.jogCat.jogDlc.get("it be", "somewhat difficult").add(16); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "jog_cat", id: "jog_dlc", label: "saunter, thisaway", val: 8, }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_dlc", label: "it be, somewhat difficult", val: 16, }, ]); }); add_task( async function test_jog_dual_labeled_counter_with_static_keys_markers() { Services.fog.testRegisterRuntimeMetric( "dual_labeled_counter", "jog_cat", "jog_dlc_keys", ["test-ping"], `"ping"`, false, JSON.stringify({ ordered_keys: ["raising heck", "duke of pop"] }) ); let markers = await runWithProfilerAndGetMarkers("IntLikeMetric", () => { Glean.jogCat.jogDlcKeys .get("raising heck", "you are out of whack") .add(32); Glean.jogCat.jogDlcKeys.get("duke of pop", "you speak in excess").add(64); }); Assert.deepEqual(markers, [ { type: "IntLikeMetric", cat: "jog_cat", id: "jog_dlc_keys", label: "raising heck, you are out of whack", val: 32, }, { type: "IntLikeMetric", cat: "jog_cat", id: "jog_dlc_keys", label: "duke of pop, you speak in excess", val: 64, }, ]); } );