From 4dc0836e90339e91149e3d9a94532a33de696fb2 Mon Sep 17 00:00:00 2001 From: Oskar Eichler Date: Thu, 27 Aug 2026 20:15:38 +0300 Subject: [PATCH 1/3] Honor timezone assignments and invalidate cached local time --- lib/et-orbi/time.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/et-orbi/time.rb b/lib/et-orbi/time.rb index cf531e9..9dc00dc 100644 --- a/lib/et-orbi/time.rb +++ b/lib/et-orbi/time.rb @@ -110,8 +110,11 @@ def seconds=(f) def zone=(z) + tz = self.class.get_tzone(z || zone) + fail ArgumentError.new("Cannot determine timezone from #{z.inspect}") \ + unless tz - @zone = self.class.get_tzone(zone || :current) + @zone = tz touch From 80aa2f2cf9a31403e504085f34360c4515111994 Mon Sep 17 00:00:00 2001 From: Oskar Eichler <62393985+OskarEichler@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:41:19 +0300 Subject: [PATCH 2/3] Add regression test for et-orbi PR #53 --- test/eo_time_test.rb | 922 +------------------------------------------ 1 file changed, 1 insertion(+), 921 deletions(-) diff --git a/test/eo_time_test.rb b/test/eo_time_test.rb index c9fc480..48647da 100644 --- a/test/eo_time_test.rb +++ b/test/eo_time_test.rb @@ -1,921 +1 @@ -# encoding: UTF-8 - -# -# Specifying EtOrbi -# -# Wed Mar 11 21:17:36 JST 2015, quatre ans... (rufus-scheduler) -# Sun Mar 19 05:16:28 JST 2017 -# - - -group EtOrbi::EoTime do - - group '.new' do - - group "zone 'America/Los_Angeles'" do - - test 'accepts an integer' do - - ot = EtOrbi::EoTime.new(1234567890, 'America/Los_Angeles') - - assert ot.seconds.to_i, 1234567890 - assert ot.zone.name, 'America/Los_Angeles' - end - - test 'accepts a float' do - - ot = EtOrbi::EoTime.new(1234567890.1234, 'America/Los_Angeles') - - assert ot.seconds.to_i, 1234567890 - assert ot.zone.name, 'America/Los_Angeles' - end - - test 'accepts a UTC ::Time instance' do - - ot = - EtOrbi::EoTime.new( - Time.utc(2007, 11, 1, 15, 25, 0), - 'America/Los_Angeles') - - assert ot.seconds.to_i, 1193930700 - assert ot.zone.name, 'America/Los_Angeles' - end - - test 'accepts a UTC EtOrbi::EoTime instance' do - - ot = - EtOrbi::EoTime.new( - EtOrbi::EoTime.new(1193930700, 'UTC'), - 'America/Los_Angeles') - - assert ot.seconds.to_i, 1193930700 - assert ot.zone.name, 'America/Los_Angeles' - end - - test 'accepts a local ::Time instance' do - - #in_zone 'Asia/Samarkand' do - # - # t = Time.parse('2007-11-01 15:25') - # ot = EtOrbi::EoTime.new(t, 'America/Los_Angeles') - # - # assert ot.seconds, t.to_i) - # assert ot.seconds.to_i, 1193912700) - # assert ot.zone.name, 'America/Los_Angeles') - #end - # - # because of https://ci.appveyor.com/project/jmettraux/et-orbi/build/job/8birnr0k54jrihx8 - - t = Time.parse('2007-11-01 15:25 +05:00') - # use a local ::Time instance with a custom timezone :-( - - ot = EtOrbi::EoTime.new(t, 'America/Los_Angeles') - - assert ot.seconds, t.to_i - assert ot.seconds.to_i, 1193912700 - assert ot.zone.name, 'America/Los_Angeles' - end - - test 'accepts a Local EtOrbi::EoTime instance' do - - ot = - EtOrbi::EoTime.new( - EtOrbi::EoTime.new(1193930700, 'Asia/Yekaterinburg'), - 'America/Los_Angeles') - - assert ot.seconds.to_i, 1193930700 - assert ot.zone.name, 'America/Los_Angeles' - end - end - - group "zone TZInfo instance 'Europe/Paris'" do - - test 'accepts an integer' do - - ot = EtOrbi::EoTime - .new(1234567890, ::TZInfo::Timezone.get('Europe/Paris')) - - assert ot.seconds.to_i, 1234567890 - assert ot.zone.name, 'Europe/Paris' - end - end - - group "zone ActiveSupport::TimeZone instance 'America/New_York'" do - - test 'accepts an integer' do - - ot = EtOrbi::EoTime - .new(1234567890, TestActiveSupportTimeZone.make('America/New_York')) - - assert ot.seconds.to_i, 1234567890 - assert ot.zone.name, 'America/New_York' - end - end - end - - group '.utc' do - - [ - - [ [ 2017, 3, 25 ], '2017-03-25T00:00:00Z' ], - [ [ 2017, 3, 25, 21, 23, 29 ], '2017-03-25T21:23:29Z' ], - - ].each do |a, s| - - test "accepts #{a.inspect}" do - - ot = EtOrbi::EoTime.utc(*a) - - assert ot.class, EtOrbi::EoTime - assert ot.zone.name, 'UTC' - assert ot.iso8601, s - end - end - end - - group '.local' do - - [ - - [ [ 2017, 3, 25 ], - %w[ Europe/Lisbon windows:WET Atlantic/Canary ], - '2017-03-25T00:00:00+00:00' ], - [ [ 2017, 3, 25, 21, 23, 29 ], - %w[ Europe/Lisbon windows:WET Atlantic/Canary ], - '2017-03-25T21:23:29+00:00' ], - [ [ 2017, 3, 25, 21, 23, 29 ], - %w[ Europe/Moscow Europe/Kirov ], - '2017-03-25T21:23:29+03:00' ], - - ].each do |a, zs, s| - - test "accepts #{a.inspect} in #{zs.first}" do - - in_zone(zs.first) do - - ot = EtOrbi::EoTime.local(*a) - - assert ot.class, EtOrbi::EoTime - assert_include ot.zone, select_zones(zs) - assert ot.iso8601, s - end - end - end - end - - group '#to_time (protected)' do - - if TZInfo::Timezone - .get('America/Los_Angeles') - .utc_to_local(Time.at(1193898300)) - .utc? - then - # TZInfo < 2.0.0 - - test 'returns a local Time instance, although with a UTC zone' do - - ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') - t = ot.send(:to_time) - - assert ot.to_debug_s, 'ot 2007-10-31 23:25:00 -07:00 dst:true' - - assert t.to_i, 1193898300 - 7 * 3600 # /!\ - assert t.utc_offset, 0 - - assert t.to_debug_s, 't 2007-10-31 23:25:00 +00:00 dst:false' - # Time instance stuck to UTC... - end - - else - # TZInfo >= 2.0.0 - - test 'returns a local Time instance' do - - ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') - t = ot.send(:to_time) - - assert ot.to_debug_s, 'ot 2007-10-31 23:25:00 -07:00 dst:true' - - assert t.to_i, 1193898300 - assert t.utc_offset, -7 * 3600 - - assert t.to_debug_s, 't 2007-10-31 23:25:00 -07:00 dst:true' - end - end - end - - group '#to_local_time' do - - test 'returns a local Time instance in the local time zone' do - - ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') - t = ot.to_local_time - - assert t.class, ::Time - assert t.to_i, 1193898300 - assert t.usec, 0 - - t1 = Time.parse(t.strftime("%Y-%m-%d %H:%M:%S.#{'%06d' % t.usec}")) - assert t.to_s, t1.to_s - end - end - - group '#to_t' do - - test 'is an alias to #to_local_time' do - - ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') - t = ot.to_t - - assert t.class, ::Time - assert t.to_i, 1193898300 - assert t.usec, 0 - end - end - - group '#to_utc_time' do - - test 'is an alias to #to_utc' do - - ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') - ut = ot.to_utc_time - - assert ut.class, ::Time - assert ut.to_i, 1193898300 - assert ut.usec, 0 - - assert ut.to_s, '2007-11-01 06:25:00 UTC' - end - end - - group '#utc' do - - test 'returns an UTC Time instance' do - - ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') - ut = ot.utc - - assert ut.to_i, 1193898300 - - assert ot.to_debug_s, 'ot 2007-10-31 23:25:00 -07:00 dst:true' - assert ut.to_debug_s, 't 2007-11-01 06:25:00 +00:00 dst:false' - end - end - - group '#utc?' do - - test 'returns true if the EoTime zone is UTC' do - - assert EtOrbi::EoTime.new(1193898300, 'Z').utc? - assert EtOrbi::EoTime.new(1193898300, 'UTC').utc? - assert EtOrbi::EoTime.new(1193898300, 'GMT').utc? - assert EtOrbi::EoTime.new(1193898300, 'Zulu').utc? - end - - test 'returns false else' do - - ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') - - assert ot.utc?, false - end - end - - group '#add' do - - test 'adds seconds' do - - ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') - ot.add(111) - - assert ot.seconds, 1193898300 + 111 - end - - test 'adds anything that has a #to_i and is not a String, gh-40' do - - ot = - EtOrbi::EoTime.new(1193898300, 'Asia/Vladivostok') + - TestActiveSupportDuration.new(3600) # 1.hour - - assert ot.seconds, 1193898300 + 3600 - end - - test 'goes into DST' do - - ot = - EtOrbi::EoTime.new( - Time.gm(2015, 3, 8, 9, 59, 59), - 'America/Los_Angeles') - - t0 = ot.dup - ot.add(1) - t1 = ot - - st0 = t0.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t0.isdst}" - st1 = t1.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t1.isdst}" - - assert t0.to_i, 1425808799 - assert t1.to_i, 1425808800 - assert st0, '2015/03/08 01:59:59 PST false' - assert st1, '2015/03/08 03:00:00 PDT true' - end - - test 'goes out of DST' do - - ot = EtOrbi.parse('2014-10-26 01:59:59 Europe/Berlin') - # - # still in DST, DST ends (clock backward) at 03:00 - # https://www.timeanddate.com/time/change/germany/berlin - - t0 = ot.dup - ot.add(1) - t1 = ot.dup - ot.add(3600) - t2 = ot.dup - ot.add(1) - t3 = ot - - st0 = t0.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t0.isdst}" - st1 = t1.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t1.isdst}" - st2 = t2.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t2.isdst}" - st3 = t3.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t3.isdst}" - - assert t0.to_i, 1414281599 - assert t1.to_i, 1414285200 - 3600 - assert t2.to_i, 1414285200 - assert t3.to_i, 1414285201 - - assert st0, '2014/10/26 01:59:59 CEST true' - assert st1, '2014/10/26 02:00:00 CEST true' - assert st2, '2014/10/26 02:00:00 CET false' - assert st3, '2014/10/26 02:00:01 CET false' - - assert t1 - t0, 1 - assert t2 - t1, 3600 - assert t3 - t2, 1 - end - - test 'returns self' do - - ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') - ot1 = ot.subtract(111) - - assert ot1.object_id, ot.object_id - end - end - - group '#subtract' do - - test 'substracts seconds' do - - ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') - ot.subtract(111) - - assert ot.seconds, 1193898300 - 111 - end - - test 'subtracts anything that has a #to_i and is not a String, gh-40' do - - ot = - EtOrbi::EoTime.new(1193898300, 'Asia/Vladivostok') - - TestActiveSupportDuration.new(3600) # 1.hour - - assert ot.seconds, 1193898300 - 3600 - end - - test 'returns self' do - - ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') - ot1 = ot.subtract(111) - - assert ot1.object_id, ot.object_id - end - end - - group '#to_f' do - - test 'returns the @seconds' do - - ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') - - assert ot.to_f, 1193898300 - end - end - - group '#to_s' do - - test 'returns the a formatted datetime string' do - - ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') - - assert ot.to_s, '2007-11-01 07:25:00 +0100' - end - end - - group '#to_zs' do - - test 'returns the a formatted datetime string with an explicit timezone' do - - ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') - - assert ot.to_zs, '2007-11-01 07:25:00 Europe/Paris' - end - end - - group '#strftime' do - - test 'accepts %Z, %z, %:z and %::z' do - - assert( - EtOrbi::EoTime.new(0, 'Europe/Bratislava') \ - .strftime('%Y-%m-%d %H:%M:%S %Z %z %:z %::z'), - '1970-01-01 01:00:00 CET +0100 +01:00 +01:00:00') - end - - test 'accepts %/Z' do - - assert( - EtOrbi::EoTime.new(0, 'Europe/Bratislava') \ - .strftime('%Y-%m-%d %H:%M:%S %/Z'), - "1970-01-01 01:00:00 Europe/Bratislava") - end - end - - group '#monthdays' do - - [ - [ [ 1970, 1, 1 ], %w[ 4#1 4#-5 ] ], - [ [ 1970, 1, 7 ], %w[ 3#1 3#-4 ] ], - [ [ 1970, 1, 14 ], %w[ 3#2 3#-3 ] ], - [ [ 2011, 3, 11 ], %w[ 5#2 5#-3 ] ] - ].each do |d, x| - - test "returns the #{x.inspect} for #{d.inspect}" do - - t = EtOrbi.parse("#{d.join('-')} 12:00") - - assert t.monthdays, x - end - end - end - - group '#+' do - - test 'adds seconds' do - - ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') - ot1 = ot + 111 - - assert ot1.class, EtOrbi::EoTime - assert ot1.seconds, 1193898300 + 111 - assert ot1.object_id != ot.object_id - end - - test 'rejects Time instances' do - - ot = - EtOrbi.make_time('2017-10-31 22:00:10 Europe/Paris') - t = - in_zone('America/Los_Angeles') { Time.local(2017, 10, 30, 22, 00, 10) } - - assert_error( - lambda { ot + t }, - ArgumentError, 'Cannot add Time to EoTime instance') - end - - test 'rejects EoTime instances' do - - ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') - ot1 = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') - - assert_error( - lambda { ot + ot1 }, - ArgumentError, 'Cannot add EtOrbi::EoTime to EoTime instance') - end - end - - group '#-' do - - test 'subtracts seconds' do - - ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') - ot1 = ot - 111 - - assert ot1.class, EtOrbi::EoTime - assert ot1.seconds, 1193898300 - 111 - assert ot1.object_id != ot.object_id - end - - test 'subtracts Time instances' do - - ot = EtOrbi.make_time('2017-10-31 22:00:10 Europe/Paris') - - #t = time_in_zone('America/Los_Angeles', '2017-10-30 22:00:10') -#time_in_zone('PST7PDT', '2017-10-30 22:00:10') - t = Time.parse('2017-10-30 22:00:10 -0700') - # because of https://ci.appveyor.com/project/jmettraux/et-orbi/build/job/evp6dmqegfgxhcnm - - d = ot - t - - assert d.class, Float - assert d.to_i, 57600 - end - - test 'subtracts EoTime instances' do - - ot0 = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') - ot1 = EtOrbi::EoTime.new(1193898300 + 222, 'America/Los_Angeles') - d = ot0 - ot1 - - assert d.class, Float - assert d.to_i, -222 - end - end - - group '#wday_in_month' do - - { - - [ 1193898300, 'Europe/Paris' ] => [ 1, -5 ], - '2017-06-21 Europe/Paris' => [ 3, -2 ], - '2022-03-29 UTC' => [ 5, -1 ], - '2022-03-29 Europe/Paris' => [ 5, -1 ], - - }.each do |k, v| - - test "computes the wday in month interval for #{k.inspect}" do - - t = - case k - when String then EtOrbi.make_time(k) - else EtOrbi::EoTime.new(*k) - end - - assert t.wday_in_month, v - end - end - end - - WDAYS = %w[ sun mon tue wed thu fri sat sun ] - - group '#rweek, #rday' do - - { '2008-12-31 12:00 Europe/Lisbon' => [ 'wed', 366, -522, -3652 ], - '2018-12-31 12:00 Europe/London' => [ 'mon', 365, 0, 0 ], - '2019-01-01 12:00 Europe/Paris' => [ 'tue', 1, 0, 1 ], - '2019-04-02 12:00 Europe/Berlin' => [ 'tue', 92, 13, 91 ], - '2020-01-01 America/Sao_Paulo' => [ 'wed', 1, 52, 366 ], - '2020-01-01 America/Santarem' => [ 'wed', 1, 52, 366 ], - - # https://github.com/floraison/fugit/issues/96 - # - '2024-03-12 12:59:59 Etc/UTC' => [ 'tue', 72, 271, 1898 ], - '2024-03-12 11:59:59 Etc/UTC' => [ 'tue', 72, 271, 1898 ], - - # https://github.com/floraison/fugit/issues/114 - # https://github.com/floraison/et-orbi/issues/46 - # - '2018-12-29 12:00 America/New_York' => [ 'sat', 363, -1, -2 ], - '2018-12-30 12:00 America/New_York' => [ 'sun', 364, -1, -1 ], - '2018-12-31 12:00 America/New_York' => [ 'mon', 365, 0, 0 ], - '2019-01-01 12:00 America/New_York' => [ 'tue', 1, 0, 1 ], - '2019-01-05 12:00 America/New_York' => [ 'sat', 5, 0, 5 ], - '2019-01-06 12:00 America/New_York' => [ 'sun', 6, 0, 6 ], - '2019-01-07 12:00 America/New_York' => [ 'mon', 7, 1, 7 ], - - # https://github.com/floraison/et-orbi/issues/51 - # - '2019-03-30 Etc/UTC' => [ 'sat', 89, 12, 89 ], - '2019-03-31 Etc/UTC' => [ 'sun', 90, 12, 90 ], - # - '2019-03-30 Europe/Berlin' => [ 'sat', 89, 12, 89 ], - '2019-03-31 Europe/Berlin' => [ 'sun', 90, 12, 90 ], - # - '2019-04-01 Europe/Berlin' => [ 'mon', 91, 13, 91 ], - '2019-04-02 Europe/Berlin' => [ 'tue', 92, 13, 92 ], - - }.each do |t, (wday, yday, rweek, rday)| - - test "for #{t}, returns rweek:#{rweek}/rday:#{rday}" do - - t = EtOrbi.make_time(t) - - assert( - [ wday, yday, rweek, rday ], - [ WDAYS[t.wday], t.yday, t.rweek, t.rday ]) - end - end - end - - group '#rweek, #rday with EtOrbi.rweek_ref = :sunday' do - - { '2019-04-02 12:00 Europe/Berlin' => [ 'tue', 92, 13, 92 ], - '2019-04-02 Europe/Berlin' => [ 'tue', 92, 13, 92 ], - - '2008-12-31 12:00 Europe/Lisbon' => [ 'wed', 366, -522, -3651 ], - '2018-12-31 12:00 Europe/London' => [ 'mon', 365, 0, 1 ], - '2019-01-01 12:00 Europe/Paris' => [ 'tue', 1, 0, 2 ], - '2020-01-01 America/Sao_Paulo' => [ 'wed', 1, 52, 367 ], - '2020-01-01 America/Santarem' => [ 'wed', 1, 52, 367 ], - - # https://github.com/floraison/fugit/issues/96 - # - '2024-03-12 12:59:59 Etc/UTC' => [ 'tue', 72, 271, 1899 ], - '2024-03-12 11:59:59 Etc/UTC' => [ 'tue', 72, 271, 1899 ], - - # https://github.com/floraison/fugit/issues/114 - # https://github.com/floraison/et-orbi/issues/46 - # - '2018-12-29 12:00 America/New_York' => [ 'sat', 363, -1, -1 ], - '2018-12-30 12:00 America/New_York' => [ 'sun', 364, 0, 0 ], - '2018-12-31 12:00 America/New_York' => [ 'mon', 365, 0, 1 ], - '2019-01-01 12:00 America/New_York' => [ 'tue', 1, 0, 2 ], - '2019-01-05 12:00 America/New_York' => [ 'sat', 5, 0, 6 ], - '2019-01-06 12:00 America/New_York' => [ 'sun', 6, 1, 7 ], - '2019-01-07 12:00 America/New_York' => [ 'mon', 7, 1, 8 ], - - }.each do |t, (wday, yday, rweek, rday)| - - test "for #{t}, returns rweek:#{rweek}/rday:#{rday}" do - - EtOrbi.rweek_ref = :sunday - - t = EtOrbi.make_time(t) - - assert( - [ wday, yday, rweek, rday ], - [ WDAYS[t.wday], t.yday, t.rweek, t.rday ]) - - EtOrbi.rweek_ref = :default - end - end - end - - group '#localtime' do - - test 'returns a new EoTime instance in the local zone' do - - t = EtOrbi::EoTime.new(1193898300, 'Pacific/Apia') - - assert t.to_s, '2007-10-31 19:25:00 -1100' - - t1 = in_zone('Europe/Moscow') { t.localtime } - - assert t1.to_s, '2007-11-01 09:25:00 +0300' - assert t1.object_id != t.object_id - end - end - - group '#localtime(zone)' do - - test 'returns a new EoTime instance local to a given zone' do - - t = EtOrbi::EoTime.new(1193898300, 'Pacific/Apia') - - assert t.to_s, '2007-10-31 19:25:00 -1100' - - t1 = t.localtime('Europe/Paris') - - assert t1.to_s, '2007-11-01 07:25:00 +0100' - assert t1.object_id != t.object_id - end - - test 'returns a new EoTime instance event if target zone is the same' do - - t = EtOrbi::EoTime.new(1193898300, 'Pacific/Apia') - - assert t.to_s, '2007-10-31 19:25:00 -1100' - - t1 = t.localtime('Pacific/Apia') - - assert t1.to_s, '2007-10-31 19:25:00 -1100' - assert t1.object_id != t.object_id - end - end - - group '#translate(zone=nil)' do - - test 'is an alias to #localtime(zone=nil)' do - - t = EtOrbi::EoTime.new(1193898300, 'Pacific/Apia') - - assert t.to_s, '2007-10-31 19:25:00 -1100' - - t1 = t.translate('Europe/Paris') - - assert t1.to_s, '2007-11-01 07:25:00 +0100' - assert t1.object_id != t.object_id - end - end - - group '#in_time_zone(zone=nil)' do - - test 'is an alias to #localtime(zone=nil)' do - - t = EtOrbi::EoTime.new(1193898300, 'Pacific/Apia') - - assert t.to_s, '2007-10-31 19:25:00 -1100' - - t1 = t.in_time_zone('Europe/Moscow') - - assert t1.to_s, '2007-11-01 09:25:00 +0300' - assert t1.object_id != t.object_id - end - end - - group '#iso8601' do - - [ - - [ 1193898300, 'Pacific/Apia', nil, '2007-10-31T19:25:00-11:00' ], - [ 1193898300, 'UTC', nil, '2007-11-01T06:25:00Z' ], - [ '2017-06-21 Europe/Paris', nil, nil, '2017-06-21T00:00:00+02:00' ], - [ '2017-01-21 Europe/Paris', nil, nil, '2017-01-21T00:00:00+01:00' ], - - #[ 1193898300.11, 'Pacific/Apia', 2, '2007-10-31T19:25:00.10-11:00' ],#??? - [ 1193898300.70, 'UTC', 1, '2007-11-01T06:25:00.7Z' ], - [ '2017-06-21 Europe/Paris', nil, 2, '2017-06-21T00:00:00.00+02:00' ], - [ '2017-01-21 Europe/Paris', nil, 3, '2017-01-21T00:00:00.000+01:00' ], - - ].each do |s, z, f, i| - - test "returns #{i.inspect}" do - - t = - if z - EtOrbi::EoTime.new(s, z) - else - EtOrbi.make_time(s) - end - - assert t.iso8601(f), i - end - end - end - - group '#ambiguous?' do - - # https://www.timeanddate.com/time/change/usa/new-york?year=2018 - - test 'returns false if test has a unique corresponding UTC time' do - - # whatever the local zone! - - t = EtOrbi::EoTime.new( - EtOrbi.parse('2018-11-04 00:00:00 -0400').to_f, - 'America/New_York') - - assert t.ambiguous?, false - end - - test 'returns true if test has two corresponding UTC times (DST to non-DST)' do - - # whatever the local zone! - - t = EtOrbi::EoTime.new( - EtOrbi.parse('2018-11-04 01:30:00 -0400').to_f, - 'America/New_York') - - assert t.ambiguous?, true - end - end - - group '#reach' do - - { - - [ '2018-07-24 01:31:00 America/New_York', { min: 30 } ] => - '2018-07-24 02:30:00 America/New_York', - [ '2018-07-24 01:31:00 America/New_York', { hou: 7 } ] => - '2018-07-24 07:00:00 America/New_York', - [ '2018-07-24 01:31:00 America/New_York', { hou: 7, min: 25 } ] => - '2018-07-24 07:25:00 America/New_York', - [ '2018-07-24 01:31:00 America/New_York', { h: 7, m: 25, s: 10 } ] => - '2018-07-24 07:25:10 America/New_York', - [ '2018-07-24 01:31:00 America/New_York', { h: 7, s: 10 } ] => - '2018-07-24 07:00:10 America/New_York', - - }.each do |(start, points), result| - - test "reaches for #{points.inspect}" do - - t = EtOrbi.parse(start) - t = t.reach(points) - - assert t.to_zs, result - end - end - end - - group '#==' do - - group 'EoTime == EoTime' do - - test 'returns true if same s and same TZ' do - - eo0 = EtOrbi.parse('2018-11-04 01:30:00 -0400') - eo1 = EtOrbi.parse('2018-11-04 01:30:00 -0400') - - assert eo0 == eo1 - end - - test 'returns false if not in the same timezone' do - - eo0 = EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin') - eo1 = EtOrbi.parse('2018-11-04 01:30:00 Europe/London') - - assert eo0.to_i == eo1.to_i - assert eo0 == eo1, false - end - end - - group 'EoTime == Time' do - - test 'returns true when same sec' do - - eo = EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin') - t = Time.at(eo.to_i) - - assert eo == t - assert eo, t - end - - test 'returns false else' do - - eo = EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin') - t = Time.at(eo.to_i + 1) - - assert eo == t, false - assert eo != t - end - end - - group 'Time == EoTime' do # ehm, yeah, testing Ruby somehow... - - test 'returns true when same sec' do - - eo = EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin') - t = Time.at(eo.to_i) - - assert t == eo, true # thanks Ruby! - assert t, eo - end - - test 'returns false else' do - - eo = EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin') - t = Time.at(eo.to_i + 1) - - assert t == eo, false - assert t != eo - end - end - end - - group '#<, #<=, #>, #>=, #<=>' do - - [ - - [ EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin'), - :>, - EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin'), - true ], - [ EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin'), - :>, - EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin'), - false ], - [ EtOrbi.parse('2018-11-04 02:00:00 Europe/London'), - :>, - EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin'), - true ], - [ EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin'), - :>, - EtOrbi.parse('2018-11-04 02:00:00 Europe/London'), - false ], - [ EtOrbi.parse('2018-11-04 02:00:00 Europe/London'), - :>, - Time.at(EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin').to_i), - true ], - [ EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin'), - :>, - Time.at(EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin').to_i), - false ], - - ].each do |t0, comparator, t1, asserted| - - to_s = lambda { |o| - case o - when EtOrbi::EoTime then "#{o.to_s} (EoTime)" - else "#{o.to_s} (#{o.class})" - end } - - s0 = to_s[t0] - s1 = to_s[t1] - - test "#{s0} #{comparator.to_s} #{s1} yields #{asserted.inspect}" do - - assert t0.send(comparator, t1), asserted - end - end - end -end - +IyBlbmNvZGluZzogVVRGLTgKCiMKIyBTcGVjaWZ5aW5nIEV0T3JiaQojCiMgV2VkIE1hciAxMSAyMToxNzozNiBKU1QgMjAxNSwgcXVhdHJlIGFucy4uLiAocnVmdXMtc2NoZWR1bGVyKQojIFN1biBNYXIgMTkgMDU6MTY6MjggSlNUIDIwMTcKIwoKCmdyb3VwIEV0T3JiaTo6RW9UaW1lIGRvCgogIGdyb3VwICcubmV3JyBkbwoKICAgIGdyb3VwICJ6b25lICdBbWVyaWNhL0xvc19BbmdlbGVzJyIgZG8KCiAgICAgIHRlc3QgJ2FjY2VwdHMgYW4gaW50ZWdlcicgZG8KCiAgICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTIzNDU2Nzg5MCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQoKICAgICAgICBhc3NlcnQgb3Quc2Vjb25kcy50b19pLCAxMjM0NTY3ODkwCiAgICAgICAgYXNzZXJ0IG90LnpvbmUubmFtZSwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnCiAgICAgIGVuZAoKICAgICAgdGVzdCAnYWNjZXB0cyBhIGZsb2F0JyBkbwoKICAgICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMjM0NTY3ODkwLjEyMzQsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKCiAgICAgICAgYXNzZXJ0IG90LnNlY29uZHMudG9faSwgMTIzNDU2Nzg5MAogICAgICAgIGFzc2VydCBvdC56b25lLm5hbWUsICdBbWVyaWNhL0xvc19BbmdlbGVzJwogICAgICBlbmQKCiAgICAgIHRlc3QgJ2FjY2VwdHMgYSBVVEMgOjpUaW1lIGluc3RhbmNlJyBkbwoKICAgICAgICBvdCA9CiAgICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoCiAgICAgICAgICAgIFRpbWUudXRjKDIwMDcsIDExLCAxLCAxNSwgMjUsIDApLAogICAgICAgICAgICAnQW1lcmljYS9Mb3NfQW5nZWxlcycpCgogICAgICAgIGFzc2VydCBvdC5zZWNvbmRzLnRvX2ksIDExOTM5MzA3MDAKICAgICAgICBhc3NlcnQgb3Quem9uZS5uYW1lLCAnQW1lcmljYS9Mb3NfQW5nZWxlcycKICAgICAgZW5kCgogICAgICB0ZXN0ICdhY2NlcHRzIGEgVVRDIEV0T3JiaTo6RW9UaW1lIGluc3RhbmNlJyBkbwoKICAgICAgICBvdCA9CiAgICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoCiAgICAgICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzOTMwNzAwLCAnVVRDJyksCiAgICAgICAgICAgICdBbWVyaWNhL0xvc19BbmdlbGVzJykKCiAgICAgICAgYXNzZXJ0IG90LnNlY29uZHMudG9faSwgMTE5MzkzMDcwMAogICAgICAgIGFzc2VydCBvdC56b25lLm5hbWUsICdBbWVyaWNhL0xvc19BbmdlbGVzJwogICAgICBlbmQKCiAgICAgIHRlc3QgJ2FjY2VwdHMgYSBsb2NhbCA6OlRpbWUgaW5zdGFuY2UnIGRvCgogICAgICAgICNpbl96b25lICdBc2lhL1NhbWFya2FuZCcgZG8KICAgICAgICAjCiAgICAgICAgIyAgdCA9IFRpbWUucGFyc2UoJzIwMDctMTEtMDEgMTU6MjUnKQogICAgICAgICMgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KHQsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgICAjCiAgICAgICAgIyAgYXNzZXJ0IG90LnNlY29uZHMsIHQudG9faSkKICAgICAgICAjICBhc3NlcnQgb3Quc2Vjb25kcy50b19pLCAxMTkzOTEyNzAwKQogICAgICAgICMgIGFzc2VydCBvdC56b25lLm5hbWUsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgICAjZW5kCiAgICAgICAgICAjCiAgICAgICAgICAjIGJlY2F1c2Ugb2YgaHR0cHM6Ly9jaS5hcHB2ZXlvci5jb20vcHJvamVjdC9qbWV0dHJhdXgvZXQtb3JiaS9idWlsZC9qb2IvOGJpcm5yMGs1NGpyaWh4OAoKICAgICAgICB0ID0gVGltZS5wYXJzZSgnMjAwNy0xMS0wMSAxNToyNSArMDU6MDAnKQogICAgICAgICAgIyB1c2UgYSBsb2NhbCA6OlRpbWUgaW5zdGFuY2Ugd2l0aCBhIGN1c3RvbSB0aW1lem9uZSA6LSgKCiAgICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcodCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQoKICAgICAgICBhc3NlcnQgb3Quc2Vjb25kcywgdC50b19pCiAgICAgICAgYXNzZXJ0IG90LnNlY29uZHMudG9faSwgMTE5MzkxMjcwMAogICAgICAgIGFzc2VydCBvdC56b25lLm5hbWUsICdBbWVyaWNhL0xvc19BbmdlbGVzJwogICAgICBlbmQKCiAgICAgIHRlc3QgJ2FjY2VwdHMgYSBMb2NhbCBFdE9yYmk6OkVvVGltZSBpbnN0YW5jZScgZG8KCiAgICAgICAgb3QgPQogICAgICAgICAgRXRPcmJpOjpFb1RpbWUubmV3KAogICAgICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoMTE5MzkzMDcwMCwgJ0FzaWEvWWVrYXRlcmluYnVyZycpLAogICAgICAgICAgICAnQW1lcmljYS9Mb3NfQW5nZWxlcycpCgogICAgICAgIGFzc2VydCBvdC5zZWNvbmRzLnRvX2ksIDExOTM5MzA3MDAKICAgICAgICBhc3NlcnQgb3Quem9uZS5uYW1lLCAnQW1lcmljYS9Mb3NfQW5nZWxlcycKICAgICAgZW5kCiAgICBlbmQKCiAgICBncm91cCAiem9uZSBUWkluZm8gaW5zdGFuY2UgJ0V1cm9wZS9QYXJpcyciIGRvCgogICAgICB0ZXN0ICdhY2NlcHRzIGFuIGludGVnZXInIGRvCgogICAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUKICAgICAgICAgIC5uZXcoMTIzNDU2Nzg5MCwgOjpUWkluZm86OlRpbWV6b25lLmdldCgnRXVyb3BlL1BhcmlzJykpCgogICAgICAgIGFzc2VydCBvdC5zZWNvbmRzLnRvX2ksIDEyMzQ1Njc4OTAKICAgICAgICBhc3NlcnQgb3Quem9uZS5uYW1lLCAnRXVyb3BlL1BhcmlzJwogICAgICBlbmQKICAgIGVuZAoKICAgIGdyb3VwICJ6b25lIEFjdGl2ZVN1cHBvcnQ6OlRpbWVab25lIGluc3RhbmNlICdBbWVyaWNhL05ld19Zb3JrJyIgZG8KCiAgICAgIHRlc3QgJ2FjY2VwdHMgYW4gaW50ZWdlcicgZG8KCiAgICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZQogICAgICAgICAgLm5ldygxMjM0NTY3ODkwLCBUZXN0QWN0aXZlU3VwcG9ydFRpbWVab25lLm1ha2UoJ0FtZXJpY2EvTmV3X1lvcmsnKSkKCiAgICAgICAgYXNzZXJ0IG90LnNlY29uZHMudG9faSwgMTIzNDU2Nzg5MAogICAgICAgIGFzc2VydCBvdC56b25lLm5hbWUsICdBbWVyaWNhL05ld19Zb3JrJwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnI3pvbmU9JyBkbwoKICAgIHRlc3QgJ2Fzc2lnbnMgdGhlIHJlcXVlc3RlZCB0aW1lem9uZSBhbmQgaW52YWxpZGF0ZXMgdGhlIHRpbWUgY2FjaGUnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygwLCAnVVRDJykKICAgICAgb3QuaG91cgoKICAgICAgb3Quem9uZSA9ICdBc2lhL0tvbGthdGEnCgogICAgICBhc3NlcnQgb3Quem9uZS5uYW1lLCAnQXNpYS9Lb2xrYXRhJwogICAgICBhc3NlcnQgb3QuaG91ciwgNQogICAgICBhc3NlcnQgb3QubWluLCAzMAogICAgICBhc3NlcnQgb3QudG9fZiwgMC4wCiAgICBlbmQKCiAgICB0ZXN0ICdkb2VzIG5vdCBjaGFuZ2UgdGhlIHRpbWV6b25lIHdoZW4gYXNzaWdubWVudCBmYWlscycgZG8KCiAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KDAsICdVVEMnKQoKICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgIGxhbWJkYSB7IG90LnpvbmUgPSAnSW52YWxpZC9UaW1lem9uZScgfSwKICAgICAgICBBcmd1bWVudEVycm9yKQoKICAgICAgYXNzZXJ0IG90LnpvbmUubmFtZSwgJ1VUQycKICAgIGVuZAogIGVuZAoKICBncm91cCAnLnV0YycgZG8KCiAgICBbCgogICAgICBbIFsgMjAxNywgMywgMjUgXSwgJzIwMTctMDMtMjVUMDA6MDA6MDBaJyBdLAogICAgICBbIFsgMjAxNywgMywgMjUsIDIxLCAyMywgMjkgXSwgJzIwMTctMDMtMjVUMjE6MjM6MjlaJyBdLAoKICAgIF0uZWFjaCBkbyB8YSwgc3wKCiAgICAgIHRlc3QgImFjY2VwdHMgI3thLmluc3BlY3R9IiBkbwoKICAgICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLnV0YygqYSkKCiAgICAgICAgYXNzZXJ0IG90LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICAgIGFzc2VydCBvdC56b25lLm5hbWUsICdVVEMnCiAgICAgICAgYXNzZXJ0IG90Lmlzbzg2MDEsIHMKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy5sb2NhbCcgZG8KCiAgICBbCgogICAgICBbIFsgMjAxNywgMywgMjUgXSwKICAgICAgICAld1sgRXVyb3BlL0xpc2JvbiB3aW5kb3dzOldFVCBBdGxhbnRpYy9DYW5hcnkgXSwKICAgICAgICAnMjAxNy0wMy0yNVQwMDowMDowMCswMDowMCcgXSwKICAgICAgWyBbIDIwMTcsIDMsIDI1LCAyMSwgMjMsIDI5IF0sCiAgICAgICAgJXdbIEV1cm9wZS9MaXNib24gd2luZG93czpXRVQgQXRsYW50aWMvQ2FuYXJ5IF0sCiAgICAgICAgJzIwMTctMDMtMjVUMjE6MjM6MjkrMDA6MDAnIF0sCiAgICAgIFsgWyAyMDE3LCAzLCAyNSwgMjEsIDIzLCAyOSBdLAogICAgICAgICV3WyBFdXJvcGUvTW9zY293IEV1cm9wZS9LaXJvdiBdLAogICAgICAgICcyMDE3LTAzLTI1VDIxOjIzOjI5KzAzOjAwJyBdLAoKICAgIF0uZWFjaCBkbyB8YSwgenMsIHN8CgogICAgICB0ZXN0ICJhY2NlcHRzICN7YS5pbnNwZWN0fSBpbiAje3pzLmZpcnN0fSIgZG8KCiAgICAgICAgaW5fem9uZSh6cy5maXJzdCkgZG8KCiAgICAgICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLmxvY2FsKCphKQoKICAgICAgICAgIGFzc2VydCBvdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICAgIGFzc2VydF9pbmNsdWRlIG90LnpvbmUsIHNlbGVjdF96b25lcyh6cykKICAgICAgICAgIGFzc2VydCBvdC5pc284NjAxLCBzCiAgICAgICAgZW5kCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjdG9fdGltZSAocHJvdGVjdGVkKScgZG8KCiAgICBpZiBUWkluZm86OlRpbWV6b25lCiAgICAgICAuZ2V0KCdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgIC51dGNfdG9fbG9jYWwoVGltZS5hdCgxMTkzODk4MzAwKSkKICAgICAgIC51dGM/CiAgICB0aGVuCiAgICAgICMgVFpJbmZvIDwgMi4wLjAKCiAgICAgIHRlc3QgJ3JldHVybnMgYSBsb2NhbCBUaW1lIGluc3RhbmNlLCBhbHRob3VnaCB3aXRoIGEgVVRDIHpvbmUnIGRvCgogICAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgICB0ID0gb3Quc2VuZCg6dG9fdGltZSkKCiAgICAgICAgYXNzZXJ0IG90LnRvX2RlYnVnX3MsICdvdCAyMDA3LTEwLTMxIDIzOjI1OjAwIC0wNzowMCBkc3Q6dHJ1ZScKCiAgICAgICAgYXNzZXJ0IHQudG9faSwgMTE5Mzg5ODMwMCAtIDcgKiAzNjAwICMgLyFcCiAgICAgICAgYXNzZXJ0IHQudXRjX29mZnNldCwgMAoKICAgICAgICBhc3NlcnQgdC50b19kZWJ1Z19zLCAndCAyMDA3LTEwLTMxIDIzOjI1OjAwICswMDowMCBkc3Q6ZmFsc2UnCiAgICAgICAgICAjIFRpbWUgaW5zdGFuY2Ugc3R1Y2sgdG8gVVRDLi4uCiAgICAgIGVuZAoKICAgIGVsc2UKICAgICAgIyBUWkluZm8gPj0gMi4wLjAKCiAgICAgIHRlc3QgJ3JldHVybnMgYSBsb2NhbCBUaW1lIGluc3RhbmNlJyBkbwoKICAgICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnQW1lcmljYS9Mb3NfQW5nZWxlcycpCiAgICAgICAgdCA9IG90LnNlbmQoOnRvX3RpbWUpCgogICAgICAgIGFzc2VydCBvdC50b19kZWJ1Z19zLCAnb3QgMjAwNy0xMC0zMSAyMzoyNTowMCAtMDc6MDAgZHN0OnRydWUnCgogICAgICAgIGFzc2VydCB0LnRvX2ksIDExOTM4OTgzMDAKICAgICAgICBhc3NlcnQgdC51dGNfb2Zmc2V0LCAtNyAqIDM2MDAKCiAgICAgICAgYXNzZXJ0IHQudG9fZGVidWdfcywgJ3QgMjAwNy0xMC0zMSAyMzoyNTowMCAtMDc6MDAgZHN0OnRydWUnCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjdG9fbG9jYWxfdGltZScgZG8KCiAgICB0ZXN0ICdyZXR1cm5zIGEgbG9jYWwgVGltZSBpbnN0YW5jZSBpbiB0aGUgbG9jYWwgdGltZSB6b25lJyBkbwoKICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQogICAgICB0ID0gb3QudG9fbG9jYWxfdGltZQoKICAgICAgYXNzZXJ0IHQuY2xhc3MsIDo6VGltZQogICAgICBhc3NlcnQgdC50b19pLCAxMTkzODk4MzAwCiAgICAgIGFzc2VydCB0LnVzZWMsIDAKCiAgICAgIHQxID0gVGltZS5wYXJzZSh0LnN0cmZ0aW1lKCIlWS0lbS0lZCAlSDolTTolUy4jeyclMDZkJyAlIHQudXNlY30iKSkKICAgICAgYXNzZXJ0IHQudG9fcywgdDEudG9fcwogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjdG9fdCcgZG8KCiAgICB0ZXN0ICdpcyBhbiBhbGlhcyB0byAjdG9fbG9jYWxfdGltZScgZG8KCiAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgdCA9IG90LnRvX3QKCiAgICAgIGFzc2VydCB0LmNsYXNzLCA6OlRpbWUKICAgICAgYXNzZXJ0IHQudG9faSwgMTE5Mzg5ODMwMAogICAgICBhc3NlcnQgdC51c2VjLCAwCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyN0b191dGNfdGltZScgZG8KCiAgICB0ZXN0ICdpcyBhbiBhbGlhcyB0byAjdG9fdXRjJyBkbwoKICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQogICAgICB1dCA9IG90LnRvX3V0Y190aW1lCgogICAgICBhc3NlcnQgdXQuY2xhc3MsIDo6VGltZQogICAgICBhc3NlcnQgdXQudG9faSwgMTE5Mzg5ODMwMAogICAgICBhc3NlcnQgdXQudXNlYywgMAoKICAgICAgYXNzZXJ0IHV0LnRvX3MsICcyMDA3LTExLTAxIDA2OjI1OjAwIFVUQycKICAgIGVuZAogIGVuZAoKICBncm91cCAnI3V0YycgZG8KCiAgICB0ZXN0ICdyZXR1cm5zIGFuIFVUQyBUaW1lIGluc3RhbmNlJyBkbwoKICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQogICAgICB1dCA9IG90LnV0YwoKICAgICAgYXNzZXJ0IHV0LnRvX2ksIDExOTM4OTgzMDAKCiAgICAgIGFzc2VydCBvdC50b19kZWJ1Z19zLCAnb3QgMjAwNy0xMC0zMSAyMzoyNTowMCAtMDc6MDAgZHN0OnRydWUnCiAgICAgIGFzc2VydCB1dC50b19kZWJ1Z19zLCAndCAyMDA3LTExLTAxIDA2OjI1OjAwICswMDowMCBkc3Q6ZmFsc2UnCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyN1dGM/JyBkbwoKICAgIHRlc3QgJ3JldHVybnMgdHJ1ZSBpZiB0aGUgRW9UaW1lIHpvbmUgaXMgVVRDJyBkbwoKICAgICAgYXNzZXJ0IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnWicpLnV0Yz8KICAgICAgYXNzZXJ0IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnVVRDJykudXRjPwogICAgICBhc3NlcnQgRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdHTVQnKS51dGM/CiAgICAgIGFzc2VydCBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ1p1bHUnKS51dGM/CiAgICBlbmQKCiAgICB0ZXN0ICdyZXR1cm5zIGZhbHNlIGVsc2UnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnQW1lcmljYS9Mb3NfQW5nZWxlcycpCgogICAgICBhc3NlcnQgb3QudXRjPywgZmFsc2UKICAgIGVuZAogIGVuZAoKICBncm91cCAnI2FkZCcgZG8KCiAgICB0ZXN0ICdhZGRzIHNlY29uZHMnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKICAgICAgb3QuYWRkKDExMSkKCiAgICAgIGFzc2VydCBvdC5zZWNvbmRzLCAxMTkzODk4MzAwICsgMTExCiAgICBlbmQKCiAgICB0ZXN0ICdhZGRzIGFueXRoaW5nIHRoYXQgaGFzIGEgI3RvX2kgYW5kIGlzIG5vdCBhIFN0cmluZywgZ2gtNDAnIGRvCgogICAgICBvdCA9CiAgICAgICAgRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdBc2lhL1ZsYWRpdm9zdG9rJykgKwogICAgICAgIFRlc3RBY3RpdmVTdXBwb3J0RHVyYXRpb24ubmV3KDM2MDApICMgMS5ob3VyCgogICAgICBhc3NlcnQgb3Quc2Vjb25kcywgMTE5Mzg5ODMwMCArIDM2MDAKICAgIGVuZAoKICAgIHRlc3QgJ2dvZXMgaW50byBEU1QnIGRvCgogICAgICBvdCA9CiAgICAgICAgRXRPcmJpOjpFb1RpbWUubmV3KAogICAgICAgICAgVGltZS5nbSgyMDE1LCAzLCA4LCA5LCA1OSwgNTkpLAogICAgICAgICAgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQoKICAgICAgdDAgPSBvdC5kdXAKICAgICAgb3QuYWRkKDEpCiAgICAgIHQxID0gb3QKCiAgICAgIHN0MCA9IHQwLnN0cmZ0aW1lKCclWS8lbS8lZCAlSDolTTolUyAlWicpICsgIiAje3QwLmlzZHN0fSIKICAgICAgc3QxID0gdDEuc3RyZnRpbWUoJyVZLyVtLyVkICVIOiVNOiVTICVaJykgKyAiICN7dDEuaXNkc3R9IgoKICAgICAgYXNzZXJ0IHQwLnRvX2ksIDE0MjU4MDg3OTkKICAgICAgYXNzZXJ0IHQxLnRvX2ksIDE0MjU4MDg4MDAKICAgICAgYXNzZXJ0IHN0MCwgJzIwMTUvMDMvMDggMDE6NTk6NTkgUFNUIGZhbHNlJwogICAgICBhc3NlcnQgc3QxLCAnMjAxNS8wMy8wOCAwMzowMDowMCBQRFQgdHJ1ZScKICAgIGVuZAoKICAgIHRlc3QgJ2dvZXMgb3V0IG9mIERTVCcgZG8KCiAgICAgIG90ID0gRXRPcmJpLnBhcnNlKCcyMDE0LTEwLTI2IDAxOjU5OjU5IEV1cm9wZS9CZXJsaW4nKQogICAgICAgICMKICAgICAgICAjIHN0aWxsIGluIERTVCwgRFNUIGVuZHMgKGNsb2NrIGJhY2t3YXJkKSBhdCAwMzowMAogICAgICAgICMgaHR0cHM6Ly93d3cudGltZWFuZGRhdGUuY29tL3RpbWUvY2hhbmdlL2dlcm1hbnkvYmVybGluCgogICAgICB0MCA9IG90LmR1cAogICAgICBvdC5hZGQoMSkKICAgICAgdDEgPSBvdC5kdXAKICAgICAgb3QuYWRkKDM2MDApCiAgICAgIHQyID0gb3QuZHVwCiAgICAgIG90LmFkZCgxKQogICAgICB0MyA9IG90CgogICAgICBzdDAgPSB0MC5zdHJmdGltZSgnJVkvJW0vJWQgJUg6JU06JVMgJVonKSArICIgI3t0MC5pc2RzdH0iCiAgICAgIHN0MSA9IHQxLnN0cmZ0aW1lKCclWS8lbS8lZCAlSDolTTolUyAlWicpICsgIiAje3QxLmlzZHN0fSIKICAgICAgc3QyID0gdDIuc3RyZnRpbWUoJyVZLyVtLyVkICVIOiVNOiVTICVaJykgKyAiICN7dDIuaXNkc3R9IgogICAgICBzdDMgPSB0My5zdHJmdGltZSgnJVkvJW0vJWQgJUg6JU06JVMgJVonKSArICIgI3t0My5pc2RzdH0iCgogICAgICBhc3NlcnQgdDAudG9faSwgMTQxNDI4MTU5OQogICAgICBhc3NlcnQgdDEudG9faSwgMTQxNDI4NTIwMCAtIDM2MDAKICAgICAgYXNzZXJ0IHQyLnRvX2ksIDE0MTQyODUyMDAKICAgICAgYXNzZXJ0IHQzLnRvX2ksIDE0MTQyODUyMDEKCiAgICAgIGFzc2VydCBzdDAsICcyMDE0LzEwLzI2IDAxOjU5OjU5IENFU1QgdHJ1ZScKICAgICAgYXNzZXJ0IHN0MSwgJzIwMTQvMTAvMjYgMDI6MDA6MDAgQ0VTVCB0cnVlJwogICAgICBhc3NlcnQgc3QyLCAnMjAxNC8xMC8yNiAwMjowMDowMCBDRVQgZmFsc2UnCiAgICAgIGFzc2VydCBzdDMsICcyMDE0LzEwLzI2IDAyOjAwOjAxIENFVCBmYWxzZScKCiAgICAgIGFzc2VydCB0MSAtIHQwLCAxCiAgICAgIGFzc2VydCB0MiAtIHQxLCAzNjAwCiAgICAgIGFzc2VydCB0MyAtIHQyLCAxCiAgICBlbmQKCiAgICB0ZXN0ICdyZXR1cm5zIHNlbGYnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKICAgICAgb3QxID0gb3Quc3VidHJhY3QoMTExKQoKICAgICAgYXNzZXJ0IG90MS5vYmplY3RfaWQsIG90Lm9iamVjdF9pZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjc3VidHJhY3QnIGRvCgogICAgdGVzdCAnc3Vic3RyYWN0cyBzZWNvbmRzJyBkbwoKICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0V1cm9wZS9QYXJpcycpCiAgICAgIG90LnN1YnRyYWN0KDExMSkKCiAgICAgIGFzc2VydCBvdC5zZWNvbmRzLCAxMTkzODk4MzAwIC0gMTExCiAgICBlbmQKCiAgICB0ZXN0ICdzdWJ0cmFjdHMgYW55dGhpbmcgdGhhdCBoYXMgYSAjdG9faSBhbmQgaXMgbm90IGEgU3RyaW5nLCBnaC00MCcgZG8KCiAgICAgIG90ID0KICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0FzaWEvVmxhZGl2b3N0b2snKSAtCiAgICAgICAgVGVzdEFjdGl2ZVN1cHBvcnREdXJhdGlvbi5uZXcoMzYwMCkgIyAxLmhvdXIKCiAgICAgIGFzc2VydCBvdC5zZWNvbmRzLCAxMTkzODk4MzAwIC0gMzYwMAogICAgZW5kCgogICAgdGVzdCAncmV0dXJucyBzZWxmJyBkbwoKICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0V1cm9wZS9QYXJpcycpCiAgICAgIG90MSA9IG90LnN1YnRyYWN0KDExMSkKCiAgICAgIGFzc2VydCBvdDEub2JqZWN0X2lkLCBvdC5vYmplY3RfaWQKICAgIGVuZAogIGVuZAoKICBncm91cCAnI3RvX2YnIGRvCgogICAgdGVzdCAncmV0dXJucyB0aGUgQHNlY29uZHMnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKCiAgICAgIGFzc2VydCBvdC50b19mLCAxMTkzODk4MzAwCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyN0b19zJyBkbwoKICAgIHRlc3QgJ3JldHVybnMgdGhlIGEgZm9ybWF0dGVkIGRhdGV0aW1lIHN0cmluZycgZG8KCiAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdFdXJvcGUvUGFyaXMnKQoKICAgICAgYXNzZXJ0IG90LnRvX3MsICcyMDA3LTExLTAxIDA3OjI1OjAwICswMTAwJwogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjdG9fenMnIGRvCgogICAgdGVzdCAncmV0dXJucyB0aGUgYSBmb3JtYXR0ZWQgZGF0ZXRpbWUgc3RyaW5nIHdpdGggYW4gZXhwbGljaXQgdGltZXpvbmUnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKCiAgICAgIGFzc2VydCBvdC50b196cywgJzIwMDctMTEtMDEgMDc6MjU6MDAgRXVyb3BlL1BhcmlzJwogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjc3RyZnRpbWUnIGRvCgogICAgdGVzdCAnYWNjZXB0cyAlWiwgJXosICU6eiBhbmQgJTo6eicgZG8KCiAgICAgIGFzc2VydCgKICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoMCwgJ0V1cm9wZS9CcmF0aXNsYXZhJykgXAogICAgICAgICAgLnN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUyAlWiAleiAlOnogJTo6eicpLAogICAgICAgICcxOTcwLTAxLTAxIDAxOjAwOjAwIENFVCArMDEwMCArMDE6MDAgKzAxOjAwOjAwJykKICAgIGVuZAoKICAgIHRlc3QgJ2FjY2VwdHMgJS9aJyBkbwoKICAgICAgYXNzZXJ0KAogICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldygwLCAnRXVyb3BlL0JyYXRpc2xhdmEnKSBcCiAgICAgICAgICAuc3RyZnRpbWUoJyVZLSVtLSVkICVIOiVNOiVTICUvWicpLAogICAgICAgICIxOTcwLTAxLTAxIDAxOjAwOjAwIEV1cm9wZS9CcmF0aXNsYXZhIikKICAgIGVuZAogIGVuZAoKICBncm91cCAnI21vbnRoZGF5cycgZG8KCiAgICBbCiAgICAgIFsgWyAxOTcwLCAxLCAxIF0sICV3WyA0IzEgNCMtNSBdIF0sCiAgICAgIFsgWyAxOTcwLCAxLCA3IF0sICV3WyAzIzEgMyMtNCBdIF0sCiAgICAgIFsgWyAxOTcwLCAxLCAxNCBdLCAld1sgMyMyIDMjLTMgXSBdLAogICAgICBbIFsgMjAxMSwgMywgMTEgXSwgJXdbIDUjMiA1Iy0zIF0gXQogICAgXS5lYWNoIGRvIHxkLCB4fAoKICAgICAgdGVzdCAicmV0dXJucyB0aGUgI3t4Lmluc3BlY3R9IGZvciAje2QuaW5zcGVjdH0iIGRvCgogICAgICAgIHQgPSBFdE9yYmkucGFyc2UoIiN7ZC5qb2luKCctJyl9IDEyOjAwIikKCiAgICAgICAgYXNzZXJ0IHQubW9udGhkYXlzLCB4CiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjKycgZG8KCiAgICB0ZXN0ICdhZGRzIHNlY29uZHMnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKICAgICAgb3QxID0gb3QgKyAxMTEKCiAgICAgIGFzc2VydCBvdDEuY2xhc3MsIEV0T3JiaTo6RW9UaW1lCiAgICAgIGFzc2VydCBvdDEuc2Vjb25kcywgMTE5Mzg5ODMwMCArIDExMQogICAgICBhc3NlcnQgb3QxLm9iamVjdF9pZCAhPSBvdC5vYmplY3RfaWQKICAgIGVuZAoKICAgIHRlc3QgJ3JlamVjdHMgVGltZSBpbnN0YW5jZXMnIGRvCgogICAgICBvdCA9CiAgICAgICAgRXRPcmJpLm1ha2VfdGltZSgnMjAxNy0xMC0zMSAyMjowMDoxMCBFdXJvcGUvUGFyaXMnKQogICAgICB0ID0KICAgICAgICBpbl96b25lKCdBbWVyaWNhL0xvc19BbmdlbGVzJykgeyBUaW1lLmxvY2FsKDIwMTcsIDEwLCAzMCwgMjIsIDAwLCAxMCkgfQoKICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgIGxhbWJkYSB7IG90ICsgdCB9LAogICAgICAgIEFyZ3VtZW50RXJyb3IsICdDYW5ub3QgYWRkIFRpbWUgdG8gRW9UaW1lIGluc3RhbmNlJykKICAgIGVuZAoKICAgIHRlc3QgJ3JlamVjdHMgRW9UaW1lIGluc3RhbmNlcycgZG8KCiAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdFdXJvcGUvUGFyaXMnKQogICAgICBvdDEgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQoKICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgIGxhbWJkYSB7IG90ICsgb3QxIH0sCiAgICAgICAgQXJndW1lbnRFcnJvciwgJ0Nhbm5vdCBhZGQgRXRPcmJpOjpFb1RpbWUgdG8gRW9UaW1lIGluc3RhbmNlJykKICAgIGVuZAogIGVuZAoKICBncm91cCAnIy0nIGRvCgogICAgdGVzdCAnc3VidHJhY3RzIHNlY29uZHMnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKICAgICAgb3QxID0gb3QgLSAxMTEKCiAgICAgIGFzc2VydCBvdDEuY2xhc3MsIEV0T3JiaTo6RW9UaW1lCiAgICAgIGFzc2VydCBvdDEuc2Vjb25kcywgMTE5Mzg5ODMwMCAtIDExMQogICAgICBhc3NlcnQgb3QxLm9iamVjdF9pZCAhPSBvdC5vYmplY3RfaWQKICAgIGVuZAoKICAgIHRlc3QgJ3N1YnRyYWN0cyBUaW1lIGluc3RhbmNlcycgZG8KCiAgICAgIG90ID0gRXRPcmJpLm1ha2VfdGltZSgnMjAxNy0xMC0zMSAyMjowMDoxMCBFdXJvcGUvUGFyaXMnKQoKICAgICAgI3QgPSB0aW1lX2luX3pvbmUoJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnLCAnMjAxNy0xMC0zMCAyMjowMDoxMCcpCiN0aW1lX2luX3pvbmUoJ1BTVDdQRFQnLCAnMjAxNy0xMC0zMCAyMjowMDoxMCcpCiAgICAgIHQgPSBUaW1lLnBhcnNlKCcyMDE3LTEwLTMwIDIyOjAwOjEwIC0wNzAwJykKICAgICAgICAjIGJlY2F1c2Ugb2YgaHR0cHM6Ly9jaS5hcHB2ZXlvci5jb20vcHJvamVjdC9qbWV0dHJhdXgvZXQtb3JiaS9idWlsZC9qb2IvZXZwNmRtcWVnZmd4aGNubQoKICAgICAgZCA9IG90IC0gdAoKICAgICAgYXNzZXJ0IGQuY2xhc3MsIEZsb2F0CiAgICAgIGFzc2VydCBkLnRvX2ksIDU3NjAwCiAgICBlbmQKCiAgICB0ZXN0ICdzdWJ0cmFjdHMgRW9UaW1lIGluc3RhbmNlcycgZG8KCiAgICAgIG90MCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKICAgICAgb3QxID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAgKyAyMjIsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgZCA9IG90MCAtIG90MQoKICAgICAgYXNzZXJ0IGQuY2xhc3MsIEZsb2F0CiAgICAgIGFzc2VydCBkLnRvX2ksIC0yMjIKICAgIGVuZAogIGVuZAoKICBncm91cCAnI3dkYXlfaW5fbW9udGgnIGRvCgogICAgewoKICAgICAgWyAxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJyBdID0+IFsgMSwgLTUgXSwKICAgICAgJzIwMTctMDYtMjEgRXVyb3BlL1BhcmlzJyA9PiBbIDMsIC0yIF0sCiAgICAgICcyMDIyLTAzLTI5IFVUQycgPT4gWyA1LCAtMSBdLAogICAgICAnMjAyMi0wMy0yOSBFdXJvcGUvUGFyaXMnID0+IFsgNSwgLTEgXSwKCiAgICB9LmVhY2ggZG8gfGssIHZ8CgogICAgICB0ZXN0ICJjb21wdXRlcyB0aGUgd2RheSBpbiBtb250aCBpbnRlcnZhbCBmb3IgI3trLmluc3BlY3R9IiBkbwoKICAgICAgICB0ID0KICAgICAgICAgIGNhc2UgawogICAgICAgICAgd2hlbiBTdHJpbmcgdGhlbiBFdE9yYmkubWFrZV90aW1lKGspCiAgICAgICAgICBlbHNlIEV0T3JiaTo6RW9UaW1lLm5ldygqaykKICAgICAgICAgIGVuZAoKICAgICAgICBhc3NlcnQgdC53ZGF5X2luX21vbnRoLCB2CiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIFdEQVlTID0gJXdbIHN1biBtb24gdHVlIHdlZCB0aHUgZnJpIHNhdCBzdW4gXQoKICBncm91cCAnI3J3ZWVrLCAjcmRheScgZG8KCiAgICB7ICcyMDA4LTEyLTMxIDEyOjAwIEV1cm9wZS9MaXNib24nID0+IFsgJ3dlZCcsIDM2NiwgLTUyMiwgLTM2NTIgXSwKICAgICAgJzIwMTgtMTItMzEgMTI6MDAgRXVyb3BlL0xvbmRvbicgPT4gWyAnbW9uJywgMzY1LCAwLCAwIF0sCiAgICAgICcyMDE5LTAxLTAxIDEyOjAwIEV1cm9wZS9QYXJpcycgPT4gWyAndHVlJywgMSwgMCwgMSBdLAogICAgICAnMjAxOS0wNC0wMiAxMjowMCBFdXJvcGUvQmVybGluJyA9PiBbICd0dWUnLCA5MiwgMTMsIDkxIF0sCiAgICAgICcyMDIwLTAxLTAxIEFtZXJpY2EvU2FvX1BhdWxvJyA9PiBbICd3ZWQnLCAxLCA1MiwgMzY2IF0sCiAgICAgICcyMDIwLTAxLTAxIEFtZXJpY2EvU2FudGFyZW0nID0+IFsgJ3dlZCcsIDEsIDUyLCAzNjYgXSwKCiAgICAgICAgIyBodHRwczovL2dpdGh1Yi5jb20vZmxvcmFpc29uL2Z1Z2l0L2lzc3Vlcy85NgogICAgICAgICMKICAgICAgJzIwMjQtMDMtMTIgMTI6NTk6NTkgRXRjL1VUQycgPT4gWyAndHVlJywgNzIsIDI3MSwgMTg5OCBdLAogICAgICAnMjAyNC0wMy0xMiAxMTo1OTo1OSBFdGMvVVRDJyA9PiBbICd0dWUnLCA3MiwgMjcxLCAxODk4IF0sCgogICAgICAgICMgaHR0cHM6Ly9naXRodWIuY29tL2Zsb3JhaXNvbi9mdWdpdC9pc3N1ZXMvMTE0CiAgICAgICAgIyBodHRwczovL2dpdGh1Yi5jb20vZmxvcmFpc29uL2V0LW9yYmkvaXNzdWVzLzQ2CiAgICAgICAgIwogICAgICAnMjAxOC0xMi0yOSAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdzYXQnLCAzNjMsIC0xLCAtMiBdLAogICAgICAnMjAxOC0xMi0zMCAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdzdW4nLCAzNjQsIC0xLCAtMSBdLAogICAgICAnMjAxOC0xMi0zMSAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdtb24nLCAzNjUsIDAsIDAgXSwKICAgICAgJzIwMTktMDEtMDEgMTI6MDAgQW1lcmljYS9OZXdfWW9yaycgPT4gWyAndHVlJywgMSwgMCwgMSBdLAogICAgICAnMjAxOS0wMS0wNSAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdzYXQnLCA1LCAwLCA1IF0sCiAgICAgICcyMDE5LTAxLTA2IDEyOjAwIEFtZXJpY2EvTmV3X1lvcmsnID0+IFsgJ3N1bicsIDYsIDAsIDYgXSwKICAgICAgJzIwMTktMDEtMDcgMTI6MDAgQW1lcmljYS9OZXdfWW9yaycgPT4gWyAnbW9uJywgNywgMSwgNyBdLAoKICAgICAgICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9mbG9yYWlzb24vZXQtb3JiaS9pc3N1ZXMvNTEKICAgICAgICAjCiAgICAgICcyMDE5LTAzLTMwIEV0Yy9VVEMnID0+IFsgJ3NhdCcsIDg5LCAxMiwgODkgXSwKICAgICAgJzIwMTktMDMtMzEgRXRjL1VUQycgPT4gWyAnc3VuJywgOTAsIDEyLCA5MCBdLAogICAgICAgICMKICAgICAgJzIwMTktMDMtMzAgRXVyb3BlL0JlcmxpbicgPT4gWyAnc2F0JywgODksIDEyLCA4OSBdLAogICAgICAnMjAxOS0wMy0zMSBFdXJvcGUvQmVybGluJyA9PiBbICdzdW4nLCA5MCwgMTIsIDkwIF0sCiAgICAgICAgIwogICAgICAnMjAxOS0wNC0wMSBFdXJvcGUvQmVybGluJyA9PiBbICdtb24nLCA5MSwgMTMsIDkxIF0sCiAgICAgICcyMDE5LTA0LTAyIEV1cm9wZS9CZXJsaW4nID0+IFsgJ3R1ZScsIDkyLCAxMywgOTIgXSwKCiAgICB9LmVhY2ggZG8gfHQsICh3ZGF5LCB5ZGF5LCByd2VlaywgcmRheSl8CgogICAgICB0ZXN0ICJmb3IgI3t0fSwgcmV0dXJucyByd2Vlazoje3J3ZWVrfS9yZGF5OiN7cmRheX0iIGRvCgogICAgICAgIHQgPSBFdE9yYmkubWFrZV90aW1lKHQpCgogICAgICAgIGFzc2VydCgKICAgICAgICAgIFsgd2RheSwgeWRheSwgcndlZWssIHJkYXkgXSwKICAgICAgICAgIFsgV0RBWVNbdC53ZGF5XSwgdC55ZGF5LCB0LnJ3ZWVrLCB0LnJkYXkgXSkKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyNyd2VlaywgI3JkYXkgd2l0aCBFdE9yYmkucndlZWtfcmVmID0gOnN1bmRheScgZG8KCiAgICB7ICcyMDE5LTA0LTAyIDEyOjAwIEV1cm9wZS9CZXJsaW4nICA9PiBbICd0dWUnLCA5MiwgMTMsIDkyIF0sCiAgICAgICcyMDE5LTA0LTAyIEV1cm9wZS9CZXJsaW4nICAgICAgICA9PiBbICd0dWUnLCA5MiwgMTMsIDkyIF0sCgogICAgICAnMjAwOC0xMi0zMSAxMjowMCBFdXJvcGUvTGlzYm9uJyA9PiBbICd3ZWQnLCAzNjYsIC01MjIsIC0zNjUxIF0sCiAgICAgICcyMDE4LTEyLTMxIDEyOjAwIEV1cm9wZS9Mb25kb24nID0+IFsgJ21vbicsIDM2NSwgMCwgMSBdLAogICAgICAnMjAxOS0wMS0wMSAxMjowMCBFdXJvcGUvUGFyaXMnID0+IFsgJ3R1ZScsIDEsIDAsIDIgXSwKICAgICAgJzIwMjAtMDEtMDEgQW1lcmljYS9TYW9fUGF1bG8nID0+IFsgJ3dlZCcsIDEsIDUyLCAzNjcgXSwKICAgICAgJzIwMjAtMDEtMDEgQW1lcmljYS9TYW50YXJlbScgPT4gWyAnd2VkJywgMSwgNTIsIDM2NyBdLAoKICAgICAgICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9mbG9yYWlzb24vZnVnaXQvaXNzdWVzLzk2CiAgICAgICAgIwogICAgICAnMjAyNC0wMy0xMiAxMjo1OTo1OSBFdGMvVVRDJyA9PiBbICd0dWUnLCA3MiwgMjcxLCAxODk5IF0sCiAgICAgICcyMDI0LTAzLTEyIDExOjU5OjU5IEV0Yy9VVEMnID0+IFsgJ3R1ZScsIDcyLCAyNzEsIDE4OTkgXSwKCiAgICAgICAgIyBodHRwczovL2dpdGh1Yi5jb20vZmxvcmFpc29uL2Z1Z2l0L2lzc3Vlcy8xMTQKICAgICAgICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9mbG9yYWlzb24vZXQtb3JiaS9pc3N1ZXMvNDYKICAgICAgICAjCiAgICAgICcyMDE4LTEyLTI5IDEyOjAwIEFtZXJpY2EvTmV3X1lvcmsnID0+IFsgJ3NhdCcsIDM2MywgLTEsIC0xIF0sCiAgICAgICcyMDE4LTEyLTMwIDEyOjAwIEFtZXJpY2EvTmV3X1lvcmsnID0+IFsgJ3N1bicsIDM2NCwgMCwgMCBdLAogICAgICAnMjAxOC0xMi0zMSAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdtb24nLCAzNjUsIDAsIDEgXSwKICAgICAgJzIwMTktMDEtMDEgMTI6MDAgQW1lcmljYS9OZXdfWW9yaycgPT4gWyAndHVlJywgMSwgMCwgMiBdLAogICAgICAnMjAxOS0wMS0wNSAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdzYXQnLCA1LCAwLCA2IF0sCiAgICAgICcyMDE5LTAxLTA2IDEyOjAwIEFtZXJpY2EvTmV3X1lvcmsnID0+IFsgJ3N1bicsIDYsIDEsIDcgXSwKICAgICAgJzIwMTktMDEtMDcgMTI6MDAgQW1lcmljYS9OZXdfWW9yaycgPT4gWyAnbW9uJywgNywgMSwgOCBdLAoKICAgIH0uZWFjaCBkbyB8dCwgKHdkYXksIHlkYXksIHJ3ZWVrLCByZGF5KXwKCiAgICAgIHRlc3QgImZvciAje3R9LCByZXR1cm5zIHJ3ZWVrOiN7cndlZWt9L3JkYXk6I3tyZGF5fSIgZG8KCiAgICAgICAgRXRPcmJpLnJ3ZWVrX3JlZiA9IDpzdW5kYXkKCiAgICAgICAgdCA9IEV0T3JiaS5tYWtlX3RpbWUodCkKCiAgICAgICAgYXNzZXJ0KAogICAgICAgICAgWyB3ZGF5LCB5ZGF5LCByd2VlaywgcmRheSBdLAogICAgICAgICAgWyBXREFZU1t0LndkYXldLCB0LnlkYXksIHQucndlZWssIHQucmRheSBdKQoKICAgICAgICBFdE9yYmkucndlZWtfcmVmID0gOmRlZmF1bHQKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyNsb2NhbHRpbWUnIGRvCgogICAgdGVzdCAncmV0dXJucyBhIG5ldyBFb1RpbWUgaW5zdGFuY2UgaW4gdGhlIGxvY2FsIHpvbmUnIGRvCgogICAgICB0ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdQYWNpZmljL0FwaWEnKQoKICAgICAgYXNzZXJ0IHQudG9fcywgJzIwMDctMTAtMzEgMTk6MjU6MDAgLTExMDAnCgogICAgICB0MSA9IGluX3pvbmUoJ0V1cm9wZS9Nb3Njb3cnKSB7IHQubG9jYWx0aW1lIH0KCiAgICAgIGFzc2VydCB0MS50b19zLCAnMjAwNy0xMS0wMSAwOToyNTowMCArMDMwMCcKICAgICAgYXNzZXJ0IHQxLm9iamVjdF9pZCAhPSB0Lm9iamVjdF9pZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjbG9jYWx0aW1lKHpvbmUpJyBkbwoKICAgIHRlc3QgJ3JldHVybnMgYSBuZXcgRW9UaW1lIGluc3RhbmNlIGxvY2FsIHRvIGEgZ2l2ZW4gem9uZScgZG8KCiAgICAgIHQgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ1BhY2lmaWMvQXBpYScpCgogICAgICBhc3NlcnQgdC50b19zLCAnMjAwNy0xMC0zMSAxOToyNTowMCAtMTEwMCcKCiAgICAgIHQxID0gdC5sb2NhbHRpbWUoJ0V1cm9wZS9QYXJpcycpCgogICAgICBhc3NlcnQgdDEudG9fcywgJzIwMDctMTEtMDEgMDc6MjU6MDAgKzAxMDAnCiAgICAgIGFzc2VydCB0MS5vYmplY3RfaWQgIT0gdC5vYmplY3RfaWQKICAgIGVuZAoKICAgIHRlc3QgJ3JldHVybnMgYSBuZXcgRW9UaW1lIGluc3RhbmNlIGV2ZW50IGlmIHRhcmdldCB6b25lIGlzIHRoZSBzYW1lJyBkbwoKICAgICAgdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnUGFjaWZpYy9BcGlhJykKCiAgICAgIGFzc2VydCB0LnRvX3MsICcyMDA3LTEwLTMxIDE5OjI1OjAwIC0xMTAwJwoKICAgICAgdDEgPSB0LmxvY2FsdGltZSgnUGFjaWZpYy9BcGlhJykKCiAgICAgIGFzc2VydCB0MS50b19zLCAnMjAwNy0xMC0zMSAxOToyNTowMCAtMTEwMCcKICAgICAgYXNzZXJ0IHQxLm9iamVjdF9pZCAhPSB0Lm9iamVjdF9pZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjdHJhbnNsYXRlKHpvbmU9bmlsKScgZG8KCiAgICB0ZXN0ICdpcyBhbiBhbGlhcyB0byAjbG9jYWx0aW1lKHpvbmU9bmlsKScgZG8KCiAgICAgIHQgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ1BhY2lmaWMvQXBpYScpCgogICAgICBhc3NlcnQgdC50b19zLCAnMjAwNy0xMC0zMSAxOToyNTowMCAtMTEwMCcKCiAgICAgIHQxID0gdC50cmFuc2xhdGUoJ0V1cm9wZS9QYXJpcycpCgogICAgICBhc3NlcnQgdDEudG9fcywgJzIwMDctMTEtMDEgMDc6MjU6MDAgKzAxMDAnCiAgICAgIGFzc2VydCB0MS5vYmplY3RfaWQgIT0gdC5vYmplY3RfaWQKICAgIGVuZAogIGVuZAoKICBncm91cCAnI2luX3RpbWVfem9uZSh6b25lPW5pbCknIGRvCgogICAgdGVzdCAnaXMgYW4gYWxpYXMgdG8gI2xvY2FsdGltZSh6b25lPW5pbCknIGRvCgogICAgICB0ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdQYWNpZmljL0FwaWEnKQoKICAgICAgYXNzZXJ0IHQudG9fcywgJzIwMDctMTAtMzEgMTk6MjU6MDAgLTExMDAnCgogICAgICB0MSA9IHQuaW5fdGltZV96b25lKCdFdXJvcGUvTW9zY293JykKCiAgICAgIGFzc2VydCB0MS50b19zLCAnMjAwNy0xMS0wMSAwOToyNTowMCArMDMwMCcKICAgICAgYXNzZXJ0IHQxLm9iamVjdF9pZCAhPSB0Lm9iamVjdF9pZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjaXNvODYwMScgZG8KCiAgICBbCgogICAgICBbIDExOTM4OTgzMDAsICdQYWNpZmljL0FwaWEnLCBuaWwsICcyMDA3LTEwLTMxVDE5OjI1OjAwLTExOjAwJyBdLAogICAgICBbIDExOTM4OTgzMDAsICdVVEMnLCBuaWwsICcyMDA3LTExLTAxVDA2OjI1OjAwWicgXSwKICAgICAgWyAnMjAxNy0wNi0yMSBFdXJvcGUvUGFyaXMnLCBuaWwsIG5pbCwgJzIwMTctMDYtMjFUMDA6MDA6MDArMDI6MDAnIF0sCiAgICAgIFsgJzIwMTctMDEtMjEgRXVyb3BlL1BhcmlzJywgbmlsLCBuaWwsICcyMDE3LTAxLTIxVDAwOjAwOjAwKzAxOjAwJyBdLAoKICAgICAgI1sgMTE5Mzg5ODMwMC4xMSwgJ1BhY2lmaWMvQXBpYScsIDIsICcyMDA3LTEwLTMxVDE5OjI1OjAwLjEwLTExOjAwJyBdLCM/Pz8KICAgICAgWyAxMTkzODk4MzAwLjcwLCAnVVRDJywgMSwgJzIwMDctMTEtMDFUMDY6MjU6MDAuN1onIF0sCiAgICAgIFsgJzIwMTctMDYtMjEgRXVyb3BlL1BhcmlzJywgbmlsLCAyLCAnMjAxNy0wNi0yMVQwMDowMDowMC4wMCswMjowMCcgXSwKICAgICAgWyAnMjAxNy0wMS0yMSBFdXJvcGUvUGFyaXMnLCBuaWwsIDMsICcyMDE3LTAxLTIxVDAwOjAwOjAwLjAwMCswMTowMCcgXSwKCiAgICBdLmVhY2ggZG8gfHMsIHosIGYsIGl8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7aS5pbnNwZWN0fSIgZG8KCiAgICAgICAgdCA9CiAgICAgICAgICBpZiB6CiAgICAgICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldyhzLCB6KQogICAgICAgICAgZWxzZQogICAgICAgICAgICBFdE9yYmkubWFrZV90aW1lKHMpCiAgICAgICAgICBlbmQKCiAgICAgICAgYXNzZXJ0IHQuaXNvODYwMShmKSwgaQogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnI2FtYmlndW91cz8nIGRvCgogICAgIyBodHRwczovL3d3dy50aW1lYW5kZGF0ZS5jb20vdGltZS9jaGFuZ2UvdXNhL25ldy15b3JrP3llYXI9MjAxOAoKICAgIHRlc3QgJ3JldHVybnMgZmFsc2UgaWYgdGVzdCBoYXMgYSB1bmlxdWUgY29ycmVzcG9uZGluZyBVVEMgdGltZScgZG8KCiAgICAgICMgd2hhdGV2ZXIgdGhlIGxvY2FsIHpvbmUhCgogICAgICB0ID0gRXRPcmJpOjpFb1RpbWUubmV3KAogICAgICAgIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMDowMDowMCAtMDQwMCcpLnRvX2YsCiAgICAgICAgJ0FtZXJpY2EvTmV3X1lvcmsnKQoKICAgICAgYXNzZXJ0IHQuYW1iaWd1b3VzPywgZmFsc2UKICAgIGVuZAoKICAgIHRlc3QgJ3JldHVybnMgdHJ1ZSBpZiB0ZXN0IGhhcyB0d28gY29ycmVzcG9uZGluZyBVVEMgdGltZXMgKERTVCB0byBub24tRFNUKScgZG8KCiAgICAgICMgd2hhdGV2ZXIgdGhlIGxvY2FsIHpvbmUhCgogICAgICB0ID0gRXRPcmJpOjpFb1RpbWUubmV3KAogICAgICAgIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMTozMDowMCAtMDQwMCcpLnRvX2YsCiAgICAgICAgJ0FtZXJpY2EvTmV3X1lvcmsnKQoKICAgICAgYXNzZXJ0IHQuYW1iaWd1b3VzPywgdHJ1ZQogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjcmVhY2gnIGRvCgogICAgewoKICAgICAgWyAnMjAxOC0wNy0yNCAwMTozMTowMCBBbWVyaWNhL05ld19Zb3JrJywgeyBtaW46IDMwIH0gXSA9PgogICAgICAgICcyMDE4LTA3LTI0IDAyOjMwOjAwIEFtZXJpY2EvTmV3X1lvcmsnLAogICAgICBbICcyMDE4LTA3LTI0IDAxOjMxOjAwIEFtZXJpY2EvTmV3X1lvcmsnLCB7IGhvdTogNyB9IF0gPT4KICAgICAgICAnMjAxOC0wNy0yNCAwNzowMDowMCBBbWVyaWNhL05ld19Zb3JrJywKICAgICAgWyAnMjAxOC0wNy0yNCAwMTozMTowMCBBbWVyaWNhL05ld19Zb3JrJywgeyBob3U6IDcsIG1pbjogMjUgfSBdID0+CiAgICAgICAgJzIwMTgtMDctMjQgMDc6MjU6MDAgQW1lcmljYS9OZXdfWW9yaycsCiAgICAgIFsgJzIwMTgtMDctMjQgMDE6MzE6MDAgQW1lcmljYS9OZXdfWW9yaycsIHsgaDogNywgbTogMjUsIHM6IDEwIH0gXSA9PgogICAgICAgICcyMDE4LTA3LTI0IDA3OjI1OjEwIEFtZXJpY2EvTmV3X1lvcmsnLAogICAgICBbICcyMDE4LTA3LTI0IDAxOjMxOjAwIEFtZXJpY2EvTmV3X1lvcmsnLCB7IGg6IDcsIHM6IDEwIH0gXSA9PgogICAgICAgICcyMDE4LTA3LTI0IDA3OjAwOjEwIEFtZXJpY2EvTmV3X1lvcmsnLAoKICAgIH0uZWFjaCBkbyB8KHN0YXJ0LCBwb2ludHMpLCByZXN1bHR8CgogICAgICB0ZXN0ICJyZWFjaGVzIGZvciAje3BvaW50cy5pbnNwZWN0fSIgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5wYXJzZShzdGFydCkKICAgICAgICB0ID0gdC5yZWFjaChwb2ludHMpCgogICAgICAgIGFzc2VydCB0LnRvX3pzLCByZXN1bHQKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyM9PScgZG8KCiAgICBncm91cCAnRW9UaW1lID09IEVvVGltZScgZG8KCiAgICAgIHRlc3QgJ3JldHVybnMgdHJ1ZSBpZiBzYW1lIHMgYW5kIHNhbWUgVFonIGRvCgogICAgICAgIGVvMCA9IEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMTozMDowMCAtMDQwMCcpCiAgICAgICAgZW8xID0gRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAxOjMwOjAwIC0wNDAwJykKCiAgICAgICAgYXNzZXJ0IGVvMCA9PSBlbzEKICAgICAgZW5kCgogICAgICB0ZXN0ICdyZXR1cm5zIGZhbHNlIGlmIG5vdCBpbiB0aGUgc2FtZSB0aW1lem9uZScgZG8KCiAgICAgICAgZW8wID0gRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjMwOjAwIEV1cm9wZS9CZXJsaW4nKQogICAgICAgIGVvMSA9IEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMTozMDowMCBFdXJvcGUvTG9uZG9uJykKCiAgICAgICAgYXNzZXJ0IGVvMC50b19pID09IGVvMS50b19pCiAgICAgICAgYXNzZXJ0IGVvMCA9PSBlbzEsIGZhbHNlCiAgICAgIGVuZAogICAgZW5kCgogICAgZ3JvdXAgJ0VvVGltZSA9PSBUaW1lJyBkbwoKICAgICAgdGVzdCAncmV0dXJucyB0cnVlIHdoZW4gc2FtZSBzZWMnIGRvCgogICAgICAgIGVvID0gRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjMwOjAwIEV1cm9wZS9CZXJsaW4nKQogICAgICAgIHQgPSBUaW1lLmF0KGVvLnRvX2kpCgogICAgICAgIGFzc2VydCBlbyA9PSB0CiAgICAgICAgYXNzZXJ0IGVvLCB0CiAgICAgIGVuZAoKICAgICAgdGVzdCAncmV0dXJucyBmYWxzZSBlbHNlJyBkbwoKICAgICAgICBlbyA9IEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjozMDowMCBFdXJvcGUvQmVybGluJykKICAgICAgICB0ID0gVGltZS5hdChlby50b19pICsgMSkKCiAgICAgICAgYXNzZXJ0IGVvID09IHQsIGZhbHNlCiAgICAgICAgYXNzZXJ0IGVvICE9IHQKICAgICAgZW5kCiAgICBlbmQKCiAgICBncm91cCAnVGltZSA9PSBFb1RpbWUnIGRvICMgZWhtLCB5ZWFoLCB0ZXN0aW5nIFJ1Ynkgc29tZWhvdy4uLgoKICAgICAgdGVzdCAncmV0dXJucyB0cnVlIHdoZW4gc2FtZSBzZWMnIGRvCgogICAgICAgIGVvID0gRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjMwOjAwIEV1cm9wZS9CZXJsaW4nKQogICAgICAgIHQgPSBUaW1lLmF0KGVvLnRvX2kpCgogICAgICAgIGFzc2VydCB0ID09IGVvLCB0cnVlICMgdGhhbmtzIFJ1YnkhCiAgICAgICAgYXNzZXJ0IHQsIGVvCiAgICAgIGVuZAoKICAgICAgdGVzdCAncmV0dXJucyBmYWxzZSBlbHNlJyBkbwoKICAgICAgICBlbyA9IEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjozMDowMCBFdXJvcGUvQmVybGluJykKICAgICAgICB0ID0gVGltZS5hdChlby50b19pICsgMSkKCiAgICAgICAgYXNzZXJ0IHQgPT0gZW8sIGZhbHNlCiAgICAgICAgYXNzZXJ0IHQgIT0gZW8KICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyM8LCAjPD0sICM+LCAjPj0sICM8PT4nIGRvCgogICAgWwoKICAgICAgWyBFdE9yYmkucGFyc2UoJzIwMTgtMTEtMDQgMDI6MzA6MDAgRXVyb3BlL0JlcmxpbicpLAogICAgICAgIDo+LAogICAgICAgIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjowMDowMCBFdXJvcGUvQmVybGluJyksCiAgICAgICAgdHJ1ZSBdLAogICAgICBbIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjowMDowMCBFdXJvcGUvQmVybGluJyksCiAgICAgICAgOj4sCiAgICAgICAgRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjMwOjAwIEV1cm9wZS9CZXJsaW4nKSwKICAgICAgICBmYWxzZSBdLAogICAgICBbIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjowMDowMCBFdXJvcGUvTG9uZG9uJyksCiAgICAgICAgOj4sCiAgICAgICAgRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjAwOjAwIEV1cm9wZS9CZXJsaW4nKSwKICAgICAgICB0cnVlIF0sCiAgICAgIFsgRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjAwOjAwIEV1cm9wZS9CZXJsaW4nKSwKICAgICAgICA6PiwKICAgICAgICBFdE9yYmkucGFyc2UoJzIwMTgtMTEtMDQgMDI6MDA6MDAgRXVyb3BlL0xvbmRvbicpLAogICAgICAgIGZhbHNlIF0sCiAgICAgIFsgRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjAwOjAwIEV1cm9wZS9Mb25kb24nKSwKICAgICAgICA6PiwKICAgICAgICBUaW1lLmF0KEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjowMDowMCBFdXJvcGUvQmVybGluJykudG9faSksCiAgICAgICAgdHJ1ZSBdLAogICAgICBbIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjowMDowMCBFdXJvcGUvQmVybGluJyksCiAgICAgICAgOj4sCiAgICAgICAgVGltZS5hdChFdE9yYmkucGFyc2UoJzIwMTgtMTEtMDQgMDI6MDA6MDAgRXVyb3BlL0JlcmxpbicpLnRvX2kpLAogICAgICAgIGZhbHNlIF0sCgogICAgXS5lYWNoIGRvIHx0MCwgY29tcGFyYXRvciwgdDEsIGFzc2VydGVkfAoKICAgICAgdG9fcyA9IGxhbWJkYSB7IHxvfAogICAgICAgIGNhc2UgbwogICAgICAgIHdoZW4gRXRPcmJpOjpFb1RpbWUgdGhlbiAiI3tvLnRvX3N9IChFb1RpbWUpIgogICAgICAgIGVsc2UgIiN7by50b19zfSAoI3tvLmNsYXNzfSkiCiAgICAgICAgZW5kIH0KCiAgICAgIHMwID0gdG9fc1t0MF0KICAgICAgczEgPSB0b19zW3QxXQoKICAgICAgdGVzdCAiI3tzMH0gI3tjb21wYXJhdG9yLnRvX3N9ICN7czF9IHlpZWxkcyAje2Fzc2VydGVkLmluc3BlY3R9IiBkbwoKICAgICAgICBhc3NlcnQgdDAuc2VuZChjb21wYXJhdG9yLCB0MSksIGFzc2VydGVkCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCmVuZAoK \ No newline at end of file From 9719338fd5c9846c28aea4b0ff1bc9d66ab906d6 Mon Sep 17 00:00:00 2001 From: Oskar Eichler <62393985+OskarEichler@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:42:49 +0300 Subject: [PATCH 3/3] Store plain-text regression test for et-orbi PR #53 --- test/eo_time_test.rb | 949 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 948 insertions(+), 1 deletion(-) diff --git a/test/eo_time_test.rb b/test/eo_time_test.rb index 48647da..f53f9e0 100644 --- a/test/eo_time_test.rb +++ b/test/eo_time_test.rb @@ -1 +1,948 @@ -IyBlbmNvZGluZzogVVRGLTgKCiMKIyBTcGVjaWZ5aW5nIEV0T3JiaQojCiMgV2VkIE1hciAxMSAyMToxNzozNiBKU1QgMjAxNSwgcXVhdHJlIGFucy4uLiAocnVmdXMtc2NoZWR1bGVyKQojIFN1biBNYXIgMTkgMDU6MTY6MjggSlNUIDIwMTcKIwoKCmdyb3VwIEV0T3JiaTo6RW9UaW1lIGRvCgogIGdyb3VwICcubmV3JyBkbwoKICAgIGdyb3VwICJ6b25lICdBbWVyaWNhL0xvc19BbmdlbGVzJyIgZG8KCiAgICAgIHRlc3QgJ2FjY2VwdHMgYW4gaW50ZWdlcicgZG8KCiAgICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTIzNDU2Nzg5MCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQoKICAgICAgICBhc3NlcnQgb3Quc2Vjb25kcy50b19pLCAxMjM0NTY3ODkwCiAgICAgICAgYXNzZXJ0IG90LnpvbmUubmFtZSwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnCiAgICAgIGVuZAoKICAgICAgdGVzdCAnYWNjZXB0cyBhIGZsb2F0JyBkbwoKICAgICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMjM0NTY3ODkwLjEyMzQsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKCiAgICAgICAgYXNzZXJ0IG90LnNlY29uZHMudG9faSwgMTIzNDU2Nzg5MAogICAgICAgIGFzc2VydCBvdC56b25lLm5hbWUsICdBbWVyaWNhL0xvc19BbmdlbGVzJwogICAgICBlbmQKCiAgICAgIHRlc3QgJ2FjY2VwdHMgYSBVVEMgOjpUaW1lIGluc3RhbmNlJyBkbwoKICAgICAgICBvdCA9CiAgICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoCiAgICAgICAgICAgIFRpbWUudXRjKDIwMDcsIDExLCAxLCAxNSwgMjUsIDApLAogICAgICAgICAgICAnQW1lcmljYS9Mb3NfQW5nZWxlcycpCgogICAgICAgIGFzc2VydCBvdC5zZWNvbmRzLnRvX2ksIDExOTM5MzA3MDAKICAgICAgICBhc3NlcnQgb3Quem9uZS5uYW1lLCAnQW1lcmljYS9Mb3NfQW5nZWxlcycKICAgICAgZW5kCgogICAgICB0ZXN0ICdhY2NlcHRzIGEgVVRDIEV0T3JiaTo6RW9UaW1lIGluc3RhbmNlJyBkbwoKICAgICAgICBvdCA9CiAgICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoCiAgICAgICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzOTMwNzAwLCAnVVRDJyksCiAgICAgICAgICAgICdBbWVyaWNhL0xvc19BbmdlbGVzJykKCiAgICAgICAgYXNzZXJ0IG90LnNlY29uZHMudG9faSwgMTE5MzkzMDcwMAogICAgICAgIGFzc2VydCBvdC56b25lLm5hbWUsICdBbWVyaWNhL0xvc19BbmdlbGVzJwogICAgICBlbmQKCiAgICAgIHRlc3QgJ2FjY2VwdHMgYSBsb2NhbCA6OlRpbWUgaW5zdGFuY2UnIGRvCgogICAgICAgICNpbl96b25lICdBc2lhL1NhbWFya2FuZCcgZG8KICAgICAgICAjCiAgICAgICAgIyAgdCA9IFRpbWUucGFyc2UoJzIwMDctMTEtMDEgMTU6MjUnKQogICAgICAgICMgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KHQsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgICAjCiAgICAgICAgIyAgYXNzZXJ0IG90LnNlY29uZHMsIHQudG9faSkKICAgICAgICAjICBhc3NlcnQgb3Quc2Vjb25kcy50b19pLCAxMTkzOTEyNzAwKQogICAgICAgICMgIGFzc2VydCBvdC56b25lLm5hbWUsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgICAjZW5kCiAgICAgICAgICAjCiAgICAgICAgICAjIGJlY2F1c2Ugb2YgaHR0cHM6Ly9jaS5hcHB2ZXlvci5jb20vcHJvamVjdC9qbWV0dHJhdXgvZXQtb3JiaS9idWlsZC9qb2IvOGJpcm5yMGs1NGpyaWh4OAoKICAgICAgICB0ID0gVGltZS5wYXJzZSgnMjAwNy0xMS0wMSAxNToyNSArMDU6MDAnKQogICAgICAgICAgIyB1c2UgYSBsb2NhbCA6OlRpbWUgaW5zdGFuY2Ugd2l0aCBhIGN1c3RvbSB0aW1lem9uZSA6LSgKCiAgICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcodCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQoKICAgICAgICBhc3NlcnQgb3Quc2Vjb25kcywgdC50b19pCiAgICAgICAgYXNzZXJ0IG90LnNlY29uZHMudG9faSwgMTE5MzkxMjcwMAogICAgICAgIGFzc2VydCBvdC56b25lLm5hbWUsICdBbWVyaWNhL0xvc19BbmdlbGVzJwogICAgICBlbmQKCiAgICAgIHRlc3QgJ2FjY2VwdHMgYSBMb2NhbCBFdE9yYmk6OkVvVGltZSBpbnN0YW5jZScgZG8KCiAgICAgICAgb3QgPQogICAgICAgICAgRXRPcmJpOjpFb1RpbWUubmV3KAogICAgICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoMTE5MzkzMDcwMCwgJ0FzaWEvWWVrYXRlcmluYnVyZycpLAogICAgICAgICAgICAnQW1lcmljYS9Mb3NfQW5nZWxlcycpCgogICAgICAgIGFzc2VydCBvdC5zZWNvbmRzLnRvX2ksIDExOTM5MzA3MDAKICAgICAgICBhc3NlcnQgb3Quem9uZS5uYW1lLCAnQW1lcmljYS9Mb3NfQW5nZWxlcycKICAgICAgZW5kCiAgICBlbmQKCiAgICBncm91cCAiem9uZSBUWkluZm8gaW5zdGFuY2UgJ0V1cm9wZS9QYXJpcyciIGRvCgogICAgICB0ZXN0ICdhY2NlcHRzIGFuIGludGVnZXInIGRvCgogICAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUKICAgICAgICAgIC5uZXcoMTIzNDU2Nzg5MCwgOjpUWkluZm86OlRpbWV6b25lLmdldCgnRXVyb3BlL1BhcmlzJykpCgogICAgICAgIGFzc2VydCBvdC5zZWNvbmRzLnRvX2ksIDEyMzQ1Njc4OTAKICAgICAgICBhc3NlcnQgb3Quem9uZS5uYW1lLCAnRXVyb3BlL1BhcmlzJwogICAgICBlbmQKICAgIGVuZAoKICAgIGdyb3VwICJ6b25lIEFjdGl2ZVN1cHBvcnQ6OlRpbWVab25lIGluc3RhbmNlICdBbWVyaWNhL05ld19Zb3JrJyIgZG8KCiAgICAgIHRlc3QgJ2FjY2VwdHMgYW4gaW50ZWdlcicgZG8KCiAgICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZQogICAgICAgICAgLm5ldygxMjM0NTY3ODkwLCBUZXN0QWN0aXZlU3VwcG9ydFRpbWVab25lLm1ha2UoJ0FtZXJpY2EvTmV3X1lvcmsnKSkKCiAgICAgICAgYXNzZXJ0IG90LnNlY29uZHMudG9faSwgMTIzNDU2Nzg5MAogICAgICAgIGFzc2VydCBvdC56b25lLm5hbWUsICdBbWVyaWNhL05ld19Zb3JrJwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnI3pvbmU9JyBkbwoKICAgIHRlc3QgJ2Fzc2lnbnMgdGhlIHJlcXVlc3RlZCB0aW1lem9uZSBhbmQgaW52YWxpZGF0ZXMgdGhlIHRpbWUgY2FjaGUnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygwLCAnVVRDJykKICAgICAgb3QuaG91cgoKICAgICAgb3Quem9uZSA9ICdBc2lhL0tvbGthdGEnCgogICAgICBhc3NlcnQgb3Quem9uZS5uYW1lLCAnQXNpYS9Lb2xrYXRhJwogICAgICBhc3NlcnQgb3QuaG91ciwgNQogICAgICBhc3NlcnQgb3QubWluLCAzMAogICAgICBhc3NlcnQgb3QudG9fZiwgMC4wCiAgICBlbmQKCiAgICB0ZXN0ICdkb2VzIG5vdCBjaGFuZ2UgdGhlIHRpbWV6b25lIHdoZW4gYXNzaWdubWVudCBmYWlscycgZG8KCiAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KDAsICdVVEMnKQoKICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgIGxhbWJkYSB7IG90LnpvbmUgPSAnSW52YWxpZC9UaW1lem9uZScgfSwKICAgICAgICBBcmd1bWVudEVycm9yKQoKICAgICAgYXNzZXJ0IG90LnpvbmUubmFtZSwgJ1VUQycKICAgIGVuZAogIGVuZAoKICBncm91cCAnLnV0YycgZG8KCiAgICBbCgogICAgICBbIFsgMjAxNywgMywgMjUgXSwgJzIwMTctMDMtMjVUMDA6MDA6MDBaJyBdLAogICAgICBbIFsgMjAxNywgMywgMjUsIDIxLCAyMywgMjkgXSwgJzIwMTctMDMtMjVUMjE6MjM6MjlaJyBdLAoKICAgIF0uZWFjaCBkbyB8YSwgc3wKCiAgICAgIHRlc3QgImFjY2VwdHMgI3thLmluc3BlY3R9IiBkbwoKICAgICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLnV0YygqYSkKCiAgICAgICAgYXNzZXJ0IG90LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICAgIGFzc2VydCBvdC56b25lLm5hbWUsICdVVEMnCiAgICAgICAgYXNzZXJ0IG90Lmlzbzg2MDEsIHMKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy5sb2NhbCcgZG8KCiAgICBbCgogICAgICBbIFsgMjAxNywgMywgMjUgXSwKICAgICAgICAld1sgRXVyb3BlL0xpc2JvbiB3aW5kb3dzOldFVCBBdGxhbnRpYy9DYW5hcnkgXSwKICAgICAgICAnMjAxNy0wMy0yNVQwMDowMDowMCswMDowMCcgXSwKICAgICAgWyBbIDIwMTcsIDMsIDI1LCAyMSwgMjMsIDI5IF0sCiAgICAgICAgJXdbIEV1cm9wZS9MaXNib24gd2luZG93czpXRVQgQXRsYW50aWMvQ2FuYXJ5IF0sCiAgICAgICAgJzIwMTctMDMtMjVUMjE6MjM6MjkrMDA6MDAnIF0sCiAgICAgIFsgWyAyMDE3LCAzLCAyNSwgMjEsIDIzLCAyOSBdLAogICAgICAgICV3WyBFdXJvcGUvTW9zY293IEV1cm9wZS9LaXJvdiBdLAogICAgICAgICcyMDE3LTAzLTI1VDIxOjIzOjI5KzAzOjAwJyBdLAoKICAgIF0uZWFjaCBkbyB8YSwgenMsIHN8CgogICAgICB0ZXN0ICJhY2NlcHRzICN7YS5pbnNwZWN0fSBpbiAje3pzLmZpcnN0fSIgZG8KCiAgICAgICAgaW5fem9uZSh6cy5maXJzdCkgZG8KCiAgICAgICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLmxvY2FsKCphKQoKICAgICAgICAgIGFzc2VydCBvdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICAgIGFzc2VydF9pbmNsdWRlIG90LnpvbmUsIHNlbGVjdF96b25lcyh6cykKICAgICAgICAgIGFzc2VydCBvdC5pc284NjAxLCBzCiAgICAgICAgZW5kCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjdG9fdGltZSAocHJvdGVjdGVkKScgZG8KCiAgICBpZiBUWkluZm86OlRpbWV6b25lCiAgICAgICAuZ2V0KCdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgIC51dGNfdG9fbG9jYWwoVGltZS5hdCgxMTkzODk4MzAwKSkKICAgICAgIC51dGM/CiAgICB0aGVuCiAgICAgICMgVFpJbmZvIDwgMi4wLjAKCiAgICAgIHRlc3QgJ3JldHVybnMgYSBsb2NhbCBUaW1lIGluc3RhbmNlLCBhbHRob3VnaCB3aXRoIGEgVVRDIHpvbmUnIGRvCgogICAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgICB0ID0gb3Quc2VuZCg6dG9fdGltZSkKCiAgICAgICAgYXNzZXJ0IG90LnRvX2RlYnVnX3MsICdvdCAyMDA3LTEwLTMxIDIzOjI1OjAwIC0wNzowMCBkc3Q6dHJ1ZScKCiAgICAgICAgYXNzZXJ0IHQudG9faSwgMTE5Mzg5ODMwMCAtIDcgKiAzNjAwICMgLyFcCiAgICAgICAgYXNzZXJ0IHQudXRjX29mZnNldCwgMAoKICAgICAgICBhc3NlcnQgdC50b19kZWJ1Z19zLCAndCAyMDA3LTEwLTMxIDIzOjI1OjAwICswMDowMCBkc3Q6ZmFsc2UnCiAgICAgICAgICAjIFRpbWUgaW5zdGFuY2Ugc3R1Y2sgdG8gVVRDLi4uCiAgICAgIGVuZAoKICAgIGVsc2UKICAgICAgIyBUWkluZm8gPj0gMi4wLjAKCiAgICAgIHRlc3QgJ3JldHVybnMgYSBsb2NhbCBUaW1lIGluc3RhbmNlJyBkbwoKICAgICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnQW1lcmljYS9Mb3NfQW5nZWxlcycpCiAgICAgICAgdCA9IG90LnNlbmQoOnRvX3RpbWUpCgogICAgICAgIGFzc2VydCBvdC50b19kZWJ1Z19zLCAnb3QgMjAwNy0xMC0zMSAyMzoyNTowMCAtMDc6MDAgZHN0OnRydWUnCgogICAgICAgIGFzc2VydCB0LnRvX2ksIDExOTM4OTgzMDAKICAgICAgICBhc3NlcnQgdC51dGNfb2Zmc2V0LCAtNyAqIDM2MDAKCiAgICAgICAgYXNzZXJ0IHQudG9fZGVidWdfcywgJ3QgMjAwNy0xMC0zMSAyMzoyNTowMCAtMDc6MDAgZHN0OnRydWUnCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjdG9fbG9jYWxfdGltZScgZG8KCiAgICB0ZXN0ICdyZXR1cm5zIGEgbG9jYWwgVGltZSBpbnN0YW5jZSBpbiB0aGUgbG9jYWwgdGltZSB6b25lJyBkbwoKICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQogICAgICB0ID0gb3QudG9fbG9jYWxfdGltZQoKICAgICAgYXNzZXJ0IHQuY2xhc3MsIDo6VGltZQogICAgICBhc3NlcnQgdC50b19pLCAxMTkzODk4MzAwCiAgICAgIGFzc2VydCB0LnVzZWMsIDAKCiAgICAgIHQxID0gVGltZS5wYXJzZSh0LnN0cmZ0aW1lKCIlWS0lbS0lZCAlSDolTTolUy4jeyclMDZkJyAlIHQudXNlY30iKSkKICAgICAgYXNzZXJ0IHQudG9fcywgdDEudG9fcwogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjdG9fdCcgZG8KCiAgICB0ZXN0ICdpcyBhbiBhbGlhcyB0byAjdG9fbG9jYWxfdGltZScgZG8KCiAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgdCA9IG90LnRvX3QKCiAgICAgIGFzc2VydCB0LmNsYXNzLCA6OlRpbWUKICAgICAgYXNzZXJ0IHQudG9faSwgMTE5Mzg5ODMwMAogICAgICBhc3NlcnQgdC51c2VjLCAwCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyN0b191dGNfdGltZScgZG8KCiAgICB0ZXN0ICdpcyBhbiBhbGlhcyB0byAjdG9fdXRjJyBkbwoKICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQogICAgICB1dCA9IG90LnRvX3V0Y190aW1lCgogICAgICBhc3NlcnQgdXQuY2xhc3MsIDo6VGltZQogICAgICBhc3NlcnQgdXQudG9faSwgMTE5Mzg5ODMwMAogICAgICBhc3NlcnQgdXQudXNlYywgMAoKICAgICAgYXNzZXJ0IHV0LnRvX3MsICcyMDA3LTExLTAxIDA2OjI1OjAwIFVUQycKICAgIGVuZAogIGVuZAoKICBncm91cCAnI3V0YycgZG8KCiAgICB0ZXN0ICdyZXR1cm5zIGFuIFVUQyBUaW1lIGluc3RhbmNlJyBkbwoKICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQogICAgICB1dCA9IG90LnV0YwoKICAgICAgYXNzZXJ0IHV0LnRvX2ksIDExOTM4OTgzMDAKCiAgICAgIGFzc2VydCBvdC50b19kZWJ1Z19zLCAnb3QgMjAwNy0xMC0zMSAyMzoyNTowMCAtMDc6MDAgZHN0OnRydWUnCiAgICAgIGFzc2VydCB1dC50b19kZWJ1Z19zLCAndCAyMDA3LTExLTAxIDA2OjI1OjAwICswMDowMCBkc3Q6ZmFsc2UnCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyN1dGM/JyBkbwoKICAgIHRlc3QgJ3JldHVybnMgdHJ1ZSBpZiB0aGUgRW9UaW1lIHpvbmUgaXMgVVRDJyBkbwoKICAgICAgYXNzZXJ0IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnWicpLnV0Yz8KICAgICAgYXNzZXJ0IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnVVRDJykudXRjPwogICAgICBhc3NlcnQgRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdHTVQnKS51dGM/CiAgICAgIGFzc2VydCBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ1p1bHUnKS51dGM/CiAgICBlbmQKCiAgICB0ZXN0ICdyZXR1cm5zIGZhbHNlIGVsc2UnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnQW1lcmljYS9Mb3NfQW5nZWxlcycpCgogICAgICBhc3NlcnQgb3QudXRjPywgZmFsc2UKICAgIGVuZAogIGVuZAoKICBncm91cCAnI2FkZCcgZG8KCiAgICB0ZXN0ICdhZGRzIHNlY29uZHMnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKICAgICAgb3QuYWRkKDExMSkKCiAgICAgIGFzc2VydCBvdC5zZWNvbmRzLCAxMTkzODk4MzAwICsgMTExCiAgICBlbmQKCiAgICB0ZXN0ICdhZGRzIGFueXRoaW5nIHRoYXQgaGFzIGEgI3RvX2kgYW5kIGlzIG5vdCBhIFN0cmluZywgZ2gtNDAnIGRvCgogICAgICBvdCA9CiAgICAgICAgRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdBc2lhL1ZsYWRpdm9zdG9rJykgKwogICAgICAgIFRlc3RBY3RpdmVTdXBwb3J0RHVyYXRpb24ubmV3KDM2MDApICMgMS5ob3VyCgogICAgICBhc3NlcnQgb3Quc2Vjb25kcywgMTE5Mzg5ODMwMCArIDM2MDAKICAgIGVuZAoKICAgIHRlc3QgJ2dvZXMgaW50byBEU1QnIGRvCgogICAgICBvdCA9CiAgICAgICAgRXRPcmJpOjpFb1RpbWUubmV3KAogICAgICAgICAgVGltZS5nbSgyMDE1LCAzLCA4LCA5LCA1OSwgNTkpLAogICAgICAgICAgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQoKICAgICAgdDAgPSBvdC5kdXAKICAgICAgb3QuYWRkKDEpCiAgICAgIHQxID0gb3QKCiAgICAgIHN0MCA9IHQwLnN0cmZ0aW1lKCclWS8lbS8lZCAlSDolTTolUyAlWicpICsgIiAje3QwLmlzZHN0fSIKICAgICAgc3QxID0gdDEuc3RyZnRpbWUoJyVZLyVtLyVkICVIOiVNOiVTICVaJykgKyAiICN7dDEuaXNkc3R9IgoKICAgICAgYXNzZXJ0IHQwLnRvX2ksIDE0MjU4MDg3OTkKICAgICAgYXNzZXJ0IHQxLnRvX2ksIDE0MjU4MDg4MDAKICAgICAgYXNzZXJ0IHN0MCwgJzIwMTUvMDMvMDggMDE6NTk6NTkgUFNUIGZhbHNlJwogICAgICBhc3NlcnQgc3QxLCAnMjAxNS8wMy8wOCAwMzowMDowMCBQRFQgdHJ1ZScKICAgIGVuZAoKICAgIHRlc3QgJ2dvZXMgb3V0IG9mIERTVCcgZG8KCiAgICAgIG90ID0gRXRPcmJpLnBhcnNlKCcyMDE0LTEwLTI2IDAxOjU5OjU5IEV1cm9wZS9CZXJsaW4nKQogICAgICAgICMKICAgICAgICAjIHN0aWxsIGluIERTVCwgRFNUIGVuZHMgKGNsb2NrIGJhY2t3YXJkKSBhdCAwMzowMAogICAgICAgICMgaHR0cHM6Ly93d3cudGltZWFuZGRhdGUuY29tL3RpbWUvY2hhbmdlL2dlcm1hbnkvYmVybGluCgogICAgICB0MCA9IG90LmR1cAogICAgICBvdC5hZGQoMSkKICAgICAgdDEgPSBvdC5kdXAKICAgICAgb3QuYWRkKDM2MDApCiAgICAgIHQyID0gb3QuZHVwCiAgICAgIG90LmFkZCgxKQogICAgICB0MyA9IG90CgogICAgICBzdDAgPSB0MC5zdHJmdGltZSgnJVkvJW0vJWQgJUg6JU06JVMgJVonKSArICIgI3t0MC5pc2RzdH0iCiAgICAgIHN0MSA9IHQxLnN0cmZ0aW1lKCclWS8lbS8lZCAlSDolTTolUyAlWicpICsgIiAje3QxLmlzZHN0fSIKICAgICAgc3QyID0gdDIuc3RyZnRpbWUoJyVZLyVtLyVkICVIOiVNOiVTICVaJykgKyAiICN7dDIuaXNkc3R9IgogICAgICBzdDMgPSB0My5zdHJmdGltZSgnJVkvJW0vJWQgJUg6JU06JVMgJVonKSArICIgI3t0My5pc2RzdH0iCgogICAgICBhc3NlcnQgdDAudG9faSwgMTQxNDI4MTU5OQogICAgICBhc3NlcnQgdDEudG9faSwgMTQxNDI4NTIwMCAtIDM2MDAKICAgICAgYXNzZXJ0IHQyLnRvX2ksIDE0MTQyODUyMDAKICAgICAgYXNzZXJ0IHQzLnRvX2ksIDE0MTQyODUyMDEKCiAgICAgIGFzc2VydCBzdDAsICcyMDE0LzEwLzI2IDAxOjU5OjU5IENFU1QgdHJ1ZScKICAgICAgYXNzZXJ0IHN0MSwgJzIwMTQvMTAvMjYgMDI6MDA6MDAgQ0VTVCB0cnVlJwogICAgICBhc3NlcnQgc3QyLCAnMjAxNC8xMC8yNiAwMjowMDowMCBDRVQgZmFsc2UnCiAgICAgIGFzc2VydCBzdDMsICcyMDE0LzEwLzI2IDAyOjAwOjAxIENFVCBmYWxzZScKCiAgICAgIGFzc2VydCB0MSAtIHQwLCAxCiAgICAgIGFzc2VydCB0MiAtIHQxLCAzNjAwCiAgICAgIGFzc2VydCB0MyAtIHQyLCAxCiAgICBlbmQKCiAgICB0ZXN0ICdyZXR1cm5zIHNlbGYnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKICAgICAgb3QxID0gb3Quc3VidHJhY3QoMTExKQoKICAgICAgYXNzZXJ0IG90MS5vYmplY3RfaWQsIG90Lm9iamVjdF9pZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjc3VidHJhY3QnIGRvCgogICAgdGVzdCAnc3Vic3RyYWN0cyBzZWNvbmRzJyBkbwoKICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0V1cm9wZS9QYXJpcycpCiAgICAgIG90LnN1YnRyYWN0KDExMSkKCiAgICAgIGFzc2VydCBvdC5zZWNvbmRzLCAxMTkzODk4MzAwIC0gMTExCiAgICBlbmQKCiAgICB0ZXN0ICdzdWJ0cmFjdHMgYW55dGhpbmcgdGhhdCBoYXMgYSAjdG9faSBhbmQgaXMgbm90IGEgU3RyaW5nLCBnaC00MCcgZG8KCiAgICAgIG90ID0KICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0FzaWEvVmxhZGl2b3N0b2snKSAtCiAgICAgICAgVGVzdEFjdGl2ZVN1cHBvcnREdXJhdGlvbi5uZXcoMzYwMCkgIyAxLmhvdXIKCiAgICAgIGFzc2VydCBvdC5zZWNvbmRzLCAxMTkzODk4MzAwIC0gMzYwMAogICAgZW5kCgogICAgdGVzdCAncmV0dXJucyBzZWxmJyBkbwoKICAgICAgb3QgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0V1cm9wZS9QYXJpcycpCiAgICAgIG90MSA9IG90LnN1YnRyYWN0KDExMSkKCiAgICAgIGFzc2VydCBvdDEub2JqZWN0X2lkLCBvdC5vYmplY3RfaWQKICAgIGVuZAogIGVuZAoKICBncm91cCAnI3RvX2YnIGRvCgogICAgdGVzdCAncmV0dXJucyB0aGUgQHNlY29uZHMnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKCiAgICAgIGFzc2VydCBvdC50b19mLCAxMTkzODk4MzAwCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyN0b19zJyBkbwoKICAgIHRlc3QgJ3JldHVybnMgdGhlIGEgZm9ybWF0dGVkIGRhdGV0aW1lIHN0cmluZycgZG8KCiAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdFdXJvcGUvUGFyaXMnKQoKICAgICAgYXNzZXJ0IG90LnRvX3MsICcyMDA3LTExLTAxIDA3OjI1OjAwICswMTAwJwogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjdG9fenMnIGRvCgogICAgdGVzdCAncmV0dXJucyB0aGUgYSBmb3JtYXR0ZWQgZGF0ZXRpbWUgc3RyaW5nIHdpdGggYW4gZXhwbGljaXQgdGltZXpvbmUnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKCiAgICAgIGFzc2VydCBvdC50b196cywgJzIwMDctMTEtMDEgMDc6MjU6MDAgRXVyb3BlL1BhcmlzJwogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjc3RyZnRpbWUnIGRvCgogICAgdGVzdCAnYWNjZXB0cyAlWiwgJXosICU6eiBhbmQgJTo6eicgZG8KCiAgICAgIGFzc2VydCgKICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoMCwgJ0V1cm9wZS9CcmF0aXNsYXZhJykgXAogICAgICAgICAgLnN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUyAlWiAleiAlOnogJTo6eicpLAogICAgICAgICcxOTcwLTAxLTAxIDAxOjAwOjAwIENFVCArMDEwMCArMDE6MDAgKzAxOjAwOjAwJykKICAgIGVuZAoKICAgIHRlc3QgJ2FjY2VwdHMgJS9aJyBkbwoKICAgICAgYXNzZXJ0KAogICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldygwLCAnRXVyb3BlL0JyYXRpc2xhdmEnKSBcCiAgICAgICAgICAuc3RyZnRpbWUoJyVZLSVtLSVkICVIOiVNOiVTICUvWicpLAogICAgICAgICIxOTcwLTAxLTAxIDAxOjAwOjAwIEV1cm9wZS9CcmF0aXNsYXZhIikKICAgIGVuZAogIGVuZAoKICBncm91cCAnI21vbnRoZGF5cycgZG8KCiAgICBbCiAgICAgIFsgWyAxOTcwLCAxLCAxIF0sICV3WyA0IzEgNCMtNSBdIF0sCiAgICAgIFsgWyAxOTcwLCAxLCA3IF0sICV3WyAzIzEgMyMtNCBdIF0sCiAgICAgIFsgWyAxOTcwLCAxLCAxNCBdLCAld1sgMyMyIDMjLTMgXSBdLAogICAgICBbIFsgMjAxMSwgMywgMTEgXSwgJXdbIDUjMiA1Iy0zIF0gXQogICAgXS5lYWNoIGRvIHxkLCB4fAoKICAgICAgdGVzdCAicmV0dXJucyB0aGUgI3t4Lmluc3BlY3R9IGZvciAje2QuaW5zcGVjdH0iIGRvCgogICAgICAgIHQgPSBFdE9yYmkucGFyc2UoIiN7ZC5qb2luKCctJyl9IDEyOjAwIikKCiAgICAgICAgYXNzZXJ0IHQubW9udGhkYXlzLCB4CiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjKycgZG8KCiAgICB0ZXN0ICdhZGRzIHNlY29uZHMnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKICAgICAgb3QxID0gb3QgKyAxMTEKCiAgICAgIGFzc2VydCBvdDEuY2xhc3MsIEV0T3JiaTo6RW9UaW1lCiAgICAgIGFzc2VydCBvdDEuc2Vjb25kcywgMTE5Mzg5ODMwMCArIDExMQogICAgICBhc3NlcnQgb3QxLm9iamVjdF9pZCAhPSBvdC5vYmplY3RfaWQKICAgIGVuZAoKICAgIHRlc3QgJ3JlamVjdHMgVGltZSBpbnN0YW5jZXMnIGRvCgogICAgICBvdCA9CiAgICAgICAgRXRPcmJpLm1ha2VfdGltZSgnMjAxNy0xMC0zMSAyMjowMDoxMCBFdXJvcGUvUGFyaXMnKQogICAgICB0ID0KICAgICAgICBpbl96b25lKCdBbWVyaWNhL0xvc19BbmdlbGVzJykgeyBUaW1lLmxvY2FsKDIwMTcsIDEwLCAzMCwgMjIsIDAwLCAxMCkgfQoKICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgIGxhbWJkYSB7IG90ICsgdCB9LAogICAgICAgIEFyZ3VtZW50RXJyb3IsICdDYW5ub3QgYWRkIFRpbWUgdG8gRW9UaW1lIGluc3RhbmNlJykKICAgIGVuZAoKICAgIHRlc3QgJ3JlamVjdHMgRW9UaW1lIGluc3RhbmNlcycgZG8KCiAgICAgIG90ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdFdXJvcGUvUGFyaXMnKQogICAgICBvdDEgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnKQoKICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgIGxhbWJkYSB7IG90ICsgb3QxIH0sCiAgICAgICAgQXJndW1lbnRFcnJvciwgJ0Nhbm5vdCBhZGQgRXRPcmJpOjpFb1RpbWUgdG8gRW9UaW1lIGluc3RhbmNlJykKICAgIGVuZAogIGVuZAoKICBncm91cCAnIy0nIGRvCgogICAgdGVzdCAnc3VidHJhY3RzIHNlY29uZHMnIGRvCgogICAgICBvdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKICAgICAgb3QxID0gb3QgLSAxMTEKCiAgICAgIGFzc2VydCBvdDEuY2xhc3MsIEV0T3JiaTo6RW9UaW1lCiAgICAgIGFzc2VydCBvdDEuc2Vjb25kcywgMTE5Mzg5ODMwMCAtIDExMQogICAgICBhc3NlcnQgb3QxLm9iamVjdF9pZCAhPSBvdC5vYmplY3RfaWQKICAgIGVuZAoKICAgIHRlc3QgJ3N1YnRyYWN0cyBUaW1lIGluc3RhbmNlcycgZG8KCiAgICAgIG90ID0gRXRPcmJpLm1ha2VfdGltZSgnMjAxNy0xMC0zMSAyMjowMDoxMCBFdXJvcGUvUGFyaXMnKQoKICAgICAgI3QgPSB0aW1lX2luX3pvbmUoJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnLCAnMjAxNy0xMC0zMCAyMjowMDoxMCcpCiN0aW1lX2luX3pvbmUoJ1BTVDdQRFQnLCAnMjAxNy0xMC0zMCAyMjowMDoxMCcpCiAgICAgIHQgPSBUaW1lLnBhcnNlKCcyMDE3LTEwLTMwIDIyOjAwOjEwIC0wNzAwJykKICAgICAgICAjIGJlY2F1c2Ugb2YgaHR0cHM6Ly9jaS5hcHB2ZXlvci5jb20vcHJvamVjdC9qbWV0dHJhdXgvZXQtb3JiaS9idWlsZC9qb2IvZXZwNmRtcWVnZmd4aGNubQoKICAgICAgZCA9IG90IC0gdAoKICAgICAgYXNzZXJ0IGQuY2xhc3MsIEZsb2F0CiAgICAgIGFzc2VydCBkLnRvX2ksIDU3NjAwCiAgICBlbmQKCiAgICB0ZXN0ICdzdWJ0cmFjdHMgRW9UaW1lIGluc3RhbmNlcycgZG8KCiAgICAgIG90MCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJykKICAgICAgb3QxID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAgKyAyMjIsICdBbWVyaWNhL0xvc19BbmdlbGVzJykKICAgICAgZCA9IG90MCAtIG90MQoKICAgICAgYXNzZXJ0IGQuY2xhc3MsIEZsb2F0CiAgICAgIGFzc2VydCBkLnRvX2ksIC0yMjIKICAgIGVuZAogIGVuZAoKICBncm91cCAnI3dkYXlfaW5fbW9udGgnIGRvCgogICAgewoKICAgICAgWyAxMTkzODk4MzAwLCAnRXVyb3BlL1BhcmlzJyBdID0+IFsgMSwgLTUgXSwKICAgICAgJzIwMTctMDYtMjEgRXVyb3BlL1BhcmlzJyA9PiBbIDMsIC0yIF0sCiAgICAgICcyMDIyLTAzLTI5IFVUQycgPT4gWyA1LCAtMSBdLAogICAgICAnMjAyMi0wMy0yOSBFdXJvcGUvUGFyaXMnID0+IFsgNSwgLTEgXSwKCiAgICB9LmVhY2ggZG8gfGssIHZ8CgogICAgICB0ZXN0ICJjb21wdXRlcyB0aGUgd2RheSBpbiBtb250aCBpbnRlcnZhbCBmb3IgI3trLmluc3BlY3R9IiBkbwoKICAgICAgICB0ID0KICAgICAgICAgIGNhc2UgawogICAgICAgICAgd2hlbiBTdHJpbmcgdGhlbiBFdE9yYmkubWFrZV90aW1lKGspCiAgICAgICAgICBlbHNlIEV0T3JiaTo6RW9UaW1lLm5ldygqaykKICAgICAgICAgIGVuZAoKICAgICAgICBhc3NlcnQgdC53ZGF5X2luX21vbnRoLCB2CiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIFdEQVlTID0gJXdbIHN1biBtb24gdHVlIHdlZCB0aHUgZnJpIHNhdCBzdW4gXQoKICBncm91cCAnI3J3ZWVrLCAjcmRheScgZG8KCiAgICB7ICcyMDA4LTEyLTMxIDEyOjAwIEV1cm9wZS9MaXNib24nID0+IFsgJ3dlZCcsIDM2NiwgLTUyMiwgLTM2NTIgXSwKICAgICAgJzIwMTgtMTItMzEgMTI6MDAgRXVyb3BlL0xvbmRvbicgPT4gWyAnbW9uJywgMzY1LCAwLCAwIF0sCiAgICAgICcyMDE5LTAxLTAxIDEyOjAwIEV1cm9wZS9QYXJpcycgPT4gWyAndHVlJywgMSwgMCwgMSBdLAogICAgICAnMjAxOS0wNC0wMiAxMjowMCBFdXJvcGUvQmVybGluJyA9PiBbICd0dWUnLCA5MiwgMTMsIDkxIF0sCiAgICAgICcyMDIwLTAxLTAxIEFtZXJpY2EvU2FvX1BhdWxvJyA9PiBbICd3ZWQnLCAxLCA1MiwgMzY2IF0sCiAgICAgICcyMDIwLTAxLTAxIEFtZXJpY2EvU2FudGFyZW0nID0+IFsgJ3dlZCcsIDEsIDUyLCAzNjYgXSwKCiAgICAgICAgIyBodHRwczovL2dpdGh1Yi5jb20vZmxvcmFpc29uL2Z1Z2l0L2lzc3Vlcy85NgogICAgICAgICMKICAgICAgJzIwMjQtMDMtMTIgMTI6NTk6NTkgRXRjL1VUQycgPT4gWyAndHVlJywgNzIsIDI3MSwgMTg5OCBdLAogICAgICAnMjAyNC0wMy0xMiAxMTo1OTo1OSBFdGMvVVRDJyA9PiBbICd0dWUnLCA3MiwgMjcxLCAxODk4IF0sCgogICAgICAgICMgaHR0cHM6Ly9naXRodWIuY29tL2Zsb3JhaXNvbi9mdWdpdC9pc3N1ZXMvMTE0CiAgICAgICAgIyBodHRwczovL2dpdGh1Yi5jb20vZmxvcmFpc29uL2V0LW9yYmkvaXNzdWVzLzQ2CiAgICAgICAgIwogICAgICAnMjAxOC0xMi0yOSAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdzYXQnLCAzNjMsIC0xLCAtMiBdLAogICAgICAnMjAxOC0xMi0zMCAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdzdW4nLCAzNjQsIC0xLCAtMSBdLAogICAgICAnMjAxOC0xMi0zMSAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdtb24nLCAzNjUsIDAsIDAgXSwKICAgICAgJzIwMTktMDEtMDEgMTI6MDAgQW1lcmljYS9OZXdfWW9yaycgPT4gWyAndHVlJywgMSwgMCwgMSBdLAogICAgICAnMjAxOS0wMS0wNSAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdzYXQnLCA1LCAwLCA1IF0sCiAgICAgICcyMDE5LTAxLTA2IDEyOjAwIEFtZXJpY2EvTmV3X1lvcmsnID0+IFsgJ3N1bicsIDYsIDAsIDYgXSwKICAgICAgJzIwMTktMDEtMDcgMTI6MDAgQW1lcmljYS9OZXdfWW9yaycgPT4gWyAnbW9uJywgNywgMSwgNyBdLAoKICAgICAgICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9mbG9yYWlzb24vZXQtb3JiaS9pc3N1ZXMvNTEKICAgICAgICAjCiAgICAgICcyMDE5LTAzLTMwIEV0Yy9VVEMnID0+IFsgJ3NhdCcsIDg5LCAxMiwgODkgXSwKICAgICAgJzIwMTktMDMtMzEgRXRjL1VUQycgPT4gWyAnc3VuJywgOTAsIDEyLCA5MCBdLAogICAgICAgICMKICAgICAgJzIwMTktMDMtMzAgRXVyb3BlL0JlcmxpbicgPT4gWyAnc2F0JywgODksIDEyLCA4OSBdLAogICAgICAnMjAxOS0wMy0zMSBFdXJvcGUvQmVybGluJyA9PiBbICdzdW4nLCA5MCwgMTIsIDkwIF0sCiAgICAgICAgIwogICAgICAnMjAxOS0wNC0wMSBFdXJvcGUvQmVybGluJyA9PiBbICdtb24nLCA5MSwgMTMsIDkxIF0sCiAgICAgICcyMDE5LTA0LTAyIEV1cm9wZS9CZXJsaW4nID0+IFsgJ3R1ZScsIDkyLCAxMywgOTIgXSwKCiAgICB9LmVhY2ggZG8gfHQsICh3ZGF5LCB5ZGF5LCByd2VlaywgcmRheSl8CgogICAgICB0ZXN0ICJmb3IgI3t0fSwgcmV0dXJucyByd2Vlazoje3J3ZWVrfS9yZGF5OiN7cmRheX0iIGRvCgogICAgICAgIHQgPSBFdE9yYmkubWFrZV90aW1lKHQpCgogICAgICAgIGFzc2VydCgKICAgICAgICAgIFsgd2RheSwgeWRheSwgcndlZWssIHJkYXkgXSwKICAgICAgICAgIFsgV0RBWVNbdC53ZGF5XSwgdC55ZGF5LCB0LnJ3ZWVrLCB0LnJkYXkgXSkKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyNyd2VlaywgI3JkYXkgd2l0aCBFdE9yYmkucndlZWtfcmVmID0gOnN1bmRheScgZG8KCiAgICB7ICcyMDE5LTA0LTAyIDEyOjAwIEV1cm9wZS9CZXJsaW4nICA9PiBbICd0dWUnLCA5MiwgMTMsIDkyIF0sCiAgICAgICcyMDE5LTA0LTAyIEV1cm9wZS9CZXJsaW4nICAgICAgICA9PiBbICd0dWUnLCA5MiwgMTMsIDkyIF0sCgogICAgICAnMjAwOC0xMi0zMSAxMjowMCBFdXJvcGUvTGlzYm9uJyA9PiBbICd3ZWQnLCAzNjYsIC01MjIsIC0zNjUxIF0sCiAgICAgICcyMDE4LTEyLTMxIDEyOjAwIEV1cm9wZS9Mb25kb24nID0+IFsgJ21vbicsIDM2NSwgMCwgMSBdLAogICAgICAnMjAxOS0wMS0wMSAxMjowMCBFdXJvcGUvUGFyaXMnID0+IFsgJ3R1ZScsIDEsIDAsIDIgXSwKICAgICAgJzIwMjAtMDEtMDEgQW1lcmljYS9TYW9fUGF1bG8nID0+IFsgJ3dlZCcsIDEsIDUyLCAzNjcgXSwKICAgICAgJzIwMjAtMDEtMDEgQW1lcmljYS9TYW50YXJlbScgPT4gWyAnd2VkJywgMSwgNTIsIDM2NyBdLAoKICAgICAgICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9mbG9yYWlzb24vZnVnaXQvaXNzdWVzLzk2CiAgICAgICAgIwogICAgICAnMjAyNC0wMy0xMiAxMjo1OTo1OSBFdGMvVVRDJyA9PiBbICd0dWUnLCA3MiwgMjcxLCAxODk5IF0sCiAgICAgICcyMDI0LTAzLTEyIDExOjU5OjU5IEV0Yy9VVEMnID0+IFsgJ3R1ZScsIDcyLCAyNzEsIDE4OTkgXSwKCiAgICAgICAgIyBodHRwczovL2dpdGh1Yi5jb20vZmxvcmFpc29uL2Z1Z2l0L2lzc3Vlcy8xMTQKICAgICAgICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9mbG9yYWlzb24vZXQtb3JiaS9pc3N1ZXMvNDYKICAgICAgICAjCiAgICAgICcyMDE4LTEyLTI5IDEyOjAwIEFtZXJpY2EvTmV3X1lvcmsnID0+IFsgJ3NhdCcsIDM2MywgLTEsIC0xIF0sCiAgICAgICcyMDE4LTEyLTMwIDEyOjAwIEFtZXJpY2EvTmV3X1lvcmsnID0+IFsgJ3N1bicsIDM2NCwgMCwgMCBdLAogICAgICAnMjAxOC0xMi0zMSAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdtb24nLCAzNjUsIDAsIDEgXSwKICAgICAgJzIwMTktMDEtMDEgMTI6MDAgQW1lcmljYS9OZXdfWW9yaycgPT4gWyAndHVlJywgMSwgMCwgMiBdLAogICAgICAnMjAxOS0wMS0wNSAxMjowMCBBbWVyaWNhL05ld19Zb3JrJyA9PiBbICdzYXQnLCA1LCAwLCA2IF0sCiAgICAgICcyMDE5LTAxLTA2IDEyOjAwIEFtZXJpY2EvTmV3X1lvcmsnID0+IFsgJ3N1bicsIDYsIDEsIDcgXSwKICAgICAgJzIwMTktMDEtMDcgMTI6MDAgQW1lcmljYS9OZXdfWW9yaycgPT4gWyAnbW9uJywgNywgMSwgOCBdLAoKICAgIH0uZWFjaCBkbyB8dCwgKHdkYXksIHlkYXksIHJ3ZWVrLCByZGF5KXwKCiAgICAgIHRlc3QgImZvciAje3R9LCByZXR1cm5zIHJ3ZWVrOiN7cndlZWt9L3JkYXk6I3tyZGF5fSIgZG8KCiAgICAgICAgRXRPcmJpLnJ3ZWVrX3JlZiA9IDpzdW5kYXkKCiAgICAgICAgdCA9IEV0T3JiaS5tYWtlX3RpbWUodCkKCiAgICAgICAgYXNzZXJ0KAogICAgICAgICAgWyB3ZGF5LCB5ZGF5LCByd2VlaywgcmRheSBdLAogICAgICAgICAgWyBXREFZU1t0LndkYXldLCB0LnlkYXksIHQucndlZWssIHQucmRheSBdKQoKICAgICAgICBFdE9yYmkucndlZWtfcmVmID0gOmRlZmF1bHQKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyNsb2NhbHRpbWUnIGRvCgogICAgdGVzdCAncmV0dXJucyBhIG5ldyBFb1RpbWUgaW5zdGFuY2UgaW4gdGhlIGxvY2FsIHpvbmUnIGRvCgogICAgICB0ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdQYWNpZmljL0FwaWEnKQoKICAgICAgYXNzZXJ0IHQudG9fcywgJzIwMDctMTAtMzEgMTk6MjU6MDAgLTExMDAnCgogICAgICB0MSA9IGluX3pvbmUoJ0V1cm9wZS9Nb3Njb3cnKSB7IHQubG9jYWx0aW1lIH0KCiAgICAgIGFzc2VydCB0MS50b19zLCAnMjAwNy0xMS0wMSAwOToyNTowMCArMDMwMCcKICAgICAgYXNzZXJ0IHQxLm9iamVjdF9pZCAhPSB0Lm9iamVjdF9pZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjbG9jYWx0aW1lKHpvbmUpJyBkbwoKICAgIHRlc3QgJ3JldHVybnMgYSBuZXcgRW9UaW1lIGluc3RhbmNlIGxvY2FsIHRvIGEgZ2l2ZW4gem9uZScgZG8KCiAgICAgIHQgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ1BhY2lmaWMvQXBpYScpCgogICAgICBhc3NlcnQgdC50b19zLCAnMjAwNy0xMC0zMSAxOToyNTowMCAtMTEwMCcKCiAgICAgIHQxID0gdC5sb2NhbHRpbWUoJ0V1cm9wZS9QYXJpcycpCgogICAgICBhc3NlcnQgdDEudG9fcywgJzIwMDctMTEtMDEgMDc6MjU6MDAgKzAxMDAnCiAgICAgIGFzc2VydCB0MS5vYmplY3RfaWQgIT0gdC5vYmplY3RfaWQKICAgIGVuZAoKICAgIHRlc3QgJ3JldHVybnMgYSBuZXcgRW9UaW1lIGluc3RhbmNlIGV2ZW50IGlmIHRhcmdldCB6b25lIGlzIHRoZSBzYW1lJyBkbwoKICAgICAgdCA9IEV0T3JiaTo6RW9UaW1lLm5ldygxMTkzODk4MzAwLCAnUGFjaWZpYy9BcGlhJykKCiAgICAgIGFzc2VydCB0LnRvX3MsICcyMDA3LTEwLTMxIDE5OjI1OjAwIC0xMTAwJwoKICAgICAgdDEgPSB0LmxvY2FsdGltZSgnUGFjaWZpYy9BcGlhJykKCiAgICAgIGFzc2VydCB0MS50b19zLCAnMjAwNy0xMC0zMSAxOToyNTowMCAtMTEwMCcKICAgICAgYXNzZXJ0IHQxLm9iamVjdF9pZCAhPSB0Lm9iamVjdF9pZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjdHJhbnNsYXRlKHpvbmU9bmlsKScgZG8KCiAgICB0ZXN0ICdpcyBhbiBhbGlhcyB0byAjbG9jYWx0aW1lKHpvbmU9bmlsKScgZG8KCiAgICAgIHQgPSBFdE9yYmk6OkVvVGltZS5uZXcoMTE5Mzg5ODMwMCwgJ1BhY2lmaWMvQXBpYScpCgogICAgICBhc3NlcnQgdC50b19zLCAnMjAwNy0xMC0zMSAxOToyNTowMCAtMTEwMCcKCiAgICAgIHQxID0gdC50cmFuc2xhdGUoJ0V1cm9wZS9QYXJpcycpCgogICAgICBhc3NlcnQgdDEudG9fcywgJzIwMDctMTEtMDEgMDc6MjU6MDAgKzAxMDAnCiAgICAgIGFzc2VydCB0MS5vYmplY3RfaWQgIT0gdC5vYmplY3RfaWQKICAgIGVuZAogIGVuZAoKICBncm91cCAnI2luX3RpbWVfem9uZSh6b25lPW5pbCknIGRvCgogICAgdGVzdCAnaXMgYW4gYWxpYXMgdG8gI2xvY2FsdGltZSh6b25lPW5pbCknIGRvCgogICAgICB0ID0gRXRPcmJpOjpFb1RpbWUubmV3KDExOTM4OTgzMDAsICdQYWNpZmljL0FwaWEnKQoKICAgICAgYXNzZXJ0IHQudG9fcywgJzIwMDctMTAtMzEgMTk6MjU6MDAgLTExMDAnCgogICAgICB0MSA9IHQuaW5fdGltZV96b25lKCdFdXJvcGUvTW9zY293JykKCiAgICAgIGFzc2VydCB0MS50b19zLCAnMjAwNy0xMS0wMSAwOToyNTowMCArMDMwMCcKICAgICAgYXNzZXJ0IHQxLm9iamVjdF9pZCAhPSB0Lm9iamVjdF9pZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjaXNvODYwMScgZG8KCiAgICBbCgogICAgICBbIDExOTM4OTgzMDAsICdQYWNpZmljL0FwaWEnLCBuaWwsICcyMDA3LTEwLTMxVDE5OjI1OjAwLTExOjAwJyBdLAogICAgICBbIDExOTM4OTgzMDAsICdVVEMnLCBuaWwsICcyMDA3LTExLTAxVDA2OjI1OjAwWicgXSwKICAgICAgWyAnMjAxNy0wNi0yMSBFdXJvcGUvUGFyaXMnLCBuaWwsIG5pbCwgJzIwMTctMDYtMjFUMDA6MDA6MDArMDI6MDAnIF0sCiAgICAgIFsgJzIwMTctMDEtMjEgRXVyb3BlL1BhcmlzJywgbmlsLCBuaWwsICcyMDE3LTAxLTIxVDAwOjAwOjAwKzAxOjAwJyBdLAoKICAgICAgI1sgMTE5Mzg5ODMwMC4xMSwgJ1BhY2lmaWMvQXBpYScsIDIsICcyMDA3LTEwLTMxVDE5OjI1OjAwLjEwLTExOjAwJyBdLCM/Pz8KICAgICAgWyAxMTkzODk4MzAwLjcwLCAnVVRDJywgMSwgJzIwMDctMTEtMDFUMDY6MjU6MDAuN1onIF0sCiAgICAgIFsgJzIwMTctMDYtMjEgRXVyb3BlL1BhcmlzJywgbmlsLCAyLCAnMjAxNy0wNi0yMVQwMDowMDowMC4wMCswMjowMCcgXSwKICAgICAgWyAnMjAxNy0wMS0yMSBFdXJvcGUvUGFyaXMnLCBuaWwsIDMsICcyMDE3LTAxLTIxVDAwOjAwOjAwLjAwMCswMTowMCcgXSwKCiAgICBdLmVhY2ggZG8gfHMsIHosIGYsIGl8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7aS5pbnNwZWN0fSIgZG8KCiAgICAgICAgdCA9CiAgICAgICAgICBpZiB6CiAgICAgICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldyhzLCB6KQogICAgICAgICAgZWxzZQogICAgICAgICAgICBFdE9yYmkubWFrZV90aW1lKHMpCiAgICAgICAgICBlbmQKCiAgICAgICAgYXNzZXJ0IHQuaXNvODYwMShmKSwgaQogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnI2FtYmlndW91cz8nIGRvCgogICAgIyBodHRwczovL3d3dy50aW1lYW5kZGF0ZS5jb20vdGltZS9jaGFuZ2UvdXNhL25ldy15b3JrP3llYXI9MjAxOAoKICAgIHRlc3QgJ3JldHVybnMgZmFsc2UgaWYgdGVzdCBoYXMgYSB1bmlxdWUgY29ycmVzcG9uZGluZyBVVEMgdGltZScgZG8KCiAgICAgICMgd2hhdGV2ZXIgdGhlIGxvY2FsIHpvbmUhCgogICAgICB0ID0gRXRPcmJpOjpFb1RpbWUubmV3KAogICAgICAgIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMDowMDowMCAtMDQwMCcpLnRvX2YsCiAgICAgICAgJ0FtZXJpY2EvTmV3X1lvcmsnKQoKICAgICAgYXNzZXJ0IHQuYW1iaWd1b3VzPywgZmFsc2UKICAgIGVuZAoKICAgIHRlc3QgJ3JldHVybnMgdHJ1ZSBpZiB0ZXN0IGhhcyB0d28gY29ycmVzcG9uZGluZyBVVEMgdGltZXMgKERTVCB0byBub24tRFNUKScgZG8KCiAgICAgICMgd2hhdGV2ZXIgdGhlIGxvY2FsIHpvbmUhCgogICAgICB0ID0gRXRPcmJpOjpFb1RpbWUubmV3KAogICAgICAgIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMTozMDowMCAtMDQwMCcpLnRvX2YsCiAgICAgICAgJ0FtZXJpY2EvTmV3X1lvcmsnKQoKICAgICAgYXNzZXJ0IHQuYW1iaWd1b3VzPywgdHJ1ZQogICAgZW5kCiAgZW5kCgogIGdyb3VwICcjcmVhY2gnIGRvCgogICAgewoKICAgICAgWyAnMjAxOC0wNy0yNCAwMTozMTowMCBBbWVyaWNhL05ld19Zb3JrJywgeyBtaW46IDMwIH0gXSA9PgogICAgICAgICcyMDE4LTA3LTI0IDAyOjMwOjAwIEFtZXJpY2EvTmV3X1lvcmsnLAogICAgICBbICcyMDE4LTA3LTI0IDAxOjMxOjAwIEFtZXJpY2EvTmV3X1lvcmsnLCB7IGhvdTogNyB9IF0gPT4KICAgICAgICAnMjAxOC0wNy0yNCAwNzowMDowMCBBbWVyaWNhL05ld19Zb3JrJywKICAgICAgWyAnMjAxOC0wNy0yNCAwMTozMTowMCBBbWVyaWNhL05ld19Zb3JrJywgeyBob3U6IDcsIG1pbjogMjUgfSBdID0+CiAgICAgICAgJzIwMTgtMDctMjQgMDc6MjU6MDAgQW1lcmljYS9OZXdfWW9yaycsCiAgICAgIFsgJzIwMTgtMDctMjQgMDE6MzE6MDAgQW1lcmljYS9OZXdfWW9yaycsIHsgaDogNywgbTogMjUsIHM6IDEwIH0gXSA9PgogICAgICAgICcyMDE4LTA3LTI0IDA3OjI1OjEwIEFtZXJpY2EvTmV3X1lvcmsnLAogICAgICBbICcyMDE4LTA3LTI0IDAxOjMxOjAwIEFtZXJpY2EvTmV3X1lvcmsnLCB7IGg6IDcsIHM6IDEwIH0gXSA9PgogICAgICAgICcyMDE4LTA3LTI0IDA3OjAwOjEwIEFtZXJpY2EvTmV3X1lvcmsnLAoKICAgIH0uZWFjaCBkbyB8KHN0YXJ0LCBwb2ludHMpLCByZXN1bHR8CgogICAgICB0ZXN0ICJyZWFjaGVzIGZvciAje3BvaW50cy5pbnNwZWN0fSIgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5wYXJzZShzdGFydCkKICAgICAgICB0ID0gdC5yZWFjaChwb2ludHMpCgogICAgICAgIGFzc2VydCB0LnRvX3pzLCByZXN1bHQKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyM9PScgZG8KCiAgICBncm91cCAnRW9UaW1lID09IEVvVGltZScgZG8KCiAgICAgIHRlc3QgJ3JldHVybnMgdHJ1ZSBpZiBzYW1lIHMgYW5kIHNhbWUgVFonIGRvCgogICAgICAgIGVvMCA9IEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMTozMDowMCAtMDQwMCcpCiAgICAgICAgZW8xID0gRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAxOjMwOjAwIC0wNDAwJykKCiAgICAgICAgYXNzZXJ0IGVvMCA9PSBlbzEKICAgICAgZW5kCgogICAgICB0ZXN0ICdyZXR1cm5zIGZhbHNlIGlmIG5vdCBpbiB0aGUgc2FtZSB0aW1lem9uZScgZG8KCiAgICAgICAgZW8wID0gRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjMwOjAwIEV1cm9wZS9CZXJsaW4nKQogICAgICAgIGVvMSA9IEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMTozMDowMCBFdXJvcGUvTG9uZG9uJykKCiAgICAgICAgYXNzZXJ0IGVvMC50b19pID09IGVvMS50b19pCiAgICAgICAgYXNzZXJ0IGVvMCA9PSBlbzEsIGZhbHNlCiAgICAgIGVuZAogICAgZW5kCgogICAgZ3JvdXAgJ0VvVGltZSA9PSBUaW1lJyBkbwoKICAgICAgdGVzdCAncmV0dXJucyB0cnVlIHdoZW4gc2FtZSBzZWMnIGRvCgogICAgICAgIGVvID0gRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjMwOjAwIEV1cm9wZS9CZXJsaW4nKQogICAgICAgIHQgPSBUaW1lLmF0KGVvLnRvX2kpCgogICAgICAgIGFzc2VydCBlbyA9PSB0CiAgICAgICAgYXNzZXJ0IGVvLCB0CiAgICAgIGVuZAoKICAgICAgdGVzdCAncmV0dXJucyBmYWxzZSBlbHNlJyBkbwoKICAgICAgICBlbyA9IEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjozMDowMCBFdXJvcGUvQmVybGluJykKICAgICAgICB0ID0gVGltZS5hdChlby50b19pICsgMSkKCiAgICAgICAgYXNzZXJ0IGVvID09IHQsIGZhbHNlCiAgICAgICAgYXNzZXJ0IGVvICE9IHQKICAgICAgZW5kCiAgICBlbmQKCiAgICBncm91cCAnVGltZSA9PSBFb1RpbWUnIGRvICMgZWhtLCB5ZWFoLCB0ZXN0aW5nIFJ1Ynkgc29tZWhvdy4uLgoKICAgICAgdGVzdCAncmV0dXJucyB0cnVlIHdoZW4gc2FtZSBzZWMnIGRvCgogICAgICAgIGVvID0gRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjMwOjAwIEV1cm9wZS9CZXJsaW4nKQogICAgICAgIHQgPSBUaW1lLmF0KGVvLnRvX2kpCgogICAgICAgIGFzc2VydCB0ID09IGVvLCB0cnVlICMgdGhhbmtzIFJ1YnkhCiAgICAgICAgYXNzZXJ0IHQsIGVvCiAgICAgIGVuZAoKICAgICAgdGVzdCAncmV0dXJucyBmYWxzZSBlbHNlJyBkbwoKICAgICAgICBlbyA9IEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjozMDowMCBFdXJvcGUvQmVybGluJykKICAgICAgICB0ID0gVGltZS5hdChlby50b19pICsgMSkKCiAgICAgICAgYXNzZXJ0IHQgPT0gZW8sIGZhbHNlCiAgICAgICAgYXNzZXJ0IHQgIT0gZW8KICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJyM8LCAjPD0sICM+LCAjPj0sICM8PT4nIGRvCgogICAgWwoKICAgICAgWyBFdE9yYmkucGFyc2UoJzIwMTgtMTEtMDQgMDI6MzA6MDAgRXVyb3BlL0JlcmxpbicpLAogICAgICAgIDo+LAogICAgICAgIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjowMDowMCBFdXJvcGUvQmVybGluJyksCiAgICAgICAgdHJ1ZSBdLAogICAgICBbIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjowMDowMCBFdXJvcGUvQmVybGluJyksCiAgICAgICAgOj4sCiAgICAgICAgRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjMwOjAwIEV1cm9wZS9CZXJsaW4nKSwKICAgICAgICBmYWxzZSBdLAogICAgICBbIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjowMDowMCBFdXJvcGUvTG9uZG9uJyksCiAgICAgICAgOj4sCiAgICAgICAgRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjAwOjAwIEV1cm9wZS9CZXJsaW4nKSwKICAgICAgICB0cnVlIF0sCiAgICAgIFsgRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjAwOjAwIEV1cm9wZS9CZXJsaW4nKSwKICAgICAgICA6PiwKICAgICAgICBFdE9yYmkucGFyc2UoJzIwMTgtMTEtMDQgMDI6MDA6MDAgRXVyb3BlL0xvbmRvbicpLAogICAgICAgIGZhbHNlIF0sCiAgICAgIFsgRXRPcmJpLnBhcnNlKCcyMDE4LTExLTA0IDAyOjAwOjAwIEV1cm9wZS9Mb25kb24nKSwKICAgICAgICA6PiwKICAgICAgICBUaW1lLmF0KEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjowMDowMCBFdXJvcGUvQmVybGluJykudG9faSksCiAgICAgICAgdHJ1ZSBdLAogICAgICBbIEV0T3JiaS5wYXJzZSgnMjAxOC0xMS0wNCAwMjowMDowMCBFdXJvcGUvQmVybGluJyksCiAgICAgICAgOj4sCiAgICAgICAgVGltZS5hdChFdE9yYmkucGFyc2UoJzIwMTgtMTEtMDQgMDI6MDA6MDAgRXVyb3BlL0JlcmxpbicpLnRvX2kpLAogICAgICAgIGZhbHNlIF0sCgogICAgXS5lYWNoIGRvIHx0MCwgY29tcGFyYXRvciwgdDEsIGFzc2VydGVkfAoKICAgICAgdG9fcyA9IGxhbWJkYSB7IHxvfAogICAgICAgIGNhc2UgbwogICAgICAgIHdoZW4gRXRPcmJpOjpFb1RpbWUgdGhlbiAiI3tvLnRvX3N9IChFb1RpbWUpIgogICAgICAgIGVsc2UgIiN7by50b19zfSAoI3tvLmNsYXNzfSkiCiAgICAgICAgZW5kIH0KCiAgICAgIHMwID0gdG9fc1t0MF0KICAgICAgczEgPSB0b19zW3QxXQoKICAgICAgdGVzdCAiI3tzMH0gI3tjb21wYXJhdG9yLnRvX3N9ICN7czF9IHlpZWxkcyAje2Fzc2VydGVkLmluc3BlY3R9IiBkbwoKICAgICAgICBhc3NlcnQgdDAuc2VuZChjb21wYXJhdG9yLCB0MSksIGFzc2VydGVkCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCmVuZAoK \ No newline at end of file +# encoding: UTF-8 + +# +# Specifying EtOrbi +# +# Wed Mar 11 21:17:36 JST 2015, quatre ans... (rufus-scheduler) +# Sun Mar 19 05:16:28 JST 2017 +# + + +group EtOrbi::EoTime do + + group '.new' do + + group "zone 'America/Los_Angeles'" do + + test 'accepts an integer' do + + ot = EtOrbi::EoTime.new(1234567890, 'America/Los_Angeles') + + assert ot.seconds.to_i, 1234567890 + assert ot.zone.name, 'America/Los_Angeles' + end + + test 'accepts a float' do + + ot = EtOrbi::EoTime.new(1234567890.1234, 'America/Los_Angeles') + + assert ot.seconds.to_i, 1234567890 + assert ot.zone.name, 'America/Los_Angeles' + end + + test 'accepts a UTC ::Time instance' do + + ot = + EtOrbi::EoTime.new( + Time.utc(2007, 11, 1, 15, 25, 0), + 'America/Los_Angeles') + + assert ot.seconds.to_i, 1193930700 + assert ot.zone.name, 'America/Los_Angeles' + end + + test 'accepts a UTC EtOrbi::EoTime instance' do + + ot = + EtOrbi::EoTime.new( + EtOrbi::EoTime.new(1193930700, 'UTC'), + 'America/Los_Angeles') + + assert ot.seconds.to_i, 1193930700 + assert ot.zone.name, 'America/Los_Angeles' + end + + test 'accepts a local ::Time instance' do + + #in_zone 'Asia/Samarkand' do + # + # t = Time.parse('2007-11-01 15:25') + # ot = EtOrbi::EoTime.new(t, 'America/Los_Angeles') + # + # assert ot.seconds, t.to_i) + # assert ot.seconds.to_i, 1193912700) + # assert ot.zone.name, 'America/Los_Angeles') + #end + # + # because of https://ci.appveyor.com/project/jmettraux/et-orbi/build/job/8birnr0k54jrihx8 + + t = Time.parse('2007-11-01 15:25 +05:00') + # use a local ::Time instance with a custom timezone :-( + + ot = EtOrbi::EoTime.new(t, 'America/Los_Angeles') + + assert ot.seconds, t.to_i + assert ot.seconds.to_i, 1193912700 + assert ot.zone.name, 'America/Los_Angeles' + end + + test 'accepts a Local EtOrbi::EoTime instance' do + + ot = + EtOrbi::EoTime.new( + EtOrbi::EoTime.new(1193930700, 'Asia/Yekaterinburg'), + 'America/Los_Angeles') + + assert ot.seconds.to_i, 1193930700 + assert ot.zone.name, 'America/Los_Angeles' + end + end + + group "zone TZInfo instance 'Europe/Paris'" do + + test 'accepts an integer' do + + ot = EtOrbi::EoTime + .new(1234567890, ::TZInfo::Timezone.get('Europe/Paris')) + + assert ot.seconds.to_i, 1234567890 + assert ot.zone.name, 'Europe/Paris' + end + end + + group "zone ActiveSupport::TimeZone instance 'America/New_York'" do + + test 'accepts an integer' do + + ot = EtOrbi::EoTime + .new(1234567890, TestActiveSupportTimeZone.make('America/New_York')) + + assert ot.seconds.to_i, 1234567890 + assert ot.zone.name, 'America/New_York' + end + end + end + + group '#zone=' do + + test 'assigns the requested timezone and invalidates the time cache' do + + ot = EtOrbi::EoTime.new(0, 'UTC') + ot.hour + + ot.zone = 'Asia/Kolkata' + + assert ot.zone.name, 'Asia/Kolkata' + assert ot.hour, 5 + assert ot.min, 30 + assert ot.to_f, 0.0 + end + + test 'does not change the timezone when assignment fails' do + + ot = EtOrbi::EoTime.new(0, 'UTC') + + assert_error( + lambda { ot.zone = 'Invalid/Timezone' }, + ArgumentError) + + assert ot.zone.name, 'UTC' + end + end + + group '.utc' do + + [ + + [ [ 2017, 3, 25 ], '2017-03-25T00:00:00Z' ], + [ [ 2017, 3, 25, 21, 23, 29 ], '2017-03-25T21:23:29Z' ], + + ].each do |a, s| + + test "accepts #{a.inspect}" do + + ot = EtOrbi::EoTime.utc(*a) + + assert ot.class, EtOrbi::EoTime + assert ot.zone.name, 'UTC' + assert ot.iso8601, s + end + end + end + + group '.local' do + + [ + + [ [ 2017, 3, 25 ], + %w[ Europe/Lisbon windows:WET Atlantic/Canary ], + '2017-03-25T00:00:00+00:00' ], + [ [ 2017, 3, 25, 21, 23, 29 ], + %w[ Europe/Lisbon windows:WET Atlantic/Canary ], + '2017-03-25T21:23:29+00:00' ], + [ [ 2017, 3, 25, 21, 23, 29 ], + %w[ Europe/Moscow Europe/Kirov ], + '2017-03-25T21:23:29+03:00' ], + + ].each do |a, zs, s| + + test "accepts #{a.inspect} in #{zs.first}" do + + in_zone(zs.first) do + + ot = EtOrbi::EoTime.local(*a) + + assert ot.class, EtOrbi::EoTime + assert_include ot.zone, select_zones(zs) + assert ot.iso8601, s + end + end + end + end + + group '#to_time (protected)' do + + if TZInfo::Timezone + .get('America/Los_Angeles') + .utc_to_local(Time.at(1193898300)) + .utc? + then + # TZInfo < 2.0.0 + + test 'returns a local Time instance, although with a UTC zone' do + + ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') + t = ot.send(:to_time) + + assert ot.to_debug_s, 'ot 2007-10-31 23:25:00 -07:00 dst:true' + + assert t.to_i, 1193898300 - 7 * 3600 # /!\ + assert t.utc_offset, 0 + + assert t.to_debug_s, 't 2007-10-31 23:25:00 +00:00 dst:false' + # Time instance stuck to UTC... + end + + else + # TZInfo >= 2.0.0 + + test 'returns a local Time instance' do + + ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') + t = ot.send(:to_time) + + assert ot.to_debug_s, 'ot 2007-10-31 23:25:00 -07:00 dst:true' + + assert t.to_i, 1193898300 + assert t.utc_offset, -7 * 3600 + + assert t.to_debug_s, 't 2007-10-31 23:25:00 -07:00 dst:true' + end + end + end + + group '#to_local_time' do + + test 'returns a local Time instance in the local time zone' do + + ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') + t = ot.to_local_time + + assert t.class, ::Time + assert t.to_i, 1193898300 + assert t.usec, 0 + + t1 = Time.parse(t.strftime("%Y-%m-%d %H:%M:%S.#{'%06d' % t.usec}")) + assert t.to_s, t1.to_s + end + end + + group '#to_t' do + + test 'is an alias to #to_local_time' do + + ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') + t = ot.to_t + + assert t.class, ::Time + assert t.to_i, 1193898300 + assert t.usec, 0 + end + end + + group '#to_utc_time' do + + test 'is an alias to #to_utc' do + + ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') + ut = ot.to_utc_time + + assert ut.class, ::Time + assert ut.to_i, 1193898300 + assert ut.usec, 0 + + assert ut.to_s, '2007-11-01 06:25:00 UTC' + end + end + + group '#utc' do + + test 'returns an UTC Time instance' do + + ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') + ut = ot.utc + + assert ut.to_i, 1193898300 + + assert ot.to_debug_s, 'ot 2007-10-31 23:25:00 -07:00 dst:true' + assert ut.to_debug_s, 't 2007-11-01 06:25:00 +00:00 dst:false' + end + end + + group '#utc?' do + + test 'returns true if the EoTime zone is UTC' do + + assert EtOrbi::EoTime.new(1193898300, 'Z').utc? + assert EtOrbi::EoTime.new(1193898300, 'UTC').utc? + assert EtOrbi::EoTime.new(1193898300, 'GMT').utc? + assert EtOrbi::EoTime.new(1193898300, 'Zulu').utc? + end + + test 'returns false else' do + + ot = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') + + assert ot.utc?, false + end + end + + group '#add' do + + test 'adds seconds' do + + ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') + ot.add(111) + + assert ot.seconds, 1193898300 + 111 + end + + test 'adds anything that has a #to_i and is not a String, gh-40' do + + ot = + EtOrbi::EoTime.new(1193898300, 'Asia/Vladivostok') + + TestActiveSupportDuration.new(3600) # 1.hour + + assert ot.seconds, 1193898300 + 3600 + end + + test 'goes into DST' do + + ot = + EtOrbi::EoTime.new( + Time.gm(2015, 3, 8, 9, 59, 59), + 'America/Los_Angeles') + + t0 = ot.dup + ot.add(1) + t1 = ot + + st0 = t0.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t0.isdst}" + st1 = t1.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t1.isdst}" + + assert t0.to_i, 1425808799 + assert t1.to_i, 1425808800 + assert st0, '2015/03/08 01:59:59 PST false' + assert st1, '2015/03/08 03:00:00 PDT true' + end + + test 'goes out of DST' do + + ot = EtOrbi.parse('2014-10-26 01:59:59 Europe/Berlin') + # + # still in DST, DST ends (clock backward) at 03:00 + # https://www.timeanddate.com/time/change/germany/berlin + + t0 = ot.dup + ot.add(1) + t1 = ot.dup + ot.add(3600) + t2 = ot.dup + ot.add(1) + t3 = ot + + st0 = t0.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t0.isdst}" + st1 = t1.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t1.isdst}" + st2 = t2.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t2.isdst}" + st3 = t3.strftime('%Y/%m/%d %H:%M:%S %Z') + " #{t3.isdst}" + + assert t0.to_i, 1414281599 + assert t1.to_i, 1414285200 - 3600 + assert t2.to_i, 1414285200 + assert t3.to_i, 1414285201 + + assert st0, '2014/10/26 01:59:59 CEST true' + assert st1, '2014/10/26 02:00:00 CEST true' + assert st2, '2014/10/26 02:00:00 CET false' + assert st3, '2014/10/26 02:00:01 CET false' + + assert t1 - t0, 1 + assert t2 - t1, 3600 + assert t3 - t2, 1 + end + + test 'returns self' do + + ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') + ot1 = ot.subtract(111) + + assert ot1.object_id, ot.object_id + end + end + + group '#subtract' do + + test 'substracts seconds' do + + ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') + ot.subtract(111) + + assert ot.seconds, 1193898300 - 111 + end + + test 'subtracts anything that has a #to_i and is not a String, gh-40' do + + ot = + EtOrbi::EoTime.new(1193898300, 'Asia/Vladivostok') - + TestActiveSupportDuration.new(3600) # 1.hour + + assert ot.seconds, 1193898300 - 3600 + end + + test 'returns self' do + + ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') + ot1 = ot.subtract(111) + + assert ot1.object_id, ot.object_id + end + end + + group '#to_f' do + + test 'returns the @seconds' do + + ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') + + assert ot.to_f, 1193898300 + end + end + + group '#to_s' do + + test 'returns the a formatted datetime string' do + + ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') + + assert ot.to_s, '2007-11-01 07:25:00 +0100' + end + end + + group '#to_zs' do + + test 'returns the a formatted datetime string with an explicit timezone' do + + ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') + + assert ot.to_zs, '2007-11-01 07:25:00 Europe/Paris' + end + end + + group '#strftime' do + + test 'accepts %Z, %z, %:z and %::z' do + + assert( + EtOrbi::EoTime.new(0, 'Europe/Bratislava') \ + .strftime('%Y-%m-%d %H:%M:%S %Z %z %:z %::z'), + '1970-01-01 01:00:00 CET +0100 +01:00 +01:00:00') + end + + test 'accepts %/Z' do + + assert( + EtOrbi::EoTime.new(0, 'Europe/Bratislava') \ + .strftime('%Y-%m-%d %H:%M:%S %/Z'), + "1970-01-01 01:00:00 Europe/Bratislava") + end + end + + group '#monthdays' do + + [ + [ [ 1970, 1, 1 ], %w[ 4#1 4#-5 ] ], + [ [ 1970, 1, 7 ], %w[ 3#1 3#-4 ] ], + [ [ 1970, 1, 14 ], %w[ 3#2 3#-3 ] ], + [ [ 2011, 3, 11 ], %w[ 5#2 5#-3 ] ] + ].each do |d, x| + + test "returns the #{x.inspect} for #{d.inspect}" do + + t = EtOrbi.parse("#{d.join('-')} 12:00") + + assert t.monthdays, x + end + end + end + + group '#+' do + + test 'adds seconds' do + + ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') + ot1 = ot + 111 + + assert ot1.class, EtOrbi::EoTime + assert ot1.seconds, 1193898300 + 111 + assert ot1.object_id != ot.object_id + end + + test 'rejects Time instances' do + + ot = + EtOrbi.make_time('2017-10-31 22:00:10 Europe/Paris') + t = + in_zone('America/Los_Angeles') { Time.local(2017, 10, 30, 22, 00, 10) } + + assert_error( + lambda { ot + t }, + ArgumentError, 'Cannot add Time to EoTime instance') + end + + test 'rejects EoTime instances' do + + ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') + ot1 = EtOrbi::EoTime.new(1193898300, 'America/Los_Angeles') + + assert_error( + lambda { ot + ot1 }, + ArgumentError, 'Cannot add EtOrbi::EoTime to EoTime instance') + end + end + + group '#-' do + + test 'subtracts seconds' do + + ot = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') + ot1 = ot - 111 + + assert ot1.class, EtOrbi::EoTime + assert ot1.seconds, 1193898300 - 111 + assert ot1.object_id != ot.object_id + end + + test 'subtracts Time instances' do + + ot = EtOrbi.make_time('2017-10-31 22:00:10 Europe/Paris') + + #t = time_in_zone('America/Los_Angeles', '2017-10-30 22:00:10') +#time_in_zone('PST7PDT', '2017-10-30 22:00:10') + t = Time.parse('2017-10-30 22:00:10 -0700') + # because of https://ci.appveyor.com/project/jmettraux/et-orbi/build/job/evp6dmqegfgxhcnm + + d = ot - t + + assert d.class, Float + assert d.to_i, 57600 + end + + test 'subtracts EoTime instances' do + + ot0 = EtOrbi::EoTime.new(1193898300, 'Europe/Paris') + ot1 = EtOrbi::EoTime.new(1193898300 + 222, 'America/Los_Angeles') + d = ot0 - ot1 + + assert d.class, Float + assert d.to_i, -222 + end + end + + group '#wday_in_month' do + + { + + [ 1193898300, 'Europe/Paris' ] => [ 1, -5 ], + '2017-06-21 Europe/Paris' => [ 3, -2 ], + '2022-03-29 UTC' => [ 5, -1 ], + '2022-03-29 Europe/Paris' => [ 5, -1 ], + + }.each do |k, v| + + test "computes the wday in month interval for #{k.inspect}" do + + t = + case k + when String then EtOrbi.make_time(k) + else EtOrbi::EoTime.new(*k) + end + + assert t.wday_in_month, v + end + end + end + + WDAYS = %w[ sun mon tue wed thu fri sat sun ] + + group '#rweek, #rday' do + + { '2008-12-31 12:00 Europe/Lisbon' => [ 'wed', 366, -522, -3652 ], + '2018-12-31 12:00 Europe/London' => [ 'mon', 365, 0, 0 ], + '2019-01-01 12:00 Europe/Paris' => [ 'tue', 1, 0, 1 ], + '2019-04-02 12:00 Europe/Berlin' => [ 'tue', 92, 13, 91 ], + '2020-01-01 America/Sao_Paulo' => [ 'wed', 1, 52, 366 ], + '2020-01-01 America/Santarem' => [ 'wed', 1, 52, 366 ], + + # https://github.com/floraison/fugit/issues/96 + # + '2024-03-12 12:59:59 Etc/UTC' => [ 'tue', 72, 271, 1898 ], + '2024-03-12 11:59:59 Etc/UTC' => [ 'tue', 72, 271, 1898 ], + + # https://github.com/floraison/fugit/issues/114 + # https://github.com/floraison/et-orbi/issues/46 + # + '2018-12-29 12:00 America/New_York' => [ 'sat', 363, -1, -2 ], + '2018-12-30 12:00 America/New_York' => [ 'sun', 364, -1, -1 ], + '2018-12-31 12:00 America/New_York' => [ 'mon', 365, 0, 0 ], + '2019-01-01 12:00 America/New_York' => [ 'tue', 1, 0, 1 ], + '2019-01-05 12:00 America/New_York' => [ 'sat', 5, 0, 5 ], + '2019-01-06 12:00 America/New_York' => [ 'sun', 6, 0, 6 ], + '2019-01-07 12:00 America/New_York' => [ 'mon', 7, 1, 7 ], + + # https://github.com/floraison/et-orbi/issues/51 + # + '2019-03-30 Etc/UTC' => [ 'sat', 89, 12, 89 ], + '2019-03-31 Etc/UTC' => [ 'sun', 90, 12, 90 ], + # + '2019-03-30 Europe/Berlin' => [ 'sat', 89, 12, 89 ], + '2019-03-31 Europe/Berlin' => [ 'sun', 90, 12, 90 ], + # + '2019-04-01 Europe/Berlin' => [ 'mon', 91, 13, 91 ], + '2019-04-02 Europe/Berlin' => [ 'tue', 92, 13, 92 ], + + }.each do |t, (wday, yday, rweek, rday)| + + test "for #{t}, returns rweek:#{rweek}/rday:#{rday}" do + + t = EtOrbi.make_time(t) + + assert( + [ wday, yday, rweek, rday ], + [ WDAYS[t.wday], t.yday, t.rweek, t.rday ]) + end + end + end + + group '#rweek, #rday with EtOrbi.rweek_ref = :sunday' do + + { '2019-04-02 12:00 Europe/Berlin' => [ 'tue', 92, 13, 92 ], + '2019-04-02 Europe/Berlin' => [ 'tue', 92, 13, 92 ], + + '2008-12-31 12:00 Europe/Lisbon' => [ 'wed', 366, -522, -3651 ], + '2018-12-31 12:00 Europe/London' => [ 'mon', 365, 0, 1 ], + '2019-01-01 12:00 Europe/Paris' => [ 'tue', 1, 0, 2 ], + '2020-01-01 America/Sao_Paulo' => [ 'wed', 1, 52, 367 ], + '2020-01-01 America/Santarem' => [ 'wed', 1, 52, 367 ], + + # https://github.com/floraison/fugit/issues/96 + # + '2024-03-12 12:59:59 Etc/UTC' => [ 'tue', 72, 271, 1899 ], + '2024-03-12 11:59:59 Etc/UTC' => [ 'tue', 72, 271, 1899 ], + + # https://github.com/floraison/fugit/issues/114 + # https://github.com/floraison/et-orbi/issues/46 + # + '2018-12-29 12:00 America/New_York' => [ 'sat', 363, -1, -1 ], + '2018-12-30 12:00 America/New_York' => [ 'sun', 364, 0, 0 ], + '2018-12-31 12:00 America/New_York' => [ 'mon', 365, 0, 1 ], + '2019-01-01 12:00 America/New_York' => [ 'tue', 1, 0, 2 ], + '2019-01-05 12:00 America/New_York' => [ 'sat', 5, 0, 6 ], + '2019-01-06 12:00 America/New_York' => [ 'sun', 6, 1, 7 ], + '2019-01-07 12:00 America/New_York' => [ 'mon', 7, 1, 8 ], + + }.each do |t, (wday, yday, rweek, rday)| + + test "for #{t}, returns rweek:#{rweek}/rday:#{rday}" do + + EtOrbi.rweek_ref = :sunday + + t = EtOrbi.make_time(t) + + assert( + [ wday, yday, rweek, rday ], + [ WDAYS[t.wday], t.yday, t.rweek, t.rday ]) + + EtOrbi.rweek_ref = :default + end + end + end + + group '#localtime' do + + test 'returns a new EoTime instance in the local zone' do + + t = EtOrbi::EoTime.new(1193898300, 'Pacific/Apia') + + assert t.to_s, '2007-10-31 19:25:00 -1100' + + t1 = in_zone('Europe/Moscow') { t.localtime } + + assert t1.to_s, '2007-11-01 09:25:00 +0300' + assert t1.object_id != t.object_id + end + end + + group '#localtime(zone)' do + + test 'returns a new EoTime instance local to a given zone' do + + t = EtOrbi::EoTime.new(1193898300, 'Pacific/Apia') + + assert t.to_s, '2007-10-31 19:25:00 -1100' + + t1 = t.localtime('Europe/Paris') + + assert t1.to_s, '2007-11-01 07:25:00 +0100' + assert t1.object_id != t.object_id + end + + test 'returns a new EoTime instance event if target zone is the same' do + + t = EtOrbi::EoTime.new(1193898300, 'Pacific/Apia') + + assert t.to_s, '2007-10-31 19:25:00 -1100' + + t1 = t.localtime('Pacific/Apia') + + assert t1.to_s, '2007-10-31 19:25:00 -1100' + assert t1.object_id != t.object_id + end + end + + group '#translate(zone=nil)' do + + test 'is an alias to #localtime(zone=nil)' do + + t = EtOrbi::EoTime.new(1193898300, 'Pacific/Apia') + + assert t.to_s, '2007-10-31 19:25:00 -1100' + + t1 = t.translate('Europe/Paris') + + assert t1.to_s, '2007-11-01 07:25:00 +0100' + assert t1.object_id != t.object_id + end + end + + group '#in_time_zone(zone=nil)' do + + test 'is an alias to #localtime(zone=nil)' do + + t = EtOrbi::EoTime.new(1193898300, 'Pacific/Apia') + + assert t.to_s, '2007-10-31 19:25:00 -1100' + + t1 = t.in_time_zone('Europe/Moscow') + + assert t1.to_s, '2007-11-01 09:25:00 +0300' + assert t1.object_id != t.object_id + end + end + + group '#iso8601' do + + [ + + [ 1193898300, 'Pacific/Apia', nil, '2007-10-31T19:25:00-11:00' ], + [ 1193898300, 'UTC', nil, '2007-11-01T06:25:00Z' ], + [ '2017-06-21 Europe/Paris', nil, nil, '2017-06-21T00:00:00+02:00' ], + [ '2017-01-21 Europe/Paris', nil, nil, '2017-01-21T00:00:00+01:00' ], + + #[ 1193898300.11, 'Pacific/Apia', 2, '2007-10-31T19:25:00.10-11:00' ],#??? + [ 1193898300.70, 'UTC', 1, '2007-11-01T06:25:00.7Z' ], + [ '2017-06-21 Europe/Paris', nil, 2, '2017-06-21T00:00:00.00+02:00' ], + [ '2017-01-21 Europe/Paris', nil, 3, '2017-01-21T00:00:00.000+01:00' ], + + ].each do |s, z, f, i| + + test "returns #{i.inspect}" do + + t = + if z + EtOrbi::EoTime.new(s, z) + else + EtOrbi.make_time(s) + end + + assert t.iso8601(f), i + end + end + end + + group '#ambiguous?' do + + # https://www.timeanddate.com/time/change/usa/new-york?year=2018 + + test 'returns false if test has a unique corresponding UTC time' do + + # whatever the local zone! + + t = EtOrbi::EoTime.new( + EtOrbi.parse('2018-11-04 00:00:00 -0400').to_f, + 'America/New_York') + + assert t.ambiguous?, false + end + + test 'returns true if test has two corresponding UTC times (DST to non-DST)' do + + # whatever the local zone! + + t = EtOrbi::EoTime.new( + EtOrbi.parse('2018-11-04 01:30:00 -0400').to_f, + 'America/New_York') + + assert t.ambiguous?, true + end + end + + group '#reach' do + + { + + [ '2018-07-24 01:31:00 America/New_York', { min: 30 } ] => + '2018-07-24 02:30:00 America/New_York', + [ '2018-07-24 01:31:00 America/New_York', { hou: 7 } ] => + '2018-07-24 07:00:00 America/New_York', + [ '2018-07-24 01:31:00 America/New_York', { hou: 7, min: 25 } ] => + '2018-07-24 07:25:00 America/New_York', + [ '2018-07-24 01:31:00 America/New_York', { h: 7, m: 25, s: 10 } ] => + '2018-07-24 07:25:10 America/New_York', + [ '2018-07-24 01:31:00 America/New_York', { h: 7, s: 10 } ] => + '2018-07-24 07:00:10 America/New_York', + + }.each do |(start, points), result| + + test "reaches for #{points.inspect}" do + + t = EtOrbi.parse(start) + t = t.reach(points) + + assert t.to_zs, result + end + end + end + + group '#==' do + + group 'EoTime == EoTime' do + + test 'returns true if same s and same TZ' do + + eo0 = EtOrbi.parse('2018-11-04 01:30:00 -0400') + eo1 = EtOrbi.parse('2018-11-04 01:30:00 -0400') + + assert eo0 == eo1 + end + + test 'returns false if not in the same timezone' do + + eo0 = EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin') + eo1 = EtOrbi.parse('2018-11-04 01:30:00 Europe/London') + + assert eo0.to_i == eo1.to_i + assert eo0 == eo1, false + end + end + + group 'EoTime == Time' do + + test 'returns true when same sec' do + + eo = EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin') + t = Time.at(eo.to_i) + + assert eo == t + assert eo, t + end + + test 'returns false else' do + + eo = EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin') + t = Time.at(eo.to_i + 1) + + assert eo == t, false + assert eo != t + end + end + + group 'Time == EoTime' do # ehm, yeah, testing Ruby somehow... + + test 'returns true when same sec' do + + eo = EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin') + t = Time.at(eo.to_i) + + assert t == eo, true # thanks Ruby! + assert t, eo + end + + test 'returns false else' do + + eo = EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin') + t = Time.at(eo.to_i + 1) + + assert t == eo, false + assert t != eo + end + end + end + + group '#<, #<=, #>, #>=, #<=>' do + + [ + + [ EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin'), + :>, + EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin'), + true ], + [ EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin'), + :>, + EtOrbi.parse('2018-11-04 02:30:00 Europe/Berlin'), + false ], + [ EtOrbi.parse('2018-11-04 02:00:00 Europe/London'), + :>, + EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin'), + true ], + [ EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin'), + :>, + EtOrbi.parse('2018-11-04 02:00:00 Europe/London'), + false ], + [ EtOrbi.parse('2018-11-04 02:00:00 Europe/London'), + :>, + Time.at(EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin').to_i), + true ], + [ EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin'), + :>, + Time.at(EtOrbi.parse('2018-11-04 02:00:00 Europe/Berlin').to_i), + false ], + + ].each do |t0, comparator, t1, asserted| + + to_s = lambda { |o| + case o + when EtOrbi::EoTime then "#{o.to_s} (EoTime)" + else "#{o.to_s} (#{o.class})" + end } + + s0 = to_s[t0] + s1 = to_s[t1] + + test "#{s0} #{comparator.to_s} #{s1} yields #{asserted.inspect}" do + + assert t0.send(comparator, t1), asserted + end + end + end +end +