chromium/chrome/browser/ash/policy/scheduled_task_handler/test/device_scheduled_reboot_handler_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 "chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_reboot_handler.h"

#include <memory>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/test/mock_log.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/policy/scheduled_task_handler/scheduled_task_executor_impl.h"
#include "chrome/browser/ash/policy/scheduled_task_handler/scheduled_task_util.h"
#include "chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.h"
#include "chrome/browser/ash/policy/scheduled_task_handler/test/fake_scheduled_task_executor.h"
#include "chrome/browser/ash/policy/scheduled_task_handler/test/scheduled_task_test_util.h"
#include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
#include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "components/prefs/testing_pref_service.h"
#include "components/user_manager/scoped_user_manager.h"
#include "services/device/public/cpp/test/test_wake_lock_provider.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace policy {

namespace {
constexpr char kRebootTaskTimeFieldName[] = "reboot_time";
constexpr base::TimeDelta kExternalRebootDelay = base::Seconds(100);
constexpr char kESTTimeZoneID[] = "America/New_York";
constexpr char kTestName[] = "test@test";
constexpr char kKioskName[] = "[email protected]";
}  // namespace

using ::testing::_;
#define EXPECT_ERROR_LOG(matcher)                                    \
  if (DLOG_IS_ON(ERROR)) {                                           \
    EXPECT_CALL(log_, Log(logging::LOGGING_ERROR, _, _, _, matcher)) \
        .WillOnce(testing::Return(true)); /* suppress logging */     \
  }

class DeviceScheduledRebootHandlerForTest
    : public DeviceScheduledRebootHandler {
 public:
  using DeviceScheduledRebootHandler::GetBootTimeCallback;

  template <class... Args>
  explicit DeviceScheduledRebootHandlerForTest(Args... args)
      : DeviceScheduledRebootHandler(std::forward<Args>(args)...) {}

  DeviceScheduledRebootHandlerForTest(
      const DeviceScheduledRebootHandlerForTest&) = delete;
  DeviceScheduledRebootHandlerForTest& operator=(
      const DeviceScheduledRebootHandlerForTest&) = delete;

  ~DeviceScheduledRebootHandlerForTest() override {
    TestingBrowserProcess::GetGlobal()->ShutdownBrowserPolicyConnector();
  }

  int GetRebootTimerExpirations() const { return reboot_timer_expirations_; }
  int GetPolicyChangesProcessedCount() const {
    return policy_changes_processed_;
  }

 private:
  void OnRebootTimerExpired() override {
    ++reboot_timer_expirations_;
    DeviceScheduledRebootHandler::OnRebootTimerExpired();
  }

  void OnScheduledRebootDataChanged() override {
    ++policy_changes_processed_;
    DeviceScheduledRebootHandler::OnScheduledRebootDataChanged();
  }

  // Number of calls to |OnRebootTimerExpired|.
  int reboot_timer_expirations_ = 0;

  // Number of calls to |OnScheduledRebootDataChanged|.
  int policy_changes_processed_ = 0;
};

class DeviceScheduledRebootHandlerTest : public testing::Test {
 public:
  DeviceScheduledRebootHandlerTest()
      : task_environment_(base::test::TaskEnvironment::MainThreadType::IO,
                          base::test::TaskEnvironment::TimeSource::MOCK_TIME),
        user_manager_enabler_(std::make_unique<ash::FakeChromeUserManager>()),
        prefs_(std::make_unique<TestingPrefServiceSimple>()),
        notifications_scheduler_(task_environment_.GetMockClock(),
                                 task_environment_.GetMockTickClock(),
                                 prefs_.get()),
        start_time_(task_environment_.GetMockClock()->Now()) {
    ScopedWakeLock::OverrideWakeLockProviderBinderForTesting(
        base::BindRepeating(&device::TestWakeLockProvider::BindReceiver,
                            base::Unretained(&wake_lock_provider_)));
    chromeos::PowerManagerClient::InitializeFake();
    chromeos::FakePowerManagerClient::Get()->set_tick_clock(
        task_environment_.GetMockTickClock());

    auto task_executor = std::make_unique<FakeScheduledTaskExecutor>(
        task_environment_.GetMockClock());
    scheduled_task_executor_ = task_executor.get();
    RebootNotificationsScheduler::RegisterProfilePrefs(prefs_->registry());
    device_scheduled_reboot_handler_ =
        std::make_unique<DeviceScheduledRebootHandlerForTest>(
            ash::CrosSettings::Get(), std::move(task_executor),
            &notifications_scheduler_,
            /*get_boot_time_callback=*/base::BindLambdaForTesting([this]() {
              return start_time_;
            }));
    // Set 0 delay for tests.
    device_scheduled_reboot_handler_->SetRebootDelayForTest(base::TimeDelta());
  }

  ~DeviceScheduledRebootHandlerTest() override {
    device_scheduled_reboot_handler_.reset();
    chromeos::PowerManagerClient::Shutdown();
    ScopedWakeLock::OverrideWakeLockProviderBinderForTesting(
        base::NullCallback());
  }

 protected:
  bool CheckStats(int expected_scheduled_reboots,
                  int expected_reboot_requests) {
    if (device_scheduled_reboot_handler_->GetRebootTimerExpirations() !=
        expected_scheduled_reboots) {
      LOG(ERROR)
          << "Current reboot timer expirations: "
          << device_scheduled_reboot_handler_->GetRebootTimerExpirations()
          << " Expected reboot timer expirations: "
          << expected_scheduled_reboots;
      return false;
    }

    if (chromeos::FakePowerManagerClient::Get()->num_request_restart_calls() !=
        expected_reboot_requests) {
      LOG(ERROR) << "Current reboot requests: "
                 << chromeos::FakePowerManagerClient::Get()
                        ->num_request_restart_calls()
                 << " Expected reboot requests: " << expected_reboot_requests;
      return false;
    }

    return true;
  }

  bool CheckNotificationStats(int notifications_shown, int dialogs_shown) {
    if (notifications_scheduler_.GetShowNotificationCalls() !=
        notifications_shown) {
      LOG(ERROR) << "Current notifications shown count: "
                 << notifications_scheduler_.GetShowNotificationCalls()
                 << " Expected notifications shown count: "
                 << notifications_shown;
      return false;
    }

    if (notifications_scheduler_.GetShowDialogCalls() != dialogs_shown) {
      LOG(ERROR) << "Current dialogs shown count: "
                 << notifications_scheduler_.GetShowDialogCalls()
                 << " Expected dialogs shown count: " << dialogs_shown;
      return false;
    }

    return true;
  }

  const base::TimeDelta GetRebootDelay() const {
    return (scheduled_task_executor_->GetScheduledTaskTime() -
            task_environment_.GetMockClock()->Now());
  }

  void InitWithFeatureFlag(bool enable_force_scheduled_reboots) {
    if (enable_force_scheduled_reboots) {
      scoped_feature_list_.InitWithFeatures(
          /* enabled_features */ {ash::features::kDeviceForceScheduledReboot},
          /* disabled_features */ {});
      return;
    }
    scoped_feature_list_.InitWithFeatures(
        /* enabled_features */ {},
        /* disabled_features */ {ash::features::kDeviceForceScheduledReboot});
  }

  ash::FakeChromeUserManager* GetFakeUserManager() {
    return static_cast<ash::FakeChromeUserManager*>(
        user_manager::UserManager::Get());
  }

  base::test::TaskEnvironment task_environment_;
  user_manager::ScopedUserManager user_manager_enabler_;
  raw_ptr<FakeScheduledTaskExecutor, DanglingUntriaged>
      scheduled_task_executor_;
  std::unique_ptr<DeviceScheduledRebootHandlerForTest>
      device_scheduled_reboot_handler_;
  ash::ScopedTestingCrosSettings cros_settings_;
  std::unique_ptr<TestingPrefServiceSimple> prefs_;
  device::TestWakeLockProvider wake_lock_provider_;
  FakeRebootNotificationsScheduler notifications_scheduler_;
  base::test::ScopedFeatureList scoped_feature_list_;
  const base::Time start_time_;
};

TEST_F(DeviceScheduledRebootHandlerTest,
       CheckIfDailyRebootIsScheduledForKiosk) {
  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);
  auto* user_manager = GetFakeUserManager();
  auto* user =
      user_manager->AddKioskAppUser(AccountId::FromUserEmail(kKioskName));
  user_manager->UserLoggedIn(user->GetAccountId(), user->username_hash(),
                             /*browser_restart=*/false, /*is_child=*/false);

  // Calculate time from one hour from now and set the reboot policy to
  // happen daily at that time.
  base::TimeDelta delay_from_now = base::Hours(1);
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      scheduled_task_executor_->GetTimeZone(),
      scheduled_task_executor_->GetCurrentTime(), delay_from_now,
      ScheduledTaskExecutor::Frequency::kDaily, kRebootTaskTimeFieldName);

  // Set a new scheduled reboot, fast forward to right before the
  // expected reboot and then check if an reboot is not scheduled.
  const base::TimeDelta small_delay = base::Milliseconds(1);
  cros_settings_.device_settings()->Set(
      ash::kDeviceScheduledReboot,
      std::move(policy_and_next_reboot_time.first));
  int expected_scheduled_reboots = 0;
  int expected_reboot_requests = 0;
  task_environment_.FastForwardBy(delay_from_now - small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the expected reboot time and then check if the
  // reboot is scheduled.
  expected_scheduled_reboots += 1;
  expected_reboot_requests += 1;
  task_environment_.FastForwardBy(small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // After the reboot, the current handler is destroyed and the new one is
  // created which will schedule reboot for the next day. Check that current
  // handler is not scheduling any more reboots.
  task_environment_.FastForwardBy(base::Days(1));
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));
}

TEST_F(DeviceScheduledRebootHandlerTest,
       CheckIfDailyRebootIsScheduledForNonKiosk) {
  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);
  auto* user_manager = GetFakeUserManager();
  auto* user = user_manager->AddUser(AccountId::FromUserEmail(kTestName));
  user_manager->UserLoggedIn(user->GetAccountId(), user->username_hash(),
                             /*browser_restart=*/false, /*is_child=*/false);

  // Calculate time from one hour from now and set the reboot policy to
  // happen daily at that time.
  base::TimeDelta delay_from_now = base::Hours(1);
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      scheduled_task_executor_->GetTimeZone(),
      scheduled_task_executor_->GetCurrentTime(), delay_from_now,
      ScheduledTaskExecutor::Frequency::kDaily, kRebootTaskTimeFieldName);

  // Set a new scheduled reboot, fast forward to right before the
  // expected reboot and then check if an reboot is not scheduled.
  const base::TimeDelta small_delay = base::Milliseconds(1);
  cros_settings_.device_settings()->Set(
      ash::kDeviceScheduledReboot,
      std::move(policy_and_next_reboot_time.first));
  int expected_scheduled_reboots = 0;
  int expected_reboot_requests = 0;
  task_environment_.FastForwardBy(delay_from_now - small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the expected reboot time and then check if the
  // reboot is scheduled, but not executed since we are not in the kiosk mode.
  expected_scheduled_reboots += 1;
  task_environment_.FastForwardBy(small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the next day and then check if the reboot is scheduled
  // again.
  expected_scheduled_reboots += 1;
  task_environment_.FastForwardBy(base::Days(1));
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Switch to the kiosk mode, fast forward to the next day and check that the
  // reboot is scheduled and executed.
  auto* kiosk_user =
      user_manager->AddKioskAppUser(AccountId::FromUserEmail(kKioskName));
  user_manager->UserLoggedIn(kiosk_user->GetAccountId(),
                             kiosk_user->username_hash(),
                             /*browser_restart=*/false, /*is_child=*/false);
  user_manager->SwitchActiveUser(kiosk_user->GetAccountId());
  expected_scheduled_reboots += 1;
  expected_reboot_requests += 1;
  task_environment_.FastForwardBy(base::Days(1));
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));
}

TEST_F(DeviceScheduledRebootHandlerTest,
       CheckIfWeeklyUpdateCheckIsScheduledForKiosk) {
  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);
  auto* user_manager = GetFakeUserManager();
  auto* user = user_manager->AddUser(AccountId::FromUserEmail(kTestName));
  user_manager->UserLoggedIn(user->GetAccountId(), user->username_hash(),
                             /*browser_restart=*/false, /*is_child=*/false);
  // Set the first reboot to happen 49 hours from now (i.e. 1 hour from 2
  // days from now) and then weekly after.
  base::TimeDelta delay_from_now = base::Hours(49);
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      scheduled_task_executor_->GetTimeZone(),
      scheduled_task_executor_->GetCurrentTime(), delay_from_now,
      ScheduledTaskExecutor::Frequency::kWeekly, kRebootTaskTimeFieldName);

  // Set a new scheduled reboot setting, fast forward to right before the
  // expected reboot and then check if a reboot is not scheduled.
  int expected_scheduled_reboots = 0;
  int expected_reboot_requests = 0;
  const base::TimeDelta small_delay = base::Milliseconds(1);
  cros_settings_.device_settings()->Set(
      ash::kDeviceScheduledReboot,
      std::move(policy_and_next_reboot_time.first));
  task_environment_.FastForwardBy(delay_from_now - small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the expected reboot time and then check if the reboot
  // is scheduled, but not executed, since we are not in the kiosk mode.
  expected_scheduled_reboots += 1;
  task_environment_.FastForwardBy(small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Switch to the kiosk mode, fast forward to the next week and check that the
  // reboot is scheduled and executed.
  auto* kiosk_user =
      user_manager->AddKioskAppUser(AccountId::FromUserEmail(kKioskName));
  user_manager->UserLoggedIn(kiosk_user->GetAccountId(),
                             kiosk_user->username_hash(),
                             /*browser_restart=*/false, /*is_child=*/false);
  user_manager->SwitchActiveUser(kiosk_user->GetAccountId());
  expected_scheduled_reboots += 1;
  expected_reboot_requests += 1;
  task_environment_.FastForwardBy(base::Days(7));
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));
}

TEST_F(DeviceScheduledRebootHandlerTest,
       CheckIfMonthlyRebootIsScheduledForKiosk) {
  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);
  auto* user_manager = GetFakeUserManager();
  auto* user = user_manager->AddUser(AccountId::FromUserEmail(kTestName));
  user_manager->UserLoggedIn(user->GetAccountId(), user->username_hash(),
                             /*browser_restart=*/false, /*is_child=*/false);

  // Set the first reboot to happen 1 hour from now.
  base::TimeDelta delay_from_now = base::Hours(1);
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      scheduled_task_executor_->GetTimeZone(),
      scheduled_task_executor_->GetCurrentTime(), delay_from_now,
      ScheduledTaskExecutor::Frequency::kMonthly, kRebootTaskTimeFieldName);
  auto scheduled_reboot_data = scheduled_task_util::ParseScheduledTask(
      policy_and_next_reboot_time.first, kRebootTaskTimeFieldName);
  ASSERT_TRUE(scheduled_reboot_data);
  ASSERT_TRUE(scheduled_reboot_data->day_of_month);
  auto first_reboot_icu_time = std::move(policy_and_next_reboot_time.second);

  // Set a new scheduled reboot setting, fast forward to right before the
  // expected reboot and then check if a reboot is not scheduled.
  int expected_scheduled_reboots = 0;
  int expected_reboot_requests = 0;
  const base::TimeDelta small_delay = base::Milliseconds(1);
  cros_settings_.device_settings()->Set(
      ash::kDeviceScheduledReboot,
      std::move(policy_and_next_reboot_time.first));
  task_environment_.FastForwardBy(delay_from_now - small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the expected reboot time and then check if the reboot
  // is scheduled, but not executed since we are not in the kiosk mode.
  expected_scheduled_reboots += 1;
  task_environment_.FastForwardBy(small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // The next reboot should happen at the same day of month next month. Switch
  // to the kiosk mode and verify the reboot is executed.
  auto* kiosk_user =
      user_manager->AddKioskAppUser(AccountId::FromUserEmail(kKioskName));
  user_manager->UserLoggedIn(kiosk_user->GetAccountId(),
                             kiosk_user->username_hash(),
                             /*browser_restart=*/false, /*is_child=*/false);
  user_manager->SwitchActiveUser(kiosk_user->GetAccountId());
  expected_scheduled_reboots += 1;
  expected_reboot_requests += 1;
  EXPECT_TRUE(scheduled_task_test_util::AdvanceTimeAndSetDayOfMonth(
      scheduled_reboot_data->day_of_month.value(),
      first_reboot_icu_time.get()));
  base::Time second_reboot_time =
      scheduled_task_test_util::IcuToBaseTime(*first_reboot_icu_time);
  std::optional<base::TimeDelta> second_reboot_delay =
      second_reboot_time - scheduled_task_executor_->GetCurrentTime();
  ASSERT_TRUE(second_reboot_delay.has_value());
  task_environment_.FastForwardBy(second_reboot_delay.value());
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));
}

TEST_F(DeviceScheduledRebootHandlerTest,
       CheckIfDailyRebootIsScheduledWithExternalDelay) {
  // Login user and disable kDeviceScheduledReboot flag. The reboot should not
  // occur.
  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);
  auto* user_manager = GetFakeUserManager();
  auto* user = user_manager->AddUser(AccountId::FromUserEmail(kTestName));
  user_manager->UserLoggedIn(user->GetAccountId(), user->username_hash(),
                             /*browser_restart=*/false, /*is_child=*/false);
  device_scheduled_reboot_handler_->SetRebootDelayForTest(kExternalRebootDelay);

  // Calculate time from one hour from now and set the reboot policy to
  // happen daily at that time.
  base::TimeDelta delay_from_now = base::Hours(1);
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      scheduled_task_executor_->GetTimeZone(),
      scheduled_task_executor_->GetCurrentTime(), delay_from_now,
      ScheduledTaskExecutor::Frequency::kDaily, kRebootTaskTimeFieldName);

  // Set a new scheduled reboot, fast forward to right before the
  // expected reboot and then check if an reboot is not scheduled.
  const base::TimeDelta small_delay = base::Milliseconds(1);
  cros_settings_.device_settings()->Set(
      ash::kDeviceScheduledReboot,
      std::move(policy_and_next_reboot_time.first));
  int expected_scheduled_reboots = 0;
  int expected_reboot_requests = 0;

  // Verify that final delay is equal to delay_from_now + external_delay.
  base::TimeDelta final_delay = GetRebootDelay();
  EXPECT_EQ(final_delay, delay_from_now + kExternalRebootDelay);
  task_environment_.FastForwardBy(final_delay - small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the expected reboot time and then check if the
  // reboot is scheduled, but not executed since we are not in the kiosk mode.
  expected_scheduled_reboots += 1;
  task_environment_.FastForwardBy(small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the next day and then check if the reboot is scheduled
  // again.
  expected_scheduled_reboots += 1;
  task_environment_.FastForwardBy(base::Days(1));
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));
}

TEST_F(DeviceScheduledRebootHandlerTest,
       CheckIfDailyRebootIsScheduledForLoginScreen) {
  InitWithFeatureFlag(true /* enable_force_scheduled_reboots */);

  // Set device uptime to 10 minutes and schedule reboot in 30 minutes. Apply
  // grace time - reboot should not occur.
  task_environment_.FastForwardBy(base::Minutes(10));
  base::TimeDelta delay_from_now = base::Minutes(30);
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      scheduled_task_executor_->GetTimeZone(),
      scheduled_task_executor_->GetCurrentTime(), delay_from_now,
      ScheduledTaskExecutor::Frequency::kDaily, kRebootTaskTimeFieldName);
  int expected_scheduled_reboots = 0;
  int expected_reboot_requests = 0;
  // Fast forward without an actual time so that the timer task is triggered.
  task_environment_.FastForwardBy(base::TimeDelta());
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Set a new scheduled reboot, fast forward to right before the
  // expected reboot and then verify reboot timer has not yet expired.
  const base::TimeDelta small_delay = base::Milliseconds(1);
  cros_settings_.device_settings()->Set(
      ash::kDeviceScheduledReboot,
      std::move(policy_and_next_reboot_time.first));
  task_environment_.FastForwardBy(delay_from_now - small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the expected reboot time and then check if the
  // reboot timer has expired and the reboot is not executed.
  expected_scheduled_reboots += 1;
  task_environment_.FastForwardBy(small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the next day at the same time and verify reboot is
  // executed.
  expected_scheduled_reboots += 1;
  expected_reboot_requests += 1;
  task_environment_.FastForwardBy(base::Days(1));
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // After the reboot, the current handler is destroyed and the new one is
  // created which will schedule reboot for the next day. Check that current
  // handler is not scheduling any more reboots.
  task_environment_.FastForwardBy(base::Days(1));
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));
}

TEST_F(DeviceScheduledRebootHandlerTest,
       VerifyLoginScreenRebootIsGuardedByFeatureFlag) {
  // Disable the feature. Reboot should not occur.
  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);

  // Schedule reboot in 30 minutes. Reboot should not occur because the flag is
  // disabled.
  base::TimeDelta delay_from_now = base::Minutes(30);
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      scheduled_task_executor_->GetTimeZone(),
      scheduled_task_executor_->GetCurrentTime(), delay_from_now,
      ScheduledTaskExecutor::Frequency::kDaily, kRebootTaskTimeFieldName);

  // Set a new scheduled reboot, fast forward to right before the
  // expected reboot and then verify reboot timer has not yet expired.
  const base::TimeDelta small_delay = base::Milliseconds(1);
  cros_settings_.device_settings()->Set(
      ash::kDeviceScheduledReboot,
      std::move(policy_and_next_reboot_time.first));
  int expected_scheduled_reboots = 0;
  int expected_reboot_requests = 0;
  task_environment_.FastForwardBy(delay_from_now - small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the expected reboot time and then check if the
  // reboot timer has expired and the reboot is not executed.
  expected_scheduled_reboots += 1;
  task_environment_.FastForwardBy(small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the next day and then check if the reboot is scheduled
  // again, but not executed.
  expected_scheduled_reboots += 1;
  task_environment_.FastForwardBy(base::Days(1));
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));
}

TEST_F(DeviceScheduledRebootHandlerTest, EnableForceRebootFeatureInKiosk) {
  InitWithFeatureFlag(true /* enable_force_scheduled_reboots */);

  // Set device uptime to 10 minutes and enable kiosk mode. We don't apply grace
  // period to kiosks, so reboot should occur.
  task_environment_.FastForwardBy(base::Minutes(10));
  auto* user_manager = GetFakeUserManager();
  auto* user =
      user_manager->AddKioskAppUser(AccountId::FromUserEmail(kKioskName));
  user_manager->UserLoggedIn(user->GetAccountId(), user->username_hash(),
                             /*browser_restart=*/false, /*is_child=*/false);

  // Calculate time 30 minutes from now and set the reboot policy to
  // happen daily at that time.
  base::TimeDelta delay_from_now = base::Minutes(30);
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      scheduled_task_executor_->GetTimeZone(),
      scheduled_task_executor_->GetCurrentTime(), delay_from_now,
      ScheduledTaskExecutor::Frequency::kDaily, kRebootTaskTimeFieldName);

  // Set a new scheduled reboot, fast forward to right before the
  // expected reboot and and then verify reboot timer has not yet expired.
  const base::TimeDelta small_delay = base::Milliseconds(1);
  cros_settings_.device_settings()->Set(
      ash::kDeviceScheduledReboot,
      std::move(policy_and_next_reboot_time.first));
  int expected_scheduled_reboots = 0;
  int expected_reboot_requests = 0;
  task_environment_.FastForwardBy(delay_from_now - small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the expected reboot time and then check if the
  // reboot timer has expired and the reboot is executed.
  expected_scheduled_reboots += 1;
  expected_reboot_requests += 1;
  task_environment_.FastForwardBy(small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));
}

TEST_F(DeviceScheduledRebootHandlerTest,
       EnableForceRebootFeatureNonKioskSession) {
  InitWithFeatureFlag(true /* enable_force_scheduled_reboots */);
  auto* user_manager = GetFakeUserManager();
  auto* user = user_manager->AddUser(AccountId::FromUserEmail(kTestName));
  user_manager->UserLoggedIn(user->GetAccountId(), user->username_hash(),
                             /*browser_restart=*/false, /*is_child=*/false);
  EXPECT_FALSE(prefs_->GetBoolean(ash::prefs::kShowPostRebootNotification));

  // Set device uptime to 10 minutes and schedule reboot in 30 minutes. Apply
  // grace time - reboot should not occur.
  task_environment_.FastForwardBy(base::Minutes(10));
  base::TimeDelta delay_from_now = base::Minutes(30);
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      scheduled_task_executor_->GetTimeZone(),
      scheduled_task_executor_->GetCurrentTime(), delay_from_now,
      ScheduledTaskExecutor::Frequency::kDaily, kRebootTaskTimeFieldName);

  // Set a new scheduled reboot, fast forward to right before the
  // expected reboot and then check if the reboot timer has not yet expired.
  // Verify notifications are not shown.
  const base::TimeDelta small_delay = base::Milliseconds(1);
  cros_settings_.device_settings()->Set(
      ash::kDeviceScheduledReboot,
      std::move(policy_and_next_reboot_time.first));
  int expected_scheduled_reboots = 0;
  int expected_reboot_requests = 0;
  int expected_notification_count = 0;
  int expected_dialog_count = 0;
  task_environment_.FastForwardBy(delay_from_now - small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));
  EXPECT_TRUE(CheckNotificationStats(expected_notification_count,
                                     expected_dialog_count));

  // Fast forward to the expected reboot time and then check if the
  // reboot timer has expred but the reboot is not executed.
  expected_scheduled_reboots += 1;
  task_environment_.FastForwardBy(small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Fast forward to the next day at the same time and verify reboot is executed
  // and notifications are shown.
  expected_scheduled_reboots += 1;
  expected_reboot_requests += 1;
  expected_notification_count += 1;
  expected_dialog_count += 1;
  task_environment_.FastForwardBy(base::Days(1));
  EXPECT_TRUE(CheckNotificationStats(expected_notification_count,
                                     expected_dialog_count));
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Verify post reboot notification flag is set.
  EXPECT_TRUE(prefs_->GetBoolean(ash::prefs::kShowPostRebootNotification));
}

TEST_F(DeviceScheduledRebootHandlerTest, SimulateNotificationButtonClick) {
  InitWithFeatureFlag(true /* enable_force_scheduled_reboots */);
  auto* user_manager = GetFakeUserManager();
  auto* user = user_manager->AddUser(AccountId::FromUserEmail(kTestName));
  user_manager->UserLoggedIn(user->GetAccountId(), user->username_hash(),
                             /*browser_restart=*/false, /*is_child=*/false);

  /// Schedule reboot to happen in 3 hours.
  base::TimeDelta delay_from_now = base::Hours(3);
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      scheduled_task_executor_->GetTimeZone(),
      scheduled_task_executor_->GetCurrentTime(), delay_from_now,
      ScheduledTaskExecutor::Frequency::kDaily, kRebootTaskTimeFieldName);

  // Set a new scheduled reboot, fast forward to 5 minutes before the
  // expected reboot and then check that notification are shown, but the reboot
  // timer has not yet expired.
  const base::TimeDelta small_delay = base::Minutes(5);
  cros_settings_.device_settings()->Set(
      ash::kDeviceScheduledReboot,
      std::move(policy_and_next_reboot_time.first));
  int expected_scheduled_reboots = 0;
  int expected_reboot_requests = 0;
  int expected_notification_count = 1;
  int expected_dialog_count = 1;
  task_environment_.FastForwardBy(delay_from_now - small_delay);
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));
  EXPECT_TRUE(CheckNotificationStats(expected_notification_count,
                                     expected_dialog_count));

  // Simulate reboot button click on the notification. This should execute the
  // reboot.
  notifications_scheduler_.SimulateRebootButtonClick();
  expected_reboot_requests += 1;
  EXPECT_TRUE(CheckStats(expected_scheduled_reboots, expected_reboot_requests));

  // Verify post reboot notification flag is not set.
  EXPECT_FALSE(prefs_->GetBoolean(ash::prefs::kShowPostRebootNotification));
}

class ScheduledRebootTimerFailureTest : public testing::Test {
 public:
  ScheduledRebootTimerFailureTest()
      : task_environment_(base::test::TaskEnvironment::MainThreadType::IO,
                          base::test::TaskEnvironment::TimeSource::MOCK_TIME),
        notifications_scheduler_(task_environment_.GetMockClock(),
                                 task_environment_.GetMockTickClock(),
                                 nullptr),
        start_time_(task_environment_.GetMockClock()->Now()) {
    ScopedWakeLock::OverrideWakeLockProviderBinderForTesting(
        base::BindRepeating(&device::TestWakeLockProvider::BindReceiver,
                            base::Unretained(&wake_lock_provider_)));
    chromeos::PowerManagerClient::InitializeFake();
    chromeos::FakePowerManagerClient::Get()->set_tick_clock(
        task_environment_.GetMockTickClock());
    auto task_executor =
        std::make_unique<ScheduledTaskExecutorImpl>("test_tag");
    scheduled_task_executor_ = task_executor.get();
    device_scheduled_reboot_handler_ =
        std::make_unique<DeviceScheduledRebootHandlerForTest>(
            ash::CrosSettings::Get(), std::move(task_executor),
            &notifications_scheduler_,
            /*get_boot_time_callback=*/base::BindLambdaForTesting([this]() {
              return start_time_;
            }));
  }

  ~ScheduledRebootTimerFailureTest() override {
    device_scheduled_reboot_handler_.reset();
    chromeos::PowerManagerClient::Shutdown();
    ScopedWakeLock::OverrideWakeLockProviderBinderForTesting(
        base::NullCallback());
  }

 protected:
  base::test::TaskEnvironment task_environment_;
  raw_ptr<ScheduledTaskExecutorImpl, DanglingUntriaged>
      scheduled_task_executor_;
  std::unique_ptr<DeviceScheduledRebootHandlerForTest>
      device_scheduled_reboot_handler_;
  ash::ScopedTestingCrosSettings cros_settings_;
  device::TestWakeLockProvider wake_lock_provider_;
  FakeRebootNotificationsScheduler notifications_scheduler_;
  base::test::MockLog log_;
  const base::Time start_time_;
};

TEST_F(ScheduledRebootTimerFailureTest, SimulateTimerStartFailure) {
  // Schedule reboot in 30 minutes.
  base::TimeDelta delay_from_now = base::Minutes(30);
  auto time_zone_ = base::WrapUnique(icu::TimeZone::createTimeZone(
      icu::UnicodeString::fromUTF8(kESTTimeZoneID)));
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      *time_zone_.get(), task_environment_.GetMockClock()->Now(),
      delay_from_now, ScheduledTaskExecutor::Frequency::kDaily,
      kRebootTaskTimeFieldName);

  // Simulate timer creation failure.
  auto scoped_timer_failure =
      chromeos::NativeTimer::ScopedFailureSimulatorForTesting();

  // Verify timer start failure once the policy is set. Notification scheduler
  // should close all pending notifications and reset state.
  EXPECT_ERROR_LOG(testing::HasSubstr("Failed to start reboot timer"));
  log_.StartCapturingLogs();
  cros_settings_.device_settings()->Set(
      ash::kDeviceScheduledReboot,
      std::move(policy_and_next_reboot_time.first));

  // Verify that the notifications were not shown.
  EXPECT_EQ(notifications_scheduler_.GetShowNotificationCalls(), 0);
  EXPECT_EQ(notifications_scheduler_.GetShowDialogCalls(), 0);
  EXPECT_EQ(notifications_scheduler_.GetCloseNotificationCalls(), 0);
  // Verify that the state is reset.
  EXPECT_EQ(device_scheduled_reboot_handler_->GetScheduledRebootDataForTest(),
            std::nullopt);
  EXPECT_EQ(device_scheduled_reboot_handler_->IsRebootSkippedForTest(), false);
}

class ScheduledRebootDelayedServiceTest : public testing::Test {
 public:
  ScheduledRebootDelayedServiceTest()
      : task_environment_(base::test::TaskEnvironment::MainThreadType::IO,
                          base::test::TaskEnvironment::TimeSource::MOCK_TIME),
        notifications_scheduler_(task_environment_.GetMockClock(),
                                 task_environment_.GetMockTickClock(),
                                 nullptr),
        start_time_(task_environment_.GetMockClock()->Now()) {
    ScopedWakeLock::OverrideWakeLockProviderBinderForTesting(
        base::BindRepeating(&device::TestWakeLockProvider::BindReceiver,
                            base::Unretained(&wake_lock_provider_)));
    chromeos::PowerManagerClient::InitializeFake();
    chromeos::FakePowerManagerClient::Get()->SetServiceAvailability(
        /*availability=*/std::nullopt);
    auto task_executor = std::make_unique<FakeScheduledTaskExecutor>(
        task_environment_.GetMockClock());
    scheduled_task_executor_ = task_executor.get();
    device_scheduled_reboot_handler_ =
        std::make_unique<DeviceScheduledRebootHandlerForTest>(
            ash::CrosSettings::Get(), std::move(task_executor),
            &notifications_scheduler_,
            /*get_boot_time_callback=*/base::BindLambdaForTesting([this]() {
              return start_time_;
            }));
  }

  ~ScheduledRebootDelayedServiceTest() override {
    device_scheduled_reboot_handler_.reset();
    chromeos::PowerManagerClient::Shutdown();
  }

 protected:
  base::test::TaskEnvironment task_environment_;
  raw_ptr<FakeScheduledTaskExecutor, DanglingUntriaged>
      scheduled_task_executor_;
  std::unique_ptr<DeviceScheduledRebootHandlerForTest>
      device_scheduled_reboot_handler_;
  ash::ScopedTestingCrosSettings cros_settings_;
  FakeRebootNotificationsScheduler notifications_scheduler_;
  device::TestWakeLockProvider wake_lock_provider_;
  const base::Time start_time_;
};

TEST_F(ScheduledRebootDelayedServiceTest, SimulateServiceIsAvailableLaterTest) {
  int expected_policy_processed_count = 0;
  EXPECT_EQ(device_scheduled_reboot_handler_->GetPolicyChangesProcessedCount(),
            expected_policy_processed_count);
  chromeos::FakePowerManagerClient::Get()->SetServiceAvailability(
      /*availability=*/true);
  expected_policy_processed_count += 1;
  EXPECT_EQ(device_scheduled_reboot_handler_->GetPolicyChangesProcessedCount(),
            expected_policy_processed_count);
}

TEST_F(ScheduledRebootDelayedServiceTest,
       SimulateServiceStopsBeingAvailableTest) {
  // Schedule reboot in 30 minutes.
  base::TimeDelta delay_from_now = base::Minutes(30);
  auto time_zone_ = base::WrapUnique(icu::TimeZone::createTimeZone(
      icu::UnicodeString::fromUTF8(kESTTimeZoneID)));
  auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
      *time_zone_.get(), task_environment_.GetMockClock()->Now(),
      delay_from_now, ScheduledTaskExecutor::Frequency::kDaily,
      kRebootTaskTimeFieldName);
  int expected_policy_processed_count =
      device_scheduled_reboot_handler_->GetPolicyChangesProcessedCount();

  // Notify that the service is available and expect processing policy.
  chromeos::FakePowerManagerClient::Get()->SetServiceAvailability(
      /*availability=*/true);
  expected_policy_processed_count += 1;
  EXPECT_EQ(device_scheduled_reboot_handler_->GetPolicyChangesProcessedCount(),
            expected_policy_processed_count);

  // Notify that the service stopped being available and verify that the state
  // is reset and that the policy is not processed.
  chromeos::FakePowerManagerClient::Get()->SetServiceAvailability(
      /*availability=*/false);
  EXPECT_EQ(device_scheduled_reboot_handler_->GetScheduledRebootDataForTest(),
            std::nullopt);
  EXPECT_EQ(device_scheduled_reboot_handler_->IsRebootSkippedForTest(), false);
  EXPECT_EQ(device_scheduled_reboot_handler_->GetPolicyChangesProcessedCount(),
            expected_policy_processed_count);
}
}  // namespace policy