Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'android'
- Manifest: toolkit/components/glean/tests/xpcshell/xpcshell.toml
/* Any copyright is dedicated to the Public Domain.
"use strict";
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
const { ContentTaskUtils } = ChromeUtils.importESModule(
);
const { setTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
const Telemetry = Services.telemetry;
const { TelemetryTestUtils } = ChromeUtils.importESModule(
);
function sleep(ms) {
/* eslint-disable mozilla/no-arbitrary-setTimeout */
return new Promise(resolve => setTimeout(resolve, ms));
}
function scalarValue(aScalarName, aProcessName) {
let snapshot = Telemetry.getSnapshotForScalars();
return aProcessName in snapshot
? snapshot[aProcessName][aScalarName]
: undefined;
}
function keyedScalarValue(aScalarName, aProcessName) {
let snapshot = Telemetry.getSnapshotForKeyedScalars();
return aProcessName in snapshot
? snapshot[aProcessName][aScalarName]
: undefined;
}
add_setup({ skip_if: () => !runningInParent }, function test_setup() {
// Give FOG a temp profile to init within.
do_get_profile();
// Allows these tests to properly run on e.g. Thunderbird
Services.prefs.setBoolPref(
"toolkit.telemetry.testing.overrideProductsCheck",
true
);
// We need to initialize it once, otherwise operations will be stuck in the pre-init queue.
// on Android FOG is set up through head.js
if (AppConstants.platform != "android") {
Services.fog.initializeFOG();
}
});
const COUNT = 42;
const CHEESY_STRING = "a very cheesy string!";
const CHEESIER_STRING = "a much cheesier string!";
const CUSTOM_SAMPLES = [3, 4];
const EVENT_EXTRA = { extra1: "so very extra" };
const MEMORIES = [13, 31];
const MEMORY_BUCKETS = ["13193", "31378"]; // buckets are strings : |
const A_LABEL_COUNT = 3;
const ANOTHER_LABEL_COUNT = 5;
const INVALID_COUNTERS = 7;
const IRATE_NUMERATOR = 44;
const IRATE_DENOMINATOR = 14;
const LABELED_MEMORY_BUCKETS = ["13509772", "32131834"];
add_task({ skip_if: () => runningInParent }, async function run_child_stuff() {
let oldCanRecordBase = Telemetry.canRecordBase;
Telemetry.canRecordBase = true; // Ensure we're able to record things.
Glean.testOnlyIpc.aCounter.add(COUNT);
Glean.testOnlyIpc.aStringList.add(CHEESY_STRING);
Glean.testOnlyIpc.aStringList.add(CHEESIER_STRING);
Glean.testOnlyIpc.noExtraEvent.record();
Glean.testOnlyIpc.anEvent.record(EVENT_EXTRA);
for (let memory of MEMORIES) {
Glean.testOnlyIpc.aMemoryDist.accumulate(memory);
}
let t1 = Glean.testOnlyIpc.aTimingDist.start();
let t2 = Glean.testOnlyIpc.aTimingDist.start();
await sleep(5);
let t3 = Glean.testOnlyIpc.aTimingDist.start();
Glean.testOnlyIpc.aTimingDist.cancel(t1);
await sleep(5);
Glean.testOnlyIpc.aTimingDist.stopAndAccumulate(t2); // 10ms
Glean.testOnlyIpc.aTimingDist.stopAndAccumulate(t3); // 5ms
Glean.testOnlyIpc.aCustomDist.accumulateSamples(CUSTOM_SAMPLES);
Glean.testOnlyIpc.aLabeledCounter.a_label.add(A_LABEL_COUNT);
Glean.testOnlyIpc.aLabeledCounter.another_label.add(ANOTHER_LABEL_COUNT);
// Has to be different from aLabeledCounter so the error we record doesn't
// get in the way.
Glean.testOnlyIpc.anotherLabeledCounter["1".repeat(72)].add(INVALID_COUNTERS);
Glean.testOnlyIpc.irate.addToNumerator(IRATE_NUMERATOR);
Glean.testOnlyIpc.irate.addToDenominator(IRATE_DENOMINATOR);
Glean.testOnly.mabelsCustomLabelLengths.weird_jars.accumulateSamples(
CUSTOM_SAMPLES
);
for (let memory of MEMORIES) {
Glean.testOnly.whatDoYouRemember.breakfast.accumulate(memory);
}
let l1 = Glean.testOnly.whereHasTheTimeGone["long time passing"].start();
let l2 = Glean.testOnly.whereHasTheTimeGone["long time passing"].start();
await sleep(5);
let l3 = Glean.testOnly.whereHasTheTimeGone["long time passing"].start();
Glean.testOnly.whereHasTheTimeGone["long time passing"].cancel(l1);
await sleep(5);
Glean.testOnly.whereHasTheTimeGone["long time passing"].stopAndAccumulate(l2); // 10ms
Glean.testOnly.whereHasTheTimeGone["long time passing"].stopAndAccumulate(l3); // 5ms
Glean.testOnlyIpc.anUnorderedBool.set(true);
Glean.testOnlyIpc.anUnorderedLabeledBoolean.aLabel.set(true);
Telemetry.canRecordBase = oldCanRecordBase;
});
add_task(
{ skip_if: () => !runningInParent },
async function test_child_metrics() {
// Clear any stray Telemetry data
Telemetry.clearScalars();
Telemetry.getSnapshotForHistograms("main", true);
Telemetry.clearEvents();
await run_test_in_child("test_GIFFTIPC.js");
// Wait for both IPC mechanisms to flush.
await Services.fog.testFlushAllChildren();
await ContentTaskUtils.waitForCondition(() => {
let snapshot = Telemetry.getSnapshotForKeyedScalars();
return (
"content" in snapshot &&
// Update this to be the mirrored-to probe of the bottom-most call in
// run_child_stuff().
"telemetry.test.mirror_for_unordered_labeled_bool" in snapshot.content
);
}, "failed to find content telemetry in parent");
// boolean
Assert.ok(Glean.testOnlyIpc.anUnorderedBool.testGetValue());
Assert.ok(
scalarValue("telemetry.test.mirror_for_unordered_bool", "content"),
"content-process Scalar has expected bool value"
);
// counter
Assert.equal(Glean.testOnlyIpc.aCounter.testGetValue(), COUNT);
Assert.equal(
scalarValue("telemetry.test.mirror_for_counter", "content"),
COUNT,
"content-process Scalar has expected count"
);
// custom_distribution
const customSampleSum = CUSTOM_SAMPLES.reduce((acc, a) => acc + a, 0);
const customData = Glean.testOnlyIpc.aCustomDist.testGetValue("store1");
Assert.equal(customSampleSum, customData.sum, "Sum's correct");
for (let [bucket, count] of Object.entries(customData.values)) {
Assert.ok(
count == 0 || (count == CUSTOM_SAMPLES.length && bucket == 1), // both values in the low bucket
`Only two buckets have a sample ${bucket} ${count}`
);
}
const histSnapshot = Telemetry.getSnapshotForHistograms(
"main",
false,
false
);
const histData = histSnapshot.content.TELEMETRY_TEST_MIRROR_FOR_CUSTOM;
Assert.equal(customSampleSum, histData.sum, "Sum in histogram's correct");
Assert.equal(2, histData.values["1"], "Two samples in the first bucket");
// datetime
// Doesn't work over IPC
// event
var events = Glean.testOnlyIpc.noExtraEvent.testGetValue();
Assert.equal(1, events.length);
Assert.equal("test_only.ipc", events[0].category);
Assert.equal("no_extra_event", events[0].name);
events = Glean.testOnlyIpc.anEvent.testGetValue();
Assert.equal(1, events.length);
Assert.equal("test_only.ipc", events[0].category);
Assert.equal("an_event", events[0].name);
Assert.deepEqual(EVENT_EXTRA, events[0].extra);
TelemetryTestUtils.assertEvents(
[
[
"telemetry.test",
"not_expired_optout",
"object1",
undefined,
undefined,
],
["telemetry.test", "mirror_with_extra", "object1", null, EVENT_EXTRA],
],
{ category: "telemetry.test" },
{ process: "content" }
);
// labeled_boolean
Assert.ok(
Glean.testOnlyIpc.anUnorderedLabeledBoolean.aLabel.testGetValue()
);
let value = keyedScalarValue(
"telemetry.test.mirror_for_unordered_labeled_bool",
"content"
);
Assert.deepEqual({ aLabel: true }, value);
// labeled_counter
const counters = Glean.testOnlyIpc.aLabeledCounter;
Assert.equal(counters.a_label.testGetValue(), A_LABEL_COUNT);
Assert.equal(counters.another_label.testGetValue(), ANOTHER_LABEL_COUNT);
Assert.throws(
() => Glean.testOnlyIpc.anotherLabeledCounter.__other__.testGetValue(),
/DataError/,
"Invalid labels record errors, which throw"
);
value = keyedScalarValue(
"telemetry.test.another_mirror_for_labeled_counter",
"content"
);
Assert.deepEqual(
{
a_label: A_LABEL_COUNT,
another_label: ANOTHER_LABEL_COUNT,
},
value
);
value = keyedScalarValue(
"telemetry.test.mirror_for_labeled_counter",
"content"
);
Assert.deepEqual(
{
["1".repeat(72)]: INVALID_COUNTERS,
},
value
);
// labeled_string
// Doesn't work over IPC
// memory_distribution
const memoryData = Glean.testOnlyIpc.aMemoryDist.testGetValue();
const memorySum = MEMORIES.reduce((acc, a) => acc + a, 0);
// The sum's in bytes, but the metric's in KB
Assert.equal(memorySum * 1024, memoryData.sum);
for (let [bucket, count] of Object.entries(memoryData.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 memoryHist = histSnapshot.content.TELEMETRY_TEST_LINEAR;
Assert.equal(
memorySum,
memoryHist.sum,
"Histogram's in `memory_unit` units"
);
Assert.equal(2, memoryHist.values["1"], "Samples are in the right bucket");
// quantity
// Doesn't work over IPC
// rate
Assert.deepEqual(
{ numerator: IRATE_NUMERATOR, denominator: IRATE_DENOMINATOR },
Glean.testOnlyIpc.irate.testGetValue()
);
Assert.deepEqual(
{ numerator: IRATE_NUMERATOR, denominator: IRATE_DENOMINATOR },
keyedScalarValue("telemetry.test.mirror_for_rate", "content")
);
// string
// Doesn't work over IPC
// string_list
// Note: this will break if string list ever rearranges its items.
const cheesyStrings = Glean.testOnlyIpc.aStringList.testGetValue();
Assert.deepEqual(cheesyStrings, [CHEESY_STRING, CHEESIER_STRING]);
// Note: this will break if keyed scalars rearrange their items.
Assert.deepEqual(
{
[CHEESY_STRING]: true,
[CHEESIER_STRING]: true,
},
keyedScalarValue("telemetry.test.keyed_boolean_kind", "content")
);
// timespan
// Doesn't work over IPC
// timing_distribution
const NANOS_IN_MILLIS = 1e6;
const times = Glean.testOnlyIpc.aTimingDist.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, [, count]) => acc + count, 0)
);
const timingHist = histSnapshot.content.TELEMETRY_TEST_EXPONENTIAL;
Assert.greaterOrEqual(timingHist.sum, 13, "Histogram's in milliseconds.");
// Both values, 10 and 5, are truncated by a cast in AccumulateTimeDelta
// Minimally downcast 9. + 4. could realistically result in 13.
Assert.equal(
2,
Object.entries(timingHist.values).reduce(
(acc, [, count]) => acc + count,
0
),
"Only two samples"
);
// uuid
// Doesn't work over IPC
// labeled_custom_distribution
const labeledCustomData =
Glean.testOnly.mabelsCustomLabelLengths.weird_jars.testGetValue();
Assert.equal(customSampleSum, labeledCustomData.sum, "Sum's correct");
for (let [bucket, count] of Object.entries(labeledCustomData.values)) {
Assert.ok(
count == 0 || (count == CUSTOM_SAMPLES.length && bucket == 1), // both values in the low bucket
`Only two buckets have a sample ${bucket} ${count}`
);
}
const keyedHistSnapshot = Telemetry.getSnapshotForKeyedHistograms(
"main",
false,
false
);
const keyedHistData = keyedHistSnapshot.content.TELEMETRY_TEST_KEYED_LINEAR;
Assert.ok("weird_jars" in keyedHistData, "Key's present");
Assert.equal(
customSampleSum,
keyedHistData.weird_jars.sum,
"Sum in histogram's correct"
);
Assert.equal(
2,
keyedHistData.weird_jars.values["1"],
"Two samples in the first bucket"
);
// labeled_memory_distribution
const labeledMemoryData =
Glean.testOnly.whatDoYouRemember.breakfast.testGetValue();
const labeledMemorySum = MEMORIES.reduce((acc, a) => acc + a, 0);
// The sum's in bytes, but the metric's in MB
Assert.equal(labeledMemorySum * 1024 * 1024, labeledMemoryData.sum);
info(JSON.stringify(labeledMemoryData.values));
for (let [bucket, count] of Object.entries(labeledMemoryData.values)) {
// We could assert instead, but let's skip to save the logspam.
if (count == 0) {
continue;
}
Assert.ok(count == 1 && LABELED_MEMORY_BUCKETS.includes(bucket));
}
const labeledMemoryHist =
keyedHistSnapshot.content.TELEMETRY_TEST_MIRROR_FOR_LABELED_MEMORY;
Assert.ok("breakfast" in labeledMemoryHist, "Has key");
Assert.equal(
memorySum,
labeledMemoryHist.breakfast.sum,
"Histogram's in `memory_unit` units"
);
Assert.equal(
2,
labeledMemoryHist.breakfast.values["1"],
"Samples are in the right bucket"
);
// labeled_timing_distribution
const lTimes =
Glean.testOnly.whereHasTheTimeGone["long time passing"].testGetValue();
Assert.greater(lTimes.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(lTimes.values).reduce((acc, [, count]) => acc + count, 0)
);
const labeledTimingHist =
keyedHistSnapshot.content.TELEMETRY_TEST_MIRROR_FOR_LABELED_TIMING;
Assert.ok("long time passing" in labeledTimingHist);
// Both values, 10 and 5, are truncated by a cast in AccumulateTimeDelta
// Minimally downcast 9. + 4. could realistically result in 13.
Assert.greaterOrEqual(
labeledTimingHist["long time passing"].sum,
13,
"Histogram's in milliseconds."
);
Assert.equal(
2,
Object.entries(labeledTimingHist["long time passing"].values).reduce(
(acc, [, count]) => acc + count,
0
),
"Only two samples"
);
}
);