tor-browser

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

shell.js (10258B)


      1 /*---
      2 defines: [msPerHour, TZ_ADJUST, UTC_01_JAN_1900, UTC_01_JAN_2000, UTC_29_FEB_2000, UTC_01_JAN_2005, inTimeZone, withLocale, Month, assertDateTime, runDSTOffsetCachingTestsFraction]
      3 allow_unused: True
      4 ---*/
      5 
      6 /**
      7 * Date functions used by tests in Date suite
      8 */
      9 (function(global) {
     10    const msPerDay = 1000 * 60 * 60 * 24;
     11    const msPerHour = 1000 * 60 * 60;
     12    global.msPerHour = msPerHour;
     13 
     14    // Offset of tester's time zone from UTC.
     15    const TZ_DIFF = GetRawTimezoneOffset();
     16    global.TZ_ADJUST = TZ_DIFF * msPerHour;
     17 
     18    const UTC_01_JAN_1900 = -2208988800000;
     19    const UTC_01_JAN_2000 = 946684800000;
     20    const UTC_29_FEB_2000 = UTC_01_JAN_2000 + 31 * msPerDay + 28 * msPerDay;
     21    const UTC_01_JAN_2005 = UTC_01_JAN_2000 + TimeInYear(2000) + TimeInYear(2001) +
     22                            TimeInYear(2002) + TimeInYear(2003) + TimeInYear(2004);
     23    global.UTC_01_JAN_1900 = UTC_01_JAN_1900;
     24    global.UTC_01_JAN_2000 = UTC_01_JAN_2000;
     25    global.UTC_29_FEB_2000 = UTC_29_FEB_2000;
     26    global.UTC_01_JAN_2005 = UTC_01_JAN_2005;
     27 
     28    /*
     29     * Originally, the test suite used a hard-coded value TZ_DIFF = -8.
     30     * But that was only valid for testers in the Pacific Standard Time Zone!
     31     * We calculate the proper number dynamically for any tester. We just
     32     * have to be careful not to use a date subject to Daylight Savings Time...
     33     */
     34    function GetRawTimezoneOffset() {
     35        let t1 = new Date(2000, 1, 1).getTimezoneOffset();
     36        let t2 = new Date(2000, 1 + 6, 1).getTimezoneOffset();
     37 
     38        // 1) Time zone without daylight saving time.
     39        // 2) Northern hemisphere with daylight saving time.
     40        if ((t1 - t2) >= 0)
     41            return -t1 / 60;
     42 
     43        // 3) Southern hemisphere with daylight saving time.
     44        return -t2 / 60;
     45    }
     46 
     47    function DaysInYear(y) {
     48        return y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0) ? 366 : 365;
     49    }
     50 
     51    function TimeInYear(y) {
     52        return DaysInYear(y) * msPerDay;
     53    }
     54 
     55    function getDefaultTimeZone() {
     56        var tz = getTimeZone();
     57        switch (tz) {
     58          case "EST":
     59          case "EDT":
     60            return "EST5EDT";
     61 
     62          case "CST":
     63          case "CDT":
     64            return "CST6CDT";
     65 
     66          case "MST":
     67          case "MDT":
     68            return "MST7MDT";
     69 
     70          case "PST":
     71          case "PDT":
     72            return "PST8PDT";
     73 
     74          default:
     75            // Other time zones abbrevations are not supported.
     76            return tz;
     77        }
     78    }
     79 
     80    function getDefaultLocale() {
     81        // If the default locale looks like a BCP-47 language tag, return it.
     82        var locale = global.getDefaultLocale();
     83        if (locale.match(/^[a-z][a-z0-9\-]+$/i))
     84            return locale;
     85 
     86        // Otherwise use undefined to reset to the default locale.
     87        return undefined;
     88    }
     89 
     90    let defaultTimeZone = null;
     91    let defaultLocale = null;
     92 
     93    // Run the given test in the requested time zone.
     94    function inTimeZone(tzname, fn) {
     95        if (defaultTimeZone === null)
     96            defaultTimeZone = getDefaultTimeZone();
     97 
     98        setTimeZone(tzname);
     99        try {
    100            fn();
    101        } finally {
    102            setTimeZone(defaultTimeZone);
    103        }
    104    }
    105    global.inTimeZone = inTimeZone;
    106 
    107    // Run the given test with the requested locale.
    108    function withLocale(locale, fn) {
    109        if (defaultLocale === null)
    110            defaultLocale = getDefaultLocale();
    111 
    112        setDefaultLocale(locale);
    113        try {
    114            fn();
    115        } finally {
    116            setDefaultLocale(defaultLocale);
    117        }
    118    }
    119    global.withLocale = withLocale;
    120 
    121    const Month = {
    122        January: 0,
    123        February: 1,
    124        March: 2,
    125        April: 3,
    126        May: 4,
    127        June: 5,
    128        July: 6,
    129        August: 7,
    130        September: 8,
    131        October: 9,
    132        November: 10,
    133        December: 11,
    134    };
    135    global.Month = Month;
    136 
    137    const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].join("|");
    138    const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].join("|");
    139    const datePart = String.raw `(?:${weekdays}) (?:${months}) \d{2} -?\d{4,6}`;
    140    const timePart = String.raw `\d{2}:\d{2}:\d{2} GMT[+-]\d{4}`;
    141    const dateTimeRE = new RegExp(String.raw `^(${datePart} ${timePart})(?: \((.+)\))?$`);
    142 
    143    function assertDateTime(date, expected, ...alternativeTimeZones) {
    144        let actual = date.toString();
    145        assertEq(dateTimeRE.test(expected), true, `${expected}`);
    146        assertEq(dateTimeRE.test(actual), true, `${actual}`);
    147 
    148        let [, expectedDateTime, expectedTimeZone] = dateTimeRE.exec(expected);
    149        let [, actualDateTime, actualTimeZone] = dateTimeRE.exec(actual);
    150 
    151        assertEq(actualDateTime, expectedDateTime);
    152 
    153        // The time zone identifier is optional, so only compare its value if
    154        // it's present in |actual| and |expected|.
    155        if (expectedTimeZone !== undefined && actualTimeZone !== undefined) {
    156            // Test against the alternative time zone identifiers if necessary.
    157            if (actualTimeZone !== expectedTimeZone) {
    158                for (let alternativeTimeZone of alternativeTimeZones) {
    159                    if (actualTimeZone === alternativeTimeZone) {
    160                        expectedTimeZone = alternativeTimeZone;
    161                        break;
    162                    }
    163                }
    164            }
    165            assertEq(actualTimeZone, expectedTimeZone);
    166        }
    167    }
    168    global.assertDateTime = assertDateTime;
    169 })(this);
    170 
    171 
    172 function runDSTOffsetCachingTestsFraction(part, parts)
    173 {
    174  var BUGNUMBER = 563938;
    175  var summary = 'Cache DST offsets to improve SunSpider score';
    176 
    177  print(BUGNUMBER + ": " + summary);
    178 
    179  var MAX_UNIX_TIMET = 2145859200; // "2037-12-31T08:00:00.000Z" (PST8PDT based!)
    180  var RANGE_EXPANSION_AMOUNT = 30 * 24 * 60 * 60;
    181 
    182  /**
    183   * Computes the time zone offset in minutes at the given timestamp.
    184   */
    185  function tzOffsetFromUnixTimestamp(timestamp)
    186  {
    187    var d = new Date(NaN);
    188    d.setTime(timestamp); // local slot = NaN, UTC slot = timestamp
    189    return d.getTimezoneOffset(); // get UTC, calculate local => diff in minutes
    190  }
    191 
    192  /**
    193   * Clear the DST offset cache, leaving it initialized to include a timestamp
    194   * completely unlike the provided one (i.e. one very, very far away in time
    195   * from it).  Thus an immediately following lookup for the provided timestamp
    196   * will cache-miss and compute a clean value.
    197   */
    198  function clearDSTOffsetCache(undesiredTimestamp)
    199  {
    200    var opposite = (undesiredTimestamp + MAX_UNIX_TIMET / 2) % MAX_UNIX_TIMET;
    201 
    202    // Generic purge to known, but not necessarily desired, state
    203    tzOffsetFromUnixTimestamp(0);
    204    tzOffsetFromUnixTimestamp(MAX_UNIX_TIMET);
    205 
    206    // Purge to desired state.  Cycle 2x in case opposite or undesiredTimestamp
    207    // is close to 0 or MAX_UNIX_TIMET.
    208    tzOffsetFromUnixTimestamp(opposite);
    209    tzOffsetFromUnixTimestamp(undesiredTimestamp);
    210    tzOffsetFromUnixTimestamp(opposite);
    211    tzOffsetFromUnixTimestamp(undesiredTimestamp);
    212  }
    213 
    214  function computeCanonicalTZOffset(timestamp)
    215  {
    216    clearDSTOffsetCache(timestamp);
    217    return tzOffsetFromUnixTimestamp(timestamp);
    218  }
    219 
    220  var TEST_TIMESTAMPS_SECONDS =
    221    [
    222     // Special-ish timestamps
    223     0,
    224     RANGE_EXPANSION_AMOUNT,
    225     MAX_UNIX_TIMET,
    226    ];
    227 
    228  var ONE_DAY = 24 * 60 * 60;
    229  var EIGHTY_THREE_HOURS = 83 * 60 * 60;
    230  var NINETY_EIGHT_HOURS = 98 * 60 * 60;
    231  function nextIncrement(i)
    232  {
    233    return i === EIGHTY_THREE_HOURS ? NINETY_EIGHT_HOURS : EIGHTY_THREE_HOURS;
    234  }
    235 
    236  // Now add a long sequence of non-special timestamps, from a fixed range, that
    237  // overlaps a DST change by "a bit" on each side.  67 days should be enough
    238  // displacement that we can occasionally exercise the implementation's
    239  // thirty-day expansion and the DST-offset-change logic.  Use two different
    240  // increments just to be safe and catch something a single increment might not.
    241  var DST_CHANGE_DATE = 1268553600; // March 14, 2010
    242  for (var t = DST_CHANGE_DATE - 67 * ONE_DAY,
    243           i = nextIncrement(NINETY_EIGHT_HOURS),
    244           end = DST_CHANGE_DATE + 67 * ONE_DAY;
    245       t < end;
    246       i = nextIncrement(i), t += i)
    247  {
    248    TEST_TIMESTAMPS_SECONDS.push(t);
    249  }
    250 
    251  var TEST_TIMESTAMPS =
    252    TEST_TIMESTAMPS_SECONDS.map(function(v) { return v * 1000; });
    253 
    254  /**************
    255   * BEGIN TEST *
    256   **************/
    257 
    258  // Compute the correct time zone offsets for all timestamps to be tested.
    259  var CORRECT_TZOFFSETS = TEST_TIMESTAMPS.map(computeCanonicalTZOffset);
    260 
    261  // Intentionally and knowingly invoking every single logic path in the cache
    262  // isn't easy for a human to get right (and know he's gotten it right), so
    263  // let's do it the easy way: exhaustively try all possible four-date sequences
    264  // selecting from our array of possible timestamps.
    265 
    266  var sz = TEST_TIMESTAMPS.length;
    267  var start = Math.floor((part - 1) / parts * sz);
    268  var end = Math.floor(part / parts * sz);
    269 
    270  print("Exhaustively testing timestamps " +
    271        "[" + start + ", " + end + ") of " + sz + "...");
    272 
    273  try
    274  {
    275    for (var i = start; i < end; i++)
    276    {
    277      print("Testing timestamp " + i + "...");
    278 
    279      var t1 = TEST_TIMESTAMPS[i];
    280      for (var j = 0; j < sz; j++)
    281      {
    282        var t2 = TEST_TIMESTAMPS[j];
    283        for (var k = 0; k < sz; k++)
    284        {
    285          var t3 = TEST_TIMESTAMPS[k];
    286          for (var w = 0; w < sz; w++)
    287          {
    288            var t4 = TEST_TIMESTAMPS[w];
    289 
    290            clearDSTOffsetCache(t1);
    291 
    292            var tzo1 = tzOffsetFromUnixTimestamp(t1);
    293            var tzo2 = tzOffsetFromUnixTimestamp(t2);
    294            var tzo3 = tzOffsetFromUnixTimestamp(t3);
    295            var tzo4 = tzOffsetFromUnixTimestamp(t4);
    296 
    297            assertEq(tzo1, CORRECT_TZOFFSETS[i]);
    298            assertEq(tzo2, CORRECT_TZOFFSETS[j]);
    299            assertEq(tzo3, CORRECT_TZOFFSETS[k]);
    300            assertEq(tzo4, CORRECT_TZOFFSETS[w]);
    301          }
    302        }
    303      }
    304    }
    305  }
    306  catch (e)
    307  {
    308    assertEq(true, false,
    309             "Error when testing with timestamps " +
    310             i + ", " + j + ", " + k + ", " + w +
    311             " (" + t1 + ", " + t2 + ", " + t3 + ", " + t4 + ")!");
    312  }
    313 
    314  reportCompare(true, true);
    315  print("All tests passed!");
    316 }