| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/metrics/clean_exit_beacon.h" |
| |
| #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/memory/scoped_refptr.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/gtest_util.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/mock_entropy_provider.h" |
| #include "base/test/task_environment.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "components/metrics/metrics_pref_names.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service_factory.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "components/prefs/testing_pref_store.h" |
| #include "components/variations/pref_names.h" |
| #include "components/variations/service/variations_safe_mode_constants.h" |
| #include "components/variations/variations_test_utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace metrics { |
| namespace { |
| |
| using ::variations::SetUpExtendedSafeModeExperiment; |
| |
| const wchar_t kDummyWindowsRegistryKey[] = L""; |
| |
| // Creates and returns well-formed beacon file contents with the given values. |
| std::string CreateWellFormedBeaconFileContents(bool exited_cleanly, |
| int crash_streak) { |
| const std::string exited_cleanly_str = exited_cleanly ? "true" : "false"; |
| return base::StringPrintf( |
| "{\n" |
| " \"user_experience_metrics.stability.exited_cleanly\": %s,\n" |
| " \"variations_crash_streak\": %s\n" |
| "}", |
| exited_cleanly_str.data(), base::NumberToString(crash_streak).data()); |
| } |
| |
| } // namespace |
| |
| class TestCleanExitBeacon : public CleanExitBeacon { |
| public: |
| explicit TestCleanExitBeacon( |
| PrefService* local_state, |
| const base::FilePath& user_data_dir = base::FilePath(), |
| version_info::Channel channel = version_info::Channel::UNKNOWN) |
| : CleanExitBeacon(kDummyWindowsRegistryKey, |
| user_data_dir, |
| local_state, |
| channel) { |
| Initialize(); |
| } |
| |
| ~TestCleanExitBeacon() override = default; |
| }; |
| |
| class CleanExitBeaconTest : public ::testing::Test { |
| public: |
| void SetUp() override { |
| metrics::CleanExitBeacon::RegisterPrefs(prefs_.registry()); |
| ASSERT_TRUE(user_data_dir_.CreateUniqueTempDir()); |
| } |
| |
| protected: |
| base::HistogramTester histogram_tester_; |
| TestingPrefServiceSimple prefs_; |
| base::ScopedTempDir user_data_dir_; |
| |
| private: |
| base::test::TaskEnvironment task_environment_; |
| }; |
| |
| struct BadBeaconTestParams { |
| const std::string test_name; |
| bool beacon_file_exists; |
| const std::string beacon_file_contents; |
| BeaconFileState beacon_file_state; |
| }; |
| |
| // Used for testing beacon files that are not well-formed, do not exist, etc. |
| class BadBeaconFileTest |
| : public testing::WithParamInterface<BadBeaconTestParams>, |
| public CleanExitBeaconTest {}; |
| |
| struct BeaconConsistencyTestParams { |
| // Inputs: |
| const std::string test_name; |
| absl::optional<bool> beacon_file_beacon_value; |
| absl::optional<bool> platform_specific_beacon_value; |
| absl::optional<bool> local_state_beacon_value; |
| // Result: |
| CleanExitBeaconConsistency expected_consistency; |
| }; |
| |
| #if BUILDFLAG(IS_IOS) |
| // Used for testing the logic that emits to the UMA.CleanExitBeaconConsistency3 |
| // histogram. |
| class BeaconFileAndPlatformBeaconConsistencyTest |
| : public testing::WithParamInterface<BeaconConsistencyTestParams>, |
| public CleanExitBeaconTest {}; |
| #endif // BUILDFLAG(IS_IOS) |
| |
| // Verify that the crash streak metric is 0 when default pref values are used. |
| TEST_F(CleanExitBeaconTest, CrashStreakMetricWithDefaultPrefs) { |
| CleanExitBeacon::ResetStabilityExitedCleanlyForTesting(&prefs_); |
| TestCleanExitBeacon clean_exit_beacon(&prefs_); |
| histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 0, |
| 1); |
| } |
| |
| // Verify that the crash streak metric is 0 when prefs are explicitly set to |
| // their defaults. |
| TEST_F(CleanExitBeaconTest, CrashStreakMetricWithNoCrashes) { |
| // The default value for kStabilityExitedCleanly is true, but defaults can |
| // change, so we explicitly set it to true here. Similarly, we explicitly set |
| // kVariationsCrashStreak to 0. |
| CleanExitBeacon::SetStabilityExitedCleanlyForTesting(&prefs_, true); |
| prefs_.SetInteger(variations::prefs::kVariationsCrashStreak, 0); |
| TestCleanExitBeacon clean_exit_beacon(&prefs_); |
| histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 0, |
| 1); |
| } |
| |
| // Verify that the crash streak metric is correctly recorded when there is a |
| // non-zero crash streak. |
| TEST_F(CleanExitBeaconTest, CrashStreakMetricWithSomeCrashes) { |
| // The default value for kStabilityExitedCleanly is true, but defaults can |
| // change, so we explicitly set it to true here. |
| CleanExitBeacon::SetStabilityExitedCleanlyForTesting(&prefs_, true); |
| prefs_.SetInteger(variations::prefs::kVariationsCrashStreak, 1); |
| TestCleanExitBeacon clean_exit_beacon(&prefs_); |
| histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 1, |
| 1); |
| } |
| |
| // Verify that the crash streak is correctly incremented and recorded when the |
| // last Chrome session did not exit cleanly. |
| TEST_F(CleanExitBeaconTest, CrashIncrementsCrashStreak) { |
| CleanExitBeacon::SetStabilityExitedCleanlyForTesting(&prefs_, false); |
| prefs_.SetInteger(variations::prefs::kVariationsCrashStreak, 1); |
| TestCleanExitBeacon clean_exit_beacon(&prefs_); |
| EXPECT_EQ(prefs_.GetInteger(variations::prefs::kVariationsCrashStreak), 2); |
| histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 2, |
| 1); |
| } |
| |
| // Verify that the crash streak is correctly incremented and recorded when the |
| // last Chrome session did not exit cleanly and the default crash streak value |
| // is used. |
| TEST_F(CleanExitBeaconTest, |
| CrashIncrementsCrashStreakWithDefaultCrashStreakPref) { |
| CleanExitBeacon::SetStabilityExitedCleanlyForTesting(&prefs_, false); |
| TestCleanExitBeacon clean_exit_beacon(&prefs_); |
| EXPECT_EQ(prefs_.GetInteger(variations::prefs::kVariationsCrashStreak), 1); |
| histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 1, |
| 1); |
| } |
| |
| // Verify that (a) the client is excluded from the Extended Variations Safe Mode |
| // experiment and (b) no attempt is made to read the beacon file when no user |
| // data dir is provided. |
| TEST_F(CleanExitBeaconTest, InitWithoutUserDataDir) { |
| TestCleanExitBeacon beacon(&prefs_, base::FilePath()); |
| EXPECT_FALSE( |
| base::FieldTrialList::IsTrialActive(variations::kExtendedSafeModeTrial)); |
| histogram_tester_.ExpectTotalCount( |
| "Variations.ExtendedSafeMode.BeaconFileStateAtStartup", 0); |
| } |
| |
| // Verify that the beacon file is not read when the client is not in the |
| // SignalAndWriteViaFileUtil experiment group. It is possible for a client to |
| // have the file and to not be in the SignalAndWriteViaFileUtil group when the |
| // client was in the group in a previous session and then switched groups, e.g. |
| // via kResetVariationState. |
| TEST_F(CleanExitBeaconTest, FileIgnoredByControlGroup) { |
| // Deliberately set the prefs so that we can later verify that their values |
| // have not changed. |
| int expected_crash_streak = 0; |
| prefs_.SetInteger(variations::prefs::kVariationsCrashStreak, |
| expected_crash_streak); |
| CleanExitBeacon::SetStabilityExitedCleanlyForTesting(&prefs_, true); |
| |
| // Prepare a well-formed beacon file, which we expect to be ignored. (If it |
| // were used, then the prefs' values would change.) |
| const base::FilePath user_data_dir_path = user_data_dir_.GetPath(); |
| const base::FilePath temp_beacon_file_path = |
| user_data_dir_path.Append(variations::kCleanExitBeaconFilename); |
| ASSERT_LT(0, base::WriteFile(temp_beacon_file_path, |
| CreateWellFormedBeaconFileContents( |
| /*exited_cleanly=*/false, /*crash_streak=*/2) |
| .data())); |
| const std::string group_name = variations::kControlGroup; |
| SetUpExtendedSafeModeExperiment(group_name); |
| ASSERT_EQ(group_name, base::FieldTrialList::FindFullName( |
| variations::kExtendedSafeModeTrial)); |
| TestCleanExitBeacon beacon(&prefs_, user_data_dir_path); |
| |
| EXPECT_TRUE(prefs_.GetBoolean(prefs::kStabilityExitedCleanly)); |
| EXPECT_EQ(prefs_.GetInteger(variations::prefs::kVariationsCrashStreak), |
| expected_crash_streak); |
| histogram_tester_.ExpectTotalCount( |
| "Variations.ExtendedSafeMode.BeaconFileStateAtStartup", 0); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| BadBeaconFileTest, |
| ::testing::Values( |
| BadBeaconTestParams{ |
| .test_name = "NoVariationsFile", |
| .beacon_file_exists = false, |
| .beacon_file_contents = "", |
| .beacon_file_state = BeaconFileState::kNotDeserializable}, |
| BadBeaconTestParams{ |
| .test_name = "EmptyVariationsFile", |
| .beacon_file_exists = true, |
| .beacon_file_contents = "", |
| .beacon_file_state = BeaconFileState::kNotDeserializable}, |
| BadBeaconTestParams{ |
| .test_name = "NotDictionary", |
| .beacon_file_exists = true, |
| .beacon_file_contents = "{abc123", |
| .beacon_file_state = BeaconFileState::kNotDeserializable}, |
| BadBeaconTestParams{ |
| .test_name = "EmptyDictionary", |
| .beacon_file_exists = true, |
| .beacon_file_contents = "{}", |
| .beacon_file_state = BeaconFileState::kMissingDictionary}, |
| BadBeaconTestParams{ |
| .test_name = "MissingCrashStreak", |
| .beacon_file_exists = true, |
| .beacon_file_contents = |
| "{\"user_experience_metrics.stability.exited_cleanly\": true}", |
| .beacon_file_state = BeaconFileState::kMissingCrashStreak}, |
| BadBeaconTestParams{ |
| .test_name = "MissingBeacon", |
| .beacon_file_exists = true, |
| .beacon_file_contents = "{\"variations_crash_streak\": 1}", |
| .beacon_file_state = BeaconFileState::kMissingBeacon}), |
| [](const ::testing::TestParamInfo<BadBeaconTestParams>& params) { |
| return params.param.test_name; |
| }); |
| |
| // Verify that the inability to get the beacon file's contents for a plethora of |
| // reasons (a) doesn't crash and (b) correctly records the BeaconFileState |
| // metric. |
| TEST_P(BadBeaconFileTest, InitWithUnusableBeaconFile) { |
| SetUpExtendedSafeModeExperiment(variations::kEnabledGroup); |
| BadBeaconTestParams params = GetParam(); |
| |
| const base::FilePath user_data_dir_path = user_data_dir_.GetPath(); |
| if (params.beacon_file_exists) { |
| const base::FilePath temp_beacon_file_path = |
| user_data_dir_path.Append(variations::kCleanExitBeaconFilename); |
| ASSERT_LT(0, base::WriteFile(temp_beacon_file_path, |
| params.beacon_file_contents.data())); |
| } |
| |
| TestCleanExitBeacon beacon(&prefs_, user_data_dir_path); |
| histogram_tester_.ExpectUniqueSample( |
| "Variations.ExtendedSafeMode.BeaconFileStateAtStartup", |
| params.beacon_file_state, 1); |
| } |
| |
| // Verify that successfully reading the beacon file's contents results in |
| // correctly (a) setting the |did_previous_session_exit_cleanly_| field and (b) |
| // recording metrics when the last session exited cleanly. |
| TEST_F(CleanExitBeaconTest, InitWithBeaconFile) { |
| SetUpExtendedSafeModeExperiment(variations::kEnabledGroup); |
| const base::FilePath user_data_dir_path = user_data_dir_.GetPath(); |
| const base::FilePath temp_beacon_file_path = |
| user_data_dir_path.Append(variations::kCleanExitBeaconFilename); |
| const int num_crashes = 2; |
| ASSERT_LT(0, base::WriteFile( |
| temp_beacon_file_path, |
| CreateWellFormedBeaconFileContents( |
| /*exited_cleanly=*/true, /*crash_streak=*/num_crashes) |
| .data())); |
| |
| TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path); |
| histogram_tester_.ExpectUniqueSample( |
| "Variations.ExtendedSafeMode.BeaconFileStateAtStartup", |
| BeaconFileState::kReadable, 1); |
| EXPECT_TRUE(clean_exit_beacon.exited_cleanly()); |
| histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", |
| num_crashes, 1); |
| } |
| |
| // Verify that successfully reading the beacon file's contents results in |
| // correctly (a) setting the |did_previous_session_exit_cleanly_| field and (b) |
| // recording metrics when the last session did not exit cleanly. |
| TEST_F(CleanExitBeaconTest, InitWithCrashAndBeaconFile) { |
| SetUpExtendedSafeModeExperiment(variations::kEnabledGroup); |
| const base::FilePath user_data_dir_path = user_data_dir_.GetPath(); |
| const base::FilePath temp_beacon_file_path = |
| user_data_dir_path.Append(variations::kCleanExitBeaconFilename); |
| const int last_session_num_crashes = 2; |
| ASSERT_LT(0, base::WriteFile(temp_beacon_file_path, |
| CreateWellFormedBeaconFileContents( |
| /*exited_cleanly=*/false, |
| /*crash_streak=*/last_session_num_crashes) |
| .data())); |
| |
| const int updated_num_crashes = last_session_num_crashes + 1; |
| TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path); |
| histogram_tester_.ExpectUniqueSample( |
| "Variations.ExtendedSafeMode.BeaconFileStateAtStartup", |
| BeaconFileState::kReadable, 1); |
| EXPECT_FALSE(clean_exit_beacon.exited_cleanly()); |
| histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", |
| updated_num_crashes, 1); |
| } |
| |
| TEST_F(CleanExitBeaconTest, WriteBeaconValueWhenNotExitingCleanly) { |
| const base::FilePath user_data_dir_path = user_data_dir_.GetPath(); |
| const base::FilePath beacon_file_path = |
| user_data_dir_path.Append(variations::kCleanExitBeaconFilename); |
| ASSERT_FALSE(base::PathExists(beacon_file_path)); |
| |
| SetUpExtendedSafeModeExperiment(variations::kEnabledGroup); |
| TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path); |
| clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/false, |
| /*is_extended_safe_mode=*/true); |
| |
| // Verify that the beacon file exists and has well-formed contents after |
| // updating the beacon value. |
| EXPECT_TRUE(base::PathExists(beacon_file_path)); |
| std::string beacon_file_contents1; |
| ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents1)); |
| EXPECT_EQ(beacon_file_contents1, |
| "{\"user_experience_metrics.stability.exited_cleanly\":false," |
| "\"variations_crash_streak\":0}"); |
| // Verify that the BeaconFileWrite metric was emitted. |
| histogram_tester_.ExpectUniqueSample( |
| "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1); |
| |
| // Write the beacon value again. This is done because it is possible for |
| // WriteBeaconValue() to be called twice during startup or shutdown with the |
| // same value for |exited_cleanly|. |
| clean_exit_beacon.WriteBeaconValue(/*exited_cleanly*/ false, |
| /*is_extended_safe_mode=*/false); |
| |
| // Verify that the beacon file exists and has well-formed contents after |
| // updating the beacon value. |
| EXPECT_TRUE(base::PathExists(beacon_file_path)); |
| std::string beacon_file_contents2; |
| ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents2)); |
| EXPECT_EQ(beacon_file_contents2, |
| "{\"user_experience_metrics.stability.exited_cleanly\":false," |
| "\"variations_crash_streak\":0}"); |
| // Verify that the BeaconFileWrite metric was not emitted a second time. The |
| // beacon file should not have been written again since the beacon value did |
| // not change. |
| histogram_tester_.ExpectUniqueSample( |
| "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1); |
| } |
| |
| TEST_F(CleanExitBeaconTest, WriteBeaconValueWhenExitingCleanly) { |
| const base::FilePath user_data_dir_path = user_data_dir_.GetPath(); |
| const base::FilePath beacon_file_path = |
| user_data_dir_path.Append(variations::kCleanExitBeaconFilename); |
| ASSERT_FALSE(base::PathExists(beacon_file_path)); |
| |
| SetUpExtendedSafeModeExperiment(variations::kEnabledGroup); |
| TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path); |
| clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/true, |
| /*is_extended_safe_mode=*/false); |
| |
| // Verify that the beacon file exists and has well-formed contents after |
| // updating the beacon value. |
| EXPECT_TRUE(base::PathExists(beacon_file_path)); |
| std::string beacon_file_contents1; |
| ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents1)); |
| EXPECT_EQ(beacon_file_contents1, |
| "{\"user_experience_metrics.stability.exited_cleanly\":true," |
| "\"variations_crash_streak\":0}"); |
| // Verify that the BeaconFileWrite metric was emitted. |
| histogram_tester_.ExpectUniqueSample( |
| "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1); |
| |
| // Write the beacon value again. This is done because it is possible for |
| // WriteBeaconValue() to be called twice during startup or shutdown with the |
| // same value for |exited_cleanly|. |
| clean_exit_beacon.WriteBeaconValue(/*exited_cleanly*/ true, |
| /*is_extended_safe_mode=*/false); |
| |
| // Verify that the beacon file exists and has well-formed contents after |
| // updating the beacon value. |
| EXPECT_TRUE(base::PathExists(beacon_file_path)); |
| std::string beacon_file_contents2; |
| ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents2)); |
| EXPECT_EQ(beacon_file_contents2, |
| "{\"user_experience_metrics.stability.exited_cleanly\":true," |
| "\"variations_crash_streak\":0}"); |
| // Verify that the BeaconFileWrite metric was not emitted a second time. The |
| // beacon file should not have been written again since the beacon value did |
| // not change. |
| histogram_tester_.ExpectUniqueSample( |
| "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1); |
| } |
| |
| // Verify that there's a DCHECK when an Extended Variations Safe Mode client |
| // attempts to write a clean beacon with |is_extended_safe_mode| set to true. |
| // |is_extended_safe_mode| should only be set to true in one call site: |
| // VariationsFieldTrialCreator::MaybeExtendVariationsSafeMode(). |
| // |
| // TODO(crbug/1241702): Re-enable this test once the FieldTrial is cleaned up. |
| TEST_F(CleanExitBeaconTest, |
| DISABLED_WriteBeaconValue_SynchronousWriteDcheck_ExperimentGroup) { |
| SetUpExtendedSafeModeExperiment(variations::kEnabledGroup); |
| ASSERT_EQ(variations::kEnabledGroup, base::FieldTrialList::FindFullName( |
| variations::kExtendedSafeModeTrial)); |
| |
| TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_.GetPath()); |
| EXPECT_DCHECK_DEATH( |
| clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/true, |
| /*is_extended_safe_mode=*/true)); |
| } |
| |
| #if BUILDFLAG(IS_IOS) |
| // Verify that the logic for recording UMA.CleanExitBeaconConsistency3 is |
| // correct for clients in the Extended Variations Safe Mode experiment's enabled |
| // group. |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| BeaconFileAndPlatformBeaconConsistencyTest, |
| ::testing::Values( |
| BeaconConsistencyTestParams{ |
| .test_name = "MissingMissing", |
| .expected_consistency = |
| CleanExitBeaconConsistency::kMissingMissing}, |
| BeaconConsistencyTestParams{ |
| .test_name = "MissingClean", |
| .platform_specific_beacon_value = true, |
| .expected_consistency = CleanExitBeaconConsistency::kMissingClean}, |
| BeaconConsistencyTestParams{ |
| .test_name = "MissingDirty", |
| .platform_specific_beacon_value = false, |
| .expected_consistency = CleanExitBeaconConsistency::kMissingDirty}, |
| BeaconConsistencyTestParams{ |
| .test_name = "CleanMissing", |
| .beacon_file_beacon_value = true, |
| .expected_consistency = CleanExitBeaconConsistency::kCleanMissing}, |
| BeaconConsistencyTestParams{ |
| .test_name = "DirtyMissing", |
| .beacon_file_beacon_value = false, |
| .expected_consistency = CleanExitBeaconConsistency::kDirtyMissing}, |
| BeaconConsistencyTestParams{ |
| .test_name = "CleanClean", |
| .beacon_file_beacon_value = true, |
| .platform_specific_beacon_value = true, |
| .expected_consistency = CleanExitBeaconConsistency::kCleanClean}, |
| BeaconConsistencyTestParams{ |
| .test_name = "CleanDirty", |
| .beacon_file_beacon_value = true, |
| .platform_specific_beacon_value = false, |
| .expected_consistency = CleanExitBeaconConsistency::kCleanDirty}, |
| BeaconConsistencyTestParams{ |
| .test_name = "DirtyClean", |
| .beacon_file_beacon_value = false, |
| .platform_specific_beacon_value = true, |
| .expected_consistency = CleanExitBeaconConsistency::kDirtyClean}, |
| BeaconConsistencyTestParams{ |
| .test_name = "DirtyDirty", |
| .beacon_file_beacon_value = false, |
| .platform_specific_beacon_value = false, |
| .expected_consistency = CleanExitBeaconConsistency::kDirtyDirty}), |
| [](const ::testing::TestParamInfo<BeaconConsistencyTestParams>& params) { |
| return params.param.test_name; |
| }); |
| |
| TEST_P(BeaconFileAndPlatformBeaconConsistencyTest, BeaconConsistency) { |
| // Verify that the beacon file is not present. Unless set below, this beacon |
| // is considered missing. |
| const base::FilePath user_data_dir_path = user_data_dir_.GetPath(); |
| const base::FilePath temp_beacon_file_path = |
| user_data_dir_path.Append(variations::kCleanExitBeaconFilename); |
| ASSERT_FALSE(base::PathExists(temp_beacon_file_path)); |
| // Clear the platform-specific beacon. Unless set below, this beacon is also |
| // considered missing. |
| CleanExitBeacon::ResetStabilityExitedCleanlyForTesting(&prefs_); |
| |
| BeaconConsistencyTestParams params = GetParam(); |
| if (params.beacon_file_beacon_value) { |
| ASSERT_LT( |
| 0, base::WriteFile( |
| temp_beacon_file_path, |
| CreateWellFormedBeaconFileContents( |
| /*exited_cleanly=*/params.beacon_file_beacon_value.value(), |
| /*crash_streak=*/0) |
| .data())); |
| } |
| if (params.platform_specific_beacon_value) { |
| CleanExitBeacon::SetUserDefaultsBeacon( |
| /*exited_cleanly=*/params.platform_specific_beacon_value.value()); |
| } |
| |
| SetUpExtendedSafeModeExperiment(variations::kEnabledGroup); |
| ASSERT_EQ(variations::kEnabledGroup, base::FieldTrialList::FindFullName( |
| variations::kExtendedSafeModeTrial)); |
| |
| TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path); |
| histogram_tester_.ExpectUniqueSample("UMA.CleanExitBeaconConsistency3", |
| params.expected_consistency, 1); |
| } |
| #endif // BUILDFLAG(IS_IOS) |
| |
| #if BUILDFLAG(IS_ANDROID) |
| TEST_F(CleanExitBeaconTest, EnabledGroupEmitsStageDurationMetric) { |
| // Force the client into the Extended Variations Safe Mode experiment's |
| // enabled group. |
| SetUpExtendedSafeModeExperiment(variations::kEnabledGroup); |
| |
| // Create and initialize the CleanExitBeacon. |
| TestCleanExitBeacon clean_exit_beacon(&prefs_); |
| |
| // Simulate Chrome starting to watch for browser crashes for enabled-group |
| // clients. |
| clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/false, |
| /*is_extended_safe_mode=*/true); |
| // Verify that the metric has not yet been emitted. |
| histogram_tester_.ExpectTotalCount( |
| "UMA.CleanExitBeacon.ExtendedMonitoringStageDuration", 0); |
| |
| // Simulate Chrome continuing to watch for crashes once the app enters the |
| // foreground. |
| clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/false, |
| /*is_extended_safe_mode=*/false); |
| // Verify that the metric was emitted. |
| histogram_tester_.ExpectTotalCount( |
| "UMA.CleanExitBeacon.ExtendedMonitoringStageDuration", 1); |
| |
| // Make the same call. Note that these two identical, consecutive calls to |
| // WriteBeaconValue() shouldn't actually happen, but this is done for the |
| // purpose of the test. |
| clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/false, |
| /*is_extended_safe_mode=*/false); |
| // Verify that the metric was not emitted again. |
| histogram_tester_.ExpectTotalCount( |
| "UMA.CleanExitBeacon.ExtendedMonitoringStageDuration", 1); |
| } |
| |
| TEST_F(CleanExitBeaconTest, ControlGroupDoesNotEmitStageDurationMetric) { |
| // Force the client into the Extended Variations Safe Mode experiment's |
| // control group. |
| SetUpExtendedSafeModeExperiment(variations::kControlGroup); |
| |
| // Create and initialize the CleanExitBeacon. |
| TestCleanExitBeacon clean_exit_beacon(&prefs_); |
| |
| // Simulate Chrome starting to watch for browser crashes for control-group |
| // clients once the app enters the foreground. |
| clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/false, |
| /*is_extended_safe_mode=*/false); |
| // Verify that the metric was not emitted. |
| histogram_tester_.ExpectTotalCount( |
| "UMA.CleanExitBeacon.ExtendedMonitoringStageDuration", 0); |
| |
| // Make the same call. Note that these two identical, consecutive calls to |
| // WriteBeaconValue() shouldn't actually happen, but this is done for the |
| // purpose of the test. |
| clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/false, |
| /*is_extended_safe_mode=*/false); |
| // Verify that the metric was not emitted. |
| histogram_tester_.ExpectTotalCount( |
| "UMA.CleanExitBeacon.ExtendedMonitoringStageDuration", 0); |
| } |
| |
| #endif // BUILDFLAG(IS_ANDROID) |
| |
| } // namespace metrics |