Bug 1468761 - Migrate Telemetry tests to always assume packed histograms r=chutten
authorJan-Erik Rediger <jrediger@mozilla.com>
Thu, 01 Nov 2018 13:46:57 +0000
changeset 443944 1774d920a1706dfb4e705039a0e1ee38d8fb54fc
parent 443943 6365c218150455fe61e575f6c5e131fc429c5780
child 443945 76dccff8312e6c8de36f87ab246bf489358bca7a
push id34976
push userdvarga@mozilla.com
push dateThu, 01 Nov 2018 22:26:55 +0000
treeherdermozilla-central@b953c577afe2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschutten
bugs1468761
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1468761 - Migrate Telemetry tests to always assume packed histograms r=chutten Depends on D9235 Differential Revision: https://phabricator.services.mozilla.com/D9236
toolkit/components/telemetry/tests/gtest/TestHistograms.cpp
toolkit/components/telemetry/tests/unit/head.js
toolkit/components/telemetry/tests/unit/test_PingAPI.js
toolkit/components/telemetry/tests/unit/test_TelemetryEvents.js
toolkit/components/telemetry/tests/unit/test_TelemetryFlagClear.js
toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js
toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
toolkit/components/telemetry/tests/unit/test_TelemetryStopwatch.js
toolkit/components/telemetry/tests/unit/test_TelemetryUtils.js
--- a/toolkit/components/telemetry/tests/gtest/TestHistograms.cpp
+++ b/toolkit/components/telemetry/tests/gtest/TestHistograms.cpp
@@ -161,25 +161,25 @@ TEST_F(TelemetryTestFixture, AccumulateC
   // Get a snapshot for all the histograms
   JS::RootedValue snapshot(cx.GetJSContext());
   GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_CATEGORICAL", &snapshot, false);
 
   // Get our histogram from the snapshot
   JS::RootedValue histogram(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_CATEGORICAL", snapshot, &histogram);
 
-  // Get counts array from histogram. Each entry in the array maps to a label in the histogram.
-  JS::RootedValue counts(cx.GetJSContext());
-  GetProperty(cx.GetJSContext(), "counts", histogram,  &counts);
+  // Get values object from histogram. Each entry in the object maps to a label in the histogram.
+  JS::RootedValue values(cx.GetJSContext());
+  GetProperty(cx.GetJSContext(), "values", histogram,  &values);
 
   // Get the value for the label we care about
   JS::RootedValue value(cx.GetJSContext());
   GetElement(cx.GetJSContext(),
              static_cast<uint32_t>(Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL::CommonLabel),
-             counts, &value);
+             values, &value);
 
   // Check that the value stored in the histogram matches with |kExpectedValue|
   uint32_t uValue = 0;
   JS::ToUint32(cx.GetJSContext(), value, &uValue);
   ASSERT_EQ(uValue, kExpectedValue) << "The histogram is not returning expected value";
 }
 
 TEST_F(TelemetryTestFixture, AccumulateKeyedCategoricalHistogram)
@@ -208,40 +208,40 @@ TEST_F(TelemetryTestFixture, AccumulateK
   GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_KEYED_CATEGORICAL", &snapshot, true);
   // Get the histogram from the snapshot
   JS::RootedValue histogram(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_KEYED_CATEGORICAL", snapshot, &histogram);
 
   // Check that the sample histogram contains the values we expect
   JS::RootedValue sample(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "sample", histogram,  &sample);
-  // Get counts array from the sample. Each entry in the array maps to a label in the histogram.
-  JS::RootedValue sampleCounts(cx.GetJSContext());
-  GetProperty(cx.GetJSContext(), "counts", sample,  &sampleCounts);
+  // Get values object from sample. Each entry in the object maps to a label in the histogram.
+  JS::RootedValue sampleValues(cx.GetJSContext());
+  GetProperty(cx.GetJSContext(), "values", sample,  &sampleValues);
   // Get the value for the label we care about
   JS::RootedValue sampleValue(cx.GetJSContext());
   GetElement(cx.GetJSContext(),
              static_cast<uint32_t>(Telemetry::LABELS_TELEMETRY_TEST_KEYED_CATEGORICAL::CommonLabel),
-             sampleCounts, &sampleValue);
+             sampleValues, &sampleValue);
   // Check that the value stored in the histogram matches with |kSampleExpectedValue|
   uint32_t uSampleValue = 0;
   JS::ToUint32(cx.GetJSContext(), sampleValue, &uSampleValue);
   ASSERT_EQ(uSampleValue, kSampleExpectedValue) << "The sample histogram is not returning expected value";
 
   // Check that the other-sample histogram contains the values we expect
   JS::RootedValue otherSample(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "other-sample", histogram,  &otherSample);
-  // Get counts array from the other-sample. Each entry in the array maps to a label in the histogram.
-  JS::RootedValue otherCounts(cx.GetJSContext());
-  GetProperty(cx.GetJSContext(), "counts", otherSample,  &otherCounts);
+  // Get values object from the other-sample. Each entry in the object maps to a label in the histogram.
+  JS::RootedValue otherValues(cx.GetJSContext());
+  GetProperty(cx.GetJSContext(), "values", otherSample,  &otherValues);
   // Get the value for the label we care about
   JS::RootedValue otherValue(cx.GetJSContext());
   GetElement(cx.GetJSContext(),
              static_cast<uint32_t>(Telemetry::LABELS_TELEMETRY_TEST_KEYED_CATEGORICAL::CommonLabel),
-             otherCounts, &otherValue);
+             otherValues, &otherValue);
   // Check that the value stored in the histogram matches with |kOtherSampleExpectedValue|
   uint32_t uOtherValue = 0;
   JS::ToUint32(cx.GetJSContext(), otherValue, &uOtherValue);
   ASSERT_EQ(uOtherValue, kOtherSampleExpectedValue) << "The other-sample histogram is not returning expected value";
 }
 
 TEST_F(TelemetryTestFixture, AccumulateCountHistogram_MultipleSamples)
 {
@@ -290,24 +290,24 @@ TEST_F(TelemetryTestFixture, AccumulateL
   // Get a snapshot of all the histograms
   JS::RootedValue snapshot(cx.GetJSContext());
   GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_LINEAR", &snapshot, false);
 
   // Get histogram from snapshot
   JS::RootedValue histogram(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_LINEAR", snapshot, &histogram);
 
-  // Get "counts" array from histogram
-  JS::RootedValue counts(cx.GetJSContext());
-  GetProperty(cx.GetJSContext(), "counts", histogram, &counts);
+  // Get "values" object from histogram
+  JS::RootedValue values(cx.GetJSContext());
+  GetProperty(cx.GetJSContext(), "values", histogram, &values);
 
   // Index 0 is only for values less than 'low'. Values within range start at index 1
   JS::RootedValue count(cx.GetJSContext());
   const uint32_t index = 1;
-  GetElement(cx.GetJSContext(), index, counts, &count);
+  GetElement(cx.GetJSContext(), index, values, &count);
 
   // Check that this count matches with nSamples
   uint32_t uCount = 0;
   JS::ToUint32(cx.GetJSContext(), count, &uCount);
   ASSERT_EQ(uCount, kExpectedCount) << "The histogram did not accumulate the correct number of values";
 }
 
 TEST_F(TelemetryTestFixture, AccumulateLinearHistogram_DifferentSamples)
@@ -326,29 +326,30 @@ TEST_F(TelemetryTestFixture, AccumulateL
   // Get a snapshot of all histograms
   JS::RootedValue snapshot(cx.GetJSContext());
   GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_LINEAR", &snapshot, false);
 
   // Get histogram from snapshot
   JS::RootedValue histogram(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_LINEAR", snapshot, &histogram);
 
-  // Get counts array from histogram
-  JS::RootedValue counts(cx.GetJSContext());
-  GetProperty(cx.GetJSContext(), "counts", histogram, &counts);
+  // Get values object from histogram
+  JS::RootedValue values(cx.GetJSContext());
+  GetProperty(cx.GetJSContext(), "values", histogram, &values);
 
-  // Get counts in first and last buckets
+  // Get values in first and last buckets
   JS::RootedValue countFirst(cx.GetJSContext());
   JS::RootedValue countLast(cx.GetJSContext());
   const uint32_t firstIndex = 1;
-  const uint32_t lastIndex = 9;
-  GetElement(cx.GetJSContext(), firstIndex, counts, &countFirst);
-  GetElement(cx.GetJSContext(), lastIndex, counts, &countLast);
+  // Buckets are indexed by their start value
+  const uint32_t lastIndex = INT32_MAX - 1;
+  GetElement(cx.GetJSContext(), firstIndex, values, &countFirst);
+  GetElement(cx.GetJSContext(), lastIndex, values, &countLast);
 
-  // Check that the counts match
+  // Check that the values match
   uint32_t uCountFirst = 0;
   uint32_t uCountLast = 0;
   JS::ToUint32(cx.GetJSContext(), countFirst, &uCountFirst);
   JS::ToUint32(cx.GetJSContext(), countLast, &uCountLast);
 
   const uint32_t kExpectedCountFirst = 2;
   // We expect 2147483646 to be in the last bucket, as well the two samples above 2^31
   // (prior to bug 1438335, values between INT_MAX and UINT32_MAX would end up as 0s)
@@ -425,29 +426,30 @@ TEST_F(TelemetryTestFixture, TestKeyedLi
   GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_KEYED_LINEAR", snapshot, &histogram);
 
   // Get "testkey" property from histogram.
   JS::RootedValue expectedKeyData(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "testkey", histogram,  &expectedKeyData);
   ASSERT_TRUE(!expectedKeyData.isUndefined())
     << "Cannot find the expected key in the histogram data";
 
-  // Get counts array from 'testkey' histogram.
-  JS::RootedValue counts(cx.GetJSContext());
-  GetProperty(cx.GetJSContext(), "counts", expectedKeyData, &counts);
+  // Get values object from 'testkey' histogram.
+  JS::RootedValue values(cx.GetJSContext());
+  GetProperty(cx.GetJSContext(), "values", expectedKeyData, &values);
 
-  // Get counts in first and last buckets.
+  // Get values in first and last buckets.
   JS::RootedValue countFirst(cx.GetJSContext());
   JS::RootedValue countLast(cx.GetJSContext());
   const uint32_t firstIndex = 1;
-  const uint32_t lastIndex = 9;
-  GetElement(cx.GetJSContext(), firstIndex, counts, &countFirst);
-  GetElement(cx.GetJSContext(), lastIndex, counts, &countLast);
+  // Buckets are indexed by their start value
+  const uint32_t lastIndex = 250000;
+  GetElement(cx.GetJSContext(), firstIndex, values, &countFirst);
+  GetElement(cx.GetJSContext(), lastIndex, values, &countLast);
 
-  // Check that the counts match.
+  // Check that the values match.
   uint32_t uCountFirst = 0;
   uint32_t uCountLast = 0;
   JS::ToUint32(cx.GetJSContext(), countFirst, &uCountFirst);
   JS::ToUint32(cx.GetJSContext(), countLast, &uCountLast);
 
   const uint32_t kExpectedCountFirst = 2;
   const uint32_t kExpectedCountLast = 2;
   ASSERT_EQ(uCountFirst, kExpectedCountFirst)
@@ -490,31 +492,31 @@ TEST_F(TelemetryTestFixture, TestKeyedKe
   GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_KEYED_KEYS", snapshot, &histogram);
 
   // Get "testkey" property from histogram and check that it stores the correct data.
   JS::RootedValue testKeyData(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "testkey", histogram,  &testKeyData);
   ASSERT_TRUE(!testKeyData.isUndefined())
     << "Cannot find the key 'testkey' in the histogram data";
 
-  JS::RootedValue counts(cx.GetJSContext());
-  GetProperty(cx.GetJSContext(), "counts", testKeyData,  &counts);
+  JS::RootedValue values(cx.GetJSContext());
+  GetProperty(cx.GetJSContext(), "values", testKeyData,  &values);
 
-  // Get counts in buckets 0,1,2
+  // Get values in buckets 0,1,2
   const uint32_t falseIndex = 0;
   const uint32_t trueIndex = 1;
   const uint32_t otherIndex = 2;
 
   JS::RootedValue countFalse(cx.GetJSContext());
   JS::RootedValue countTrue(cx.GetJSContext());
   JS::RootedValue countOther(cx.GetJSContext());
 
-  GetElement(cx.GetJSContext(), falseIndex, counts, &countFalse);
-  GetElement(cx.GetJSContext(), trueIndex, counts, &countTrue);
-  GetElement(cx.GetJSContext(), otherIndex, counts, &countOther);
+  GetElement(cx.GetJSContext(), falseIndex, values, &countFalse);
+  GetElement(cx.GetJSContext(), trueIndex, values, &countTrue);
+  GetElement(cx.GetJSContext(), otherIndex, values, &countOther);
 
   uint32_t uCountFalse = 0;
   uint32_t uCountTrue = 0;
   uint32_t uCountOther = 0;
   JS::ToUint32(cx.GetJSContext(), countFalse, &uCountFalse);
   JS::ToUint32(cx.GetJSContext(), countTrue, &uCountTrue);
   JS::ToUint32(cx.GetJSContext(), countOther, &uCountOther);
 
@@ -573,56 +575,56 @@ TEST_F(TelemetryTestFixture, AccumulateC
   // Get a snapshot for all the histograms
   JS::RootedValue snapshot(cx.GetJSContext());
   GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_CATEGORICAL", &snapshot, false);
 
   // Get our histogram from the snapshot
   JS::RootedValue histogram(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_CATEGORICAL", snapshot, &histogram);
 
-  // Get counts array from histogram. Each entry in the array maps to a label in the histogram.
-  JS::RootedValue counts(cx.GetJSContext());
-  GetProperty(cx.GetJSContext(), "counts", histogram,  &counts);
+  // Get values object from histogram. Each entry in the object maps to a label in the histogram.
+  JS::RootedValue values(cx.GetJSContext());
+  GetProperty(cx.GetJSContext(), "values", histogram,  &values);
 
   // Get the value for the label we care about
   JS::RootedValue value(cx.GetJSContext());
   GetElement(cx.GetJSContext(),
              static_cast<uint32_t>(Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL::CommonLabel),
-             counts, &value);
+             values, &value);
 
   // Check that the value stored in the histogram matches with |kExpectedValue|
   uint32_t uValue = 0;
   JS::ToUint32(cx.GetJSContext(), value, &uValue);
   ASSERT_EQ(uValue, kExpectedValue) << "The histogram is not returning expected value";
 
   // Now we check for no accumulation when a bad label is present in the array.
   //
-  // The 'counts' property is not initialized unless data is accumulated so keeping another test
+  // The 'values' property is not initialized unless data is accumulated so keeping another test
   // to check for this case alone is wasteful as we will have to accumulate some data anyway.
 
   const nsTArray<nsCString> badLabelArray({
                             NS_LITERAL_CSTRING("CommonLabel"),
                             NS_LITERAL_CSTRING("BadLabel")});
 
   // Try to accumulate the array into the histogram.
   Telemetry::AccumulateCategorical(Telemetry::TELEMETRY_TEST_CATEGORICAL, badLabelArray);
 
   // Get snapshot of all the histograms
   GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_CATEGORICAL", &snapshot, false);
 
   // Get our histogram from the snapshot
   GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_CATEGORICAL", snapshot, &histogram);
 
-  // Get counts array from histogram
-  GetProperty(cx.GetJSContext(), "counts", histogram, &counts);
+  // Get values array from histogram
+  GetProperty(cx.GetJSContext(), "values", histogram, &values);
 
   // Get the value for the label we care about
   GetElement(cx.GetJSContext(),
              static_cast<uint32_t>(Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL::CommonLabel),
-             counts, &value);
+             values, &value);
 
   // Check that the value stored in the histogram matches with |kExpectedValue|
   uValue = 0;
   JS::ToUint32(cx.GetJSContext(), value, &uValue);
   ASSERT_EQ(uValue, kExpectedValue) << "The histogram accumulated data when it should not have";
 }
 
 TEST_F(TelemetryTestFixture, AccumulateCategoricalHistogram_MultipleEnumValues)
@@ -643,25 +645,25 @@ TEST_F(TelemetryTestFixture, AccumulateC
   // Get a snapshot for all the histograms
   JS::RootedValue snapshot(cx.GetJSContext());
   GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_CATEGORICAL", &snapshot, false);
 
   // Get our histogram from the snapshot
   JS::RootedValue histogram(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_CATEGORICAL", snapshot, &histogram);
 
-  // Get counts array from histogram. Each entry in the array maps to a label in the histogram.
-  JS::RootedValue counts(cx.GetJSContext());
-  GetProperty(cx.GetJSContext(), "counts", histogram,  &counts);
+  // Get values object from histogram. Each entry in the object maps to a label in the histogram.
+  JS::RootedValue values(cx.GetJSContext());
+  GetProperty(cx.GetJSContext(), "values", histogram,  &values);
 
   // Get the value for the label we care about
   JS::RootedValue value(cx.GetJSContext());
   GetElement(cx.GetJSContext(),
              static_cast<uint32_t>(Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL::CommonLabel),
-             counts, &value);
+             values, &value);
 
   // Check that the value stored in the histogram matches with |kExpectedValue|
   uint32_t uValue = 0;
   JS::ToUint32(cx.GetJSContext(), value, &uValue);
   ASSERT_EQ(uValue, kExpectedValue) << "The histogram is not returning expected value";
 }
 
 TEST_F(TelemetryTestFixture, AccumulateKeyedCategoricalHistogram_MultipleEnumValues)
@@ -688,38 +690,38 @@ TEST_F(TelemetryTestFixture, AccumulateK
   // Get the histogram from the snapshot
   JS::RootedValue histogram(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_KEYED_CATEGORICAL", snapshot, &histogram);
 
   // Check that the sampleKey histogram contains correct number of CommonLabel samples
   JS::RootedValue sample(cx.GetJSContext());
   GetProperty(cx.GetJSContext(), "sampleKey", histogram,  &sample);
 
-  // Get counts array from the sample. Each entry in the array maps to a label in the histogram.
-  JS::RootedValue sampleKeyCounts(cx.GetJSContext());
-  GetProperty(cx.GetJSContext(), "counts", sample,  &sampleKeyCounts);
+  // Get values object from the sample. Each entry in the object maps to a label in the histogram.
+  JS::RootedValue sampleKeyValues(cx.GetJSContext());
+  GetProperty(cx.GetJSContext(), "values", sample,  &sampleKeyValues);
 
   // Get the count of CommonLabel
   JS::RootedValue commonLabelValue(cx.GetJSContext());
   GetElement(cx.GetJSContext(),
              static_cast<uint32_t>(Telemetry::LABELS_TELEMETRY_TEST_KEYED_CATEGORICAL::CommonLabel),
-             sampleKeyCounts, &commonLabelValue);
+             sampleKeyValues, &commonLabelValue);
 
   // Check that the value stored in the histogram matches with |kExpectedCommonLabel|
   uint32_t uCommonLabelValue = 0;
   JS::ToUint32(cx.GetJSContext(), commonLabelValue, &uCommonLabelValue);
   ASSERT_EQ(uCommonLabelValue, kExpectedCommonLabel)
         << "The sampleKey histogram did not accumulate the correct number of CommonLabel samples";
 
   // Check that the sampleKey histogram contains the correct number of Label2 values
   // Get the count of Label2
   JS::RootedValue label2Value(cx.GetJSContext());
   GetElement(cx.GetJSContext(),
              static_cast<uint32_t>(Telemetry::LABELS_TELEMETRY_TEST_KEYED_CATEGORICAL::Label2),
-             sampleKeyCounts, &label2Value);
+             sampleKeyValues, &label2Value);
 
   // Check that the value stored in the histogram matches with |kExpectedLabel2|
   uint32_t uLabel2Value = 0;
   JS::ToUint32(cx.GetJSContext(), label2Value, &uLabel2Value);
   ASSERT_EQ(uLabel2Value, kExpectedLabel2)
         << "The sampleKey histogram did not accumulate the correct number of Label2 samples";
 }
 
--- a/toolkit/components/telemetry/tests/unit/head.js
+++ b/toolkit/components/telemetry/tests/unit/head.js
@@ -339,20 +339,16 @@ function setEmptyPrefWatchlist() {
   let TelemetryEnvironment =
     ChromeUtils.import("resource://gre/modules/TelemetryEnvironment.jsm").TelemetryEnvironment;
   return TelemetryEnvironment.onInitialized().then(() => {
     TelemetryEnvironment.testWatchPreferences(new Map());
 
   });
 }
 
-function histogramValueCount(histogramSnapshot) {
-  return histogramSnapshot.counts.reduce((a, b) => a + b);
-}
-
 if (runningInParent) {
   // Set logging preferences for all the tests.
   Services.prefs.setCharPref("toolkit.telemetry.log.level", "Trace");
   // Telemetry archiving should be on.
   Services.prefs.setBoolPref(TelemetryUtils.Preferences.ArchiveEnabled, true);
   // Telemetry xpcshell tests cannot show the infobar.
   Services.prefs.setBoolPref(TelemetryUtils.Preferences.BypassNotification, true);
   // FHR uploads should be enabled.
--- a/toolkit/components/telemetry/tests/unit/test_PingAPI.js
+++ b/toolkit/components/telemetry/tests/unit/test_PingAPI.js
@@ -388,17 +388,17 @@ add_task(async function test_archiveClea
   await TelemetryArchive.promiseArchivedPingList();
   // The following also checks that non oversized pings are not removed.
   await checkArchive();
 
   // Make sure we're correctly updating the related histograms.
   h = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_ARCHIVED").snapshot();
   Assert.equal(h.sum, 1, "Telemetry must report 1 oversized ping in the archive.");
   h = Telemetry.getHistogramById("TELEMETRY_DISCARDED_ARCHIVED_PINGS_SIZE_MB").snapshot();
-  Assert.equal(h.counts[archivedPingSizeMB], 1,
+  Assert.equal(h.values[archivedPingSizeMB], 1,
                "Telemetry must report the correct size for the oversized ping.");
 });
 
 add_task(async function test_clientId() {
   // Check that a ping submitted after the delayed telemetry initialization completed
   // should get a valid client id.
   await TelemetryController.testReset();
   const clientId = await ClientID.getClientID();
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEvents.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEvents.js
@@ -59,32 +59,32 @@ function checkEventSummary(summaries, cl
 }
 
 function checkRegistrationFailure(failureType) {
   let snapshot = Telemetry.snapshotHistograms(OPTIN, true);
   Assert.ok("parent" in snapshot,
             "There should be at least one parent histogram when checking for registration failures.");
   Assert.ok("TELEMETRY_EVENT_REGISTRATION_ERROR" in snapshot.parent,
             "TELEMETRY_EVENT_REGISTRATION_ERROR should exist when checking for registration failures.");
-  let counts = snapshot.parent.TELEMETRY_EVENT_REGISTRATION_ERROR.counts;
-  Assert.ok(!!counts,
-            "TELEMETRY_EVENT_REGISTRATION_ERROR's counts should exist when checking for registration failures.");
-  Assert.equal(counts[failureType], 1, `Event registration ought to have failed due to type ${failureType}`);
+  let values = snapshot.parent.TELEMETRY_EVENT_REGISTRATION_ERROR.values;
+  Assert.ok(!!values,
+            "TELEMETRY_EVENT_REGISTRATION_ERROR's values should exist when checking for registration failures.");
+  Assert.equal(values[failureType], 1, `Event registration ought to have failed due to type ${failureType}`);
 }
 
 function checkRecordingFailure(failureType) {
   let snapshot = Telemetry.snapshotHistograms(OPTIN, true);
   Assert.ok("parent" in snapshot,
             "There should be at least one parent histogram when checking for recording failures.");
   Assert.ok("TELEMETRY_EVENT_RECORDING_ERROR" in snapshot.parent,
             "TELEMETRY_EVENT_RECORDING_ERROR should exist when checking for recording failures.");
-  let counts = snapshot.parent.TELEMETRY_EVENT_RECORDING_ERROR.counts;
-  Assert.ok(!!counts,
-            "TELEMETRY_EVENT_RECORDING_ERROR's counts should exist when checking for recording failures.");
-  Assert.equal(counts[failureType], 1, `Event recording ought to have failed due to type ${failureType}`);
+  let values = snapshot.parent.TELEMETRY_EVENT_RECORDING_ERROR.values;
+  Assert.ok(!!values,
+            "TELEMETRY_EVENT_RECORDING_ERROR's values should exist when checking for recording failures.");
+  Assert.equal(values[failureType], 1, `Event recording ought to have failed due to type ${failureType}`);
 }
 
 add_task(async function test_event_summary_limit() {
   if (AppConstants.DEBUG) {
     // This test will intentionally assert in DEBUG builds
     return;
   }
   Telemetry.clearEvents();
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryFlagClear.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryFlagClear.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function run_test() {
   let testFlag = Services.telemetry.getHistogramById("TELEMETRY_TEST_FLAG");
-  equal(JSON.stringify(testFlag.snapshot().counts), "[1,0,0]", "Original value is correct");
+  deepEqual(testFlag.snapshot().values, {0: 1, 1: 0}, "Original value is correct");
   testFlag.add(1);
-  equal(JSON.stringify(testFlag.snapshot().counts), "[0,1,0]", "Value is correct after ping.");
+  deepEqual(testFlag.snapshot().values, {0: 0, 1: 1, 2: 0}, "Value is correct after ping");
   testFlag.clear();
-  equal(JSON.stringify(testFlag.snapshot().counts), "[1,0,0]", "Value is correct after calling clear()");
+  deepEqual(testFlag.snapshot().values, {0: 1, 1: 0}, "Value is correct after calling clear()");
   testFlag.add(1);
-  equal(JSON.stringify(testFlag.snapshot().counts), "[0,1,0]", "Value is correct after ping.");
+  deepEqual(testFlag.snapshot().values, {0: 0, 1: 1, 2: 0}, "Value is correct after ping");
 }
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
@@ -32,79 +32,45 @@ function expect_success(f) {
     f();
     succeeded = true;
   } catch (e) {
     succeeded = false;
   }
   Assert.ok(succeeded);
 }
 
-function compareHistograms(h1, h2) {
-  let s1 = h1.snapshot();
-  let s2 = h2.snapshot();
-
-  Assert.equal(s1.histogram_type, s2.histogram_type);
-  Assert.equal(s1.min, s2.min);
-  Assert.equal(s1.max, s2.max);
-  Assert.equal(s1.sum, s2.sum);
-
-  Assert.equal(s1.counts.length, s2.counts.length);
-  for (let i = 0; i < s1.counts.length; i++)
-    Assert.equal(s1.counts[i], s2.counts[i]);
-
-  Assert.equal(s1.ranges.length, s2.ranges.length);
-  for (let i = 0; i < s1.ranges.length; i++)
-    Assert.equal(s1.ranges[i], s2.ranges[i]);
-}
-
 function check_histogram(histogram_type, name, min, max, bucket_count) {
   var h = Telemetry.getHistogramById(name);
-  var r = h.snapshot().ranges;
-  var sum = 0;
-  for (let i = 0;i < r.length;i++) {
-    var v = r[i];
-    sum += v;
-    h.add(v);
-  }
+  h.add(0);
   var s = h.snapshot();
-  // verify properties
-  Assert.equal(sum, s.sum);
+  Assert.equal(0, s.sum);
 
-  // there should be exactly one element per bucket
-  for (let i of s.counts) {
-    Assert.equal(i, 1);
-  }
   var hgrams = Telemetry.snapshotHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN,
                                             false).parent;
   let gh = hgrams[name];
   Assert.equal(gh.histogram_type, histogram_type);
 
-  Assert.equal(gh.min, min);
-  Assert.equal(gh.max, max);
+  Assert.deepEqual(gh.range, [min, max]);
 
   // Check that booleans work with nonboolean histograms
   h.add(false);
   h.add(true);
-  s = h.snapshot().counts;
-  Assert.equal(s[0], 2);
-  Assert.equal(s[1], 2);
+  s = Object.values(h.snapshot().values);
+  Assert.deepEqual(s, [2, 1, 0]);
 
   // Check that clearing works.
   h.clear();
   s = h.snapshot();
-  for (var i of s.counts) {
-    Assert.equal(i, 0);
-  }
+  Assert.deepEqual(s.values, {});
   Assert.equal(s.sum, 0);
 
   h.add(0);
   h.add(1);
-  var c = h.snapshot().counts;
-  Assert.equal(c[0], 1);
-  Assert.equal(c[1], 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";
@@ -176,127 +142,114 @@ add_task(async function test_noSerializa
   Telemetry.getHistogramById("NEWTAB_PAGE_PINNED_SITES_COUNT");
   let histograms = Telemetry.snapshotHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN,
                                                 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().ranges;
+  var r = h.snapshot().range;
   // boolean histograms ignore numeric parameters
-  Assert.equal(uneval(r), uneval([0, 1, 2]));
-  for (var i = 0;i < r.length;i++) {
-    var v = r[i];
-    h.add(v);
-  }
+  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.equal(s.counts[2], 0);
+  Assert.deepEqual(s.values, {0: 2, 1: 3, 2: 0});
   Assert.equal(s.sum, 3);
-  Assert.equal(s.counts[0], 2);
 });
 
 add_task(async function test_flag_histogram() {
   var h = Telemetry.getHistogramById("TELEMETRY_TEST_FLAG");
-  var r = h.snapshot().ranges;
+  var r = h.snapshot().range;
   // Flag histograms ignore numeric parameters.
-  Assert.equal(uneval(r), uneval([0, 1, 2]));
+  Assert.deepEqual(r, [1, 2]);
   // Should already have a 0 counted.
-  var c = h.snapshot().counts;
+  var v = h.snapshot().values;
   var s = h.snapshot().sum;
-  Assert.equal(uneval(c), uneval([1, 0, 0]));
+  Assert.deepEqual(v, {0: 1, 1: 0});
   Assert.equal(s, 0);
   // Should switch counts.
   h.add(1);
-  var c2 = h.snapshot().counts;
+  var v2 = h.snapshot().values;
   var s2 = h.snapshot().sum;
-  Assert.equal(uneval(c2), uneval([0, 1, 0]));
+  Assert.deepEqual(v2, {0: 0, 1: 1, 2: 0});
   Assert.equal(s2, 1);
   // Should only switch counts once.
   h.add(1);
-  var c3 = h.snapshot().counts;
+  var v3 = h.snapshot().values;
   var s3 = h.snapshot().sum;
-  Assert.equal(uneval(c3), uneval([0, 1, 0]));
+  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.equal(uneval(s.ranges), uneval([0, 1, 2]));
-  Assert.equal(uneval(s.counts), uneval([0, 0, 0]));
+  Assert.deepEqual(s.range, [1, 2]);
+  Assert.deepEqual(s.values, {});
   Assert.equal(s.sum, 0);
   h.add();
   s = h.snapshot();
-  Assert.equal(uneval(s.counts), uneval([1, 0, 0]));
+  Assert.deepEqual(s.values, {0: 1, 1: 0});
   Assert.equal(s.sum, 1);
   h.add();
   s = h.snapshot();
-  Assert.equal(uneval(s.counts), uneval([2, 0, 0]));
+  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);
   }
 
-  // Categorical histograms default to 50 linear buckets.
-  let expectedRanges = [];
-  for (let i = 0; i < 51; ++i) {
-    expectedRanges.push(i);
-  }
-
   let snapshot = h1.snapshot();
   Assert.equal(snapshot.sum, 6);
-  Assert.deepEqual(snapshot.ranges, expectedRanges);
-  Assert.deepEqual(snapshot.counts.slice(0, 4), [3, 2, 2, 0]);
+  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.ranges, expectedRanges);
-  Assert.deepEqual(snapshot.counts.slice(0, 5), [3, 2, 1, 1, 0]);
+  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);
   }
 
-  expectedRanges = [];
-  for (let i = 0; i < 71; ++i) {
-    expectedRanges.push(i);
-  }
-
   snapshot = h3.snapshot();
   Assert.equal(snapshot.sum, 3);
-  Assert.equal(snapshot.ranges.length, expectedRanges.length);
-  Assert.deepEqual(snapshot.ranges, expectedRanges);
-  Assert.deepEqual(snapshot.counts.slice(0, 4), [1, 1, 1, 0]);
+  Assert.deepEqual(snapshot.range, [1, 70]);
+  Assert.deepEqual(snapshot.values, {0: 1, 1: 1, 2: 1, 3: 0});
 });
 
 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",
@@ -348,18 +301,17 @@ add_task(async function test_getHistogra
     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.equal(s.min, 1);
-  Assert.equal(s.max, 10000);
+  Assert.deepEqual(s.range, [1, 10000]);
 });
 
 add_task(async function test_getSlowSQL() {
   var slow = Telemetry.slowSQL;
   Assert.ok(("mainThread" in slow) && ("otherThreads" in slow));
 });
 
 add_task(async function test_getWebrtc() {
@@ -370,20 +322,20 @@ add_task(async function test_getWebrtc()
 });
 
 // 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.equal(uneval(orig), uneval(h.snapshot()));
+  Assert.deepEqual(orig, h.snapshot());
   Telemetry.canRecordExtended = true;
   h.add(1);
-  Assert.notEqual(uneval(orig), uneval(h.snapshot()));
+  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;
 
@@ -485,22 +437,21 @@ add_task(async function test_keyed_histo
   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 = {
-    "min": 1,
-    "max": 2,
+    "range": [1, 2],
+    "bucket_count": 3,
     "histogram_type": 2,
     "sum": 1,
-    "ranges": [0, 1, 2],
-    "counts": [0, 1, 0],
+    "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];
@@ -516,71 +467,70 @@ add_task(async function test_keyed_boole
   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].counts = [1, 0, 0];
+  testSnapShot[key].values = {0: 1, 1: 0};
   Assert.deepEqual(h.keys().sort(), testKeys);
   Assert.deepEqual(h.snapshot(), testSnapShot);
 
   let parentHgrams = Telemetry.snapshotKeyedHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN,
                                                        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 = {
-    "min": 1,
-    "max": 2,
+    "range": [1, 2],
+    "bucket_count": 3,
     "histogram_type": 4,
     "sum": 0,
-    "ranges": [0, 1, 2],
-    "counts": [1, 0, 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].counts[0] = value;
+    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].counts[0] = 1;
+  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.snapshotKeyedHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN,
                                                        false /* clear */).parent;
@@ -610,52 +560,45 @@ add_task(async function test_keyed_categ
 
     // 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);
     }
   }
 
-  // Categorical histograms default to 50 linear buckets.
-  let expectedRanges = [];
-  for (let i = 0; i < 51; ++i) {
-    expectedRanges.push(i);
-  }
-
   // 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].ranges, expectedRanges);
-    Assert.deepEqual(snapshot[k].counts.slice(0, 4), [3, 2, 2, 0]);
+    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] = {
-    "min": 1,
-    "max": 2,
+    "range": [1, 2],
+    "bucket_count": 3,
     "histogram_type": 3,
     "sum": 1,
-    "ranges": [0, 1, 2],
-    "counts": [0, 1, 0],
+    "values": {0: 0, 1: 1, 2: 0},
   };
 
   Assert.deepEqual(h.keys().sort(), [KEY]);
   Assert.deepEqual(h.snapshot(), testSnapshot);
 
   let parentHgrams = Telemetry.snapshotKeyedHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN,
                                                        false /* clear */).parent;
   Assert.deepEqual(parentHgrams[KEYED_ID], testSnapshot);
@@ -855,19 +798,19 @@ add_task(async function test_keyed_keys(
   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.counts, [0, 1, 0],
+  Assert.deepEqual(snap.testkey.values, {0: 0, 1: 1, 2: 0},
                    "'testkey' must contain the correct value.");
-  Assert.deepEqual(snap.thirdKey.counts, [1, 0, 0],
+  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.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false).parent;
@@ -889,60 +832,60 @@ add_task(async function test_count_multi
   // 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.equal(s1.counts.reduce((acc, cur) => acc + cur), 0);
+  Assert.deepEqual({}, s1.values);
 
   h.add(valid);
   let s2 = h.snapshot();
-  Assert.equal(uneval(s2.counts), uneval([4, 0, 0]));
+  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.equal(s1.counts.reduce((acc, cur) => acc + cur), 0);
+  Assert.deepEqual({}, s1.values);
 
   h.add(valid);
   let snapshot = h.snapshot();
   Assert.equal(snapshot.sum, 6);
-  Assert.deepEqual(snapshot.counts.slice(0, 4), [3, 2, 2, 0]);
+  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.equal(s1.counts.reduce((acc, cur) => acc + cur), 0);
+  Assert.deepEqual({}, s1.values);
 
   h.add(valid);
   let s = h.snapshot();
-  Assert.deepEqual(s.counts, [2, 3, 0]);
+  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"];
@@ -950,24 +893,23 @@ add_task(async function test_linear_mult
   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.equal(s1.counts.reduce((acc, cur) => acc + cur), 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.equal(s2.counts[9], 1);
-  Assert.deepEqual(s2.counts.slice(0, 3), [1, 3, 2]);
+  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();
@@ -999,62 +941,62 @@ add_task(async function test_keyed_count
   // 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(key);
   Assert.equal(s1.sum, 0);
   // Ensure that no accumulations of 0-like values took place.
   // These accumulations won't increase the sum.
-  Assert.equal(s1.counts.reduce((acc, cur) => acc + cur), 0);
+  Assert.deepEqual({}, s1.values);
 
   h.add(key, valid);
   let s2 = h.snapshot(key);
-  Assert.equal(uneval(s2.counts), uneval([4, 0, 0]));
+  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(key);
   Assert.equal(s1.sum, 0);
-  Assert.equal(s1.counts.reduce((acc, cur) => acc + cur), 0);
+  Assert.deepEqual({}, s1.values);
 
   h.add(key, valid);
   let snapshot = h.snapshot(key);
   Assert.equal(snapshot.sum, 6);
-  Assert.deepEqual(snapshot.counts.slice(0, 4), [3, 2, 2, 0]);
+  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(key);
   Assert.equal(s1.sum, 0);
-  Assert.equal(s1.counts.reduce((acc, cur) => acc + cur), 0);
+  Assert.deepEqual({}, s1.values);
 
   h.add(key, valid);
   let s = h.snapshot(key);
-  Assert.deepEqual(s.counts, [2, 3, 0]);
+  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"];
@@ -1063,24 +1005,24 @@ add_task(async function test_keyed_linea
   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(key);
    Assert.equal(s1.sum, 0);
-   Assert.equal(s1.counts.reduce((acc, cur) => acc + cur), 0);
+   Assert.deepEqual({}, s1.values);
 
   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.equal(s2.counts[9], 3);
-  Assert.deepEqual(s2.counts.slice(0, 3), [1, 3, 0]);
+  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";
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
@@ -73,17 +73,17 @@ var checkPingsSaved = async function(pin
       allFound = false;
     }
   }
 
   return allFound;
 };
 
 function histogramValueCount(h) {
-  return h.counts.reduce((a, b) => a + b);
+  return Object.values(h.values).reduce((a, b) => a + b, 0);
 }
 
 add_task(async function test_setup() {
   // Trigger a proper telemetry init.
   do_get_profile(true);
   // Make sure we don't generate unexpected pings due to pref changes.
   await setEmptyPrefWatchlist();
   Services.prefs.setBoolPref(TelemetryUtils.Preferences.HealthPingEnabled, true);
@@ -130,17 +130,17 @@ add_task(async function test_sendPending
     fakePingId("b", i);
     const id = await TelemetryController.submitExternalPing(TEST_TYPE_B, {});
     await setPingLastModified(id, now.getTime() + (i * 1000));
   }
 
   Assert.equal(TelemetrySend.pendingPingCount, TYPE_A_COUNT + TYPE_B_COUNT,
                "Should have correct pending ping count");
 
-  Assert.deepEqual(histSuccess.snapshot().counts, [0, 0, 0],
+  Assert.deepEqual(histSuccess.snapshot().values, {},
                "Should not have recorded any sending in histograms yet.");
   Assert.equal(histSendTimeSuccess.snapshot().sum, 0,
                "Should not have recorded any sending in histograms yet.");
   Assert.equal(histSendTimeFail.snapshot().sum, 0,
                "Should not have recorded any sending in histograms yet.");
 
   // Now enable sending to the ping server.
   now = fakeNow(futureDate(now, MS_IN_A_MINUTE));
@@ -161,17 +161,17 @@ add_task(async function test_sendPending
   PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now"));
   let countByType = countPingTypes(pings);
 
   Assert.equal(countByType.get(TEST_TYPE_B), TYPE_B_COUNT,
                "Should have received the correct amount of type B pings");
   Assert.equal(countByType.get(TEST_TYPE_A), 10 - TYPE_B_COUNT,
                "Should have received the correct amount of type A pings");
 
-  Assert.deepEqual(histSuccess.snapshot().counts, [0, 10, 0],
+  Assert.deepEqual(histSuccess.snapshot().values, {0: 0, 1: 10, 2: 0},
                "Should have recorded sending success in histograms.");
   Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 10,
                "Should have recorded successful send times in histograms.");
   Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0,
                "Should not have recorded any failed sending in histograms yet.");
 
   // As we hit the ping send limit and still have pending pings, a send tick should
   // be scheduled in a minute.
@@ -267,17 +267,17 @@ add_task(async function test_backoffTime
   }
 
   timerPromise = waitForTimer();
   await pingSendTimerCallback();
   [pingSendTimerCallback, pingSendTimeout] = await timerPromise;
   Assert.equal(pingSendTimeout, MAX_BACKOFF_TIMEOUT, "Tick timeout should be capped");
   ++sendAttempts;
 
-  Assert.deepEqual(histSuccess.snapshot().counts, [sendAttempts, 0, 0],
+  Assert.deepEqual(histSuccess.snapshot().values, {0: sendAttempts, 1: 0},
                "Should have recorded sending failure in histograms.");
   Assert.equal(histSendTimeSuccess.snapshot().sum, 0,
                "Should not have recorded any sending success in histograms yet.");
   Assert.greaterOrEqual(histSendTimeFail.snapshot().sum, 0,
                         "Should have recorded send failure times in histograms.");
   Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), sendAttempts,
                "Should have recorded send failure times in histograms.");
 
@@ -304,17 +304,17 @@ add_task(async function test_backoffTime
 
   Assert.equal(countByType.get(TEST_TYPE_C), 1, "Should have received the correct amount of type C pings");
   Assert.equal(countByType.get(TEST_TYPE_D), 1, "Should have received the correct amount of type D pings");
   Assert.equal(countByType.get(TEST_TYPE_E), 1, "Should have received the correct amount of type E pings");
 
   await TelemetrySend.testWaitOnOutgoingPings();
   Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings left");
 
-  Assert.deepEqual(histSuccess.snapshot().counts, [sendAttempts, 3, 0],
+  Assert.deepEqual(histSuccess.snapshot().values, {0: sendAttempts, 1: 3, 2: 0},
                "Should have recorded sending failure in histograms.");
   Assert.greaterOrEqual(histSendTimeSuccess.snapshot().sum, 0,
                         "Should have recorded sending success in histograms.");
   Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 3,
                "Should have recorded sending success in histograms.");
   Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), sendAttempts,
                "Should have recorded send failure times in histograms.");
 
@@ -336,17 +336,17 @@ add_task(async function test_discardBigP
 
   // Submit a ping of a normal size and check that we don't count it in the histogram.
   await TelemetryController.submitExternalPing(TEST_PING_TYPE, { test: "test" });
   await TelemetrySend.testWaitOnOutgoingPings();
   await PingServer.promiseNextPing();
 
   Assert.equal(histSizeExceeded.snapshot().sum, 0, "Telemetry must report no oversized ping submitted.");
   Assert.equal(histDiscardedSize.snapshot().sum, 0, "Telemetry must report no oversized pings.");
-  Assert.deepEqual(histSuccess.snapshot().counts, [0, 1, 0], "Should have recorded sending success.");
+  Assert.deepEqual(histSuccess.snapshot().values, {0: 0, 1: 1, 2: 0}, "Should have recorded sending success.");
   Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 1, "Should have recorded send success time.");
   Assert.greaterOrEqual(histSendTimeSuccess.snapshot().sum, 0, "Should have recorded send success time.");
   Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0, "Should not have recorded send failure time.");
 
   // Submit an oversized ping and check that it gets discarded.
   TelemetryHealthPing.testReset();
   // Ensure next ping has a 2 MB gzipped payload.
   fakeGzipCompressStringForNextPing(2 * 1024 * 1024);
@@ -357,19 +357,19 @@ add_task(async function test_discardBigP
 
   Assert.equal(ping.type, TelemetryHealthPing.HEALTH_PING_TYPE, "Should have received a health ping.");
   Assert.equal(ping.payload.reason, TelemetryHealthPing.Reason.IMMEDIATE, "Health ping should have the right reason.");
   Assert.deepEqual(ping.payload[TelemetryHealthPing.FailureType.DISCARDED_FOR_SIZE],
     {[TEST_PING_TYPE]: 1}, "Should have recorded correct type of oversized ping.");
   Assert.deepEqual(ping.payload.os, TelemetryHealthPing.OsInfo, "Should have correct os info.");
 
   Assert.equal(histSizeExceeded.snapshot().sum, 1, "Telemetry must report 1 oversized ping submitted.");
-  Assert.equal(histDiscardedSize.snapshot().counts[2], 1, "Telemetry must report a 2MB, oversized, ping submitted.");
+  Assert.equal(histDiscardedSize.snapshot().values[2], 1, "Telemetry must report a 2MB, oversized, ping submitted.");
 
-  Assert.deepEqual(histSuccess.snapshot().counts, [0, 2, 0], "Should have recorded sending success.");
+  Assert.deepEqual(histSuccess.snapshot().values, {0: 0, 1: 2, 2: 0}, "Should have recorded sending success.");
   Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 2, "Should have recorded send success time.");
   Assert.greaterOrEqual(histSendTimeSuccess.snapshot().sum, 0, "Should have recorded send success time.");
   Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0, "Should not have recorded send failure time.");
 });
 
 add_task(async function test_largeButWithinLimit() {
   const TEST_PING_TYPE = "test-ping-type";
 
@@ -379,17 +379,17 @@ add_task(async function test_largeButWit
   // Next ping will have a 900KB gzip payload.
   fakeGzipCompressStringForNextPing(900 * 1024);
   const LARGE_PAYLOAD = {"data": "empty on purpose - policy takes care of size"};
 
   await TelemetryController.submitExternalPing(TEST_PING_TYPE, LARGE_PAYLOAD);
   await TelemetrySend.testWaitOnOutgoingPings();
   await PingServer.promiseNextRequest();
 
-  Assert.deepEqual(histSuccess.snapshot().counts, [0, 1, 0], "Should have sent large ping.");
+  Assert.deepEqual(histSuccess.snapshot().values, {0: 0, 1: 1, 2: 0}, "Should have sent large ping.");
 });
 
 add_task(async function test_evictedOnServerErrors() {
   const TEST_TYPE = "test-evicted";
 
   await TelemetrySend.reset();
 
   let histEvicted = Telemetry.getHistogramById("TELEMETRY_PING_EVICTED_FOR_SERVER_ERRORS");
@@ -409,17 +409,17 @@ add_task(async function test_evictedOnSe
   });
 
   // Clear the histogram and submit a ping.
   let pingId = await TelemetryController.submitExternalPing(TEST_TYPE, {});
   await TelemetrySend.testWaitOnOutgoingPings();
 
   Assert.equal(histEvicted.snapshot().sum, 1,
                "Telemetry must report a ping evicted due to server errors");
-  Assert.deepEqual(histSuccess.snapshot().counts, [0, 1, 0]);
+  Assert.deepEqual(histSuccess.snapshot().values, {0: 0, 1: 1, 2: 0});
   Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 1);
   Assert.greaterOrEqual(histSendTimeSuccess.snapshot().sum, 0);
   Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0);
 
   // The ping should not be persisted.
   await Assert.rejects(TelemetryStorage.loadPendingPing(pingId),
                        /TelemetryStorage.loadPendingPing - no ping with id/,
                        "The ping must not be persisted.");
@@ -429,17 +429,17 @@ add_task(async function test_evictedOnSe
   pingId = await TelemetryController.submitExternalPing(TEST_TYPE, {});
 
   let ping = await PingServer.promiseNextPings(1);
   Assert.equal(ping[0].id, pingId, "The correct ping must be received");
 
   // We should not have updated the error histogram.
   await TelemetrySend.testWaitOnOutgoingPings();
   Assert.equal(histEvicted.snapshot().sum, 1, "Telemetry must report only one ping evicted due to server errors");
-  Assert.deepEqual(histSuccess.snapshot().counts, [0, 2, 0]);
+  Assert.deepEqual(histSuccess.snapshot().values, {0: 0, 1: 2, 2: 0});
   Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 2);
   Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0);
 });
 
 add_task(async function test_tooLateToSend() {
   Assert.ok(true, "TEST BEGIN");
   const TEST_TYPE = "test-too-late-to-send";
 
@@ -454,17 +454,17 @@ add_task(async function test_tooLateToSe
   const id = await TelemetryController.submitExternalPing(TEST_TYPE, {});
 
   // Triggering a shutdown should persist the pings
   await TelemetrySend.shutdown();
   const pendingPings = TelemetryStorage.getPendingPingList();
   Assert.equal(pendingPings.length, 1, "Should have a pending ping in storage");
   Assert.equal(pendingPings[0].id, id, "Should have pended our test's ping");
 
-  Assert.equal(Telemetry.getHistogramById("TELEMETRY_SEND_FAILURE_TYPE").snapshot().counts[7], 1,
+  Assert.equal(Telemetry.getHistogramById("TELEMETRY_SEND_FAILURE_TYPE").snapshot().values[7], 1,
     "Should have registered the failed attempt to send");
 
   await TelemetryStorage.reset();
   Assert.equal(TelemetrySend.pendingPingCount, 0, "Should clean up after yourself");
 });
 
 // Test that the current, non-persisted pending pings are properly saved on shutdown.
 add_task(async function test_persistCurrentPingsOnShutdown() {
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js
@@ -474,31 +474,31 @@ add_task(async function test_pendingPing
                        "The oversized ping should have been pruned.");
   Assert.ok(!(await OS.File.exists(getSavePathForPingId(OVERSIZED_PING_ID))),
             "The ping should not be on the disk anymore.");
 
   // Make sure we're correctly updating the related histograms.
   h = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").snapshot();
   Assert.equal(h.sum, 1, "Telemetry must report 1 oversized ping in the pending pings directory.");
   h = Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB").snapshot();
-  Assert.equal(h.counts[2], 1, "Telemetry must report a 2MB, oversized, ping.");
+  Assert.equal(h.values[2], 1, "Telemetry must report a 2MB, oversized, ping.");
 
   // Save the ping again to check if it gets pruned when scanning the pings directory.
   await TelemetryStorage.savePendingPing(OVERSIZED_PING);
   expectedPrunedPings.push(OVERSIZED_PING_ID);
 
   // Scan the pending pings directory.
   await TelemetryController.testReset();
   await TelemetryStorage.testPendingQuotaTaskPromise();
   await checkPendingPings();
 
   // Make sure we're correctly updating the related histograms.
   h = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").snapshot();
   Assert.equal(h.sum, 2, "Telemetry must report 1 oversized ping in the pending pings directory.");
   h = Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB").snapshot();
-  Assert.equal(h.counts[2], 2, "Telemetry must report two 2MB, oversized, pings.");
+  Assert.equal(h.values[2], 2, "Telemetry must report two 2MB, oversized, pings.");
 
   Services.prefs.setBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, true);
 });
 
 add_task(async function teardown() {
   await PingServer.stop();
 });
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -325,40 +325,40 @@ function checkPayload(payload, reason, s
   const expected_flag = {
     range: [1, 2],
     bucket_count: 3,
     histogram_type: 3,
     values: {0: 1, 1: 0},
     sum: 0,
   };
   let flag = payload.histograms[TELEMETRY_TEST_FLAG];
-  Assert.equal(uneval(flag), uneval(expected_flag));
+  Assert.deepEqual(flag, expected_flag);
 
   // We should have a test count.
   const expected_count = {
     range: [1, 2],
     bucket_count: 3,
     histogram_type: 4,
     values: {0: 1, 1: 0},
     sum: 1,
   };
   let count = payload.histograms[TELEMETRY_TEST_COUNT];
-  Assert.equal(uneval(count), uneval(expected_count));
+  Assert.deepEqual(count, expected_count);
 
   // There should be one successful report from the previous telemetry ping.
   if (successfulPings > 0) {
     const expected_tc = {
       range: [1, 2],
       bucket_count: 3,
       histogram_type: 2,
       values: {0: 2, 1: successfulPings, 2: 0},
       sum: successfulPings,
     };
     let tc = payload.histograms[TELEMETRY_SUCCESS];
-    Assert.equal(uneval(tc), uneval(expected_tc));
+    Assert.deepEqual(tc, expected_tc);
   }
 
   // The ping should include data from memory reporters.  We can't check that
   // this data is correct, because we can't control the values returned by the
   // memory reporters.  But we can at least check that the data is there.
   //
   // It's important to check for the presence of reporters with a mix of units,
   // because MemoryTelemetry has separate logic for each one.  But we can't
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryStopwatch.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryStopwatch.js
@@ -11,25 +11,25 @@ const KEYED_HIST = { id: "TELEMETRY_INVA
 
 var refObj = {}, refObj2 = {};
 
 var originalCount1, originalCount2, originalCount3;
 
 function run_test() {
   let histogram = Telemetry.getHistogramById(HIST_NAME);
   let snapshot = histogram.snapshot();
-  originalCount1 = snapshot.counts.reduce((a, b) => a += b);
+  originalCount1 = Object.values(snapshot.values).reduce((a, b) => a += b, 0);
 
   histogram = Telemetry.getHistogramById(HIST_NAME2);
   snapshot = histogram.snapshot();
-  originalCount2 = snapshot.counts.reduce((a, b) => a += b);
+  originalCount2 = Object.values(snapshot.values).reduce((a, b) => a += b, 0);
 
   histogram = Telemetry.getKeyedHistogramById(KEYED_HIST.id);
   snapshot = histogram.snapshot(KEYED_HIST.key);
-  originalCount3 = snapshot.counts.reduce((a, b) => a += b);
+  originalCount3 = Object.values(snapshot.values).reduce((a, b) => a += b, 0);
 
   Assert.ok(!TelemetryStopwatch.start(3));
   Assert.ok(!TelemetryStopwatch.start({}));
   Assert.ok(!TelemetryStopwatch.start("", 3));
   Assert.ok(!TelemetryStopwatch.start("", ""));
   Assert.ok(!TelemetryStopwatch.start({}, {}));
 
   Assert.ok(TelemetryStopwatch.start("mark1"));
@@ -172,24 +172,24 @@ function run_test() {
   Assert.ok(!TelemetryStopwatch.cancelKeyed(KEYED_HIST.id, KEYED_HIST.key));
 
   finishTest();
 }
 
 function finishTest() {
   let histogram = Telemetry.getHistogramById(HIST_NAME);
   let snapshot = histogram.snapshot();
-  let newCount = snapshot.counts.reduce((a, b) => a += b);
+  let newCount = Object.values(snapshot.values).reduce((a, b) => a += b, 0);
 
   Assert.equal(newCount - originalCount1, 5, "The correct number of histograms were added for histogram 1.");
 
   histogram = Telemetry.getHistogramById(HIST_NAME2);
   snapshot = histogram.snapshot();
-  newCount = snapshot.counts.reduce((a, b) => a += b);
+  newCount = Object.values(snapshot.values).reduce((a, b) => a += b, 0);
 
   Assert.equal(newCount - originalCount2, 3, "The correct number of histograms were added for histogram 2.");
 
   histogram = Telemetry.getKeyedHistogramById(KEYED_HIST.id);
   snapshot = histogram.snapshot(KEYED_HIST.key);
-  newCount = snapshot.counts.reduce((a, b) => a += b);
+  newCount = Object.values(snapshot.values).reduce((a, b) => a += b, 0);
 
   Assert.equal(newCount - originalCount3, 2, "The correct number of histograms were added for histogram 3.");
 }
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryUtils.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryUtils.js
@@ -5,38 +5,34 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/Preferences.jsm", this);
 ChromeUtils.import("resource://gre/modules/TelemetryUtils.jsm", this);
 ChromeUtils.import("resource://gre/modules/UpdateUtils.jsm", this);
 
 add_task(async function testHistogramPacking() {
   const HISTOGRAM_SNAPSHOT = {
     sample_process: {
       HISTOGRAM_1_DATA: {
-        counts: [
-          1, 0, 0,
-        ],
-        ranges: [
-          0, 1, 2,
-        ],
-        max: 2,
-        min: 1,
+        range: [1, 2],
+        bucket_count: 3,
+        histogram_type: 4,
+        values: {
+          "0": 1,
+          "1": 0,
+        },
         sum: 1,
-        histogram_type: 4,
       },
       TELEMETRY_TEST_HISTOGRAM_2_DATA: {
-        counts: [
-          1, 0, 0,
-        ],
-        ranges: [
-          0, 1, 2,
-        ],
-        max: 2,
-        min: 1,
+        range: [1, 2],
+        bucket_count: 3,
+        histogram_type: 4,
+        values: {
+          "0": 1,
+          "1": 0,
+        },
         sum: 1,
-        histogram_type: 4,
       },
     },
   };
 
   const HISTOGRAM_1_DATA = {
     range: [1, 2],
     bucket_count: 3,
     histogram_type: 4,
@@ -78,52 +74,46 @@ add_task(async function testHistogramPac
             "Packed data must be correct.");
 });
 
 add_task(async function testKeyedHistogramPacking() {
   const KEYED_HISTOGRAM_SNAPSHOT = {
     sample_process: {
       HISTOGRAM_1_DATA: {
         someKey: {
-          counts: [
-            1, 0, 0,
-          ],
-          ranges: [
-            0, 1, 2,
-          ],
-          max: 2,
-          min: 1,
+          range: [1, 2],
+          bucket_count: 3,
+          histogram_type: 4,
+          values: {
+            "0": 1,
+            "1": 0,
+          },
           sum: 1,
-          histogram_type: 4,
         },
         otherKey: {
-          counts: [
-            1, 0, 0,
-          ],
-          ranges: [
-            0, 1, 2,
-          ],
-          max: 2,
-          min: 1,
+          range: [1, 2],
+          bucket_count: 3,
+          histogram_type: 4,
+          values: {
+            "0": 1,
+            "1": 0,
+          },
           sum: 1,
-          histogram_type: 4,
         },
       },
       TELEMETRY_TEST_HISTOGRAM_2_DATA: {
         someKey: {
-          counts: [
-            1, 0, 0,
-          ],
-          ranges: [
-            0, 1, 2,
-          ],
-          max: 2,
-          min: 1,
+          range: [1, 2],
+          bucket_count: 3,
+          histogram_type: 4,
+          values: {
+            "0": 1,
+            "1": 0,
+          },
           sum: 1,
-          histogram_type: 4,
         },
       },
     },
   };
 
   const someKey = {
     range: [1, 2],
     bucket_count: 3,