/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const { AppConstants } = ChromeUtils.importESModule( "resource://gre/modules/AppConstants.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_task( /* on Android FOG is set up through head.js */ { skip_if: () => !runningInParent || AppConstants.platform == "android" }, function test_setup() { // Give FOG a temp profile to init within. do_get_profile(); // We need to initialize it once, otherwise operations will be stuck in the pre-init queue. Services.fog.initializeFOG(); } ); const COUNT = 42; const STRING = "a string!"; const ANOTHER_STRING = "another string!"; const EVENT_EXTRA = { extra1: "so very extra" }; const MEMORIES = [13, 31]; const MEMORY_BUCKETS = ["13509772", "32131834"]; // buckets are strings : | const COUNTERS_1 = 3; const COUNTERS_2 = 5; const INVALID_COUNTERS = 7; // It is CRUCIAL that we register metrics in the same order in the parent and // in the child or their metric ids will not line up and ALL WILL EXPLODE. const METRICS = [ ["counter", "jog_ipc", "jog_counter", ["test-only"], `"ping"`, false], ["string_list", "jog_ipc", "jog_string_list", ["test-only"], `"ping"`, false], ["event", "jog_ipc", "jog_event_no_extra", ["test-only"], `"ping"`, false], [ "event", "jog_ipc", "jog_event", ["test-only"], `"ping"`, false, JSON.stringify({ allowed_extra_keys: ["extra1"] }), ], [ "memory_distribution", "jog_ipc", "jog_memory_dist", ["test-only"], `"ping"`, false, JSON.stringify({ memory_unit: "megabyte" }), ], [ "timing_distribution", "jog_ipc", "jog_timing_dist", ["test-only"], `"ping"`, false, JSON.stringify({ time_unit: "nanosecond" }), ], [ "custom_distribution", "jog_ipc", "jog_custom_dist", ["test-only"], `"ping"`, false, JSON.stringify({ range_min: 1, range_max: 2147483646, bucket_count: 10, histogram_type: "linear", }), ], [ "labeled_counter", "jog_ipc", "jog_labeled_counter", ["test-only"], `"ping"`, false, ], [ "labeled_counter", "jog_ipc", "jog_labeled_counter_err", ["test-only"], `"ping"`, false, ], [ "labeled_counter", "jog_ipc", "jog_labeled_counter_with_labels", ["test-only"], `"ping"`, false, JSON.stringify({ ordered_labels: ["label_1", "label_2"] }), ], [ "labeled_counter", "jog_ipc", "jog_labeled_counter_with_labels_err", ["test-only"], `"ping"`, false, JSON.stringify({ ordered_labels: ["label_1", "label_2"] }), ], ["rate", "jog_ipc", "jog_rate", ["test-only"], `"ping"`, false], ]; add_task({ skip_if: () => runningInParent }, async function run_child_stuff() { // Ensure any _actual_ runtime metrics are registered first. // Otherwise the jog_ipc.* ones will have incorrect ids. Glean.testOnly.badCode; for (let metric of METRICS) { Services.fog.testRegisterRuntimeMetric(...metric); } Glean.jogIpc.jogCounter.add(COUNT); Glean.jogIpc.jogStringList.add(STRING); Glean.jogIpc.jogStringList.add(ANOTHER_STRING); Glean.jogIpc.jogEventNoExtra.record(); Glean.jogIpc.jogEvent.record(EVENT_EXTRA); for (let memory of MEMORIES) { Glean.jogIpc.jogMemoryDist.accumulate(memory); } let t1 = Glean.jogIpc.jogTimingDist.start(); let t2 = Glean.jogIpc.jogTimingDist.start(); await sleep(5); let t3 = Glean.jogIpc.jogTimingDist.start(); Glean.jogIpc.jogTimingDist.cancel(t1); await sleep(5); Glean.jogIpc.jogTimingDist.stopAndAccumulate(t2); // 10ms Glean.jogIpc.jogTimingDist.stopAndAccumulate(t3); // 5ms Glean.jogIpc.jogCustomDist.accumulateSamples([3, 4]); Glean.jogIpc.jogLabeledCounter.label_1.add(COUNTERS_1); Glean.jogIpc.jogLabeledCounter.label_2.add(COUNTERS_2); Glean.jogIpc.jogLabeledCounterErr["1".repeat(72)].add(INVALID_COUNTERS); Glean.jogIpc.jogLabeledCounterWithLabels.label_1.add(COUNTERS_1); Glean.jogIpc.jogLabeledCounterWithLabels.label_2.add(COUNTERS_2); Glean.jogIpc.jogLabeledCounterWithLabelsErr["1".repeat(72)].add( INVALID_COUNTERS ); Glean.jogIpc.jogRate.addToNumerator(44); Glean.jogIpc.jogRate.addToDenominator(14); }); add_task( { skip_if: () => !runningInParent }, async function test_child_metrics() { // Ensure any _actual_ runtime metrics are registered first. // Otherwise the jog_ipc.* ones will have incorrect ids. Glean.testOnly.badCode; for (let metric of METRICS) { Services.fog.testRegisterRuntimeMetric(...metric); } await run_test_in_child("test_JOGIPC.js"); await Services.fog.testFlushAllChildren(); Assert.equal(Glean.jogIpc.jogCounter.testGetValue(), COUNT); // Note: this will break if string list ever rearranges its items. const strings = Glean.jogIpc.jogStringList.testGetValue(); Assert.deepEqual(strings, [STRING, ANOTHER_STRING]); const data = Glean.jogIpc.jogMemoryDist.testGetValue(); Assert.equal(MEMORIES.reduce((a, b) => a + b, 0) * 1024 * 1024, data.sum); for (let [bucket, count] of Object.entries(data.values)) { // We could assert instead, but let's skip to save the logspam. if (count == 0) { continue; } Assert.ok(count == 1 && MEMORY_BUCKETS.includes(bucket)); } const customData = Glean.jogIpc.jogCustomDist.testGetValue(); Assert.equal(3 + 4, customData.sum, "Sum's correct"); for (let [bucket, count] of Object.entries(customData.values)) { Assert.ok( count == 0 || (count == 2 && bucket == 1), // both values in the low bucket `Only two buckets have a sample ${bucket} ${count}` ); } let events = Glean.jogIpc.jogEventNoExtra.testGetValue(); Assert.equal(1, events.length); Assert.equal("jog_ipc", events[0].category); Assert.equal("jog_event_no_extra", events[0].name); events = Glean.jogIpc.jogEvent.testGetValue(); Assert.equal(1, events.length); Assert.equal("jog_ipc", events[0].category); Assert.equal("jog_event", events[0].name); Assert.deepEqual(EVENT_EXTRA, events[0].extra); const NANOS_IN_MILLIS = 1e6; const EPSILON = 40000; // bug 1701949 const times = Glean.jogIpc.jogTimingDist.testGetValue(); Assert.greater(times.sum, 15 * NANOS_IN_MILLIS - EPSILON); // We can't guarantee any specific time values (thank you clocks), // but we can assert there are only two samples. Assert.equal( 2, Object.entries(times.values).reduce( (acc, [bucket, count]) => acc + count, 0 ) ); const labeledCounter = Glean.jogIpc.jogLabeledCounter; Assert.equal(labeledCounter.label_1.testGetValue(), COUNTERS_1); Assert.equal(labeledCounter.label_2.testGetValue(), COUNTERS_2); Assert.throws( () => Glean.jogIpc.jogLabeledCounterErr.__other__.testGetValue(), /DataError/, "Invalid labels record errors, which throw" ); const labeledCounterWLabels = Glean.jogIpc.jogLabeledCounterWithLabels; Assert.equal(labeledCounterWLabels.label_1.testGetValue(), COUNTERS_1); Assert.equal(labeledCounterWLabels.label_2.testGetValue(), COUNTERS_2); // TODO:(bug 1766515) - This should throw. /*Assert.throws( () => Glean.jogIpc.jogLabeledCounterWithLabelsErr.__other__.testGetValue(), /DataError/, "Invalid labels record errors, which throw" );*/ Assert.equal( Glean.jogIpc.jogLabeledCounterWithLabelsErr.__other__.testGetValue(), INVALID_COUNTERS ); Assert.deepEqual( { numerator: 44, denominator: 14 }, Glean.jogIpc.jogRate.testGetValue() ); } );