/* 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 "gmock/gmock.h" #include "gtest/gtest.h" #include "mozilla/gtest/MozAssertions.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/Telemetry.h" #include "nsIObserverService.h" #include "nsThreadUtils.h" #include "TelemetryFixture.h" #include "TelemetryTestHelpers.h" #include "streaming/GeckoViewStreamingTelemetry.h" using namespace mozilla; using namespace mozilla::Telemetry; using namespace TelemetryTestHelpers; using GeckoViewStreamingTelemetry::StreamingTelemetryDelegate; using mozilla::Telemetry::ScalarID; using ::testing::_; using ::testing::Eq; using ::testing::StrictMock; namespace { const char* kGeckoViewStreamingPref = "toolkit.telemetry.geckoview.streaming"; const char* kBatchTimeoutPref = "toolkit.telemetry.geckoview.batchDurationMS"; constexpr auto kTestHgramName = "TELEMETRY_TEST_STREAMING"_ns; constexpr auto kTestHgramName2 = "TELEMETRY_TEST_STREAMING_2"_ns; constexpr auto kTestCategoricalName = "TELEMETRY_TEST_CATEGORICAL_OPTOUT"_ns; const HistogramID kTestHgram = Telemetry::TELEMETRY_TEST_STREAMING; const HistogramID kTestHgram2 = Telemetry::TELEMETRY_TEST_STREAMING_2; class TelemetryStreamingFixture : public TelemetryTestFixture { protected: virtual void SetUp() { TelemetryTestFixture::SetUp(); Preferences::SetBool(kGeckoViewStreamingPref, true); Preferences::SetInt(kBatchTimeoutPref, 5000); } virtual void TearDown() { TelemetryTestFixture::TearDown(); Preferences::SetBool(kGeckoViewStreamingPref, false); GeckoViewStreamingTelemetry::RegisterDelegate(nullptr); } }; class MockDelegate : public StreamingTelemetryDelegate { public: MOCK_METHOD2(ReceiveHistogramSamples, void(const nsCString& aHistogramName, const nsTArray& aSamples)); MOCK_METHOD2(ReceiveCategoricalHistogramSamples, void(const nsCString& aHistogramName, const nsTArray& aSamples)); MOCK_METHOD2(ReceiveBoolScalarValue, void(const nsCString& aScalarName, bool aValue)); MOCK_METHOD2(ReceiveStringScalarValue, void(const nsCString& aScalarName, const nsCString& aValue)); MOCK_METHOD2(ReceiveUintScalarValue, void(const nsCString& aScalarName, uint32_t aValue)); }; // class MockDelegate TEST_F(TelemetryStreamingFixture, HistogramSamples) { const uint32_t kSampleOne = 401; const uint32_t kSampleTwo = 2019; CopyableTArray samplesArray; samplesArray.AppendElement(kSampleOne); samplesArray.AppendElement(kSampleTwo); auto md = MakeRefPtr(); EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName), Eq(std::move(samplesArray)))); GeckoViewStreamingTelemetry::RegisterDelegate(md); Telemetry::Accumulate(Telemetry::TELEMETRY_TEST_STREAMING, kSampleOne); Preferences::SetInt(kBatchTimeoutPref, 0); Telemetry::Accumulate(Telemetry::TELEMETRY_TEST_STREAMING, kSampleTwo); } TEST_F(TelemetryStreamingFixture, CategoricalHistogramSamples) { auto kSampleOne = Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL_OPTOUT::CommonLabel; auto kSampleTwo = Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL_OPTOUT::Label5; CopyableTArray samplesArray; samplesArray.AppendElement(static_cast(kSampleOne)); samplesArray.AppendElement(static_cast(kSampleOne)); samplesArray.AppendElement(static_cast(kSampleTwo)); auto md = MakeRefPtr(); EXPECT_CALL(*md, ReceiveCategoricalHistogramSamples( Eq(kTestCategoricalName), Eq(std::move(samplesArray)))); GeckoViewStreamingTelemetry::RegisterDelegate(md); Telemetry::AccumulateCategorical(kSampleOne); Telemetry::AccumulateCategorical(kSampleOne); Preferences::SetInt(kBatchTimeoutPref, 0); Telemetry::AccumulateCategorical(kSampleTwo); } TEST_F(TelemetryStreamingFixture, MultipleHistograms) { const uint32_t kSample1 = 400; const uint32_t kSample2 = 1 << 31; const uint32_t kSample3 = 7; CopyableTArray samplesArray1; samplesArray1.AppendElement(kSample1); samplesArray1.AppendElement(kSample2); CopyableTArray samplesArray2; samplesArray2.AppendElement(kSample3); auto md = MakeRefPtr(); EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName), Eq(std::move(samplesArray1)))); EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName2), Eq(std::move(samplesArray2)))); GeckoViewStreamingTelemetry::RegisterDelegate(md); Telemetry::Accumulate(kTestHgram, kSample1); Telemetry::Accumulate(kTestHgram2, kSample3); Preferences::SetInt(kBatchTimeoutPref, 0); Telemetry::Accumulate(kTestHgram, kSample2); } // If we can find a way to convert the expectation's arg into an stl container, // we can use gmock's own ::testing::UnorderedElementsAre() instead. auto MatchUnordered(uint32_t sample1, uint32_t sample2) { CopyableTArray samplesArray1; samplesArray1.AppendElement(sample1); samplesArray1.AppendElement(sample2); CopyableTArray samplesArray2; samplesArray2.AppendElement(sample2); samplesArray2.AppendElement(sample1); return ::testing::AnyOf(Eq(std::move(samplesArray1)), Eq(std::move(samplesArray2))); } TEST_F(TelemetryStreamingFixture, MultipleThreads) { const uint32_t kSample1 = 4; const uint32_t kSample2 = 14; auto md = MakeRefPtr(); // In this test, samples for the second test hgram are uninteresting. EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName2), _)); EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName), MatchUnordered(kSample1, kSample2))); GeckoViewStreamingTelemetry::RegisterDelegate(md); nsCOMPtr t1; nsCOMPtr t2; nsCOMPtr t3; nsCOMPtr r1 = NS_NewRunnableFunction( "accumulate 4", [&]() { Telemetry::Accumulate(kTestHgram, kSample1); }); nsCOMPtr r2 = NS_NewRunnableFunction( "accumulate 14", [&]() { Telemetry::Accumulate(kTestHgram, kSample2); }); nsresult rv = NS_NewNamedThread("t1", getter_AddRefs(t1), r1); EXPECT_NS_SUCCEEDED(rv); rv = NS_NewNamedThread("t2", getter_AddRefs(t2), r2); EXPECT_NS_SUCCEEDED(rv); // Give the threads a chance to do their work. PR_Sleep(PR_MillisecondsToInterval(1)); Preferences::SetInt(kBatchTimeoutPref, 0); Telemetry::Accumulate(kTestHgram2, kSample1); } TEST_F(TelemetryStreamingFixture, ScalarValues) { constexpr auto kBoolScalarName = "telemetry.test.boolean_kind"_ns; constexpr auto kStringScalarName = "telemetry.test.string_kind"_ns; constexpr auto kUintScalarName = "telemetry.test.unsigned_int_kind"_ns; const bool kBoolScalarValue = true; constexpr auto kStringScalarValue = "a string scalar value"_ns; const uint32_t kUintScalarValue = 42; auto md = MakeRefPtr(); EXPECT_CALL( *md, ReceiveBoolScalarValue(Eq(kBoolScalarName), Eq(kBoolScalarValue))); EXPECT_CALL(*md, ReceiveStringScalarValue(Eq(kStringScalarName), Eq(kStringScalarValue))); EXPECT_CALL( *md, ReceiveUintScalarValue(Eq(kUintScalarName), Eq(kUintScalarValue))); GeckoViewStreamingTelemetry::RegisterDelegate(md); Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, kBoolScalarValue); Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_STRING_KIND, NS_ConvertUTF8toUTF16(kStringScalarValue)); Preferences::SetInt(kBatchTimeoutPref, 0); // Trigger batch on next accumulation. Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, kUintScalarValue); } TEST_F(TelemetryStreamingFixture, ExpiredHistogram) { const HistogramID kExpiredHistogram = Telemetry::TELEMETRY_TEST_EXPIRED; const uint32_t kSample = 401; // Strict Mock fails on any method calls. auto md = MakeRefPtr>(); GeckoViewStreamingTelemetry::RegisterDelegate(md); Preferences::SetInt(kBatchTimeoutPref, 0); Telemetry::Accumulate(kExpiredHistogram, kSample); } TEST_F(TelemetryStreamingFixture, SendOnAppBackground) { constexpr auto kBoolScalarName = "telemetry.test.boolean_kind"_ns; const bool kBoolScalarValue = true; const char* kApplicationBackgroundTopic = "application-background"; auto md = MakeRefPtr(); EXPECT_CALL( *md, ReceiveBoolScalarValue(Eq(kBoolScalarName), Eq(kBoolScalarValue))); GeckoViewStreamingTelemetry::RegisterDelegate(md); Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, kBoolScalarValue); nsCOMPtr os = mozilla::services::GetObserverService(); ASSERT_TRUE(!!os) << "Observer Service unavailable?!?!"; os->NotifyObservers(nullptr, kApplicationBackgroundTopic, nullptr); } } // namespace