summaryrefslogtreecommitdiffstats
path: root/toolkit/components/glean/tests/xpcshell/test_Glean.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/glean/tests/xpcshell/test_Glean.js')
-rw-r--r--toolkit/components/glean/tests/xpcshell/test_Glean.js405
1 files changed, 405 insertions, 0 deletions
diff --git a/toolkit/components/glean/tests/xpcshell/test_Glean.js b/toolkit/components/glean/tests/xpcshell/test_Glean.js
new file mode 100644
index 0000000000..cfab9d77ff
--- /dev/null
+++ b/toolkit/components/glean/tests/xpcshell/test_Glean.js
@@ -0,0 +1,405 @@
+/* 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_setup(
+ /* on Android FOG is set up through 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();
+ }
+);
+
+add_task(function test_fog_counter_works() {
+ Glean.testOnly.badCode.add(31);
+ Assert.equal(31, Glean.testOnly.badCode.testGetValue("test-ping"));
+});
+
+add_task(async function test_fog_string_works() {
+ const value = "a cheesy string!";
+ Glean.testOnly.cheesyString.set(value);
+
+ Assert.equal(value, Glean.testOnly.cheesyString.testGetValue("test-ping"));
+});
+
+add_task(async function test_fog_string_list_works() {
+ const value = "a cheesy string!";
+ const value2 = "a cheesier string!";
+ const value3 = "the cheeziest of strings.";
+
+ const cheeseList = [value, value2];
+ Glean.testOnly.cheesyStringList.set(cheeseList);
+
+ let val = Glean.testOnly.cheesyStringList.testGetValue();
+ // Note: This is incredibly fragile and will break if we ever rearrange items
+ // in the string list.
+ Assert.deepEqual(cheeseList, val);
+
+ Glean.testOnly.cheesyStringList.add(value3);
+ Assert.ok(Glean.testOnly.cheesyStringList.testGetValue().includes(value3));
+});
+
+add_task(async function test_fog_timespan_works() {
+ Glean.testOnly.canWeTimeIt.start();
+ Glean.testOnly.canWeTimeIt.cancel();
+ Assert.equal(undefined, Glean.testOnly.canWeTimeIt.testGetValue());
+
+ // We start, briefly sleep and then stop.
+ // That guarantees some time to measure.
+ Glean.testOnly.canWeTimeIt.start();
+ await sleep(10);
+ Glean.testOnly.canWeTimeIt.stop();
+
+ Assert.ok(Glean.testOnly.canWeTimeIt.testGetValue("test-ping") > 0);
+});
+
+add_task(async function test_fog_timespan_throws_on_stop_wout_start() {
+ Glean.testOnly.canWeTimeIt.stop();
+ Assert.throws(
+ () => Glean.testOnly.canWeTimeIt.testGetValue(),
+ /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/,
+ "Should throw because stop was called without start."
+ );
+});
+
+add_task(async function test_fog_uuid_works() {
+ const kTestUuid = "decafdec-afde-cafd-ecaf-decafdecafde";
+ Glean.testOnly.whatIdIt.set(kTestUuid);
+ Assert.equal(kTestUuid, Glean.testOnly.whatIdIt.testGetValue("test-ping"));
+
+ Glean.testOnly.whatIdIt.generateAndSet();
+ // Since we generate v4 UUIDs, and the first character of the third group
+ // isn't 4, this won't ever collide with kTestUuid.
+ Assert.notEqual(kTestUuid, Glean.testOnly.whatIdIt.testGetValue("test-ping"));
+});
+
+add_task(function test_fog_datetime_works() {
+ const value = new Date("2020-06-11T12:00:00");
+
+ Glean.testOnly.whatADate.set(value.getTime() * 1000);
+
+ const received = Glean.testOnly.whatADate.testGetValue("test-ping");
+ Assert.equal(received.getTime(), value.getTime());
+});
+
+add_task(function test_fog_boolean_works() {
+ Glean.testOnly.canWeFlagIt.set(false);
+ Assert.equal(false, Glean.testOnly.canWeFlagIt.testGetValue("test-ping"));
+ // While you're here, might as well test that the ping name's optional.
+ Assert.equal(false, Glean.testOnly.canWeFlagIt.testGetValue());
+});
+
+add_task(async function test_fog_event_works() {
+ Glean.testOnlyIpc.noExtraEvent.record();
+ 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);
+
+ let extra = { extra1: "can set extras", extra2: "passing more data" };
+ Glean.testOnlyIpc.anEvent.record(extra);
+ 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(extra, events[0].extra);
+
+ let extra2 = {
+ extra1: "can set extras",
+ extra2: 37,
+ extra3_longer_name: false,
+ };
+ Glean.testOnlyIpc.eventWithExtra.record(extra2);
+ events = Glean.testOnlyIpc.eventWithExtra.testGetValue();
+ Assert.equal(1, events.length);
+ Assert.equal("test_only.ipc", events[0].category);
+ Assert.equal("event_with_extra", events[0].name);
+ let expectedExtra = {
+ extra1: "can set extras",
+ extra2: "37",
+ extra3_longer_name: "false",
+ };
+ Assert.deepEqual(expectedExtra, events[0].extra);
+
+ // Quantities need to be non-negative.
+ // This does not record a Glean error.
+ let extra4 = {
+ extra2: -1,
+ };
+ Glean.testOnlyIpc.eventWithExtra.record(extra4);
+ events = Glean.testOnlyIpc.eventWithExtra.testGetValue();
+ // Unchanged number of events
+ Assert.equal(1, events.length, "Recorded one event too many.");
+
+ // Invalid extra keys don't crash, the event is not recorded,
+ // but an error is recorded.
+ let extra3 = {
+ extra1_nonexistent_extra: "this does not crash",
+ };
+ Glean.testOnlyIpc.eventWithExtra.record(extra3);
+ Assert.throws(
+ () => Glean.testOnlyIpc.eventWithExtra.testGetValue(),
+ /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/,
+ "Should throw because of a recording error."
+ );
+});
+
+add_task(async function test_fog_memory_distribution_works() {
+ Glean.testOnly.doYouRemember.accumulate(7);
+ Glean.testOnly.doYouRemember.accumulate(17);
+
+ let data = Glean.testOnly.doYouRemember.testGetValue("test-ping");
+ // `data.sum` is in bytes, but the metric is in MB.
+ Assert.equal(24 * 1024 * 1024, data.sum, "Sum's correct");
+ for (let [bucket, count] of Object.entries(data.values)) {
+ Assert.ok(
+ count == 0 || (count == 1 && (bucket == 17520006 || bucket == 7053950)),
+ "Only two buckets have a sample"
+ );
+ }
+});
+
+add_task(async function test_fog_custom_distribution_works() {
+ Glean.testOnlyIpc.aCustomDist.accumulateSamples([7, 268435458]);
+
+ let data = Glean.testOnlyIpc.aCustomDist.testGetValue("store1");
+ Assert.equal(7 + 268435458, data.sum, "Sum's correct");
+ for (let [bucket, count] of Object.entries(data.values)) {
+ Assert.ok(
+ count == 0 || (count == 1 && (bucket == 1 || bucket == 268435456)),
+ `Only two buckets have a sample ${bucket} ${count}`
+ );
+ }
+
+ // Negative values will not be recorded, instead an error is recorded.
+ Glean.testOnlyIpc.aCustomDist.accumulateSamples([-7]);
+ Assert.throws(
+ () => Glean.testOnlyIpc.aCustomDist.testGetValue(),
+ /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/
+ );
+});
+
+add_task(
+ /* TODO(bug 1737520): Enable custom ping support on Android */
+ { skip_if: () => AppConstants.platform == "android" },
+ function test_fog_custom_pings() {
+ Assert.ok("onePingOnly" in GleanPings);
+ let submitted = false;
+ Glean.testOnly.onePingOneBool.set(false);
+ GleanPings.onePingOnly.testBeforeNextSubmit(reason => {
+ submitted = true;
+ Assert.equal(false, Glean.testOnly.onePingOneBool.testGetValue());
+ });
+ GleanPings.onePingOnly.submit();
+ Assert.ok(submitted, "Ping was submitted, callback was called.");
+ }
+);
+
+add_task(async function test_fog_timing_distribution_works() {
+ 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
+
+ let data = Glean.testOnly.whatTimeIsIt.testGetValue();
+ const NANOS_IN_MILLIS = 1e6;
+ // bug 1701949 - Sleep gets close, but sometimes doesn't wait long enough.
+ const EPSILON = 40000;
+
+ // Variance in timing makes getting the sum impossible to know.
+ Assert.greater(data.sum, 15 * NANOS_IN_MILLIS - EPSILON);
+
+ // No guarantees from timers means no guarantees on buckets.
+ // But we can guarantee it's only two samples.
+ Assert.equal(
+ 2,
+ Object.entries(data.values).reduce(
+ (acc, [bucket, count]) => acc + count,
+ 0
+ ),
+ "Only two buckets with samples"
+ );
+});
+
+add_task(async function test_fog_labels_conform() {
+ Glean.testOnly.mabelsLabelMaker.singleword.set("portmanteau");
+ Assert.equal(
+ "portmanteau",
+ Glean.testOnly.mabelsLabelMaker.singleword.testGetValue()
+ );
+ Glean.testOnly.mabelsLabelMaker.snake_case.set("snek");
+ Assert.equal(
+ "snek",
+ Glean.testOnly.mabelsLabelMaker.snake_case.testGetValue()
+ );
+ Glean.testOnly.mabelsLabelMaker["dash-character"].set("Dash Rendar");
+ Assert.equal(
+ "Dash Rendar",
+ Glean.testOnly.mabelsLabelMaker["dash-character"].testGetValue()
+ );
+ Glean.testOnly.mabelsLabelMaker["dot.separated"].set("dot product");
+ Assert.equal(
+ "dot product",
+ Glean.testOnly.mabelsLabelMaker["dot.separated"].testGetValue()
+ );
+ Glean.testOnly.mabelsLabelMaker.camelCase.set("wednesday");
+ Assert.throws(
+ () => Glean.testOnly.mabelsLabelMaker.camelCase.testGetValue(),
+ /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/,
+ "Should throw because of an invalid label."
+ );
+ Assert.throws(
+ () => Glean.testOnly.mabelsLabelMaker.__other__.testGetValue(),
+ /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/,
+ "Should throw because of an invalid label."
+ );
+ // This test _should_ throw because we are calling data after an invalid label
+ // has been set.
+ Assert.throws(
+ () => Glean.testOnly.mabelsLabelMaker["dot.separated"].testGetValue(),
+ /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/,
+ "Should throw because of an invalid label."
+ );
+});
+
+add_task(async function test_fog_labeled_boolean_works() {
+ Assert.equal(
+ undefined,
+ Glean.testOnly.mabelsLikeBalloons.at_parties.testGetValue(),
+ "New labels with no values should return undefined"
+ );
+ Glean.testOnly.mabelsLikeBalloons.at_parties.set(true);
+ Glean.testOnly.mabelsLikeBalloons.at_funerals.set(false);
+ Assert.equal(
+ true,
+ Glean.testOnly.mabelsLikeBalloons.at_parties.testGetValue()
+ );
+ Assert.equal(
+ false,
+ Glean.testOnly.mabelsLikeBalloons.at_funerals.testGetValue()
+ );
+ // What about invalid/__other__?
+ Assert.equal(
+ undefined,
+ Glean.testOnly.mabelsLikeBalloons.__other__.testGetValue()
+ );
+ Glean.testOnly.mabelsLikeBalloons.InvalidLabel.set(true);
+ Assert.throws(
+ () => Glean.testOnly.mabelsLikeBalloons.__other__.testGetValue(),
+ /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/,
+ "Should throw because of a recording error."
+ );
+});
+
+add_task(async function test_fog_labeled_counter_works() {
+ Assert.equal(
+ undefined,
+ Glean.testOnly.mabelsKitchenCounters.near_the_sink.testGetValue(),
+ "New labels with no values should return undefined"
+ );
+ Glean.testOnly.mabelsKitchenCounters.near_the_sink.add(1);
+ Glean.testOnly.mabelsKitchenCounters.with_junk_on_them.add(2);
+ Assert.equal(
+ 1,
+ Glean.testOnly.mabelsKitchenCounters.near_the_sink.testGetValue()
+ );
+ Assert.equal(
+ 2,
+ Glean.testOnly.mabelsKitchenCounters.with_junk_on_them.testGetValue()
+ );
+ // What about invalid/__other__?
+ Assert.equal(
+ undefined,
+ Glean.testOnly.mabelsKitchenCounters.__other__.testGetValue()
+ );
+ Glean.testOnly.mabelsKitchenCounters.InvalidLabel.add(1);
+ Assert.throws(
+ () => Glean.testOnly.mabelsKitchenCounters.__other__.testGetValue(),
+ /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/,
+ "Should throw because of a recording error."
+ );
+});
+
+add_task(async function test_fog_labeled_string_works() {
+ Assert.equal(
+ undefined,
+ Glean.testOnly.mabelsBalloonStrings.colour_of_99.testGetValue(),
+ "New labels with no values should return undefined"
+ );
+ Glean.testOnly.mabelsBalloonStrings.colour_of_99.set("crimson");
+ Glean.testOnly.mabelsBalloonStrings.string_lengths.set("various");
+ Assert.equal(
+ "crimson",
+ Glean.testOnly.mabelsBalloonStrings.colour_of_99.testGetValue()
+ );
+ Assert.equal(
+ "various",
+ Glean.testOnly.mabelsBalloonStrings.string_lengths.testGetValue()
+ );
+ // What about invalid/__other__?
+ Assert.equal(
+ undefined,
+ Glean.testOnly.mabelsBalloonStrings.__other__.testGetValue()
+ );
+ Glean.testOnly.mabelsBalloonStrings.InvalidLabel.set("valid");
+ Assert.throws(
+ () => Glean.testOnly.mabelsBalloonStrings.__other__.testGetValue(),
+ /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/
+ );
+});
+
+add_task(function test_fog_quantity_works() {
+ Glean.testOnly.meaningOfLife.set(42);
+ Assert.equal(42, Glean.testOnly.meaningOfLife.testGetValue());
+});
+
+add_task(function test_fog_rate_works() {
+ // 1) Standard rate with internal denominator
+ Glean.testOnlyIpc.irate.addToNumerator(22);
+ Glean.testOnlyIpc.irate.addToDenominator(7);
+ Assert.deepEqual(
+ { numerator: 22, denominator: 7 },
+ Glean.testOnlyIpc.irate.testGetValue()
+ );
+
+ // 2) Rate with external denominator
+ Glean.testOnlyIpc.anExternalDenominator.add(11);
+ Glean.testOnlyIpc.rateWithExternalDenominator.addToNumerator(121);
+ Assert.equal(11, Glean.testOnlyIpc.anExternalDenominator.testGetValue());
+ Assert.deepEqual(
+ { numerator: 121, denominator: 11 },
+ Glean.testOnlyIpc.rateWithExternalDenominator.testGetValue()
+ );
+});
+
+add_task(async function test_fog_url_works() {
+ const value = "https://www.example.com/fog";
+ Glean.testOnlyIpc.aUrl.set(value);
+
+ Assert.equal(value, Glean.testOnlyIpc.aUrl.testGetValue("store1"));
+});