// Copyright 2019 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/metrics/structured/structured_metrics_provider.h" #include #include #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/strings/string_number_conversions.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/threading/scoped_blocking_call.h" #include "components/metrics/structured/event.h" #include "components/metrics/structured/recorder.h" #include "components/metrics/structured/storage.pb.h" #include "components/metrics/structured/structured_events.h" #include "components/metrics/structured/structured_metrics_features.h" #include "components/metrics/structured/structured_metrics_recorder.h" #include "components/metrics/structured/test/test_event_storage.h" #include "components/metrics/structured/test/test_key_data_provider.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h" namespace metrics::structured { namespace { constexpr char kHwid[] = "hwid"; constexpr size_t kUserCount = 3; class TestRecorder : public StructuredMetricsClient::RecordingDelegate { public: TestRecorder() = default; TestRecorder(const TestRecorder& recorder) = delete; TestRecorder& operator=(const TestRecorder& recorder) = delete; ~TestRecorder() override = default; void RecordEvent(Event&& event) override { Recorder::GetInstance()->RecordEvent(std::move(event)); } bool IsReadyToRecord() const override { return true; } }; class TestSystemProfileProvider : public metrics::MetricsProvider { public: TestSystemProfileProvider() = default; TestSystemProfileProvider(const TestSystemProfileProvider& recorder) = delete; TestSystemProfileProvider& operator=( const TestSystemProfileProvider& recorder) = delete; ~TestSystemProfileProvider() override = default; void ProvideSystemProfileMetrics( metrics::SystemProfileProto* proto) override { proto->set_multi_profile_user_count(kUserCount); proto->mutable_hardware()->set_full_hardware_class(kHwid); } }; } // namespace class StructuredMetricsProviderTest : public testing::Test { protected: void SetUp() override { // Provider is being deprecated and new features are breaking the provider. GTEST_SKIP(); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); Recorder::GetInstance()->SetUiTaskRunner( task_environment_.GetMainThreadTaskRunner()); StructuredMetricsClient::Get()->SetDelegate(&recorder_); // Move the mock date forward from day 0, because KeyData assumes that day 0 // is a bug. task_environment_.AdvanceClock(base::Days(1000)); scoped_feature_list_.InitAndDisableFeature( kEnabledStructuredMetricsService); } void TearDown() override { StructuredMetricsClient::Get()->UnsetDelegate(); } base::FilePath TempDirPath() { return temp_dir_.GetPath(); } base::FilePath ProfileKeyFilePath() { return temp_dir_.GetPath() .Append(FILE_PATH_LITERAL("structured_metrics")) .Append(FILE_PATH_LITERAL("keys")); } base::FilePath DeviceKeyFilePath() { return temp_dir_.GetPath() .Append(FILE_PATH_LITERAL("structured_metrics")) .Append(FILE_PATH_LITERAL("device_keys")); } void Wait() { task_environment_.RunUntilIdle(); } // Simulates the three external events that the structure metrics system cares // about: the metrics service initializing and enabling its providers, and a // user logging in. void Init() { // Create a system profile, normally done by ChromeMetricsServiceClient. structured_metrics_recorder_ = std::make_unique( std::make_unique(DeviceKeyFilePath(), ProfileKeyFilePath()), std::make_unique()); // Create the provider, normally done by the ChromeMetricsServiceClient. provider_ = std::unique_ptr( new StructuredMetricsProvider( /*min_independent_metrics_interval=*/base::Seconds(0), structured_metrics_recorder_.get())); // Enable recording, normally done after the metrics service has checked // consent allows recording. provider_->OnRecordingEnabled(); } void OnRecordingEnabled() { provider_->OnRecordingEnabled(); } void OnProfileAdded(const base::FilePath& path) { provider_->recorder().OnProfileAdded(path); } StructuredDataProto GetSessionData() { ChromeUserMetricsExtension uma_proto; provider_->ProvideCurrentSessionData(&uma_proto); Wait(); return uma_proto.structured_data(); } StructuredDataProto GetIndependentMetrics() { ChromeUserMetricsExtension uma_proto; if (provider_->HasIndependentMetrics()) { provider_->ProvideIndependentMetrics( base::DoNothing(), base::BindOnce([](bool success) { CHECK(success); }), &uma_proto, nullptr); Wait(); return uma_proto.structured_data(); } auto p = StructuredDataProto(); return p; } void ExpectNoErrors() { histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.InternalError", 0); } bool RecorderInitialized() { return provider_->recorder().IsInitialized(); } protected: std::unique_ptr system_profile_provider_; std::unique_ptr structured_metrics_recorder_; std::unique_ptr provider_; // Feature list should be constructed before task environment. base::test::ScopedFeatureList scoped_feature_list_; base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment::MainThreadType::UI, base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED, base::test::TaskEnvironment::TimeSource::MOCK_TIME}; base::HistogramTester histogram_tester_; base::ScopedTempDir temp_dir_; private: TestRecorder recorder_; }; // Ensure that disabling independent upload of non-client_id metrics via feature // flag instead uploads them in the main UMA upload. TEST_F(StructuredMetricsProviderTest, DisableIndependentUploads) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeatureWithParameters( features::kStructuredMetrics, {{"enable_independent_metrics_upload", "false"}}); Init(); // Add a profile, normally done by the ChromeMetricsServiceClient after a // user logs in. OnProfileAdded(TempDirPath()); Wait(); OnRecordingEnabled(); events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1).Record(); events::v2::test_project_three::TestEventFour().SetTestMetricFour(1).Record(); EXPECT_EQ(GetIndependentMetrics().events_size(), 0); EXPECT_EQ(GetSessionData().events_size(), 2); ExpectNoErrors(); } TEST_F(StructuredMetricsProviderTest, NoIndependentUploadsBeforeInitialized) { Init(); // Verify the recorder is not initialized. EXPECT_FALSE(RecorderInitialized()); events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1).Record(); events::v2::test_project_three::TestEventFour().SetTestMetricFour(1).Record(); EXPECT_EQ(GetIndependentMetrics().events_size(), 0); EXPECT_EQ(GetSessionData().events_size(), 0); ExpectNoErrors(); } } // namespace metrics::structured