| // Copyright 2014 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/metrics_service.h" |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| |
| #include "base/containers/contains.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/histogram_snapshot_manager.h" |
| #include "base/metrics/metrics_hashes.h" |
| #include "base/metrics/statistics_recorder.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/bind.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_simple_task_runner.h" |
| #include "base/threading/platform_thread.h" |
| #include "build/build_config.h" |
| #include "components/metrics/clean_exit_beacon.h" |
| #include "components/metrics/client_info.h" |
| #include "components/metrics/cloned_install_detector.h" |
| #include "components/metrics/environment_recorder.h" |
| #include "components/metrics/log_decoder.h" |
| #include "components/metrics/metrics_features.h" |
| #include "components/metrics/metrics_log.h" |
| #include "components/metrics/metrics_pref_names.h" |
| #include "components/metrics/metrics_state_manager.h" |
| #include "components/metrics/metrics_upload_scheduler.h" |
| #include "components/metrics/stability_metrics_helper.h" |
| #include "components/metrics/test/test_enabled_state_provider.h" |
| #include "components/metrics/test/test_metrics_provider.h" |
| #include "components/metrics/test/test_metrics_service_client.h" |
| #include "components/metrics/unsent_log_store_metrics_impl.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "components/variations/active_field_trials.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h" |
| #include "third_party/metrics_proto/system_profile.pb.h" |
| #include "third_party/zlib/google/compression_utils.h" |
| |
| namespace metrics { |
| namespace { |
| |
| const char kTestPrefName[] = "TestPref"; |
| |
| class TestUnsentLogStore : public UnsentLogStore { |
| public: |
| explicit TestUnsentLogStore(PrefService* service) |
| : UnsentLogStore(std::make_unique<UnsentLogStoreMetricsImpl>(), |
| service, |
| kTestPrefName, |
| nullptr, |
| /*min_log_count=*/3, |
| /*min_log_bytes=*/1, |
| /*max_log_size=*/0, |
| /*signing_key=*/std::string(), |
| /*logs_event_manager=*/nullptr) {} |
| ~TestUnsentLogStore() override = default; |
| |
| TestUnsentLogStore(const TestUnsentLogStore&) = delete; |
| TestUnsentLogStore& operator=(const TestUnsentLogStore&) = delete; |
| |
| static void RegisterPrefs(PrefRegistrySimple* registry) { |
| registry->RegisterListPref(kTestPrefName); |
| } |
| }; |
| |
| void YieldUntil(base::Time when) { |
| while (base::Time::Now() <= when) |
| base::PlatformThread::YieldCurrentThread(); |
| } |
| |
| // Returns true if |id| is present in |proto|'s collection of FieldTrials. |
| bool IsFieldTrialPresent(const SystemProfileProto& proto, |
| const std::string& trial_name, |
| const std::string& group_name) { |
| const variations::ActiveGroupId id = |
| variations::MakeActiveGroupId(trial_name, group_name); |
| |
| for (const auto& trial : proto.field_trial()) { |
| if (trial.name_id() == id.name && trial.group_id() == id.group) |
| return true; |
| } |
| return false; |
| } |
| |
| class TestMetricsService : public MetricsService { |
| public: |
| TestMetricsService(MetricsStateManager* state_manager, |
| MetricsServiceClient* client, |
| PrefService* local_state) |
| : MetricsService(state_manager, client, local_state) {} |
| |
| TestMetricsService(const TestMetricsService&) = delete; |
| TestMetricsService& operator=(const TestMetricsService&) = delete; |
| |
| ~TestMetricsService() override = default; |
| |
| using MetricsService::INIT_TASK_SCHEDULED; |
| using MetricsService::RecordCurrentEnvironmentHelper; |
| using MetricsService::SENDING_LOGS; |
| using MetricsService::state; |
| |
| // MetricsService: |
| void SetPersistentSystemProfile(const std::string& serialized_proto, |
| bool complete) override { |
| persistent_system_profile_provided_ = true; |
| persistent_system_profile_complete_ = complete; |
| } |
| |
| bool persistent_system_profile_provided() const { |
| return persistent_system_profile_provided_; |
| } |
| bool persistent_system_profile_complete() const { |
| return persistent_system_profile_complete_; |
| } |
| |
| private: |
| bool persistent_system_profile_provided_ = false; |
| bool persistent_system_profile_complete_ = false; |
| }; |
| |
| class TestMetricsLog : public MetricsLog { |
| public: |
| TestMetricsLog(const std::string& client_id, |
| int session_id, |
| MetricsServiceClient* client) |
| : MetricsLog(client_id, session_id, MetricsLog::ONGOING_LOG, client) {} |
| |
| TestMetricsLog(const TestMetricsLog&) = delete; |
| TestMetricsLog& operator=(const TestMetricsLog&) = delete; |
| |
| ~TestMetricsLog() override {} |
| }; |
| |
| const char kOnDidCreateMetricsLogHistogramName[] = "Test.OnDidCreateMetricsLog"; |
| |
| class TestMetricsProviderForOnDidCreateMetricsLog : public TestMetricsProvider { |
| public: |
| TestMetricsProviderForOnDidCreateMetricsLog() = default; |
| ~TestMetricsProviderForOnDidCreateMetricsLog() override = default; |
| |
| void OnDidCreateMetricsLog() override { |
| base::UmaHistogramBoolean(kOnDidCreateMetricsLogHistogramName, true); |
| } |
| }; |
| |
| const char kProvideHistogramsHistogramName[] = "Test.ProvideHistograms"; |
| |
| class TestMetricsProviderForProvideHistograms : public TestMetricsProvider { |
| public: |
| TestMetricsProviderForProvideHistograms() = default; |
| ~TestMetricsProviderForProvideHistograms() override = default; |
| |
| bool ProvideHistograms() override { |
| base::UmaHistogramBoolean(kProvideHistogramsHistogramName, true); |
| return true; |
| } |
| |
| void ProvideCurrentSessionData( |
| ChromeUserMetricsExtension* uma_proto) override { |
| MetricsProvider::ProvideCurrentSessionData(uma_proto); |
| } |
| }; |
| |
| class TestMetricsProviderForProvideHistogramsEarlyReturn |
| : public TestMetricsProviderForProvideHistograms { |
| public: |
| TestMetricsProviderForProvideHistogramsEarlyReturn() = default; |
| ~TestMetricsProviderForProvideHistogramsEarlyReturn() override = default; |
| |
| void OnDidCreateMetricsLog() override {} |
| }; |
| |
| class TestIndependentMetricsProvider : public MetricsProvider { |
| public: |
| TestIndependentMetricsProvider() = default; |
| ~TestIndependentMetricsProvider() override = default; |
| |
| // MetricsProvider: |
| bool HasIndependentMetrics() override { |
| // Only return true the first time this is called (i.e., we only have one |
| // independent log to provide). |
| if (!has_independent_metrics_called_) { |
| has_independent_metrics_called_ = true; |
| return true; |
| } |
| return false; |
| } |
| void ProvideIndependentMetrics( |
| base::OnceCallback<void(bool)> done_callback, |
| ChromeUserMetricsExtension* uma_proto, |
| base::HistogramSnapshotManager* snapshot_manager) override { |
| provide_independent_metrics_called_ = true; |
| uma_proto->set_client_id(123); |
| std::move(done_callback).Run(true); |
| } |
| |
| bool has_independent_metrics_called() const { |
| return has_independent_metrics_called_; |
| } |
| |
| bool provide_independent_metrics_called() const { |
| return provide_independent_metrics_called_; |
| } |
| |
| private: |
| bool has_independent_metrics_called_ = false; |
| bool provide_independent_metrics_called_ = false; |
| }; |
| |
| class MetricsServiceTest : public testing::Test { |
| public: |
| MetricsServiceTest() |
| : task_runner_(new base::TestSimpleTaskRunner), |
| task_runner_current_default_handle_(task_runner_), |
| enabled_state_provider_(new TestEnabledStateProvider(false, false)) { |
| base::SetRecordActionTaskRunner(task_runner_); |
| MetricsService::RegisterPrefs(testing_local_state_.registry()); |
| } |
| |
| MetricsServiceTest(const MetricsServiceTest&) = delete; |
| MetricsServiceTest& operator=(const MetricsServiceTest&) = delete; |
| |
| ~MetricsServiceTest() override {} |
| |
| void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } |
| |
| MetricsStateManager* GetMetricsStateManager( |
| const base::FilePath& user_data_dir = base::FilePath(), |
| StartupVisibility startup_visibility = StartupVisibility::kUnknown) { |
| // Lazy-initialize the metrics_state_manager so that it correctly reads the |
| // stability state from prefs after tests have a chance to initialize it. |
| if (!metrics_state_manager_) { |
| metrics_state_manager_ = MetricsStateManager::Create( |
| GetLocalState(), enabled_state_provider_.get(), std::wstring(), |
| user_data_dir, startup_visibility); |
| metrics_state_manager_->InstantiateFieldTrialList(); |
| } |
| return metrics_state_manager_.get(); |
| } |
| |
| std::unique_ptr<TestUnsentLogStore> InitializeTestLogStoreAndGet() { |
| TestUnsentLogStore::RegisterPrefs(testing_local_state_.registry()); |
| return std::make_unique<TestUnsentLogStore>(GetLocalState()); |
| } |
| |
| PrefService* GetLocalState() { return &testing_local_state_; } |
| |
| // Sets metrics reporting as enabled for testing. |
| void EnableMetricsReporting() { SetMetricsReporting(true); } |
| |
| // Sets metrics reporting for testing. |
| void SetMetricsReporting(bool enabled) { |
| enabled_state_provider_->set_consent(enabled); |
| enabled_state_provider_->set_enabled(enabled); |
| } |
| |
| // Finds a histogram with the specified |name_hash| in |histograms|. |
| const base::HistogramBase* FindHistogram( |
| const base::StatisticsRecorder::Histograms& histograms, |
| uint64_t name_hash) { |
| for (const base::HistogramBase* histogram : histograms) { |
| if (name_hash == base::HashMetricName(histogram->histogram_name())) |
| return histogram; |
| } |
| return nullptr; |
| } |
| |
| // Checks whether |uma_log| contains any histograms that are not flagged |
| // with kUmaStabilityHistogramFlag. Stability logs should only contain such |
| // histograms. |
| void CheckForNonStabilityHistograms( |
| const ChromeUserMetricsExtension& uma_log) { |
| const int kStabilityFlags = base::HistogramBase::kUmaStabilityHistogramFlag; |
| const base::StatisticsRecorder::Histograms histograms = |
| base::StatisticsRecorder::GetHistograms(); |
| for (int i = 0; i < uma_log.histogram_event_size(); ++i) { |
| const uint64_t hash = uma_log.histogram_event(i).name_hash(); |
| |
| const base::HistogramBase* histogram = FindHistogram(histograms, hash); |
| EXPECT_TRUE(histogram) << hash; |
| |
| EXPECT_EQ(kStabilityFlags, histogram->flags() & kStabilityFlags) << hash; |
| } |
| } |
| |
| // Returns the number of samples logged to the specified histogram or 0 if |
| // the histogram was not found. |
| int GetHistogramSampleCount(const ChromeUserMetricsExtension& uma_log, |
| base::StringPiece histogram_name) { |
| const auto histogram_name_hash = base::HashMetricName(histogram_name); |
| int samples = 0; |
| for (int i = 0; i < uma_log.histogram_event_size(); ++i) { |
| const auto& histogram = uma_log.histogram_event(i); |
| if (histogram.name_hash() == histogram_name_hash) { |
| for (int j = 0; j < histogram.bucket_size(); ++j) { |
| const auto& bucket = histogram.bucket(j); |
| // Per proto comments, count field not being set means 1 sample. |
| samples += (!bucket.has_count() ? 1 : bucket.count()); |
| } |
| } |
| } |
| return samples; |
| } |
| |
| // Returns the sampled count of the |kOnDidCreateMetricsLogHistogramName| |
| // histogram in the currently staged log in |test_log_store|. |
| int GetSampleCountOfOnDidCreateLogHistogram(MetricsLogStore* test_log_store) { |
| ChromeUserMetricsExtension log; |
| EXPECT_TRUE(DecodeLogDataToProto(test_log_store->staged_log(), &log)); |
| return GetHistogramSampleCount(log, kOnDidCreateMetricsLogHistogramName); |
| } |
| |
| int GetNumberOfUserActions(MetricsLogStore* test_log_store) { |
| ChromeUserMetricsExtension log; |
| EXPECT_TRUE(DecodeLogDataToProto(test_log_store->staged_log(), &log)); |
| return log.user_action_event_size(); |
| } |
| |
| const base::FilePath user_data_dir_path() { return temp_dir_.GetPath(); } |
| |
| protected: |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner_; |
| base::SingleThreadTaskRunner::CurrentDefaultHandle |
| task_runner_current_default_handle_; |
| base::test::ScopedFeatureList feature_list_; |
| |
| private: |
| std::unique_ptr<TestEnabledStateProvider> enabled_state_provider_; |
| TestingPrefServiceSimple testing_local_state_; |
| std::unique_ptr<MetricsStateManager> metrics_state_manager_; |
| base::ScopedTempDir temp_dir_; |
| }; |
| |
| class MetricsServiceTestWithFeatures |
| : public MetricsServiceTest, |
| public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> { |
| public: |
| MetricsServiceTestWithFeatures() = default; |
| ~MetricsServiceTestWithFeatures() override = default; |
| |
| bool ShouldEmitHistogramsEarlier() { return std::get<0>(GetParam()); } |
| |
| bool ShouldEmitHistogramsForIndependentLogs() { |
| return std::get<1>(GetParam()); |
| } |
| |
| bool ShouldClearLogsOnClonedInstall() { return std::get<2>(GetParam()); } |
| |
| void SetUp() override { |
| MetricsServiceTest::SetUp(); |
| std::vector<base::test::FeatureRefAndParams> enabled_features; |
| std::vector<base::test::FeatureRef> disabled_features; |
| |
| if (ShouldEmitHistogramsEarlier()) { |
| const std::map<std::string, std::string> params = { |
| {"emit_for_independent_logs", |
| ShouldEmitHistogramsForIndependentLogs() ? "true" : "false"}}; |
| enabled_features.emplace_back(features::kEmitHistogramsEarlier, params); |
| } else { |
| disabled_features.emplace_back(features::kEmitHistogramsEarlier); |
| } |
| |
| if (ShouldClearLogsOnClonedInstall()) { |
| enabled_features.emplace_back( |
| features::kMetricsClearLogsOnClonedInstall, |
| /*params=*/std::map<std::string, std::string>()); |
| } else { |
| disabled_features.emplace_back( |
| features::kMetricsClearLogsOnClonedInstall); |
| } |
| |
| feature_list_.InitWithFeaturesAndParameters(enabled_features, |
| disabled_features); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| struct StartupVisibilityTestParams { |
| metrics::StartupVisibility startup_visibility; |
| bool expected_beacon_value; |
| }; |
| |
| class MetricsServiceTestWithStartupVisibility |
| : public MetricsServiceTest, |
| public ::testing::WithParamInterface< |
| std::tuple<StartupVisibilityTestParams, |
| std::tuple<bool, bool, bool>>> { |
| public: |
| MetricsServiceTestWithStartupVisibility() = default; |
| ~MetricsServiceTestWithStartupVisibility() override = default; |
| |
| bool ShouldEmitHistogramsEarlier() { |
| return std::get<0>(std::get<1>(GetParam())); |
| } |
| |
| bool ShouldEmitHistogramsForIndependentLogs() { |
| return std::get<1>(std::get<1>(GetParam())); |
| } |
| |
| bool ShouldClearLogsOnClonedInstall() { |
| return std::get<2>(std::get<1>(GetParam())); |
| } |
| |
| void SetUp() override { |
| MetricsServiceTest::SetUp(); |
| std::vector<base::test::FeatureRefAndParams> enabled_features; |
| std::vector<base::test::FeatureRef> disabled_features; |
| |
| if (ShouldEmitHistogramsEarlier()) { |
| const std::map<std::string, std::string> params = { |
| {"emit_for_independent_logs", |
| ShouldEmitHistogramsForIndependentLogs() ? "true" : "false"}}; |
| enabled_features.emplace_back(features::kEmitHistogramsEarlier, params); |
| } else { |
| disabled_features.emplace_back(features::kEmitHistogramsEarlier); |
| } |
| |
| if (ShouldClearLogsOnClonedInstall()) { |
| enabled_features.emplace_back( |
| features::kMetricsClearLogsOnClonedInstall, |
| /*params=*/std::map<std::string, std::string>()); |
| } else { |
| disabled_features.emplace_back( |
| features::kMetricsClearLogsOnClonedInstall); |
| } |
| |
| feature_list_.InitWithFeaturesAndParameters(enabled_features, |
| disabled_features); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| class ExperimentTestMetricsProvider : public TestMetricsProvider { |
| public: |
| explicit ExperimentTestMetricsProvider( |
| base::FieldTrial* profile_metrics_trial, |
| base::FieldTrial* session_data_trial) |
| : profile_metrics_trial_(profile_metrics_trial), |
| session_data_trial_(session_data_trial) {} |
| |
| ~ExperimentTestMetricsProvider() override = default; |
| |
| void ProvideSystemProfileMetrics( |
| SystemProfileProto* system_profile_proto) override { |
| TestMetricsProvider::ProvideSystemProfileMetrics(system_profile_proto); |
| profile_metrics_trial_->Activate(); |
| } |
| |
| void ProvideCurrentSessionData( |
| ChromeUserMetricsExtension* uma_proto) override { |
| TestMetricsProvider::ProvideCurrentSessionData(uma_proto); |
| session_data_trial_->Activate(); |
| } |
| |
| private: |
| raw_ptr<base::FieldTrial> profile_metrics_trial_; |
| raw_ptr<base::FieldTrial> session_data_trial_; |
| }; |
| |
| bool HistogramExists(base::StringPiece name) { |
| return base::StatisticsRecorder::FindHistogram(name) != nullptr; |
| } |
| |
| base::HistogramBase::Count GetHistogramDeltaTotalCount(base::StringPiece name) { |
| return base::StatisticsRecorder::FindHistogram(name) |
| ->SnapshotDelta() |
| ->TotalCount(); |
| } |
| |
| } // namespace |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| MetricsServiceTestWithFeatures, |
| testing::Combine(testing::Bool(), |
| testing::Bool(), |
| testing::Bool())); |
| |
| TEST_P(MetricsServiceTestWithFeatures, InitialStabilityLogAfterCleanShutDown) { |
| base::HistogramTester histogram_tester; |
| EnableMetricsReporting(); |
| // Write a beacon file indicating that Chrome exited cleanly. Note that the |
| // crash streak value is arbitrary. |
| const base::FilePath beacon_file_path = |
| user_data_dir_path().Append(kCleanExitBeaconFilename); |
| ASSERT_LT(0, |
| base::WriteFile(beacon_file_path, |
| CleanExitBeacon::CreateBeaconFileContentsForTesting( |
| /*exited_cleanly=*/true, /*crash_streak=*/1) |
| .data())); |
| |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(user_data_dir_path()), |
| &client, GetLocalState()); |
| |
| TestMetricsProvider* test_provider = new TestMetricsProvider(); |
| service.RegisterMetricsProvider( |
| std::unique_ptr<MetricsProvider>(test_provider)); |
| |
| service.InitializeMetricsRecordingState(); |
| |
| // No initial stability log should be generated. |
| EXPECT_FALSE(service.has_unsent_logs()); |
| |
| // Ensure that HasPreviousSessionData() is always called on providers, |
| // for consistency, even if other conditions already indicate their presence. |
| EXPECT_TRUE(test_provider->has_initial_stability_metrics_called()); |
| |
| // The test provider should not have been called upon to provide initial |
| // stability nor regular stability metrics. |
| EXPECT_FALSE(test_provider->provide_initial_stability_metrics_called()); |
| EXPECT_FALSE(test_provider->provide_stability_metrics_called()); |
| |
| // As there wasn't an unclean shutdown, no browser crash samples should have |
| // been emitted. |
| histogram_tester.ExpectBucketCount("Stability.Counts2", |
| StabilityEventType::kBrowserCrash, 0); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, InitialStabilityLogAtProviderRequest) { |
| base::HistogramTester histogram_tester; |
| EnableMetricsReporting(); |
| |
| // Save an existing system profile to prefs, to correspond to what would be |
| // saved from a previous session. |
| TestMetricsServiceClient client; |
| TestMetricsLog log("client", 1, &client); |
| DelegatingProvider delegating_provider; |
| TestMetricsService::RecordCurrentEnvironmentHelper(&log, GetLocalState(), |
| &delegating_provider); |
| |
| // Record stability build time and version from previous session, so that |
| // stability metrics (including exited cleanly flag) won't be cleared. |
| EnvironmentRecorder(GetLocalState()) |
| .SetBuildtimeAndVersion(MetricsLog::GetBuildTime(), |
| client.GetVersionString()); |
| |
| // Write a beacon file indicating that Chrome exited cleanly. Note that the |
| // crash streak value is arbitrary. |
| const base::FilePath beacon_file_path = |
| user_data_dir_path().Append(kCleanExitBeaconFilename); |
| ASSERT_LT(0, |
| base::WriteFile(beacon_file_path, |
| CleanExitBeacon::CreateBeaconFileContentsForTesting( |
| /*exited_cleanly=*/true, /*crash_streak=*/1) |
| .data())); |
| |
| TestMetricsService service(GetMetricsStateManager(user_data_dir_path()), |
| &client, GetLocalState()); |
| // Add a metrics provider that requests a stability log. |
| TestMetricsProvider* test_provider = new TestMetricsProvider(); |
| test_provider->set_has_initial_stability_metrics(true); |
| service.RegisterMetricsProvider( |
| std::unique_ptr<MetricsProvider>(test_provider)); |
| |
| service.InitializeMetricsRecordingState(); |
| |
| // The initial stability log should be generated and persisted in unsent logs. |
| MetricsLogStore* test_log_store = service.LogStoreForTest(); |
| EXPECT_TRUE(test_log_store->has_unsent_logs()); |
| EXPECT_FALSE(test_log_store->has_staged_log()); |
| |
| // Ensure that HasPreviousSessionData() is always called on providers, |
| // for consistency, even if other conditions already indicate their presence. |
| EXPECT_TRUE(test_provider->has_initial_stability_metrics_called()); |
| |
| // The test provider should have been called upon to provide initial |
| // stability and regular stability metrics. |
| EXPECT_TRUE(test_provider->provide_initial_stability_metrics_called()); |
| EXPECT_TRUE(test_provider->provide_stability_metrics_called()); |
| |
| // Stage the log and retrieve it. |
| test_log_store->StageNextLog(); |
| EXPECT_TRUE(test_log_store->has_staged_log()); |
| |
| ChromeUserMetricsExtension uma_log; |
| EXPECT_TRUE(DecodeLogDataToProto(test_log_store->staged_log(), &uma_log)); |
| |
| EXPECT_TRUE(uma_log.has_client_id()); |
| EXPECT_TRUE(uma_log.has_session_id()); |
| EXPECT_TRUE(uma_log.has_system_profile()); |
| EXPECT_EQ(0, uma_log.user_action_event_size()); |
| EXPECT_EQ(0, uma_log.omnibox_event_size()); |
| CheckForNonStabilityHistograms(uma_log); |
| |
| // As there wasn't an unclean shutdown, no browser crash samples should have |
| // been emitted. |
| histogram_tester.ExpectBucketCount("Stability.Counts2", |
| StabilityEventType::kBrowserCrash, 0); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, IndependentLogAtProviderRequest) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| // Create a a provider that will have one independent log to provide. |
| auto* test_provider = new TestIndependentMetricsProvider(); |
| service.RegisterMetricsProvider( |
| std::unique_ptr<MetricsProvider>(test_provider)); |
| |
| service.InitializeMetricsRecordingState(); |
| // Start() will create the first ongoing log. |
| service.Start(); |
| ASSERT_EQ(TestMetricsService::INIT_TASK_SCHEDULED, service.state()); |
| |
| // Verify that the independent log provider has not yet been called, and emit |
| // a histogram. This histogram should not be put into the independent log. |
| EXPECT_FALSE(test_provider->has_independent_metrics_called()); |
| EXPECT_FALSE(test_provider->provide_independent_metrics_called()); |
| const std::string test_histogram = "Test.Histogram"; |
| base::UmaHistogramBoolean(test_histogram, true); |
| |
| // Run pending tasks to finish init task and complete the first ongoing log. |
| // It should also have called the independent log provider (which should have |
| // produced a log). |
| task_runner_->RunPendingTasks(); |
| EXPECT_EQ(TestMetricsService::SENDING_LOGS, service.state()); |
| EXPECT_TRUE(test_provider->has_independent_metrics_called()); |
| EXPECT_TRUE(test_provider->provide_independent_metrics_called()); |
| |
| MetricsLogStore* test_log_store = service.LogStoreForTest(); |
| |
| // The currently staged log should be the independent log created by the |
| // independent log provider. The log should have a client id of 123. It should |
| // also not contain |test_histogram|. |
| ASSERT_TRUE(test_log_store->has_staged_log()); |
| ChromeUserMetricsExtension uma_log; |
| EXPECT_TRUE(DecodeLogDataToProto(test_log_store->staged_log(), &uma_log)); |
| EXPECT_EQ(uma_log.client_id(), 123UL); |
| EXPECT_EQ(GetHistogramSampleCount(uma_log, test_histogram), 0); |
| |
| // Discard the staged log and stage the next one. It should be the first |
| // ongoing log. |
| test_log_store->DiscardStagedLog(); |
| ASSERT_TRUE(test_log_store->has_unsent_logs()); |
| test_log_store->StageNextLog(); |
| ASSERT_TRUE(test_log_store->has_staged_log()); |
| |
| // Verify that the first ongoing log contains |test_histogram| (it should not |
| // have been put into the independent log). |
| EXPECT_TRUE(DecodeLogDataToProto(test_log_store->staged_log(), &uma_log)); |
| EXPECT_EQ(GetHistogramSampleCount(uma_log, test_histogram), 1); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, OnDidCreateMetricsLogAtShutdown) { |
| base::HistogramTester histogram_tester; |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| // Create a provider that will log to |kOnDidCreateMetricsLogHistogramName| |
| // in OnDidCreateMetricsLog(). |
| auto* test_provider = new TestMetricsProviderForOnDidCreateMetricsLog(); |
| service.RegisterMetricsProvider( |
| std::unique_ptr<MetricsProvider>(test_provider)); |
| |
| service.InitializeMetricsRecordingState(); |
| // Start() will create the first ongoing log. |
| service.Start(); |
| |
| // OnDidCreateMetricsLog() is called once when the first ongoing log is |
| // created. |
| histogram_tester.ExpectBucketCount(kOnDidCreateMetricsLogHistogramName, true, |
| 1); |
| service.Stop(); |
| |
| // If the feature kEmitHistogramsEarlier is enabled and parameter |
| // kEmitHistogramsForIndependentLogs is set to true, OnDidCreateMetricsLog() |
| // will be called during shutdown to emit histograms. |
| histogram_tester.ExpectBucketCount( |
| kOnDidCreateMetricsLogHistogramName, true, |
| ShouldEmitHistogramsEarlier() && ShouldEmitHistogramsForIndependentLogs() |
| ? 2 |
| : 1); |
| |
| // Clean up histograms. |
| base::StatisticsRecorder::ForgetHistogramForTesting( |
| kOnDidCreateMetricsLogHistogramName); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, ProvideHistograms) { |
| base::HistogramTester histogram_tester; |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| // Create a provider that will log to |kProvideHistogramsHistogramName| |
| // in ProvideHistograms(). |
| auto* test_provider = new TestMetricsProviderForProvideHistograms(); |
| service.RegisterMetricsProvider( |
| std::unique_ptr<MetricsProvider>(test_provider)); |
| |
| service.InitializeMetricsRecordingState(); |
| // Start() will create the first ongoing log. |
| service.Start(); |
| |
| // If the feature kEmitHistogramsEarlier is enabled, ProvideHistograms() is |
| // called in OnDidCreateMetricsLog(). |
| histogram_tester.ExpectBucketCount(kProvideHistogramsHistogramName, true, |
| ShouldEmitHistogramsEarlier() ? 1 : 0); |
| |
| service.StageCurrentLogForTest(); |
| // Make sure if kEmitHistogramsEarlier is not set, ProvideHistograms() is |
| // called in ProvideCurrentSessionData(). |
| histogram_tester.ExpectBucketCount(kProvideHistogramsHistogramName, true, |
| ShouldEmitHistogramsEarlier() ? 2 : 1); |
| |
| service.Stop(); |
| |
| // Clean up histograms. |
| base::StatisticsRecorder::ForgetHistogramForTesting( |
| kProvideHistogramsHistogramName); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, ProvideHistogramsEarlyReturn) { |
| base::HistogramTester histogram_tester; |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| // Create a provider that will log to |kOnDidCreateMetricsLogHistogramName| |
| // in OnDidCreateMetricsLog(). |
| auto* test_provider = |
| new TestMetricsProviderForProvideHistogramsEarlyReturn(); |
| service.RegisterMetricsProvider( |
| std::unique_ptr<MetricsProvider>(test_provider)); |
| |
| service.InitializeMetricsRecordingState(); |
| // Start() will create the first ongoing log. |
| service.Start(); |
| |
| // Make sure no histogram is emitted when having an early return. |
| histogram_tester.ExpectBucketCount(kProvideHistogramsHistogramName, true, 0); |
| |
| service.StageCurrentLogForTest(); |
| // ProvideHistograms() should be called in ProvideCurrentSessionData() if |
| // histograms haven't been emitted. |
| histogram_tester.ExpectBucketCount(kProvideHistogramsHistogramName, true, 1); |
| |
| // Try another log to make sure emission status is reset between logs. |
| service.LogStoreForTest()->DiscardStagedLog(); |
| service.StageCurrentLogForTest(); |
| histogram_tester.ExpectBucketCount(kProvideHistogramsHistogramName, true, 2); |
| |
| service.Stop(); |
| |
| // Clean up histograms. |
| base::StatisticsRecorder::ForgetHistogramForTesting( |
| kProvideHistogramsHistogramName); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| MetricsServiceTestWithStartupVisibility, |
| ::testing::Combine( |
| ::testing::Values( |
| StartupVisibilityTestParams{ |
| .startup_visibility = StartupVisibility::kUnknown, |
| .expected_beacon_value = true}, |
| StartupVisibilityTestParams{ |
| .startup_visibility = StartupVisibility::kBackground, |
| .expected_beacon_value = true}, |
| StartupVisibilityTestParams{ |
| .startup_visibility = StartupVisibility::kForeground, |
| .expected_beacon_value = false}), |
| ::testing::Combine(::testing::Bool(), |
| ::testing::Bool(), |
| ::testing::Bool()))); |
| |
| TEST_P(MetricsServiceTestWithStartupVisibility, InitialStabilityLogAfterCrash) { |
| base::HistogramTester histogram_tester; |
| PrefService* local_state = GetLocalState(); |
| EnableMetricsReporting(); |
| |
| // Write a beacon file indicating that Chrome exited uncleanly. Note that the |
| // crash streak value is arbitrary. |
| const base::FilePath beacon_file_path = |
| user_data_dir_path().Append(kCleanExitBeaconFilename); |
| ASSERT_LT(0, |
| base::WriteFile(beacon_file_path, |
| CleanExitBeacon::CreateBeaconFileContentsForTesting( |
| /*exited_cleanly=*/false, /*crash_streak=*/1) |
| .data())); |
| |
| // Set up prefs to simulate restarting after a crash. |
| |
| // Save an existing system profile to prefs, to correspond to what would be |
| // saved from a previous session. |
| TestMetricsServiceClient client; |
| const std::string kCrashedVersion = "4.0.321.0-64-devel"; |
| client.set_version_string(kCrashedVersion); |
| TestMetricsLog log("client", 1, &client); |
| DelegatingProvider delegating_provider; |
| TestMetricsService::RecordCurrentEnvironmentHelper(&log, local_state, |
| &delegating_provider); |
| |
| // Record stability build time and version from previous session, so that |
| // stability metrics (including exited cleanly flag) won't be cleared. |
| EnvironmentRecorder(local_state) |
| .SetBuildtimeAndVersion(MetricsLog::GetBuildTime(), |
| client.GetVersionString()); |
| |
| const std::string kCurrentVersion = "5.0.322.0-64-devel"; |
| client.set_version_string(kCurrentVersion); |
| |
| StartupVisibilityTestParams params = std::get<0>(GetParam()); |
| TestMetricsService service( |
| GetMetricsStateManager(user_data_dir_path(), params.startup_visibility), |
| &client, local_state); |
| // Add a provider. |
| TestMetricsProvider* test_provider = new TestMetricsProvider(); |
| service.RegisterMetricsProvider( |
| std::unique_ptr<MetricsProvider>(test_provider)); |
| service.InitializeMetricsRecordingState(); |
| |
| // Verify that Chrome is (or is not) watching for crashes by checking the |
| // beacon value. |
| std::string beacon_file_contents; |
| ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents)); |
| std::string partial_expected_contents; |
| #if BUILDFLAG(IS_ANDROID) |
| // Whether Chrome is watching for crashes after |
| // InitializeMetricsRecordingState() depends on the type of Android Chrome |
| // session. See the comments in MetricsService::InitializeMetricsState() for |
| // more details. |
| const std::string beacon_value = |
| params.expected_beacon_value ? "true" : "false"; |
| partial_expected_contents = "exited_cleanly\":" + beacon_value; |
| #else |
| partial_expected_contents = "exited_cleanly\":false"; |
| #endif // BUILDFLAG(IS_ANDROID) |
| EXPECT_TRUE(base::Contains(beacon_file_contents, partial_expected_contents)); |
| |
| // The initial stability log should be generated and persisted in unsent logs. |
| MetricsLogStore* test_log_store = service.LogStoreForTest(); |
| EXPECT_TRUE(test_log_store->has_unsent_logs()); |
| EXPECT_FALSE(test_log_store->has_staged_log()); |
| |
| // Ensure that HasPreviousSessionData() is always called on providers, |
| // for consistency, even if other conditions already indicate their presence. |
| EXPECT_TRUE(test_provider->has_initial_stability_metrics_called()); |
| |
| // The test provider should have been called upon to provide initial |
| // stability and regular stability metrics. |
| EXPECT_TRUE(test_provider->provide_initial_stability_metrics_called()); |
| EXPECT_TRUE(test_provider->provide_stability_metrics_called()); |
| |
| // The test provider should have been called when the initial stability log |
| // was closed. |
| EXPECT_TRUE(test_provider->record_initial_histogram_snapshots_called()); |
| |
| // Stage the log and retrieve it. |
| test_log_store->StageNextLog(); |
| EXPECT_TRUE(test_log_store->has_staged_log()); |
| |
| ChromeUserMetricsExtension uma_log; |
| EXPECT_TRUE(DecodeLogDataToProto(test_log_store->staged_log(), &uma_log)); |
| |
| EXPECT_TRUE(uma_log.has_client_id()); |
| EXPECT_TRUE(uma_log.has_session_id()); |
| EXPECT_TRUE(uma_log.has_system_profile()); |
| EXPECT_EQ(0, uma_log.user_action_event_size()); |
| EXPECT_EQ(0, uma_log.omnibox_event_size()); |
| CheckForNonStabilityHistograms(uma_log); |
| |
| // Verify that the histograms emitted by the test provider made it into the |
| // log. |
| EXPECT_EQ(GetHistogramSampleCount(uma_log, "TestMetricsProvider.Initial"), 1); |
| EXPECT_EQ(GetHistogramSampleCount(uma_log, "TestMetricsProvider.Regular"), 1); |
| |
| EXPECT_EQ(kCrashedVersion, uma_log.system_profile().app_version()); |
| EXPECT_EQ(kCurrentVersion, |
| uma_log.system_profile().log_written_by_app_version()); |
| |
| histogram_tester.ExpectBucketCount("Stability.Counts2", |
| StabilityEventType::kBrowserCrash, 1); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, |
| InitialLogsHaveOnDidCreateMetricsLogHistograms) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| // Create a provider that will log to |kOnDidCreateMetricsLogHistogramName| |
| // in OnDidCreateMetricsLog() |
| auto* test_provider = new TestMetricsProviderForOnDidCreateMetricsLog(); |
| service.RegisterMetricsProvider( |
| std::unique_ptr<MetricsProvider>(test_provider)); |
| |
| service.InitializeMetricsRecordingState(); |
| // Start() will create the first ongoing log. |
| service.Start(); |
| ASSERT_EQ(TestMetricsService::INIT_TASK_SCHEDULED, service.state()); |
| |
| // Run pending tasks to finish init task and complete the first ongoing log. |
| // Also verify that the test provider was called when closing the log. |
| task_runner_->RunPendingTasks(); |
| ASSERT_EQ(TestMetricsService::SENDING_LOGS, service.state()); |
| EXPECT_TRUE(test_provider->record_histogram_snapshots_called()); |
| |
| MetricsLogStore* test_log_store = service.LogStoreForTest(); |
| |
| // Stage the next log, which should be the first ongoing log. |
| // Check that it has one sample in |kOnDidCreateMetricsLogHistogramName|. |
| test_log_store->StageNextLog(); |
| EXPECT_EQ(1, GetSampleCountOfOnDidCreateLogHistogram(test_log_store)); |
| |
| // Discard the staged log and close and stage the next log, which is the |
| // second "ongoing log". |
| // Check that it has one sample in |kOnDidCreateMetricsLogHistogramName|. |
| // Also verify that the test provider was called when closing the new log. |
| test_provider->set_record_histogram_snapshots_called(false); |
| test_log_store->DiscardStagedLog(); |
| service.StageCurrentLogForTest(); |
| EXPECT_EQ(1, GetSampleCountOfOnDidCreateLogHistogram(test_log_store)); |
| EXPECT_TRUE(test_provider->record_histogram_snapshots_called()); |
| |
| // Check one more log for good measure. |
| test_provider->set_record_histogram_snapshots_called(false); |
| test_log_store->DiscardStagedLog(); |
| service.StageCurrentLogForTest(); |
| EXPECT_EQ(1, GetSampleCountOfOnDidCreateLogHistogram(test_log_store)); |
| EXPECT_TRUE(test_provider->record_histogram_snapshots_called()); |
| |
| service.Stop(); |
| |
| // Clean up histograms. |
| base::StatisticsRecorder::ForgetHistogramForTesting( |
| kOnDidCreateMetricsLogHistogramName); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, MarkCurrentHistogramsAsReported) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| // Emit to histogram |Test.Before.Histogram|. |
| ASSERT_FALSE(HistogramExists("Test.Before.Histogram")); |
| base::UmaHistogramBoolean("Test.Before.Histogram", true); |
| ASSERT_TRUE(HistogramExists("Test.Before.Histogram")); |
| |
| // Mark histogram data that has been collected until now (in particular, the |
| // |Test.Before.Histogram| sample) as reported. |
| service.MarkCurrentHistogramsAsReported(); |
| |
| // Emit to histogram |Test.After.Histogram|. |
| ASSERT_FALSE(HistogramExists("Test.After.Histogram")); |
| base::UmaHistogramBoolean("Test.After.Histogram", true); |
| ASSERT_TRUE(HistogramExists("Test.After.Histogram")); |
| |
| // Verify that the |Test.Before.Histogram| sample was marked as reported, and |
| // is not included in the next snapshot. |
| EXPECT_EQ(0, GetHistogramDeltaTotalCount("Test.Before.Histogram")); |
| // Verify that the |Test.After.Histogram| sample was not marked as reported, |
| // and is included in the next snapshot. |
| EXPECT_EQ(1, GetHistogramDeltaTotalCount("Test.After.Histogram")); |
| |
| // Clean up histograms. |
| base::StatisticsRecorder::ForgetHistogramForTesting("Test.Before.Histogram"); |
| base::StatisticsRecorder::ForgetHistogramForTesting("Test.After.Histogram"); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, LogHasUserActions) { |
| // This test verifies that user actions are properly captured in UMA logs. |
| // In particular, it checks that the first log has actions, a behavior that |
| // was buggy in the past, plus additional checks for subsequent logs with |
| // different numbers of actions. |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| service.InitializeMetricsRecordingState(); |
| |
| // Start() will create an initial log. |
| service.Start(); |
| ASSERT_EQ(TestMetricsService::INIT_TASK_SCHEDULED, service.state()); |
| |
| base::RecordAction(base::UserMetricsAction("TestAction")); |
| base::RecordAction(base::UserMetricsAction("TestAction")); |
| base::RecordAction(base::UserMetricsAction("DifferentAction")); |
| |
| // Run pending tasks to finish init task and complete the first ongoing log. |
| task_runner_->RunPendingTasks(); |
| ASSERT_EQ(TestMetricsService::SENDING_LOGS, service.state()); |
| |
| MetricsLogStore* test_log_store = service.LogStoreForTest(); |
| |
| // Stage the next log, which should be the initial metrics log. |
| test_log_store->StageNextLog(); |
| EXPECT_EQ(3, GetNumberOfUserActions(test_log_store)); |
| |
| // Log another action. |
| base::RecordAction(base::UserMetricsAction("TestAction")); |
| test_log_store->DiscardStagedLog(); |
| service.StageCurrentLogForTest(); |
| EXPECT_EQ(1, GetNumberOfUserActions(test_log_store)); |
| |
| // Check a log with no actions. |
| test_log_store->DiscardStagedLog(); |
| service.StageCurrentLogForTest(); |
| EXPECT_EQ(0, GetNumberOfUserActions(test_log_store)); |
| |
| // And another one with a couple. |
| base::RecordAction(base::UserMetricsAction("TestAction")); |
| base::RecordAction(base::UserMetricsAction("TestAction")); |
| test_log_store->DiscardStagedLog(); |
| service.StageCurrentLogForTest(); |
| EXPECT_EQ(2, GetNumberOfUserActions(test_log_store)); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, FirstLogCreatedBeforeUnsentLogsSent) { |
| // This test checks that we will create and serialize the first ongoing log |
| // before starting to send unsent logs from the past session. The latter is |
| // simulated by injecting some fake ongoing logs into the MetricsLogStore. |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| service.InitializeMetricsRecordingState(); |
| // Start() will create the first ongoing log. |
| service.Start(); |
| ASSERT_EQ(TestMetricsService::INIT_TASK_SCHEDULED, service.state()); |
| |
| MetricsLogStore* test_log_store = service.LogStoreForTest(); |
| |
| // Set up the log store with an existing fake log entry. The string content |
| // is never deserialized to proto, so we're just passing some dummy content. |
| ASSERT_EQ(0u, test_log_store->initial_log_count()); |
| ASSERT_EQ(0u, test_log_store->ongoing_log_count()); |
| test_log_store->StoreLog("blah_blah", MetricsLog::ONGOING_LOG, LogMetadata(), |
| MetricsLogsEventManager::CreateReason::kUnknown); |
| // Note: |initial_log_count()| refers to initial stability logs, so the above |
| // log is counted an ongoing log (per its type). |
| ASSERT_EQ(0u, test_log_store->initial_log_count()); |
| ASSERT_EQ(1u, test_log_store->ongoing_log_count()); |
| |
| // Run pending tasks to finish init task and complete the first ongoing log. |
| task_runner_->RunPendingTasks(); |
| ASSERT_EQ(TestMetricsService::SENDING_LOGS, service.state()); |
| // When the init task is complete, the first ongoing log should be created |
| // and added to the ongoing logs. |
| EXPECT_EQ(0u, test_log_store->initial_log_count()); |
| EXPECT_EQ(2u, test_log_store->ongoing_log_count()); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, |
| MetricsProviderOnRecordingDisabledCalledOnInitialStop) { |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| TestMetricsProvider* test_provider = new TestMetricsProvider(); |
| service.RegisterMetricsProvider( |
| std::unique_ptr<MetricsProvider>(test_provider)); |
| |
| service.InitializeMetricsRecordingState(); |
| service.Stop(); |
| |
| EXPECT_TRUE(test_provider->on_recording_disabled_called()); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, MetricsProvidersInitialized) { |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| TestMetricsProvider* test_provider = new TestMetricsProvider(); |
| service.RegisterMetricsProvider( |
| std::unique_ptr<MetricsProvider>(test_provider)); |
| |
| service.InitializeMetricsRecordingState(); |
| |
| EXPECT_TRUE(test_provider->init_called()); |
| } |
| |
| // Verify that FieldTrials activated by a MetricsProvider are reported by the |
| // FieldTrialsProvider. |
| TEST_P(MetricsServiceTestWithFeatures, ActiveFieldTrialsReported) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| // Set up FieldTrials. |
| const std::string trial_name1 = "CoffeeExperiment"; |
| const std::string group_name1 = "Free"; |
| base::FieldTrial* trial1 = |
| base::FieldTrialList::CreateFieldTrial(trial_name1, group_name1); |
| |
| const std::string trial_name2 = "DonutExperiment"; |
| const std::string group_name2 = "MapleBacon"; |
| base::FieldTrial* trial2 = |
| base::FieldTrialList::CreateFieldTrial(trial_name2, group_name2); |
| |
| service.RegisterMetricsProvider( |
| std::make_unique<ExperimentTestMetricsProvider>(trial1, trial2)); |
| |
| service.InitializeMetricsRecordingState(); |
| service.Start(); |
| service.StageCurrentLogForTest(); |
| |
| MetricsLogStore* test_log_store = service.LogStoreForTest(); |
| ChromeUserMetricsExtension uma_log; |
| EXPECT_TRUE(DecodeLogDataToProto(test_log_store->staged_log(), &uma_log)); |
| |
| // Verify that the reported FieldTrial IDs are for the trial set up by this |
| // test. |
| EXPECT_TRUE( |
| IsFieldTrialPresent(uma_log.system_profile(), trial_name1, group_name1)); |
| EXPECT_TRUE( |
| IsFieldTrialPresent(uma_log.system_profile(), trial_name2, group_name2)); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, |
| SystemProfileDataProvidedOnEnableRecording) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| TestMetricsProvider* test_provider = new TestMetricsProvider(); |
| service.RegisterMetricsProvider( |
| std::unique_ptr<MetricsProvider>(test_provider)); |
| |
| service.InitializeMetricsRecordingState(); |
| |
| // ProvideSystemProfileMetrics() shouldn't be called initially. |
| EXPECT_FALSE(test_provider->provide_system_profile_metrics_called()); |
| EXPECT_FALSE(service.persistent_system_profile_provided()); |
| |
| service.Start(); |
| |
| // Start should call ProvideSystemProfileMetrics(). |
| EXPECT_TRUE(test_provider->provide_system_profile_metrics_called()); |
| EXPECT_TRUE(service.persistent_system_profile_provided()); |
| EXPECT_FALSE(service.persistent_system_profile_complete()); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, SplitRotation) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| service.InitializeMetricsRecordingState(); |
| service.Start(); |
| // Rotation loop should create a log and mark state as idle. |
| // Upload loop should start upload or be restarted. |
| // The independent-metrics upload job will be started and always be a task. |
| task_runner_->RunPendingTasks(); |
| // Rotation loop should terminated due to being idle. |
| // Upload loop should start uploading if it isn't already. |
| task_runner_->RunPendingTasks(); |
| EXPECT_TRUE(client.uploader()->is_uploading()); |
| EXPECT_EQ(1U, task_runner_->NumPendingTasks()); |
| service.OnApplicationNotIdle(); |
| EXPECT_TRUE(client.uploader()->is_uploading()); |
| EXPECT_EQ(2U, task_runner_->NumPendingTasks()); |
| // Log generation should be suppressed due to unsent log. |
| // Idle state should not be reset. |
| task_runner_->RunPendingTasks(); |
| EXPECT_TRUE(client.uploader()->is_uploading()); |
| EXPECT_EQ(2U, task_runner_->NumPendingTasks()); |
| // Make sure idle state was not reset. |
| task_runner_->RunPendingTasks(); |
| EXPECT_TRUE(client.uploader()->is_uploading()); |
| EXPECT_EQ(2U, task_runner_->NumPendingTasks()); |
| // Upload should not be rescheduled, since there are no other logs. |
| client.uploader()->CompleteUpload(200); |
| EXPECT_FALSE(client.uploader()->is_uploading()); |
| EXPECT_EQ(2U, task_runner_->NumPendingTasks()); |
| // Running should generate a log, restart upload loop, and mark idle. |
| task_runner_->RunPendingTasks(); |
| EXPECT_FALSE(client.uploader()->is_uploading()); |
| EXPECT_EQ(3U, task_runner_->NumPendingTasks()); |
| // Upload should start, and rotation loop should idle out. |
| task_runner_->RunPendingTasks(); |
| EXPECT_TRUE(client.uploader()->is_uploading()); |
| EXPECT_EQ(1U, task_runner_->NumPendingTasks()); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, LastLiveTimestamp) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| base::Time initial_last_live_time = |
| GetLocalState()->GetTime(prefs::kStabilityBrowserLastLiveTimeStamp); |
| |
| service.InitializeMetricsRecordingState(); |
| service.Start(); |
| |
| task_runner_->RunPendingTasks(); |
| size_t num_pending_tasks = task_runner_->NumPendingTasks(); |
| |
| service.StartUpdatingLastLiveTimestamp(); |
| |
| // Starting the update sequence should not write anything, but should |
| // set up for a later write. |
| EXPECT_EQ( |
| initial_last_live_time, |
| GetLocalState()->GetTime(prefs::kStabilityBrowserLastLiveTimeStamp)); |
| EXPECT_EQ(num_pending_tasks + 1, task_runner_->NumPendingTasks()); |
| |
| // To avoid flakiness, yield until we're over a microsecond threshold. |
| YieldUntil(initial_last_live_time + base::Microseconds(2)); |
| |
| task_runner_->RunPendingTasks(); |
| |
| // Verify that the time has updated in local state. |
| base::Time updated_last_live_time = |
| GetLocalState()->GetTime(prefs::kStabilityBrowserLastLiveTimeStamp); |
| EXPECT_LT(initial_last_live_time, updated_last_live_time); |
| |
| // Double check that an update schedules again... |
| YieldUntil(updated_last_live_time + base::Microseconds(2)); |
| |
| task_runner_->RunPendingTasks(); |
| EXPECT_LT( |
| updated_last_live_time, |
| GetLocalState()->GetTime(prefs::kStabilityBrowserLastLiveTimeStamp)); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, EnablementObserverNotification) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| service.InitializeMetricsRecordingState(); |
| |
| absl::optional<bool> enabled; |
| auto observer = [&enabled](bool notification) { enabled = notification; }; |
| |
| auto subscription = |
| service.AddEnablementObserver(base::BindLambdaForTesting(observer)); |
| |
| service.Start(); |
| ASSERT_TRUE(enabled.has_value()); |
| EXPECT_TRUE(enabled.value()); |
| |
| enabled.reset(); |
| |
| service.Stop(); |
| ASSERT_TRUE(enabled.has_value()); |
| EXPECT_FALSE(enabled.value()); |
| } |
| |
| // Verifies that when a cloned install is detected, logs are purged. |
| TEST_P(MetricsServiceTestWithFeatures, PurgeLogsOnClonedInstallDetected) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| service.InitializeMetricsRecordingState(); |
| |
| // Store various logs. |
| MetricsLogStore* test_log_store = service.LogStoreForTest(); |
| test_log_store->StoreLog("dummy log data", MetricsLog::ONGOING_LOG, |
| LogMetadata(), |
| MetricsLogsEventManager::CreateReason::kUnknown); |
| test_log_store->StageNextLog(); |
| test_log_store->StoreLog("more dummy log data", MetricsLog::ONGOING_LOG, |
| LogMetadata(), |
| MetricsLogsEventManager::CreateReason::kUnknown); |
| test_log_store->StoreLog("dummy stability log", |
| MetricsLog::INITIAL_STABILITY_LOG, LogMetadata(), |
| MetricsLogsEventManager::CreateReason::kUnknown); |
| test_log_store->SetAlternateOngoingLogStore(InitializeTestLogStoreAndGet()); |
| test_log_store->StoreLog("dummy log for alternate ongoing log store", |
| MetricsLog::ONGOING_LOG, LogMetadata(), |
| MetricsLogsEventManager::CreateReason::kUnknown); |
| EXPECT_TRUE(test_log_store->has_staged_log()); |
| EXPECT_TRUE(test_log_store->has_unsent_logs()); |
| |
| ClonedInstallDetector* cloned_install_detector = |
| GetMetricsStateManager()->cloned_install_detector_for_testing(); |
| |
| static constexpr char kTestRawId[] = "test"; |
| // Hashed machine id for |kTestRawId|. |
| static constexpr int kTestHashedId = 2216819; |
| |
| // Save a machine id that will not cause a clone to be detected. |
| GetLocalState()->SetInteger(prefs::kMetricsMachineId, kTestHashedId); |
| cloned_install_detector->SaveMachineId(GetLocalState(), kTestRawId); |
| // Verify that the logs are still present. |
| EXPECT_TRUE(test_log_store->has_staged_log()); |
| EXPECT_TRUE(test_log_store->has_unsent_logs()); |
| |
| // Save a machine id that will cause a clone to be detected. |
| GetLocalState()->SetInteger(prefs::kMetricsMachineId, kTestHashedId + 1); |
| cloned_install_detector->SaveMachineId(GetLocalState(), kTestRawId); |
| // Verify that the logs were purged if the |kMetricsClearLogsOnClonedInstall| |
| // feature is enabled. |
| if (ShouldClearLogsOnClonedInstall()) { |
| EXPECT_FALSE(test_log_store->has_staged_log()); |
| EXPECT_FALSE(test_log_store->has_unsent_logs()); |
| } else { |
| EXPECT_TRUE(test_log_store->has_staged_log()); |
| EXPECT_TRUE(test_log_store->has_unsent_logs()); |
| } |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| // ResetClientId is only enabled on certain targets. |
| TEST_P(MetricsServiceTestWithFeatures, SetClientIdToExternalId) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| const std::string client_id = "d92ad666-a420-4c73-8718-94311ae2ff5f"; |
| |
| EXPECT_NE(service.GetClientId(), client_id); |
| |
| service.SetExternalClientId(client_id); |
| // Reset will cause the client id to be regenerated. If an external client id |
| // is provided, it should defer to using that id instead of creating its own. |
| service.ResetClientId(); |
| |
| EXPECT_EQ(service.GetClientId(), client_id); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS_LACROS) |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| TEST_P(MetricsServiceTestWithFeatures, |
| OngoingLogNotFlushedBeforeInitialLogWhenUserLogStoreSet) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| service.InitializeMetricsRecordingState(); |
| // Start() will create the first ongoing log. |
| service.Start(); |
| |
| MetricsLogStore* test_log_store = service.LogStoreForTest(); |
| std::unique_ptr<TestUnsentLogStore> alternate_ongoing_log_store = |
| InitializeTestLogStoreAndGet(); |
| TestUnsentLogStore* alternate_ongoing_log_store_ptr = |
| alternate_ongoing_log_store.get(); |
| |
| ASSERT_EQ(0u, test_log_store->initial_log_count()); |
| ASSERT_EQ(0u, test_log_store->ongoing_log_count()); |
| |
| service.SetUserLogStore(std::move(alternate_ongoing_log_store)); |
| |
| // Initial logs should not have been collected so the ongoing log being |
| // recorded should not be flushed when a user log store is mounted. |
| ASSERT_EQ(0u, test_log_store->initial_log_count()); |
| ASSERT_EQ(0u, test_log_store->ongoing_log_count()); |
| |
| // Run pending tasks to finish init task and complete the first ongoing log. |
| task_runner_->RunPendingTasks(); |
| ASSERT_EQ(TestMetricsService::SENDING_LOGS, service.state()); |
| // When the init task is complete, the first ongoing log should be created |
| // in the alternate ongoing log store. |
| EXPECT_EQ(0u, test_log_store->initial_log_count()); |
| EXPECT_EQ(0u, test_log_store->ongoing_log_count()); |
| EXPECT_EQ(1u, alternate_ongoing_log_store_ptr->size()); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, |
| OngoingLogFlushedAfterInitialLogWhenUserLogStoreSet) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| service.InitializeMetricsRecordingState(); |
| // Start() will create the first ongoing log. |
| service.Start(); |
| |
| MetricsLogStore* test_log_store = service.LogStoreForTest(); |
| std::unique_ptr<TestUnsentLogStore> alternate_ongoing_log_store = |
| InitializeTestLogStoreAndGet(); |
| |
| // Init state. |
| ASSERT_EQ(0u, test_log_store->initial_log_count()); |
| ASSERT_EQ(0u, test_log_store->ongoing_log_count()); |
| |
| // Run pending tasks to finish init task and complete the first ongoing log. |
| task_runner_->RunPendingTasks(); |
| ASSERT_EQ(TestMetricsService::SENDING_LOGS, service.state()); |
| ASSERT_EQ(0u, test_log_store->initial_log_count()); |
| ASSERT_EQ(1u, test_log_store->ongoing_log_count()); |
| |
| // User log store set post-init. |
| service.SetUserLogStore(std::move(alternate_ongoing_log_store)); |
| |
| // Another log should have been flushed from setting the user log store. |
| ASSERT_EQ(0u, test_log_store->initial_log_count()); |
| ASSERT_EQ(2u, test_log_store->ongoing_log_count()); |
| } |
| |
| TEST_P(MetricsServiceTestWithFeatures, |
| OngoingLogDiscardedAfterEarlyUnsetUserLogStore) { |
| EnableMetricsReporting(); |
| TestMetricsServiceClient client; |
| TestMetricsService service(GetMetricsStateManager(), &client, |
| GetLocalState()); |
| |
| service.InitializeMetricsRecordingState(); |
| // Start() will create the first ongoing log. |
| service.Start(); |
| ASSERT_EQ(TestMetricsService::INIT_TASK_SCHEDULED, service.state()); |
| |
| MetricsLogStore* test_log_store = service.LogStoreForTest(); |
| std::unique_ptr<TestUnsentLogStore> alternate_ongoing_log_store = |
| InitializeTestLogStoreAndGet(); |
| |
| ASSERT_EQ(0u, test_log_store->initial_log_count()); |
| ASSERT_EQ(0u, test_log_store->ongoing_log_count()); |
| |
| service.SetUserLogStore(std::move(alternate_ongoing_log_store)); |
| |
| // Unset the user log store before we started sending logs. |
| base::UmaHistogramBoolean("Test.Before.Histogram", true); |
| service.UnsetUserLogStore(); |
| base::UmaHistogramBoolean("Test.After.Histogram", true); |
| |
| // Verify that the current log was discarded. |
| EXPECT_FALSE(service.GetCurrentLogForTest()); |
| |
| // Verify that histograms from before unsetting the user log store were |
| // flushed. |
| EXPECT_EQ(0, GetHistogramDeltaTotalCount("Test.Before.Histogram")); |
| EXPECT_EQ(1, GetHistogramDeltaTotalCount("Test.After.Histogram")); |
| |
| // Clean up histograms. |
| base::StatisticsRecorder::ForgetHistogramForTesting("Test.Before.Histogram"); |
| base::StatisticsRecorder::ForgetHistogramForTesting("Test.After.Histogram"); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS_LACROS) |
| |
| } // namespace metrics |