• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/metrics/clean_exit_beacon.h"
6 
7 #include <memory>
8 #include <optional>
9 #include <string>
10 
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/memory/scoped_refptr.h"
15 #include "base/metrics/field_trial.h"
16 #include "base/test/gtest_util.h"
17 #include "base/test/metrics/histogram_tester.h"
18 #include "base/test/mock_entropy_provider.h"
19 #include "base/test/task_environment.h"
20 #include "base/time/time.h"
21 #include "build/build_config.h"
22 #include "components/metrics/metrics_pref_names.h"
23 #include "components/prefs/pref_registry_simple.h"
24 #include "components/prefs/pref_service_factory.h"
25 #include "components/prefs/testing_pref_service.h"
26 #include "components/prefs/testing_pref_store.h"
27 #include "components/variations/pref_names.h"
28 #include "components/variations/variations_test_utils.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 
31 namespace metrics {
32 namespace {
33 
34 using ::variations::prefs::kVariationsCrashStreak;
35 
36 const wchar_t kDummyWindowsRegistryKey[] = L"";
37 
38 class TestCleanExitBeacon : public CleanExitBeacon {
39  public:
TestCleanExitBeacon(PrefService * local_state,const base::FilePath & user_data_dir=base::FilePath ())40   explicit TestCleanExitBeacon(
41       PrefService* local_state,
42       const base::FilePath& user_data_dir = base::FilePath())
43       : CleanExitBeacon(kDummyWindowsRegistryKey, user_data_dir, local_state) {
44     Initialize();
45   }
46 
47   ~TestCleanExitBeacon() override = default;
48 };
49 
50 class CleanExitBeaconTest : public ::testing::Test {
51  public:
SetUp()52   void SetUp() override {
53     metrics::CleanExitBeacon::RegisterPrefs(prefs_.registry());
54     ASSERT_TRUE(user_data_dir_.CreateUniqueTempDir());
55   }
56 
57  protected:
58   base::HistogramTester histogram_tester_;
59   TestingPrefServiceSimple prefs_;
60   base::ScopedTempDir user_data_dir_;
61 
62  private:
63   base::test::TaskEnvironment task_environment_;
64 };
65 
66 struct BadBeaconTestParams {
67   const std::string test_name;
68   bool beacon_file_exists;
69   const std::string beacon_file_contents;
70   BeaconFileState beacon_file_state;
71 };
72 
73 // Used for testing beacon files that are not well-formed, do not exist, etc.
74 class BadBeaconFileTest
75     : public testing::WithParamInterface<BadBeaconTestParams>,
76       public CleanExitBeaconTest {};
77 
78 struct BeaconConsistencyTestParams {
79   // Inputs:
80   const std::string test_name;
81   std::optional<bool> beacon_file_beacon_value;
82   std::optional<bool> platform_specific_beacon_value;
83   std::optional<bool> local_state_beacon_value;
84   // Result:
85   CleanExitBeaconConsistency expected_consistency;
86 };
87 
88 #if BUILDFLAG(IS_IOS)
89 // Used for testing the logic that emits to the UMA.CleanExitBeaconConsistency3
90 // histogram.
91 class BeaconFileAndPlatformBeaconConsistencyTest
92     : public testing::WithParamInterface<BeaconConsistencyTestParams>,
93       public CleanExitBeaconTest {};
94 #endif  // BUILDFLAG(IS_IOS)
95 
96 // Verify that the crash streak metric is 0 when default pref values are used.
TEST_F(CleanExitBeaconTest,CrashStreakMetricWithDefaultPrefs)97 TEST_F(CleanExitBeaconTest, CrashStreakMetricWithDefaultPrefs) {
98   CleanExitBeacon::ResetStabilityExitedCleanlyForTesting(&prefs_);
99   TestCleanExitBeacon clean_exit_beacon(&prefs_);
100   histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 0,
101                                        1);
102 }
103 
104 // Verify that the crash streak metric is 0 when prefs are explicitly set to
105 // their defaults.
TEST_F(CleanExitBeaconTest,CrashStreakMetricWithNoCrashes)106 TEST_F(CleanExitBeaconTest, CrashStreakMetricWithNoCrashes) {
107   // The default value for kStabilityExitedCleanly is true, but defaults can
108   // change, so we explicitly set it to true here. Similarly, we explicitly set
109   // kVariationsCrashStreak to 0.
110   CleanExitBeacon::SetStabilityExitedCleanlyForTesting(&prefs_, true);
111   prefs_.SetInteger(kVariationsCrashStreak, 0);
112   TestCleanExitBeacon clean_exit_beacon(&prefs_);
113   histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 0,
114                                        1);
115 }
116 
117 // Verify that the crash streak metric is correctly recorded when there is a
118 // non-zero crash streak.
TEST_F(CleanExitBeaconTest,CrashStreakMetricWithSomeCrashes)119 TEST_F(CleanExitBeaconTest, CrashStreakMetricWithSomeCrashes) {
120   // The default value for kStabilityExitedCleanly is true, but defaults can
121   // change, so we explicitly set it to true here.
122   CleanExitBeacon::SetStabilityExitedCleanlyForTesting(&prefs_, true);
123   prefs_.SetInteger(kVariationsCrashStreak, 1);
124   TestCleanExitBeacon clean_exit_beacon(&prefs_);
125   histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 1,
126                                        1);
127 }
128 
129 // Verify that the crash streak is correctly incremented and recorded when the
130 // last Chrome session did not exit cleanly.
TEST_F(CleanExitBeaconTest,CrashIncrementsCrashStreak)131 TEST_F(CleanExitBeaconTest, CrashIncrementsCrashStreak) {
132   CleanExitBeacon::SetStabilityExitedCleanlyForTesting(&prefs_, false);
133   prefs_.SetInteger(kVariationsCrashStreak, 1);
134   TestCleanExitBeacon clean_exit_beacon(&prefs_);
135   EXPECT_EQ(prefs_.GetInteger(kVariationsCrashStreak), 2);
136   histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 2,
137                                        1);
138 }
139 
140 // Verify that the crash streak is correctly incremented and recorded when the
141 // last Chrome session did not exit cleanly and the default crash streak value
142 // is used.
TEST_F(CleanExitBeaconTest,CrashIncrementsCrashStreakWithDefaultCrashStreakPref)143 TEST_F(CleanExitBeaconTest,
144        CrashIncrementsCrashStreakWithDefaultCrashStreakPref) {
145   CleanExitBeacon::SetStabilityExitedCleanlyForTesting(&prefs_, false);
146   TestCleanExitBeacon clean_exit_beacon(&prefs_);
147   EXPECT_EQ(prefs_.GetInteger(kVariationsCrashStreak), 1);
148   histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 1,
149                                        1);
150 }
151 
152 // Verify that no attempt is made to read the beacon file when no user
153 // data dir is provided.
TEST_F(CleanExitBeaconTest,InitWithoutUserDataDir)154 TEST_F(CleanExitBeaconTest, InitWithoutUserDataDir) {
155   TestCleanExitBeacon beacon(&prefs_, base::FilePath());
156   EXPECT_TRUE(beacon.GetUserDataDirForTesting().empty());
157   EXPECT_TRUE(beacon.GetBeaconFilePathForTesting().empty());
158   histogram_tester_.ExpectTotalCount(
159       "Variations.ExtendedSafeMode.BeaconFileStateAtStartup", 0);
160 }
161 
162 INSTANTIATE_TEST_SUITE_P(
163     All,
164     BadBeaconFileTest,
165     ::testing::Values(
166         BadBeaconTestParams{
167             .test_name = "NoVariationsFile",
168             .beacon_file_exists = false,
169             .beacon_file_contents = "",
170             .beacon_file_state = BeaconFileState::kNotDeserializable},
171         BadBeaconTestParams{
172             .test_name = "EmptyVariationsFile",
173             .beacon_file_exists = true,
174             .beacon_file_contents = "",
175             .beacon_file_state = BeaconFileState::kNotDeserializable},
176         BadBeaconTestParams{
177             .test_name = "NotDictionary",
178             .beacon_file_exists = true,
179             .beacon_file_contents = "{abc123",
180             .beacon_file_state = BeaconFileState::kNotDeserializable},
181         BadBeaconTestParams{
182             .test_name = "EmptyDictionary",
183             .beacon_file_exists = true,
184             .beacon_file_contents = "{}",
185             .beacon_file_state = BeaconFileState::kMissingDictionary},
186         BadBeaconTestParams{
187             .test_name = "MissingCrashStreak",
188             .beacon_file_exists = true,
189             .beacon_file_contents =
190                 "{\"user_experience_metrics.stability.exited_cleanly\":true}",
191             .beacon_file_state = BeaconFileState::kMissingCrashStreak},
192         BadBeaconTestParams{
193             .test_name = "MissingBeacon",
194             .beacon_file_exists = true,
195             .beacon_file_contents = "{\"variations_crash_streak\":1}",
196             .beacon_file_state = BeaconFileState::kMissingBeacon}),
__anonb40812d80202(const ::testing::TestParamInfo<BadBeaconTestParams>& params) 197     [](const ::testing::TestParamInfo<BadBeaconTestParams>& params) {
198       return params.param.test_name;
199     });
200 
201 // Verify that the inability to get the beacon file's contents for a plethora of
202 // reasons (a) doesn't crash and (b) correctly records the  BeaconFileState
203 // metric.
TEST_P(BadBeaconFileTest,InitWithUnusableBeaconFile)204 TEST_P(BadBeaconFileTest, InitWithUnusableBeaconFile) {
205   BadBeaconTestParams params = GetParam();
206 
207   const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
208   if (params.beacon_file_exists) {
209     const base::FilePath temp_beacon_file_path =
210         user_data_dir_path.Append(kCleanExitBeaconFilename);
211     ASSERT_TRUE(
212         base::WriteFile(temp_beacon_file_path, params.beacon_file_contents));
213   }
214 
215   TestCleanExitBeacon beacon(&prefs_, user_data_dir_path);
216   histogram_tester_.ExpectUniqueSample(
217       "Variations.ExtendedSafeMode.BeaconFileStateAtStartup",
218       params.beacon_file_state, 1);
219 }
220 
221 // Verify that successfully reading the beacon file's contents results in
222 // correctly (a) setting the |did_previous_session_exit_cleanly_| field and (b)
223 // recording metrics when the last session exited cleanly.
TEST_F(CleanExitBeaconTest,InitWithBeaconFile)224 TEST_F(CleanExitBeaconTest, InitWithBeaconFile) {
225   const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
226   const base::FilePath temp_beacon_file_path =
227       user_data_dir_path.Append(kCleanExitBeaconFilename);
228   const int num_crashes = 2;
229   ASSERT_TRUE(base::WriteFile(
230       temp_beacon_file_path,
231       CleanExitBeacon::CreateBeaconFileContentsForTesting(
232           /*exited_cleanly=*/true, /*crash_streak=*/num_crashes)));
233 
234   TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
235   histogram_tester_.ExpectUniqueSample(
236       "Variations.ExtendedSafeMode.BeaconFileStateAtStartup",
237       BeaconFileState::kReadable, 1);
238   EXPECT_TRUE(clean_exit_beacon.exited_cleanly());
239   histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes",
240                                        num_crashes, 1);
241 }
242 
243 // Verify that successfully reading the beacon file's contents results in
244 // correctly (a) setting the |did_previous_session_exit_cleanly_| field and (b)
245 // recording metrics when the last session did not exit cleanly.
TEST_F(CleanExitBeaconTest,InitWithCrashAndBeaconFile)246 TEST_F(CleanExitBeaconTest, InitWithCrashAndBeaconFile) {
247   const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
248   const base::FilePath temp_beacon_file_path =
249       user_data_dir_path.Append(kCleanExitBeaconFilename);
250   const int last_session_num_crashes = 2;
251   ASSERT_TRUE(
252       base::WriteFile(temp_beacon_file_path,
253                       CleanExitBeacon::CreateBeaconFileContentsForTesting(
254                           /*exited_cleanly=*/false,
255                           /*crash_streak=*/last_session_num_crashes)));
256 
257   const int updated_num_crashes = last_session_num_crashes + 1;
258   TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
259   histogram_tester_.ExpectUniqueSample(
260       "Variations.ExtendedSafeMode.BeaconFileStateAtStartup",
261       BeaconFileState::kReadable, 1);
262   EXPECT_FALSE(clean_exit_beacon.exited_cleanly());
263   histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes",
264                                        updated_num_crashes, 1);
265 }
266 
TEST_F(CleanExitBeaconTest,RecordNoCrashStreakDiscrepancy)267 TEST_F(CleanExitBeaconTest, RecordNoCrashStreakDiscrepancy) {
268   const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
269   const base::FilePath temp_beacon_file_path =
270       user_data_dir_path.Append(kCleanExitBeaconFilename);
271   const int num_crashes = 2;
272   ASSERT_TRUE(
273       base::WriteFile(temp_beacon_file_path,
274                       CleanExitBeacon::CreateBeaconFileContentsForTesting(
275                           /*exited_cleanly=*/false,
276                           /*crash_streak=*/num_crashes)));
277 
278   prefs_.SetInteger(kVariationsCrashStreak, num_crashes);
279 
280   TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
281   histogram_tester_.ExpectUniqueSample(
282       "Variations.SafeMode.CrashStreakDiscrepancy", 0, 1);
283 }
284 
TEST_F(CleanExitBeaconTest,RecordCrashStreakDiscrepancy)285 TEST_F(CleanExitBeaconTest, RecordCrashStreakDiscrepancy) {
286   const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
287   const base::FilePath temp_beacon_file_path =
288       user_data_dir_path.Append(kCleanExitBeaconFilename);
289   const int num_crashes = 2;
290   ASSERT_TRUE(
291       base::WriteFile(temp_beacon_file_path,
292                       CleanExitBeacon::CreateBeaconFileContentsForTesting(
293                           /*exited_cleanly=*/false,
294                           /*crash_streak=*/num_crashes)));
295   const int discrepancy = 10;
296   prefs_.SetInteger(kVariationsCrashStreak, num_crashes + discrepancy);
297 
298   TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
299   histogram_tester_.ExpectUniqueSample(
300       "Variations.SafeMode.CrashStreakDiscrepancy", discrepancy, 1);
301 }
302 
TEST_F(CleanExitBeaconTest,WriteBeaconValueWhenNotExitingCleanly)303 TEST_F(CleanExitBeaconTest, WriteBeaconValueWhenNotExitingCleanly) {
304   const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
305   const base::FilePath beacon_file_path =
306       user_data_dir_path.Append(kCleanExitBeaconFilename);
307   ASSERT_FALSE(base::PathExists(beacon_file_path));
308 
309   TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
310   clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/false,
311                                      /*is_extended_safe_mode=*/true);
312 
313   // Verify that the beacon file exists and has well-formed contents after
314   // updating the beacon value.
315   EXPECT_TRUE(base::PathExists(beacon_file_path));
316   std::string beacon_file_contents1;
317   ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents1));
318   EXPECT_EQ(beacon_file_contents1,
319             "{\"user_experience_metrics.stability.exited_cleanly\":false,"
320             "\"variations_crash_streak\":0}");
321   // Verify that the BeaconFileWrite metric was emitted.
322   histogram_tester_.ExpectUniqueSample(
323       "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1);
324 
325   // Write the beacon value again. This is done because it is possible for
326   // WriteBeaconValue() to be called twice during startup or shutdown with the
327   // same value for |exited_cleanly|.
328   clean_exit_beacon.WriteBeaconValue(/*exited_cleanly*/ false,
329                                      /*is_extended_safe_mode=*/false);
330 
331   // Verify that the beacon file exists and has well-formed contents after
332   // updating the beacon value.
333   EXPECT_TRUE(base::PathExists(beacon_file_path));
334   std::string beacon_file_contents2;
335   ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents2));
336   EXPECT_EQ(beacon_file_contents2,
337             "{\"user_experience_metrics.stability.exited_cleanly\":false,"
338             "\"variations_crash_streak\":0}");
339   // Verify that the BeaconFileWrite metric was not emitted a second time. The
340   // beacon file should not have been written again since the beacon value did
341   // not change.
342   histogram_tester_.ExpectUniqueSample(
343       "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1);
344 }
345 
TEST_F(CleanExitBeaconTest,WriteBeaconValueWhenExitingCleanly)346 TEST_F(CleanExitBeaconTest, WriteBeaconValueWhenExitingCleanly) {
347   const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
348   const base::FilePath beacon_file_path =
349       user_data_dir_path.Append(kCleanExitBeaconFilename);
350   ASSERT_FALSE(base::PathExists(beacon_file_path));
351 
352   TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
353   clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/true,
354                                      /*is_extended_safe_mode=*/false);
355 
356   // Verify that the beacon file exists and has well-formed contents after
357   // updating the beacon value.
358   EXPECT_TRUE(base::PathExists(beacon_file_path));
359   std::string beacon_file_contents1;
360   ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents1));
361   EXPECT_EQ(beacon_file_contents1,
362             "{\"user_experience_metrics.stability.exited_cleanly\":true,"
363             "\"variations_crash_streak\":0}");
364   // Verify that the BeaconFileWrite metric was emitted.
365   histogram_tester_.ExpectUniqueSample(
366       "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1);
367 
368   // Write the beacon value again. This is done because it is possible for
369   // WriteBeaconValue() to be called twice during startup or shutdown with the
370   // same value for |exited_cleanly|.
371   clean_exit_beacon.WriteBeaconValue(/*exited_cleanly*/ true,
372                                      /*is_extended_safe_mode=*/false);
373 
374   // Verify that the beacon file exists and has well-formed contents after
375   // updating the beacon value.
376   EXPECT_TRUE(base::PathExists(beacon_file_path));
377   std::string beacon_file_contents2;
378   ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents2));
379   EXPECT_EQ(beacon_file_contents2,
380             "{\"user_experience_metrics.stability.exited_cleanly\":true,"
381             "\"variations_crash_streak\":0}");
382   // Verify that the BeaconFileWrite metric was not emitted a second time. The
383   // beacon file should not have been written again since the beacon value did
384   // not change.
385   histogram_tester_.ExpectUniqueSample(
386       "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1);
387 }
388 
389 // Verify that there's a DCHECK when attempting to write a clean beacon with
390 // |is_extended_safe_mode| set to true. When |is_extended_safe_mode| is true,
391 // the only valid value for |exited_cleanly| is false.
TEST_F(CleanExitBeaconTest,InvalidWriteBeaconValueArgsTriggerDcheck)392 TEST_F(CleanExitBeaconTest, InvalidWriteBeaconValueArgsTriggerDcheck) {
393   TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_.GetPath());
394   EXPECT_DCHECK_DEATH(
395       clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/true,
396                                          /*is_extended_safe_mode=*/true));
397 }
398 
399 #if BUILDFLAG(IS_IOS)
400 // Verify the logic for recording UMA.CleanExitBeaconConsistency3.
401 INSTANTIATE_TEST_SUITE_P(
402     All,
403     BeaconFileAndPlatformBeaconConsistencyTest,
404     ::testing::Values(
405         BeaconConsistencyTestParams{
406             .test_name = "MissingMissing",
407             .expected_consistency =
408                 CleanExitBeaconConsistency::kMissingMissing},
409         BeaconConsistencyTestParams{
410             .test_name = "MissingClean",
411             .platform_specific_beacon_value = true,
412             .expected_consistency = CleanExitBeaconConsistency::kMissingClean},
413         BeaconConsistencyTestParams{
414             .test_name = "MissingDirty",
415             .platform_specific_beacon_value = false,
416             .expected_consistency = CleanExitBeaconConsistency::kMissingDirty},
417         BeaconConsistencyTestParams{
418             .test_name = "CleanMissing",
419             .beacon_file_beacon_value = true,
420             .expected_consistency = CleanExitBeaconConsistency::kCleanMissing},
421         BeaconConsistencyTestParams{
422             .test_name = "DirtyMissing",
423             .beacon_file_beacon_value = false,
424             .expected_consistency = CleanExitBeaconConsistency::kDirtyMissing},
425         BeaconConsistencyTestParams{
426             .test_name = "CleanClean",
427             .beacon_file_beacon_value = true,
428             .platform_specific_beacon_value = true,
429             .expected_consistency = CleanExitBeaconConsistency::kCleanClean},
430         BeaconConsistencyTestParams{
431             .test_name = "CleanDirty",
432             .beacon_file_beacon_value = true,
433             .platform_specific_beacon_value = false,
434             .expected_consistency = CleanExitBeaconConsistency::kCleanDirty},
435         BeaconConsistencyTestParams{
436             .test_name = "DirtyClean",
437             .beacon_file_beacon_value = false,
438             .platform_specific_beacon_value = true,
439             .expected_consistency = CleanExitBeaconConsistency::kDirtyClean},
440         BeaconConsistencyTestParams{
441             .test_name = "DirtyDirty",
442             .beacon_file_beacon_value = false,
443             .platform_specific_beacon_value = false,
444             .expected_consistency = CleanExitBeaconConsistency::kDirtyDirty}),
__anonb40812d80302(const ::testing::TestParamInfo<BeaconConsistencyTestParams>& params) 445     [](const ::testing::TestParamInfo<BeaconConsistencyTestParams>& params) {
446       return params.param.test_name;
447     });
448 
TEST_P(BeaconFileAndPlatformBeaconConsistencyTest,BeaconConsistency)449 TEST_P(BeaconFileAndPlatformBeaconConsistencyTest, BeaconConsistency) {
450   // Verify that the beacon file is not present. Unless set below, this beacon
451   // is considered missing.
452   const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
453   const base::FilePath temp_beacon_file_path =
454       user_data_dir_path.Append(kCleanExitBeaconFilename);
455   ASSERT_FALSE(base::PathExists(temp_beacon_file_path));
456   // Clear the platform-specific beacon. Unless set below, this beacon is also
457   // considered missing.
458   CleanExitBeacon::ResetStabilityExitedCleanlyForTesting(&prefs_);
459 
460   BeaconConsistencyTestParams params = GetParam();
461   if (params.beacon_file_beacon_value) {
462     ASSERT_TRUE(base::WriteFile(
463         temp_beacon_file_path,
464         CleanExitBeacon::CreateBeaconFileContentsForTesting(
465             /*exited_cleanly=*/params.beacon_file_beacon_value.value(),
466             /*crash_streak=*/0)));
467   }
468   if (params.platform_specific_beacon_value) {
469     CleanExitBeacon::SetUserDefaultsBeacon(
470         /*exited_cleanly=*/params.platform_specific_beacon_value.value());
471   }
472 
473   TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
474   histogram_tester_.ExpectUniqueSample("UMA.CleanExitBeaconConsistency3",
475                                        params.expected_consistency, 1);
476 }
477 #endif  // BUILDFLAG(IS_IOS)
478 
479 }  // namespace
480 }  // namespace metrics
481