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

#include <memory>
#include <optional>

#include "ash/constants/ash_switches.h"
#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_command_line.h"
#include "base/time/time.h"
#include "base/time/time_delta_from_string.h"
#include "chrome/browser/ash/policy/scheduled_task_handler/scheduled_task_executor.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/icu/source/common/unicode/unistr.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"

namespace policy {

namespace {

constexpr base::TimeDelta kDefaultGracePeriod = base::Hours(1);

base::Time TimeFromUtcString(const char* time) {
  base::Time delayult;
  bool success = base::Time::FromUTCString(time, &delayult);
  CHECK(success);
  return delayult;
}

std::unique_ptr<icu::TimeZone> GetUtcTimeZone() {
  return base::WrapUnique(
      icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8("UTC")));
}

}  // namespace

using scheduled_task_util::CalculateNextScheduledTaskTimerDelay;

TEST(ScheduledTaskUtilTest, DailyTaskShouldBeScheduledInSameDay) {
  ScheduledTaskExecutor::ScheduledTaskData data;
  data.frequency = ScheduledTaskExecutor::Frequency::kDaily;
  data.hour = 12;
  data.minute = 52;

  std::optional<base::TimeDelta> delay = CalculateNextScheduledTaskTimerDelay(
      data, TimeFromUtcString("Jan 3 2021, 09:33"), *GetUtcTimeZone());

  ASSERT_TRUE(delay.has_value());
  EXPECT_EQ(delay.value(), base::TimeDeltaFromString("3h19m"));
}

TEST(ScheduledTaskUtilTest, TaskShouldBeDelayedIfTimesMatch) {
  ScheduledTaskExecutor::ScheduledTaskData data;
  data.frequency = ScheduledTaskExecutor::Frequency::kDaily;
  data.hour = 12;
  data.minute = 52;

  std::optional<base::TimeDelta> delay = CalculateNextScheduledTaskTimerDelay(
      data, TimeFromUtcString("Jan 3 2021, 12:52"), *GetUtcTimeZone());

  ASSERT_TRUE(delay.has_value());
  EXPECT_EQ(delay.value(), base::TimeDeltaFromString("24h"));
}

TEST(ScheduledTaskUtilTest, DailyTaskShouldBeScheduledNextDay) {
  ScheduledTaskExecutor::ScheduledTaskData data;
  data.frequency = ScheduledTaskExecutor::Frequency::kDaily;
  data.hour = 8;
  data.minute = 52;

  std::optional<base::TimeDelta> delay = CalculateNextScheduledTaskTimerDelay(
      data, TimeFromUtcString("Jan 3 2021, 09:33"), *GetUtcTimeZone());

  ASSERT_TRUE(delay.has_value());
  EXPECT_EQ(delay.value(), base::TimeDeltaFromString("23h19m"));
}

TEST(ScheduledTaskUtilTest,
     DailyTaskShouldBeScheduledNextDayAcrossMonthBoundary) {
  ScheduledTaskExecutor::ScheduledTaskData data;
  data.frequency = ScheduledTaskExecutor::Frequency::kDaily;
  data.hour = 8;
  data.minute = 52;

  std::optional<base::TimeDelta> delay = CalculateNextScheduledTaskTimerDelay(
      data, TimeFromUtcString("Jan 31 2021, 09:33"), *GetUtcTimeZone());

  ASSERT_TRUE(delay.has_value());
  EXPECT_EQ(delay.value(), base::Hours(23) + base::Minutes(19));
}

TEST(ScheduledTaskUtilTest, DailyTaskShouldBeScheduledNextDayBeforeLeapDay) {
  ScheduledTaskExecutor::ScheduledTaskData data;
  data.frequency = ScheduledTaskExecutor::Frequency::kDaily;
  data.hour = 8;
  data.minute = 52;

  const base::Time current_time = TimeFromUtcString("Feb 28 1964, 09:33");

  const std::optional<base::TimeDelta> delay =
      CalculateNextScheduledTaskTimerDelay(data, current_time,
                                           *GetUtcTimeZone());

  ASSERT_TRUE(delay.has_value());
  EXPECT_EQ(delay.value(), base::Hours(23) + base::Minutes(19));
}

TEST(ScheduledTaskUtilTest, DailyTaskShouldBeScheduledNextDayOnLeapDay) {
  ScheduledTaskExecutor::ScheduledTaskData data;
  data.frequency = ScheduledTaskExecutor::Frequency::kDaily;
  data.hour = 8;
  data.minute = 52;

  const base::Time current_time = TimeFromUtcString("Feb 29 1964, 09:33");

  const std::optional<base::TimeDelta> delay =
      CalculateNextScheduledTaskTimerDelay(data, current_time,
                                           *GetUtcTimeZone());

  ASSERT_TRUE(delay.has_value());
  EXPECT_EQ(delay.value(), base::TimeDeltaFromString("23h19m"));
}

TEST(ScheduledTaskUtilTest, WeeklyTaskShouldBeScheduled) {
  ScheduledTaskExecutor::ScheduledTaskData data;
  data.frequency = ScheduledTaskExecutor::Frequency::kWeekly;
  data.hour = 11;
  data.minute = 52;
  data.day_of_week = UCAL_TUESDAY;

  std::optional<base::TimeDelta> delay = CalculateNextScheduledTaskTimerDelay(
      data, TimeFromUtcString("Sunday Jan 3 2021, 09:33"), *GetUtcTimeZone());

  ASSERT_TRUE(delay.has_value());
  EXPECT_EQ(delay.value(), base::TimeDeltaFromString("50h19m"));  // 2d2h19m
}

TEST(ScheduledTaskUtilTest, MonthlyTaskShouldBeScheduled) {
  ScheduledTaskExecutor::ScheduledTaskData data;
  data.frequency = ScheduledTaskExecutor::Frequency::kMonthly;
  data.hour = 11;
  data.minute = 52;
  data.day_of_month = 23;

  std::optional<base::TimeDelta> delay = CalculateNextScheduledTaskTimerDelay(
      data, TimeFromUtcString("Jan 3 2021, 09:33"), *GetUtcTimeZone());

  ASSERT_TRUE(delay.has_value());
  EXPECT_EQ(delay.value(), base::TimeDeltaFromString("482h19m"));  // 20d2h19m
}

TEST(ScheduledTaskUtilTest, MonthlyTaskShouldBeScheduledForNextMonth) {
  ScheduledTaskExecutor::ScheduledTaskData data;
  data.frequency = ScheduledTaskExecutor::Frequency::kMonthly;
  data.hour = 11;
  data.minute = 52;
  data.day_of_month = 23;

  std::optional<base::TimeDelta> delay = CalculateNextScheduledTaskTimerDelay(
      data, TimeFromUtcString("Jan 31 2021, 09:33"), *GetUtcTimeZone());

  ASSERT_TRUE(delay.has_value());
  EXPECT_EQ(delay.value(), base::TimeDeltaFromString("554h19m"));  // 23d2h19m
}

TEST(ScheduledTaskUtilTest,
     MonthlyTaskShouldBeScheduledForEndOfMonthIfMonthShorter) {
  ScheduledTaskExecutor::ScheduledTaskData data;
  data.frequency = ScheduledTaskExecutor::Frequency::kMonthly;
  data.hour = 8;
  data.minute = 52;
  data.day_of_month = 31;

  std::optional<base::TimeDelta> delay = CalculateNextScheduledTaskTimerDelay(
      data, TimeFromUtcString("Sunday Jan 31 2021, 09:33"), *GetUtcTimeZone());

  ASSERT_TRUE(delay.has_value());
  EXPECT_EQ(delay.value(), base::TimeDeltaFromString("671h19m"));  // 27d23h19m
}

TEST(ScheduledTaskUtilTest, ParsesEmptyGracePeriodSwitch) {
  base::test::ScopedCommandLine command_line;

  // Check that returns default with empty command line.
  EXPECT_EQ(scheduled_task_util::GetScheduledRebootGracePeriod(),
            kDefaultGracePeriod);

  // Check that returns default with empty switch.
  command_line.GetProcessCommandLine()->AppendSwitchASCII(
      ash::switches::kScheduledRebootGracePeriodInSecondsForTesting, "");
  EXPECT_EQ(scheduled_task_util::GetScheduledRebootGracePeriod(),
            kDefaultGracePeriod);
}

TEST(ScheduledTaskUtilTest, ParsesNonNumbericGracePeriodSwitch) {
  base::test::ScopedCommandLine command_line;

  // Check that returns default with incorrect value.
  command_line.GetProcessCommandLine()->AppendSwitchASCII(
      ash::switches::kScheduledRebootGracePeriodInSecondsForTesting,
      "one-hundred");
  EXPECT_EQ(scheduled_task_util::GetScheduledRebootGracePeriod(),
            kDefaultGracePeriod);
}

TEST(ScheduledTaskUtilTest, ParsesNegativeGracePeriodSwitch) {
  base::test::ScopedCommandLine command_line;

  // Check that returns default with negative value.
  command_line.GetProcessCommandLine()->AppendSwitchASCII(
      ash::switches::kScheduledRebootGracePeriodInSecondsForTesting, "-100");
  EXPECT_EQ(scheduled_task_util::GetScheduledRebootGracePeriod(),
            kDefaultGracePeriod);
}

TEST(ScheduledTaskUtilTest, ParsesValidGracePeriodSwitch) {
  base::test::ScopedCommandLine command_line;

  // Check that returns default with negative value.
  command_line.GetProcessCommandLine()->AppendSwitchASCII(
      ash::switches::kScheduledRebootGracePeriodInSecondsForTesting, "100");
  EXPECT_EQ(scheduled_task_util::GetScheduledRebootGracePeriod(),
            base::Seconds(100));
}

TEST(ScheduledTaskUtilTest, SkipsRebootWithinGracePeriod) {
  constexpr base::TimeDelta kGracePeriod = base::Hours(1);

  base::test::ScopedCommandLine command_line;
  command_line.GetProcessCommandLine()->AppendSwitchASCII(
      ash::switches::kScheduledRebootGracePeriodInSecondsForTesting,
      base::NumberToString(kGracePeriod.InSeconds()));

  const base::Time boot_time;

  // Check that allows reboot outside of grace time period.
  const base::Time reboot_time_outside_grace_time =
      boot_time + kGracePeriod + base::Seconds(1);
  EXPECT_FALSE(scheduled_task_util::ShouldSkipRebootDueToGracePeriod(
      boot_time, reboot_time_outside_grace_time));
}

TEST(ScheduledTaskUtilTest, DoesNotSkipRebootWithinGracePeriod) {
  constexpr base::TimeDelta kGracePeriod = base::Hours(1);

  base::test::ScopedCommandLine command_line;
  command_line.GetProcessCommandLine()->AppendSwitchASCII(
      ash::switches::kScheduledRebootGracePeriodInSecondsForTesting,
      base::NumberToString(kGracePeriod.InSeconds()));

  const base::Time boot_time;

  // Check that does not allow reboot inside grace time period.
  const base::Time reboot_time_within_grace_time = boot_time + kGracePeriod;
  EXPECT_TRUE(scheduled_task_util::ShouldSkipRebootDueToGracePeriod(
      boot_time, reboot_time_within_grace_time));
}

}  // namespace policy