/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ const INT_MAX = 0x7fffffff; // Return an array of numbers from lower up to, excluding, upper function numberRange(lower, upper) { let a = []; for (let i = lower; i < upper; ++i) { a.push(i); } return a; } function check_histogram(histogram_type, name, min, max, bucket_count) { var h = Telemetry.getHistogramById(name); h.add(0); var s = h.snapshot(); Assert.equal(0, s.sum); var hgrams = Telemetry.getSnapshotForHistograms("main", false).parent; let gh = hgrams[name]; Assert.equal(gh.histogram_type, histogram_type); Assert.deepEqual(gh.range, [min, max]); // Check that booleans work with nonboolean histograms h.add(false); h.add(true); s = Object.values(h.snapshot().values); Assert.deepEqual(s, [2, 1, 0]); // Check that clearing works. h.clear(); s = h.snapshot(); Assert.deepEqual(s.values, {}); Assert.equal(s.sum, 0); h.add(0); h.add(1); var c = Object.values(h.snapshot().values); Assert.deepEqual(c, [1, 1, 0]); } // This MUST be the very first test of this file. add_task( { skip_if: () => gIsAndroid, }, function test_instantiate() { const ID = "TELEMETRY_TEST_COUNT"; let h = Telemetry.getHistogramById(ID); // Instantiate the subsession histogram through |add| and make sure they match. // This MUST be the first use of "TELEMETRY_TEST_COUNT" in this file, otherwise // |add| will not instantiate the histogram. h.add(1); let snapshot = h.snapshot(); let subsession = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ).parent; Assert.ok(ID in subsession); Assert.equal( snapshot.sum, subsession[ID].sum, "Histogram and subsession histogram sum must match." ); // Clear the histogram, so we don't void the assumptions from the other tests. h.clear(); } ); add_task(async function test_parameterChecks() { let kinds = [Telemetry.HISTOGRAM_EXPONENTIAL, Telemetry.HISTOGRAM_LINEAR]; let testNames = ["TELEMETRY_TEST_EXPONENTIAL", "TELEMETRY_TEST_LINEAR"]; for (let i = 0; i < kinds.length; i++) { let histogram_type = kinds[i]; let test_type = testNames[i]; let [min, max, bucket_count] = [1, INT_MAX - 1, 10]; check_histogram(histogram_type, test_type, min, max, bucket_count); } }); add_task(async function test_parameterCounts() { let histogramIds = [ "TELEMETRY_TEST_EXPONENTIAL", "TELEMETRY_TEST_LINEAR", "TELEMETRY_TEST_FLAG", "TELEMETRY_TEST_CATEGORICAL", "TELEMETRY_TEST_BOOLEAN", ]; for (let id of histogramIds) { let h = Telemetry.getHistogramById(id); h.clear(); h.add(); Assert.equal( h.snapshot().sum, 0, "Calling add() without a value should only log an error." ); h.clear(); } }); add_task(async function test_parameterCountsKeyed() { let histogramIds = [ "TELEMETRY_TEST_KEYED_FLAG", "TELEMETRY_TEST_KEYED_BOOLEAN", "TELEMETRY_TEST_KEYED_EXPONENTIAL", "TELEMETRY_TEST_KEYED_LINEAR", ]; for (let id of histogramIds) { let h = Telemetry.getKeyedHistogramById(id); h.clear(); h.add("key"); Assert.deepEqual( h.snapshot(), {}, "Calling add('key') without a value should only log an error." ); h.clear(); } }); add_task(async function test_noSerialization() { // Instantiate the storage for this histogram and make sure it doesn't // get reflected into JS, as it has no interesting data in it. Telemetry.getHistogramById("NEWTAB_PAGE_PINNED_SITES_COUNT"); let histograms = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ).parent; Assert.equal(false, "NEWTAB_PAGE_PINNED_SITES_COUNT" in histograms); }); add_task(async function test_boolean_histogram() { var h = Telemetry.getHistogramById("TELEMETRY_TEST_BOOLEAN"); var r = h.snapshot().range; // boolean histograms ignore numeric parameters Assert.deepEqual(r, [1, 2]); h.add(0); h.add(1); h.add(2); h.add(true); h.add(false); var s = h.snapshot(); Assert.equal(s.histogram_type, Telemetry.HISTOGRAM_BOOLEAN); // last bucket should always be 0 since .add parameters are normalized to either 0 or 1 Assert.deepEqual(s.values, { 0: 2, 1: 3, 2: 0 }); Assert.equal(s.sum, 3); }); add_task(async function test_flag_histogram() { var h = Telemetry.getHistogramById("TELEMETRY_TEST_FLAG"); var r = h.snapshot().range; // Flag histograms ignore numeric parameters. Assert.deepEqual(r, [1, 2]); // Should already have a 0 counted. var v = h.snapshot().values; var s = h.snapshot().sum; Assert.deepEqual(v, { 0: 1, 1: 0 }); Assert.equal(s, 0); // Should switch counts. h.add(1); var v2 = h.snapshot().values; var s2 = h.snapshot().sum; Assert.deepEqual(v2, { 0: 0, 1: 1, 2: 0 }); Assert.equal(s2, 1); // Should only switch counts once. h.add(1); var v3 = h.snapshot().values; var s3 = h.snapshot().sum; Assert.deepEqual(v3, { 0: 0, 1: 1, 2: 0 }); Assert.equal(s3, 1); Assert.equal(h.snapshot().histogram_type, Telemetry.HISTOGRAM_FLAG); }); add_task(async function test_count_histogram() { let h = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT2"); let s = h.snapshot(); Assert.deepEqual(s.range, [1, 2]); Assert.deepEqual(s.values, {}); Assert.equal(s.sum, 0); h.add(); s = h.snapshot(); Assert.deepEqual(s.values, { 0: 1, 1: 0 }); Assert.equal(s.sum, 1); h.add(); s = h.snapshot(); Assert.deepEqual(s.values, { 0: 2, 1: 0 }); Assert.equal(s.sum, 2); }); add_task(async function test_categorical_histogram() { let h1 = Telemetry.getHistogramById("TELEMETRY_TEST_CATEGORICAL"); for (let v of ["CommonLabel", "Label2", "Label3", "Label3", 0, 0, 1]) { h1.add(v); } for (let s of ["", "Label4", "1234"]) { // The |add| method should not throw for unexpected values, but rather // print an error message in the console. h1.add(s); } let snapshot = h1.snapshot(); Assert.equal(snapshot.sum, 6); Assert.deepEqual(snapshot.range, [1, 50]); Assert.deepEqual(snapshot.values, { 0: 3, 1: 2, 2: 2, 3: 0 }); let h2 = Telemetry.getHistogramById("TELEMETRY_TEST_CATEGORICAL_OPTOUT"); for (let v of [ "CommonLabel", "CommonLabel", "Label4", "Label5", "Label6", 0, 1, ]) { h2.add(v); } for (let s of ["", "Label3", "1234"]) { // The |add| method should not throw for unexpected values, but rather // print an error message in the console. h2.add(s); } snapshot = h2.snapshot(); Assert.equal(snapshot.sum, 7); Assert.deepEqual(snapshot.range, [1, 50]); Assert.deepEqual(snapshot.values, { 0: 3, 1: 2, 2: 1, 3: 1, 4: 0 }); // This histogram overrides the default of 50 values to 70. let h3 = Telemetry.getHistogramById("TELEMETRY_TEST_CATEGORICAL_NVALUES"); for (let v of ["CommonLabel", "Label7", "Label8"]) { h3.add(v); } snapshot = h3.snapshot(); Assert.equal(snapshot.sum, 3); Assert.deepEqual(snapshot.range, [1, 70]); Assert.deepEqual(snapshot.values, { 0: 1, 1: 1, 2: 1, 3: 0 }); }); add_task(async function test_getCategoricalLabels() { let h = Telemetry.getCategoricalLabels(); Assert.deepEqual(h.TELEMETRY_TEST_CATEGORICAL, [ "CommonLabel", "Label2", "Label3", ]); Assert.deepEqual(h.TELEMETRY_TEST_CATEGORICAL_OPTOUT, [ "CommonLabel", "Label4", "Label5", "Label6", ]); Assert.deepEqual(h.TELEMETRY_TEST_CATEGORICAL_NVALUES, [ "CommonLabel", "Label7", "Label8", ]); Assert.deepEqual(h.TELEMETRY_TEST_KEYED_CATEGORICAL, [ "CommonLabel", "Label2", "Label3", ]); }); add_task(async function test_add_error_behaviour() { const PLAIN_HISTOGRAMS_TO_TEST = [ "TELEMETRY_TEST_FLAG", "TELEMETRY_TEST_EXPONENTIAL", "TELEMETRY_TEST_LINEAR", "TELEMETRY_TEST_BOOLEAN", ]; const KEYED_HISTOGRAMS_TO_TEST = [ "TELEMETRY_TEST_KEYED_FLAG", "TELEMETRY_TEST_KEYED_COUNT", "TELEMETRY_TEST_KEYED_BOOLEAN", ]; // Check that |add| doesn't throw for plain histograms. for (let hist of PLAIN_HISTOGRAMS_TO_TEST) { const returnValue = Telemetry.getHistogramById(hist).add("unexpected-value"); Assert.strictEqual( returnValue, undefined, "Adding to an histogram must return 'undefined'." ); } // And for keyed histograms. for (let hist of KEYED_HISTOGRAMS_TO_TEST) { const returnValue = Telemetry.getKeyedHistogramById(hist).add( "some-key", "unexpected-value" ); Assert.strictEqual( returnValue, undefined, "Adding to a keyed histogram must return 'undefined'." ); } }); add_task(async function test_API_return_values() { // Check that the plain scalar functions don't allow to crash the browser. // We expect 'undefined' to be returned so that .add(1).add() can't be called. // See bug 1321349 for context. let hist = Telemetry.getHistogramById("TELEMETRY_TEST_LINEAR"); let keyedHist = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT"); const RETURN_VALUES = [ hist.clear(), hist.add(1), keyedHist.clear(), keyedHist.add("some-key", 1), ]; for (let returnValue of RETURN_VALUES) { Assert.strictEqual( returnValue, undefined, "The function must return undefined" ); } }); add_task(async function test_getHistogramById() { try { Telemetry.getHistogramById("nonexistent"); do_throw("This can't happen"); } catch (e) {} var h = Telemetry.getHistogramById("CYCLE_COLLECTOR"); var s = h.snapshot(); Assert.equal(s.histogram_type, Telemetry.HISTOGRAM_EXPONENTIAL); Assert.deepEqual(s.range, [1, 10000]); }); add_task(async function test_getSlowSQL() { var slow = Telemetry.slowSQL; Assert.ok("mainThread" in slow && "otherThreads" in slow); }); // Check that telemetry doesn't record in private mode add_task(async function test_privateMode() { var h = Telemetry.getHistogramById("TELEMETRY_TEST_BOOLEAN"); var orig = h.snapshot(); Telemetry.canRecordExtended = false; h.add(1); Assert.deepEqual(orig, h.snapshot()); Telemetry.canRecordExtended = true; h.add(1); Assert.notDeepEqual(orig, h.snapshot()); }); // Check that telemetry records only when it is suppose to. add_task(async function test_histogramRecording() { // Check that no histogram is recorded if both base and extended recording are off. Telemetry.canRecordBase = false; Telemetry.canRecordExtended = false; let h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTOUT"); h.clear(); let orig = h.snapshot(); h.add(1); Assert.equal(orig.sum, h.snapshot().sum); // Check that only base histograms are recorded. Telemetry.canRecordBase = true; h.add(1); Assert.equal( orig.sum + 1, h.snapshot().sum, "Histogram value should have incremented by 1 due to recording." ); // Extended histograms should not be recorded. h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTIN"); orig = h.snapshot(); h.add(1); Assert.equal( orig.sum, h.snapshot().sum, "Histograms should be equal after recording." ); // Runtime created histograms should not be recorded. h = Telemetry.getHistogramById("TELEMETRY_TEST_BOOLEAN"); orig = h.snapshot(); h.add(1); Assert.equal( orig.sum, h.snapshot().sum, "Histograms should be equal after recording." ); // Check that extended histograms are recorded when required. Telemetry.canRecordExtended = true; h.add(1); Assert.equal( orig.sum + 1, h.snapshot().sum, "Runtime histogram value should have incremented by 1 due to recording." ); h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTIN"); orig = h.snapshot(); h.add(1); Assert.equal( orig.sum + 1, h.snapshot().sum, "Histogram value should have incremented by 1 due to recording." ); // Check that base histograms are still being recorded. h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTOUT"); h.clear(); orig = h.snapshot(); h.add(1); Assert.equal( orig.sum + 1, h.snapshot().sum, "Histogram value should have incremented by 1 due to recording." ); }); add_task(async function test_expired_histogram() { var test_expired_id = "TELEMETRY_TEST_EXPIRED"; var dummy = Telemetry.getHistogramById(test_expired_id); dummy.add(1); for (let process of ["main", "content", "gpu", "extension"]) { let histograms = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ); if (!(process in histograms)) { info("Nothing present for process " + process); continue; } Assert.equal(histograms[process].__expired__, undefined); } let parentHgrams = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ).parent; Assert.equal(parentHgrams[test_expired_id], undefined); }); add_task(async function test_keyed_expired_histogram() { var test_expired_id = "TELEMETRY_TEST_EXPIRED_KEYED"; var dummy = Telemetry.getKeyedHistogramById(test_expired_id); dummy.add("someKey", 1); const histograms = Telemetry.getSnapshotForKeyedHistograms( "main", false /* clear */ ); for (let process of ["parent", "content", "gpu", "extension"]) { if (!(process in histograms)) { info("Nothing present for process " + process); continue; } Assert.ok( !(test_expired_id in histograms[process]), "The expired keyed histogram must not be reported" ); } }); add_task(async function test_keyed_histogram() { // Check that invalid names get rejected. let threw = false; try { Telemetry.getKeyedHistogramById( "test::unknown histogram", "never", Telemetry.HISTOGRAM_BOOLEAN ); } catch (e) { // This should throw as it is an unknown ID threw = true; } Assert.ok(threw, "getKeyedHistogramById should have thrown"); }); add_task(async function test_keyed_boolean_histogram() { const KEYED_ID = "TELEMETRY_TEST_KEYED_BOOLEAN"; let KEYS = numberRange(0, 2).map(i => "key" + (i + 1)); KEYS.push("漢語"); let histogramBase = { range: [1, 2], bucket_count: 3, histogram_type: 2, sum: 1, values: { 0: 0, 1: 1, 2: 0 }, }; let testHistograms = numberRange(0, 3).map(i => JSON.parse(JSON.stringify(histogramBase)) ); let testKeys = []; let testSnapShot = {}; let h = Telemetry.getKeyedHistogramById(KEYED_ID); for (let i = 0; i < 2; ++i) { let key = KEYS[i]; h.add(key, true); testSnapShot[key] = testHistograms[i]; testKeys.push(key); Assert.deepEqual(h.keys().sort(), testKeys); Assert.deepEqual(h.snapshot(), testSnapShot); } h = Telemetry.getKeyedHistogramById(KEYED_ID); Assert.deepEqual(h.keys().sort(), testKeys); Assert.deepEqual(h.snapshot(), testSnapShot); let key = KEYS[2]; h.add(key, false); testKeys.push(key); testSnapShot[key] = testHistograms[2]; testSnapShot[key].sum = 0; testSnapShot[key].values = { 0: 1, 1: 0 }; Assert.deepEqual(h.keys().sort(), testKeys); Assert.deepEqual(h.snapshot(), testSnapShot); let parentHgrams = Telemetry.getSnapshotForKeyedHistograms( "main", false /* clear */ ).parent; Assert.deepEqual(parentHgrams[KEYED_ID], testSnapShot); h.clear(); Assert.deepEqual(h.keys(), []); Assert.deepEqual(h.snapshot(), {}); }); add_task(async function test_keyed_count_histogram() { const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT"; const KEYS = numberRange(0, 5).map(i => "key" + (i + 1)); let histogramBase = { range: [1, 2], bucket_count: 3, histogram_type: 4, sum: 0, values: { 0: 1, 1: 0 }, }; let testHistograms = numberRange(0, 5).map(i => JSON.parse(JSON.stringify(histogramBase)) ); let testKeys = []; let testSnapShot = {}; let h = Telemetry.getKeyedHistogramById(KEYED_ID); h.clear(); for (let i = 0; i < 4; ++i) { let key = KEYS[i]; let value = i * 2 + 1; for (let k = 0; k < value; ++k) { h.add(key); } testHistograms[i].values[0] = value; testHistograms[i].sum = value; testSnapShot[key] = testHistograms[i]; testKeys.push(key); Assert.deepEqual(h.keys().sort(), testKeys); Assert.deepEqual(h.snapshot()[key], testHistograms[i]); Assert.deepEqual(h.snapshot(), testSnapShot); } h = Telemetry.getKeyedHistogramById(KEYED_ID); Assert.deepEqual(h.keys().sort(), testKeys); Assert.deepEqual(h.snapshot(), testSnapShot); let key = KEYS[4]; h.add(key); testKeys.push(key); testHistograms[4].values[0] = 1; testHistograms[4].sum = 1; testSnapShot[key] = testHistograms[4]; Assert.deepEqual(h.keys().sort(), testKeys); Assert.deepEqual(h.snapshot(), testSnapShot); let parentHgrams = Telemetry.getSnapshotForKeyedHistograms( "main", false /* clear */ ).parent; Assert.deepEqual(parentHgrams[KEYED_ID], testSnapShot); // Test clearing categorical histogram. h.clear(); Assert.deepEqual(h.keys(), []); Assert.deepEqual(h.snapshot(), {}); // Test leaving out the value argument. That should increment by 1. h.add("key"); Assert.equal(h.snapshot().key.sum, 1); }); add_task(async function test_keyed_categorical_histogram() { const KEYED_ID = "TELEMETRY_TEST_KEYED_CATEGORICAL"; const KEYS = numberRange(0, 5).map(i => "key" + (i + 1)); let h = Telemetry.getKeyedHistogramById(KEYED_ID); for (let k of KEYS) { // Test adding both per label and index. for (let v of ["CommonLabel", "Label2", "Label3", "Label3", 0, 0, 1]) { h.add(k, v); } // The |add| method should not throw for unexpected values, but rather // print an error message in the console. for (let s of ["", "Label4", "1234"]) { h.add(k, s); } } // Check that the set of keys in the snapshot is what we expect. let snapshot = h.snapshot(); let snapshotKeys = Object.keys(snapshot); Assert.equal(KEYS.length, snapshotKeys.length); Assert.ok(KEYS.every(k => snapshotKeys.includes(k))); // Check the snapshot values. for (let k of KEYS) { Assert.ok(k in snapshot); Assert.equal(snapshot[k].sum, 6); Assert.deepEqual(snapshot[k].range, [1, 50]); Assert.deepEqual(snapshot[k].values, { 0: 3, 1: 2, 2: 2, 3: 0 }); } }); add_task(async function test_keyed_flag_histogram() { const KEYED_ID = "TELEMETRY_TEST_KEYED_FLAG"; let h = Telemetry.getKeyedHistogramById(KEYED_ID); const KEY = "default"; h.add(KEY, true); let testSnapshot = {}; testSnapshot[KEY] = { range: [1, 2], bucket_count: 3, histogram_type: 3, sum: 1, values: { 0: 0, 1: 1, 2: 0 }, }; Assert.deepEqual(h.keys().sort(), [KEY]); Assert.deepEqual(h.snapshot(), testSnapshot); let parentHgrams = Telemetry.getSnapshotForKeyedHistograms( "main", false /* clear */ ).parent; Assert.deepEqual(parentHgrams[KEYED_ID], testSnapshot); h.clear(); Assert.deepEqual(h.keys(), []); Assert.deepEqual(h.snapshot(), {}); }); add_task(async function test_keyed_histogram_recording() { // Check that no histogram is recorded if both base and extended recording are off. Telemetry.canRecordBase = false; Telemetry.canRecordExtended = false; const TEST_KEY = "record_foo"; let h = Telemetry.getKeyedHistogramById( "TELEMETRY_TEST_KEYED_RELEASE_OPTOUT" ); h.clear(); h.add(TEST_KEY, 1); Assert.ok(!(TEST_KEY in h.snapshot())); // Check that only base histograms are recorded. Telemetry.canRecordBase = true; h.add(TEST_KEY, 1); Assert.equal( h.snapshot()[TEST_KEY].sum, 1, "The keyed histogram should record the correct value." ); // Extended set keyed histograms should not be recorded. h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTIN"); h.clear(); h.add(TEST_KEY, 1); Assert.ok( !(TEST_KEY in h.snapshot()), "The keyed histograms should not record any data." ); // Check that extended histograms are recorded when required. Telemetry.canRecordExtended = true; h.add(TEST_KEY, 1); Assert.equal( h.snapshot()[TEST_KEY].sum, 1, "The runtime keyed histogram should record the correct value." ); h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTIN"); h.clear(); h.add(TEST_KEY, 1); Assert.equal( h.snapshot()[TEST_KEY].sum, 1, "The keyed histogram should record the correct value." ); // Check that base histograms are still being recorded. h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT"); h.clear(); h.add(TEST_KEY, 1); Assert.equal(h.snapshot()[TEST_KEY].sum, 1); }); add_task(async function test_histogram_recording_enabled() { Telemetry.canRecordBase = true; Telemetry.canRecordExtended = true; // Check that a "normal" histogram respects recording-enabled on/off var h = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT"); var orig = h.snapshot(); h.add(1); Assert.equal(orig.sum + 1, h.snapshot().sum, "add should record by default."); // Check that when recording is disabled - add is ignored Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_COUNT", false); h.add(1); Assert.equal( orig.sum + 1, h.snapshot().sum, "When recording is disabled add should not record." ); // Check that we're back to normal after recording is enabled Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_COUNT", true); h.add(1); Assert.equal( orig.sum + 2, h.snapshot().sum, "When recording is re-enabled add should record." ); // Check that we're correctly accumulating values other than 1. h.clear(); h.add(3); Assert.equal( 3, h.snapshot().sum, "Recording counts greater than 1 should work." ); // Check that a histogram with recording disabled by default behaves correctly h = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT_INIT_NO_RECORD"); orig = h.snapshot(); h.add(1); Assert.equal( orig.sum, h.snapshot().sum, "When recording is disabled by default, add should not record by default." ); Telemetry.setHistogramRecordingEnabled( "TELEMETRY_TEST_COUNT_INIT_NO_RECORD", true ); h.add(1); Assert.equal( orig.sum + 1, h.snapshot().sum, "When recording is enabled add should record." ); // Restore to disabled Telemetry.setHistogramRecordingEnabled( "TELEMETRY_TEST_COUNT_INIT_NO_RECORD", false ); h.add(1); Assert.equal( orig.sum + 1, h.snapshot().sum, "When recording is disabled add should not record." ); }); add_task(async function test_keyed_histogram_recording_enabled() { Telemetry.canRecordBase = true; Telemetry.canRecordExtended = true; // Check RecordingEnabled for keyed histograms which are recording by default const TEST_KEY = "record_foo"; let h = Telemetry.getKeyedHistogramById( "TELEMETRY_TEST_KEYED_RELEASE_OPTOUT" ); h.clear(); h.add(TEST_KEY, 1); Assert.equal( h.snapshot()[TEST_KEY].sum, 1, "Keyed histogram add should record by default" ); Telemetry.setHistogramRecordingEnabled( "TELEMETRY_TEST_KEYED_RELEASE_OPTOUT", false ); h.add(TEST_KEY, 1); Assert.equal( h.snapshot()[TEST_KEY].sum, 1, "Keyed histogram add should not record when recording is disabled" ); Telemetry.setHistogramRecordingEnabled( "TELEMETRY_TEST_KEYED_RELEASE_OPTOUT", true ); h.clear(); h.add(TEST_KEY, 1); Assert.equal( h.snapshot()[TEST_KEY].sum, 1, "Keyed histogram add should record when recording is re-enabled" ); // Check that a histogram with recording disabled by default behaves correctly h = Telemetry.getKeyedHistogramById( "TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD" ); h.clear(); h.add(TEST_KEY, 1); Assert.ok( !(TEST_KEY in h.snapshot()), "Keyed histogram add should not record by default for histograms which don't record by default" ); Telemetry.setHistogramRecordingEnabled( "TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD", true ); h.add(TEST_KEY, 1); Assert.equal( h.snapshot()[TEST_KEY].sum, 1, "Keyed histogram add should record when recording is enabled" ); // Restore to disabled Telemetry.setHistogramRecordingEnabled( "TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD", false ); h.add(TEST_KEY, 1); Assert.equal( h.snapshot()[TEST_KEY].sum, 1, "Keyed histogram add should not record when recording is disabled" ); }); add_task(async function test_histogramSnapshots() { let keyed = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT"); keyed.add("a", 1); // Check that keyed histograms are not returned let parentHgrams = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ).parent; Assert.ok(!("TELEMETRY_TEST_KEYED_COUNT" in parentHgrams)); }); add_task(async function test_datasets() { // Check that datasets work as expected. const currentRecordExtended = Telemetry.canRecordExtended; // Clear everything out Telemetry.getSnapshotForHistograms("main", true /* clear */); Telemetry.getSnapshotForKeyedHistograms("main", true /* clear */); // Empty histograms are filtered. Let's record what we check below. Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTIN").add(1); Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTOUT").add(1); // Keyed flag histograms are skipped if empty, let's add data Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_FLAG").add("a", 1); Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTIN").add( "a", 1 ); Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT").add( "a", 1 ); // Check that registeredHistogram works properly Telemetry.canRecordExtended = true; let registered = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ); registered = new Set(Object.keys(registered.parent)); Assert.ok(registered.has("TELEMETRY_TEST_FLAG")); Assert.ok(registered.has("TELEMETRY_TEST_RELEASE_OPTIN")); Assert.ok(registered.has("TELEMETRY_TEST_RELEASE_OPTOUT")); Telemetry.canRecordExtended = false; registered = Telemetry.getSnapshotForHistograms("main", false /* clear */); registered = new Set(Object.keys(registered.parent)); Assert.ok(!registered.has("TELEMETRY_TEST_FLAG")); Assert.ok(!registered.has("TELEMETRY_TEST_RELEASE_OPTIN")); Assert.ok(registered.has("TELEMETRY_TEST_RELEASE_OPTOUT")); // Check that registeredKeyedHistograms works properly Telemetry.canRecordExtended = true; registered = Telemetry.getSnapshotForKeyedHistograms( "main", false /* clear */ ); registered = new Set(Object.keys(registered.parent)); Assert.ok(registered.has("TELEMETRY_TEST_KEYED_FLAG")); Assert.ok(registered.has("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT")); Telemetry.canRecordExtended = false; registered = Telemetry.getSnapshotForKeyedHistograms( "main", false /* clear */ ); registered = new Set(Object.keys(registered.parent)); Assert.ok(!registered.has("TELEMETRY_TEST_KEYED_FLAG")); Assert.ok(registered.has("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT")); Telemetry.canRecordExtended = currentRecordExtended; }); add_task(async function test_keyed_keys() { let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_KEYS"); h.clear(); Telemetry.clearScalars(); // The |add| method should not throw for keys that are not allowed. h.add("testkey", true); h.add("thirdKey", false); h.add("not-allowed", true); // Check that we have the expected keys. let snap = h.snapshot(); Assert.equal(Object.keys(snap).length, 2, "Only 2 keys must be recorded."); Assert.ok("testkey" in snap, "'testkey' must be recorded."); Assert.ok("thirdKey" in snap, "'thirdKey' must be recorded."); Assert.deepEqual( snap.testkey.values, { 0: 0, 1: 1, 2: 0 }, "'testkey' must contain the correct value." ); Assert.deepEqual( snap.thirdKey.values, { 0: 1, 1: 0 }, "'thirdKey' must contain the correct value." ); // Keys that are not allowed must not be recorded. Assert.ok(!("not-allowed" in snap), "'not-allowed' must not be recorded."); // Check that these failures were correctly tracked. const parentScalars = Telemetry.getSnapshotForKeyedScalars( "main", false ).parent; const scalarName = "telemetry.accumulate_unknown_histogram_keys"; Assert.ok( scalarName in parentScalars, "Accumulation to unallowed keys must be reported." ); Assert.ok( "TELEMETRY_TEST_KEYED_KEYS" in parentScalars[scalarName], "Accumulation to unallowed keys must be recorded with the correct key." ); Assert.equal( parentScalars[scalarName].TELEMETRY_TEST_KEYED_KEYS, 1, "Accumulation to unallowed keys must report the correct value." ); }); add_task(async function test_count_multiple_samples() { let valid = [1, 1, 3, 0]; let invalid = ["1", "0", "", "random"]; let h = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT"); h.clear(); // If the array contains even a single invalid value, no accumulation should take place // Keep the valid values in front of invalid to check if it is simply accumulating as // it's traversing the array and throwing upon first invalid value. That should not happen. h.add(valid.concat(invalid)); let s1 = h.snapshot(); Assert.equal(s1.sum, 0); // Ensure that no accumulations of 0-like values took place. // These accumulations won't increase the sum. Assert.deepEqual({}, s1.values); h.add(valid); let s2 = h.snapshot(); Assert.deepEqual(s2.values, { 0: 4, 1: 0 }); Assert.equal(s2.sum, 5); }); add_task(async function test_categorical_multiple_samples() { let h = Telemetry.getHistogramById("TELEMETRY_TEST_CATEGORICAL"); h.clear(); let valid = ["CommonLabel", "Label2", "Label3", "Label3", 0, 0, 1]; let invalid = ["", "Label4", "1234", "0", "1", 5000]; // At least one invalid parameter, so no accumulation should happen here // Valid values in front of invalid. h.add(valid.concat(invalid)); let s1 = h.snapshot(); Assert.equal(s1.sum, 0); Assert.deepEqual({}, s1.values); h.add(valid); let snapshot = h.snapshot(); Assert.equal(snapshot.sum, 6); Assert.deepEqual(snapshot.values, { 0: 3, 1: 2, 2: 2, 3: 0 }); }); add_task(async function test_boolean_multiple_samples() { let valid = [true, false, 0, 1, 2]; let invalid = ["", "0", "1", ",2", "true", "false", "random"]; let h = Telemetry.getHistogramById("TELEMETRY_TEST_BOOLEAN"); h.clear(); // At least one invalid parameter, so no accumulation should happen here // Valid values in front of invalid. h.add(valid.concat(invalid)); let s1 = h.snapshot(); Assert.equal(s1.sum, 0); Assert.deepEqual({}, s1.values); h.add(valid); let s = h.snapshot(); Assert.deepEqual(s.values, { 0: 2, 1: 3, 2: 0 }); Assert.equal(s.sum, 3); }); add_task(async function test_linear_multiple_samples() { // According to telemetry.mozilla.org/histogram-simulator, bucket at // index 1 of TELEMETRY_TEST_LINEAR has max value of 268.44M let valid = [0, 1, 5, 10, 268450000, 268450001, Math.pow(2, 31) + 1]; let invalid = ["", "0", "1", "random"]; let h = Telemetry.getHistogramById("TELEMETRY_TEST_LINEAR"); h.clear(); // At least one invalid paramater, so no accumulations. // Valid values in front of invalid. h.add(valid.concat(invalid)); let s1 = h.snapshot(); Assert.equal(s1.sum, 0); Assert.deepEqual({}, s1.values); h.add(valid); let s2 = h.snapshot(); // Values >= INT32_MAX are accumulated as INT32_MAX - 1 Assert.equal(s2.sum, valid.reduce((acc, cur) => acc + cur) - 3); Assert.deepEqual(Object.values(s2.values), [1, 3, 2, 1]); }); add_task(async function test_keyed_no_arguments() { // Test for no accumulation when add is called with no arguments let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_LINEAR"); h.clear(); h.add(); // No keys should be added due to no accumulation. Assert.equal(h.keys().length, 0); }); add_task(async function test_keyed_categorical_invalid_string() { // Test for no accumulation when add is called on a // keyed categorical histogram with an invalid string label. let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_CATEGORICAL"); h.clear(); h.add("someKey", "#notALabel"); // No keys should be added due to no accumulation. Assert.equal(h.keys().length, 0); }); add_task(async function test_keyed_count_multiple_samples() { let valid = [1, 1, 3, 0]; let invalid = ["1", "0", "", "random"]; let key = "somekeystring"; let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT"); h.clear(); // If the array contains even a single invalid value, no accumulation should take place // Keep the valid values in front of invalid to check if it is simply accumulating as // it's traversing the array and throwing upon first invalid value. That should not happen. h.add(key, valid.concat(invalid)); let s1 = h.snapshot(); Assert.ok(!(key in s1)); h.add(key, valid); let s2 = h.snapshot()[key]; Assert.deepEqual(s2.values, { 0: 4, 1: 0 }); Assert.equal(s2.sum, 5); }); add_task(async function test_keyed_categorical_multiple_samples() { let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_CATEGORICAL"); h.clear(); let valid = ["CommonLabel", "Label2", "Label3", "Label3", 0, 0, 1]; let invalid = ["", "Label4", "1234", "0", "1", 5000]; let key = "somekeystring"; // At least one invalid parameter, so no accumulation should happen here // Valid values in front of invalid. h.add(key, valid.concat(invalid)); let s1 = h.snapshot(); Assert.ok(!(key in s1)); h.add(key, valid); let snapshot = h.snapshot()[key]; Assert.equal(snapshot.sum, 6); Assert.deepEqual(Object.values(snapshot.values), [3, 2, 2, 0]); }); add_task(async function test_keyed_boolean_multiple_samples() { let valid = [true, false, 0, 1, 2]; let invalid = ["", "0", "1", ",2", "true", "false", "random"]; let key = "somekey"; let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_BOOLEAN"); h.clear(); // At least one invalid parameter, so no accumulation should happen here // Valid values in front of invalid. h.add(key, valid.concat(invalid)); let s1 = h.snapshot(); Assert.ok(!(key in s1)); h.add(key, valid); let s = h.snapshot()[key]; Assert.deepEqual(s.values, { 0: 2, 1: 3, 2: 0 }); Assert.equal(s.sum, 3); }); add_task(async function test_keyed_linear_multiple_samples() { // According to telemetry.mozilla.org/histogram-simulator, bucket at // index 1 of TELEMETRY_TEST_LINEAR has max value of 3.13K let valid = [0, 1, 5, 10, 268450000, 268450001, Math.pow(2, 31) + 1]; let invalid = ["", "0", "1", "random"]; let key = "somestring"; let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_LINEAR"); h.clear(); // At least one invalid paramater, so no accumulations. // Valid values in front of invalid. h.add(key, valid.concat(invalid)); let s1 = h.snapshot(); Assert.ok(!(key in s1)); h.add(key, valid); let s2 = h.snapshot()[key]; // Values >= INT32_MAX are accumulated as INT32_MAX - 1 Assert.equal(s2.sum, valid.reduce((acc, cur) => acc + cur) - 3); Assert.deepEqual(s2.range, [1, 250000]); Assert.deepEqual(s2.values, { 0: 1, 1: 3, 250000: 3 }); }); add_task(async function test_non_array_non_string_obj() { let invalid_obj = { prop1: "someValue", prop2: "someOtherValue", }; let key = "someString"; let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_LINEAR"); h.clear(); h.add(key, invalid_obj); Assert.equal(h.keys().length, 0); }); add_task( { skip_if: () => gIsAndroid, }, async function test_productSpecificHistograms() { const DEFAULT_PRODUCTS_HISTOGRAM = "TELEMETRY_TEST_DEFAULT_PRODUCTS"; const DESKTOP_ONLY_HISTOGRAM = "TELEMETRY_TEST_DESKTOP_ONLY"; const MULTIPRODUCT_HISTOGRAM = "TELEMETRY_TEST_MULTIPRODUCT"; const MOBILE_ONLY_HISTOGRAM = "TELEMETRY_TEST_MOBILE_ONLY"; var default_histo = Telemetry.getHistogramById(DEFAULT_PRODUCTS_HISTOGRAM); var desktop_histo = Telemetry.getHistogramById(DESKTOP_ONLY_HISTOGRAM); var multiproduct_histo = Telemetry.getHistogramById(MULTIPRODUCT_HISTOGRAM); var mobile_histo = Telemetry.getHistogramById(MOBILE_ONLY_HISTOGRAM); default_histo.clear(); desktop_histo.clear(); multiproduct_histo.clear(); mobile_histo.clear(); default_histo.add(42); desktop_histo.add(42); multiproduct_histo.add(42); mobile_histo.add(42); let histograms = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ).parent; Assert.ok( DEFAULT_PRODUCTS_HISTOGRAM in histograms, "Should have recorded default products histogram" ); Assert.ok( DESKTOP_ONLY_HISTOGRAM in histograms, "Should have recorded desktop-only histogram" ); Assert.ok( MULTIPRODUCT_HISTOGRAM in histograms, "Should have recorded multiproduct histogram" ); Assert.ok( !(MOBILE_ONLY_HISTOGRAM in histograms), "Should not have recorded mobile-only histogram" ); } ); add_task( { skip_if: () => !gIsAndroid, }, async function test_mobileSpecificHistograms() { const DEFAULT_PRODUCTS_HISTOGRAM = "TELEMETRY_TEST_DEFAULT_PRODUCTS"; const DESKTOP_ONLY_HISTOGRAM = "TELEMETRY_TEST_DESKTOP_ONLY"; const MULTIPRODUCT_HISTOGRAM = "TELEMETRY_TEST_MULTIPRODUCT"; const MOBILE_ONLY_HISTOGRAM = "TELEMETRY_TEST_MOBILE_ONLY"; var default_histo = Telemetry.getHistogramById(DEFAULT_PRODUCTS_HISTOGRAM); var desktop_histo = Telemetry.getHistogramById(DESKTOP_ONLY_HISTOGRAM); var multiproduct_histo = Telemetry.getHistogramById(MULTIPRODUCT_HISTOGRAM); var mobile_histo = Telemetry.getHistogramById(MOBILE_ONLY_HISTOGRAM); default_histo.clear(); desktop_histo.clear(); multiproduct_histo.clear(); mobile_histo.clear(); default_histo.add(1); desktop_histo.add(1); multiproduct_histo.add(1); mobile_histo.add(1); let histograms = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ).parent; Assert.ok( DEFAULT_PRODUCTS_HISTOGRAM in histograms, "Should have recorded default products histogram" ); Assert.ok( MOBILE_ONLY_HISTOGRAM in histograms, "Should have recorded mobile-only histogram" ); Assert.ok( MULTIPRODUCT_HISTOGRAM in histograms, "Should have recorded multiproduct histogram" ); Assert.ok( !(DESKTOP_ONLY_HISTOGRAM in histograms), "Should not have recorded desktop-only histogram" ); } ); add_task(async function test_productsOverride() { Services.prefs.setBoolPref( "toolkit.telemetry.testing.overrideProductsCheck", true ); const DEFAULT_PRODUCTS_HISTOGRAM = "TELEMETRY_TEST_DEFAULT_PRODUCTS"; const DESKTOP_ONLY_HISTOGRAM = "TELEMETRY_TEST_DESKTOP_ONLY"; const MULTIPRODUCT_HISTOGRAM = "TELEMETRY_TEST_MULTIPRODUCT"; const MOBILE_ONLY_HISTOGRAM = "TELEMETRY_TEST_MOBILE_ONLY"; var default_histo = Telemetry.getHistogramById(DEFAULT_PRODUCTS_HISTOGRAM); var desktop_histo = Telemetry.getHistogramById(DESKTOP_ONLY_HISTOGRAM); var multiproduct_histo = Telemetry.getHistogramById(MULTIPRODUCT_HISTOGRAM); var mobile_histo = Telemetry.getHistogramById(MOBILE_ONLY_HISTOGRAM); default_histo.clear(); desktop_histo.clear(); multiproduct_histo.clear(); mobile_histo.clear(); default_histo.add(1); desktop_histo.add(1); multiproduct_histo.add(1); mobile_histo.add(1); let histograms = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ).parent; Assert.ok( DEFAULT_PRODUCTS_HISTOGRAM in histograms, "Should have recorded default products histogram" ); Assert.ok( MOBILE_ONLY_HISTOGRAM in histograms, "Should have recorded mobile-only histogram" ); Assert.ok( MULTIPRODUCT_HISTOGRAM in histograms, "Should have recorded multiproduct histogram" ); Assert.ok( DESKTOP_ONLY_HISTOGRAM in histograms, "Should not have recorded desktop-only histogram" ); Services.prefs.clearUserPref( "toolkit.telemetry.testing.overrideProductsCheck" ); }); add_task( { skip_if: () => gIsAndroid, }, async function test_clearHistogramsOnSnapshot() { const COUNT = "TELEMETRY_TEST_COUNT"; let h = Telemetry.getHistogramById(COUNT); h.clear(); let snapshot; // The first snapshot should be empty, nothing recorded. snapshot = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ).parent; Assert.ok(!(COUNT in snapshot)); // After recording into a histogram, the data should be in the snapshot. Don't delete it. h.add(1); Assert.equal(h.snapshot().sum, 1); snapshot = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ).parent; Assert.ok(COUNT in snapshot); Assert.equal(snapshot[COUNT].sum, 1); // After recording into a histogram again, the data should be updated and in the snapshot. // Clean up after. h.add(41); Assert.equal(h.snapshot().sum, 42); snapshot = Telemetry.getSnapshotForHistograms( "main", true /* clear */ ).parent; Assert.ok(COUNT in snapshot); Assert.equal(snapshot[COUNT].sum, 42); // Finally, no data should be in the snapshot. Assert.equal(h.snapshot().sum, 0); snapshot = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ).parent; Assert.ok(!(COUNT in snapshot)); } ); add_task(async function test_valid_os_smoketest() { let nonExistingProbe; let existingProbe; switch (AppConstants.platform) { case "linux": nonExistingProbe = "TELEMETRY_TEST_OS_ANDROID_ONLY"; existingProbe = "TELEMETRY_TEST_OS_LINUX_ONLY"; break; case "macosx": nonExistingProbe = "TELEMETRY_TEST_OS_ANDROID_ONLY"; existingProbe = "TELEMETRY_TEST_OS_MAC_ONLY"; break; case "win": nonExistingProbe = "TELEMETRY_TEST_OS_ANDROID_ONLY"; existingProbe = "TELEMETRY_TEST_OS_WIN_ONLY"; break; case "android": nonExistingProbe = "TELEMETRY_TEST_OS_LINUX_ONLY"; existingProbe = "TELEMETRY_TEST_OS_ANDROID_ONLY"; break; default: /* Unknown OS. Let's not test OS-specific probes */ return; } Assert.throws( () => Telemetry.getHistogramById(nonExistingProbe), /NS_ERROR_FAILURE/, `Should throw on ${nonExistingProbe} probe that's not available on ${AppConstants.platform}` ); let h = Telemetry.getHistogramById(existingProbe); h.clear(); h.add(1); let snapshot = Telemetry.getSnapshotForHistograms( "main", false /* clear */ ).parent; Assert.ok( existingProbe in snapshot, `${existingProbe} should be recorded on ${AppConstants.platform}` ); Assert.equal(snapshot[existingProbe].sum, 1); }); add_task(async function test_multistore_individual_histogram() { Telemetry.canRecordExtended = true; let id; let hist; let snapshot; id = "TELEMETRY_TEST_MAIN_ONLY"; hist = Telemetry.getHistogramById(id); snapshot = hist.snapshot(); Assert.equal(0, snapshot.sum, `Histogram ${id} should be empty.`); hist.add(1); snapshot = hist.snapshot(); Assert.equal( 1, snapshot.sum, `Histogram ${id} should have recorded one value.` ); hist.clear(); snapshot = hist.snapshot(); Assert.equal(0, snapshot.sum, `Histogram ${id} should be cleared.`); id = "TELEMETRY_TEST_MULTIPLE_STORES"; hist = Telemetry.getHistogramById(id); snapshot = hist.snapshot(); Assert.equal(0, snapshot.sum, `Histogram ${id} should be empty.`); hist.add(1); snapshot = hist.snapshot(); Assert.equal( 1, snapshot.sum, `Histogram ${id} should have recorded one value.` ); hist.clear(); snapshot = hist.snapshot(); Assert.equal(0, snapshot.sum, `Histogram ${id} should be cleared.`); // When sync only, then the snapshot will be empty on the main store id = "TELEMETRY_TEST_SYNC_ONLY"; hist = Telemetry.getHistogramById(id); snapshot = hist.snapshot(); Assert.equal( undefined, snapshot, `Histogram ${id} should not be in the 'main' storage` ); hist.add(1); snapshot = hist.snapshot(); Assert.equal( undefined, snapshot, `Histogram ${id} should not be in the 'main' storage` ); hist.clear(); snapshot = hist.snapshot(); Assert.equal( undefined, snapshot, `Histogram ${id} should not be in the 'main' storage` ); id = "TELEMETRY_TEST_KEYED_MULTIPLE_STORES"; hist = Telemetry.getKeyedHistogramById(id); snapshot = hist.snapshot(); Assert.deepEqual({}, snapshot, `Histogram ${id} should be empty.`); hist.add("key-a", 1); snapshot = hist.snapshot(); Assert.equal( 1, snapshot["key-a"].sum, `Histogram ${id} should have recorded one value.` ); hist.clear(); snapshot = hist.snapshot(); Assert.deepEqual({}, snapshot, `Histogram ${id} should be cleared.`); // When sync only, then the snapshot will be empty on the main store id = "TELEMETRY_TEST_KEYED_SYNC_ONLY"; hist = Telemetry.getKeyedHistogramById(id); snapshot = hist.snapshot(); Assert.equal( undefined, snapshot, `Histogram ${id} should not be in the 'main' storage` ); hist.add("key-a", 1); snapshot = hist.snapshot(); Assert.equal( undefined, snapshot, `Histogram ${id} should not be in the 'main' storage` ); hist.clear(); snapshot = hist.snapshot(); Assert.equal( undefined, snapshot, `Histogram ${id} should not be in the 'main' storage` ); }); add_task(async function test_multistore_main_snapshot() { Telemetry.canRecordExtended = true; // Clear histograms Telemetry.getSnapshotForHistograms("main", true); Telemetry.getSnapshotForKeyedHistograms("main", true); let id; let hist; let snapshot; // Plain histograms // Fill with data id = "TELEMETRY_TEST_MAIN_ONLY"; hist = Telemetry.getHistogramById(id); hist.add(1); id = "TELEMETRY_TEST_MULTIPLE_STORES"; hist = Telemetry.getHistogramById(id); hist.add(1); id = "TELEMETRY_TEST_SYNC_ONLY"; hist = Telemetry.getHistogramById(id); hist.add(1); // Getting snapshot and NOT clearing (using default values for optional parameters) snapshot = Telemetry.getSnapshotForHistograms().parent; id = "TELEMETRY_TEST_MAIN_ONLY"; Assert.ok(id in snapshot, `${id} should be in a main store snapshot`); id = "TELEMETRY_TEST_MULTIPLE_STORES"; Assert.ok(id in snapshot, `${id} should be in a main store snapshot`); id = "TELEMETRY_TEST_SYNC_ONLY"; Assert.ok(!(id in snapshot), `${id} should not be in a main store snapshot`); // Data should still be in, getting snapshot and clearing snapshot = Telemetry.getSnapshotForHistograms( "main", /* clear */ true ).parent; id = "TELEMETRY_TEST_MAIN_ONLY"; Assert.ok(id in snapshot, `${id} should be in a main store snapshot`); id = "TELEMETRY_TEST_MULTIPLE_STORES"; Assert.ok(id in snapshot, `${id} should be in a main store snapshot`); id = "TELEMETRY_TEST_SYNC_ONLY"; Assert.ok(!(id in snapshot), `${id} should not be in a main store snapshot`); // Should be empty after clearing snapshot = Telemetry.getSnapshotForHistograms( "main", /* clear */ false ).parent; id = "TELEMETRY_TEST_MAIN_ONLY"; Assert.ok(!(id in snapshot), `${id} should not be in a main store snapshot`); id = "TELEMETRY_TEST_MULTIPLE_STORES"; Assert.ok(!(id in snapshot), `${id} should not be in a main store snapshot`); id = "TELEMETRY_TEST_SYNC_ONLY"; Assert.ok(!(id in snapshot), `${id} should not be in a main store snapshot`); // Keyed histograms // Fill with data id = "TELEMETRY_TEST_KEYED_MULTIPLE_STORES"; hist = Telemetry.getKeyedHistogramById(id); hist.add("key-a", 1); id = "TELEMETRY_TEST_KEYED_SYNC_ONLY"; hist = Telemetry.getKeyedHistogramById(id); hist.add("key-b", 1); // Getting snapshot and NOT clearing (using default values for optional parameters) snapshot = Telemetry.getSnapshotForKeyedHistograms().parent; id = "TELEMETRY_TEST_KEYED_MULTIPLE_STORES"; Assert.ok(id in snapshot, `${id} should be in a main store snapshot`); id = "TELEMETRY_TEST_KEYED_SYNC_ONLY"; Assert.ok(!(id in snapshot), `${id} should not be in a main store snapshot`); // Data should still be in, getting snapshot and clearing snapshot = Telemetry.getSnapshotForKeyedHistograms( "main", /* clear */ true ).parent; id = "TELEMETRY_TEST_KEYED_MULTIPLE_STORES"; Assert.ok(id in snapshot, `${id} should be in a main store snapshot`); id = "TELEMETRY_TEST_KEYED_SYNC_ONLY"; Assert.ok(!(id in snapshot), `${id} should not be in a main store snapshot`); // Should be empty after clearing snapshot = Telemetry.getSnapshotForKeyedHistograms( "main", /* clear */ false ).parent; id = "TELEMETRY_TEST_KEYED_MULTIPLE_STORES"; Assert.ok(!(id in snapshot), `${id} should not be in a main store snapshot`); id = "TELEMETRY_TEST_KEYED_SYNC_ONLY"; Assert.ok(!(id in snapshot), `${id} should not be in a main store snapshot`); }); add_task(async function test_multistore_argument_handling() { Telemetry.canRecordExtended = true; // Clear histograms Telemetry.getSnapshotForHistograms("main", true); Telemetry.getSnapshotForHistograms("sync", true); Telemetry.getSnapshotForKeyedHistograms("main", true); Telemetry.getSnapshotForKeyedHistograms("sync", true); let id; let hist; let snapshot; // Plain Histograms id = "TELEMETRY_TEST_MULTIPLE_STORES"; hist = Telemetry.getHistogramById(id); hist.add(37); // No argument snapshot = hist.snapshot(); Assert.equal(37, snapshot.sum, `${id} should be in a default store snapshot`); hist.clear(); snapshot = hist.snapshot(); Assert.equal(0, snapshot.sum, `${id} should be cleared in the default store`); snapshot = hist.snapshot({ store: "sync" }); Assert.equal( 37, snapshot.sum, `${id} should not have been cleared in the sync store` ); Assert.throws( () => hist.snapshot(2, "or", "more", "arguments"), /one argument/, "snapshot should check argument count" ); Assert.throws( () => hist.snapshot(2), /object argument/, "snapshot should check argument type" ); Assert.throws( () => hist.snapshot({}), /property/, "snapshot should check for object property" ); Assert.throws( () => hist.snapshot({ store: 1 }), /string/, "snapshot should check object property's type" ); Assert.throws( () => hist.clear(2, "or", "more", "arguments"), /one argument/, "clear should check argument count" ); Assert.throws( () => hist.clear(2), /object argument/, "clear should check argument type" ); Assert.throws( () => hist.clear({}), /property/, "clear should check for object property" ); Assert.throws( () => hist.clear({ store: 1 }), /string/, "clear should check object property's type" ); // Keyed Histogram id = "TELEMETRY_TEST_KEYED_MULTIPLE_STORES"; hist = Telemetry.getKeyedHistogramById(id); hist.add("key-1", 37); // No argument snapshot = hist.snapshot(); Assert.equal( 37, snapshot["key-1"].sum, `${id} should be in a default store snapshot` ); hist.clear(); snapshot = hist.snapshot(); Assert.ok( !("key-1" in snapshot), `${id} should be cleared in the default store` ); snapshot = hist.snapshot({ store: "sync" }); Assert.equal( 37, snapshot["key-1"].sum, `${id} should not have been cleared in the sync store` ); Assert.throws( () => hist.snapshot(2, "or", "more", "arguments"), /one argument/, "snapshot should check argument count" ); Assert.throws( () => hist.snapshot(2), /object argument/, "snapshot should check argument type" ); Assert.throws( () => hist.snapshot({}), /property/, "snapshot should check for object property" ); Assert.throws( () => hist.snapshot({ store: 1 }), /string/, "snapshot should check object property's type" ); Assert.throws( () => hist.clear(2, "or", "more", "arguments"), /one argument/, "clear should check argument count" ); Assert.throws( () => hist.clear(2), /object argument/, "clear should check argument type" ); Assert.throws( () => hist.clear({}), /property/, "clear should check for object property" ); Assert.throws( () => hist.clear({ store: 1 }), /string/, "clear should check object property's type" ); }); add_task(async function test_multistore_sync_snapshot() { Telemetry.canRecordExtended = true; // Clear histograms Telemetry.getSnapshotForHistograms("main", true); Telemetry.getSnapshotForHistograms("sync", true); let id; let hist; let snapshot; // Plain histograms // Fill with data id = "TELEMETRY_TEST_MAIN_ONLY"; hist = Telemetry.getHistogramById(id); hist.add(1); id = "TELEMETRY_TEST_MULTIPLE_STORES"; hist = Telemetry.getHistogramById(id); hist.add(1); id = "TELEMETRY_TEST_SYNC_ONLY"; hist = Telemetry.getHistogramById(id); hist.add(1); // Getting snapshot and clearing snapshot = Telemetry.getSnapshotForHistograms( "main", /* clear */ true ).parent; id = "TELEMETRY_TEST_MAIN_ONLY"; Assert.ok(id in snapshot, `${id} should be in a main store snapshot`); id = "TELEMETRY_TEST_MULTIPLE_STORES"; Assert.ok(id in snapshot, `${id} should be in a main store snapshot`); id = "TELEMETRY_TEST_SYNC_ONLY"; Assert.ok(!(id in snapshot), `${id} should not be in a main store snapshot`); snapshot = Telemetry.getSnapshotForHistograms( "sync", /* clear */ true ).parent; id = "TELEMETRY_TEST_MAIN_ONLY"; Assert.ok(!(id in snapshot), `${id} should not be in a sync store snapshot`); id = "TELEMETRY_TEST_MULTIPLE_STORES"; Assert.ok(id in snapshot, `${id} should be in a sync store snapshot`); id = "TELEMETRY_TEST_SYNC_ONLY"; Assert.ok(id in snapshot, `${id} should be in a sync store snapshot`); }); add_task(async function test_multistore_keyed_sync_snapshot() { Telemetry.canRecordExtended = true; // Clear histograms Telemetry.getSnapshotForKeyedHistograms("main", true); Telemetry.getSnapshotForKeyedHistograms("sync", true); let id; let hist; let snapshot; // Plain histograms // Fill with data id = "TELEMETRY_TEST_KEYED_LINEAR"; hist = Telemetry.getKeyedHistogramById(id); hist.add("key-1", 1); id = "TELEMETRY_TEST_KEYED_MULTIPLE_STORES"; hist = Telemetry.getKeyedHistogramById(id); hist.add("key-1", 1); id = "TELEMETRY_TEST_KEYED_SYNC_ONLY"; hist = Telemetry.getKeyedHistogramById(id); hist.add("key-1", 1); // Getting snapshot and clearing snapshot = Telemetry.getSnapshotForKeyedHistograms( "main", /* clear */ true ).parent; id = "TELEMETRY_TEST_KEYED_LINEAR"; Assert.ok(id in snapshot, `${id} should be in a main store snapshot`); id = "TELEMETRY_TEST_KEYED_MULTIPLE_STORES"; Assert.ok(id in snapshot, `${id} should be in a main store snapshot`); id = "TELEMETRY_TEST_KEYED_SYNC_ONLY"; Assert.ok(!(id in snapshot), `${id} should not be in a main store snapshot`); snapshot = Telemetry.getSnapshotForKeyedHistograms( "sync", /* clear */ true ).parent; id = "TELEMETRY_TEST_KEYED_LINEAR"; Assert.ok(!(id in snapshot), `${id} should not be in a sync store snapshot`); id = "TELEMETRY_TEST_KEYED_MULTIPLE_STORES"; Assert.ok(id in snapshot, `${id} should be in a sync store snapshot`); id = "TELEMETRY_TEST_KEYED_SYNC_ONLY"; Assert.ok(id in snapshot, `${id} should be in a sync store snapshot`); }); add_task(async function test_multistore_plain_individual_snapshot() { Telemetry.canRecordExtended = true; // Clear histograms Telemetry.getSnapshotForHistograms("main", true); Telemetry.getSnapshotForHistograms("sync", true); let id; let hist; id = "TELEMETRY_TEST_MAIN_ONLY"; hist = Telemetry.getHistogramById(id); hist.add(37); Assert.deepEqual(37, hist.snapshot({ store: "main" }).sum); Assert.deepEqual(undefined, hist.snapshot({ store: "sync" })); hist.clear({ store: "main" }); Assert.deepEqual(0, hist.snapshot({ store: "main" }).sum); Assert.deepEqual(undefined, hist.snapshot({ store: "sync" })); id = "TELEMETRY_TEST_MULTIPLE_STORES"; hist = Telemetry.getHistogramById(id); hist.add(37); Assert.deepEqual(37, hist.snapshot({ store: "main" }).sum); Assert.deepEqual(37, hist.snapshot({ store: "sync" }).sum); hist.clear({ store: "main" }); Assert.deepEqual(0, hist.snapshot({ store: "main" }).sum); Assert.deepEqual(37, hist.snapshot({ store: "sync" }).sum); hist.add(3); Assert.deepEqual(3, hist.snapshot({ store: "main" }).sum); Assert.deepEqual(40, hist.snapshot({ store: "sync" }).sum); hist.clear({ store: "sync" }); Assert.deepEqual(3, hist.snapshot({ store: "main" }).sum); Assert.deepEqual(0, hist.snapshot({ store: "sync" }).sum); id = "TELEMETRY_TEST_SYNC_ONLY"; hist = Telemetry.getHistogramById(id); hist.add(37); Assert.deepEqual(undefined, hist.snapshot({ store: "main" })); Assert.deepEqual(37, hist.snapshot({ store: "sync" }).sum); hist.clear({ store: "main" }); Assert.deepEqual(undefined, hist.snapshot({ store: "main" })); Assert.deepEqual(37, hist.snapshot({ store: "sync" }).sum); hist.add(3); Assert.deepEqual(undefined, hist.snapshot({ store: "main" })); Assert.deepEqual(40, hist.snapshot({ store: "sync" }).sum); hist.clear({ store: "sync" }); Assert.deepEqual(undefined, hist.snapshot({ store: "main" })); Assert.deepEqual(0, hist.snapshot({ store: "sync" }).sum); }); add_task(async function test_multistore_keyed_individual_snapshot() { Telemetry.canRecordExtended = true; // Clear histograms Telemetry.getSnapshotForKeyedHistograms("main", true); Telemetry.getSnapshotForKeyedHistograms("sync", true); let id; let hist; id = "TELEMETRY_TEST_KEYED_LINEAR"; hist = Telemetry.getKeyedHistogramById(id); hist.add("key-1", 37); Assert.deepEqual(37, hist.snapshot({ store: "main" })["key-1"].sum); Assert.deepEqual(undefined, hist.snapshot({ store: "sync" })); hist.clear({ store: "main" }); Assert.deepEqual({}, hist.snapshot({ store: "main" })); Assert.deepEqual(undefined, hist.snapshot({ store: "sync" })); hist.add("key-1", 4); hist.clear({ store: "sync" }); Assert.deepEqual(4, hist.snapshot({ store: "main" })["key-1"].sum); Assert.deepEqual(undefined, hist.snapshot({ store: "sync" })); id = "TELEMETRY_TEST_KEYED_MULTIPLE_STORES"; hist = Telemetry.getKeyedHistogramById(id); hist.add("key-1", 37); Assert.deepEqual(37, hist.snapshot({ store: "main" })["key-1"].sum); Assert.deepEqual(37, hist.snapshot({ store: "sync" })["key-1"].sum); hist.clear({ store: "main" }); Assert.deepEqual({}, hist.snapshot({ store: "main" })); Assert.deepEqual(37, hist.snapshot({ store: "sync" })["key-1"].sum); hist.add("key-1", 3); Assert.deepEqual(3, hist.snapshot({ store: "main" })["key-1"].sum); Assert.deepEqual(40, hist.snapshot({ store: "sync" })["key-1"].sum); hist.clear({ store: "sync" }); Assert.deepEqual(3, hist.snapshot({ store: "main" })["key-1"].sum); Assert.deepEqual({}, hist.snapshot({ store: "sync" })); id = "TELEMETRY_TEST_KEYED_SYNC_ONLY"; hist = Telemetry.getKeyedHistogramById(id); hist.add("key-1", 37); Assert.deepEqual(undefined, hist.snapshot({ store: "main" })); Assert.deepEqual(37, hist.snapshot({ store: "sync" })["key-1"].sum); hist.clear({ store: "main" }); Assert.deepEqual(undefined, hist.snapshot({ store: "main" })); Assert.deepEqual(37, hist.snapshot({ store: "sync" })["key-1"].sum); hist.add("key-1", 3); Assert.deepEqual(undefined, hist.snapshot({ store: "main" })); Assert.deepEqual(40, hist.snapshot({ store: "sync" })["key-1"].sum); hist.clear({ store: "sync" }); Assert.deepEqual(undefined, hist.snapshot({ store: "main" })); Assert.deepEqual({}, hist.snapshot({ store: "sync" })); }); add_task(async function test_can_record_in_process_regression_bug_1530361() { Telemetry.getSnapshotForHistograms("main", true); // The socket and gpu processes should not have any histograms. // Flag and count histograms have defaults, so if we're accidentally recording them // in these processes they'd show up even immediately after being cleared. let snapshot = Telemetry.getSnapshotForHistograms("main", true); Assert.deepEqual( snapshot.gpu, {}, "No histograms should have been recorded for the gpu process" ); Assert.deepEqual( snapshot.socket, {}, "No histograms should have been recorded for the socket process" ); }); add_task(function test_knows_its_name() { let h; // Plain histograms const histNames = [ "TELEMETRY_TEST_FLAG", "TELEMETRY_TEST_COUNT", "TELEMETRY_TEST_CATEGORICAL", "TELEMETRY_TEST_EXPIRED", ]; for (let name of histNames) { h = Telemetry.getHistogramById(name); Assert.equal(name, h.name()); } // Keyed histograms const keyedHistNames = [ "TELEMETRY_TEST_KEYED_EXPONENTIAL", "TELEMETRY_TEST_KEYED_BOOLEAN", "TELEMETRY_TEST_EXPIRED_KEYED", ]; for (let name of keyedHistNames) { h = Telemetry.getKeyedHistogramById(name); Assert.equal(name, h.name()); } });