chromium/ash/system/time/calendar_unittest_utils.h

// 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.

#ifndef ASH_SYSTEM_TIME_CALENDAR_UNITTEST_UTILS_H_
#define ASH_SYSTEM_TIME_CALENDAR_UNITTEST_UTILS_H_

#include <list>
#include <memory>
#include <set>
#include <string>

#include "ash/calendar/calendar_client.h"
#include "ash/system/time/calendar_utils.h"
#include "base/time/time.h"
#include "google_apis/calendar/calendar_api_response_types.h"

namespace ash {

namespace {
// This list is from "chromeos/ash/components/settings/timezone_settings.cc".
const char* kAllTimeZones[] = {"Pacific/Midway",
                               "Pacific/Honolulu",
                               "America/Anchorage",
                               "America/Los_Angeles",
                               "America/Vancouver",
                               "America/Tijuana",
                               "America/Phoenix",
                               "America/Chihuahua",
                               "America/Denver",
                               "America/Edmonton",
                               "America/Mazatlan",
                               "America/Regina",
                               "America/Costa_Rica",
                               "America/Chicago",
                               "America/Mexico_City",
                               "America/Tegucigalpa",
                               "America/Winnipeg",
                               "Pacific/Easter",
                               "America/Bogota",
                               "America/Lima",
                               "America/New_York",
                               "America/Toronto",
                               "America/Caracas",
                               "America/Barbados",
                               "America/Halifax",
                               "America/Manaus",
                               "America/Santiago",
                               "America/St_Johns",
                               "America/Araguaina",
                               "America/Argentina/Buenos_Aires",
                               "America/Argentina/San_Luis",
                               "America/Montevideo",
                               "America/Santiago",
                               "America/Sao_Paulo",
                               "America/Godthab",
                               "Atlantic/South_Georgia",
                               "Atlantic/Cape_Verde",
                               "Etc/GMT",
                               "Atlantic/Azores",
                               "Atlantic/Reykjavik",
                               "Atlantic/St_Helena",
                               "Africa/Casablanca",
                               "Atlantic/Faroe",
                               "Europe/Dublin",
                               "Europe/Lisbon",
                               "Europe/London",
                               "Europe/Amsterdam",
                               "Europe/Belgrade",
                               "Europe/Berlin",
                               "Europe/Bratislava",
                               "Europe/Brussels",
                               "Europe/Budapest",
                               "Europe/Copenhagen",
                               "Europe/Ljubljana",
                               "Europe/Madrid",
                               "Europe/Malta",
                               "Europe/Oslo",
                               "Europe/Paris",
                               "Europe/Prague",
                               "Europe/Rome",
                               "Europe/Stockholm",
                               "Europe/Sarajevo",
                               "Europe/Tirane",
                               "Europe/Vaduz",
                               "Europe/Vienna",
                               "Europe/Warsaw",
                               "Europe/Zagreb",
                               "Europe/Zurich",
                               "Africa/Windhoek",
                               "Africa/Lagos",
                               "Africa/Brazzaville",
                               "Africa/Cairo",
                               "Africa/Harare",
                               "Africa/Maputo",
                               "Africa/Johannesburg",
                               "Europe/Kaliningrad",
                               "Europe/Athens",
                               "Europe/Bucharest",
                               "Europe/Chisinau",
                               "Europe/Helsinki",
                               "Europe/Istanbul",
                               "Europe/Kiev",
                               "Europe/Riga",
                               "Europe/Sofia",
                               "Europe/Tallinn",
                               "Europe/Vilnius",
                               "Asia/Amman",
                               "Asia/Beirut",
                               "Asia/Jerusalem",
                               "Africa/Nairobi",
                               "Asia/Baghdad",
                               "Asia/Riyadh",
                               "Asia/Kuwait",
                               "Europe/Minsk",
                               "Europe/Moscow",
                               "Asia/Tehran",
                               "Europe/Samara",
                               "Asia/Dubai",
                               "Asia/Tbilisi",
                               "Indian/Mauritius",
                               "Asia/Baku",
                               "Asia/Yerevan",
                               "Asia/Kabul",
                               "Asia/Karachi",
                               "Asia/Aqtobe",
                               "Asia/Ashgabat",
                               "Asia/Oral",
                               "Asia/Yekaterinburg",
                               "Asia/Calcutta",
                               "Asia/Colombo",
                               "Asia/Katmandu",
                               "Asia/Omsk",
                               "Asia/Almaty",
                               "Asia/Dhaka",
                               "Asia/Novosibirsk",
                               "Asia/Rangoon",
                               "Asia/Bangkok",
                               "Asia/Jakarta",
                               "Asia/Krasnoyarsk",
                               "Asia/Novokuznetsk",
                               "Asia/Ho_Chi_Minh",
                               "Asia/Phnom_Penh",
                               "Asia/Vientiane",
                               "Asia/Shanghai",
                               "Asia/Hong_Kong",
                               "Asia/Kuala_Lumpur",
                               "Asia/Singapore",
                               "Asia/Manila",
                               "Asia/Taipei",
                               "Asia/Ulaanbaatar",
                               "Asia/Makassar",
                               "Asia/Irkutsk",
                               "Asia/Yakutsk",
                               "Australia/Perth",
                               "Australia/Eucla",
                               "Asia/Seoul",
                               "Asia/Tokyo",
                               "Asia/Jayapura",
                               "Asia/Sakhalin",
                               "Asia/Vladivostok",
                               "Asia/Magadan",
                               "Australia/Darwin",
                               "Australia/Adelaide",
                               "Pacific/Guam",
                               "Australia/Brisbane",
                               "Australia/Hobart",
                               "Australia/Sydney",
                               "Asia/Anadyr",
                               "Pacific/Port_Moresby",
                               "Asia/Kamchatka",
                               "Pacific/Fiji",
                               "Pacific/Majuro",
                               "Pacific/Auckland",
                               "Pacific/Tongatapu",
                               "Pacific/Apia",
                               "Pacific/Kiritimati"};

// These are from "third_party/fontconfig/include/fc-lang/fclang.h" data.
// Locales "und-zmth" and "und-zsye" are omitted since they cannot be set.
const char* kLocales[] = {
    "aa",     "ab",  "af",    "ak",    "am",    "an",    "ar",    "as",
    "ast",    "av",  "ay",    "az-az", "az-ir", "ba",    "be",    "ber-dz",
    "ber-ma", "bg",  "bh",    "bho",   "bi",    "bin",   "bm",    "bn",
    "bo",     "br",  "brx",   "bs",    "bua",   "byn",   "ca",    "ce",
    "ch",     "chm", "chr",   "co",    "crh",   "cs",    "csb",   "cu",
    "cv",     "cy",  "da",    "de",    "doi",   "dv",    "dz",    "ee",
    "el",     "en",  "eo",    "es",    "et",    "eu",    "fa",    "fat",
    "ff",     "fi",  "fil",   "fj",    "fo",    "fr",    "fur",   "fy",
    "ga",     "gd",  "gez",   "gl",    "gn",    "gu",    "gv",    "ha",
    "haw",    "he",  "hi",    "hne",   "ho",    "hr",    "ht",    "hu",
    "hy",     "hz",  "ia",    "id",    "ie",    "ig",    "ii",    "ik",
    "io",     "is",  "it",    "iu",    "ja",    "jv",    "ka",    "kaa",
    "kab",    "ki",  "kj",    "kk",    "kl",    "km",    "kn",    "ko",
    "kok",    "kr",  "ks",    "ku-am", "ku-iq", "ku-ir", "ku-tr", "kum",
    "kv",     "kw",  "kwm",   "ky",    "la",    "lah",   "lb",    "lez",
    "lg",     "li",  "ln",    "lo",    "lt",    "lv",    "mai",   "mg",
    "mh",     "mi",  "mk",    "ml",    "mn-cn", "mn-mn", "mni",   "mo",
    "mr",     "ms",  "mt",    "my",    "na",    "nb",    "nds",   "ne",
    "ng",     "nl",  "nn",    "no",    "nr",    "nso",   "nv",    "ny",
    "oc",     "om",  "or",    "os",    "ota",   "pa",    "pa-pk", "pap-an",
    "pap-aw", "pl",  "ps-af", "ps-pk", "pt",    "qu",    "quz",   "rm",
    "rn",     "ro",  "ru",    "rw",    "sa",    "sah",   "sat",   "sc",
    "sco",    "sd",  "se",    "sel",   "sg",    "sh",    "shs",   "si",
    "sid",    "sk",  "sl",    "sm",    "sma",   "smj",   "smn",   "sms",
    "sn",     "so",  "sq",    "sr",    "ss",    "st",    "su",    "sv",
    "sw",     "syr", "ta",    "te",    "tg",    "th",    "ti-er", "ti-et",
    "tig",    "tk",  "tl",    "tn",    "to",    "tr",    "ts",    "tt",
    "tw",     "ty",  "tyv",   "ug",    "uk",    "ur",    "uz",    "ve",
    "vi",     "vo",  "vot",   "wa",    "wal",   "wen",   "wo",    "xh",
    "yap",    "yi",  "yo",    "za",    "zh-cn", "zh-hk", "zh-mo", "zh-sg",
    "zh-tw",  "zu"};

std::set<std::string> kLocalesWithUniqueNumerals{"bn", "fa", "mr", "pa-pk"};

}  // namespace

namespace calendar_test_utils {

// Used for over-riding the locale that `base::Time` uses. Copied from
// `time_unittest.cc`.
class ScopedLibcTimeZone {
 public:
  explicit ScopedLibcTimeZone(const std::string& timezone);
  ~ScopedLibcTimeZone();

  ScopedLibcTimeZone(const ScopedLibcTimeZone& other) = delete;
  ScopedLibcTimeZone& operator=(const ScopedLibcTimeZone& other) = delete;

  bool is_success() const { return success_; }

 private:
  static constexpr char kTimeZoneEnvVarName[] = "TZ";

  bool success_ = true;
  std::optional<std::string> old_timezone_;
};

// A duration to let the animation finish and pass the cool down duration in
// tests.
constexpr base::TimeDelta kAnimationSettleDownDuration = base::Seconds(3);

// A duration which is smaller than any of the animation duration. So if there's
// an animation, the view should be in the middle of the animation.
constexpr base::TimeDelta kAnimationStartBufferDuration =
    base::Milliseconds(90);

// Creates a `google_apis::calendar::SingleCalendar` for testing only.
std::unique_ptr<google_apis::calendar::SingleCalendar> CreateCalendar(
    const std::string& id,
    const std::string& summary,
    const std::string& color_id,
    bool selected,
    bool primary);

std::unique_ptr<google_apis::calendar::CalendarList> CreateMockCalendarList(
    std::list<std::unique_ptr<google_apis::calendar::SingleCalendar>>
        calendars);

// Creates a `google_apis::calendar::CalendarEvent` for testing, that converts
// start/end time strings to `google_apis::calendar::DateTime`.
std::unique_ptr<google_apis::calendar::CalendarEvent> CreateEvent(
    const char* id,
    const char* summary,
    const char* start_time,
    const char* end_time,
    const google_apis::calendar::CalendarEvent::EventStatus event_status =
        google_apis::calendar::CalendarEvent::EventStatus::kConfirmed,
    const google_apis::calendar::CalendarEvent::ResponseStatus
        self_response_status =
            google_apis::calendar::CalendarEvent::ResponseStatus::kAccepted,
    bool all_day_event = false,
    GURL video_conference_url = GURL());

// Creates a `google_apis::calendar::CalendarEvent` for testing, that converts
// start/end `base::Time` objects to `google_apis::calendar::DateTime`.
std::unique_ptr<google_apis::calendar::CalendarEvent> CreateEvent(
    const char* id,
    const char* summary,
    base::Time start_time,
    base::Time end_time,
    const google_apis::calendar::CalendarEvent::EventStatus event_status =
        google_apis::calendar::CalendarEvent::EventStatus::kConfirmed,
    const google_apis::calendar::CalendarEvent::ResponseStatus
        self_response_status =
            google_apis::calendar::CalendarEvent::ResponseStatus::kAccepted,
    bool all_day_event = false,
    GURL video_conference_url = GURL());

std::unique_ptr<google_apis::calendar::EventList> CreateMockEventList(
    std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>> events);

// Checks if the two exploded are in the same month.
bool IsTheSameMonth(const base::Time date_a, const base::Time date_b);

// Returns the `base:Time` from the given string.
base::Time GetTimeFromString(const char* start_time);

// A mock `CalendarClient` which uses `SetEventList` and `SetError` to set the
// response. This mock client's `GetEventList` waits for a short duration to
// mock the fetching process. This client can be used in calendar unittests to
// mock the process of fetching events and getting back event list or error
// message. It needs to be registered to a calendar client in `SetUp`.
class CalendarClientTestImpl : public CalendarClient {
 public:
  CalendarClientTestImpl();
  CalendarClientTestImpl(const CalendarClientTestImpl& other) = delete;
  CalendarClientTestImpl& operator=(const CalendarClientTestImpl& other) =
      delete;
  ~CalendarClientTestImpl() override;

  void set_is_disabled_by_admin(bool is_disabled_by_admin) {
    is_disabled_by_admin_ = is_disabled_by_admin;
  }

  // CalendarClient:
  bool IsDisabledByAdmin() const override;
  base::OnceClosure GetCalendarList(
      google_apis::calendar::CalendarListCallback callback) override;
  base::OnceClosure GetEventList(
      google_apis::calendar::CalendarEventListCallback callback,
      const base::Time start_time,
      const base::Time end_time) override;
  base::OnceClosure GetEventList(
      google_apis::calendar::CalendarEventListCallback callback,
      const base::Time start_time,
      const base::Time end_time,
      const std::string& calendar_id,
      const std::string& calendar_color_id) override;

  // Sets `calendars` as the fetched calendar list.
  void SetCalendarList(
      std::unique_ptr<google_apis::calendar::CalendarList> calendars);

  // Sets `events` as the fetched event list.
  void SetEventList(std::unique_ptr<google_apis::calendar::EventList> events);

  // Sets `error` as the error message. The value of `error` is
  // `google_apis::HTTP_SUCCESS` by default.
  void SetError(google_apis::ApiErrorCode error) { error_ = error; }

  // Sets `delay` as the response delay. By default, the response delay is
  // `kAnimationSettleDownDuration` plus 2 seconds.
  void SetResponseDelay(const base::TimeDelta delay) { task_delay_ = delay; }

  // Force the task to take longer than the default timeout, causing an internal
  // error to be propagated.
  void ForceTimeout() {
    task_delay_ = calendar_utils::kCalendarDataFetchTimeout + base::Seconds(1);
  }

 private:
  bool is_disabled_by_admin_ = false;
  google_apis::ApiErrorCode error_ = google_apis::HTTP_SUCCESS;
  std::unique_ptr<google_apis::calendar::CalendarList> calendars_;
  std::unique_ptr<google_apis::calendar::EventList> events_;
  base::TimeDelta task_delay_ = kAnimationSettleDownDuration + base::Seconds(2);
};

}  // namespace calendar_test_utils

}  // namespace ash

#endif  // ASH_SYSTEM_TIME_CALENDAR_UNITTEST_UTILS_H_