summaryrefslogtreecommitdiffstats
path: root/toolkit/components/telemetry/tests/gtest/TestScalars.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /toolkit/components/telemetry/tests/gtest/TestScalars.cpp
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/telemetry/tests/gtest/TestScalars.cpp')
-rw-r--r--toolkit/components/telemetry/tests/gtest/TestScalars.cpp591
1 files changed, 591 insertions, 0 deletions
diff --git a/toolkit/components/telemetry/tests/gtest/TestScalars.cpp b/toolkit/components/telemetry/tests/gtest/TestScalars.cpp
new file mode 100644
index 0000000000..4a95ebecd0
--- /dev/null
+++ b/toolkit/components/telemetry/tests/gtest/TestScalars.cpp
@@ -0,0 +1,591 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "core/TelemetryScalar.h"
+#include "gtest/gtest.h"
+#include "js/Conversions.h"
+#include "js/PropertyAndElement.h" // JS_GetProperty, JS_HasProperty
+#include "mozilla/Telemetry.h"
+#include "mozilla/TelemetryProcessEnums.h"
+#include "mozilla/Unused.h"
+#include "nsJSUtils.h" // nsAutoJSString
+#include "nsThreadUtils.h"
+#include "TelemetryFixture.h"
+#include "TelemetryTestHelpers.h"
+
+using namespace mozilla;
+using namespace TelemetryTestHelpers;
+using mozilla::Telemetry::ProcessID;
+
+#define EXPECTED_STRING "Nice, expected and creative string."
+
+// Test that we can properly write unsigned scalars using the C++ API.
+TEST_F(TelemetryTestFixture, ScalarUnsigned) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ // Make sure we don't get scalars from other tests.
+ Unused << mTelemetry->ClearScalars();
+
+ // Set the test scalar to a known value.
+ const uint32_t kInitialValue = 1172015;
+ const uint32_t kExpectedUint = 1172017;
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
+ kInitialValue);
+ Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
+ kExpectedUint - kInitialValue);
+
+ // Check the recorded value.
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+ CheckUintScalar("telemetry.test.unsigned_int_kind", cx.GetJSContext(),
+ scalarsSnapshot, kExpectedUint);
+
+ // Try to use SetMaximum.
+ const uint32_t kExpectedUintMaximum = kExpectedUint * 2;
+ Telemetry::ScalarSetMaximum(
+ Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
+ kExpectedUintMaximum);
+
+// Make sure that calls of the unsupported type don't corrupt the stored value.
+// Don't run this part in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
+ false);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
+ u"test"_ns);
+#endif
+
+ // Check the recorded value.
+ GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+ CheckUintScalar("telemetry.test.unsigned_int_kind", cx.GetJSContext(),
+ scalarsSnapshot, kExpectedUintMaximum);
+}
+
+// Test that the AutoScalarTimer records a proper uint32_t value to a
+// scalar once it goes out of scope.
+TEST_F(TelemetryTestFixture, AutoScalarTimer) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ // Make sure we don't get scalars from other tests.
+ Unused << mTelemetry->ClearScalars();
+ {
+ Telemetry::AutoScalarTimer<
+ Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND>
+ timer;
+ }
+
+ const char* kScalarName = "telemetry.test.unsigned_int_kind";
+
+ // Check that there's a recorded value that is greater than 0. Since
+ // this is a timer, we'll not check the non-deterministic value - just
+ // that it exists.
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+
+ // Validate the value of the test scalar.
+ JS::Rooted<JS::Value> value(cx.GetJSContext());
+ JS::Rooted<JSObject*> scalarObj(cx.GetJSContext(),
+ &scalarsSnapshot.toObject());
+ ASSERT_TRUE(JS_GetProperty(cx.GetJSContext(), scalarObj, kScalarName, &value))
+ << "The test scalar must be reported.";
+
+ JS_GetProperty(cx.GetJSContext(), scalarObj, kScalarName, &value);
+ ASSERT_TRUE(value.isInt32())
+ << "The scalar value must be of the correct type.";
+ ASSERT_TRUE(value.toInt32() >= 0)
+ << "The uint scalar type must contain a value >= 0.";
+}
+
+// Test that we can properly write boolean scalars using the C++ API.
+TEST_F(TelemetryTestFixture, ScalarBoolean) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ Unused << mTelemetry->ClearScalars();
+
+ // Set the test scalar to a known value.
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, true);
+
+// Make sure that calls of the unsupported type don't corrupt the stored value.
+// Don't run this part in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND,
+ static_cast<uint32_t>(12));
+ Telemetry::ScalarSetMaximum(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND,
+ 20);
+ Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, 2);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND,
+ u"test"_ns);
+#endif
+
+ // Check the recorded value.
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+ CheckBoolScalar("telemetry.test.boolean_kind", cx.GetJSContext(),
+ scalarsSnapshot, true);
+}
+
+// Test that we can properly write string scalars using the C++ API.
+TEST_F(TelemetryTestFixture, ScalarString) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ Unused << mTelemetry->ClearScalars();
+
+ // Set the test scalar to a known value.
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND,
+ NS_LITERAL_STRING_FROM_CSTRING(EXPECTED_STRING));
+
+// Make sure that calls of the unsupported type don't corrupt the stored value.
+// Don't run this part in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND,
+ static_cast<uint32_t>(12));
+ Telemetry::ScalarSetMaximum(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND,
+ 20);
+ Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND, 2);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND, true);
+#endif
+
+ // Check the recorded value.
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+ CheckStringScalar("telemetry.test.string_kind", cx.GetJSContext(),
+ scalarsSnapshot, EXPECTED_STRING);
+}
+
+// Test that we can properly write keyed unsigned scalars using the C++ API.
+TEST_F(TelemetryTestFixture, KeyedScalarUnsigned) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ Unused << mTelemetry->ClearScalars();
+
+ // Set the test scalar to a known value.
+ const char* kScalarName = "telemetry.test.keyed_unsigned_int";
+ const uint32_t kKey1Value = 1172015;
+ const uint32_t kKey2Value = 1172017;
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+ u"key1"_ns, kKey1Value);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+ u"key2"_ns, kKey1Value);
+ Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+ u"key2"_ns, 2);
+
+// Make sure that calls of the unsupported type don't corrupt the stored value.
+// Don't run this part in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+ u"key1"_ns, false);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+ u"test"_ns);
+#endif
+
+ // Check the recorded value.
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+
+ // Check the keyed scalar we're interested in.
+ CheckKeyedUintScalar(kScalarName, "key1", cx.GetJSContext(), scalarsSnapshot,
+ kKey1Value);
+ CheckKeyedUintScalar(kScalarName, "key2", cx.GetJSContext(), scalarsSnapshot,
+ kKey2Value);
+ CheckNumberOfProperties(kScalarName, cx.GetJSContext(), scalarsSnapshot, 2);
+
+ // Try to use SetMaximum.
+ const uint32_t kExpectedUintMaximum = kKey1Value * 2;
+ Telemetry::ScalarSetMaximum(
+ Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT, u"key1"_ns,
+ kExpectedUintMaximum);
+
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+ // The first key should be different and te second is expected to be the same.
+ CheckKeyedUintScalar(kScalarName, "key1", cx.GetJSContext(), scalarsSnapshot,
+ kExpectedUintMaximum);
+ CheckKeyedUintScalar(kScalarName, "key2", cx.GetJSContext(), scalarsSnapshot,
+ kKey2Value);
+ CheckNumberOfProperties(kScalarName, cx.GetJSContext(), scalarsSnapshot, 2);
+}
+
+TEST_F(TelemetryTestFixture, KeyedScalarBoolean) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ Unused << mTelemetry->ClearScalars();
+
+ // Set the test scalar to a known value.
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
+ u"key1"_ns, false);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
+ u"key2"_ns, true);
+
+// Make sure that calls of the unsupported type don't corrupt the stored value.
+// Don't run this part in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
+ u"key1"_ns, static_cast<uint32_t>(12));
+ Telemetry::ScalarSetMaximum(
+ Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND, u"key1"_ns, 20);
+ Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
+ u"key1"_ns, 2);
+#endif
+
+ // Check the recorded value.
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+
+ // Make sure that the keys contain the expected values.
+ const char* kScalarName = "telemetry.test.keyed_boolean_kind";
+ CheckKeyedBoolScalar(kScalarName, "key1", cx.GetJSContext(), scalarsSnapshot,
+ false);
+ CheckKeyedBoolScalar(kScalarName, "key2", cx.GetJSContext(), scalarsSnapshot,
+ true);
+ CheckNumberOfProperties(kScalarName, cx.GetJSContext(), scalarsSnapshot, 2);
+}
+
+TEST_F(TelemetryTestFixture, NonMainThreadAdd) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ Unused << mTelemetry->ClearScalars();
+
+ // Define the function that will be called on the testing thread.
+ nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
+ "TelemetryTestFixture_NonMainThreadAdd_Test::TestBody", []() -> void {
+ Telemetry::ScalarAdd(
+ Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, 37);
+ });
+
+ // Spawn the testing thread and run the function.
+ nsCOMPtr<nsIThread> testingThread;
+ nsresult rv =
+ NS_NewNamedThread("Test thread", getter_AddRefs(testingThread), runnable);
+ ASSERT_EQ(rv, NS_OK);
+
+ // Shutdown the thread. This also waits for the runnable to complete.
+ testingThread->Shutdown();
+
+ // Check the recorded value.
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+ CheckUintScalar("telemetry.test.unsigned_int_kind", cx.GetJSContext(),
+ scalarsSnapshot, 37);
+}
+
+TEST_F(TelemetryTestFixture, ScalarUnknownID) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ // Make sure we don't get scalars from other tests.
+ Unused << mTelemetry->ClearScalars();
+
+// Don't run this part in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ const uint32_t kTestFakeIds[] = {
+ static_cast<uint32_t>(Telemetry::ScalarID::ScalarCount),
+ static_cast<uint32_t>(Telemetry::ScalarID::ScalarCount) + 378537,
+ std::numeric_limits<uint32_t>::max()};
+
+ for (auto id : kTestFakeIds) {
+ Telemetry::ScalarID scalarId = static_cast<Telemetry::ScalarID>(id);
+ Telemetry::ScalarSet(scalarId, static_cast<uint32_t>(1));
+ Telemetry::ScalarSet(scalarId, true);
+ Telemetry::ScalarSet(scalarId, u"test"_ns);
+ Telemetry::ScalarAdd(scalarId, 1);
+ Telemetry::ScalarSetMaximum(scalarId, 1);
+
+ // Make sure that nothing was recorded in the plain scalars.
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+ ASSERT_TRUE(scalarsSnapshot.isUndefined())
+ << "No scalar must be recorded";
+
+ // Same for the keyed scalars.
+ Telemetry::ScalarSet(scalarId, u"key1"_ns, static_cast<uint32_t>(1));
+ Telemetry::ScalarSet(scalarId, u"key1"_ns, true);
+ Telemetry::ScalarAdd(scalarId, u"key1"_ns, 1);
+ Telemetry::ScalarSetMaximum(scalarId, u"key1"_ns, 1);
+
+ // Make sure that nothing was recorded in the keyed scalars.
+ JS::Rooted<JS::Value> keyedSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(true, cx.GetJSContext(), &keyedSnapshot);
+ ASSERT_TRUE(keyedSnapshot.isUndefined())
+ << "No keyed scalar must be recorded";
+ }
+#endif
+}
+
+TEST_F(TelemetryTestFixture, ScalarEventSummary) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ // Make sure we don't get scalars from other tests.
+ Unused << mTelemetry->ClearScalars();
+
+ const char* kScalarName = "telemetry.event_counts";
+
+ const char* kLongestEvent =
+ "oohwowlookthiscategoryissolong#thismethodislongtooo#"
+ "thisobjectisnoslouch";
+ TelemetryScalar::SummarizeEvent(nsCString(kLongestEvent), ProcessID::Parent,
+ false /* aDynamic */);
+
+ // Check the recorded value.
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+
+ CheckKeyedUintScalar(kScalarName, kLongestEvent, cx.GetJSContext(),
+ scalarsSnapshot, 1);
+
+// Don't run this part in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ const char* kTooLongEvent =
+ "oohwowlookthiscategoryissolong#thismethodislongtooo#"
+ "thisobjectisnoslouch2";
+ TelemetryScalar::SummarizeEvent(nsCString(kTooLongEvent), ProcessID::Parent,
+ false /* aDynamic */);
+
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+ CheckNumberOfProperties(kScalarName, cx.GetJSContext(), scalarsSnapshot, 1);
+#endif // #ifndef DEBUG
+
+ // Test we can fill the next 499 keys up to our 500 maximum
+ for (int i = 1; i < 500; i++) {
+ std::ostringstream eventName;
+ eventName << "category#method#object" << i;
+ TelemetryScalar::SummarizeEvent(nsCString(eventName.str().c_str()),
+ ProcessID::Parent, false /* aDynamic */);
+ }
+
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+ CheckNumberOfProperties(kScalarName, cx.GetJSContext(), scalarsSnapshot, 500);
+
+// Don't run this part in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ TelemetryScalar::SummarizeEvent(nsCString("whoops#too#many"),
+ ProcessID::Parent, false /* aDynamic */);
+
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+ CheckNumberOfProperties(kScalarName, cx.GetJSContext(), scalarsSnapshot, 500);
+#endif // #ifndef DEBUG
+}
+
+TEST_F(TelemetryTestFixture, ScalarEventSummary_Dynamic) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ // Make sure we don't get scalars from other tests.
+ Unused << mTelemetry->ClearScalars();
+
+ const char* kScalarName = "telemetry.dynamic_event_counts";
+ const char* kLongestEvent =
+ "oohwowlookthiscategoryissolong#thismethodislongtooo#"
+ "thisobjectisnoslouch";
+ TelemetryScalar::SummarizeEvent(nsCString(kLongestEvent), ProcessID::Parent,
+ true /* aDynamic */);
+ TelemetryScalar::SummarizeEvent(nsCString(kLongestEvent), ProcessID::Content,
+ true /* aDynamic */);
+
+ // Check the recorded value.
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot,
+ ProcessID::Dynamic);
+
+ // Recording in parent or content doesn't matter for dynamic scalars
+ // which all end up in the same place.
+ CheckKeyedUintScalar(kScalarName, kLongestEvent, cx.GetJSContext(),
+ scalarsSnapshot, 2);
+}
+
+TEST_F(TelemetryTestFixture, WrongScalarOperator) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ // Make sure we don't get scalars from other tests.
+ Unused << mTelemetry->ClearScalars();
+
+ const uint32_t expectedValue = 1172015;
+
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
+ expectedValue);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND,
+ NS_LITERAL_STRING_FROM_CSTRING(EXPECTED_STRING));
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, true);
+
+ TelemetryScalar::DeserializationStarted();
+
+ Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND, 1447);
+ Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, 1447);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
+ true);
+ TelemetryScalar::ApplyPendingOperations();
+
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+ CheckStringScalar("telemetry.test.string_kind", cx.GetJSContext(),
+ scalarsSnapshot, EXPECTED_STRING);
+ CheckBoolScalar("telemetry.test.boolean_kind", cx.GetJSContext(),
+ scalarsSnapshot, true);
+ CheckUintScalar("telemetry.test.unsigned_int_kind", cx.GetJSContext(),
+ scalarsSnapshot, expectedValue);
+}
+
+TEST_F(TelemetryTestFixture, WrongKeyedScalarOperator) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ // Make sure we don't get scalars from other tests.
+ Unused << mTelemetry->ClearScalars();
+
+ const uint32_t kExpectedUint = 1172017;
+
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+ u"key1"_ns, kExpectedUint);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
+ u"key2"_ns, true);
+
+ TelemetryScalar::DeserializationStarted();
+
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+ u"key1"_ns, false);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
+ u"key2"_ns, static_cast<uint32_t>(13));
+
+ TelemetryScalar::ApplyPendingOperations();
+
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+ CheckKeyedUintScalar("telemetry.test.keyed_unsigned_int", "key1",
+ cx.GetJSContext(), scalarsSnapshot, kExpectedUint);
+ CheckKeyedBoolScalar("telemetry.test.keyed_boolean_kind", "key2",
+ cx.GetJSContext(), scalarsSnapshot, true);
+}
+
+TEST_F(TelemetryTestFixture, TestKeyedScalarAllowedKeys) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+ // Make sure we don't get scalars from other tests.
+ Unused << mTelemetry->ClearScalars();
+
+ const uint32_t kExpectedUint = 1172017;
+
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_WITH_KEYS,
+ u"only"_ns, kExpectedUint);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_WITH_KEYS,
+ u"meant"_ns, kExpectedUint);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_WITH_KEYS,
+ u"for"_ns, kExpectedUint);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_WITH_KEYS,
+ u"testing"_ns, kExpectedUint);
+
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_WITH_KEYS,
+ u"invalid"_ns, kExpectedUint);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_WITH_KEYS,
+ u"not-valid"_ns, kExpectedUint);
+
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+ CheckKeyedUintScalar("telemetry.test.keyed_with_keys", "only",
+ cx.GetJSContext(), scalarsSnapshot, kExpectedUint);
+ CheckKeyedUintScalar("telemetry.test.keyed_with_keys", "meant",
+ cx.GetJSContext(), scalarsSnapshot, kExpectedUint);
+ CheckKeyedUintScalar("telemetry.test.keyed_with_keys", "for",
+ cx.GetJSContext(), scalarsSnapshot, kExpectedUint);
+ CheckKeyedUintScalar("telemetry.test.keyed_with_keys", "testing",
+ cx.GetJSContext(), scalarsSnapshot, kExpectedUint);
+ CheckNumberOfProperties("telemetry.test.keyed_with_keys", cx.GetJSContext(),
+ scalarsSnapshot, 4);
+
+ CheckKeyedUintScalar("telemetry.keyed_scalars_unknown_keys",
+ "telemetry.test.keyed_with_keys", cx.GetJSContext(),
+ scalarsSnapshot, 2);
+}
+
+// Test that we can properly handle too long key.
+TEST_F(TelemetryTestFixture, TooLongKey) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ // Make sure we don't get scalars from other tests.
+ Unused << mTelemetry->ClearScalars();
+
+// Don't run this part in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ const char* kScalarName = "telemetry.test.keyed_unsigned_int";
+ const uint32_t kKey1Value = 1172015;
+
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+ u"123456789012345678901234567890123456789012345678901234"
+ u"5678901234567890morethanseventy"_ns,
+ kKey1Value);
+
+ const uint32_t kDummyUint = 1172017;
+
+ // add dummy value so that object can be created from snapshot
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_WITH_KEYS,
+ u"dummy"_ns, kDummyUint);
+ // Check the recorded value
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+
+ bool foundp = true;
+ JS::Rooted<JSObject*> scalarObj(cx.GetJSContext(),
+ &scalarsSnapshot.toObject());
+ ASSERT_TRUE(
+ JS_HasProperty(cx.GetJSContext(), scalarObj, kScalarName, &foundp));
+ EXPECT_FALSE(foundp);
+#endif // #ifndef DEBUG
+}
+
+// Test that we can properly handle empty key
+TEST_F(TelemetryTestFixture, EmptyKey) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ // Make sure we don't get scalars from other tests.
+ Unused << mTelemetry->ClearScalars();
+
+// Don't run this part in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ const char* kScalarName = "telemetry.test.keyed_unsigned_int";
+ const uint32_t kKey1Value = 1172015;
+
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+ u""_ns, kKey1Value);
+
+ const uint32_t kDummyUint = 1172017;
+
+ // add dummy value so that object can be created from snapshot
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_WITH_KEYS,
+ u"dummy"_ns, kDummyUint);
+
+ // Check the recorded value
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+
+ bool foundp = true;
+ JS::Rooted<JSObject*> scalarObj(cx.GetJSContext(),
+ &scalarsSnapshot.toObject());
+ ASSERT_TRUE(
+ JS_HasProperty(cx.GetJSContext(), scalarObj, kScalarName, &foundp));
+ EXPECT_FALSE(foundp);
+#endif // #ifndef DEBUG
+}
+
+// Test that we can properly handle too many keys
+TEST_F(TelemetryTestFixture, TooManyKeys) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ // Make sure we don't get scalars from other tests.
+ Unused << mTelemetry->ClearScalars();
+
+// Don't run this part in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ const char* kScalarName = "telemetry.test.keyed_unsigned_int";
+ const uint32_t kKey1Value = 1172015;
+
+ for (int i = 0; i < 150; i++) {
+ std::u16string key = u"key";
+ char16_t n = i + '0';
+ key.push_back(n);
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+ nsString(key.c_str()), kKey1Value);
+ }
+
+ // Check the recorded value
+ JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext());
+ GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+
+ // Check 100 keys are present.
+ CheckNumberOfProperties(kScalarName, cx.GetJSContext(), scalarsSnapshot, 100);
+#endif // #ifndef DEBUG
+}