Files
tubestation/toolkit/components/telemetry/geckoview/gtest/TestGeckoView.cpp

675 lines
23 KiB
C++

/* 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 "gtest/gtest.h"
#include "mozilla/JSONWriter.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIOutputStream.h"
#include "nsITelemetry.h"
#include "nsJSUtils.h"
#include "nsNetUtil.h"
#include "nsPrintfCString.h"
#include "nsThreadUtils.h"
#include "prenv.h"
#include "mozilla/Telemetry.h"
#include "TelemetryFixture.h"
#include "TelemetryGeckoViewPersistence.h"
#include "core/TelemetryScalar.h"
#include "TelemetryTestHelpers.h"
using namespace mozilla;
using namespace TelemetryTestHelpers;
const char kSampleData[] = R"({
"scalars": {
"content": {
"telemetry.test.all_processes_uint": 37
}
},
"keyedScalars": {
"parent": {
"telemetry.test.keyed_unsigned_int": {
"testKey": 73
}
}
}
})";
const char16_t kPersistedFilename[] = u"gv_measurements.json";
const char kDataLoadedTopic[] = "internal-telemetry-geckoview-load-complete";
namespace {
/**
* Using gtest assertion macros requires the containing function to return
* a void type. For this reason, all the functions below are using that return
* type.
*/
void GetMockedDataDir(nsAString& aMockedDir) {
// Get the OS temporary directory.
nsCOMPtr<nsIFile> tmpDir;
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
ASSERT_EQ(NS_SUCCEEDED(rv), true);
// Return the mocked dir.
rv = tmpDir->GetPath(aMockedDir);
ASSERT_EQ(NS_SUCCEEDED(rv), true);
}
void MockAndroidDataDir() {
// Get the OS temporary directory.
nsAutoString mockedPath;
GetMockedDataDir(mockedPath);
// Set the environment variable to mock.
// Note: we intentionally leak it with |ToNewCString| as PR_SetEnv forces
// us to!
nsAutoCString mockedEnv(nsPrintfCString(
"MOZ_ANDROID_DATA_DIR=%s", NS_ConvertUTF16toUTF8(mockedPath).get()));
ASSERT_EQ(PR_SetEnv(ToNewCString(mockedEnv)), PR_SUCCESS);
}
void WritePersistenceFile(const nsACString& aData) {
// Write the file to the temporary directory.
nsCOMPtr<nsIFile> file;
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file));
ASSERT_EQ(NS_SUCCEEDED(rv), true);
// Append the filename and the extension.
nsAutoString fileName;
fileName.Append(kPersistedFilename);
file->Append(fileName);
nsCOMPtr<nsIOutputStream> stream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), file);
ASSERT_EQ(NS_SUCCEEDED(rv), true);
uint32_t count;
rv = stream->Write(aData.Data(), aData.Length(), &count);
// Make sure we wrote correctly.
ASSERT_EQ(NS_SUCCEEDED(rv), true);
ASSERT_EQ(count, aData.Length());
stream->Close();
}
void RemovePersistenceFile() {
nsCOMPtr<nsIFile> file;
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file));
ASSERT_EQ(NS_SUCCEEDED(rv), true);
// Append the filename and the extension.
nsAutoString fileName;
fileName.Append(kPersistedFilename);
file->Append(fileName);
bool exists = true;
rv = file->Exists(&exists);
ASSERT_EQ(NS_OK, rv) << "nsIFile::Exists cannot fail";
if (exists) {
rv = file->Remove(false);
ASSERT_EQ(NS_OK, rv) << "nsIFile::Remove cannot delete the requested file";
}
}
void CheckPersistenceFileExists(bool& aFileExists) {
nsCOMPtr<nsIFile> file;
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file));
ASSERT_EQ(NS_OK, rv)
<< "NS_GetSpecialDirectory must return a valid directory";
// Append the filename and the extension.
nsAutoString fileName;
fileName.Append(kPersistedFilename);
file->Append(fileName);
rv = file->Exists(&aFileExists);
ASSERT_EQ(NS_OK, rv) << "nsIFile::Exists must not fail";
}
/**
* A helper class to wait for the internal "data loaded"
* topic.
*/
class DataLoadedObserver final : public nsIObserver {
~DataLoadedObserver() = default;
public:
NS_DECL_ISUPPORTS
explicit DataLoadedObserver() : mDataLoaded(false) {
// The following line can fail to fetch the observer service. However,
// since we're test code, we're fine with crashing due to that.
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->AddObserver(this, kDataLoadedTopic, false);
}
void WaitForNotification() {
mozilla::SpinEventLoopUntil([&]() { return mDataLoaded; });
}
NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) override {
if (!strcmp(aTopic, kDataLoadedTopic)) {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->RemoveObserver(this, kDataLoadedTopic);
mDataLoaded = true;
}
return NS_OK;
}
private:
bool mDataLoaded;
};
NS_IMPL_ISUPPORTS(DataLoadedObserver, nsIObserver)
} // namespace
/**
* A GeckoView specific test fixture. Please note that this
* can't live in the above anonymous namespace.
*/
class TelemetryGeckoViewFixture : public TelemetryTestFixture {
protected:
virtual void SetUp() {
TelemetryTestFixture::SetUp();
MockAndroidDataDir();
}
};
namespace TelemetryGeckoViewTesting {
void TestDispatchPersist();
} // namespace TelemetryGeckoViewTesting
/**
* Test that corrupted JSON files don't crash the Telemetry core.
*/
TEST_F(TelemetryGeckoViewFixture, CorruptedPersistenceFiles) {
AutoJSContextWithGlobal cx(mCleanGlobal);
// Try to load a corrupted file.
WritePersistenceFile(NS_LITERAL_CSTRING("{"));
TelemetryGeckoViewPersistence::InitPersistence();
TelemetryGeckoViewPersistence::DeInitPersistence();
// Cleanup/remove the files.
RemovePersistenceFile();
}
/**
* Test that valid and empty JSON files don't crash the Telemetry core.
*/
TEST_F(TelemetryGeckoViewFixture, EmptyPersistenceFiles) {
AutoJSContextWithGlobal cx(mCleanGlobal);
// Try to load an empty file/corrupted file.
WritePersistenceFile(EmptyCString());
TelemetryGeckoViewPersistence::InitPersistence();
TelemetryGeckoViewPersistence::DeInitPersistence();
// Cleanup/remove the files.
RemovePersistenceFile();
}
/**
* Test that we're able to clear the persistence storage.
*/
TEST_F(TelemetryGeckoViewFixture, ClearPersistenceFiles) {
AutoJSContextWithGlobal cx(mCleanGlobal);
bool fileExists = false;
CheckPersistenceFileExists(fileExists);
ASSERT_FALSE(fileExists)
<< "No persisted measurements must exist on the disk";
WritePersistenceFile(nsDependentCString(kSampleData));
CheckPersistenceFileExists(fileExists);
ASSERT_TRUE(fileExists)
<< "We should have written the test persistence file to disk";
// Init the persistence: this will trigger the measurements to be written
// to disk off-the-main thread.
TelemetryGeckoViewPersistence::InitPersistence();
TelemetryGeckoViewPersistence::ClearPersistenceData();
TelemetryGeckoViewPersistence::DeInitPersistence();
CheckPersistenceFileExists(fileExists);
ASSERT_FALSE(fileExists)
<< "ClearPersistenceData must remove the persistence file";
}
/**
* Test that the data loaded topic gets notified correctly.
*/
TEST_F(TelemetryGeckoViewFixture, CheckDataLoadedTopic) {
AutoJSContextWithGlobal cx(mCleanGlobal);
bool fileExists = false;
CheckPersistenceFileExists(fileExists);
ASSERT_FALSE(fileExists)
<< "No persisted measurements must exist on the disk";
// Check that the data loaded topic is notified after attempting the load
// if no measurement file exists.
RefPtr<DataLoadedObserver> loadingFinished = new DataLoadedObserver();
TelemetryGeckoViewPersistence::InitPersistence();
loadingFinished->WaitForNotification();
TelemetryGeckoViewPersistence::DeInitPersistence();
// Check that the topic is triggered when the measuements file exists.
WritePersistenceFile(nsDependentCString(kSampleData));
CheckPersistenceFileExists(fileExists);
ASSERT_TRUE(fileExists)
<< "The persisted measurements must exist on the disk";
// Check that the data loaded topic is triggered when the measurement file
// exists.
loadingFinished = new DataLoadedObserver();
TelemetryGeckoViewPersistence::InitPersistence();
loadingFinished->WaitForNotification();
TelemetryGeckoViewPersistence::DeInitPersistence();
// Cleanup/remove the files.
RemovePersistenceFile();
}
/**
* Test that we can correctly persist the scalar data.
*/
TEST_F(TelemetryGeckoViewFixture, PersistScalars) {
AutoJSContextWithGlobal cx(mCleanGlobal);
Unused << mTelemetry->ClearScalars();
bool fileExists = false;
CheckPersistenceFileExists(fileExists);
ASSERT_FALSE(fileExists)
<< "No persisted measurements must exist on the disk";
RefPtr<DataLoadedObserver> loadingFinished = new DataLoadedObserver();
// Init the persistence: this will trigger the measurements to be written
// to disk off-the-main thread.
TelemetryGeckoViewPersistence::InitPersistence();
loadingFinished->WaitForNotification();
// Set some scalars: we can only test the parent process as we don't support
// other processes in gtests.
const uint32_t kExpectedUintValue = 37;
const uint32_t kExpectedKeyedUintValue = 73;
Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_ALL_PROCESSES_UINT,
kExpectedUintValue);
Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
NS_LITERAL_STRING("gv_key"), kExpectedKeyedUintValue);
// Dispatch the persisting task: we don't wait for the timer to expire
// as we need a reliable and reproducible way to kick this off. We ensure
// that the task runs by shutting down the persistence: this shuts down the
// thread which executes the task as the last action.
TelemetryGeckoViewTesting::TestDispatchPersist();
TelemetryGeckoViewPersistence::DeInitPersistence();
CheckPersistenceFileExists(fileExists);
ASSERT_TRUE(fileExists)
<< "The persisted measurements must exist on the disk";
// Clear the in-memory scalars again. They will be restored from the disk.
Unused << mTelemetry->ClearScalars();
// Load the persisted file again.
TelemetryGeckoViewPersistence::InitPersistence();
TelemetryGeckoViewPersistence::DeInitPersistence();
// Get a snapshot of the keyed and plain scalars.
JS::RootedValue scalarsSnapshot(cx.GetJSContext());
JS::RootedValue keyedScalarsSnapshot(cx.GetJSContext());
GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
GetScalarsSnapshot(true, cx.GetJSContext(), &keyedScalarsSnapshot);
// Verify that the scalars were correctly persisted and restored.
CheckUintScalar("telemetry.test.all_processes_uint", cx.GetJSContext(),
scalarsSnapshot, kExpectedUintValue);
CheckKeyedUintScalar("telemetry.test.keyed_unsigned_int", "gv_key",
cx.GetJSContext(), keyedScalarsSnapshot,
kExpectedKeyedUintValue);
// Cleanup/remove the files.
RemovePersistenceFile();
}
/**
* Test that we can correctly persist the histogram data.
*/
TEST_F(TelemetryGeckoViewFixture, PersistHistograms) {
AutoJSContextWithGlobal cx(mCleanGlobal);
// Clear the histogram data.
GetAndClearHistogram(cx.GetJSContext(), mTelemetry,
NS_LITERAL_CSTRING("TELEMETRY_TEST_MULTIPRODUCT"),
false /* is_keyed */);
GetAndClearHistogram(cx.GetJSContext(), mTelemetry,
NS_LITERAL_CSTRING("TELEMETRY_TEST_KEYED_COUNT"),
true /* is_keyed */);
bool fileExists = false;
CheckPersistenceFileExists(fileExists);
ASSERT_FALSE(fileExists)
<< "No persisted measurements must exist on the disk";
RefPtr<DataLoadedObserver> loadingFinished = new DataLoadedObserver();
// Init the persistence: this will trigger the measurements to be written
// to disk off-the-main thread.
TelemetryGeckoViewPersistence::InitPersistence();
loadingFinished->WaitForNotification();
// Set some histograms: we can only test the parent process as we don't
// support other processes in gtests.
const uint32_t kExpectedUintValue = 37;
const nsTArray<uint32_t> keyedSamples({5, 10, 15});
const uint32_t kExpectedKeyedSum = 5 + 10 + 15;
Telemetry::Accumulate(Telemetry::TELEMETRY_TEST_MULTIPRODUCT,
kExpectedUintValue);
Telemetry::Accumulate(Telemetry::TELEMETRY_TEST_KEYED_COUNT,
NS_LITERAL_CSTRING("gv_key"), keyedSamples);
// Dispatch the persisting task: we don't wait for the timer to expire
// as we need a reliable and reproducible way to kick off this. We ensure
// that the task runs by shutting down the persistence: this shuts down the
// thread which executes the task as the last action.
TelemetryGeckoViewTesting::TestDispatchPersist();
TelemetryGeckoViewPersistence::DeInitPersistence();
CheckPersistenceFileExists(fileExists);
ASSERT_TRUE(fileExists)
<< "The persisted measurements must exist on the disk";
// Clear the in-memory histograms again. They will be restored from the disk.
GetAndClearHistogram(cx.GetJSContext(), mTelemetry,
NS_LITERAL_CSTRING("TELEMETRY_TEST_MULTIPRODUCT"),
false /* is_keyed */);
GetAndClearHistogram(cx.GetJSContext(), mTelemetry,
NS_LITERAL_CSTRING("TELEMETRY_TEST_KEYED_COUNT"),
true /* is_keyed */);
// Load the persisted file again.
TelemetryGeckoViewPersistence::InitPersistence();
TelemetryGeckoViewPersistence::DeInitPersistence();
// Get a snapshot of the keyed and plain histograms.
JS::RootedValue snapshot(cx.GetJSContext());
JS::RootedValue keyedSnapshot(cx.GetJSContext());
GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_MULTIPRODUCT",
&snapshot, false /* is_keyed */);
GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_KEYED_COUNT",
&keyedSnapshot, true /* is_keyed */);
// Validate the loaded histogram data.
JS::RootedValue histogram(cx.GetJSContext());
GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_MULTIPRODUCT", snapshot,
&histogram);
// Get "sum" property from histogram
JS::RootedValue sum(cx.GetJSContext());
GetProperty(cx.GetJSContext(), "sum", histogram, &sum);
// Check that the "sum" stored in the histogram matches with |kExpectedValue|
uint32_t uSum = 0;
JS::ToUint32(cx.GetJSContext(), sum, &uSum);
ASSERT_EQ(uSum, kExpectedUintValue)
<< "The histogram is not returning the expected value";
// Validate the keyed histogram data.
GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_KEYED_COUNT", keyedSnapshot,
&histogram);
// Get "testkey" property from histogram and check that it stores the correct
// data.
JS::RootedValue expectedKeyData(cx.GetJSContext());
GetProperty(cx.GetJSContext(), "gv_key", histogram, &expectedKeyData);
ASSERT_FALSE(expectedKeyData.isUndefined())
<< "Cannot find the expected key in the keyed histogram data";
GetProperty(cx.GetJSContext(), "sum", expectedKeyData, &sum);
JS::ToUint32(cx.GetJSContext(), sum, &uSum);
ASSERT_EQ(uSum, kExpectedKeyedSum)
<< "The histogram is not returning the expected sum for 'gv_key'";
// Cleanup/remove the files.
RemovePersistenceFile();
}
/**
* Test GeckoView timer telemetry is correctly recorded.
*/
TEST_F(TelemetryGeckoViewFixture, TimerHitCountProbe) {
AutoJSContextWithGlobal cx(mCleanGlobal);
Unused << mTelemetry->ClearScalars();
// Init the persistence and wait for loading to complete.
RefPtr<DataLoadedObserver> loadingFinished = new DataLoadedObserver();
TelemetryGeckoViewPersistence::InitPersistence();
loadingFinished->WaitForNotification();
// Simulate hitting the timer twice.
TelemetryGeckoViewTesting::TestDispatchPersist();
TelemetryGeckoViewTesting::TestDispatchPersist();
TelemetryGeckoViewPersistence::DeInitPersistence();
// Get a snapshot of the keyed and plain scalars.
JS::RootedValue scalarsSnapshot(cx.GetJSContext());
GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
// Verify that the scalars were correctly persisted and restored.
CheckUintScalar("telemetry.persistence_timer_hit_count", cx.GetJSContext(),
scalarsSnapshot, 2);
// Cleanup/remove the files.
RemovePersistenceFile();
}
TEST_F(TelemetryGeckoViewFixture, EmptyPendingOperations) {
AutoJSContextWithGlobal cx(mCleanGlobal);
Unused << mTelemetry->ClearScalars();
// Force loading mode
TelemetryScalar::DeserializationStarted();
// Do nothing explicitely
// Force pending operations to be applied and end load mode.
// It should not crash and don't change any scalars.
TelemetryScalar::ApplyPendingOperations();
// Check that the snapshot is empty
JS::RootedValue scalarsSnapshot(cx.GetJSContext());
GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
ASSERT_TRUE(scalarsSnapshot.isUndefined())
<< "Scalars snapshot should not contain any data.";
}
TEST_F(TelemetryGeckoViewFixture, SimpleAppendOperation) {
AutoJSContextWithGlobal cx(mCleanGlobal);
Unused << mTelemetry->ClearScalars();
// Set an initial value, so we can test that it is not overwritten.
uint32_t initialValue = 1;
Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
initialValue);
// Force loading mode
TelemetryScalar::DeserializationStarted();
// Add to a scalar
uint32_t value = 37;
Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
value);
// Verify that this was not yet applied.
JS::RootedValue scalarsSnapshot(cx.GetJSContext());
GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
CheckUintScalar("telemetry.test.unsigned_int_kind", cx.GetJSContext(),
scalarsSnapshot, initialValue);
// Force pending operations to be applied and end load mode
TelemetryScalar::ApplyPendingOperations();
// Verify recorded operations are applied
GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
CheckUintScalar("telemetry.test.unsigned_int_kind", cx.GetJSContext(),
scalarsSnapshot, initialValue + value);
}
TEST_F(TelemetryGeckoViewFixture, ApplyPendingOperationsAfterLoad) {
AutoJSContextWithGlobal cx(mCleanGlobal);
Unused << mTelemetry->ClearScalars();
const char persistedData[] = R"({
"scalars": {
"parent": {
"telemetry.test.unsigned_int_kind": 14
}
}
})";
// Force loading mode
TelemetryScalar::DeserializationStarted();
// Add to a scalar, this should be recorded
uint32_t addValue = 10;
Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
addValue);
// Load persistence file
RefPtr<DataLoadedObserver> loadingFinished = new DataLoadedObserver();
WritePersistenceFile(nsDependentCString(persistedData));
TelemetryGeckoViewPersistence::InitPersistence();
loadingFinished->WaitForNotification();
// At this point all pending operations should have been applied.
// Increment again, now directly applied
uint32_t val = 1;
Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
val);
JS::RootedValue scalarsSnapshot(cx.GetJSContext());
GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
uint32_t expectedValue = 25;
CheckUintScalar("telemetry.test.unsigned_int_kind", cx.GetJSContext(),
scalarsSnapshot, expectedValue);
}
TEST_F(TelemetryGeckoViewFixture, MultipleAppendOperations) {
AutoJSContextWithGlobal cx(mCleanGlobal);
Unused << mTelemetry->ClearScalars();
// Force loading mode
TelemetryScalar::DeserializationStarted();
// Modify all kinds of scalars
uint32_t startValue = 35;
uint32_t expectedValue = 40;
Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
startValue);
Telemetry::ScalarSetMaximum(
Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, startValue + 2);
Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
3);
Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, true);
Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND,
NS_LITERAL_STRING("Star Wars VI"));
// Modify all kinds of keyed scalars
Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
NS_LITERAL_STRING("chewbacca"), startValue);
Telemetry::ScalarSetMaximum(
Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
NS_LITERAL_STRING("chewbacca"), startValue + 2);
Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
NS_LITERAL_STRING("chewbacca"), 3);
Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
NS_LITERAL_STRING("chewbacca"), true);
// Force pending operations to be applied and end load mode
TelemetryScalar::ApplyPendingOperations();
JS::RootedValue scalarsSnapshot(cx.GetJSContext());
JS::RootedValue keyedScalarsSnapshot(cx.GetJSContext());
GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
GetScalarsSnapshot(true, cx.GetJSContext(), &keyedScalarsSnapshot);
CheckUintScalar("telemetry.test.unsigned_int_kind", cx.GetJSContext(),
scalarsSnapshot, expectedValue);
CheckBoolScalar("telemetry.test.boolean_kind", cx.GetJSContext(),
scalarsSnapshot, true);
CheckStringScalar("telemetry.test.string_kind", cx.GetJSContext(),
scalarsSnapshot, "Star Wars VI");
CheckKeyedUintScalar("telemetry.test.keyed_unsigned_int", "chewbacca",
cx.GetJSContext(), keyedScalarsSnapshot, expectedValue);
CheckKeyedBoolScalar("telemetry.test.keyed_boolean_kind", "chewbacca",
cx.GetJSContext(), keyedScalarsSnapshot, true);
}
TEST_F(TelemetryGeckoViewFixture, PendingOperationsHighWater) {
AutoJSContextWithGlobal cx(mCleanGlobal);
const char* testProbeName = "telemetry.test.unsigned_int_kind";
const char* reachedName =
"telemetry.pending_operations_highwatermark_reached";
Unused << mTelemetry->ClearScalars();
// Setting initial values so we can test them easily
Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
0u);
Telemetry::ScalarSet(
Telemetry::ScalarID::TELEMETRY_PENDING_OPERATIONS_HIGHWATERMARK_REACHED,
0u);
// Force loading mode
TelemetryScalar::DeserializationStarted();
// Fill up the pending operations list
uint32_t expectedValue = 10000;
for (uint32_t i = 0; i < expectedValue; i++) {
Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
1);
}
// Nothing should be applied yet
JS::RootedValue scalarsSnapshot(cx.GetJSContext());
GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
CheckUintScalar(testProbeName, cx.GetJSContext(), scalarsSnapshot, 0);
CheckUintScalar(reachedName, cx.GetJSContext(), scalarsSnapshot, 0);
// Spill over the buffer to immediately apply all operations
Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND,
1);
// Now we should see all values
GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
CheckUintScalar(testProbeName, cx.GetJSContext(), scalarsSnapshot,
expectedValue + 1);
CheckUintScalar(reachedName, cx.GetJSContext(), scalarsSnapshot, 1);
}