chromium/components/metrics/clean_exit_beacon_unittest.cc

// Copyright 2021 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/clean_exit_beacon.h"

#include <memory>
#include <optional>
#include <string>

#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/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/variations_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace metrics {
namespace {

const wchar_t kDummyWindowsRegistryKey[] =;

}  // namespace

class TestCleanExitBeacon : public CleanExitBeacon {};

class CleanExitBeaconTest : public ::testing::Test {};

struct BadBeaconTestParams {};

// Used for testing beacon files that are not well-formed, do not exist, etc.
class BadBeaconFileTest
    : public testing::WithParamInterface<BadBeaconTestParams>,
      public CleanExitBeaconTest {};

struct BeaconConsistencyTestParams {};

#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) {}

// Verify that the crash streak metric is 0 when prefs are explicitly set to
// their defaults.
TEST_F(CleanExitBeaconTest, CrashStreakMetricWithNoCrashes) {}

// Verify that the crash streak metric is correctly recorded when there is a
// non-zero crash streak.
TEST_F(CleanExitBeaconTest, CrashStreakMetricWithSomeCrashes) {}

// Verify that the crash streak is correctly incremented and recorded when the
// last Chrome session did not exit cleanly.
TEST_F(CleanExitBeaconTest, CrashIncrementsCrashStreak) {}

// 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) {}

// Verify that no attempt is made to read the beacon file when no user
// data dir is provided.
TEST_F(CleanExitBeaconTest, InitWithoutUserDataDir) {}

INSTANTIATE_TEST_SUITE_P();

// 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) {}

// 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) {}

// 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) {}

TEST_F(CleanExitBeaconTest, WriteBeaconValueWhenNotExitingCleanly) {}

TEST_F(CleanExitBeaconTest, WriteBeaconValueWhenExitingCleanly) {}

// Verify that there's a DCHECK when attempting to write a clean beacon with
// |is_extended_safe_mode| set to true. When |is_extended_safe_mode| is true,
// the only valid value for |exited_cleanly| is false.
TEST_F(CleanExitBeaconTest, InvalidWriteBeaconValueArgsTriggerDcheck) {}

#if BUILDFLAG(IS_IOS)
// Verify the logic for recording UMA.CleanExitBeaconConsistency3.
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(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_TRUE(base::WriteFile(
        temp_beacon_file_path,
        CleanExitBeacon::CreateBeaconFileContentsForTesting(
            /*exited_cleanly=*/params.beacon_file_beacon_value.value(),
            /*crash_streak=*/0)));
  }
  if (params.platform_specific_beacon_value) {
    CleanExitBeacon::SetUserDefaultsBeacon(
        /*exited_cleanly=*/params.platform_specific_beacon_value.value());
  }

  TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
  histogram_tester_.ExpectUniqueSample("UMA.CleanExitBeaconConsistency3",
                                       params.expected_consistency, 1);
}
#endif  // BUILDFLAG(IS_IOS)

}  // namespace metrics