tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestTimeZone.cpp (8621B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 #include "gtest/gtest.h"
      5 
      6 #include "mozilla/intl/TimeZone.h"
      7 #include "mozilla/Maybe.h"
      8 #include "mozilla/Span.h"
      9 #include "mozilla/TextUtils.h"
     10 #include "TestBuffer.h"
     11 
     12 #include <algorithm>
     13 #include <string>
     14 
     15 namespace mozilla::intl {
     16 
     17 // Firefox 1.0 release date.
     18 static constexpr int64_t RELEASE_DATE = 1'032'800'850'000;
     19 
     20 // Date.UTC(2021, 11-1, 7, 2, 0, 0)
     21 static constexpr int64_t DST_CHANGE_DATE = 1'636'250'400'000;
     22 
     23 // These tests are dependent on the machine that this test is being run on.
     24 // Unwrap the results to ensure it doesn't fail, but don't check the values.
     25 TEST(IntlTimeZone, SystemDependentTests)
     26 {
     27  // e.g. "America/Chicago"
     28  TestBuffer<char16_t> buffer;
     29  TimeZone::GetDefaultTimeZone(buffer).unwrap();
     30 }
     31 
     32 TEST(IntlTimeZone, GetRawOffsetMs)
     33 {
     34  auto timeZone = TimeZone::TryCreate(Some(MakeStringSpan("GMT+3"))).unwrap();
     35  ASSERT_EQ(timeZone->GetRawOffsetMs().unwrap(), 3 * 60 * 60 * 1000);
     36 
     37  timeZone = TimeZone::TryCreate(Some(MakeStringSpan("Etc/GMT+3"))).unwrap();
     38  ASSERT_EQ(timeZone->GetRawOffsetMs().unwrap(), -(3 * 60 * 60 * 1000));
     39 
     40  timeZone =
     41      TimeZone::TryCreate(Some(MakeStringSpan("America/New_York"))).unwrap();
     42  ASSERT_EQ(timeZone->GetRawOffsetMs().unwrap(), -(5 * 60 * 60 * 1000));
     43 }
     44 
     45 TEST(IntlTimeZone, GetDSTOffsetMs)
     46 {
     47  auto timeZone = TimeZone::TryCreate(Some(MakeStringSpan("GMT+3"))).unwrap();
     48  ASSERT_EQ(timeZone->GetDSTOffsetMs(0).unwrap(), 0);
     49 
     50  timeZone = TimeZone::TryCreate(Some(MakeStringSpan("Etc/GMT+3"))).unwrap();
     51  ASSERT_EQ(timeZone->GetDSTOffsetMs(0).unwrap(), 0);
     52 
     53  timeZone =
     54      TimeZone::TryCreate(Some(MakeStringSpan("America/New_York"))).unwrap();
     55  ASSERT_EQ(timeZone->GetDSTOffsetMs(0).unwrap(), 0);
     56  ASSERT_EQ(timeZone->GetDSTOffsetMs(RELEASE_DATE).unwrap(),
     57            1 * 60 * 60 * 1000);
     58  ASSERT_EQ(timeZone->GetDSTOffsetMs(DST_CHANGE_DATE).unwrap(),
     59            1 * 60 * 60 * 1000);
     60 }
     61 
     62 TEST(IntlTimeZone, GetOffsetMs)
     63 {
     64  auto timeZone = TimeZone::TryCreate(Some(MakeStringSpan("GMT+3"))).unwrap();
     65  ASSERT_EQ(timeZone->GetOffsetMs(0).unwrap(), 3 * 60 * 60 * 1000);
     66 
     67  timeZone = TimeZone::TryCreate(Some(MakeStringSpan("Etc/GMT+3"))).unwrap();
     68  ASSERT_EQ(timeZone->GetOffsetMs(0).unwrap(), -(3 * 60 * 60 * 1000));
     69 
     70  timeZone =
     71      TimeZone::TryCreate(Some(MakeStringSpan("America/New_York"))).unwrap();
     72  ASSERT_EQ(timeZone->GetOffsetMs(0).unwrap(), -(5 * 60 * 60 * 1000));
     73  ASSERT_EQ(timeZone->GetOffsetMs(RELEASE_DATE).unwrap(),
     74            -(4 * 60 * 60 * 1000));
     75  ASSERT_EQ(timeZone->GetOffsetMs(DST_CHANGE_DATE).unwrap(),
     76            -(4 * 60 * 60 * 1000));
     77 }
     78 
     79 TEST(IntlTimeZone, GetUTCOffsetMs)
     80 {
     81  auto timeZone = TimeZone::TryCreate(Some(MakeStringSpan("GMT+3"))).unwrap();
     82  ASSERT_EQ(timeZone->GetUTCOffsetMs(0).unwrap(), 3 * 60 * 60 * 1000);
     83 
     84  timeZone = TimeZone::TryCreate(Some(MakeStringSpan("Etc/GMT+3"))).unwrap();
     85  ASSERT_EQ(timeZone->GetUTCOffsetMs(0).unwrap(), -(3 * 60 * 60 * 1000));
     86 
     87  timeZone =
     88      TimeZone::TryCreate(Some(MakeStringSpan("America/New_York"))).unwrap();
     89  ASSERT_EQ(timeZone->GetUTCOffsetMs(0).unwrap(), -(5 * 60 * 60 * 1000));
     90  ASSERT_EQ(timeZone->GetUTCOffsetMs(RELEASE_DATE).unwrap(),
     91            -(4 * 60 * 60 * 1000));
     92  ASSERT_EQ(timeZone->GetUTCOffsetMs(DST_CHANGE_DATE).unwrap(),
     93            -(5 * 60 * 60 * 1000));
     94 }
     95 
     96 TEST(IntlTimeZone, GetDisplayName)
     97 {
     98  using DaylightSavings = TimeZone::DaylightSavings;
     99 
    100  TestBuffer<char16_t> buffer;
    101 
    102  auto timeZone = TimeZone::TryCreate(Some(MakeStringSpan("GMT+3"))).unwrap();
    103 
    104  buffer.clear();
    105  timeZone->GetDisplayName("en", DaylightSavings::No, buffer).unwrap();
    106  ASSERT_EQ(buffer.get_string_view(), u"GMT+03:00");
    107 
    108  buffer.clear();
    109  timeZone->GetDisplayName("en", DaylightSavings::Yes, buffer).unwrap();
    110  ASSERT_EQ(buffer.get_string_view(), u"GMT+03:00");
    111 
    112  timeZone = TimeZone::TryCreate(Some(MakeStringSpan("Etc/GMT+3"))).unwrap();
    113 
    114  buffer.clear();
    115  timeZone->GetDisplayName("en", DaylightSavings::No, buffer).unwrap();
    116  ASSERT_EQ(buffer.get_string_view(), u"GMT-03:00");
    117 
    118  buffer.clear();
    119  timeZone->GetDisplayName("en", DaylightSavings::Yes, buffer).unwrap();
    120  ASSERT_EQ(buffer.get_string_view(), u"GMT-03:00");
    121 
    122  timeZone =
    123      TimeZone::TryCreate(Some(MakeStringSpan("America/New_York"))).unwrap();
    124 
    125  buffer.clear();
    126  timeZone->GetDisplayName("en", DaylightSavings::No, buffer).unwrap();
    127  ASSERT_EQ(buffer.get_string_view(), u"Eastern Standard Time");
    128 
    129  buffer.clear();
    130  timeZone->GetDisplayName("en", DaylightSavings::Yes, buffer).unwrap();
    131  ASSERT_EQ(buffer.get_string_view(), u"Eastern Daylight Time");
    132 }
    133 
    134 TEST(IntlTimeZone, GetCanonicalTimeZoneID)
    135 {
    136  TestBuffer<char16_t> buffer;
    137 
    138  // Providing a canonical time zone results in the same string at the end.
    139  TimeZone::GetCanonicalTimeZoneID(MakeStringSpan(u"America/Chicago"), buffer)
    140      .unwrap();
    141  ASSERT_EQ(buffer.get_string_view(), u"America/Chicago");
    142 
    143  // Providing an alias will result in the canonical representation.
    144  TimeZone::GetCanonicalTimeZoneID(MakeStringSpan(u"Europe/Belfast"), buffer)
    145      .unwrap();
    146  ASSERT_EQ(buffer.get_string_view(), u"Europe/London");
    147 
    148  // An unknown time zone results in an error.
    149  ASSERT_TRUE(TimeZone::GetCanonicalTimeZoneID(
    150                  MakeStringSpan(u"Not a time zone"), buffer)
    151                  .isErr());
    152 }
    153 
    154 TEST(IntlTimeZone, GetAvailableTimeZones)
    155 {
    156  constexpr auto EuropeBerlin = MakeStringSpan("Europe/Berlin");
    157  constexpr auto EuropeBusingen = MakeStringSpan("Europe/Busingen");
    158 
    159  auto timeZones = TimeZone::GetAvailableTimeZones("DE").unwrap();
    160 
    161  bool hasEuropeBerlin = false;
    162  bool hasEuropeBusingen = false;
    163 
    164  for (auto timeZone : timeZones) {
    165    auto span = timeZone.unwrap();
    166    if (span == EuropeBerlin) {
    167      ASSERT_FALSE(hasEuropeBerlin);
    168      hasEuropeBerlin = true;
    169    } else if (span == EuropeBusingen) {
    170      ASSERT_FALSE(hasEuropeBusingen);
    171      hasEuropeBusingen = true;
    172    } else {
    173      std::string str(span.data(), span.size());
    174      ADD_FAILURE() << "Unexpected time zone: " << str;
    175    }
    176  }
    177 
    178  ASSERT_TRUE(hasEuropeBerlin);
    179  ASSERT_TRUE(hasEuropeBusingen);
    180 }
    181 
    182 TEST(IntlTimeZone, GetAvailableTimeZonesNoRegion)
    183 {
    184  constexpr auto AmericaNewYork = MakeStringSpan("America/New_York");
    185  constexpr auto AsiaTokyo = MakeStringSpan("Asia/Tokyo");
    186  constexpr auto EuropeParis = MakeStringSpan("Europe/Paris");
    187 
    188  auto timeZones = TimeZone::GetAvailableTimeZones().unwrap();
    189 
    190  bool hasAmericaNewYork = false;
    191  bool hasAsiaTokyo = false;
    192  bool hasEuropeParis = false;
    193 
    194  for (auto timeZone : timeZones) {
    195    auto span = timeZone.unwrap();
    196    if (span == AmericaNewYork) {
    197      ASSERT_FALSE(hasAmericaNewYork);
    198      hasAmericaNewYork = true;
    199    } else if (span == AsiaTokyo) {
    200      ASSERT_FALSE(hasAsiaTokyo);
    201      hasAsiaTokyo = true;
    202    } else if (span == EuropeParis) {
    203      ASSERT_FALSE(hasEuropeParis);
    204      hasEuropeParis = true;
    205    }
    206  }
    207 
    208  ASSERT_TRUE(hasAmericaNewYork);
    209  ASSERT_TRUE(hasAsiaTokyo);
    210  ASSERT_TRUE(hasEuropeParis);
    211 }
    212 
    213 TEST(IntlTimeZone, GetTZDataVersion)
    214 {
    215  // From <https://data.iana.org/time-zones/tz-link.html#download>:
    216  //
    217  // "Since 1996, each version has been a four-digit year followed by lower-case
    218  // letter (a through z, then za through zz, then zza through zzz, and so on)."
    219  //
    220  // More than 26 releases are unlikely or at least never happend. 2009 got
    221  // quite close with 21 releases, but that was the first time ever with more
    222  // than twenty releases in a single year.
    223  //
    224  // Should this assertion ever fail, because more than 26 releases were issued,
    225  // update it accordingly. And in that case we should be extra cautious that
    226  // all time zone functionality in Firefox and in external libraries we're
    227  // using can cope with more than 26 tzdata releases.
    228  //
    229  // Also see <https://mm.icann.org/pipermail/tz/2021-September/030621.html>:
    230  //
    231  // "For Android having 2021a1 and 2021b would be inconvenient. Because there
    232  // are hardcoded places which expect that tzdata version is exactly 5
    233  // characters."
    234 
    235  auto version = TimeZone::GetTZDataVersion().unwrap();
    236  auto [year, release] = version.SplitAt(4);
    237 
    238  ASSERT_TRUE(std::all_of(year.begin(), year.end(), IsAsciiDigit<char>));
    239  ASSERT_TRUE(IsAsciiAlpha(release[0]));
    240 
    241  // ICU issued a non-standard release "2021a1".
    242  ASSERT_TRUE(release.Length() == 1 || release.Length() == 2);
    243 
    244  if (release.Length() == 2) {
    245    ASSERT_TRUE(IsAsciiDigit(release[1]));
    246  }
    247 }
    248 
    249 }  // namespace mozilla::intl