Bug 1453591 - Add gtest coverage for the persistence logic. r?chutten,janerik,froydnj
This changes the build system to add a new define when on
Android or when tests are enabled, MOZ_TELEMETRY_GECKOVIEW.
MozReview-Commit-ID: 5n2A8G2ZzRK
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -90,17 +90,17 @@
#include "nsProxyRelease.h"
#include "HangReports.h"
#if defined(MOZ_GECKO_PROFILER)
#include "shared-libraries.h"
#include "KeyedStackCapturer.h"
#endif // MOZ_GECKO_PROFILER
-#if defined(MOZ_WIDGET_ANDROID)
+#if defined(MOZ_TELEMETRY_GECKOVIEW)
#include "geckoview/TelemetryGeckoViewPersistence.h"
#endif
namespace {
using namespace mozilla;
using namespace mozilla::HangMonitor;
using Telemetry::Common::AutoHashtable;
@@ -1277,17 +1277,17 @@ TelemetryImpl::CreateTelemetryInstance()
nsCOMPtr<nsITelemetry> ret = sTelemetry;
sTelemetry->mCanRecordBase = useTelemetry;
sTelemetry->mCanRecordExtended = useTelemetry;
sTelemetry->InitMemoryReporter();
InitHistogramRecordingEnabled(); // requires sTelemetry to exist
-#if defined MOZ_WIDGET_ANDROID
+#if defined(MOZ_TELEMETRY_GECKOVIEW)
// We only want to add persistence for GeckoView, but both
// GV and Fennec are on Android. So just init persistence if this
// is Android but not Fennec.
if (GetCurrentProduct() == SupportedProduct::Geckoview) {
TelemetryGeckoViewPersistence::InitPersistence();
}
#endif
@@ -1303,17 +1303,17 @@ TelemetryImpl::ShutdownTelemetry()
// Lastly, de-initialise the TelemetryHistogram and TelemetryScalar global states,
// so as to release any heap storage that would otherwise be kept alive by it.
TelemetryHistogram::DeInitializeGlobalState();
TelemetryScalar::DeInitializeGlobalState();
TelemetryEvent::DeInitializeGlobalState();
TelemetryIPCAccumulator::DeInitializeGlobalState();
-#if defined(MOZ_WIDGET_ANDROID)
+#if defined(MOZ_TELEMETRY_GECKOVIEW)
if (GetCurrentProduct() == SupportedProduct::Geckoview) {
TelemetryGeckoViewPersistence::DeInitPersistence();
}
#endif
}
void
TelemetryImpl::StoreSlowSQL(const nsACString &sql, uint32_t delay,
@@ -1855,17 +1855,17 @@ TelemetryImpl::ResetCurrentProduct()
#else
return NS_ERROR_FAILURE;
#endif
}
NS_IMETHODIMP
TelemetryImpl::ClearProbes()
{
-#if defined(MOZ_WIDGET_ANDROID)
+#if defined(MOZ_TELEMETRY_GECKOVIEW)
// We only support this in GeckoView.
if (GetCurrentProduct() != SupportedProduct::Geckoview) {
MOZ_ASSERT(false, "ClearProbes is only supported on GeckoView");
return NS_ERROR_FAILURE;
}
// TODO: supporting clear for histograms will come from bug 1457127.
TelemetryScalar::ClearScalars();
--- a/toolkit/components/telemetry/TelemetryScalar.cpp
+++ b/toolkit/components/telemetry/TelemetryScalar.cpp
@@ -13,17 +13,17 @@
#include "nsDataHashtable.h"
#include "nsIXPConnect.h"
#include "nsContentUtils.h"
#include "nsThreadUtils.h"
#include "nsJSUtils.h"
#include "nsPrintfCString.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/PContent.h"
-#if defined(MOZ_WIDGET_ANDROID)
+#if defined(MOZ_TELEMETRY_GECKOVIEW)
// This is only used on GeckoView.
#include "mozilla/JSONWriter.h"
#endif
#include "mozilla/Preferences.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Unused.h"
@@ -255,17 +255,17 @@ GetVariantFromIVariant(nsIVariant* aInpu
}
default:
MOZ_ASSERT(false, "Unknown scalar kind.");
return ScalarResult::UnknownScalar;
}
return ScalarResult::Ok;
}
-#if defined(MOZ_WIDGET_ANDROID)
+#if defined(MOZ_TELEMETRY_GECKOVIEW)
/**
* Write a nsIVariant with a JSONWriter, used for GeckoView persistence.
*/
nsresult
WriteVariantToJSONWriter(uint32_t aScalarType, nsIVariant* aInputValue,
const char* aPropertyName, mozilla::JSONWriter& aWriter)
{
MOZ_ASSERT(aInputValue);
@@ -297,17 +297,17 @@ WriteVariantToJSONWriter(uint32_t aScala
}
default:
MOZ_ASSERT(false, "Unknown scalar kind.");
return NS_ERROR_FAILURE;
}
return NS_OK;
}
-#endif // MOZ_WIDGET_ANDROID
+#endif // MOZ_TELEMETRY_GECKOVIEW
// Implements the methods for ScalarInfo.
const char *
ScalarInfo::name() const
{
return &gScalarsStringTable[this->name_offset];
}
@@ -3079,17 +3079,17 @@ TelemetryScalar::AddDynamicScalarDefinit
}
{
StaticMutexAutoLock locker(gTelemetryScalarsMutex);
internal_RegisterScalars(locker, dynamicStubs);
}
}
-#if defined(MOZ_WIDGET_ANDROID)
+#if defined(MOZ_TELEMETRY_GECKOVIEW)
/**
* Write the scalar data to the provided Json object, for
* GeckoView measurement persistence. The output format is the same one used
* for snapshotting the scalars.
*
* @param {aWriter} The JSON object to write to.
* @returns NS_OK or a failure value explaining why persistence failed.
*/
@@ -3500,9 +3500,9 @@ TelemetryScalar::DeserializePersistedKey
mozilla::Get<2>(processScalars[i]),
ProcessID(iter.Key()));
}
}
}
return NS_OK;
}
-#endif // MOZ_WIDGET_ANDROID
+#endif // MOZ_TELEMETRY_GECKOVIEW
--- a/toolkit/components/telemetry/TelemetryScalar.h
+++ b/toolkit/components/telemetry/TelemetryScalar.h
@@ -9,19 +9,18 @@
#include "mozilla/TelemetryScalarEnums.h"
#include "mozilla/TelemetryProcessEnums.h"
// This module is internal to Telemetry. It encapsulates Telemetry's
// scalar accumulation and storage logic. It should only be used by
// Telemetry.cpp. These functions should not be used anywhere else.
// For the public interface to Telemetry functionality, see Telemetry.h.
-
namespace mozilla {
-#if defined(MOZ_WIDGET_ANDROID)
+#if defined(MOZ_TELEMETRY_GECKOVIEW)
// This is only used for the GeckoView persistence.
class JSONWriter;
#endif
namespace Telemetry {
struct ScalarAction;
struct KeyedScalarAction;
struct DiscardedData;
struct DynamicScalarDefinition;
@@ -88,21 +87,19 @@ void UpdateChildKeyedData(mozilla::Telem
const nsTArray<mozilla::Telemetry::KeyedScalarAction>& aScalarActions);
void RecordDiscardedData(mozilla::Telemetry::ProcessID aProcessType,
const mozilla::Telemetry::DiscardedData& aDiscardedData);
void GetDynamicScalarDefinitions(nsTArray<mozilla::Telemetry::DynamicScalarDefinition>&);
void AddDynamicScalarDefinitions(const nsTArray<mozilla::Telemetry::DynamicScalarDefinition>&);
-// These functions are only meant to be used for GeckoView persistence.
// They are responsible for updating in-memory probes with the data persisted
// on the disk and vice-versa.
-#if defined(MOZ_WIDGET_ANDROID)
+#if defined(MOZ_TELEMETRY_GECKOVIEW)
nsresult SerializeScalars(mozilla::JSONWriter &aWriter);
nsresult SerializeKeyedScalars(mozilla::JSONWriter &aWriter);
nsresult DeserializePersistedScalars(JSContext* aCx, JS::HandleValue aData);
nsresult DeserializePersistedKeyedScalars(JSContext* aCx, JS::HandleValue aData);
-#endif // MOZ_WIDGET_ANDROID
-
+#endif // MOZ_TELEMETRY_GECKOVIEW
} // namespace TelemetryScalar
#endif // TelemetryScalar_h__
--- a/toolkit/components/telemetry/geckoview/TelemetryGeckoViewPersistence.cpp
+++ b/toolkit/components/telemetry/geckoview/TelemetryGeckoViewPersistence.cpp
@@ -406,16 +406,29 @@ PersistenceThreadLoadData()
ANDROID_LOG("PersistenceThreadLoadData - Failed to load cache file at %s",
persistenceFile->HumanReadablePath().get());
return;
}
}
} // anonymous namespace
+// This namespace exposes testing only helpers to simplify writing
+// gtest cases.
+namespace TelemetryGeckoViewTesting {
+
+void
+TestDispatchPersist()
+{
+ gPersistenceThread->Dispatch(NS_NewRunnableFunction("Persist",
+ []() -> void { ::PersistenceThreadPersist(); }));
+}
+
+} // GeckoViewTesting
+
void
TelemetryGeckoViewPersistence::InitPersistence()
{
MOZ_ASSERT(NS_IsMainThread());
if (gPersistenceThread) {
ANDROID_LOG("Init must only be called once.");
return;
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/geckoview/gtest/TestGeckoView.cpp
@@ -0,0 +1,346 @@
+/* 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 "nsIOutputStream.h"
+#include "nsITelemetry.h"
+#include "nsJSUtils.h"
+#include "nsNetUtil.h"
+#include "prenv.h"
+#include "Telemetry.h"
+#include "TelemetryFixture.h"
+#include "TelemetryGeckoViewPersistence.h"
+#include "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";
+
+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";
+}
+
+void
+CheckJSONEqual(JSContext* aCx, JS::HandleValue aData, JS::HandleValue aDataOther)
+{
+ auto JSONCreator = [](const char16_t* aBuf, uint32_t aLen, void* aData) -> bool
+ {
+ nsAString* result = static_cast<nsAString*>(aData);
+ result->Append(static_cast<const char16_t*>(aBuf),
+ static_cast<uint32_t>(aLen));
+ return true;
+ };
+
+ // Unfortunately, we dont
+ nsAutoString dataAsString;
+ JS::RootedObject dataObj(aCx, &aData.toObject());
+ ASSERT_TRUE(JS::ToJSONMaybeSafely(aCx, dataObj, JSONCreator, &dataAsString))
+ << "The JS object must be correctly converted to a JSON string";
+
+ nsAutoString otherAsString;
+ JS::RootedObject otherObj(aCx, &aDataOther.toObject());
+ ASSERT_TRUE(JS::ToJSONMaybeSafely(aCx, otherObj, JSONCreator, &otherAsString))
+ << "The JS object must be correctly converted to a JSON string";
+
+ ASSERT_TRUE(dataAsString.Equals(otherAsString))
+ << "The JSON strings must match";
+}
+
+void
+TestSerializeScalars(JSONWriter& aWriter)
+{
+ // Report the same data that's in kSampleData for scalars.
+ // We only want to make sure that I/O and parsing works, as telemetry
+ // measurement updates is taken care of by xpcshell tests.
+ aWriter.StartObjectProperty("content");
+ aWriter.IntProperty("telemetry.test.all_processes_uint", 37);
+ aWriter.EndObject();
+}
+
+void
+TestSerializeKeyedScalars(JSONWriter& aWriter)
+{
+ // Report the same data that's in kSampleData for keyed scalars.
+ // We only want to make sure that I/O and parsing works, as telemetry
+ // measurement updates is taken care of by xpcshell tests.
+ aWriter.StartObjectProperty("parent");
+ aWriter.StartObjectProperty("telemetry.test.keyed_unsigned_int");
+ aWriter.IntProperty("testKey", 73);
+ aWriter.EndObject();
+ aWriter.EndObject();
+}
+
+void
+TestDeserializePersistedScalars(JSContext* aCx, JS::HandleValue aData)
+{
+ // Get a JS object out of the JSON sample.
+ JS::RootedValue sampleData(aCx);
+ NS_ConvertUTF8toUTF16 utf16Content(kSampleData);
+ ASSERT_TRUE(JS_ParseJSON(aCx, utf16Content.BeginReading(), utf16Content.Length(), &sampleData))
+ << "Failed to create a JS object from the JSON sample";
+
+ // Get sampleData["scalars"].
+ JS::RootedObject sampleObj(aCx, &sampleData.toObject());
+ JS::RootedValue scalarData(aCx);
+ ASSERT_TRUE(JS_GetProperty(aCx, sampleObj, "scalars", &scalarData) && scalarData.isObject())
+ << "Failed to get sampleData['scalars']";
+
+ CheckJSONEqual(aCx, aData, scalarData);
+}
+
+void
+TestDeserializePersistedKeyedScalars(JSContext* aCx, JS::HandleValue aData)
+{
+ // Get a JS object out of the JSON sample.
+ JS::RootedValue sampleData(aCx);
+ NS_ConvertUTF8toUTF16 utf16Content(kSampleData);
+ ASSERT_TRUE(JS_ParseJSON(aCx, utf16Content.BeginReading(), utf16Content.Length(), &sampleData))
+ << "Failed to create a JS object from the JSON sample";
+
+ // Get sampleData["keyedScalars"].
+ JS::RootedObject sampleObj(aCx, &sampleData.toObject());
+ JS::RootedValue keyedScalarData(aCx);
+ ASSERT_TRUE(JS_GetProperty(aCx, sampleObj, "keyedScalars", &keyedScalarData)
+ && keyedScalarData.isObject()) << "Failed to get sampleData['keyedScalars']";
+
+ CheckJSONEqual(aCx, aData, keyedScalarData);
+}
+
+} // Anonymous
+
+/**
+ * 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();
+ }
+};
+
+/**
+ * We can't link TelemetryScalar.cpp to these test files, so mock up
+ * the required functions to make the linker not complain.
+ */
+namespace TelemetryScalar {
+
+nsresult SerializeScalars(JSONWriter& aWriter) { TestSerializeScalars(aWriter); return NS_OK; }
+nsresult SerializeKeyedScalars(JSONWriter& aWriter) { TestSerializeKeyedScalars(aWriter); return NS_OK; }
+nsresult DeserializePersistedScalars(JSContext* aCx, JS::HandleValue aData) { TestDeserializePersistedScalars(aCx, aData); return NS_OK; }
+nsresult DeserializePersistedKeyedScalars(JSContext* aCx, JS::HandleValue aData) { TestDeserializePersistedKeyedScalars(aCx, aData); return NS_OK; }
+
+} // TelemetryScalar
+
+namespace TelemetryGeckoViewTesting {
+
+void TestDispatchPersist();
+
+} // 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 we can correctly persist the data.
+ */
+TEST_F(TelemetryGeckoViewFixture, PersistData) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+
+ bool fileExists = false;
+ CheckPersistenceFileExists(fileExists);
+ ASSERT_FALSE(fileExists) << "No persisted measurements must exist on the disk";
+
+ // Init the persistence: this will trigger the measurements to be written
+ // to disk off-the-main thread.
+ TelemetryGeckoViewPersistence::InitPersistence();
+
+ // 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";
+
+ // Load the persisted file again: this will trigger the TestLoad* functions
+ // that will validate the data.
+ TelemetryGeckoViewPersistence::InitPersistence();
+ TelemetryGeckoViewPersistence::DeInitPersistence();
+
+ // Cleanup/remove the files.
+ RemovePersistenceFile();
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/geckoview/gtest/moz.build
@@ -0,0 +1,25 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at http://mozilla.org/MPL/2.0/.
+
+Library('telemetrygeckoviewtest')
+
+LOCAL_INCLUDES += [
+ '../',
+ '../..',
+ '../../..',
+ '/toolkit/components/jsoncpp/include',
+ '/toolkit/components/telemetry/tests/gtest',
+ '/xpcom/io',
+]
+
+DEFINES['MOZ_TELEMETRY_GECKOVIEW'] = True
+
+UNIFIED_SOURCES = [
+ '../TelemetryGeckoViewPersistence.cpp',
+ 'TestGeckoView.cpp',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/toolkit/components/telemetry/moz.build
+++ b/toolkit/components/telemetry/moz.build
@@ -26,17 +26,22 @@ SPHINX_TREES['telemetry'] = 'docs'
with Files('docs/**'):
SCHEDULES.exclusive = ['docs']
if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
CXXFLAGS += ['-Wno-error=shadow']
if CONFIG['ENABLE_TESTS']:
- DIRS += ['tests/gtest']
+ # We need to use a separate directory for GeckoView gtests. See
+ # the comment below near MOZ_TELEMETRY_GECKOVIEW.
+ DIRS += [
+ 'geckoview/gtest',
+ 'tests/gtest'
+ ]
TEST_DIRS += ['tests']
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
XPIDL_SOURCES += [
'nsITelemetry.idl',
@@ -193,16 +198,22 @@ processes_data = GENERATED_FILES['Teleme
processes_data.script = 'gen_process_data.py'
processes_data.inputs = processes_files
# Add support for GeckoView: please note that building GeckoView
# implies having an Android build. The packaging step decides
# which files to include. As a consequence, we can simply only
# include the GeckoView files on all Android builds.
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+ # Introduce this define to conditionally enable Telemetry GV code in the various
+ # C++ modules. We need this trick in order to run gtest coverage on Treeherder
+ # on platforms other than Android, since gtests on Android are not supported
+ # yet (see bug 1318091).
+ DEFINES['MOZ_TELEMETRY_GECKOVIEW'] = True
+
SOURCES += [
'geckoview/TelemetryGeckoViewPersistence.cpp'
]
EXTRA_JS_MODULES += [
'geckoview/GeckoViewTelemetryController.jsm',
]
copy from toolkit/components/telemetry/tests/gtest/TelemetryFixture.h
copy to toolkit/components/telemetry/tests/gtest/TelemetryFixture.cpp
--- a/toolkit/components/telemetry/tests/gtest/TelemetryFixture.h
+++ b/toolkit/components/telemetry/tests/gtest/TelemetryFixture.cpp
@@ -1,65 +1,32 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
-#ifndef TelemetryFixture_h_
-#define TelemetryFixture_h_
-
-#include "mozilla/CycleCollectedJSContext.h"
-#include "mozilla/dom/ScriptSettings.h"
+#include "TelemetryFixture.h"
#include "mozilla/dom/SimpleGlobalObject.h"
using namespace mozilla;
-class TelemetryTestFixture: public ::testing::Test {
-protected:
- TelemetryTestFixture() : mCleanGlobal(nullptr) {}
- virtual void SetUp();
-
- JSObject* mCleanGlobal;
-
- nsCOMPtr<nsITelemetry> mTelemetry;
-};
-
void
TelemetryTestFixture::SetUp()
{
mTelemetry = do_GetService("@mozilla.org/base/telemetry;1");
mCleanGlobal =
dom::SimpleGlobalObject::Create(dom::SimpleGlobalObject::GlobalType::BindingDetail);
// The test must fail if we failed getting the global.
ASSERT_NE(mCleanGlobal, nullptr) << "SimpleGlobalObject must return a valid global object.";
}
-
-// AutoJSAPI is annotated with MOZ_STACK_CLASS and thus cannot be
-// used as a member of TelemetryTestFixture, since gtest instantiates
-// that on the heap. To work around the problem, use the following class
-// at the beginning of each Telemetry test.
-// Note: this is very similar to AutoJSContext, but it allows to pass a
-// global JS object in.
-class MOZ_RAII AutoJSContextWithGlobal {
-public:
- explicit AutoJSContextWithGlobal(JSObject* aGlobalObject);
- JSContext* GetJSContext() const;
-
-protected:
- dom::AutoJSAPI mJsAPI;
- JSContext* mCx;
-};
-
AutoJSContextWithGlobal::AutoJSContextWithGlobal(JSObject* aGlobalObject)
: mCx(nullptr)
{
// The JS API must initialize correctly.
MOZ_ALWAYS_TRUE(mJsAPI.Init(aGlobalObject));
}
JSContext* AutoJSContextWithGlobal::GetJSContext() const
{
return mJsAPI.cx();
}
-
-#endif //TelemetryFixture_h_
--- a/toolkit/components/telemetry/tests/gtest/TelemetryFixture.h
+++ b/toolkit/components/telemetry/tests/gtest/TelemetryFixture.h
@@ -1,65 +1,39 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
#ifndef TelemetryFixture_h_
#define TelemetryFixture_h_
+#include "gtest/gtest.h"
+#include "nsITelemetry.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/dom/ScriptSettings.h"
-#include "mozilla/dom/SimpleGlobalObject.h"
-
-using namespace mozilla;
class TelemetryTestFixture: public ::testing::Test {
protected:
TelemetryTestFixture() : mCleanGlobal(nullptr) {}
virtual void SetUp();
JSObject* mCleanGlobal;
nsCOMPtr<nsITelemetry> mTelemetry;
};
-void
-TelemetryTestFixture::SetUp()
-{
- mTelemetry = do_GetService("@mozilla.org/base/telemetry;1");
-
- mCleanGlobal =
- dom::SimpleGlobalObject::Create(dom::SimpleGlobalObject::GlobalType::BindingDetail);
-
- // The test must fail if we failed getting the global.
- ASSERT_NE(mCleanGlobal, nullptr) << "SimpleGlobalObject must return a valid global object.";
-}
-
-
// AutoJSAPI is annotated with MOZ_STACK_CLASS and thus cannot be
// used as a member of TelemetryTestFixture, since gtest instantiates
// that on the heap. To work around the problem, use the following class
// at the beginning of each Telemetry test.
// Note: this is very similar to AutoJSContext, but it allows to pass a
// global JS object in.
class MOZ_RAII AutoJSContextWithGlobal {
public:
explicit AutoJSContextWithGlobal(JSObject* aGlobalObject);
JSContext* GetJSContext() const;
protected:
- dom::AutoJSAPI mJsAPI;
+ mozilla::dom::AutoJSAPI mJsAPI;
JSContext* mCx;
};
-AutoJSContextWithGlobal::AutoJSContextWithGlobal(JSObject* aGlobalObject)
- : mCx(nullptr)
-{
- // The JS API must initialize correctly.
- MOZ_ALWAYS_TRUE(mJsAPI.Init(aGlobalObject));
-}
-
-JSContext* AutoJSContextWithGlobal::GetJSContext() const
-{
- return mJsAPI.cx();
-}
-
#endif //TelemetryFixture_h_
--- a/toolkit/components/telemetry/tests/gtest/moz.build
+++ b/toolkit/components/telemetry/tests/gtest/moz.build
@@ -6,15 +6,16 @@
Library('telemetrytest')
LOCAL_INCLUDES += [
'../..',
]
UNIFIED_SOURCES = [
+ 'TelemetryFixture.cpp',
'TelemetryTestHelpers.cpp',
'TestCounters.cpp',
'TestHistograms.cpp',
'TestScalars.cpp'
]
FINAL_LIBRARY = 'xul-gtest'