chromium/ash/system/time/event_date_formatter_util_unittest.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/system/time/event_date_formatter_util.h"
#include <string>

#include "ash/shell.h"
#include "ash/system/model/system_tray_model.h"
#include "ash/system/time/calendar_unittest_utils.h"
#include "ash/test/ash_test_base.h"
#include "chromeos/ash/components/settings/scoped_timezone_settings.h"

namespace ash {
namespace {

std::unique_ptr<google_apis::calendar::CalendarEvent> CreateEvent(
    const char* start_time,
    const char* end_time,
    bool all_day_event = false) {
  return calendar_test_utils::CreateEvent(
      "id_7", "summary_7", start_time, end_time,
      google_apis::calendar::CalendarEvent::EventStatus::kConfirmed,
      google_apis::calendar::CalendarEvent::ResponseStatus::kAccepted,
      all_day_event);
}

}  // namespace

using EventDateFormatterUtilTest = AshTestBase;

TEST_F(EventDateFormatterUtilTest,
       GetStartAndEndTimesAccessibleNames_24HourClock) {
  const char* start_time_string = "22 Nov 2021 23:30 GMT";
  const char* end_time_string = "23 Nov 2021 0:30 GMT";
  base::Time start_time, end_time;
  ash::system::ScopedTimezoneSettings timezone_settings(u"PST");
  Shell::Get()->system_tray_model()->SetUse24HourClock(true);

  EXPECT_TRUE(base::Time::FromString(start_time_string, &start_time));
  EXPECT_TRUE(base::Time::FromString(end_time_string, &end_time));

  const auto [actual_start, actual_end] =
      event_date_formatter_util::GetStartAndEndTimeAccessibleNames(start_time,
                                                                   end_time);

  EXPECT_EQ(actual_start, u"15:30");
  EXPECT_EQ(actual_end, u"16:30");
}

TEST_F(EventDateFormatterUtilTest,
       GetStartAndEndTimesAccessibleNames_12HourClock) {
  const char* start_time_string = "22 Nov 2021 23:30 GMT";
  const char* end_time_string = "23 Nov 2021 0:30 GMT";
  base::Time start_time, end_time;
  ash::system::ScopedTimezoneSettings timezone_settings(u"PST");
  Shell::Get()->system_tray_model()->SetUse24HourClock(false);

  EXPECT_TRUE(base::Time::FromString(start_time_string, &start_time));
  EXPECT_TRUE(base::Time::FromString(end_time_string, &end_time));

  const auto [actual_start, actual_end] =
      event_date_formatter_util::GetStartAndEndTimeAccessibleNames(start_time,
                                                                   end_time);

  EXPECT_EQ(actual_start, u"3:30\u202fPM");
  EXPECT_EQ(actual_end, u"4:30\u202fPM");
}

TEST_F(EventDateFormatterUtilTest, GetFormattedInterval_12HourClock) {
  const char* start_time_string = "22 Nov 2021 23:30 GMT";
  const char* end_time_string = "23 Nov 2021 0:30 GMT";
  base::Time start_time, end_time;
  ash::system::ScopedTimezoneSettings timezone_settings(u"PST");
  Shell::Get()->system_tray_model()->SetUse24HourClock(false);

  EXPECT_TRUE(base::Time::FromString(start_time_string, &start_time));
  EXPECT_TRUE(base::Time::FromString(end_time_string, &end_time));

  const auto actual =
      event_date_formatter_util::GetFormattedInterval(start_time, end_time);

  // \x2013 is unicode for dash i.e. '-'
  EXPECT_EQ(actual, u"3:30\u2009\x2013\u20094:30\u202fPM");
}

TEST_F(EventDateFormatterUtilTest, GetFormattedInterval_24HourClock) {
  const char* start_time_string = "22 Nov 2021 23:30 GMT";
  const char* end_time_string = "23 Nov 2021 0:30 GMT";
  base::Time start_time, end_time;
  ash::system::ScopedTimezoneSettings timezone_settings(u"PST");
  Shell::Get()->system_tray_model()->SetUse24HourClock(true);

  EXPECT_TRUE(base::Time::FromString(start_time_string, &start_time));
  EXPECT_TRUE(base::Time::FromString(end_time_string, &end_time));

  const auto actual =
      event_date_formatter_util::GetFormattedInterval(start_time, end_time);

  // \x2013 is unicode for dash i.e. '-'
  EXPECT_EQ(actual, u"15:30\u2009\x2013\u200916:30");
}

class EventDateFormatterAllDayEventTest
    : public AshTestBase,
      public testing::WithParamInterface<
          std::tuple<const char*, std::u16string>> {
 public:
  const char* GetSelectedDateString() { return std::get<0>(GetParam()); }
  std::u16string GetExpectedResult() { return std::get<1>(GetParam()); }

  // testing::Test:
  void SetUp() override { AshTestBase::SetUp(); }

  void TearDown() override { AshTestBase::TearDown(); }
};

INSTANTIATE_TEST_SUITE_P(
    All,
    EventDateFormatterAllDayEventTest,
    testing::Values(std::make_tuple("22 Nov 2021 00:00 UTC", u"(Day 1/2)"),
                    std::make_tuple("23 Nov 2021 00:00 UTC", u"(Day 2/2)")));

TEST_P(EventDateFormatterAllDayEventTest, GetMultiDayText_AllDayEvent) {
  const char* start_time_string = "22 Nov 2021 00:00 UTC";
  const char* end_time_string = "24 Nov 2021 00:00 UTC";
  const char* selected_date_string = GetSelectedDateString();
  const auto all_day_event =
      CreateEvent(start_time_string, end_time_string, true);
  base::Time selected_time;
  ash::system::ScopedTimezoneSettings timezone_settings(u"PST");

  EXPECT_TRUE(base::Time::FromUTCString(selected_date_string, &selected_time));

  const auto result = event_date_formatter_util::GetMultiDayText(
      all_day_event.get(), selected_time.UTCMidnight(),
      selected_time.LocalMidnight());

  EXPECT_EQ(result, GetExpectedResult());
}

struct MultiDayEventTestParams {
  const char* start_time_string;
  const char* end_time_string;
  const char* selected_date_string;
  const std::u16string expected_result;
};

class EventDateFormatterMultiDayEventTest
    : public AshTestBase,
      public testing::WithParamInterface<MultiDayEventTestParams> {
 public:
  const char* GetStartTimeString() { return GetParam().start_time_string; }
  const char* GetEndTimeString() { return GetParam().end_time_string; }
  const char* GetSelectedDateString() {
    return GetParam().selected_date_string;
  }
  const std::u16string GetExpectedResult() {
    return GetParam().expected_result;
  }

  // testing::Test:
  void SetUp() override { AshTestBase::SetUp(); }

  void TearDown() override { AshTestBase::TearDown(); }
};

INSTANTIATE_TEST_SUITE_P(
    All,
    EventDateFormatterMultiDayEventTest,
    testing::Values(
        MultiDayEventTestParams{
            "22 Nov 2021 09:00 GMT", "24 Nov 2021 09:00 GMT",
            "22 Nov 2021 00:00 UTC", u"Starts at 10:00\u202fAM (Day 1/3)"},
        MultiDayEventTestParams{
            "22 Nov 2021 09:00 GMT", "24 Nov 2021 09:00 GMT",
            "23 Nov 2021 00:00 UTC", u"Starts at 10:00\u202fAM (Day 2/3)"},
        MultiDayEventTestParams{
            "22 Nov 2021 09:00 GMT", "24 Nov 2021 09:00 GMT",
            "24 Nov 2021 00:00 UTC", u"Ends at 10:00\u202fAM (Day 3/3)"},
        // Test edge case where a multi-day event falls into a single day in the
        // right timezone.
        MultiDayEventTestParams{
            "22 Nov 2021 23:00 GMT", "23 Nov 2021 23:00 GMT",
            "23 Nov 2021 00:00 UTC", u"Starts at 12:00\u202fAM (Day 1/1)"},
        // Test where a 2 hour event spans multiple days depending on timezone,
        // day 1.
        MultiDayEventTestParams{
            "22 Nov 2021 22:00 GMT", "23 Nov 2021 00:00 GMT",
            "22 Nov 2021 00:00 UTC", u"Starts at 11:00\u202fPM (Day 1/2)"},
        // Test where a 2 hour event spans multiple days depending on timezone,
        // day 2.
        MultiDayEventTestParams{
            "22 Nov 2021 22:00 GMT", "23 Nov 2021 00:00 GMT",
            "23 Nov 2021 00:00 UTC", u"Ends at 1:00\u202fAM (Day 2/2)"}));

TEST_P(EventDateFormatterMultiDayEventTest, GetMultiDayText_MultiDayEvent) {
  const char* start_time_string = GetStartTimeString();
  const char* end_time_string = GetEndTimeString();
  const char* selected_date_string = GetSelectedDateString();
  const auto event = CreateEvent(start_time_string, end_time_string);
  base::Time start_time, end_time, selected_time;
  // This is needed for the calendar util / `DateHelper` functions to use the
  // correct locale.
  ash::system::ScopedTimezoneSettings timezone_settings(u"Europe/Paris");
  // This is needed for `base::Time` to use the correct locale. Without this
  // override, it will ignore the `ash::system::ScopedTimezoneSettings` and use
  // the timezone of the environment it's running on.
  calendar_test_utils::ScopedLibcTimeZone scoped_libc_timezone("Europe/Paris");
  ASSERT_TRUE(scoped_libc_timezone.is_success());

  EXPECT_TRUE(base::Time::FromString(start_time_string, &start_time));
  EXPECT_TRUE(base::Time::FromString(end_time_string, &end_time));
  EXPECT_TRUE(base::Time::FromUTCString(selected_date_string, &selected_time));

  const auto result = event_date_formatter_util::GetMultiDayText(
      event.get(), selected_time.UTCMidnight(), selected_time.LocalMidnight());

  EXPECT_EQ(result, GetExpectedResult());
}

}  // namespace ash