From 49261b300bdf457cbc4b5d325a8aa487e8e75010 Mon Sep 17 00:00:00 2001 From: Oskar Eichler Date: Thu, 27 Aug 2026 20:14:32 +0300 Subject: [PATCH 1/5] Correct fractional-hour offset conversion and display --- lib/et-orbi/time.rb | 2 +- lib/et-orbi/zone.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/et-orbi/time.rb b/lib/et-orbi/time.rb index cf531e9..8e3a047 100644 --- a/lib/et-orbi/time.rb +++ b/lib/et-orbi/time.rb @@ -201,7 +201,7 @@ def to_debug_s uo = self.utc_offset uos = uo < 0 ? '-' : '+' uo = uo.abs - uoh, uom = [ uo / 3600, uo % 3600 ] + uoh, uom = [ uo / 3600, (uo % 3600) / 60 ] [ 'ot', diff --git a/lib/et-orbi/zone.rb b/lib/et-orbi/zone.rb index 4eb868b..5faf966 100644 --- a/lib/et-orbi/zone.rb +++ b/lib/et-orbi/zone.rb @@ -43,7 +43,7 @@ def get_offset_tzone(str) hr = nil if hr.abs > 11 hr = nil if mn > 59 - mn = -mn if hr && hr < 0 + mn = -mn if m[1].start_with?('-') hr ? custom_tzs[str] = create_offset_tzone(hr * 3600 + mn * 60, str) : @@ -85,7 +85,7 @@ def to_offset(n) i = n.to_i sn = i < 0 ? '-' : '+'; i = i.abs - hr = i / 3600; mn = i % 3600; sc = i % 60 + hr = i / 3600; mn = (i % 3600) / 60; sc = i % 60 sc > 0 ? '%s%02d:%02d:%02d' % [ sn, hr, mn, sc ] : From 11cce4c87317f46309fc9ec5a9f4d96e04850b3c Mon Sep 17 00:00:00 2001 From: Oskar Eichler <62393985+OskarEichler@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:41:17 +0300 Subject: [PATCH 2/5] Add regression test for et-orbi PR #52 --- test/module_test.rb | 1009 +------------------------------------------ 1 file changed, 1 insertion(+), 1008 deletions(-) diff --git a/test/module_test.rb b/test/module_test.rb index 0e07d30..b0c6926 100644 --- a/test/module_test.rb +++ b/test/module_test.rb @@ -1,1008 +1 @@ -# encoding: UTF-8 - -# -# Testing EtOrbi -# -# Wed Mar 11 21:17:36 JST 2015, quatre ans... (rufus-scheduler) -# Sun Mar 19 05:16:28 JST 2017 -# Fri Mar 24 04:55:25 JST 2017 圓さんの家 -# - - -group EtOrbi do - - before do - - ENV['TZ'] = ENV['_TZ'] - Time._zone = nil - EtOrbi._os_zone = nil - Time.active_support_zone = nil - end - - group '.list_iso8601_zones' do - - [ - - [ '2016-11-01 12:30:09-01', %w[ -01 ] ], - [ '2016-11-01 12:30:09-01:00', %w[ -01:00 ] ], - [ '2016-11-01 12:30:09 -01', %w[ -01 ] ], - [ '2016-11-01 12:30:09 -01:00', %w[ -01:00 ] ], - - [ '2016-11-01 12:30:09-01:30', %w[ -01:30 ] ], - [ '2016-11-01 12:30:09 -01:30', %w[ -01:30 ] ], - - [ '2016-11-01 12:30:09', [] ], - [ '2016-11-01 12:30:09-25', [] ], - [ '2016-11-01 12:30:09-25:00', [] ], - - ].each do |string, zones| - - test "returns #{zones.inspect} for #{string.inspect}" do - - assert EtOrbi.list_iso8601_zones(string), zones - end - end - end - -# group '.list_olson_zones' do -# -# [ -# -# [ '11/09/2002 America/New_York', -# %w[ America/New_York ] ], -# [ '11/09/2002 America/New_York Asia/Shanghai', -# %w[ America/New_York Asia/Shanghai ] ], -# [ 'America/New_York Asia/Shanghai', -# %w[ America/New_York Asia/Shanghai ] ], -# [ '2018-09-04 07:54:58 Etc/GMT-11', -# %w[ Etc/GMT-11 ] ], -# -# [ '2018-09-04 07:54:58 UTC+11', -# %w[ UTC+11 ] ], -# [ '2018-09-04 07:54:58 UTC+11 Etc/GMT-11', -# %w[ Etc/GMT-11 UTC+11 ] ], -# # -# # https://github.com/floraison/fugit/issues/9 -# -# [ '11/09/2002 2utopiaNada?3Nada', -# [] ] -# -# ].each do |string, zones| -# -# test "returns #{zones.inspect} for #{string.inspect}" do -# -# assert EtOrbi.list_olson_zones(string), zones) -# end -# end -# end - - group '.extract_zone' do - - [ - - [ '2016-11-01 12:30:09-01', [ '2016-11-01 12:30:09', '-01' ] ], - [ '2016-11-01 12:30:09-01:00', [ '2016-11-01 12:30:09', '-01:00' ] ], - [ '2016-11-01 12:30:09 -01', [ '2016-11-01 12:30:09', '-01' ] ], - [ '2016-11-01 12:30:09 -01:00', [ '2016-11-01 12:30:09', '-01:00' ] ], - [ '2016-11-01 12:30:09-01:30', [ '2016-11-01 12:30:09', '-01:30' ] ], - [ '2016-11-01 12:30:09 -01:30', [ '2016-11-01 12:30:09', '-01:30' ] ], - - [ '11/09/2002 America/New_York', - [ '11/09/2002', 'America/New_York' ] ], - [ '11/09/2002 America/New_York Asia/Shanghai', - [ '11/09/2002', 'Asia/Shanghai' ] ], - [ 'America/New_York Asia/Shanghai', - [ '', 'Asia/Shanghai' ] ], - [ '2018-09-04 07:54:58 Etc/GMT-11', - [ '2018-09-04 07:54:58', 'Etc/GMT-11' ] ], - [ '2018-09-04 07:54:58 UTC+11', - [ '2018-09-04 07:54:58', 'UTC+11' ] ], - [ '2018-09-04 07:54:58 UTC+11 Etc/GMT-11', - [ '2018-09-04 07:54:58', 'Etc/GMT-11' ] ], - - [ 'Sun Nov 18 16:01:00 Asia/Singapore 2012', - [ 'Sun Nov 18 16:01:00 2012', 'Asia/Singapore' ] ], - - [ '2016-11-01 12:30:09', [ '2016-11-01 12:30:09', nil ] ], - [ '2016-11-01 12:30:09-25', [ '2016-11-01 12:30:09-25', nil ] ], - [ '2016-11-01 12:30:09-25:00', [ '2016-11-01 12:30:09-25:00', nil ] ], - - [ '11/09/2002 2utopiaNada?3Nada', - [ '11/09/2002 2utopiaNada?3Nada', nil ] ], - - [ '2012-10-28 03:00:00 EET', - [ '2012-10-28 03:00:00', 'EET' ] ], - #[ '2012-10-28 03:00:00 EEST', - # [ '2012-10-28 03:00:00', 'EEST' ] ], - [ '2012-10-28 03:00:00 Europe/Tallinn', - [ '2012-10-28 03:00:00', 'Europe/Tallinn' ] ], - - ].each { |str0, (str1, zone)| - - test "returns #{[ str1, zone ].inspect} for #{str0.inspect}" do - - s1, z = EtOrbi.extract_zone(str0) - - assert [ s1, z ], [ str1, zone ] - end - } - end - - group '.parse' do - - test 'parses a time string without a timezone' do - - ot = in_zone('Europe/Moscow') { EtOrbi.parse('2015/03/08 01:59:59') } - - t = ot - u = ot.utc - - assert t.to_i, 1425769199 - assert u.to_i, 1425769199 - - assert( - t.strftime('%Y/%m/%d %H:%M:%S %Z %z') + " #{t.isdst}", - '2015/03/08 01:59:59 MSK +0300 false') - - assert u.to_debug_s, 't 2015-03-07 22:59:59 +00:00 dst:false' - end - - test 'parses a time string with a full name timezone' do - - ot = EtOrbi.parse('2015/03/08 01:59:59 America/Los_Angeles') - - t = ot - u = ot.utc - - assert t.to_i, 1425808799 - assert u.to_i, 1425808799 - - assert t.to_debug_s, 'ot 2015-03-08 01:59:59 -08:00 dst:false' - assert u.to_debug_s, 't 2015-03-08 09:59:59 +00:00 dst:false' - end - - test 'parses a time string with a delta timezone' do - - ot = in_zone('Europe/Berlin') { EtOrbi.parse('2015-12-13 12:30 -0200') } - - t = ot - u = ot.utc - - assert t.to_i, 1450017000 - assert u.to_i, 1450017000 - - assert t.to_debug_s, 'ot 2015-12-13 12:30:00 -02:00 dst:false' - assert u.to_debug_s, 't 2015-12-13 14:30:00 +00:00 dst:false' - end - - test 'parses a time string with a delta (:) timezone' do - - ot = in_zone('Europe/Berlin') { EtOrbi.parse('2015-12-13 12:30 -02:00') } - - t = ot - u = ot.utc - - assert t.to_i, 1450017000 - assert u.to_i, 1450017000 - - assert t.to_debug_s, 'ot 2015-12-13 12:30:00 -02:00 dst:false' - assert u.to_debug_s, 't 2015-12-13 14:30:00 +00:00 dst:false' - end - - test 'takes the local TZ when test does not know the timezone' do - - in_zone 'Europe/Moscow' do - - ot = EtOrbi.parse('2015/03/08 01:59:59 Nada/Nada') - - assert_include ot.zone.name, %w[ Europe/Moscow Europe/Kirov ] - end - end - - test 'parses even when the tz is out of place' do - - assert( - EtOrbi.parse('Sun Nov 18 16:01:00 Asia/Singapore 2012') - .to_debug_s, - "ot 2012-11-18 16:01:00 +08:00 dst:false") - end - - test 'fails on invalid strings' do - - assert_error( - lambda { EtOrbi.parse('xxx') }, - ArgumentError, 'No time information in "xxx"') - end - - test 'parses in the Rails-provided Time.zone (UTC)' do - - Time.active_support_zone = 'UTC' - - t = EtOrbi.parse('2019-01-01 12:10') - - assert t.class, EtOrbi::EoTime - assert t.zone, ::TZInfo::Timezone.get('UTC') - assert t.to_s, '2019-01-01 12:10:00 Z' - assert t.to_zs, '2019-01-01 12:10:00 UTC' - end - - test 'parses in the Rails-provided Time.zone (Asia/Shanghai)' do - - Time.active_support_zone = 'Asia/Shanghai' - - t = EtOrbi.parse('2019-01-01 12:10') - - assert t.class, EtOrbi::EoTime - assert t.zone, ::TZInfo::Timezone.get('Asia/Shanghai') - assert t.to_s, '2019-01-01 12:10:00 +0800' - assert t.to_zs, '2019-01-01 12:10:00 Asia/Shanghai' - end - -# # https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations -# # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones -# -# context 'TZ abbreviations' do -# -# { -# -# '2021-01-01 7AM America/Los_Angeles' => '2021-01-01 07:00:00 -0800', -# '2021-07-01 7AM America/Los_Angeles' => '2021-07-01 07:00:00 -0700', -# '2021-01-01 7AM PST' => '2021-01-01 07:00:00 -0800', -# '2021-07-01 7AM PDT' => '2021-07-01 07:00:00 -0700', -# '2021-01-01 7AM -07:00' => '2021-01-01 07:00:00 -0700', -# '2021-07-01 7AM -07:00' => '2021-07-01 07:00:00 -0700', -# '2021-01-01 7AM GMT-7' => '2021-01-01 07:00:00 -0700', -# '2021-07-01 7AM GMT-7' => '2021-07-01 07:00:00 -0700', -# '2021-01-01 7AM UTC-7' => '2021-01-01 07:00:00 -0700', -# '2021-07-01 7AM UTC-7' => '2021-07-01 07:00:00 -0700', -# '2021-01-01 7AM Etc/GMT-7' => '2021-01-01 07:00:00 -0700', -# '2021-07-01 7AM Etc/GMT-7' => '2021-07-01 07:00:00 -0700', -# -# '2021-01-01 7AM ACDT' => '2021-01-01 07:00:00 +0930', -# '2021-07-01 7AM ACST' => '2021-01-01 07:00:00 +1030', -# -# }.each do |k, v| -# -# test "parses #{k} to #{v}" do -# -# ot = EtOrbi.parse(k) -#t = Time.parse(k) -#p [ k ] -#p [ Date._parse(k) ] -#p [ t.to_s, t.zone, :t ] -#p [ ot.to_s, ot.zone, :ot ] -#p [ v ] -# #p [ k, v, ot ] -# -# assert ot.to_s, v) -# end -# end -# end - - group 'when Chronic is defined' do - - before do - require_chronic - end - after do - unrequire_chronic - end - - test 'leverages it' do - - t = EtOrbi.parse('tomorrow at 1pm') - t1 = Time.now + 24 * 3600 - - assert t.class, EtOrbi::EoTime - assert t.strftime('%F %T'), t1.strftime('%F 13:00:00') - end - - test 'leverages test in a specified time zone' do - - t = EtOrbi.parse('tomorrow at 22:00 America/New_York') - t1 = Time.now + 24 * 3600 - - assert t.class, EtOrbi::EoTime - assert t.to_zs, t1.strftime('%F 22:00:00 America/New_York') - end - - test 'picks the Rails Time.zone (UTC) if available' do - - Time.active_support_zone = 'UTC' - - t = EtOrbi.parse('tomorrow') - t1 = Time.now + 24 * 3600 - - assert t.class, EtOrbi::EoTime - assert t.zone.name, 'UTC' - assert t.strftime('%Y-%m-%d'), t1.strftime('%Y-%m-%d') - assert t.strftime('%H:%M:%S'), '12:00:00' - end - - test 'picks the Rails Time.zone (Asia/Shanghai) if available' do - - Time.active_support_zone = 'Asia/Shanghai' - - t = EtOrbi.parse('tomorrow') - t1 = Time.now + 24 * 3600 - - assert t.class, EtOrbi::EoTime - assert t.zone.name, 'Asia/Shanghai' - assert t.strftime('%Y-%m-%d'), t1.strftime('%Y-%m-%d') - assert t.strftime('%H:%M:%S'), '12:00:00' - end - - group 'when parse(x, enable_chronic: false)' do - - test 'does not pre-parse with Chronic' do - - Time.active_support_zone = 'America/Chicago' - - assert( # works - EtOrbi.parse('tomorrow').class, - EtOrbi::EoTime) - - assert_error( # doesn't work - lambda { EtOrbi.parse('tomorrow', enable_chronic: false) }, - ArgumentError, 'No time information in "tomorrow"') - end - end - - group 'and Et-Orbi.chronic_enabled? is false' do - - before do - EtOrbi.chronic_enabled = false - end - after do - EtOrbi.chronic_enabled = true - end - - test 'does not pre-parse with Chronic' do - - assert_error( - lambda { EtOrbi.parse('tomorrow') }, - ArgumentError, 'No time information in "tomorrow"') - end - - test 'pre-parses with Chronic when enable_chronic: true' do - - assert_not_error { EtOrbi.parse('tomorrow', enable_chronic: true) } - end - end - - test 'filters options given to Chronic' do - - Time.active_support_zone = 'UTC' - - #assert ::Khronic) - # .to receive(:parse) - # .with('tomorrow', { context: :future }) - # .and_call_original - - t = EtOrbi.parse( - 'tomorrow', zone: ::TZInfo::Timezone.get('UTC'), context: :future) - t1 = Time.now + 24 * 3600 - - assert t.class, EtOrbi::EoTime - assert t.zone.name, 'UTC' - assert t.strftime('%Y-%m-%d'), t1.strftime('%Y-%m-%d') - assert t.strftime('%H:%M:%S'), '12:00:00' - end - end - end - - group '.get_tzone' do - - { - - 'GB' => 'GB', - 'UTC' => 'UTC', - 'GMT' => 'GMT', - 'Zulu' => 'Zulu', - 'Japan' => 'Japan', - 'Turkey' => 'Turkey', - 'Asia/Tokyo' => 'Asia/Tokyo', - 'Europe/Paris' => 'Europe/Paris', - 'Europe/Zurich' => 'Europe/Zurich', - 'W-SU' => 'W-SU', - - 'Z' => 'UTC', - - '+09:00' => '+09:00', - '-01:30' => '-01:30', - - '_AT-3:30Asia/Tehran' => 'Asia/Tehran', - '_AT-4Asia/Tbilisi' => 'Asia/Tbilisi', - - '+08:00' => '+08:00', - '+0800' => '+0800', # no normalization to "+08:00" - - '-01' => '-01', - - 3600 => '+01:00', - - 'Tokyo Standard Time' => 'Asia/Tokyo', - 'Coordinated Universal Time' => 'UTC', - - 'Eastern Standard Time' => 'America/New_York', - 'Eastern Daylight Time' => 'America/New_York', - - 'CST+0800' => 'Asia/Chongqing', - 'CST+08:00' => 'Asia/Chongqing', - 'CST+08' => 'Asia/Chongqing', - 'CST+8' => 'Asia/Chongqing', - 'EDT-0400' => 'America/Detroit', - - 'WET-1WEST' => 'WET', - 'CET-2CEST' => 'CET', - - }.each do |a, b| - - test "returns #{b.inspect} for #{a.inspect}" do - - z = EtOrbi.get_tzone(a) - - assert_not_nil z - assert z.name, b - end - end - -# test 'returns a timezone for well-known abbreviations' do -# -# assert gtz('JST'), 'Japan' -# assert gtz('PST'), 'America/Dawson' -# assert gtz('CEST'), 'Africa/Ceuta' -# end - - [ - 'Asia/Paris', 'Nada/Nada', '7', '06', 'sun#3', 'Mazda Zoom Zoom Stadium' - ].each do |s| - - test "returns nil for #{s.inspect}" do - - assert_nil EtOrbi.get_tzone(s) - end - end - - # rufus-scheduler gh-222 - # - test "falls back to ENV['TZ'] if test doesn't know Time.now.zone" do - - begin - - current = EtOrbi.get_tzone(:local) - - Time._zone = '中国标准时间' - - #assert_nil EtOrbi.get_tzone(:current) - #assert EtOrbi.get_tzone(:current), EtOrbi.get_tzone(Time.now.zone) - # - # gh-240 introduces a way of finding the timezone by asking directly - # to the system, so those do return a timezone... - - in_zone 'Asia/Shanghai' do - - assert_include( - EtOrbi.get_tzone(:local), - [ EtOrbi.get_tzone('Asia/Shanghai'), - EtOrbi.get_tzone('Asia/Chongqing') ]) - end - - ensure - - Time._zone = nil - end - - assert EtOrbi.get_tzone(:local), current - end - - { # for rufus-scheduler gh-228 - - 'Asia/Tokyo' => %w[ - Asia/Tokyo ], - 'Asia/Shanghai' => %w[ - Asia/Shanghai Asia/Chongqing ], - 'Europe/Zurich' => %w[ - Europe/Zurich Africa/Ceuta windows:CET ], - 'Europe/London' => %w[ - Europe/London Europe/Belfast windows:GMT windows:GMT-0 ], - - }.each do |zone, targets| - - test "returns the current timezone for :current in #{zone}" do - - in_zone(zone) do - - assert_include EtOrbi.get_tzone(:local), select_zones(targets) - end - end - end - - test "doesn't mind being given a TZInfo::Timezone" do - - tz = ::TZInfo::Timezone.get('Zulu') - class << tz - def <=>(tz) - #return nil unless tz.is_a?(Timezone) - identifier <=> tz.identifier - end - end - # simulate tzinfo 0.3.53 issue - - assert EtOrbi.get_tzone(tz), ::TZInfo::Timezone.get('Zulu') - end - end - - group '.determine_local_tzone' do - - test 'favours the local timezone' do - - in_zone(:no_env_tz) do - - Time._zone = 'Cape Verde Standard Time' - EtOrbi._os_zone = '' # force #os_tz to return nil - - assert EtOrbi.determine_local_tzone.name, 'Atlantic/Cape_Verde' - end - end - - test 'favours the local timezone' do - - in_zone('Europe/Berlin') do - - assert_include( - EtOrbi.determine_local_tzone, - select_zones(%w[ Europe/Berlin Africa/Ceuta windows:CET ])) - end - end - - test 'favours the local timezone (Mitteleuropaeische Sommerzeit)' do - - in_zone(:no_env_tz) do - - Time._zone = "Mitteleurop\xE4ische Sommerzeit" - EtOrbi._os_zone = '' # force #os_tz to return nil - - assert_not_error { EtOrbi.determine_local_tzone } - end - end - - test 'returns the local timezone' do - - in_zone('America/Jamaica') do - - assert_include( - EtOrbi.determine_local_tzone.name, - [ 'America/Jamaica', 'America/Atikokan', 'EST' ]) - end - end - - test 'returns the Rails-provided Time.zone.tzinfo if available' do - - # http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html - - Time.active_support_zone = 'Europe/Vilnius' - - in_zone(:no_env_tz) do - assert EtOrbi.determine_local_tzone.class, ::TZInfo::DataTimezone - assert EtOrbi.determine_local_tzone.name, 'Europe/Vilnius' - end - end - - test "gives precedence to ENV['TZ'] over Rails Time.zone.tzinfo" do - - # http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html - - Time.active_support_zone = 'Europe/Vilnius' - - in_zone('Asia/Tehran') do - - assert EtOrbi.determine_local_tzone.class, ::TZInfo::DataTimezone - assert EtOrbi.determine_local_tzone.name, 'Asia/Tehran' - end - end - end - - group '.zone' do - - test 'is an alias to .determine_local_tzone' do - - in_zone(:no_env_tz) do - - Time._zone = 'Cape Verde Standard Time' - EtOrbi._os_zone = '' # force #os_tz to return nil - - assert EtOrbi.zone.name, 'Atlantic/Cape_Verde' - end - end - end - - group '.now' do - - test 'returns a current, local EoTime instance' do - - in_zone 'Asia/Shanghai' do - - t = EtOrbi.now - n = Time.now - - assert t.seconds > (n - 1).to_f - assert t.seconds < (n + 1).to_f - - assert_include t.zone.name, [ 'Asia/Shanghai', 'Asia/Chongqing' ] - end - end - end - - group '.make_time' do - - test 'returns an EoTime instance as is' do - - t0 = EtOrbi.parse('2017-03-21 12:00:34 Asia/Ulan_Bator') - t1 = EtOrbi.make_time(t0) - - assert t1.class, ::EtOrbi::EoTime - assert t1, t0 - assert t1.object_id, t0.object_id - end - - test 'returns an EoTime instance as is' do - - t0 = EtOrbi.parse('2017-03-21 12:00:34 Asia/Ulan_Bator') - t1 = EtOrbi.make_time(t0, t0.zone) - - assert t1.class, ::EtOrbi::EoTime - assert t1, t0 - assert t1.object_id, t0.object_id - end - - [ - [ 'an EoTime instance', - nil, - lambda { EtOrbi.parse('2017-03-21 12:00:34 Asia/Ulan_Bator') }, - 'ot 2017-03-21 12:00:34 +08:00 dst:false' ], - - [ 'a local time', - 'Asia/Tbilisi', - Time.parse('2016-11-01 12:30:09 +04:00'), # use custom tz :-( - 'ot 2016-11-01 12:30:09 +04:00 dst:false' ], - - [ 'an UTC time', - nil, - Time.utc(2016, 11, 01, 12, 30, 9), - 'ot 2016-11-01 12:30:09 +00:00 dst:false' ], - - [ 'a Date instance', - nil, - Date.new(2016, 11, 01), - lambda { - EtOrbi::EoTime.new( - Time.local(2016, 11, 01).to_f, nil - ).to_debug_s } ], - - [ 'a String', - nil, - '2016-11-01 12:30:09', - lambda { - EtOrbi::EoTime.new( - Time.local(2016, 11, 01, 12, 30, 9).to_f, nil) } ], - - [ 'a String', - 'America/Chicago', - '2016-08-01 12:30:10', - 'ot 2016-08-01 12:30:10 -05:00 dst:true' ], - #[ 'a String', - # 'America/Chicago', - # '2016-11-01 12:30:09', - # 'ot 2016-11-01 12:30:09 -05:00 dst:true' ], # fails on Appveyor :-( - [ 'a String', - 'America/Chicago', - '2016-11-06 12:30:08', - 'ot 2016-11-06 12:30:08 -06:00 dst:false' ], - - [ 'a String', - 'Europe/Paris', - '2016-1-1 12:30:07', - 'ot 2016-01-01 12:30:07 +01:00 dst:false' ], - [ 'a String', - 'Europe/Paris', - '2016-8-1 12:30:07', - 'ot 2016-08-01 12:30:07 +02:00 dst:true' ], - - [ 'a Zulu String', - nil, - '2016-11-01 12:30:09Z', - EtOrbi::EoTime.new(Time.utc(2016, 11, 01, 12, 30, 9).to_f, 'Zulu') ], - - [ 'a ss+01:00 String', - nil, - '2016-11-01 12:30:09+01:00', - 'ot 2016-11-01 12:30:09 +01:00 dst:false' ], - - [ 'a ss-01 String', - nil, - '2016-11-01 12:30:09-01', - 'ot 2016-11-01 12:30:09 -01:00 dst:false' ], - - [ 'a String with an explicit time zone', - nil, - '2016-05-01 12:30:09 America/New_York', - 'ot 2016-05-01 12:30:09 -04:00 dst:true' ], - - [ 'a Numeric', - nil, - 3600, - lambda { [ Time.now + 3600 - 0.35, Time.now + 3600 + 0.35 ] } ], - - [ 'an array [ y, m, d, ... ]', - 'Europe/Moscow', - [ [ 2017, 2, 28 ] ], - 'ot 2017-02-28 00:00:00 +03:00 dst:false' ], - - [ 'an array of args (y, m, d, ...)', - 'Europe/Moscow', - [ 2017, 1, 31, 10 ], - 'ot 2017-01-31 10:00:00 +03:00 dst:false' ], - - [ 'an array of args and a zone as last arg', - nil, - [ 2017, 1, 31, 12, 'Europe/Moscow' ], - 'ot 2017-01-31 12:00:00 +03:00 dst:false' ], - - [ 'a string and a zone as last arg', - nil, - [ '2016-05-01 12:30:09', 'America/Chicago' ], - 'ot 2016-05-01 12:30:09 -05:00 dst:true' ], - - [ 'a string and an overriding zone as last arg', - nil, - [ '2016-05-01 11:30:09 America/New_York', 'America/Chicago' ], - 'ot 2016-05-01 11:30:09 -05:00 dst:true' ], - - [ 'an array of args and a TZInfo zone as last arg', - nil, - [ 2017, 1, 31, EtOrbi.get_tzone('Europe/Oslo') ], - 'ot 2017-01-31 00:00:00 +01:00 dst:false' ], - - [ 'a string and a TZInfo zone as last arg', - nil, - [ '2017-01-31 12:30', EtOrbi.get_tzone('Europe/Oslo') ], - 'ot 2017-01-31 12:30:00 +01:00 dst:false' ], - - [ 'a string', - 'America/Chicago', - lambda { [ Time.parse('2021-03-11') ] }, - windows? ? 'CST6CDT' : 'America - Chicago', - lambda { |t| t.zone.to_s } ], - - [ 'a string', - 'America/New_York', - lambda { [ Time.parse('2021-03-11') ] }, - windows? ? %w[ EST5EDT EST ] : 'America - New York', - #windows? ? 'EST' : 'America - New York', - lambda { |t| t.zone.to_s } ], - - ].each do |name, zone, args, asserted, transformer| - - title = "turns #{name} into an EoTime instance" - title += " in #{zone}" if zone - - test(title) do - - eot, exp = - in_zone(zone) do - - as = args.is_a?(Proc) ? args.call : args - - t = as.is_a?(Array) ? - EtOrbi.make_time(*as) : - EtOrbi.make_time(as) - x = asserted.is_a?(Proc) ? - asserted.call : - asserted - - t = transformer.is_a?(Proc) ? transformer[t] : t - - [ t, x ] - end -#p eot.to_debug_s -#p eot.utc_offset -#p eot.send(:to_time).to_s -#p eot.send(:to_time).utc_offset - - case exp - when String - assert eot.respond_to?(:to_debug_s) ? eot.to_debug_s : eot, exp - when Array - if exp.collect(&:class).uniq == [ String ] - assert exp.include?(eot) - else - assert eot > exp[0] - assert eot < exp[1] - end - else - assert eot, exp - end - end - end - -# test 'accepts a duration String'# do -## -## expect( -## EtOrbi.make_time('1h') -## ).to be_between( -## Time.now + 3600 - 1, Time.now + 3600 + 1 -## ) -## end -# # -# # String parsing is fugit's job. Et-orbi should be a dependency of -# # fugit, not the other way around. When fugit is present, this -# # spec should succeed, else test should not. - - test 'accepts a Rails TimeWithZone' do - - # http://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html - - n = Time.now - z = EtOrbi.get_tzone('Pacific/Easter') - - #t = ActiveSupport::TimeWithZone.new(n, z) - # - t = n - t.instance_eval { @z = z } - class << t; def time_zone; @z; end; end - # - # fake an ActiveSupport::TimeWithZone instance with a #time_zone - - eot = EtOrbi.make_time(t) - - assert eot.class, EtOrbi::EoTime - assert eot.seconds, t.to_f - assert eot.zone, t.time_zone - end - - test 'rejects a Time in a non-local ambiguous timezone' do - - t = Time.local(2016, 11, 01, 12, 30, 9) - class << t; def zone; 'ECT'; end; end - - in_zone 'Asia/Tbilisi' do - - assert_error( - lambda { EtOrbi.make_time(t) }, - ArgumentError, /\ACannot determine timezone from "ECT"/) - end - end - - test 'rejects unparsable input' do - - assert_error( - lambda { EtOrbi.make_time('xxx') }, - ArgumentError, 'No time information in "xxx"') - # - # straight out of Time.parse() - - assert_error( - lambda { EtOrbi.make_time(Object.new) }, - ArgumentError, /\ACannot turn /) - end - end - - group '.windows_zone_name' do - - { [ 'Asia/Tokyo', '2018-05-23' ] => 'JST-9', - [ 'Asia/Kolkata', '2018-07-01' ] => 'IST-5:30', - [ 'Asia/Tehran', '2019-01-09' ] => '_AT-3:30Asia/Tehran', #'IRT-3:30', - [ 'Asia/Tehran', '2019-07-09' ] => '_AT-4:30Asia/Tehran', #'IRT-4:30', - [ 'Asia/Tbilisi', '2019-01-09' ] => '_AT-4:00Asia/Tbilisi', - [ 'Asia/Samarkand', '2017-11-01' ] => '_AS-5:00Asia/Samarkand', - [ 'Europe/Berlin', '2018-01-01' ] => 'CET-1CEST', - [ 'Europe/Berlin', '2018-07-01' ] => 'CET-2CEST', - [ 'America/New_York', '2018-01-01' ] => 'EST5EDT', - [ 'America/New_York', '2018-07-01' ] => 'EST4EDT', - [ 'America/Los_Angeles', '2017-10-30' ] => 'PST7PDT', - [ 'America/Los_Angeles', '2019-01-01' ] => 'PST8PDT', - - [ 'Europe/Tallinn', '2012-07-28' ] => 'EET-3EEST', - [ 'Europe/Tallinn', '2012-10-28' ] => 'EET-3EEST', - [ 'Europe/Tallinn', '2012-12-28' ] => 'EET-2EEST', - - }.each do |(zone, time), v| - - test "returns #{v.inspect} for #{zone.inspect} at #{time}" do - - assert EtOrbi.windows_zone_name(zone, Time.parse(time)), v - end - end - end - - group '.zone_abbreviation' do - - { [ 'Europe/Tallinn', '2012-07-28' ] => 'EEST', - [ 'Europe/Tallinn', '2012-10-28' ] => 'EEST', - [ 'Europe/Tallinn', '2012-12-28' ] => 'EET', - - [ 'Europe/Tallinn', '2012-03-25 02:00' ] => 'EET', - [ 'Europe/Tallinn', '2012-03-25 03:00' ] => 'EEST', - [ 'Europe/Tallinn', '2012-03-25 04:00' ] => 'EEST', - [ 'Europe/Tallinn', '2012-03-25 05:00' ] => 'EEST', - - [ 'Europe/Tallinn', '2012-10-28 00:00' ] => 'EEST', - [ 'Europe/Tallinn', '2012-10-28 02:59' ] => 'EEST', - [ 'Europe/Tallinn', '2012-10-28 03:00' ] => 'EET', - [ 'Europe/Tallinn', '2012-10-28 03:30' ] => 'EET', - [ 'Europe/Tallinn', '2012-10-28 04:30' ] => 'EET', - - # in case of ambiguity, .zone_abbreviation goes 2 hours forward... - - }.each do |(zone, time), v| - - test "returns #{v.inspect} for #{zone.inspect} at #{time}" do - - assert EtOrbi.zone_abbreviation(zone, Time.parse(time)), v - end - end - end - - group '.tweak_zone_name' do - - { 'EST5EDT' => 'EST5EDT', - 'UTC+12' => 'Etc/GMT-12', - 'Korea Standard Time' => 'Asia/Seoul', - - }.each do |n0, n1| - - test "turns #{n0.inspect} into #{n1.inspect}" do - - assert EtOrbi.tweak_zone_name(n0), n1 - end - end - end - - group '.chronic_enabled?' do - - test 'returns true by default' do - - assert EtOrbi.chronic_enabled? - end - end - - group '.chronic_enabled=' do - - after do - EtOrbi.chronic_enabled = true - end - - test 'lets disable chronic parsing' do - - EtOrbi.chronic_enabled = false - - assert_false EtOrbi.chronic_enabled? - end - end - -# group '.abbreviate_zone_name' do -# -# { -# -# 'EST5EDT' => 'EST5EDT', -# 'BST' => %w[ GB-Eire GB ], -# 'UTC+12' => nil, -# -# }.each do |n0, n1| -# -# n1s = n1.inspect -# n1s = n1.collect(&:inspect).join(' or ') if n1.is_a?(Array) -# -# test "turns #{n0.inspect} into #{n1s}" do -# -# if n1.is_a?(Array) -# assert n1).to include(EtOrbi.abbreviate_zone_name(n0)) -# else -# assert EtOrbi.abbreviate_zone_name(n0), n1) -# end -# end -# end -# end -end - +IyBlbmNvZGluZzogVVRGLTgKCiMKIyBUZXN0aW5nIEV0T3JiaQojCiMgV2VkIE1hciAxMSAyMToxNzozNiBKU1QgMjAxNSwgcXVhdHJlIGFucy4uLiAocnVmdXMtc2NoZWR1bGVyKQojIFN1biBNYXIgMTkgMDU6MTY6MjggSlNUIDIwMTcKIyBGcmkgTWFyIDI0IDA0OjU1OjI1IEpTVCAyMDE3IOWck+OBleOCk+OBruWutgojCgoKZ3JvdXAgRXRPcmJpIGRvCgogIGJlZm9yZSBkbwoKICAgIEVOVlsnVFonXSA9IEVOVlsnX1RaJ10KICAgIFRpbWUuX3pvbmUgPSBuaWwKICAgIEV0T3JiaS5fb3Nfem9uZSA9IG5pbAogICAgVGltZS5hY3RpdmVfc3VwcG9ydF96b25lID0gbmlsCiAgZW5kCgogIGdyb3VwICcubGlzdF9pc284NjAxX3pvbmVzJyBkbwoKICAgIFsKCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDktMDEnLCAld1sgLTAxIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0wMTowMCcsICV3WyAtMDE6MDAgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5IC0wMScsICV3WyAtMDEgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5IC0wMTowMCcsICV3WyAtMDE6MDAgXSBdLAoKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0wMTozMCcsICV3WyAtMDE6MzAgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5IC0wMTozMCcsICV3WyAtMDE6MzAgXSBdLAoKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsIFtdIF0sCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDktMjUnLCBbXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTI1OjAwJywgW10gXSwKCiAgICBdLmVhY2ggZG8gfHN0cmluZywgem9uZXN8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7em9uZXMuaW5zcGVjdH0gZm9yICN7c3RyaW5nLmluc3BlY3R9IiBkbwoKICAgICAgICBhc3NlcnQgRXRPcmJpLmxpc3RfaXNvODYwMV96b25lcyhzdHJpbmcpLCB6b25lcwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKIyAgZ3JvdXAgJy5saXN0X29sc29uX3pvbmVzJyBkbwojCiMgICAgWwojCiMgICAgICBbICcxMS8wOS8yMDAyIEFtZXJpY2EvTmV3X1lvcmsnLAojICAgICAgICAld1sgQW1lcmljYS9OZXdfWW9yayBdIF0sCiMgICAgICBbICcxMS8wOS8yMDAyIEFtZXJpY2EvTmV3X1lvcmsgQXNpYS9TaGFuZ2hhaScsCiMgICAgICAgICV3WyBBbWVyaWNhL05ld19Zb3JrIEFzaWEvU2hhbmdoYWkgXSBdLAojICAgICAgWyAnQW1lcmljYS9OZXdfWW9yayBBc2lhL1NoYW5naGFpJywKIyAgICAgICAgJXdbIEFtZXJpY2EvTmV3X1lvcmsgQXNpYS9TaGFuZ2hhaSBdIF0sCiMgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4IEV0Yy9HTVQtMTEnLAojICAgICAgICAld1sgRXRjL0dNVC0xMSBdIF0sCiMKIyAgICAgIFsgJzIwMTgtMDktMDQgMDc6NTQ6NTggVVRDKzExJywKIyAgICAgICAgJXdbIFVUQysxMSBdIF0sCiMgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4IFVUQysxMSBFdGMvR01ULTExJywKIyAgICAgICAgJXdbIEV0Yy9HTVQtMTEgVVRDKzExIF0gXSwKIyAgICAgICAgICAjCiMgICAgICAgICAgIyBodHRwczovL2dpdGh1Yi5jb20vZmxvcmFpc29uL2Z1Z2l0L2lzc3Vlcy85CiMKIyAgICAgIFsgJzExLzA5LzIwMDIgMnV0b3BpYU5hZGE/M05hZGEnLAojICAgICAgICBbXSBdCiMKIyAgICBdLmVhY2ggZG8gfHN0cmluZywgem9uZXN8CiMKIyAgICAgIHRlc3QgInJldHVybnMgI3t6b25lcy5pbnNwZWN0fSBmb3IgI3tzdHJpbmcuaW5zcGVjdH0iIGRvCiMKIyAgICAgICAgYXNzZXJ0IEV0T3JiaS5saXN0X29sc29uX3pvbmVzKHN0cmluZyksIHpvbmVzKQojICAgICAgZW5kCiMgICAgZW5kCiMgIGVuZAoKICBncm91cCAnLmV4dHJhY3Rfem9uZScgZG8KCiAgICBbCgogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTAxJywgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsICctMDEnIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0wMTowMCcsIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDknLCAnLTAxOjAwJyBdIF0sCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDkgLTAxJywgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsICctMDEnIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOSAtMDE6MDAnLCBbICcyMDE2LTExLTAxIDEyOjMwOjA5JywgJy0wMTowMCcgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTAxOjMwJywgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsICctMDE6MzAnIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOSAtMDE6MzAnLCBbICcyMDE2LTExLTAxIDEyOjMwOjA5JywgJy0wMTozMCcgXSBdLAoKICAgICAgWyAnMTEvMDkvMjAwMiBBbWVyaWNhL05ld19Zb3JrJywKICAgICAgICBbICcxMS8wOS8yMDAyJywgJ0FtZXJpY2EvTmV3X1lvcmsnIF0gXSwKICAgICAgWyAnMTEvMDkvMjAwMiBBbWVyaWNhL05ld19Zb3JrIEFzaWEvU2hhbmdoYWknLAogICAgICAgIFsgJzExLzA5LzIwMDInLCAnQXNpYS9TaGFuZ2hhaScgXSBdLAogICAgICBbICdBbWVyaWNhL05ld19Zb3JrIEFzaWEvU2hhbmdoYWknLAogICAgICAgIFsgJycsICdBc2lhL1NoYW5naGFpJyBdIF0sCiAgICAgIFsgJzIwMTgtMDktMDQgMDc6NTQ6NTggRXRjL0dNVC0xMScsCiAgICAgICAgWyAnMjAxOC0wOS0wNCAwNzo1NDo1OCcsICdFdGMvR01ULTExJyBdIF0sCiAgICAgIFsgJzIwMTgtMDktMDQgMDc6NTQ6NTggVVRDKzExJywKICAgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4JywgJ1VUQysxMScgXSBdLAogICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4IFVUQysxMSBFdGMvR01ULTExJywKICAgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4JywgJ0V0Yy9HTVQtMTEnIF0gXSwKCiAgICAgIFsgJ1N1biBOb3YgMTggMTY6MDE6MDAgQXNpYS9TaW5nYXBvcmUgMjAxMicsCiAgICAgICAgWyAnU3VuIE5vdiAxOCAxNjowMTowMCAgMjAxMicsICdBc2lhL1NpbmdhcG9yZScgXSBdLAoKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDknLCBuaWwgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTI1JywgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0yNScsIG5pbCBdIF0sCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDktMjU6MDAnLCBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTI1OjAwJywgbmlsIF0gXSwKCiAgICAgIFsgJzExLzA5LzIwMDIgMnV0b3BpYU5hZGE/M05hZGEnLAogICAgICAgIFsgJzExLzA5LzIwMDIgMnV0b3BpYU5hZGE/M05hZGEnLCBuaWwgXSBdLAoKICAgICAgWyAnMjAxMi0xMC0yOCAwMzowMDowMCBFRVQnLAogICAgICAgIFsgJzIwMTItMTAtMjggMDM6MDA6MDAnLCAnRUVUJyBdIF0sCiAgICAgICNbICcyMDEyLTEwLTI4IDAzOjAwOjAwIEVFU1QnLAogICAgICAjICBbICcyMDEyLTEwLTI4IDAzOjAwOjAwJywgJ0VFU1QnIF0gXSwKICAgICAgWyAnMjAxMi0xMC0yOCAwMzowMDowMCBFdXJvcGUvVGFsbGlubicsCiAgICAgICAgWyAnMjAxMi0xMC0yOCAwMzowMDowMCcsICdFdXJvcGUvVGFsbGlubicgXSBdLAoKICAgIF0uZWFjaCB7IHxzdHIwLCAoc3RyMSwgem9uZSl8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7WyBzdHIxLCB6b25lIF0uaW5zcGVjdH0gZm9yICN7c3RyMC5pbnNwZWN0fSIgZG8KCiAgICAgICAgczEsIHogPSBFdE9yYmkuZXh0cmFjdF96b25lKHN0cjApCgogICAgICAgIGFzc2VydCBbIHMxLCB6IF0sIFsgc3RyMSwgem9uZSBdCiAgICAgIGVuZAogICAgfQogIGVuZAoKICBncm91cCAnLnBhcnNlJyBkbwoKICAgIHRlc3QgJ3BhcnNlcyBhIHRpbWUgc3RyaW5nIHdpdGhvdXQgYSB0aW1lem9uZScgZG8KCiAgICAgIG90ID0gaW5fem9uZSgnRXVyb3BlL01vc2NvdycpIHsgRXRPcmJpLnBhcnNlKCcyMDE1LzAzLzA4IDAxOjU5OjU5JykgfQoKICAgICAgdCA9IG90CiAgICAgIHUgPSBvdC51dGMKCiAgICAgIGFzc2VydCB0LnRvX2ksIDE0MjU3NjkxOTkKICAgICAgYXNzZXJ0IHUudG9faSwgMTQyNTc2OTE5OQoKICAgICAgYXNzZXJ0KAogICAgICAgIHQuc3RyZnRpbWUoJyVZLyVtLyVkICVIOiVNOiVTICVaICV6JykgKyAiICN7dC5pc2RzdH0iLAogICAgICAgICcyMDE1LzAzLzA4IDAxOjU5OjU5IE1TSyArMDMwMCBmYWxzZScpCgogICAgICBhc3NlcnQgdS50b19kZWJ1Z19zLCAndCAyMDE1LTAzLTA3IDIyOjU5OjU5ICswMDowMCBkc3Q6ZmFsc2UnCiAgICBlbmQKCiAgICB0ZXN0ICdwYXJzZXMgYSB0aW1lIHN0cmluZyB3aXRoIGEgZnVsbCBuYW1lIHRpbWV6b25lJyBkbwoKICAgICAgb3QgPSBFdE9yYmkucGFyc2UoJzIwMTUvMDMvMDggMDE6NTk6NTkgQW1lcmljYS9Mb3NfQW5nZWxlcycpCgogICAgICB0ID0gb3QKICAgICAgdSA9IG90LnV0YwoKICAgICAgYXNzZXJ0IHQudG9faSwgMTQyNTgwODc5OQogICAgICBhc3NlcnQgdS50b19pLCAxNDI1ODA4Nzk5CgogICAgICBhc3NlcnQgdC50b19kZWJ1Z19zLCAnb3QgMjAxNS0wMy0wOCAwMTo1OTo1OSAtMDg6MDAgZHN0OmZhbHNlJwogICAgICBhc3NlcnQgdS50b19kZWJ1Z19zLCAndCAyMDE1LTAzLTA4IDA5OjU5OjU5ICswMDowMCBkc3Q6ZmFsc2UnCiAgICBlbmQKCiAgICB0ZXN0ICdwYXJzZXMgYSB0aW1lIHN0cmluZyB3aXRoIGEgZGVsdGEgdGltZXpvbmUnIGRvCgogICAgICBvdCA9IGluX3pvbmUoJ0V1cm9wZS9CZXJsaW4nKSB7IEV0T3JiaS5wYXJzZSgnMjAxNS0xMi0xMyAxMjozMCAtMDIwMCcpIH0KCiAgICAgIHQgPSBvdAogICAgICB1ID0gb3QudXRjCgogICAgICBhc3NlcnQgdC50b19pLCAxNDUwMDE3MDAwCiAgICAgIGFzc2VydCB1LnRvX2ksIDE0NTAwMTcwMDAKCiAgICAgIGFzc2VydCB0LnRvX2RlYnVnX3MsICdvdCAyMDE1LTEyLTEzIDEyOjMwOjAwIC0wMjowMCBkc3Q6ZmFsc2UnCiAgICAgIGFzc2VydCB1LnRvX2RlYnVnX3MsICd0IDIwMTUtMTItMTMgMTQ6MzA6MDAgKzAwOjAwIGRzdDpmYWxzZScKICAgIGVuZAoKICAgIHRlc3QgJ3BhcnNlcyBhIHRpbWUgc3RyaW5nIHdpdGggYSBkZWx0YSAoOikgdGltZXpvbmUnIGRvCgogICAgICBvdCA9IGluX3pvbmUoJ0V1cm9wZS9CZXJsaW4nKSB7IEV0T3JiaS5wYXJzZSgnMjAxNS0xMi0xMyAxMjozMCAtMDI6MDAnKSB9CgogICAgICB0ID0gb3QKICAgICAgdSA9IG90LnV0YwoKICAgICAgYXNzZXJ0IHQudG9faSwgMTQ1MDAxNzAwMAogICAgICBhc3NlcnQgdS50b19pLCAxNDUwMDE3MDAwCgogICAgICBhc3NlcnQgdC50b19kZWJ1Z19zLCAnb3QgMjAxNS0xMi0xMyAxMjozMDowMCAtMDI6MDAgZHN0OmZhbHNlJwogICAgICBhc3NlcnQgdS50b19kZWJ1Z19zLCAndCAyMDE1LTEyLTEzIDE0OjMwOjAwICswMDowMCBkc3Q6ZmFsc2UnCiAgICBlbmQKCiAgICB0ZXN0ICd0YWtlcyB0aGUgbG9jYWwgVFogd2hlbiB0ZXN0IGRvZXMgbm90IGtub3cgdGhlIHRpbWV6b25lJyBkbwoKICAgICAgaW5fem9uZSAnRXVyb3BlL01vc2NvdycgZG8KCiAgICAgICAgb3QgPSBFdE9yYmkucGFyc2UoJzIwMTUvMDMvMDggMDE6NTk6NTkgTmFkYS9OYWRhJykKCiAgICAgICAgYXNzZXJ0X2luY2x1ZGUgb3Quem9uZS5uYW1lLCAld1sgRXVyb3BlL01vc2NvdyBFdXJvcGUvS2lyb3YgXQogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ3BhcnNlcyBldmVuIHdoZW4gdGhlIHR6IGlzIG91dCBvZiBwbGFjZScgZG8KCiAgICAgIGFzc2VydCgKICAgICAgICBFdE9yYmkucGFyc2UoJ1N1biBOb3YgMTggMTY6MDE6MDAgQXNpYS9TaW5nYXBvcmUgMjAxMicpCiAgICAgICAgICAudG9fZGVidWdfcywKICAgICAgICAib3QgMjAxMi0xMS0xOCAxNjowMTowMCArMDg6MDAgZHN0OmZhbHNlIikKICAgIGVuZAoKICAgIHRlc3QgJ2ZhaWxzIG9uIGludmFsaWQgc3RyaW5ncycgZG8KCiAgICAgIGFzc2VydF9lcnJvcigKICAgICAgICBsYW1iZGEgeyBFdE9yYmkucGFyc2UoJ3h4eCcpIH0sCiAgICAgICAgQXJndW1lbnRFcnJvciwgJ05vIHRpbWUgaW5mb3JtYXRpb24gaW4gInh4eCInKQogICAgZW5kCgogICAgdGVzdCAncGFyc2VzIGluIHRoZSBSYWlscy1wcm92aWRlZCBUaW1lLnpvbmUgKFVUQyknIGRvCgogICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnVVRDJwoKICAgICAgdCA9IEV0T3JiaS5wYXJzZSgnMjAxOS0wMS0wMSAxMjoxMCcpCgogICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgYXNzZXJ0IHQuem9uZSwgOjpUWkluZm86OlRpbWV6b25lLmdldCgnVVRDJykKICAgICAgYXNzZXJ0IHQudG9fcywgJzIwMTktMDEtMDEgMTI6MTA6MDAgWicKICAgICAgYXNzZXJ0IHQudG9fenMsICcyMDE5LTAxLTAxIDEyOjEwOjAwIFVUQycKICAgIGVuZAoKICAgIHRlc3QgJ3BhcnNlcyBpbiB0aGUgUmFpbHMtcHJvdmlkZWQgVGltZS56b25lIChBc2lhL1NoYW5naGFpKScgZG8KCiAgICAgIFRpbWUuYWN0aXZlX3N1cHBvcnRfem9uZSA9ICdBc2lhL1NoYW5naGFpJwoKICAgICAgdCA9IEV0T3JiaS5wYXJzZSgnMjAxOS0wMS0wMSAxMjoxMCcpCgogICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgYXNzZXJ0IHQuem9uZSwgOjpUWkluZm86OlRpbWV6b25lLmdldCgnQXNpYS9TaGFuZ2hhaScpCiAgICAgIGFzc2VydCB0LnRvX3MsICcyMDE5LTAxLTAxIDEyOjEwOjAwICswODAwJwogICAgICBhc3NlcnQgdC50b196cywgJzIwMTktMDEtMDEgMTI6MTA6MDAgQXNpYS9TaGFuZ2hhaScKICAgIGVuZAoKIyAgICAjIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfdGltZV96b25lX2FiYnJldmlhdGlvbnMKIyAgICAjIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfdHpfZGF0YWJhc2VfdGltZV96b25lcwojCiMgICAgY29udGV4dCAnVFogYWJicmV2aWF0aW9ucycgZG8KIwojICAgICAgewojCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBBbWVyaWNhL0xvc19BbmdlbGVzJyA9PiAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDgwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBBbWVyaWNhL0xvc19BbmdlbGVzJyA9PiAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBQU1QnID0+ICAgICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDgwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBQRFQnID0+ICAgICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSAtMDc6MDAnID0+ICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSAtMDc6MDAnID0+ICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBHTVQtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBHTVQtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBVVEMtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBVVEMtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBFdGMvR01ULTcnID0+ICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBFdGMvR01ULTcnID0+ICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMKIyAgICAgICAgJzIwMjEtMDEtMDEgN0FNIEFDRFQnID0+ICcyMDIxLTAxLTAxIDA3OjAwOjAwICswOTMwJywKIyAgICAgICAgJzIwMjEtMDctMDEgN0FNIEFDU1QnID0+ICcyMDIxLTAxLTAxIDA3OjAwOjAwICsxMDMwJywKIwojICAgICAgfS5lYWNoIGRvIHxrLCB2fAojCiMgICAgICAgIHRlc3QgInBhcnNlcyAje2t9IHRvICN7dn0iIGRvCiMKIyAgICAgICAgICBvdCA9IEV0T3JiaS5wYXJzZShrKQojdCA9IFRpbWUucGFyc2UoaykKI3AgWyBrIF0KI3AgWyBEYXRlLl9wYXJzZShrKSBdCiNwIFsgdC50b19zLCB0LnpvbmUsIDp0IF0KI3AgWyBvdC50b19zLCBvdC56b25lLCA6b3QgXQojcCBbIHYgXQojICAjcCBbIGssIHYsIG90IF0KIwojICAgICAgICAgIGFzc2VydCBvdC50b19zLCB2KQojICAgICAgICBlbmQKIyAgICAgIGVuZAojICAgIGVuZAoKICAgIGdyb3VwICd3aGVuIENocm9uaWMgaXMgZGVmaW5lZCcgZG8KCiAgICAgIGJlZm9yZSBkbwogICAgICAgIHJlcXVpcmVfY2hyb25pYwogICAgICBlbmQKICAgICAgYWZ0ZXIgZG8KICAgICAgICB1bnJlcXVpcmVfY2hyb25pYwogICAgICBlbmQKCiAgICAgIHRlc3QgJ2xldmVyYWdlcyBpdCcgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cgYXQgMXBtJykKICAgICAgICB0MSA9IFRpbWUubm93ICsgMjQgKiAzNjAwCgogICAgICAgIGFzc2VydCB0LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclRiAlVCcpLCB0MS5zdHJmdGltZSgnJUYgMTM6MDA6MDAnKQogICAgICBlbmQKCiAgICAgIHRlc3QgJ2xldmVyYWdlcyB0ZXN0IGluIGEgc3BlY2lmaWVkIHRpbWUgem9uZScgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cgYXQgMjI6MDAgQW1lcmljYS9OZXdfWW9yaycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC50b196cywgdDEuc3RyZnRpbWUoJyVGIDIyOjAwOjAwIEFtZXJpY2EvTmV3X1lvcmsnKQogICAgICBlbmQKCiAgICAgIHRlc3QgJ3BpY2tzIHRoZSBSYWlscyBUaW1lLnpvbmUgKFVUQykgaWYgYXZhaWxhYmxlJyBkbwoKICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnVVRDJwoKICAgICAgICB0ID0gRXRPcmJpLnBhcnNlKCd0b21vcnJvdycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC56b25lLm5hbWUsICdVVEMnCiAgICAgICAgYXNzZXJ0IHQuc3RyZnRpbWUoJyVZLSVtLSVkJyksIHQxLnN0cmZ0aW1lKCclWS0lbS0lZCcpCiAgICAgICAgYXNzZXJ0IHQuc3RyZnRpbWUoJyVIOiVNOiVTJyksICcxMjowMDowMCcKICAgICAgZW5kCgogICAgICB0ZXN0ICdwaWNrcyB0aGUgUmFpbHMgVGltZS56b25lIChBc2lhL1NoYW5naGFpKSBpZiBhdmFpbGFibGUnIGRvCgogICAgICAgIFRpbWUuYWN0aXZlX3N1cHBvcnRfem9uZSA9ICdBc2lhL1NoYW5naGFpJwoKICAgICAgICB0ID0gRXRPcmJpLnBhcnNlKCd0b21vcnJvdycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC56b25lLm5hbWUsICdBc2lhL1NoYW5naGFpJwogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclWS0lbS0lZCcpLCB0MS5zdHJmdGltZSgnJVktJW0tJWQnKQogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclSDolTTolUycpLCAnMTI6MDA6MDAnCiAgICAgIGVuZAoKICAgICAgZ3JvdXAgJ3doZW4gcGFyc2UoeCwgZW5hYmxlX2Nocm9uaWM6IGZhbHNlKScgZG8KCiAgICAgICAgdGVzdCAnZG9lcyBub3QgcHJlLXBhcnNlIHdpdGggQ2hyb25pYycgZG8KCiAgICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnQW1lcmljYS9DaGljYWdvJwoKICAgICAgICAgIGFzc2VydCggIyB3b3JrcwogICAgICAgICAgICBFdE9yYmkucGFyc2UoJ3RvbW9ycm93JykuY2xhc3MsCiAgICAgICAgICAgIEV0T3JiaTo6RW9UaW1lKQoKICAgICAgICAgIGFzc2VydF9lcnJvciggIyBkb2Vzbid0IHdvcmsKICAgICAgICAgICAgbGFtYmRhIHsgRXRPcmJpLnBhcnNlKCd0b21vcnJvdycsIGVuYWJsZV9jaHJvbmljOiBmYWxzZSkgfSwKICAgICAgICAgICAgQXJndW1lbnRFcnJvciwgJ05vIHRpbWUgaW5mb3JtYXRpb24gaW4gInRvbW9ycm93IicpCiAgICAgICAgZW5kCiAgICAgIGVuZAoKICAgICAgZ3JvdXAgJ2FuZCBFdC1PcmJpLmNocm9uaWNfZW5hYmxlZD8gaXMgZmFsc2UnIGRvCgogICAgICAgIGJlZm9yZSBkbwogICAgICAgICAgRXRPcmJpLmNocm9uaWNfZW5hYmxlZCA9IGZhbHNlCiAgICAgICAgZW5kCiAgICAgICAgYWZ0ZXIgZG8KICAgICAgICAgIEV0T3JiaS5jaHJvbmljX2VuYWJsZWQgPSB0cnVlCiAgICAgICAgZW5kCgogICAgICAgIHRlc3QgJ2RvZXMgbm90IHByZS1wYXJzZSB3aXRoIENocm9uaWMnIGRvCgogICAgICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgICAgICBsYW1iZGEgeyBFdE9yYmkucGFyc2UoJ3RvbW9ycm93JykgfSwKICAgICAgICAgICAgQXJndW1lbnRFcnJvciwgJ05vIHRpbWUgaW5mb3JtYXRpb24gaW4gInRvbW9ycm93IicpCiAgICAgICAgZW5kCgogICAgICAgIHRlc3QgJ3ByZS1wYXJzZXMgd2l0aCBDaHJvbmljIHdoZW4gZW5hYmxlX2Nocm9uaWM6IHRydWUnIGRvCgogICAgICAgICAgYXNzZXJ0X25vdF9lcnJvciB7IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cnLCBlbmFibGVfY2hyb25pYzogdHJ1ZSkgfQogICAgICAgIGVuZAogICAgICBlbmQKCiAgICAgIHRlc3QgJ2ZpbHRlcnMgb3B0aW9ucyBnaXZlbiB0byBDaHJvbmljJyBkbwoKICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnVVRDJwoKICAgICAgICAjYXNzZXJ0IDo6S2hyb25pYykKICAgICAgICAjICAudG8gcmVjZWl2ZSg6cGFyc2UpCiAgICAgICAgIyAgLndpdGgoJ3RvbW9ycm93JywgeyBjb250ZXh0OiA6ZnV0dXJlIH0pCiAgICAgICAgIyAgLmFuZF9jYWxsX29yaWdpbmFsCgogICAgICAgIHQgPSBFdE9yYmkucGFyc2UoCiAgICAgICAgICAndG9tb3Jyb3cnLCB6b25lOiA6OlRaSW5mbzo6VGltZXpvbmUuZ2V0KCdVVEMnKSwgY29udGV4dDogOmZ1dHVyZSkKICAgICAgICB0MSA9IFRpbWUubm93ICsgMjQgKiAzNjAwCgogICAgICAgIGFzc2VydCB0LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICAgIGFzc2VydCB0LnpvbmUubmFtZSwgJ1VUQycKICAgICAgICBhc3NlcnQgdC5zdHJmdGltZSgnJVktJW0tJWQnKSwgdDEuc3RyZnRpbWUoJyVZLSVtLSVkJykKICAgICAgICBhc3NlcnQgdC5zdHJmdGltZSgnJUg6JU06JVMnKSwgJzEyOjAwOjAwJwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnLmdldF90em9uZScgZG8KCiAgICB7CgogICAgICAnR0InID0+ICdHQicsCiAgICAgICdVVEMnID0+ICdVVEMnLAogICAgICAnR01UJyA9PiAnR01UJywKICAgICAgJ1p1bHUnID0+ICdadWx1JywKICAgICAgJ0phcGFuJyA9PiAnSmFwYW4nLAogICAgICAnVHVya2V5JyA9PiAnVHVya2V5JywKICAgICAgJ0FzaWEvVG9reW8nID0+ICdBc2lhL1Rva3lvJywKICAgICAgJ0V1cm9wZS9QYXJpcycgPT4gJ0V1cm9wZS9QYXJpcycsCiAgICAgICdFdXJvcGUvWnVyaWNoJyA9PiAnRXVyb3BlL1p1cmljaCcsCiAgICAgICdXLVNVJyA9PiAnVy1TVScsCgogICAgICAnWicgPT4gJ1VUQycsCgogICAgICAnKzA5OjAwJyA9PiAnKzA5OjAwJywKICAgICAgJy0wMTozMCcgPT4gJy0wMTozMCcsCgogICAgICAnX0FULTM6MzBBc2lhL1RlaHJhbicgPT4gJ0FzaWEvVGVocmFuJywKICAgICAgJ19BVC00QXNpYS9UYmlsaXNpJyA9PiAnQXNpYS9UYmlsaXNpJywKCiAgICAgICcrMDg6MDAnID0+ICcrMDg6MDAnLAogICAgICAnKzA4MDAnID0+ICcrMDgwMCcsICMgbm8gbm9ybWFsaXphdGlvbiB0byAiKzA4OjAwIgoKICAgICAgJy0wMScgPT4gJy0wMScsCgogICAgICAzNjAwID0+ICcrMDE6MDAnLAoKICAgICAgJ1Rva3lvIFN0YW5kYXJkIFRpbWUnID0+ICdBc2lhL1Rva3lvJywKICAgICAgJ0Nvb3JkaW5hdGVkIFVuaXZlcnNhbCBUaW1lJyA9PiAnVVRDJywKCiAgICAgICdFYXN0ZXJuIFN0YW5kYXJkIFRpbWUnID0+ICdBbWVyaWNhL05ld19Zb3JrJywKICAgICAgJ0Vhc3Rlcm4gRGF5bGlnaHQgVGltZScgPT4gJ0FtZXJpY2EvTmV3X1lvcmsnLAoKICAgICAgJ0NTVCswODAwJyA9PiAnQXNpYS9DaG9uZ3FpbmcnLAogICAgICAnQ1NUKzA4OjAwJyA9PiAnQXNpYS9DaG9uZ3FpbmcnLAogICAgICAnQ1NUKzA4JyA9PiAnQXNpYS9DaG9uZ3FpbmcnLAogICAgICAnQ1NUKzgnID0+ICdBc2lhL0Nob25ncWluZycsCiAgICAgICdFRFQtMDQwMCcgPT4gJ0FtZXJpY2EvRGV0cm9pdCcsCgogICAgICAnV0VULTFXRVNUJyA9PiAnV0VUJywKICAgICAgJ0NFVC0yQ0VTVCcgPT4gJ0NFVCcsCgogICAgfS5lYWNoIGRvIHxhLCBifAoKICAgICAgdGVzdCAicmV0dXJucyAje2IuaW5zcGVjdH0gZm9yICN7YS5pbnNwZWN0fSIgZG8KCiAgICAgICAgeiA9IEV0T3JiaS5nZXRfdHpvbmUoYSkKCiAgICAgICAgYXNzZXJ0X25vdF9uaWwgegogICAgICAgIGFzc2VydCB6Lm5hbWUsIGIKICAgICAgZW5kCiAgICBlbmQKCiAgICB0ZXN0ICdoYW5kbGVzIGZyYWN0aW9uYWwtaG91ciBvZmZzZXRzJyBkbwoKICAgICAgYXNzZXJ0IEV0T3JiaS5nZXRfdHpvbmUoMTlfODAwKS5uYW1lLCAnKzA1OjMwJwogICAgICBhc3NlcnQgRXRPcmJpLmdldF90em9uZSgtMV84MDApLm5hbWUsICctMDA6MzAnCiAgICAgIGFzc2VydCBFdE9yYmkuZ2V0X3R6b25lKCctMDA6MzAnKS5uYW1lLCAnLTAwOjMwJwoKICAgICAgYXNzZXJ0KAogICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldygwLCAxOV84MDApLnRvX2RlYnVnX3MsCiAgICAgICAgJ290IDE5NzAtMDEtMDEgMDU6MzA6MDAgKzA1OjMwIGRzdDpmYWxzZScpCiAgICBlbmQKCiMgICAgdGVzdCAncmV0dXJucyBhIHRpbWV6b25lIGZvciB3ZWxsLWtub3duIGFiYnJldmlhdGlvbnMnIGRvCiMKIyAgICAgIGFzc2VydCBndHooJ0pTVCcpLCAnSmFwYW4nCiMgICAgICBhc3NlcnQgZ3R6KCdQU1QnKSwgJ0FtZXJpY2EvRGF3c29uJwojICAgICAgYXNzZXJ0IGd0eignQ0VTVCcpLCAnQWZyaWNhL0NldXRhJwojICAgIGVuZAoKICAgIFsKICAgICAgJ0FzaWEvUGFyaXMnLCAnTmFkYS9OYWRhJywgJzcnLCAnMDYnLCAnc3VuIzMnLCAnTWF6ZGEgWm9vbSBab29tIFN0YWRpdW0nCiAgICBdLmVhY2ggZG8gfHN8CgogICAgICB0ZXN0ICJyZXR1cm5zIG5pbCBmb3IgI3tzLmluc3BlY3R9IiBkbwoKICAgICAgICBhc3NlcnRfbmlsIEV0T3JiaS5nZXRfdHpvbmUocykKICAgICAgZW5kCiAgICBlbmQKCiAgICAjIHJ1ZnVzLXNjaGVkdWxlciBnaC0yMjIKICAgICMKICAgIHRlc3QgImZhbGxzIGJhY2sgdG8gRU5WWydUWiddIGlmIHRlc3QgZG9lc24ndCBrbm93IFRpbWUubm93LnpvbmUiIGRvCgogICAgICBiZWdpbgoKICAgICAgICBjdXJyZW50ID0gRXRPcmJpLmdldF90em9uZSg6bG9jYWwpCgogICAgICAgIFRpbWUuX3pvbmUgPSAn5Lit5Zu95qCH5YeG5pe26Ze0JwoKICAgICAgICAjYXNzZXJ0X25pbCBFdE9yYmkuZ2V0X3R6b25lKDpjdXJyZW50KQogICAgICAgICNhc3NlcnQgRXRPcmJpLmdldF90em9uZSg6Y3VycmVudCksIEV0T3JiaS5nZXRfdHpvbmUoVGltZS5ub3cuem9uZSkKICAgICAgICAgICMKICAgICAgICAgICMgZ2gtMjQwIGludHJvZHVjZXMgYSB3YXkgb2YgZmluZGluZyB0aGUgdGltZXpvbmUgYnkgYXNraW5nIGRpcmVjdGx5CiAgICAgICAgICAjIHRvIHRoZSBzeXN0ZW0sIHNvIHRob3NlIGRvIHJldHVybiBhIHRpbWV6b25lLi4uCgogICAgICAgIGluX3pvbmUgJ0FzaWEvU2hhbmdoYWknIGRvCgogICAgICAgICAgYXNzZXJ0X2luY2x1ZGUoCiAgICAgICAgICAgIEV0T3JiaS5nZXRfdHpvbmUoOmxvY2FsKSwKICAgICAgICAgICAgWyBFdE9yYmkuZ2V0X3R6b25lKCdBc2lhL1NoYW5naGFpJyksCiAgICAgICAgICAgICAgRXRPcmJpLmdldF90em9uZSgnQXNpYS9DaG9uZ3FpbmcnKSBdKQogICAgICAgIGVuZAoKICAgICAgZW5zdXJlCgogICAgICAgIFRpbWUuX3pvbmUgPSBuaWwKICAgICAgZW5kCgogICAgICBhc3NlcnQgRXRPcmJpLmdldF90em9uZSg6bG9jYWwpLCBjdXJyZW50CiAgICBlbmQKCiAgICB7ICMgZm9yIHJ1ZnVzLXNjaGVkdWxlciBnaC0yMjgKCiAgICAgICdBc2lhL1Rva3lvJyA9PiAld1sKICAgICAgICBBc2lhL1Rva3lvIF0sCiAgICAgICdBc2lhL1NoYW5naGFpJyA9PiAld1sKICAgICAgICBBc2lhL1NoYW5naGFpIEFzaWEvQ2hvbmdxaW5nIF0sCiAgICAgICdFdXJvcGUvWnVyaWNoJyA9PiAld1sKICAgICAgICBFdXJvcGUvWnVyaWNoIEFmcmljYS9DZXV0YSB3aW5kb3dzOkNFVCBdLAogICAgICAnRXVyb3BlL0xvbmRvbicgPT4gJXdbCiAgICAgICAgRXVyb3BlL0xvbmRvbiBFdXJvcGUvQmVsZmFzdCB3aW5kb3dzOkdNVCB3aW5kb3dzOkdNVC0wIF0sCgogICAgfS5lYWNoIGRvIHx6b25lLCB0YXJnZXRzfAoKICAgICAgdGVzdCAicmV0dXJucyB0aGUgY3VycmVudCB0aW1lem9uZSBmb3IgOmN1cnJlbnQgaW4gI3t6b25lfSIgZG8KCiAgICAgICAgaW5fem9uZSh6b25lKSBkbwoKICAgICAgICAgIGFzc2VydF9pbmNsdWRlIEV0T3JiaS5nZXRfdHpvbmUoOmxvY2FsKSwgc2VsZWN0X3pvbmVzKHRhcmdldHMpCiAgICAgICAgZW5kCiAgICAgIGVuZAogICAgZW5kCgogICAgdGVzdCAiZG9lc24ndCBtaW5kIGJlaW5nIGdpdmVuIGEgVFpJbmZvOjpUaW1lem9uZSIgZG8KCiAgICAgIHR6ID0gOjpUWkluZm86OlRpbWV6b25lLmdldCgnWnVsdScpCiAgICAgIGNsYXNzIDw8IHR6CiAgICAgICAgZGVmIDw9Pih0eikKICAgICAgICAgICNyZXR1cm4gbmlsIHVubGVzcyB0ei5pc19hPyhUaW1lem9uZSkKICAgICAgICAgIGlkZW50aWZpZXIgPD0+IHR6LmlkZW50aWZpZXIKICAgICAgICBlbmQKICAgICAgZW5kCiAgICAgICAgIyBzaW11bGF0ZSB0emluZm8gMC4zLjUzIGlzc3VlCgogICAgICBhc3NlcnQgRXRPcmJpLmdldF90em9uZSh0eiksIDo6VFpJbmZvOjpUaW1lem9uZS5nZXQoJ1p1bHUnKQogICAgZW5kCiAgZW5kCgogIGdyb3VwICcuZGV0ZXJtaW5lX2xvY2FsX3R6b25lJyBkbwoKICAgIHRlc3QgJ2Zhdm91cnMgdGhlIGxvY2FsIHRpbWV6b25lJyBkbwoKICAgICAgaW5fem9uZSg6bm9fZW52X3R6KSBkbwoKICAgICAgICBUaW1lLl96b25lID0gJ0NhcGUgVmVyZGUgU3RhbmRhcmQgVGltZScKICAgICAgICBFdE9yYmkuX29zX3pvbmUgPSAnJyAjIGZvcmNlICNvc190eiB0byByZXR1cm4gbmlsCgogICAgICAgIGFzc2VydCBFdE9yYmkuZGV0ZXJtaW5lX2xvY2FsX3R6b25lLm5hbWUsICdBdGxhbnRpYy9DYXBlX1ZlcmRlJwogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ2Zhdm91cnMgdGhlIGxvY2FsIHRpbWV6b25lJyBkbwoKICAgICAgaW5fem9uZSgnRXVyb3BlL0JlcmxpbicpIGRvCgogICAgICAgIGFzc2VydF9pbmNsdWRlKAogICAgICAgICAgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZSwKICAgICAgICAgIHNlbGVjdF96b25lcygld1sgRXVyb3BlL0JlcmxpbiBBZnJpY2EvQ2V1dGEgd2luZG93czpDRVQgXSkpCiAgICAgIGVuZAogICAgZW5kCgogICAgdGVzdCAnZmF2b3VycyB0aGUgbG9jYWwgdGltZXpvbmUgKE1pdHRlbGV1cm9wYWVpc2NoZSBTb21tZXJ6ZWl0KScgZG8KCiAgICAgIGluX3pvbmUoOm5vX2Vudl90eikgZG8KCiAgICAgICAgVGltZS5fem9uZSA9ICJNaXR0ZWxldXJvcFx4RTRpc2NoZSBTb21tZXJ6ZWl0IgogICAgICAgIEV0T3JiaS5fb3Nfem9uZSA9ICcnICMgZm9yY2UgI29zX3R6IHRvIHJldHVybiBuaWwKCiAgICAgICAgYXNzZXJ0X25vdF9lcnJvciB7IEV0T3JiaS5kZXRlcm1pbmVfbG9jYWxfdHpvbmUgfQogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ3JldHVybnMgdGhlIGxvY2FsIHRpbWV6b25lJyBkbwoKICAgICAgaW5fem9uZSgnQW1lcmljYS9KYW1haWNhJykgZG8KCiAgICAgICAgYXNzZXJ0X2luY2x1ZGUoCiAgICAgICAgICBFdE9yYmkuZGV0ZXJtaW5lX2xvY2FsX3R6b25lLm5hbWUsCiAgICAgICAgICBbICdBbWVyaWNhL0phbWFpY2EnLCAnQW1lcmljYS9BdGlrb2thbicsICdFU1QnIF0pCiAgICAgIGVuZAogICAgZW5kCgogICAgdGVzdCAncmV0dXJucyB0aGUgUmFpbHMtcHJvdmlkZWQgVGltZS56b25lLnR6aW5mbyBpZiBhdmFpbGFibGUnIGRvCgogICAgICAjIGh0dHA6Ly9hcGkucnVieW9ucmFpbHMub3JnL2NsYXNzZXMvQWN0aXZlU3VwcG9ydC9UaW1lWm9uZS5odG1sCgogICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnRXVyb3BlL1ZpbG5pdXMnCgogICAgICBpbl96b25lKDpub19lbnZfdHopIGRvCiAgICAgICAgYXNzZXJ0IEV0T3JiaS5kZXRlcm1pbmVfbG9jYWxfdHpvbmUuY2xhc3MsIDo6VFpJbmZvOjpEYXRhVGltZXpvbmUKICAgICAgICBhc3NlcnQgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZS5uYW1lLCAnRXVyb3BlL1ZpbG5pdXMnCiAgICAgIGVuZAogICAgZW5kCgogICAgdGVzdCAiZ2l2ZXMgcHJlY2VkZW5jZSB0byBFTlZbJ1RaJ10gb3ZlciBSYWlscyBUaW1lLnpvbmUudHppbmZvIiBkbwoKICAgICAgIyBodHRwOi8vYXBpLnJ1YnlvbnJhaWxzLm9yZy9jbGFzc2VzL0FjdGl2ZVN1cHBvcnQvVGltZVpvbmUuaHRtbAoKICAgICAgVGltZS5hY3RpdmVfc3VwcG9ydF96b25lID0gJ0V1cm9wZS9WaWxuaXVzJwoKICAgICAgaW5fem9uZSgnQXNpYS9UZWhyYW4nKSBkbwoKICAgICAgICBhc3NlcnQgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZS5jbGFzcywgOjpUWkluZm86OkRhdGFUaW1lem9uZQogICAgICAgIGFzc2VydCBFdE9yYmkuZGV0ZXJtaW5lX2xvY2FsX3R6b25lLm5hbWUsICdBc2lhL1RlaHJhbicKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy56b25lJyBkbwoKICAgIHRlc3QgJ2lzIGFuIGFsaWFzIHRvIC5kZXRlcm1pbmVfbG9jYWxfdHpvbmUnIGRvCgogICAgICBpbl96b25lKDpub19lbnZfdHopIGRvCgogICAgICAgIFRpbWUuX3pvbmUgPSAnQ2FwZSBWZXJkZSBTdGFuZGFyZCBUaW1lJwogICAgICAgIEV0T3JiaS5fb3Nfem9uZSA9ICcnICMgZm9yY2UgI29zX3R6IHRvIHJldHVybiBuaWwKCiAgICAgICAgYXNzZXJ0IEV0T3JiaS56b25lLm5hbWUsICdBdGxhbnRpYy9DYXBlX1ZlcmRlJwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnLm5vdycgZG8KCiAgICB0ZXN0ICdyZXR1cm5zIGEgY3VycmVudCwgbG9jYWwgRW9UaW1lIGluc3RhbmNlJyBkbwoKICAgICAgaW5fem9uZSAnQXNpYS9TaGFuZ2hhaScgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5ub3cKICAgICAgICBuID0gVGltZS5ub3cKCiAgICAgICAgYXNzZXJ0IHQuc2Vjb25kcyA+IChuIC0gMSkudG9fZgogICAgICAgIGFzc2VydCB0LnNlY29uZHMgPCAobiArIDEpLnRvX2YKCiAgICAgICAgYXNzZXJ0X2luY2x1ZGUgdC56b25lLm5hbWUsIFsgJ0FzaWEvU2hhbmdoYWknLCAnQXNpYS9DaG9uZ3FpbmcnIF0KICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy5tYWtlX3RpbWUnIGRvCgogICAgdGVzdCAncmV0dXJucyBhbiBFb1RpbWUgaW5zdGFuY2UgYXMgaXMnIGRvCgogICAgICB0MCA9IEV0T3JiaS5wYXJzZSgnMjAxNy0wMy0yMSAxMjowMDozNCBBc2lhL1VsYW5fQmF0b3InKQogICAgICB0MSA9IEV0T3JiaS5tYWtlX3RpbWUodDApCgogICAgICBhc3NlcnQgdDEuY2xhc3MsIDo6RXRPcmJpOjpFb1RpbWUKICAgICAgYXNzZXJ0IHQxLCB0MAogICAgICBhc3NlcnQgdDEub2JqZWN0X2lkLCB0MC5vYmplY3RfaWQKICAgIGVuZAoKICAgIHRlc3QgJ3JldHVybnMgYW4gRW9UaW1lIGluc3RhbmNlIGFzIGlzJyBkbwoKICAgICAgdDAgPSBFdE9yYmkucGFyc2UoJzIwMTctMDMtMjEgMTI6MDA6MzQgQXNpYS9VbGFuX0JhdG9yJykKICAgICAgdDEgPSBFdE9yYmkubWFrZV90aW1lKHQwLCB0MC56b25lKQoKICAgICAgYXNzZXJ0IHQxLmNsYXNzLCA6OkV0T3JiaTo6RW9UaW1lCiAgICAgIGFzc2VydCB0MSwgdDAKICAgICAgYXNzZXJ0IHQxLm9iamVjdF9pZCwgdDAub2JqZWN0X2lkCiAgICBlbmQKCiAgICBbCiAgICAgIFsgJ2FuIEVvVGltZSBpbnN0YW5jZScsCiAgICAgICAgbmlsLAogICAgICAgIGxhbWJkYSB7IEV0T3JiaS5wYXJzZSgnMjAxNy0wMy0yMSAxMjowMDozNCBBc2lhL1VsYW5fQmF0b3InKSB9LAogICAgICAgICdvdCAyMDE3LTAzLTIxIDEyOjAwOjM0ICswODowMCBkc3Q6ZmFsc2UnIF0sCgogICAgICBbICdhIGxvY2FsIHRpbWUnLAogICAgICAgICdBc2lhL1RiaWxpc2knLAogICAgICAgIFRpbWUucGFyc2UoJzIwMTYtMTEtMDEgMTI6MzA6MDkgKzA0OjAwJyksICMgdXNlIGN1c3RvbSB0eiA6LSgKICAgICAgICAnb3QgMjAxNi0xMS0wMSAxMjozMDowOSArMDQ6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYW4gVVRDIHRpbWUnLAogICAgICAgIG5pbCwKICAgICAgICBUaW1lLnV0YygyMDE2LCAxMSwgMDEsIDEyLCAzMCwgOSksCiAgICAgICAgJ290IDIwMTYtMTEtMDEgMTI6MzA6MDkgKzAwOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2EgRGF0ZSBpbnN0YW5jZScsCiAgICAgICAgbmlsLAogICAgICAgIERhdGUubmV3KDIwMTYsIDExLCAwMSksCiAgICAgICAgbGFtYmRhIHsKICAgICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldygKICAgICAgICAgICAgVGltZS5sb2NhbCgyMDE2LCAxMSwgMDEpLnRvX2YsIG5pbAogICAgICAgICAgKS50b19kZWJ1Z19zIH0gXSwKCiAgICAgIFsgJ2EgU3RyaW5nJywKICAgICAgICBuaWwsCiAgICAgICAgJzIwMTYtMTEtMDEgMTI6MzA6MDknLAogICAgICAgIGxhbWJkYSB7CiAgICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoCiAgICAgICAgICAgIFRpbWUubG9jYWwoMjAxNiwgMTEsIDAxLCAxMiwgMzAsIDkpLnRvX2YsIG5pbCkgfSBdLAoKICAgICAgWyAnYSBTdHJpbmcnLAogICAgICAgICdBbWVyaWNhL0NoaWNhZ28nLAogICAgICAgICcyMDE2LTA4LTAxIDEyOjMwOjEwJywKICAgICAgICAnb3QgMjAxNi0wOC0wMSAxMjozMDoxMCAtMDU6MDAgZHN0OnRydWUnIF0sCiAgICAgICNbICdhIFN0cmluZycsCiAgICAgICMgICdBbWVyaWNhL0NoaWNhZ28nLAogICAgICAjICAnMjAxNi0xMS0wMSAxMjozMDowOScsCiAgICAgICMgICdvdCAyMDE2LTExLTAxIDEyOjMwOjA5IC0wNTowMCBkc3Q6dHJ1ZScgXSwgIyBmYWlscyBvbiBBcHB2ZXlvciA6LSgKICAgICAgWyAnYSBTdHJpbmcnLAogICAgICAgICdBbWVyaWNhL0NoaWNhZ28nLAogICAgICAgICcyMDE2LTExLTA2IDEyOjMwOjA4JywKICAgICAgICAnb3QgMjAxNi0xMS0wNiAxMjozMDowOCAtMDY6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYSBTdHJpbmcnLAogICAgICAgICdFdXJvcGUvUGFyaXMnLAogICAgICAgICcyMDE2LTEtMSAxMjozMDowNycsCiAgICAgICAgJ290IDIwMTYtMDEtMDEgMTI6MzA6MDcgKzAxOjAwIGRzdDpmYWxzZScgXSwKICAgICAgWyAnYSBTdHJpbmcnLAogICAgICAgICdFdXJvcGUvUGFyaXMnLAogICAgICAgICcyMDE2LTgtMSAxMjozMDowNycsCiAgICAgICAgJ290IDIwMTYtMDgtMDEgMTI6MzA6MDcgKzAyOjAwIGRzdDp0cnVlJyBdLAoKICAgICAgWyAnYSBadWx1IFN0cmluZycsCiAgICAgICAgbmlsLAogICAgICAgICcyMDE2LTExLTAxIDEyOjMwOjA5WicsCiAgICAgICAgRXRPcmJpOjpFb1RpbWUubmV3KFRpbWUudXRjKDIwMTYsIDExLCAwMSwgMTIsIDMwLCA5KS50b19mLCAnWnVsdScpIF0sCgogICAgICBbICdhIHNzKzAxOjAwIFN0cmluZycsCiAgICAgICAgbmlsLAogICAgICAgICcyMDE2LTExLTAxIDEyOjMwOjA5KzAxOjAwJywKICAgICAgICAnb3QgMjAxNi0xMS0wMSAxMjozMDowOSArMDE6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYSBzcy0wMSBTdHJpbmcnLAogICAgICAgIG5pbCwKICAgICAgICAnMjAxNi0xMS0wMSAxMjozMDowOS0wMScsCiAgICAgICAgJ290IDIwMTYtMTEtMDEgMTI6MzA6MDkgLTAxOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2EgU3RyaW5nIHdpdGggYW4gZXhwbGljaXQgdGltZSB6b25lJywKICAgICAgICBuaWwsCiAgICAgICAgJzIwMTYtMDUtMDEgMTI6MzA6MDkgQW1lcmljYS9OZXdfWW9yaycsCiAgICAgICAgJ290IDIwMTYtMDUtMDEgMTI6MzA6MDkgLTA0OjAwIGRzdDp0cnVlJyBdLAoKICAgICAgWyAnYSBOdW1lcmljJywKICAgICAgICBuaWwsCiAgICAgICAgMzYwMCwKICAgICAgICBsYW1iZGEgeyBbIFRpbWUubm93ICsgMzYwMCAtIDAuMzUsIFRpbWUubm93ICsgMzYwMCArIDAuMzUgXSB9IF0sCgogICAgICBbICdhbiBhcnJheSBbIHksIG0sIGQsIC4uLiBdJywKICAgICAgICAnRXVyb3BlL01vc2NvdycsCiAgICAgICAgWyBbIDIwMTcsIDIsIDI4IF0gXSwKICAgICAgICAnb3QgMjAxNy0wMi0yOCAwMDowMDowMCArMDM6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYW4gYXJyYXkgb2YgYXJncyAoeSwgbSwgZCwgLi4uKScsCiAgICAgICAgJ0V1cm9wZS9Nb3Njb3cnLAogICAgICAgIFsgMjAxNywgMSwgMzEsIDEwIF0sCiAgICAgICAgJ290IDIwMTctMDEtMzEgMTA6MDA6MDAgKzAzOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2FuIGFycmF5IG9mIGFyZ3MgYW5kIGEgem9uZSBhcyBsYXN0IGFyZycsCiAgICAgICAgbmlsLAogICAgICAgIFsgMjAxNywgMSwgMzEsIDEyLCAnRXVyb3BlL01vc2NvdycgXSwKICAgICAgICAnb3QgMjAxNy0wMS0zMSAxMjowMDowMCArMDM6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYSBzdHJpbmcgYW5kIGEgem9uZSBhcyBsYXN0IGFyZycsCiAgICAgICAgbmlsLAogICAgICAgIFsgJzIwMTYtMDUtMDEgMTI6MzA6MDknLCAnQW1lcmljYS9DaGljYWdvJyBdLAogICAgICAgICdvdCAyMDE2LTA1LTAxIDEyOjMwOjA5IC0wNTowMCBkc3Q6dHJ1ZScgXSwKCiAgICAgIFsgJ2Egc3RyaW5nIGFuZCBhbiBvdmVycmlkaW5nIHpvbmUgYXMgbGFzdCBhcmcnLAogICAgICAgIG5pbCwKICAgICAgICBbICcyMDE2LTA1LTAxIDExOjMwOjA5IEFtZXJpY2EvTmV3X1lvcmsnLCAnQW1lcmljYS9DaGljYWdvJyBdLAogICAgICAgICdvdCAyMDE2LTA1LTAxIDExOjMwOjA5IC0wNTowMCBkc3Q6dHJ1ZScgXSwKCiAgICAgIFsgJ2FuIGFycmF5IG9mIGFyZ3MgYW5kIGEgVFpJbmZvIHpvbmUgYXMgbGFzdCBhcmcnLAogICAgICAgIG5pbCwKICAgICAgICBbIDIwMTcsIDEsIDMxLCBFdE9yYmkuZ2V0X3R6b25lKCdFdXJvcGUvT3NsbycpIF0sCiAgICAgICAgJ290IDIwMTctMDEtMzEgMDA6MDA6MDAgKzAxOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2Egc3RyaW5nIGFuZCBhIFRaSW5mbyB6b25lIGFzIGxhc3QgYXJnJywKICAgICAgICBuaWwsCiAgICAgICAgWyAnMjAxNy0wMS0zMSAxMjozMCcsIEV0T3JiaS5nZXRfdHpvbmUoJ0V1cm9wZS9Pc2xvJykgXSwKICAgICAgICAnb3QgMjAxNy0wMS0zMSAxMjozMDowMCArMDE6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYSBzdHJpbmcnLAogICAgICAgICdBbWVyaWNhL0NoaWNhZ28nLAogICAgICAgIGxhbWJkYSB7IFsgVGltZS5wYXJzZSgnMjAyMS0wMy0xMScpIF0gfSwKICAgICAgICB3aW5kb3dzPyA/ICdDU1Q2Q0RUJyA6ICdBbWVyaWNhIC0gQ2hpY2FnbycsCiAgICAgICAgbGFtYmRhIHsgfHR8IHQuem9uZS50b19zIH0gXSwKCiAgICAgIFsgJ2Egc3RyaW5nJywKICAgICAgICAnQW1lcmljYS9OZXdfWW9yaycsCiAgICAgICAgbGFtYmRhIHsgWyBUaW1lLnBhcnNlKCcyMDIxLTAzLTExJykgXSB9LAogICAgICAgIHdpbmRvd3M/ID8gJXdbIEVTVDVFRFQgRVNUIF0gOiAnQW1lcmljYSAtIE5ldyBZb3JrJywKICAgICAgICAjd2luZG93cz8gPyAnRVNUJyA6ICdBbWVyaWNhIC0gTmV3IFlvcmsnLAogICAgICAgIGxhbWJkYSB7IHx0fCB0LnpvbmUudG9fcyB9IF0sCgogICAgXS5lYWNoIGRvIHxuYW1lLCB6b25lLCBhcmdzLCBhc3NlcnRlZCwgdHJhbnNmb3JtZXJ8CgogICAgICB0aXRsZSA9ICJ0dXJucyAje25hbWV9IGludG8gYW4gRW9UaW1lIGluc3RhbmNlIgogICAgICB0aXRsZSArPSAiIGluICN7em9uZX0iIGlmIHpvbmUKCiAgICAgIHRlc3QodGl0bGUpIGRvCgogICAgICAgIGVvdCwgZXhwID0KICAgICAgICAgIGluX3pvbmUoem9uZSkgZG8KCiAgICAgICAgICAgIGFzID0gYXJncy5pc19hPyhQcm9jKSA/IGFyZ3MuY2FsbCA6IGFyZ3MKCiAgICAgICAgICAgIHQgPSBhcy5pc19hPyhBcnJheSkgPwogICAgICAgICAgICAgIEV0T3JiaS5tYWtlX3RpbWUoKmFzKSA6CiAgICAgICAgICAgICAgRXRPcmJpLm1ha2VfdGltZShhcykKICAgICAgICAgICAgeCA9IGFzc2VydGVkLmlzX2E/KFByb2MpID8KICAgICAgICAgICAgICBhc3NlcnRlZC5jYWxsIDoKICAgICAgICAgICAgICBhc3NlcnRlZAoKICAgICAgICAgICAgdCA9IHRyYW5zZm9ybWVyLmlzX2E/KFByb2MpID8gdHJhbnNmb3JtZXJbdF0gOiB0CgogICAgICAgICAgICBbIHQsIHggXQogICAgICAgICAgZW5kCiNwIGVvdC50b19kZWJ1Z19zCiNwIGVvdC51dGNfb2Zmc2V0CiNwIGVvdC5zZW5kKDp0b190aW1lKS50b19zCiNwIGVvdC5zZW5kKDp0b190aW1lKS51dGNfb2Zmc2V0CgogICAgICAgIGNhc2UgZXhwCiAgICAgICAgd2hlbiBTdHJpbmcKICAgICAgICAgIGFzc2VydCBlb3QucmVzcG9uZF90bz8oOnRvX2RlYnVnX3MpID8gZW90LnRvX2RlYnVnX3MgOiBlb3QsIGV4cAogICAgICAgIHdoZW4gQXJyYXkKICAgICAgICAgIGlmIGV4cC5jb2xsZWN0KCY6Y2xhc3MpLnVuaXEgPT0gWyBTdHJpbmcgXQogICAgICAgICAgICBhc3NlcnQgZXhwLmluY2x1ZGU/KGVvdCkKICAgICAgICAgIGVsc2UKICAgICAgICAgICAgYXNzZXJ0IGVvdCA+IGV4cFswXQogICAgICAgICAgICBhc3NlcnQgZW90IDwgZXhwWzFdCiAgICAgICAgICBlbmQKICAgICAgICBlbHNlCiAgICAgICAgICBhc3NlcnQgZW90LCBleHAKICAgICAgICBlbmQKICAgICAgZW5kCiAgICBlbmQKCiMgICAgdGVzdCAnYWNjZXB0cyBhIGR1cmF0aW9uIFN0cmluZycjIGRvCiMjCiMjICAgICAgZXhwZWN0KAojIyAgICAgICAgRXRPcmJpLm1ha2VfdGltZSgnMWgnKQojIyAgICAgICkudG8gYmVfYmV0d2VlbigKIyMgICAgICAgIFRpbWUubm93ICsgMzYwMCAtIDEsIFRpbWUubm93ICsgMzYwMCArIDEKIyMgICAgICApCiMjICAgIGVuZAojICAjCiMgICMgU3RyaW5nIHBhcnNpbmcgaXMgZnVnaXQncyBqb2IuIEV0LW9yYmkgc2hvdWxkIGJlIGEgZGVwZW5kZW5jeSBvZgojICAjIGZ1Z2l0LCBub3QgdGhlIG90aGVyIHdheSBhcm91bmQuIFdoZW4gZnVnaXQgaXMgcHJlc2VudCwgdGhpcwojICAjIHNwZWMgc2hvdWxkIHN1Y2NlZWQsIGVsc2UgdGVzdCBzaG91bGQgbm90LgoKICAgIHRlc3QgJ2FjY2VwdHMgYSBSYWlscyBUaW1lV2l0aFpvbmUnIGRvCgogICAgICAjIGh0dHA6Ly9hcGkucnVieW9ucmFpbHMub3JnL2NsYXNzZXMvQWN0aXZlU3VwcG9ydC9UaW1lV2l0aFpvbmUuaHRtbAoKICAgICAgbiA9IFRpbWUubm93CiAgICAgIHogPSBFdE9yYmkuZ2V0X3R6b25lKCdQYWNpZmljL0Vhc3RlcicpCgogICAgICAjdCA9IEFjdGl2ZVN1cHBvcnQ6OlRpbWVXaXRoWm9uZS5uZXcobiwgeikKICAgICAgICAjCiAgICAgIHQgPSBuCiAgICAgIHQuaW5zdGFuY2VfZXZhbCB7IEB6ID0geiB9CiAgICAgIGNsYXNzIDw8IHQ7IGRlZiB0aW1lX3pvbmU7IEB6OyBlbmQ7IGVuZAogICAgICAgICMKICAgICAgICAjIGZha2UgYW4gQWN0aXZlU3VwcG9ydDo6VGltZVdpdGhab25lIGluc3RhbmNlIHdpdGggYSAjdGltZV96b25lCgogICAgICBlb3QgPSBFdE9yYmkubWFrZV90aW1lKHQpCgogICAgICBhc3NlcnQgZW90LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICBhc3NlcnQgZW90LnNlY29uZHMsIHQudG9fZgogICAgICBhc3NlcnQgZW90LnpvbmUsIHQudGltZV96b25lCiAgICBlbmQKCiAgICB0ZXN0ICdyZWplY3RzIGEgVGltZSBpbiBhIG5vbi1sb2NhbCBhbWJpZ3VvdXMgdGltZXpvbmUnIGRvCgogICAgICB0ID0gVGltZS5sb2NhbCgyMDE2LCAxMSwgMDEsIDEyLCAzMCwgOSkKICAgICAgY2xhc3MgPDwgdDsgZGVmIHpvbmU7ICdFQ1QnOyBlbmQ7IGVuZAoKICAgICAgaW5fem9uZSAnQXNpYS9UYmlsaXNpJyBkbwoKICAgICAgICBhc3NlcnRfZXJyb3IoCiAgICAgICAgICBsYW1iZGEgeyBFdE9yYmkubWFrZV90aW1lKHQpIH0sCiAgICAgICAgICBBcmd1bWVudEVycm9yLCAvXEFDYW5ub3QgZGV0ZXJtaW5lIHRpbWV6b25lIGZyb20gIkVDVCIvKQogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ3JlamVjdHMgdW5wYXJzYWJsZSBpbnB1dCcgZG8KCiAgICAgIGFzc2VydF9lcnJvcigKICAgICAgICBsYW1iZGEgeyBFdE9yYmkubWFrZV90aW1lKCd4eHgnKSB9LAogICAgICAgIEFyZ3VtZW50RXJyb3IsICdObyB0aW1lIGluZm9ybWF0aW9uIGluICJ4eHgiJykKICAgICAgICAgICMKICAgICAgICAgICMgc3RyYWlnaHQgb3V0IG9mIFRpbWUucGFyc2UoKQoKICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgIGxhbWJkYSB7IEV0T3JiaS5tYWtlX3RpbWUoT2JqZWN0Lm5ldykgfSwKICAgICAgICBBcmd1bWVudEVycm9yLCAvXEFDYW5ub3QgdHVybiAvKQogICAgZW5kCiAgZW5kCgogIGdyb3VwICcud2luZG93c196b25lX25hbWUnIGRvCgogICAgeyBbICdBc2lhL1Rva3lvJywgJzIwMTgtMDUtMjMnIF0gPT4gJ0pTVC05JywKICAgICAgWyAnQXNpYS9Lb2xrYXRhJywgJzIwMTgtMDctMDEnIF0gPT4gJ0lTVC01OjMwJywKICAgICAgWyAnQXNpYS9UZWhyYW4nLCAnMjAxOS0wMS0wOScgXSA9PiAnX0FULTM6MzBBc2lhL1RlaHJhbicsICMnSVJULTM6MzAnLAogICAgICBbICdBc2lhL1RlaHJhbicsICcyMDE5LTA3LTA5JyBdID0+ICdfQVQtNDozMEFzaWEvVGVocmFuJywgIydJUlQtNDozMCcsCiAgICAgIFsgJ0FzaWEvVGJpbGlzaScsICcyMDE5LTAxLTA5JyBdID0+ICdfQVQtNDowMEFzaWEvVGJpbGlzaScsCiAgICAgIFsgJ0FzaWEvU2FtYXJrYW5kJywgJzIwMTctMTEtMDEnIF0gPT4gJ19BUy01OjAwQXNpYS9TYW1hcmthbmQnLAogICAgICBbICdFdXJvcGUvQmVybGluJywgJzIwMTgtMDEtMDEnIF0gPT4gJ0NFVC0xQ0VTVCcsCiAgICAgIFsgJ0V1cm9wZS9CZXJsaW4nLCAnMjAxOC0wNy0wMScgXSA9PiAnQ0VULTJDRVNUJywKICAgICAgWyAnQW1lcmljYS9OZXdfWW9yaycsICcyMDE4LTAxLTAxJyBdID0+ICdFU1Q1RURUJywKICAgICAgWyAnQW1lcmljYS9OZXdfWW9yaycsICcyMDE4LTA3LTAxJyBdID0+ICdFU1Q0RURUJywKICAgICAgWyAnQW1lcmljYS9Mb3NfQW5nZWxlcycsICcyMDE3LTEwLTMwJyBdID0+ICdQU1Q3UERUJywKICAgICAgWyAnQW1lcmljYS9Mb3NfQW5nZWxlcycsICcyMDE5LTAxLTAxJyBdID0+ICdQU1Q4UERUJywKCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMDctMjgnIF0gPT4gJ0VFVC0zRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTAtMjgnIF0gPT4gJ0VFVC0zRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTItMjgnIF0gPT4gJ0VFVC0yRUVTVCcsCgogICAgfS5lYWNoIGRvIHwoem9uZSwgdGltZSksIHZ8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7di5pbnNwZWN0fSBmb3IgI3t6b25lLmluc3BlY3R9IGF0ICN7dGltZX0iIGRvCgogICAgICAgIGFzc2VydCBFdE9yYmkud2luZG93c196b25lX25hbWUoem9uZSwgVGltZS5wYXJzZSh0aW1lKSksIHYKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy56b25lX2FiYnJldmlhdGlvbicgZG8KCiAgICB7IFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMDctMjgnIF0gPT4gJ0VFU1QnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4JyBdID0+ICdFRVNUJywKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0xMi0yOCcgXSA9PiAnRUVUJywKCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMDMtMjUgMDI6MDAnIF0gPT4gJ0VFVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMDMtMjUgMDM6MDAnIF0gPT4gJ0VFU1QnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTAzLTI1IDA0OjAwJyBdID0+ICdFRVNUJywKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0wMy0yNSAwNTowMCcgXSA9PiAnRUVTVCcsCgogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4IDAwOjAwJyBdID0+ICdFRVNUJywKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0xMC0yOCAwMjo1OScgXSA9PiAnRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTAtMjggMDM6MDAnIF0gPT4gJ0VFVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTAtMjggMDM6MzAnIF0gPT4gJ0VFVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTAtMjggMDQ6MzAnIF0gPT4gJ0VFVCcsCgogICAgICAjIGluIGNhc2Ugb2YgYW1iaWd1aXR5LCAuem9uZV9hYmJyZXZpYXRpb24gZ29lcyAyIGhvdXJzIGZvcndhcmQuLi4KCiAgICB9LmVhY2ggZG8gfCh6b25lLCB0aW1lKSwgdnwKCiAgICAgIHRlc3QgInJldHVybnMgI3t2Lmluc3BlY3R9IGZvciAje3pvbmUuaW5zcGVjdH0gYXQgI3t0aW1lfSIgZG8KCiAgICAgICAgYXNzZXJ0IEV0T3JiaS56b25lX2FiYnJldmlhdGlvbih6b25lLCBUaW1lLnBhcnNlKHRpbWUpKSwgdgogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnLnR3ZWFrX3pvbmVfbmFtZScgZG8KCiAgICB7ICdFU1Q1RURUJyA9PiAnRVNUNUVEVCcsCiAgICAgICdVVEMrMTInID0+ICdFdGMvR01ULTEyJywKICAgICAgJ0tvcmVhIFN0YW5kYXJkIFRpbWUnID0+ICdBc2lhL1Nlb3VsJywKCiAgICB9LmVhY2ggZG8gfG4wLCBuMXwKCiAgICAgIHRlc3QgInR1cm5zICN7bjAuaW5zcGVjdH0gaW50byAje24xLmluc3BlY3R9IiBkbwoKICAgICAgICBhc3NlcnQgRXRPcmJpLnR3ZWFrX3pvbmVfbmFtZShuMCksIG4xCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcuY2hyb25pY19lbmFibGVkPycgZG8KCiAgICB0ZXN0ICdyZXR1cm5zIHRydWUgYnkgZGVmYXVsdCcgZG8KCiAgICAgIGFzc2VydCBFdE9yYmkuY2hyb25pY19lbmFibGVkPwogICAgZW5kCiAgZW5kCgogIGdyb3VwICcuY2hyb25pY19lbmFibGVkPScgZG8KCiAgICBhZnRlciBkbwogICAgICBFdE9yYmkuY2hyb25pY19lbmFibGVkID0gdHJ1ZQogICAgZW5kCgogICAgdGVzdCAnbGV0cyBkaXNhYmxlIGNocm9uaWMgcGFyc2luZycgZG8KCiAgICAgIEV0T3JiaS5jaHJvbmljX2VuYWJsZWQgPSBmYWxzZQoKICAgICAgYXNzZXJ0X2ZhbHNlIEV0T3JiaS5jaHJvbmljX2VuYWJsZWQ/CiAgICBlbmQKICBlbmQKCiMgIGdyb3VwICcuYWJicmV2aWF0ZV96b25lX25hbWUnIGRvCiMKIyAgICB7CiMKIyAgICAgICdFU1Q1RURUJyA9PiAnRVNUNUVEVCcsCiMgICAgICAnQlNUJyA9PiAld1sgR0ItRWlyZSBHQiBdLAojICAgICAgJ1VUQysxMicgPT4gbmlsLAojCiMgICAgfS5lYWNoIGRvIHxuMCwgbjF8CiMKIyAgICAgIG4xcyA9IG4xLmluc3BlY3QKIyAgICAgIG4xcyA9IG4xLmNvbGxlY3QoJjppbnNwZWN0KS5qb2luKCcgb3IgJykgaWYgbjEuaXNfYT8oQXJyYXkpCiMKIyAgICAgIHRlc3QgInR1cm5zICN7bjAuaW5zcGVjdH0gaW50byAje24xc30iIGRvCiMKIyAgICAgICAgaWYgbjEuaXNfYT8oQXJyYXkpCiMgICAgICAgICAgYXNzZXJ0IG4xKS50byBpbmNsdWRlKEV0T3JiaS5hYmJyZXZpYXRlX3pvbmVfbmFtZShuMCkpCiMgICAgICAgIGVsc2UKIyAgICAgICAgICBhc3NlcnQgRXRPcmJpLmFiYnJldmlhdGVfem9uZV9uYW1lKG4wKSwgbjEpCiMgICAgICAgIGVuZAojICAgICAgZW5kCiMgICAgZW5kCiMgIGVuZAplbmQKCg== \ No newline at end of file From 21261014cbba9d1d333625cd0f280b65589fad94 Mon Sep 17 00:00:00 2001 From: Oskar Eichler <62393985+OskarEichler@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:42:47 +0300 Subject: [PATCH 3/5] Store plain-text regression test for et-orbi PR #52 --- test/module_test.rb | 1020 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1019 insertions(+), 1 deletion(-) diff --git a/test/module_test.rb b/test/module_test.rb index b0c6926..1855e9e 100644 --- a/test/module_test.rb +++ b/test/module_test.rb @@ -1 +1,1019 @@ -IyBlbmNvZGluZzogVVRGLTgKCiMKIyBUZXN0aW5nIEV0T3JiaQojCiMgV2VkIE1hciAxMSAyMToxNzozNiBKU1QgMjAxNSwgcXVhdHJlIGFucy4uLiAocnVmdXMtc2NoZWR1bGVyKQojIFN1biBNYXIgMTkgMDU6MTY6MjggSlNUIDIwMTcKIyBGcmkgTWFyIDI0IDA0OjU1OjI1IEpTVCAyMDE3IOWck+OBleOCk+OBruWutgojCgoKZ3JvdXAgRXRPcmJpIGRvCgogIGJlZm9yZSBkbwoKICAgIEVOVlsnVFonXSA9IEVOVlsnX1RaJ10KICAgIFRpbWUuX3pvbmUgPSBuaWwKICAgIEV0T3JiaS5fb3Nfem9uZSA9IG5pbAogICAgVGltZS5hY3RpdmVfc3VwcG9ydF96b25lID0gbmlsCiAgZW5kCgogIGdyb3VwICcubGlzdF9pc284NjAxX3pvbmVzJyBkbwoKICAgIFsKCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDktMDEnLCAld1sgLTAxIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0wMTowMCcsICV3WyAtMDE6MDAgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5IC0wMScsICV3WyAtMDEgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5IC0wMTowMCcsICV3WyAtMDE6MDAgXSBdLAoKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0wMTozMCcsICV3WyAtMDE6MzAgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5IC0wMTozMCcsICV3WyAtMDE6MzAgXSBdLAoKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsIFtdIF0sCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDktMjUnLCBbXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTI1OjAwJywgW10gXSwKCiAgICBdLmVhY2ggZG8gfHN0cmluZywgem9uZXN8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7em9uZXMuaW5zcGVjdH0gZm9yICN7c3RyaW5nLmluc3BlY3R9IiBkbwoKICAgICAgICBhc3NlcnQgRXRPcmJpLmxpc3RfaXNvODYwMV96b25lcyhzdHJpbmcpLCB6b25lcwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKIyAgZ3JvdXAgJy5saXN0X29sc29uX3pvbmVzJyBkbwojCiMgICAgWwojCiMgICAgICBbICcxMS8wOS8yMDAyIEFtZXJpY2EvTmV3X1lvcmsnLAojICAgICAgICAld1sgQW1lcmljYS9OZXdfWW9yayBdIF0sCiMgICAgICBbICcxMS8wOS8yMDAyIEFtZXJpY2EvTmV3X1lvcmsgQXNpYS9TaGFuZ2hhaScsCiMgICAgICAgICV3WyBBbWVyaWNhL05ld19Zb3JrIEFzaWEvU2hhbmdoYWkgXSBdLAojICAgICAgWyAnQW1lcmljYS9OZXdfWW9yayBBc2lhL1NoYW5naGFpJywKIyAgICAgICAgJXdbIEFtZXJpY2EvTmV3X1lvcmsgQXNpYS9TaGFuZ2hhaSBdIF0sCiMgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4IEV0Yy9HTVQtMTEnLAojICAgICAgICAld1sgRXRjL0dNVC0xMSBdIF0sCiMKIyAgICAgIFsgJzIwMTgtMDktMDQgMDc6NTQ6NTggVVRDKzExJywKIyAgICAgICAgJXdbIFVUQysxMSBdIF0sCiMgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4IFVUQysxMSBFdGMvR01ULTExJywKIyAgICAgICAgJXdbIEV0Yy9HTVQtMTEgVVRDKzExIF0gXSwKIyAgICAgICAgICAjCiMgICAgICAgICAgIyBodHRwczovL2dpdGh1Yi5jb20vZmxvcmFpc29uL2Z1Z2l0L2lzc3Vlcy85CiMKIyAgICAgIFsgJzExLzA5LzIwMDIgMnV0b3BpYU5hZGE/M05hZGEnLAojICAgICAgICBbXSBdCiMKIyAgICBdLmVhY2ggZG8gfHN0cmluZywgem9uZXN8CiMKIyAgICAgIHRlc3QgInJldHVybnMgI3t6b25lcy5pbnNwZWN0fSBmb3IgI3tzdHJpbmcuaW5zcGVjdH0iIGRvCiMKIyAgICAgICAgYXNzZXJ0IEV0T3JiaS5saXN0X29sc29uX3pvbmVzKHN0cmluZyksIHpvbmVzKQojICAgICAgZW5kCiMgICAgZW5kCiMgIGVuZAoKICBncm91cCAnLmV4dHJhY3Rfem9uZScgZG8KCiAgICBbCgogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTAxJywgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsICctMDEnIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0wMTowMCcsIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDknLCAnLTAxOjAwJyBdIF0sCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDkgLTAxJywgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsICctMDEnIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOSAtMDE6MDAnLCBbICcyMDE2LTExLTAxIDEyOjMwOjA5JywgJy0wMTowMCcgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTAxOjMwJywgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsICctMDE6MzAnIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOSAtMDE6MzAnLCBbICcyMDE2LTExLTAxIDEyOjMwOjA5JywgJy0wMTozMCcgXSBdLAoKICAgICAgWyAnMTEvMDkvMjAwMiBBbWVyaWNhL05ld19Zb3JrJywKICAgICAgICBbICcxMS8wOS8yMDAyJywgJ0FtZXJpY2EvTmV3X1lvcmsnIF0gXSwKICAgICAgWyAnMTEvMDkvMjAwMiBBbWVyaWNhL05ld19Zb3JrIEFzaWEvU2hhbmdoYWknLAogICAgICAgIFsgJzExLzA5LzIwMDInLCAnQXNpYS9TaGFuZ2hhaScgXSBdLAogICAgICBbICdBbWVyaWNhL05ld19Zb3JrIEFzaWEvU2hhbmdoYWknLAogICAgICAgIFsgJycsICdBc2lhL1NoYW5naGFpJyBdIF0sCiAgICAgIFsgJzIwMTgtMDktMDQgMDc6NTQ6NTggRXRjL0dNVC0xMScsCiAgICAgICAgWyAnMjAxOC0wOS0wNCAwNzo1NDo1OCcsICdFdGMvR01ULTExJyBdIF0sCiAgICAgIFsgJzIwMTgtMDktMDQgMDc6NTQ6NTggVVRDKzExJywKICAgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4JywgJ1VUQysxMScgXSBdLAogICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4IFVUQysxMSBFdGMvR01ULTExJywKICAgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4JywgJ0V0Yy9HTVQtMTEnIF0gXSwKCiAgICAgIFsgJ1N1biBOb3YgMTggMTY6MDE6MDAgQXNpYS9TaW5nYXBvcmUgMjAxMicsCiAgICAgICAgWyAnU3VuIE5vdiAxOCAxNjowMTowMCAgMjAxMicsICdBc2lhL1NpbmdhcG9yZScgXSBdLAoKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDknLCBuaWwgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTI1JywgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0yNScsIG5pbCBdIF0sCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDktMjU6MDAnLCBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTI1OjAwJywgbmlsIF0gXSwKCiAgICAgIFsgJzExLzA5LzIwMDIgMnV0b3BpYU5hZGE/M05hZGEnLAogICAgICAgIFsgJzExLzA5LzIwMDIgMnV0b3BpYU5hZGE/M05hZGEnLCBuaWwgXSBdLAoKICAgICAgWyAnMjAxMi0xMC0yOCAwMzowMDowMCBFRVQnLAogICAgICAgIFsgJzIwMTItMTAtMjggMDM6MDA6MDAnLCAnRUVUJyBdIF0sCiAgICAgICNbICcyMDEyLTEwLTI4IDAzOjAwOjAwIEVFU1QnLAogICAgICAjICBbICcyMDEyLTEwLTI4IDAzOjAwOjAwJywgJ0VFU1QnIF0gXSwKICAgICAgWyAnMjAxMi0xMC0yOCAwMzowMDowMCBFdXJvcGUvVGFsbGlubicsCiAgICAgICAgWyAnMjAxMi0xMC0yOCAwMzowMDowMCcsICdFdXJvcGUvVGFsbGlubicgXSBdLAoKICAgIF0uZWFjaCB7IHxzdHIwLCAoc3RyMSwgem9uZSl8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7WyBzdHIxLCB6b25lIF0uaW5zcGVjdH0gZm9yICN7c3RyMC5pbnNwZWN0fSIgZG8KCiAgICAgICAgczEsIHogPSBFdE9yYmkuZXh0cmFjdF96b25lKHN0cjApCgogICAgICAgIGFzc2VydCBbIHMxLCB6IF0sIFsgc3RyMSwgem9uZSBdCiAgICAgIGVuZAogICAgfQogIGVuZAoKICBncm91cCAnLnBhcnNlJyBkbwoKICAgIHRlc3QgJ3BhcnNlcyBhIHRpbWUgc3RyaW5nIHdpdGhvdXQgYSB0aW1lem9uZScgZG8KCiAgICAgIG90ID0gaW5fem9uZSgnRXVyb3BlL01vc2NvdycpIHsgRXRPcmJpLnBhcnNlKCcyMDE1LzAzLzA4IDAxOjU5OjU5JykgfQoKICAgICAgdCA9IG90CiAgICAgIHUgPSBvdC51dGMKCiAgICAgIGFzc2VydCB0LnRvX2ksIDE0MjU3NjkxOTkKICAgICAgYXNzZXJ0IHUudG9faSwgMTQyNTc2OTE5OQoKICAgICAgYXNzZXJ0KAogICAgICAgIHQuc3RyZnRpbWUoJyVZLyVtLyVkICVIOiVNOiVTICVaICV6JykgKyAiICN7dC5pc2RzdH0iLAogICAgICAgICcyMDE1LzAzLzA4IDAxOjU5OjU5IE1TSyArMDMwMCBmYWxzZScpCgogICAgICBhc3NlcnQgdS50b19kZWJ1Z19zLCAndCAyMDE1LTAzLTA3IDIyOjU5OjU5ICswMDowMCBkc3Q6ZmFsc2UnCiAgICBlbmQKCiAgICB0ZXN0ICdwYXJzZXMgYSB0aW1lIHN0cmluZyB3aXRoIGEgZnVsbCBuYW1lIHRpbWV6b25lJyBkbwoKICAgICAgb3QgPSBFdE9yYmkucGFyc2UoJzIwMTUvMDMvMDggMDE6NTk6NTkgQW1lcmljYS9Mb3NfQW5nZWxlcycpCgogICAgICB0ID0gb3QKICAgICAgdSA9IG90LnV0YwoKICAgICAgYXNzZXJ0IHQudG9faSwgMTQyNTgwODc5OQogICAgICBhc3NlcnQgdS50b19pLCAxNDI1ODA4Nzk5CgogICAgICBhc3NlcnQgdC50b19kZWJ1Z19zLCAnb3QgMjAxNS0wMy0wOCAwMTo1OTo1OSAtMDg6MDAgZHN0OmZhbHNlJwogICAgICBhc3NlcnQgdS50b19kZWJ1Z19zLCAndCAyMDE1LTAzLTA4IDA5OjU5OjU5ICswMDowMCBkc3Q6ZmFsc2UnCiAgICBlbmQKCiAgICB0ZXN0ICdwYXJzZXMgYSB0aW1lIHN0cmluZyB3aXRoIGEgZGVsdGEgdGltZXpvbmUnIGRvCgogICAgICBvdCA9IGluX3pvbmUoJ0V1cm9wZS9CZXJsaW4nKSB7IEV0T3JiaS5wYXJzZSgnMjAxNS0xMi0xMyAxMjozMCAtMDIwMCcpIH0KCiAgICAgIHQgPSBvdAogICAgICB1ID0gb3QudXRjCgogICAgICBhc3NlcnQgdC50b19pLCAxNDUwMDE3MDAwCiAgICAgIGFzc2VydCB1LnRvX2ksIDE0NTAwMTcwMDAKCiAgICAgIGFzc2VydCB0LnRvX2RlYnVnX3MsICdvdCAyMDE1LTEyLTEzIDEyOjMwOjAwIC0wMjowMCBkc3Q6ZmFsc2UnCiAgICAgIGFzc2VydCB1LnRvX2RlYnVnX3MsICd0IDIwMTUtMTItMTMgMTQ6MzA6MDAgKzAwOjAwIGRzdDpmYWxzZScKICAgIGVuZAoKICAgIHRlc3QgJ3BhcnNlcyBhIHRpbWUgc3RyaW5nIHdpdGggYSBkZWx0YSAoOikgdGltZXpvbmUnIGRvCgogICAgICBvdCA9IGluX3pvbmUoJ0V1cm9wZS9CZXJsaW4nKSB7IEV0T3JiaS5wYXJzZSgnMjAxNS0xMi0xMyAxMjozMCAtMDI6MDAnKSB9CgogICAgICB0ID0gb3QKICAgICAgdSA9IG90LnV0YwoKICAgICAgYXNzZXJ0IHQudG9faSwgMTQ1MDAxNzAwMAogICAgICBhc3NlcnQgdS50b19pLCAxNDUwMDE3MDAwCgogICAgICBhc3NlcnQgdC50b19kZWJ1Z19zLCAnb3QgMjAxNS0xMi0xMyAxMjozMDowMCAtMDI6MDAgZHN0OmZhbHNlJwogICAgICBhc3NlcnQgdS50b19kZWJ1Z19zLCAndCAyMDE1LTEyLTEzIDE0OjMwOjAwICswMDowMCBkc3Q6ZmFsc2UnCiAgICBlbmQKCiAgICB0ZXN0ICd0YWtlcyB0aGUgbG9jYWwgVFogd2hlbiB0ZXN0IGRvZXMgbm90IGtub3cgdGhlIHRpbWV6b25lJyBkbwoKICAgICAgaW5fem9uZSAnRXVyb3BlL01vc2NvdycgZG8KCiAgICAgICAgb3QgPSBFdE9yYmkucGFyc2UoJzIwMTUvMDMvMDggMDE6NTk6NTkgTmFkYS9OYWRhJykKCiAgICAgICAgYXNzZXJ0X2luY2x1ZGUgb3Quem9uZS5uYW1lLCAld1sgRXVyb3BlL01vc2NvdyBFdXJvcGUvS2lyb3YgXQogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ3BhcnNlcyBldmVuIHdoZW4gdGhlIHR6IGlzIG91dCBvZiBwbGFjZScgZG8KCiAgICAgIGFzc2VydCgKICAgICAgICBFdE9yYmkucGFyc2UoJ1N1biBOb3YgMTggMTY6MDE6MDAgQXNpYS9TaW5nYXBvcmUgMjAxMicpCiAgICAgICAgICAudG9fZGVidWdfcywKICAgICAgICAib3QgMjAxMi0xMS0xOCAxNjowMTowMCArMDg6MDAgZHN0OmZhbHNlIikKICAgIGVuZAoKICAgIHRlc3QgJ2ZhaWxzIG9uIGludmFsaWQgc3RyaW5ncycgZG8KCiAgICAgIGFzc2VydF9lcnJvcigKICAgICAgICBsYW1iZGEgeyBFdE9yYmkucGFyc2UoJ3h4eCcpIH0sCiAgICAgICAgQXJndW1lbnRFcnJvciwgJ05vIHRpbWUgaW5mb3JtYXRpb24gaW4gInh4eCInKQogICAgZW5kCgogICAgdGVzdCAncGFyc2VzIGluIHRoZSBSYWlscy1wcm92aWRlZCBUaW1lLnpvbmUgKFVUQyknIGRvCgogICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnVVRDJwoKICAgICAgdCA9IEV0T3JiaS5wYXJzZSgnMjAxOS0wMS0wMSAxMjoxMCcpCgogICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgYXNzZXJ0IHQuem9uZSwgOjpUWkluZm86OlRpbWV6b25lLmdldCgnVVRDJykKICAgICAgYXNzZXJ0IHQudG9fcywgJzIwMTktMDEtMDEgMTI6MTA6MDAgWicKICAgICAgYXNzZXJ0IHQudG9fenMsICcyMDE5LTAxLTAxIDEyOjEwOjAwIFVUQycKICAgIGVuZAoKICAgIHRlc3QgJ3BhcnNlcyBpbiB0aGUgUmFpbHMtcHJvdmlkZWQgVGltZS56b25lIChBc2lhL1NoYW5naGFpKScgZG8KCiAgICAgIFRpbWUuYWN0aXZlX3N1cHBvcnRfem9uZSA9ICdBc2lhL1NoYW5naGFpJwoKICAgICAgdCA9IEV0T3JiaS5wYXJzZSgnMjAxOS0wMS0wMSAxMjoxMCcpCgogICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgYXNzZXJ0IHQuem9uZSwgOjpUWkluZm86OlRpbWV6b25lLmdldCgnQXNpYS9TaGFuZ2hhaScpCiAgICAgIGFzc2VydCB0LnRvX3MsICcyMDE5LTAxLTAxIDEyOjEwOjAwICswODAwJwogICAgICBhc3NlcnQgdC50b196cywgJzIwMTktMDEtMDEgMTI6MTA6MDAgQXNpYS9TaGFuZ2hhaScKICAgIGVuZAoKIyAgICAjIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfdGltZV96b25lX2FiYnJldmlhdGlvbnMKIyAgICAjIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfdHpfZGF0YWJhc2VfdGltZV96b25lcwojCiMgICAgY29udGV4dCAnVFogYWJicmV2aWF0aW9ucycgZG8KIwojICAgICAgewojCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBBbWVyaWNhL0xvc19BbmdlbGVzJyA9PiAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDgwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBBbWVyaWNhL0xvc19BbmdlbGVzJyA9PiAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBQU1QnID0+ICAgICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDgwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBQRFQnID0+ICAgICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSAtMDc6MDAnID0+ICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSAtMDc6MDAnID0+ICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBHTVQtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBHTVQtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBVVEMtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBVVEMtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBFdGMvR01ULTcnID0+ICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBFdGMvR01ULTcnID0+ICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMKIyAgICAgICAgJzIwMjEtMDEtMDEgN0FNIEFDRFQnID0+ICcyMDIxLTAxLTAxIDA3OjAwOjAwICswOTMwJywKIyAgICAgICAgJzIwMjEtMDctMDEgN0FNIEFDU1QnID0+ICcyMDIxLTAxLTAxIDA3OjAwOjAwICsxMDMwJywKIwojICAgICAgfS5lYWNoIGRvIHxrLCB2fAojCiMgICAgICAgIHRlc3QgInBhcnNlcyAje2t9IHRvICN7dn0iIGRvCiMKIyAgICAgICAgICBvdCA9IEV0T3JiaS5wYXJzZShrKQojdCA9IFRpbWUucGFyc2UoaykKI3AgWyBrIF0KI3AgWyBEYXRlLl9wYXJzZShrKSBdCiNwIFsgdC50b19zLCB0LnpvbmUsIDp0IF0KI3AgWyBvdC50b19zLCBvdC56b25lLCA6b3QgXQojcCBbIHYgXQojICAjcCBbIGssIHYsIG90IF0KIwojICAgICAgICAgIGFzc2VydCBvdC50b19zLCB2KQojICAgICAgICBlbmQKIyAgICAgIGVuZAojICAgIGVuZAoKICAgIGdyb3VwICd3aGVuIENocm9uaWMgaXMgZGVmaW5lZCcgZG8KCiAgICAgIGJlZm9yZSBkbwogICAgICAgIHJlcXVpcmVfY2hyb25pYwogICAgICBlbmQKICAgICAgYWZ0ZXIgZG8KICAgICAgICB1bnJlcXVpcmVfY2hyb25pYwogICAgICBlbmQKCiAgICAgIHRlc3QgJ2xldmVyYWdlcyBpdCcgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cgYXQgMXBtJykKICAgICAgICB0MSA9IFRpbWUubm93ICsgMjQgKiAzNjAwCgogICAgICAgIGFzc2VydCB0LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclRiAlVCcpLCB0MS5zdHJmdGltZSgnJUYgMTM6MDA6MDAnKQogICAgICBlbmQKCiAgICAgIHRlc3QgJ2xldmVyYWdlcyB0ZXN0IGluIGEgc3BlY2lmaWVkIHRpbWUgem9uZScgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cgYXQgMjI6MDAgQW1lcmljYS9OZXdfWW9yaycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC50b196cywgdDEuc3RyZnRpbWUoJyVGIDIyOjAwOjAwIEFtZXJpY2EvTmV3X1lvcmsnKQogICAgICBlbmQKCiAgICAgIHRlc3QgJ3BpY2tzIHRoZSBSYWlscyBUaW1lLnpvbmUgKFVUQykgaWYgYXZhaWxhYmxlJyBkbwoKICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnVVRDJwoKICAgICAgICB0ID0gRXRPcmJpLnBhcnNlKCd0b21vcnJvdycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC56b25lLm5hbWUsICdVVEMnCiAgICAgICAgYXNzZXJ0IHQuc3RyZnRpbWUoJyVZLSVtLSVkJyksIHQxLnN0cmZ0aW1lKCclWS0lbS0lZCcpCiAgICAgICAgYXNzZXJ0IHQuc3RyZnRpbWUoJyVIOiVNOiVTJyksICcxMjowMDowMCcKICAgICAgZW5kCgogICAgICB0ZXN0ICdwaWNrcyB0aGUgUmFpbHMgVGltZS56b25lIChBc2lhL1NoYW5naGFpKSBpZiBhdmFpbGFibGUnIGRvCgogICAgICAgIFRpbWUuYWN0aXZlX3N1cHBvcnRfem9uZSA9ICdBc2lhL1NoYW5naGFpJwoKICAgICAgICB0ID0gRXRPcmJpLnBhcnNlKCd0b21vcnJvdycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC56b25lLm5hbWUsICdBc2lhL1NoYW5naGFpJwogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclWS0lbS0lZCcpLCB0MS5zdHJmdGltZSgnJVktJW0tJWQnKQogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclSDolTTolUycpLCAnMTI6MDA6MDAnCiAgICAgIGVuZAoKICAgICAgZ3JvdXAgJ3doZW4gcGFyc2UoeCwgZW5hYmxlX2Nocm9uaWM6IGZhbHNlKScgZG8KCiAgICAgICAgdGVzdCAnZG9lcyBub3QgcHJlLXBhcnNlIHdpdGggQ2hyb25pYycgZG8KCiAgICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnQW1lcmljYS9DaGljYWdvJwoKICAgICAgICAgIGFzc2VydCggIyB3b3JrcwogICAgICAgICAgICBFdE9yYmkucGFyc2UoJ3RvbW9ycm93JykuY2xhc3MsCiAgICAgICAgICAgIEV0T3JiaTo6RW9UaW1lKQoKICAgICAgICAgIGFzc2VydF9lcnJvciggIyBkb2Vzbid0IHdvcmsKICAgICAgICAgICAgbGFtYmRhIHsgRXRPcmJpLnBhcnNlKCd0b21vcnJvdycsIGVuYWJsZV9jaHJvbmljOiBmYWxzZSkgfSwKICAgICAgICAgICAgQXJndW1lbnRFcnJvciwgJ05vIHRpbWUgaW5mb3JtYXRpb24gaW4gInRvbW9ycm93IicpCiAgICAgICAgZW5kCiAgICAgIGVuZAoKICAgICAgZ3JvdXAgJ2FuZCBFdC1PcmJpLmNocm9uaWNfZW5hYmxlZD8gaXMgZmFsc2UnIGRvCgogICAgICAgIGJlZm9yZSBkbwogICAgICAgICAgRXRPcmJpLmNocm9uaWNfZW5hYmxlZCA9IGZhbHNlCiAgICAgICAgZW5kCiAgICAgICAgYWZ0ZXIgZG8KICAgICAgICAgIEV0T3JiaS5jaHJvbmljX2VuYWJsZWQgPSB0cnVlCiAgICAgICAgZW5kCgogICAgICAgIHRlc3QgJ2RvZXMgbm90IHByZS1wYXJzZSB3aXRoIENocm9uaWMnIGRvCgogICAgICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgICAgICBsYW1iZGEgeyBFdE9yYmkucGFyc2UoJ3RvbW9ycm93JykgfSwKICAgICAgICAgICAgQXJndW1lbnRFcnJvciwgJ05vIHRpbWUgaW5mb3JtYXRpb24gaW4gInRvbW9ycm93IicpCiAgICAgICAgZW5kCgogICAgICAgIHRlc3QgJ3ByZS1wYXJzZXMgd2l0aCBDaHJvbmljIHdoZW4gZW5hYmxlX2Nocm9uaWM6IHRydWUnIGRvCgogICAgICAgICAgYXNzZXJ0X25vdF9lcnJvciB7IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cnLCBlbmFibGVfY2hyb25pYzogdHJ1ZSkgfQogICAgICAgIGVuZAogICAgICBlbmQKCiAgICAgIHRlc3QgJ2ZpbHRlcnMgb3B0aW9ucyBnaXZlbiB0byBDaHJvbmljJyBkbwoKICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnVVRDJwoKICAgICAgICAjYXNzZXJ0IDo6S2hyb25pYykKICAgICAgICAjICAudG8gcmVjZWl2ZSg6cGFyc2UpCiAgICAgICAgIyAgLndpdGgoJ3RvbW9ycm93JywgeyBjb250ZXh0OiA6ZnV0dXJlIH0pCiAgICAgICAgIyAgLmFuZF9jYWxsX29yaWdpbmFsCgogICAgICAgIHQgPSBFdE9yYmkucGFyc2UoCiAgICAgICAgICAndG9tb3Jyb3cnLCB6b25lOiA6OlRaSW5mbzo6VGltZXpvbmUuZ2V0KCdVVEMnKSwgY29udGV4dDogOmZ1dHVyZSkKICAgICAgICB0MSA9IFRpbWUubm93ICsgMjQgKiAzNjAwCgogICAgICAgIGFzc2VydCB0LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICAgIGFzc2VydCB0LnpvbmUubmFtZSwgJ1VUQycKICAgICAgICBhc3NlcnQgdC5zdHJmdGltZSgnJVktJW0tJWQnKSwgdDEuc3RyZnRpbWUoJyVZLSVtLSVkJykKICAgICAgICBhc3NlcnQgdC5zdHJmdGltZSgnJUg6JU06JVMnKSwgJzEyOjAwOjAwJwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnLmdldF90em9uZScgZG8KCiAgICB7CgogICAgICAnR0InID0+ICdHQicsCiAgICAgICdVVEMnID0+ICdVVEMnLAogICAgICAnR01UJyA9PiAnR01UJywKICAgICAgJ1p1bHUnID0+ICdadWx1JywKICAgICAgJ0phcGFuJyA9PiAnSmFwYW4nLAogICAgICAnVHVya2V5JyA9PiAnVHVya2V5JywKICAgICAgJ0FzaWEvVG9reW8nID0+ICdBc2lhL1Rva3lvJywKICAgICAgJ0V1cm9wZS9QYXJpcycgPT4gJ0V1cm9wZS9QYXJpcycsCiAgICAgICdFdXJvcGUvWnVyaWNoJyA9PiAnRXVyb3BlL1p1cmljaCcsCiAgICAgICdXLVNVJyA9PiAnVy1TVScsCgogICAgICAnWicgPT4gJ1VUQycsCgogICAgICAnKzA5OjAwJyA9PiAnKzA5OjAwJywKICAgICAgJy0wMTozMCcgPT4gJy0wMTozMCcsCgogICAgICAnX0FULTM6MzBBc2lhL1RlaHJhbicgPT4gJ0FzaWEvVGVocmFuJywKICAgICAgJ19BVC00QXNpYS9UYmlsaXNpJyA9PiAnQXNpYS9UYmlsaXNpJywKCiAgICAgICcrMDg6MDAnID0+ICcrMDg6MDAnLAogICAgICAnKzA4MDAnID0+ICcrMDgwMCcsICMgbm8gbm9ybWFsaXphdGlvbiB0byAiKzA4OjAwIgoKICAgICAgJy0wMScgPT4gJy0wMScsCgogICAgICAzNjAwID0+ICcrMDE6MDAnLAoKICAgICAgJ1Rva3lvIFN0YW5kYXJkIFRpbWUnID0+ICdBc2lhL1Rva3lvJywKICAgICAgJ0Nvb3JkaW5hdGVkIFVuaXZlcnNhbCBUaW1lJyA9PiAnVVRDJywKCiAgICAgICdFYXN0ZXJuIFN0YW5kYXJkIFRpbWUnID0+ICdBbWVyaWNhL05ld19Zb3JrJywKICAgICAgJ0Vhc3Rlcm4gRGF5bGlnaHQgVGltZScgPT4gJ0FtZXJpY2EvTmV3X1lvcmsnLAoKICAgICAgJ0NTVCswODAwJyA9PiAnQXNpYS9DaG9uZ3FpbmcnLAogICAgICAnQ1NUKzA4OjAwJyA9PiAnQXNpYS9DaG9uZ3FpbmcnLAogICAgICAnQ1NUKzA4JyA9PiAnQXNpYS9DaG9uZ3FpbmcnLAogICAgICAnQ1NUKzgnID0+ICdBc2lhL0Nob25ncWluZycsCiAgICAgICdFRFQtMDQwMCcgPT4gJ0FtZXJpY2EvRGV0cm9pdCcsCgogICAgICAnV0VULTFXRVNUJyA9PiAnV0VUJywKICAgICAgJ0NFVC0yQ0VTVCcgPT4gJ0NFVCcsCgogICAgfS5lYWNoIGRvIHxhLCBifAoKICAgICAgdGVzdCAicmV0dXJucyAje2IuaW5zcGVjdH0gZm9yICN7YS5pbnNwZWN0fSIgZG8KCiAgICAgICAgeiA9IEV0T3JiaS5nZXRfdHpvbmUoYSkKCiAgICAgICAgYXNzZXJ0X25vdF9uaWwgegogICAgICAgIGFzc2VydCB6Lm5hbWUsIGIKICAgICAgZW5kCiAgICBlbmQKCiAgICB0ZXN0ICdoYW5kbGVzIGZyYWN0aW9uYWwtaG91ciBvZmZzZXRzJyBkbwoKICAgICAgYXNzZXJ0IEV0T3JiaS5nZXRfdHpvbmUoMTlfODAwKS5uYW1lLCAnKzA1OjMwJwogICAgICBhc3NlcnQgRXRPcmJpLmdldF90em9uZSgtMV84MDApLm5hbWUsICctMDA6MzAnCiAgICAgIGFzc2VydCBFdE9yYmkuZ2V0X3R6b25lKCctMDA6MzAnKS5uYW1lLCAnLTAwOjMwJwoKICAgICAgYXNzZXJ0KAogICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldygwLCAxOV84MDApLnRvX2RlYnVnX3MsCiAgICAgICAgJ290IDE5NzAtMDEtMDEgMDU6MzA6MDAgKzA1OjMwIGRzdDpmYWxzZScpCiAgICBlbmQKCiMgICAgdGVzdCAncmV0dXJucyBhIHRpbWV6b25lIGZvciB3ZWxsLWtub3duIGFiYnJldmlhdGlvbnMnIGRvCiMKIyAgICAgIGFzc2VydCBndHooJ0pTVCcpLCAnSmFwYW4nCiMgICAgICBhc3NlcnQgZ3R6KCdQU1QnKSwgJ0FtZXJpY2EvRGF3c29uJwojICAgICAgYXNzZXJ0IGd0eignQ0VTVCcpLCAnQWZyaWNhL0NldXRhJwojICAgIGVuZAoKICAgIFsKICAgICAgJ0FzaWEvUGFyaXMnLCAnTmFkYS9OYWRhJywgJzcnLCAnMDYnLCAnc3VuIzMnLCAnTWF6ZGEgWm9vbSBab29tIFN0YWRpdW0nCiAgICBdLmVhY2ggZG8gfHN8CgogICAgICB0ZXN0ICJyZXR1cm5zIG5pbCBmb3IgI3tzLmluc3BlY3R9IiBkbwoKICAgICAgICBhc3NlcnRfbmlsIEV0T3JiaS5nZXRfdHpvbmUocykKICAgICAgZW5kCiAgICBlbmQKCiAgICAjIHJ1ZnVzLXNjaGVkdWxlciBnaC0yMjIKICAgICMKICAgIHRlc3QgImZhbGxzIGJhY2sgdG8gRU5WWydUWiddIGlmIHRlc3QgZG9lc24ndCBrbm93IFRpbWUubm93LnpvbmUiIGRvCgogICAgICBiZWdpbgoKICAgICAgICBjdXJyZW50ID0gRXRPcmJpLmdldF90em9uZSg6bG9jYWwpCgogICAgICAgIFRpbWUuX3pvbmUgPSAn5Lit5Zu95qCH5YeG5pe26Ze0JwoKICAgICAgICAjYXNzZXJ0X25pbCBFdE9yYmkuZ2V0X3R6b25lKDpjdXJyZW50KQogICAgICAgICNhc3NlcnQgRXRPcmJpLmdldF90em9uZSg6Y3VycmVudCksIEV0T3JiaS5nZXRfdHpvbmUoVGltZS5ub3cuem9uZSkKICAgICAgICAgICMKICAgICAgICAgICMgZ2gtMjQwIGludHJvZHVjZXMgYSB3YXkgb2YgZmluZGluZyB0aGUgdGltZXpvbmUgYnkgYXNraW5nIGRpcmVjdGx5CiAgICAgICAgICAjIHRvIHRoZSBzeXN0ZW0sIHNvIHRob3NlIGRvIHJldHVybiBhIHRpbWV6b25lLi4uCgogICAgICAgIGluX3pvbmUgJ0FzaWEvU2hhbmdoYWknIGRvCgogICAgICAgICAgYXNzZXJ0X2luY2x1ZGUoCiAgICAgICAgICAgIEV0T3JiaS5nZXRfdHpvbmUoOmxvY2FsKSwKICAgICAgICAgICAgWyBFdE9yYmkuZ2V0X3R6b25lKCdBc2lhL1NoYW5naGFpJyksCiAgICAgICAgICAgICAgRXRPcmJpLmdldF90em9uZSgnQXNpYS9DaG9uZ3FpbmcnKSBdKQogICAgICAgIGVuZAoKICAgICAgZW5zdXJlCgogICAgICAgIFRpbWUuX3pvbmUgPSBuaWwKICAgICAgZW5kCgogICAgICBhc3NlcnQgRXRPcmJpLmdldF90em9uZSg6bG9jYWwpLCBjdXJyZW50CiAgICBlbmQKCiAgICB7ICMgZm9yIHJ1ZnVzLXNjaGVkdWxlciBnaC0yMjgKCiAgICAgICdBc2lhL1Rva3lvJyA9PiAld1sKICAgICAgICBBc2lhL1Rva3lvIF0sCiAgICAgICdBc2lhL1NoYW5naGFpJyA9PiAld1sKICAgICAgICBBc2lhL1NoYW5naGFpIEFzaWEvQ2hvbmdxaW5nIF0sCiAgICAgICdFdXJvcGUvWnVyaWNoJyA9PiAld1sKICAgICAgICBFdXJvcGUvWnVyaWNoIEFmcmljYS9DZXV0YSB3aW5kb3dzOkNFVCBdLAogICAgICAnRXVyb3BlL0xvbmRvbicgPT4gJXdbCiAgICAgICAgRXVyb3BlL0xvbmRvbiBFdXJvcGUvQmVsZmFzdCB3aW5kb3dzOkdNVCB3aW5kb3dzOkdNVC0wIF0sCgogICAgfS5lYWNoIGRvIHx6b25lLCB0YXJnZXRzfAoKICAgICAgdGVzdCAicmV0dXJucyB0aGUgY3VycmVudCB0aW1lem9uZSBmb3IgOmN1cnJlbnQgaW4gI3t6b25lfSIgZG8KCiAgICAgICAgaW5fem9uZSh6b25lKSBkbwoKICAgICAgICAgIGFzc2VydF9pbmNsdWRlIEV0T3JiaS5nZXRfdHpvbmUoOmxvY2FsKSwgc2VsZWN0X3pvbmVzKHRhcmdldHMpCiAgICAgICAgZW5kCiAgICAgIGVuZAogICAgZW5kCgogICAgdGVzdCAiZG9lc24ndCBtaW5kIGJlaW5nIGdpdmVuIGEgVFpJbmZvOjpUaW1lem9uZSIgZG8KCiAgICAgIHR6ID0gOjpUWkluZm86OlRpbWV6b25lLmdldCgnWnVsdScpCiAgICAgIGNsYXNzIDw8IHR6CiAgICAgICAgZGVmIDw9Pih0eikKICAgICAgICAgICNyZXR1cm4gbmlsIHVubGVzcyB0ei5pc19hPyhUaW1lem9uZSkKICAgICAgICAgIGlkZW50aWZpZXIgPD0+IHR6LmlkZW50aWZpZXIKICAgICAgICBlbmQKICAgICAgZW5kCiAgICAgICAgIyBzaW11bGF0ZSB0emluZm8gMC4zLjUzIGlzc3VlCgogICAgICBhc3NlcnQgRXRPcmJpLmdldF90em9uZSh0eiksIDo6VFpJbmZvOjpUaW1lem9uZS5nZXQoJ1p1bHUnKQogICAgZW5kCiAgZW5kCgogIGdyb3VwICcuZGV0ZXJtaW5lX2xvY2FsX3R6b25lJyBkbwoKICAgIHRlc3QgJ2Zhdm91cnMgdGhlIGxvY2FsIHRpbWV6b25lJyBkbwoKICAgICAgaW5fem9uZSg6bm9fZW52X3R6KSBkbwoKICAgICAgICBUaW1lLl96b25lID0gJ0NhcGUgVmVyZGUgU3RhbmRhcmQgVGltZScKICAgICAgICBFdE9yYmkuX29zX3pvbmUgPSAnJyAjIGZvcmNlICNvc190eiB0byByZXR1cm4gbmlsCgogICAgICAgIGFzc2VydCBFdE9yYmkuZGV0ZXJtaW5lX2xvY2FsX3R6b25lLm5hbWUsICdBdGxhbnRpYy9DYXBlX1ZlcmRlJwogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ2Zhdm91cnMgdGhlIGxvY2FsIHRpbWV6b25lJyBkbwoKICAgICAgaW5fem9uZSgnRXVyb3BlL0JlcmxpbicpIGRvCgogICAgICAgIGFzc2VydF9pbmNsdWRlKAogICAgICAgICAgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZSwKICAgICAgICAgIHNlbGVjdF96b25lcygld1sgRXVyb3BlL0JlcmxpbiBBZnJpY2EvQ2V1dGEgd2luZG93czpDRVQgXSkpCiAgICAgIGVuZAogICAgZW5kCgogICAgdGVzdCAnZmF2b3VycyB0aGUgbG9jYWwgdGltZXpvbmUgKE1pdHRlbGV1cm9wYWVpc2NoZSBTb21tZXJ6ZWl0KScgZG8KCiAgICAgIGluX3pvbmUoOm5vX2Vudl90eikgZG8KCiAgICAgICAgVGltZS5fem9uZSA9ICJNaXR0ZWxldXJvcFx4RTRpc2NoZSBTb21tZXJ6ZWl0IgogICAgICAgIEV0T3JiaS5fb3Nfem9uZSA9ICcnICMgZm9yY2UgI29zX3R6IHRvIHJldHVybiBuaWwKCiAgICAgICAgYXNzZXJ0X25vdF9lcnJvciB7IEV0T3JiaS5kZXRlcm1pbmVfbG9jYWxfdHpvbmUgfQogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ3JldHVybnMgdGhlIGxvY2FsIHRpbWV6b25lJyBkbwoKICAgICAgaW5fem9uZSgnQW1lcmljYS9KYW1haWNhJykgZG8KCiAgICAgICAgYXNzZXJ0X2luY2x1ZGUoCiAgICAgICAgICBFdE9yYmkuZGV0ZXJtaW5lX2xvY2FsX3R6b25lLm5hbWUsCiAgICAgICAgICBbICdBbWVyaWNhL0phbWFpY2EnLCAnQW1lcmljYS9BdGlrb2thbicsICdFU1QnIF0pCiAgICAgIGVuZAogICAgZW5kCgogICAgdGVzdCAncmV0dXJucyB0aGUgUmFpbHMtcHJvdmlkZWQgVGltZS56b25lLnR6aW5mbyBpZiBhdmFpbGFibGUnIGRvCgogICAgICAjIGh0dHA6Ly9hcGkucnVieW9ucmFpbHMub3JnL2NsYXNzZXMvQWN0aXZlU3VwcG9ydC9UaW1lWm9uZS5odG1sCgogICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnRXVyb3BlL1ZpbG5pdXMnCgogICAgICBpbl96b25lKDpub19lbnZfdHopIGRvCiAgICAgICAgYXNzZXJ0IEV0T3JiaS5kZXRlcm1pbmVfbG9jYWxfdHpvbmUuY2xhc3MsIDo6VFpJbmZvOjpEYXRhVGltZXpvbmUKICAgICAgICBhc3NlcnQgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZS5uYW1lLCAnRXVyb3BlL1ZpbG5pdXMnCiAgICAgIGVuZAogICAgZW5kCgogICAgdGVzdCAiZ2l2ZXMgcHJlY2VkZW5jZSB0byBFTlZbJ1RaJ10gb3ZlciBSYWlscyBUaW1lLnpvbmUudHppbmZvIiBkbwoKICAgICAgIyBodHRwOi8vYXBpLnJ1YnlvbnJhaWxzLm9yZy9jbGFzc2VzL0FjdGl2ZVN1cHBvcnQvVGltZVpvbmUuaHRtbAoKICAgICAgVGltZS5hY3RpdmVfc3VwcG9ydF96b25lID0gJ0V1cm9wZS9WaWxuaXVzJwoKICAgICAgaW5fem9uZSgnQXNpYS9UZWhyYW4nKSBkbwoKICAgICAgICBhc3NlcnQgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZS5jbGFzcywgOjpUWkluZm86OkRhdGFUaW1lem9uZQogICAgICAgIGFzc2VydCBFdE9yYmkuZGV0ZXJtaW5lX2xvY2FsX3R6b25lLm5hbWUsICdBc2lhL1RlaHJhbicKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy56b25lJyBkbwoKICAgIHRlc3QgJ2lzIGFuIGFsaWFzIHRvIC5kZXRlcm1pbmVfbG9jYWxfdHpvbmUnIGRvCgogICAgICBpbl96b25lKDpub19lbnZfdHopIGRvCgogICAgICAgIFRpbWUuX3pvbmUgPSAnQ2FwZSBWZXJkZSBTdGFuZGFyZCBUaW1lJwogICAgICAgIEV0T3JiaS5fb3Nfem9uZSA9ICcnICMgZm9yY2UgI29zX3R6IHRvIHJldHVybiBuaWwKCiAgICAgICAgYXNzZXJ0IEV0T3JiaS56b25lLm5hbWUsICdBdGxhbnRpYy9DYXBlX1ZlcmRlJwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnLm5vdycgZG8KCiAgICB0ZXN0ICdyZXR1cm5zIGEgY3VycmVudCwgbG9jYWwgRW9UaW1lIGluc3RhbmNlJyBkbwoKICAgICAgaW5fem9uZSAnQXNpYS9TaGFuZ2hhaScgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5ub3cKICAgICAgICBuID0gVGltZS5ub3cKCiAgICAgICAgYXNzZXJ0IHQuc2Vjb25kcyA+IChuIC0gMSkudG9fZgogICAgICAgIGFzc2VydCB0LnNlY29uZHMgPCAobiArIDEpLnRvX2YKCiAgICAgICAgYXNzZXJ0X2luY2x1ZGUgdC56b25lLm5hbWUsIFsgJ0FzaWEvU2hhbmdoYWknLCAnQXNpYS9DaG9uZ3FpbmcnIF0KICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy5tYWtlX3RpbWUnIGRvCgogICAgdGVzdCAncmV0dXJucyBhbiBFb1RpbWUgaW5zdGFuY2UgYXMgaXMnIGRvCgogICAgICB0MCA9IEV0T3JiaS5wYXJzZSgnMjAxNy0wMy0yMSAxMjowMDozNCBBc2lhL1VsYW5fQmF0b3InKQogICAgICB0MSA9IEV0T3JiaS5tYWtlX3RpbWUodDApCgogICAgICBhc3NlcnQgdDEuY2xhc3MsIDo6RXRPcmJpOjpFb1RpbWUKICAgICAgYXNzZXJ0IHQxLCB0MAogICAgICBhc3NlcnQgdDEub2JqZWN0X2lkLCB0MC5vYmplY3RfaWQKICAgIGVuZAoKICAgIHRlc3QgJ3JldHVybnMgYW4gRW9UaW1lIGluc3RhbmNlIGFzIGlzJyBkbwoKICAgICAgdDAgPSBFdE9yYmkucGFyc2UoJzIwMTctMDMtMjEgMTI6MDA6MzQgQXNpYS9VbGFuX0JhdG9yJykKICAgICAgdDEgPSBFdE9yYmkubWFrZV90aW1lKHQwLCB0MC56b25lKQoKICAgICAgYXNzZXJ0IHQxLmNsYXNzLCA6OkV0T3JiaTo6RW9UaW1lCiAgICAgIGFzc2VydCB0MSwgdDAKICAgICAgYXNzZXJ0IHQxLm9iamVjdF9pZCwgdDAub2JqZWN0X2lkCiAgICBlbmQKCiAgICBbCiAgICAgIFsgJ2FuIEVvVGltZSBpbnN0YW5jZScsCiAgICAgICAgbmlsLAogICAgICAgIGxhbWJkYSB7IEV0T3JiaS5wYXJzZSgnMjAxNy0wMy0yMSAxMjowMDozNCBBc2lhL1VsYW5fQmF0b3InKSB9LAogICAgICAgICdvdCAyMDE3LTAzLTIxIDEyOjAwOjM0ICswODowMCBkc3Q6ZmFsc2UnIF0sCgogICAgICBbICdhIGxvY2FsIHRpbWUnLAogICAgICAgICdBc2lhL1RiaWxpc2knLAogICAgICAgIFRpbWUucGFyc2UoJzIwMTYtMTEtMDEgMTI6MzA6MDkgKzA0OjAwJyksICMgdXNlIGN1c3RvbSB0eiA6LSgKICAgICAgICAnb3QgMjAxNi0xMS0wMSAxMjozMDowOSArMDQ6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYW4gVVRDIHRpbWUnLAogICAgICAgIG5pbCwKICAgICAgICBUaW1lLnV0YygyMDE2LCAxMSwgMDEsIDEyLCAzMCwgOSksCiAgICAgICAgJ290IDIwMTYtMTEtMDEgMTI6MzA6MDkgKzAwOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2EgRGF0ZSBpbnN0YW5jZScsCiAgICAgICAgbmlsLAogICAgICAgIERhdGUubmV3KDIwMTYsIDExLCAwMSksCiAgICAgICAgbGFtYmRhIHsKICAgICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldygKICAgICAgICAgICAgVGltZS5sb2NhbCgyMDE2LCAxMSwgMDEpLnRvX2YsIG5pbAogICAgICAgICAgKS50b19kZWJ1Z19zIH0gXSwKCiAgICAgIFsgJ2EgU3RyaW5nJywKICAgICAgICBuaWwsCiAgICAgICAgJzIwMTYtMTEtMDEgMTI6MzA6MDknLAogICAgICAgIGxhbWJkYSB7CiAgICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoCiAgICAgICAgICAgIFRpbWUubG9jYWwoMjAxNiwgMTEsIDAxLCAxMiwgMzAsIDkpLnRvX2YsIG5pbCkgfSBdLAoKICAgICAgWyAnYSBTdHJpbmcnLAogICAgICAgICdBbWVyaWNhL0NoaWNhZ28nLAogICAgICAgICcyMDE2LTA4LTAxIDEyOjMwOjEwJywKICAgICAgICAnb3QgMjAxNi0wOC0wMSAxMjozMDoxMCAtMDU6MDAgZHN0OnRydWUnIF0sCiAgICAgICNbICdhIFN0cmluZycsCiAgICAgICMgICdBbWVyaWNhL0NoaWNhZ28nLAogICAgICAjICAnMjAxNi0xMS0wMSAxMjozMDowOScsCiAgICAgICMgICdvdCAyMDE2LTExLTAxIDEyOjMwOjA5IC0wNTowMCBkc3Q6dHJ1ZScgXSwgIyBmYWlscyBvbiBBcHB2ZXlvciA6LSgKICAgICAgWyAnYSBTdHJpbmcnLAogICAgICAgICdBbWVyaWNhL0NoaWNhZ28nLAogICAgICAgICcyMDE2LTExLTA2IDEyOjMwOjA4JywKICAgICAgICAnb3QgMjAxNi0xMS0wNiAxMjozMDowOCAtMDY6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYSBTdHJpbmcnLAogICAgICAgICdFdXJvcGUvUGFyaXMnLAogICAgICAgICcyMDE2LTEtMSAxMjozMDowNycsCiAgICAgICAgJ290IDIwMTYtMDEtMDEgMTI6MzA6MDcgKzAxOjAwIGRzdDpmYWxzZScgXSwKICAgICAgWyAnYSBTdHJpbmcnLAogICAgICAgICdFdXJvcGUvUGFyaXMnLAogICAgICAgICcyMDE2LTgtMSAxMjozMDowNycsCiAgICAgICAgJ290IDIwMTYtMDgtMDEgMTI6MzA6MDcgKzAyOjAwIGRzdDp0cnVlJyBdLAoKICAgICAgWyAnYSBadWx1IFN0cmluZycsCiAgICAgICAgbmlsLAogICAgICAgICcyMDE2LTExLTAxIDEyOjMwOjA5WicsCiAgICAgICAgRXRPcmJpOjpFb1RpbWUubmV3KFRpbWUudXRjKDIwMTYsIDExLCAwMSwgMTIsIDMwLCA5KS50b19mLCAnWnVsdScpIF0sCgogICAgICBbICdhIHNzKzAxOjAwIFN0cmluZycsCiAgICAgICAgbmlsLAogICAgICAgICcyMDE2LTExLTAxIDEyOjMwOjA5KzAxOjAwJywKICAgICAgICAnb3QgMjAxNi0xMS0wMSAxMjozMDowOSArMDE6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYSBzcy0wMSBTdHJpbmcnLAogICAgICAgIG5pbCwKICAgICAgICAnMjAxNi0xMS0wMSAxMjozMDowOS0wMScsCiAgICAgICAgJ290IDIwMTYtMTEtMDEgMTI6MzA6MDkgLTAxOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2EgU3RyaW5nIHdpdGggYW4gZXhwbGljaXQgdGltZSB6b25lJywKICAgICAgICBuaWwsCiAgICAgICAgJzIwMTYtMDUtMDEgMTI6MzA6MDkgQW1lcmljYS9OZXdfWW9yaycsCiAgICAgICAgJ290IDIwMTYtMDUtMDEgMTI6MzA6MDkgLTA0OjAwIGRzdDp0cnVlJyBdLAoKICAgICAgWyAnYSBOdW1lcmljJywKICAgICAgICBuaWwsCiAgICAgICAgMzYwMCwKICAgICAgICBsYW1iZGEgeyBbIFRpbWUubm93ICsgMzYwMCAtIDAuMzUsIFRpbWUubm93ICsgMzYwMCArIDAuMzUgXSB9IF0sCgogICAgICBbICdhbiBhcnJheSBbIHksIG0sIGQsIC4uLiBdJywKICAgICAgICAnRXVyb3BlL01vc2NvdycsCiAgICAgICAgWyBbIDIwMTcsIDIsIDI4IF0gXSwKICAgICAgICAnb3QgMjAxNy0wMi0yOCAwMDowMDowMCArMDM6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYW4gYXJyYXkgb2YgYXJncyAoeSwgbSwgZCwgLi4uKScsCiAgICAgICAgJ0V1cm9wZS9Nb3Njb3cnLAogICAgICAgIFsgMjAxNywgMSwgMzEsIDEwIF0sCiAgICAgICAgJ290IDIwMTctMDEtMzEgMTA6MDA6MDAgKzAzOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2FuIGFycmF5IG9mIGFyZ3MgYW5kIGEgem9uZSBhcyBsYXN0IGFyZycsCiAgICAgICAgbmlsLAogICAgICAgIFsgMjAxNywgMSwgMzEsIDEyLCAnRXVyb3BlL01vc2NvdycgXSwKICAgICAgICAnb3QgMjAxNy0wMS0zMSAxMjowMDowMCArMDM6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYSBzdHJpbmcgYW5kIGEgem9uZSBhcyBsYXN0IGFyZycsCiAgICAgICAgbmlsLAogICAgICAgIFsgJzIwMTYtMDUtMDEgMTI6MzA6MDknLCAnQW1lcmljYS9DaGljYWdvJyBdLAogICAgICAgICdvdCAyMDE2LTA1LTAxIDEyOjMwOjA5IC0wNTowMCBkc3Q6dHJ1ZScgXSwKCiAgICAgIFsgJ2Egc3RyaW5nIGFuZCBhbiBvdmVycmlkaW5nIHpvbmUgYXMgbGFzdCBhcmcnLAogICAgICAgIG5pbCwKICAgICAgICBbICcyMDE2LTA1LTAxIDExOjMwOjA5IEFtZXJpY2EvTmV3X1lvcmsnLCAnQW1lcmljYS9DaGljYWdvJyBdLAogICAgICAgICdvdCAyMDE2LTA1LTAxIDExOjMwOjA5IC0wNTowMCBkc3Q6dHJ1ZScgXSwKCiAgICAgIFsgJ2FuIGFycmF5IG9mIGFyZ3MgYW5kIGEgVFpJbmZvIHpvbmUgYXMgbGFzdCBhcmcnLAogICAgICAgIG5pbCwKICAgICAgICBbIDIwMTcsIDEsIDMxLCBFdE9yYmkuZ2V0X3R6b25lKCdFdXJvcGUvT3NsbycpIF0sCiAgICAgICAgJ290IDIwMTctMDEtMzEgMDA6MDA6MDAgKzAxOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2Egc3RyaW5nIGFuZCBhIFRaSW5mbyB6b25lIGFzIGxhc3QgYXJnJywKICAgICAgICBuaWwsCiAgICAgICAgWyAnMjAxNy0wMS0zMSAxMjozMCcsIEV0T3JiaS5nZXRfdHpvbmUoJ0V1cm9wZS9Pc2xvJykgXSwKICAgICAgICAnb3QgMjAxNy0wMS0zMSAxMjozMDowMCArMDE6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYSBzdHJpbmcnLAogICAgICAgICdBbWVyaWNhL0NoaWNhZ28nLAogICAgICAgIGxhbWJkYSB7IFsgVGltZS5wYXJzZSgnMjAyMS0wMy0xMScpIF0gfSwKICAgICAgICB3aW5kb3dzPyA/ICdDU1Q2Q0RUJyA6ICdBbWVyaWNhIC0gQ2hpY2FnbycsCiAgICAgICAgbGFtYmRhIHsgfHR8IHQuem9uZS50b19zIH0gXSwKCiAgICAgIFsgJ2Egc3RyaW5nJywKICAgICAgICAnQW1lcmljYS9OZXdfWW9yaycsCiAgICAgICAgbGFtYmRhIHsgWyBUaW1lLnBhcnNlKCcyMDIxLTAzLTExJykgXSB9LAogICAgICAgIHdpbmRvd3M/ID8gJXdbIEVTVDVFRFQgRVNUIF0gOiAnQW1lcmljYSAtIE5ldyBZb3JrJywKICAgICAgICAjd2luZG93cz8gPyAnRVNUJyA6ICdBbWVyaWNhIC0gTmV3IFlvcmsnLAogICAgICAgIGxhbWJkYSB7IHx0fCB0LnpvbmUudG9fcyB9IF0sCgogICAgXS5lYWNoIGRvIHxuYW1lLCB6b25lLCBhcmdzLCBhc3NlcnRlZCwgdHJhbnNmb3JtZXJ8CgogICAgICB0aXRsZSA9ICJ0dXJucyAje25hbWV9IGludG8gYW4gRW9UaW1lIGluc3RhbmNlIgogICAgICB0aXRsZSArPSAiIGluICN7em9uZX0iIGlmIHpvbmUKCiAgICAgIHRlc3QodGl0bGUpIGRvCgogICAgICAgIGVvdCwgZXhwID0KICAgICAgICAgIGluX3pvbmUoem9uZSkgZG8KCiAgICAgICAgICAgIGFzID0gYXJncy5pc19hPyhQcm9jKSA/IGFyZ3MuY2FsbCA6IGFyZ3MKCiAgICAgICAgICAgIHQgPSBhcy5pc19hPyhBcnJheSkgPwogICAgICAgICAgICAgIEV0T3JiaS5tYWtlX3RpbWUoKmFzKSA6CiAgICAgICAgICAgICAgRXRPcmJpLm1ha2VfdGltZShhcykKICAgICAgICAgICAgeCA9IGFzc2VydGVkLmlzX2E/KFByb2MpID8KICAgICAgICAgICAgICBhc3NlcnRlZC5jYWxsIDoKICAgICAgICAgICAgICBhc3NlcnRlZAoKICAgICAgICAgICAgdCA9IHRyYW5zZm9ybWVyLmlzX2E/KFByb2MpID8gdHJhbnNmb3JtZXJbdF0gOiB0CgogICAgICAgICAgICBbIHQsIHggXQogICAgICAgICAgZW5kCiNwIGVvdC50b19kZWJ1Z19zCiNwIGVvdC51dGNfb2Zmc2V0CiNwIGVvdC5zZW5kKDp0b190aW1lKS50b19zCiNwIGVvdC5zZW5kKDp0b190aW1lKS51dGNfb2Zmc2V0CgogICAgICAgIGNhc2UgZXhwCiAgICAgICAgd2hlbiBTdHJpbmcKICAgICAgICAgIGFzc2VydCBlb3QucmVzcG9uZF90bz8oOnRvX2RlYnVnX3MpID8gZW90LnRvX2RlYnVnX3MgOiBlb3QsIGV4cAogICAgICAgIHdoZW4gQXJyYXkKICAgICAgICAgIGlmIGV4cC5jb2xsZWN0KCY6Y2xhc3MpLnVuaXEgPT0gWyBTdHJpbmcgXQogICAgICAgICAgICBhc3NlcnQgZXhwLmluY2x1ZGU/KGVvdCkKICAgICAgICAgIGVsc2UKICAgICAgICAgICAgYXNzZXJ0IGVvdCA+IGV4cFswXQogICAgICAgICAgICBhc3NlcnQgZW90IDwgZXhwWzFdCiAgICAgICAgICBlbmQKICAgICAgICBlbHNlCiAgICAgICAgICBhc3NlcnQgZW90LCBleHAKICAgICAgICBlbmQKICAgICAgZW5kCiAgICBlbmQKCiMgICAgdGVzdCAnYWNjZXB0cyBhIGR1cmF0aW9uIFN0cmluZycjIGRvCiMjCiMjICAgICAgZXhwZWN0KAojIyAgICAgICAgRXRPcmJpLm1ha2VfdGltZSgnMWgnKQojIyAgICAgICkudG8gYmVfYmV0d2VlbigKIyMgICAgICAgIFRpbWUubm93ICsgMzYwMCAtIDEsIFRpbWUubm93ICsgMzYwMCArIDEKIyMgICAgICApCiMjICAgIGVuZAojICAjCiMgICMgU3RyaW5nIHBhcnNpbmcgaXMgZnVnaXQncyBqb2IuIEV0LW9yYmkgc2hvdWxkIGJlIGEgZGVwZW5kZW5jeSBvZgojICAjIGZ1Z2l0LCBub3QgdGhlIG90aGVyIHdheSBhcm91bmQuIFdoZW4gZnVnaXQgaXMgcHJlc2VudCwgdGhpcwojICAjIHNwZWMgc2hvdWxkIHN1Y2NlZWQsIGVsc2UgdGVzdCBzaG91bGQgbm90LgoKICAgIHRlc3QgJ2FjY2VwdHMgYSBSYWlscyBUaW1lV2l0aFpvbmUnIGRvCgogICAgICAjIGh0dHA6Ly9hcGkucnVieW9ucmFpbHMub3JnL2NsYXNzZXMvQWN0aXZlU3VwcG9ydC9UaW1lV2l0aFpvbmUuaHRtbAoKICAgICAgbiA9IFRpbWUubm93CiAgICAgIHogPSBFdE9yYmkuZ2V0X3R6b25lKCdQYWNpZmljL0Vhc3RlcicpCgogICAgICAjdCA9IEFjdGl2ZVN1cHBvcnQ6OlRpbWVXaXRoWm9uZS5uZXcobiwgeikKICAgICAgICAjCiAgICAgIHQgPSBuCiAgICAgIHQuaW5zdGFuY2VfZXZhbCB7IEB6ID0geiB9CiAgICAgIGNsYXNzIDw8IHQ7IGRlZiB0aW1lX3pvbmU7IEB6OyBlbmQ7IGVuZAogICAgICAgICMKICAgICAgICAjIGZha2UgYW4gQWN0aXZlU3VwcG9ydDo6VGltZVdpdGhab25lIGluc3RhbmNlIHdpdGggYSAjdGltZV96b25lCgogICAgICBlb3QgPSBFdE9yYmkubWFrZV90aW1lKHQpCgogICAgICBhc3NlcnQgZW90LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICBhc3NlcnQgZW90LnNlY29uZHMsIHQudG9fZgogICAgICBhc3NlcnQgZW90LnpvbmUsIHQudGltZV96b25lCiAgICBlbmQKCiAgICB0ZXN0ICdyZWplY3RzIGEgVGltZSBpbiBhIG5vbi1sb2NhbCBhbWJpZ3VvdXMgdGltZXpvbmUnIGRvCgogICAgICB0ID0gVGltZS5sb2NhbCgyMDE2LCAxMSwgMDEsIDEyLCAzMCwgOSkKICAgICAgY2xhc3MgPDwgdDsgZGVmIHpvbmU7ICdFQ1QnOyBlbmQ7IGVuZAoKICAgICAgaW5fem9uZSAnQXNpYS9UYmlsaXNpJyBkbwoKICAgICAgICBhc3NlcnRfZXJyb3IoCiAgICAgICAgICBsYW1iZGEgeyBFdE9yYmkubWFrZV90aW1lKHQpIH0sCiAgICAgICAgICBBcmd1bWVudEVycm9yLCAvXEFDYW5ub3QgZGV0ZXJtaW5lIHRpbWV6b25lIGZyb20gIkVDVCIvKQogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ3JlamVjdHMgdW5wYXJzYWJsZSBpbnB1dCcgZG8KCiAgICAgIGFzc2VydF9lcnJvcigKICAgICAgICBsYW1iZGEgeyBFdE9yYmkubWFrZV90aW1lKCd4eHgnKSB9LAogICAgICAgIEFyZ3VtZW50RXJyb3IsICdObyB0aW1lIGluZm9ybWF0aW9uIGluICJ4eHgiJykKICAgICAgICAgICMKICAgICAgICAgICMgc3RyYWlnaHQgb3V0IG9mIFRpbWUucGFyc2UoKQoKICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgIGxhbWJkYSB7IEV0T3JiaS5tYWtlX3RpbWUoT2JqZWN0Lm5ldykgfSwKICAgICAgICBBcmd1bWVudEVycm9yLCAvXEFDYW5ub3QgdHVybiAvKQogICAgZW5kCiAgZW5kCgogIGdyb3VwICcud2luZG93c196b25lX25hbWUnIGRvCgogICAgeyBbICdBc2lhL1Rva3lvJywgJzIwMTgtMDUtMjMnIF0gPT4gJ0pTVC05JywKICAgICAgWyAnQXNpYS9Lb2xrYXRhJywgJzIwMTgtMDctMDEnIF0gPT4gJ0lTVC01OjMwJywKICAgICAgWyAnQXNpYS9UZWhyYW4nLCAnMjAxOS0wMS0wOScgXSA9PiAnX0FULTM6MzBBc2lhL1RlaHJhbicsICMnSVJULTM6MzAnLAogICAgICBbICdBc2lhL1RlaHJhbicsICcyMDE5LTA3LTA5JyBdID0+ICdfQVQtNDozMEFzaWEvVGVocmFuJywgIydJUlQtNDozMCcsCiAgICAgIFsgJ0FzaWEvVGJpbGlzaScsICcyMDE5LTAxLTA5JyBdID0+ICdfQVQtNDowMEFzaWEvVGJpbGlzaScsCiAgICAgIFsgJ0FzaWEvU2FtYXJrYW5kJywgJzIwMTctMTEtMDEnIF0gPT4gJ19BUy01OjAwQXNpYS9TYW1hcmthbmQnLAogICAgICBbICdFdXJvcGUvQmVybGluJywgJzIwMTgtMDEtMDEnIF0gPT4gJ0NFVC0xQ0VTVCcsCiAgICAgIFsgJ0V1cm9wZS9CZXJsaW4nLCAnMjAxOC0wNy0wMScgXSA9PiAnQ0VULTJDRVNUJywKICAgICAgWyAnQW1lcmljYS9OZXdfWW9yaycsICcyMDE4LTAxLTAxJyBdID0+ICdFU1Q1RURUJywKICAgICAgWyAnQW1lcmljYS9OZXdfWW9yaycsICcyMDE4LTA3LTAxJyBdID0+ICdFU1Q0RURUJywKICAgICAgWyAnQW1lcmljYS9Mb3NfQW5nZWxlcycsICcyMDE3LTEwLTMwJyBdID0+ICdQU1Q3UERUJywKICAgICAgWyAnQW1lcmljYS9Mb3NfQW5nZWxlcycsICcyMDE5LTAxLTAxJyBdID0+ICdQU1Q4UERUJywKCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMDctMjgnIF0gPT4gJ0VFVC0zRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTAtMjgnIF0gPT4gJ0VFVC0zRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTItMjgnIF0gPT4gJ0VFVC0yRUVTVCcsCgogICAgfS5lYWNoIGRvIHwoem9uZSwgdGltZSksIHZ8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7di5pbnNwZWN0fSBmb3IgI3t6b25lLmluc3BlY3R9IGF0ICN7dGltZX0iIGRvCgogICAgICAgIGFzc2VydCBFdE9yYmkud2luZG93c196b25lX25hbWUoem9uZSwgVGltZS5wYXJzZSh0aW1lKSksIHYKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy56b25lX2FiYnJldmlhdGlvbicgZG8KCiAgICB7IFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMDctMjgnIF0gPT4gJ0VFU1QnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4JyBdID0+ICdFRVNUJywKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0xMi0yOCcgXSA9PiAnRUVUJywKCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMDMtMjUgMDI6MDAnIF0gPT4gJ0VFVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMDMtMjUgMDM6MDAnIF0gPT4gJ0VFU1QnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTAzLTI1IDA0OjAwJyBdID0+ICdFRVNUJywKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0wMy0yNSAwNTowMCcgXSA9PiAnRUVTVCcsCgogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4IDAwOjAwJyBdID0+ICdFRVNUJywKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0xMC0yOCAwMjo1OScgXSA9PiAnRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTAtMjggMDM6MDAnIF0gPT4gJ0VFVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTAtMjggMDM6MzAnIF0gPT4gJ0VFVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTAtMjggMDQ6MzAnIF0gPT4gJ0VFVCcsCgogICAgICAjIGluIGNhc2Ugb2YgYW1iaWd1aXR5LCAuem9uZV9hYmJyZXZpYXRpb24gZ29lcyAyIGhvdXJzIGZvcndhcmQuLi4KCiAgICB9LmVhY2ggZG8gfCh6b25lLCB0aW1lKSwgdnwKCiAgICAgIHRlc3QgInJldHVybnMgI3t2Lmluc3BlY3R9IGZvciAje3pvbmUuaW5zcGVjdH0gYXQgI3t0aW1lfSIgZG8KCiAgICAgICAgYXNzZXJ0IEV0T3JiaS56b25lX2FiYnJldmlhdGlvbih6b25lLCBUaW1lLnBhcnNlKHRpbWUpKSwgdgogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnLnR3ZWFrX3pvbmVfbmFtZScgZG8KCiAgICB7ICdFU1Q1RURUJyA9PiAnRVNUNUVEVCcsCiAgICAgICdVVEMrMTInID0+ICdFdGMvR01ULTEyJywKICAgICAgJ0tvcmVhIFN0YW5kYXJkIFRpbWUnID0+ICdBc2lhL1Nlb3VsJywKCiAgICB9LmVhY2ggZG8gfG4wLCBuMXwKCiAgICAgIHRlc3QgInR1cm5zICN7bjAuaW5zcGVjdH0gaW50byAje24xLmluc3BlY3R9IiBkbwoKICAgICAgICBhc3NlcnQgRXRPcmJpLnR3ZWFrX3pvbmVfbmFtZShuMCksIG4xCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcuY2hyb25pY19lbmFibGVkPycgZG8KCiAgICB0ZXN0ICdyZXR1cm5zIHRydWUgYnkgZGVmYXVsdCcgZG8KCiAgICAgIGFzc2VydCBFdE9yYmkuY2hyb25pY19lbmFibGVkPwogICAgZW5kCiAgZW5kCgogIGdyb3VwICcuY2hyb25pY19lbmFibGVkPScgZG8KCiAgICBhZnRlciBkbwogICAgICBFdE9yYmkuY2hyb25pY19lbmFibGVkID0gdHJ1ZQogICAgZW5kCgogICAgdGVzdCAnbGV0cyBkaXNhYmxlIGNocm9uaWMgcGFyc2luZycgZG8KCiAgICAgIEV0T3JiaS5jaHJvbmljX2VuYWJsZWQgPSBmYWxzZQoKICAgICAgYXNzZXJ0X2ZhbHNlIEV0T3JiaS5jaHJvbmljX2VuYWJsZWQ/CiAgICBlbmQKICBlbmQKCiMgIGdyb3VwICcuYWJicmV2aWF0ZV96b25lX25hbWUnIGRvCiMKIyAgICB7CiMKIyAgICAgICdFU1Q1RURUJyA9PiAnRVNUNUVEVCcsCiMgICAgICAnQlNUJyA9PiAld1sgR0ItRWlyZSBHQiBdLAojICAgICAgJ1VUQysxMicgPT4gbmlsLAojCiMgICAgfS5lYWNoIGRvIHxuMCwgbjF8CiMKIyAgICAgIG4xcyA9IG4xLmluc3BlY3QKIyAgICAgIG4xcyA9IG4xLmNvbGxlY3QoJjppbnNwZWN0KS5qb2luKCcgb3IgJykgaWYgbjEuaXNfYT8oQXJyYXkpCiMKIyAgICAgIHRlc3QgInR1cm5zICN7bjAuaW5zcGVjdH0gaW50byAje24xc30iIGRvCiMKIyAgICAgICAgaWYgbjEuaXNfYT8oQXJyYXkpCiMgICAgICAgICAgYXNzZXJ0IG4xKS50byBpbmNsdWRlKEV0T3JiaS5hYmJyZXZpYXRlX3pvbmVfbmFtZShuMCkpCiMgICAgICAgIGVsc2UKIyAgICAgICAgICBhc3NlcnQgRXRPcmJpLmFiYnJldmlhdGVfem9uZV9uYW1lKG4wKSwgbjEpCiMgICAgICAgIGVuZAojICAgICAgZW5kCiMgICAgZW5kCiMgIGVuZAplbmQKCg== \ No newline at end of file +# encoding: UTF-8 + +# +# Testing EtOrbi +# +# Wed Mar 11 21:17:36 JST 2015, quatre ans... (rufus-scheduler) +# Sun Mar 19 05:16:28 JST 2017 +# Fri Mar 24 04:55:25 JST 2017 圓さんの家 +# + + +group EtOrbi do + + before do + + ENV['TZ'] = ENV['_TZ'] + Time._zone = nil + EtOrbi._os_zone = nil + Time.active_support_zone = nil + end + + group '.list_iso8601_zones' do + + [ + + [ '2016-11-01 12:30:09-01', %w[ -01 ] ], + [ '2016-11-01 12:30:09-01:00', %w[ -01:00 ] ], + [ '2016-11-01 12:30:09 -01', %w[ -01 ] ], + [ '2016-11-01 12:30:09 -01:00', %w[ -01:00 ] ], + + [ '2016-11-01 12:30:09-01:30', %w[ -01:30 ] ], + [ '2016-11-01 12:30:09 -01:30', %w[ -01:30 ] ], + + [ '2016-11-01 12:30:09', [] ], + [ '2016-11-01 12:30:09-25', [] ], + [ '2016-11-01 12:30:09-25:00', [] ], + + ].each do |string, zones| + + test "returns #{zones.inspect} for #{string.inspect}" do + + assert EtOrbi.list_iso8601_zones(string), zones + end + end + end + +# group '.list_olson_zones' do +# +# [ +# +# [ '11/09/2002 America/New_York', +# %w[ America/New_York ] ], +# [ '11/09/2002 America/New_York Asia/Shanghai', +# %w[ America/New_York Asia/Shanghai ] ], +# [ 'America/New_York Asia/Shanghai', +# %w[ America/New_York Asia/Shanghai ] ], +# [ '2018-09-04 07:54:58 Etc/GMT-11', +# %w[ Etc/GMT-11 ] ], +# +# [ '2018-09-04 07:54:58 UTC+11', +# %w[ UTC+11 ] ], +# [ '2018-09-04 07:54:58 UTC+11 Etc/GMT-11', +# %w[ Etc/GMT-11 UTC+11 ] ], +# # +# # https://github.com/floraison/fugit/issues/9 +# +# [ '11/09/2002 2utopiaNada?3Nada', +# [] ] +# +# ].each do |string, zones| +# +# test "returns #{zones.inspect} for #{string.inspect}" do +# +# assert EtOrbi.list_olson_zones(string), zones) +# end +# end +# end + + group '.extract_zone' do + + [ + + [ '2016-11-01 12:30:09-01', [ '2016-11-01 12:30:09', '-01' ] ], + [ '2016-11-01 12:30:09-01:00', [ '2016-11-01 12:30:09', '-01:00' ] ], + [ '2016-11-01 12:30:09 -01', [ '2016-11-01 12:30:09', '-01' ] ], + [ '2016-11-01 12:30:09 -01:00', [ '2016-11-01 12:30:09', '-01:00' ] ], + [ '2016-11-01 12:30:09-01:30', [ '2016-11-01 12:30:09', '-01:30' ] ], + [ '2016-11-01 12:30:09 -01:30', [ '2016-11-01 12:30:09', '-01:30' ] ], + + [ '11/09/2002 America/New_York', + [ '11/09/2002', 'America/New_York' ] ], + [ '11/09/2002 America/New_York Asia/Shanghai', + [ '11/09/2002', 'Asia/Shanghai' ] ], + [ 'America/New_York Asia/Shanghai', + [ '', 'Asia/Shanghai' ] ], + [ '2018-09-04 07:54:58 Etc/GMT-11', + [ '2018-09-04 07:54:58', 'Etc/GMT-11' ] ], + [ '2018-09-04 07:54:58 UTC+11', + [ '2018-09-04 07:54:58', 'UTC+11' ] ], + [ '2018-09-04 07:54:58 UTC+11 Etc/GMT-11', + [ '2018-09-04 07:54:58', 'Etc/GMT-11' ] ], + + [ 'Sun Nov 18 16:01:00 Asia/Singapore 2012', + [ 'Sun Nov 18 16:01:00 2012', 'Asia/Singapore' ] ], + + [ '2016-11-01 12:30:09', [ '2016-11-01 12:30:09', nil ] ], + [ '2016-11-01 12:30:09-25', [ '2016-11-01 12:30:09-25', nil ] ], + [ '2016-11-01 12:30:09-25:00', [ '2016-11-01 12:30:09-25:00', nil ] ], + + [ '11/09/2002 2utopiaNada?3Nada', + [ '11/09/2002 2utopiaNada?3Nada', nil ] ], + + [ '2012-10-28 03:00:00 EET', + [ '2012-10-28 03:00:00', 'EET' ] ], + #[ '2012-10-28 03:00:00 EEST', + # [ '2012-10-28 03:00:00', 'EEST' ] ], + [ '2012-10-28 03:00:00 Europe/Tallinn', + [ '2012-10-28 03:00:00', 'Europe/Tallinn' ] ], + + ].each { |str0, (str1, zone)| + + test "returns #{[ str1, zone ].inspect} for #{str0.inspect}" do + + s1, z = EtOrbi.extract_zone(str0) + + assert [ s1, z ], [ str1, zone ] + end + } + end + + group '.parse' do + + test 'parses a time string without a timezone' do + + ot = in_zone('Europe/Moscow') { EtOrbi.parse('2015/03/08 01:59:59') } + + t = ot + u = ot.utc + + assert t.to_i, 1425769199 + assert u.to_i, 1425769199 + + assert( + t.strftime('%Y/%m/%d %H:%M:%S %Z %z') + " #{t.isdst}", + '2015/03/08 01:59:59 MSK +0300 false') + + assert u.to_debug_s, 't 2015-03-07 22:59:59 +00:00 dst:false' + end + + test 'parses a time string with a full name timezone' do + + ot = EtOrbi.parse('2015/03/08 01:59:59 America/Los_Angeles') + + t = ot + u = ot.utc + + assert t.to_i, 1425808799 + assert u.to_i, 1425808799 + + assert t.to_debug_s, 'ot 2015-03-08 01:59:59 -08:00 dst:false' + assert u.to_debug_s, 't 2015-03-08 09:59:59 +00:00 dst:false' + end + + test 'parses a time string with a delta timezone' do + + ot = in_zone('Europe/Berlin') { EtOrbi.parse('2015-12-13 12:30 -0200') } + + t = ot + u = ot.utc + + assert t.to_i, 1450017000 + assert u.to_i, 1450017000 + + assert t.to_debug_s, 'ot 2015-12-13 12:30:00 -02:00 dst:false' + assert u.to_debug_s, 't 2015-12-13 14:30:00 +00:00 dst:false' + end + + test 'parses a time string with a delta (:) timezone' do + + ot = in_zone('Europe/Berlin') { EtOrbi.parse('2015-12-13 12:30 -02:00') } + + t = ot + u = ot.utc + + assert t.to_i, 1450017000 + assert u.to_i, 1450017000 + + assert t.to_debug_s, 'ot 2015-12-13 12:30:00 -02:00 dst:false' + assert u.to_debug_s, 't 2015-12-13 14:30:00 +00:00 dst:false' + end + + test 'takes the local TZ when test does not know the timezone' do + + in_zone 'Europe/Moscow' do + + ot = EtOrbi.parse('2015/03/08 01:59:59 Nada/Nada') + + assert_include ot.zone.name, %w[ Europe/Moscow Europe/Kirov ] + end + end + + test 'parses even when the tz is out of place' do + + assert( + EtOrbi.parse('Sun Nov 18 16:01:00 Asia/Singapore 2012') + .to_debug_s, + "ot 2012-11-18 16:01:00 +08:00 dst:false") + end + + test 'fails on invalid strings' do + + assert_error( + lambda { EtOrbi.parse('xxx') }, + ArgumentError, 'No time information in "xxx"') + end + + test 'parses in the Rails-provided Time.zone (UTC)' do + + Time.active_support_zone = 'UTC' + + t = EtOrbi.parse('2019-01-01 12:10') + + assert t.class, EtOrbi::EoTime + assert t.zone, ::TZInfo::Timezone.get('UTC') + assert t.to_s, '2019-01-01 12:10:00 Z' + assert t.to_zs, '2019-01-01 12:10:00 UTC' + end + + test 'parses in the Rails-provided Time.zone (Asia/Shanghai)' do + + Time.active_support_zone = 'Asia/Shanghai' + + t = EtOrbi.parse('2019-01-01 12:10') + + assert t.class, EtOrbi::EoTime + assert t.zone, ::TZInfo::Timezone.get('Asia/Shanghai') + assert t.to_s, '2019-01-01 12:10:00 +0800' + assert t.to_zs, '2019-01-01 12:10:00 Asia/Shanghai' + end + +# # https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations +# # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +# +# context 'TZ abbreviations' do +# +# { +# +# '2021-01-01 7AM America/Los_Angeles' => '2021-01-01 07:00:00 -0800', +# '2021-07-01 7AM America/Los_Angeles' => '2021-07-01 07:00:00 -0700', +# '2021-01-01 7AM PST' => '2021-01-01 07:00:00 -0800', +# '2021-07-01 7AM PDT' => '2021-07-01 07:00:00 -0700', +# '2021-01-01 7AM -07:00' => '2021-01-01 07:00:00 -0700', +# '2021-07-01 7AM -07:00' => '2021-07-01 07:00:00 -0700', +# '2021-01-01 7AM GMT-7' => '2021-01-01 07:00:00 -0700', +# '2021-07-01 7AM GMT-7' => '2021-07-01 07:00:00 -0700', +# '2021-01-01 7AM UTC-7' => '2021-01-01 07:00:00 -0700', +# '2021-07-01 7AM UTC-7' => '2021-07-01 07:00:00 -0700', +# '2021-01-01 7AM Etc/GMT-7' => '2021-01-01 07:00:00 -0700', +# '2021-07-01 7AM Etc/GMT-7' => '2021-07-01 07:00:00 -0700', +# +# '2021-01-01 7AM ACDT' => '2021-01-01 07:00:00 +0930', +# '2021-07-01 7AM ACST' => '2021-01-01 07:00:00 +1030', +# +# }.each do |k, v| +# +# test "parses #{k} to #{v}" do +# +# ot = EtOrbi.parse(k) +#t = Time.parse(k) +#p [ k ] +#p [ Date._parse(k) ] +#p [ t.to_s, t.zone, :t ] +#p [ ot.to_s, ot.zone, :ot ] +#p [ v ] +# #p [ k, v, ot ] +# +# assert ot.to_s, v) +# end +# end +# end + + group 'when Chronic is defined' do + + before do + require_chronic + end + after do + unrequire_chronic + end + + test 'leverages it' do + + t = EtOrbi.parse('tomorrow at 1pm') + t1 = Time.now + 24 * 3600 + + assert t.class, EtOrbi::EoTime + assert t.strftime('%F %T'), t1.strftime('%F 13:00:00') + end + + test 'leverages test in a specified time zone' do + + t = EtOrbi.parse('tomorrow at 22:00 America/New_York') + t1 = Time.now + 24 * 3600 + + assert t.class, EtOrbi::EoTime + assert t.to_zs, t1.strftime('%F 22:00:00 America/New_York') + end + + test 'picks the Rails Time.zone (UTC) if available' do + + Time.active_support_zone = 'UTC' + + t = EtOrbi.parse('tomorrow') + t1 = Time.now + 24 * 3600 + + assert t.class, EtOrbi::EoTime + assert t.zone.name, 'UTC' + assert t.strftime('%Y-%m-%d'), t1.strftime('%Y-%m-%d') + assert t.strftime('%H:%M:%S'), '12:00:00' + end + + test 'picks the Rails Time.zone (Asia/Shanghai) if available' do + + Time.active_support_zone = 'Asia/Shanghai' + + t = EtOrbi.parse('tomorrow') + t1 = Time.now + 24 * 3600 + + assert t.class, EtOrbi::EoTime + assert t.zone.name, 'Asia/Shanghai' + assert t.strftime('%Y-%m-%d'), t1.strftime('%Y-%m-%d') + assert t.strftime('%H:%M:%S'), '12:00:00' + end + + group 'when parse(x, enable_chronic: false)' do + + test 'does not pre-parse with Chronic' do + + Time.active_support_zone = 'America/Chicago' + + assert( # works + EtOrbi.parse('tomorrow').class, + EtOrbi::EoTime) + + assert_error( # doesn't work + lambda { EtOrbi.parse('tomorrow', enable_chronic: false) }, + ArgumentError, 'No time information in "tomorrow"') + end + end + + group 'and Et-Orbi.chronic_enabled? is false' do + + before do + EtOrbi.chronic_enabled = false + end + after do + EtOrbi.chronic_enabled = true + end + + test 'does not pre-parse with Chronic' do + + assert_error( + lambda { EtOrbi.parse('tomorrow') }, + ArgumentError, 'No time information in "tomorrow"') + end + + test 'pre-parses with Chronic when enable_chronic: true' do + + assert_not_error { EtOrbi.parse('tomorrow', enable_chronic: true) } + end + end + + test 'filters options given to Chronic' do + + Time.active_support_zone = 'UTC' + + #assert ::Khronic) + # .to receive(:parse) + # .with('tomorrow', { context: :future }) + # .and_call_original + + t = EtOrbi.parse( + 'tomorrow', zone: ::TZInfo::Timezone.get('UTC'), context: :future) + t1 = Time.now + 24 * 3600 + + assert t.class, EtOrbi::EoTime + assert t.zone.name, 'UTC' + assert t.strftime('%Y-%m-%d'), t1.strftime('%Y-%m-%d') + assert t.strftime('%H:%M:%S'), '12:00:00' + end + end + end + + group '.get_tzone' do + + { + + 'GB' => 'GB', + 'UTC' => 'UTC', + 'GMT' => 'GMT', + 'Zulu' => 'Zulu', + 'Japan' => 'Japan', + 'Turkey' => 'Turkey', + 'Asia/Tokyo' => 'Asia/Tokyo', + 'Europe/Paris' => 'Europe/Paris', + 'Europe/Zurich' => 'Europe/Zurich', + 'W-SU' => 'W-SU', + + 'Z' => 'UTC', + + '+09:00' => '+09:00', + '-01:30' => '-01:30', + + '_AT-3:30Asia/Tehran' => 'Asia/Tehran', + '_AT-4Asia/Tbilisi' => 'Asia/Tbilisi', + + '+08:00' => '+08:00', + '+0800' => '+0800', # no normalization to "+08:00" + + '-01' => '-01', + + 3600 => '+01:00', + + 'Tokyo Standard Time' => 'Asia/Tokyo', + 'Coordinated Universal Time' => 'UTC', + + 'Eastern Standard Time' => 'America/New_York', + 'Eastern Daylight Time' => 'America/New_York', + + 'CST+0800' => 'Asia/Chongqing', + 'CST+08:00' => 'Asia/Chongqing', + 'CST+08' => 'Asia/Chongqing', + 'CST+8' => 'Asia/Chongqing', + 'EDT-0400' => 'America/Detroit', + + 'WET-1WEST' => 'WET', + 'CET-2CEST' => 'CET', + + }.each do |a, b| + + test "returns #{b.inspect} for #{a.inspect}" do + + z = EtOrbi.get_tzone(a) + + assert_not_nil z + assert z.name, b + end + end + + test 'handles fractional-hour offsets' do + + assert EtOrbi.get_tzone(19_800).name, '+05:30' + assert EtOrbi.get_tzone(-1_800).name, '-00:30' + assert EtOrbi.get_tzone('-00:30').name, '-00:30' + + assert( + EtOrbi::EoTime.new(0, 19_800).to_debug_s, + 'ot 1970-01-01 05:30:00 +05:30 dst:false') + end + +# test 'returns a timezone for well-known abbreviations' do +# +# assert gtz('JST'), 'Japan' +# assert gtz('PST'), 'America/Dawson' +# assert gtz('CEST'), 'Africa/Ceuta' +# end + + [ + 'Asia/Paris', 'Nada/Nada', '7', '06', 'sun#3', 'Mazda Zoom Zoom Stadium' + ].each do |s| + + test "returns nil for #{s.inspect}" do + + assert_nil EtOrbi.get_tzone(s) + end + end + + # rufus-scheduler gh-222 + # + test "falls back to ENV['TZ'] if test doesn't know Time.now.zone" do + + begin + + current = EtOrbi.get_tzone(:local) + + Time._zone = '中国标准时间' + + #assert_nil EtOrbi.get_tzone(:current) + #assert EtOrbi.get_tzone(:current), EtOrbi.get_tzone(Time.now.zone) + # + # gh-240 introduces a way of finding the timezone by asking directly + # to the system, so those do return a timezone... + + in_zone 'Asia/Shanghai' do + + assert_include( + EtOrbi.get_tzone(:local), + [ EtOrbi.get_tzone('Asia/Shanghai'), + EtOrbi.get_tzone('Asia/Chongqing') ]) + end + + ensure + + Time._zone = nil + end + + assert EtOrbi.get_tzone(:local), current + end + + { # for rufus-scheduler gh-228 + + 'Asia/Tokyo' => %w[ + Asia/Tokyo ], + 'Asia/Shanghai' => %w[ + Asia/Shanghai Asia/Chongqing ], + 'Europe/Zurich' => %w[ + Europe/Zurich Africa/Ceuta windows:CET ], + 'Europe/London' => %w[ + Europe/London Europe/Belfast windows:GMT windows:GMT-0 ], + + }.each do |zone, targets| + + test "returns the current timezone for :current in #{zone}" do + + in_zone(zone) do + + assert_include EtOrbi.get_tzone(:local), select_zones(targets) + end + end + end + + test "doesn't mind being given a TZInfo::Timezone" do + + tz = ::TZInfo::Timezone.get('Zulu') + class << tz + def <=>(tz) + #return nil unless tz.is_a?(Timezone) + identifier <=> tz.identifier + end + end + # simulate tzinfo 0.3.53 issue + + assert EtOrbi.get_tzone(tz), ::TZInfo::Timezone.get('Zulu') + end + end + + group '.determine_local_tzone' do + + test 'favours the local timezone' do + + in_zone(:no_env_tz) do + + Time._zone = 'Cape Verde Standard Time' + EtOrbi._os_zone = '' # force #os_tz to return nil + + assert EtOrbi.determine_local_tzone.name, 'Atlantic/Cape_Verde' + end + end + + test 'favours the local timezone' do + + in_zone('Europe/Berlin') do + + assert_include( + EtOrbi.determine_local_tzone, + select_zones(%w[ Europe/Berlin Africa/Ceuta windows:CET ])) + end + end + + test 'favours the local timezone (Mitteleuropaeische Sommerzeit)' do + + in_zone(:no_env_tz) do + + Time._zone = "Mitteleurop\xE4ische Sommerzeit" + EtOrbi._os_zone = '' # force #os_tz to return nil + + assert_not_error { EtOrbi.determine_local_tzone } + end + end + + test 'returns the local timezone' do + + in_zone('America/Jamaica') do + + assert_include( + EtOrbi.determine_local_tzone.name, + [ 'America/Jamaica', 'America/Atikokan', 'EST' ]) + end + end + + test 'returns the Rails-provided Time.zone.tzinfo if available' do + + # http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html + + Time.active_support_zone = 'Europe/Vilnius' + + in_zone(:no_env_tz) do + assert EtOrbi.determine_local_tzone.class, ::TZInfo::DataTimezone + assert EtOrbi.determine_local_tzone.name, 'Europe/Vilnius' + end + end + + test "gives precedence to ENV['TZ'] over Rails Time.zone.tzinfo" do + + # http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html + + Time.active_support_zone = 'Europe/Vilnius' + + in_zone('Asia/Tehran') do + + assert EtOrbi.determine_local_tzone.class, ::TZInfo::DataTimezone + assert EtOrbi.determine_local_tzone.name, 'Asia/Tehran' + end + end + end + + group '.zone' do + + test 'is an alias to .determine_local_tzone' do + + in_zone(:no_env_tz) do + + Time._zone = 'Cape Verde Standard Time' + EtOrbi._os_zone = '' # force #os_tz to return nil + + assert EtOrbi.zone.name, 'Atlantic/Cape_Verde' + end + end + end + + group '.now' do + + test 'returns a current, local EoTime instance' do + + in_zone 'Asia/Shanghai' do + + t = EtOrbi.now + n = Time.now + + assert t.seconds > (n - 1).to_f + assert t.seconds < (n + 1).to_f + + assert_include t.zone.name, [ 'Asia/Shanghai', 'Asia/Chongqing' ] + end + end + end + + group '.make_time' do + + test 'returns an EoTime instance as is' do + + t0 = EtOrbi.parse('2017-03-21 12:00:34 Asia/Ulan_Bator') + t1 = EtOrbi.make_time(t0) + + assert t1.class, ::EtOrbi::EoTime + assert t1, t0 + assert t1.object_id, t0.object_id + end + + test 'returns an EoTime instance as is' do + + t0 = EtOrbi.parse('2017-03-21 12:00:34 Asia/Ulan_Bator') + t1 = EtOrbi.make_time(t0, t0.zone) + + assert t1.class, ::EtOrbi::EoTime + assert t1, t0 + assert t1.object_id, t0.object_id + end + + [ + [ 'an EoTime instance', + nil, + lambda { EtOrbi.parse('2017-03-21 12:00:34 Asia/Ulan_Bator') }, + 'ot 2017-03-21 12:00:34 +08:00 dst:false' ], + + [ 'a local time', + 'Asia/Tbilisi', + Time.parse('2016-11-01 12:30:09 +04:00'), # use custom tz :-( + 'ot 2016-11-01 12:30:09 +04:00 dst:false' ], + + [ 'an UTC time', + nil, + Time.utc(2016, 11, 01, 12, 30, 9), + 'ot 2016-11-01 12:30:09 +00:00 dst:false' ], + + [ 'a Date instance', + nil, + Date.new(2016, 11, 01), + lambda { + EtOrbi::EoTime.new( + Time.local(2016, 11, 01).to_f, nil + ).to_debug_s } ], + + [ 'a String', + nil, + '2016-11-01 12:30:09', + lambda { + EtOrbi::EoTime.new( + Time.local(2016, 11, 01, 12, 30, 9).to_f, nil) } ], + + [ 'a String', + 'America/Chicago', + '2016-08-01 12:30:10', + 'ot 2016-08-01 12:30:10 -05:00 dst:true' ], + #[ 'a String', + # 'America/Chicago', + # '2016-11-01 12:30:09', + # 'ot 2016-11-01 12:30:09 -05:00 dst:true' ], # fails on Appveyor :-( + [ 'a String', + 'America/Chicago', + '2016-11-06 12:30:08', + 'ot 2016-11-06 12:30:08 -06:00 dst:false' ], + + [ 'a String', + 'Europe/Paris', + '2016-1-1 12:30:07', + 'ot 2016-01-01 12:30:07 +01:00 dst:false' ], + [ 'a String', + 'Europe/Paris', + '2016-8-1 12:30:07', + 'ot 2016-08-01 12:30:07 +02:00 dst:true' ], + + [ 'a Zulu String', + nil, + '2016-11-01 12:30:09Z', + EtOrbi::EoTime.new(Time.utc(2016, 11, 01, 12, 30, 9).to_f, 'Zulu') ], + + [ 'a ss+01:00 String', + nil, + '2016-11-01 12:30:09+01:00', + 'ot 2016-11-01 12:30:09 +01:00 dst:false' ], + + [ 'a ss-01 String', + nil, + '2016-11-01 12:30:09-01', + 'ot 2016-11-01 12:30:09 -01:00 dst:false' ], + + [ 'a String with an explicit time zone', + nil, + '2016-05-01 12:30:09 America/New_York', + 'ot 2016-05-01 12:30:09 -04:00 dst:true' ], + + [ 'a Numeric', + nil, + 3600, + lambda { [ Time.now + 3600 - 0.35, Time.now + 3600 + 0.35 ] } ], + + [ 'an array [ y, m, d, ... ]', + 'Europe/Moscow', + [ [ 2017, 2, 28 ] ], + 'ot 2017-02-28 00:00:00 +03:00 dst:false' ], + + [ 'an array of args (y, m, d, ...)', + 'Europe/Moscow', + [ 2017, 1, 31, 10 ], + 'ot 2017-01-31 10:00:00 +03:00 dst:false' ], + + [ 'an array of args and a zone as last arg', + nil, + [ 2017, 1, 31, 12, 'Europe/Moscow' ], + 'ot 2017-01-31 12:00:00 +03:00 dst:false' ], + + [ 'a string and a zone as last arg', + nil, + [ '2016-05-01 12:30:09', 'America/Chicago' ], + 'ot 2016-05-01 12:30:09 -05:00 dst:true' ], + + [ 'a string and an overriding zone as last arg', + nil, + [ '2016-05-01 11:30:09 America/New_York', 'America/Chicago' ], + 'ot 2016-05-01 11:30:09 -05:00 dst:true' ], + + [ 'an array of args and a TZInfo zone as last arg', + nil, + [ 2017, 1, 31, EtOrbi.get_tzone('Europe/Oslo') ], + 'ot 2017-01-31 00:00:00 +01:00 dst:false' ], + + [ 'a string and a TZInfo zone as last arg', + nil, + [ '2017-01-31 12:30', EtOrbi.get_tzone('Europe/Oslo') ], + 'ot 2017-01-31 12:30:00 +01:00 dst:false' ], + + [ 'a string', + 'America/Chicago', + lambda { [ Time.parse('2021-03-11') ] }, + windows? ? 'CST6CDT' : 'America - Chicago', + lambda { |t| t.zone.to_s } ], + + [ 'a string', + 'America/New_York', + lambda { [ Time.parse('2021-03-11') ] }, + windows? ? %w[ EST5EDT EST ] : 'America - New York', + #windows? ? 'EST' : 'America - New York', + lambda { |t| t.zone.to_s } ], + + ].each do |name, zone, args, asserted, transformer| + + title = "turns #{name} into an EoTime instance" + title += " in #{zone}" if zone + + test(title) do + + eot, exp = + in_zone(zone) do + + as = args.is_a?(Proc) ? args.call : args + + t = as.is_a?(Array) ? + EtOrbi.make_time(*as) : + EtOrbi.make_time(as) + x = asserted.is_a?(Proc) ? + asserted.call : + asserted + + t = transformer.is_a?(Proc) ? transformer[t] : t + + [ t, x ] + end +#p eot.to_debug_s +#p eot.utc_offset +#p eot.send(:to_time).to_s +#p eot.send(:to_time).utc_offset + + case exp + when String + assert eot.respond_to?(:to_debug_s) ? eot.to_debug_s : eot, exp + when Array + if exp.collect(&:class).uniq == [ String ] + assert exp.include?(eot) + else + assert eot > exp[0] + assert eot < exp[1] + end + else + assert eot, exp + end + end + end + +# test 'accepts a duration String'# do +## +## expect( +## EtOrbi.make_time('1h') +## ).to be_between( +## Time.now + 3600 - 1, Time.now + 3600 + 1 +## ) +## end +# # +# # String parsing is fugit's job. Et-orbi should be a dependency of +# # fugit, not the other way around. When fugit is present, this +# # spec should succeed, else test should not. + + test 'accepts a Rails TimeWithZone' do + + # http://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html + + n = Time.now + z = EtOrbi.get_tzone('Pacific/Easter') + + #t = ActiveSupport::TimeWithZone.new(n, z) + # + t = n + t.instance_eval { @z = z } + class << t; def time_zone; @z; end; end + # + # fake an ActiveSupport::TimeWithZone instance with a #time_zone + + eot = EtOrbi.make_time(t) + + assert eot.class, EtOrbi::EoTime + assert eot.seconds, t.to_f + assert eot.zone, t.time_zone + end + + test 'rejects a Time in a non-local ambiguous timezone' do + + t = Time.local(2016, 11, 01, 12, 30, 9) + class << t; def zone; 'ECT'; end; end + + in_zone 'Asia/Tbilisi' do + + assert_error( + lambda { EtOrbi.make_time(t) }, + ArgumentError, /\ACannot determine timezone from "ECT"/) + end + end + + test 'rejects unparsable input' do + + assert_error( + lambda { EtOrbi.make_time('xxx') }, + ArgumentError, 'No time information in "xxx"') + # + # straight out of Time.parse() + + assert_error( + lambda { EtOrbi.make_time(Object.new) }, + ArgumentError, /\ACannot turn /) + end + end + + group '.windows_zone_name' do + + { [ 'Asia/Tokyo', '2018-05-23' ] => 'JST-9', + [ 'Asia/Kolkata', '2018-07-01' ] => 'IST-5:30', + [ 'Asia/Tehran', '2019-01-09' ] => '_AT-3:30Asia/Tehran', #'IRT-3:30', + [ 'Asia/Tehran', '2019-07-09' ] => '_AT-4:30Asia/Tehran', #'IRT-4:30', + [ 'Asia/Tbilisi', '2019-01-09' ] => '_AT-4:00Asia/Tbilisi', + [ 'Asia/Samarkand', '2017-11-01' ] => '_AS-5:00Asia/Samarkand', + [ 'Europe/Berlin', '2018-01-01' ] => 'CET-1CEST', + [ 'Europe/Berlin', '2018-07-01' ] => 'CET-2CEST', + [ 'America/New_York', '2018-01-01' ] => 'EST5EDT', + [ 'America/New_York', '2018-07-01' ] => 'EST4EDT', + [ 'America/Los_Angeles', '2017-10-30' ] => 'PST7PDT', + [ 'America/Los_Angeles', '2019-01-01' ] => 'PST8PDT', + + [ 'Europe/Tallinn', '2012-07-28' ] => 'EET-3EEST', + [ 'Europe/Tallinn', '2012-10-28' ] => 'EET-3EEST', + [ 'Europe/Tallinn', '2012-12-28' ] => 'EET-2EEST', + + }.each do |(zone, time), v| + + test "returns #{v.inspect} for #{zone.inspect} at #{time}" do + + assert EtOrbi.windows_zone_name(zone, Time.parse(time)), v + end + end + end + + group '.zone_abbreviation' do + + { [ 'Europe/Tallinn', '2012-07-28' ] => 'EEST', + [ 'Europe/Tallinn', '2012-10-28' ] => 'EEST', + [ 'Europe/Tallinn', '2012-12-28' ] => 'EET', + + [ 'Europe/Tallinn', '2012-03-25 02:00' ] => 'EET', + [ 'Europe/Tallinn', '2012-03-25 03:00' ] => 'EEST', + [ 'Europe/Tallinn', '2012-03-25 04:00' ] => 'EEST', + [ 'Europe/Tallinn', '2012-03-25 05:00' ] => 'EEST', + + [ 'Europe/Tallinn', '2012-10-28 00:00' ] => 'EEST', + [ 'Europe/Tallinn', '2012-10-28 02:59' ] => 'EEST', + [ 'Europe/Tallinn', '2012-10-28 03:00' ] => 'EET', + [ 'Europe/Tallinn', '2012-10-28 03:30' ] => 'EET', + [ 'Europe/Tallinn', '2012-10-28 04:30' ] => 'EET', + + # in case of ambiguity, .zone_abbreviation goes 2 hours forward... + + }.each do |(zone, time), v| + + test "returns #{v.inspect} for #{zone.inspect} at #{time}" do + + assert EtOrbi.zone_abbreviation(zone, Time.parse(time)), v + end + end + end + + group '.tweak_zone_name' do + + { 'EST5EDT' => 'EST5EDT', + 'UTC+12' => 'Etc/GMT-12', + 'Korea Standard Time' => 'Asia/Seoul', + + }.each do |n0, n1| + + test "turns #{n0.inspect} into #{n1.inspect}" do + + assert EtOrbi.tweak_zone_name(n0), n1 + end + end + end + + group '.chronic_enabled?' do + + test 'returns true by default' do + + assert EtOrbi.chronic_enabled? + end + end + + group '.chronic_enabled=' do + + after do + EtOrbi.chronic_enabled = true + end + + test 'lets disable chronic parsing' do + + EtOrbi.chronic_enabled = false + + assert_false EtOrbi.chronic_enabled? + end + end + +# group '.abbreviate_zone_name' do +# +# { +# +# 'EST5EDT' => 'EST5EDT', +# 'BST' => %w[ GB-Eire GB ], +# 'UTC+12' => nil, +# +# }.each do |n0, n1| +# +# n1s = n1.inspect +# n1s = n1.collect(&:inspect).join(' or ') if n1.is_a?(Array) +# +# test "turns #{n0.inspect} into #{n1s}" do +# +# if n1.is_a?(Array) +# assert n1).to include(EtOrbi.abbreviate_zone_name(n0)) +# else +# assert EtOrbi.abbreviate_zone_name(n0), n1) +# end +# end +# end +# end +end + From 79707e0a36b2ef1dd0fc1e197207a85dee2fdc57 Mon Sep 17 00:00:00 2001 From: Oskar Eichler <62393985+OskarEichler@users.noreply.github.com> Date: Sat, 29 Aug 2026 21:01:30 +0200 Subject: [PATCH 4/5] Fix fractional offset assertions --- test/module_test.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/module_test.rb b/test/module_test.rb index 1855e9e..3053cf3 100644 --- a/test/module_test.rb +++ b/test/module_test.rb @@ -449,13 +449,13 @@ test 'handles fractional-hour offsets' do - assert EtOrbi.get_tzone(19_800).name, '+05:30' - assert EtOrbi.get_tzone(-1_800).name, '-00:30' - assert EtOrbi.get_tzone('-00:30').name, '-00:30' + assert_equal '+05:30', EtOrbi.get_tzone(19_800).name + assert_equal '-00:30', EtOrbi.get_tzone(-1_800).name + assert_equal '-00:30', EtOrbi.get_tzone('-00:30').name - assert( - EtOrbi::EoTime.new(0, 19_800).to_debug_s, - 'ot 1970-01-01 05:30:00 +05:30 dst:false') + assert_equal( + 'ot 1970-01-01 05:30:00 +05:30 dst:false', + EtOrbi::EoTime.new(0, 19_800).to_debug_s) end # test 'returns a timezone for well-known abbreviations' do @@ -1016,4 +1016,3 @@ class << t; def zone; 'ECT'; end; end # end # end end - From c72d3de8a3e6aace129dd5de751820f4581f4949 Mon Sep 17 00:00:00 2001 From: Oskar Eichler Date: Sun, 30 Aug 2026 15:15:37 +0700 Subject: [PATCH 5/5] Match fractional offset test style --- test/module_test.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/module_test.rb b/test/module_test.rb index 3053cf3..1855e9e 100644 --- a/test/module_test.rb +++ b/test/module_test.rb @@ -449,13 +449,13 @@ test 'handles fractional-hour offsets' do - assert_equal '+05:30', EtOrbi.get_tzone(19_800).name - assert_equal '-00:30', EtOrbi.get_tzone(-1_800).name - assert_equal '-00:30', EtOrbi.get_tzone('-00:30').name + assert EtOrbi.get_tzone(19_800).name, '+05:30' + assert EtOrbi.get_tzone(-1_800).name, '-00:30' + assert EtOrbi.get_tzone('-00:30').name, '-00:30' - assert_equal( - 'ot 1970-01-01 05:30:00 +05:30 dst:false', - EtOrbi::EoTime.new(0, 19_800).to_debug_s) + assert( + EtOrbi::EoTime.new(0, 19_800).to_debug_s, + 'ot 1970-01-01 05:30:00 +05:30 dst:false') end # test 'returns a timezone for well-known abbreviations' do @@ -1016,3 +1016,4 @@ class << t; def zone; 'ECT'; end; end # end # end end +