From 15b6c8d3ff0b79d0276a7a0c73a56e677a168b90 Mon Sep 17 00:00:00 2001 From: Oskar Eichler Date: Thu, 27 Aug 2026 20:16:02 +0300 Subject: [PATCH 1/3] Resolve macOS timezone names relative to zoneinfo --- lib/et-orbi/zones.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/et-orbi/zones.rb b/lib/et-orbi/zones.rb index 3507ca5..736e7e0 100644 --- a/lib/et-orbi/zones.rb +++ b/lib/et-orbi/zones.rb @@ -124,7 +124,7 @@ def osx_tz path = '/etc/localtime' File.symlink?(path) ? - File.readlink(path).split('/')[4..-1].join('/') : + File.readlink(path).split('/zoneinfo/', 2)[1] : nil rescue; nil; end From d393bfa4c9274eea825ecfd522c84afdbadda889 Mon Sep 17 00:00:00 2001 From: Oskar Eichler <62393985+OskarEichler@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:41:24 +0300 Subject: [PATCH 2/3] Add regression test for et-orbi PR #56 --- 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..8de29d0 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+ICcyMDIxLTAxLTAxIDA3OjAwOjAwICsxMDMwJywKIwojICAgICAgfS5lYWNoIGRvIHxrLCB2fAojCiMgICAgICAgIHRlc3QgInBhcnNlcyAje2t9IHRvICN7dn0iIGRvCiMKIyAgICAgICAgICBvdCA9IEV0T3JiaS5wYXJzZShrKQojdCA9IFRpbWUucGFyc2UoaykKI3AgWyBrIF0KI3AgWyBEYXRlLl9wYXJzZShrKSBdCiNwIFsgdC50b19zLCB0LnpvbmUsIDp0IF0KI3AgWyBvdC50b19zLCBvdC56b25lLCA6b3QgXQojcCBbIHYgXQojICAjcCBbIGssIHYsIG90IF0KIwojICAgICAgICAgIGFzc2VydCBvdC50b19zLCB2KQojICAgICAgICBlbmQKIyAgICAgIGVuZAojICAgIGVuZAoKICAgIGdyb3VwICd3aGVuIENocm9uaWMgaXMgZGVmaW5lZCcgZG8KCiAgICAgIGJlZm9yZSBkbwogICAgICAgIHJlcXVpcmVfY2hyb25pYwogICAgICBlbmQKICAgICAgYWZ0ZXIgZG8KICAgICAgICB1bnJlcXVpcmVfY2hyb25pYwogICAgICBlbmQKCiAgICAgIHRlc3QgJ2xldmVyYWdlcyBpdCcgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cgYXQgMXBtJykKICAgICAgICB0MSA9IFRpbWUubm93ICsgMjQgKiAzNjAwCgogICAgICAgIGFzc2VydCB0LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclRiAlVCcpLCB0MS5zdHJmdGltZSgnJUYgMTM6MDA6MDAnKQogICAgICBlbmQKCiAgICAgIHRlc3QgJ2xldmVyYWdlcyB0ZXN0IGluIGEgc3BlY2lmaWVkIHRpbWUgem9uZScgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cgYXQgMjI6MDAgQW1lcmljYS9OZXdfWW9yaycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC50b196cywgdDEuc3RyZnRpbWUoJyVGIDIyOjAwOjAwIEFtZXJpY2EvTmV3X1lvcmsnKQogICAgICBlbmQKCiAgICAgIHRlc3QgJ3BpY2tzIHRoZSBSYWlscyBUaW1lLnpvbmUgKFVUQykgaWYgYXZhaWxhYmxlJyBkbwoKICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnVVRDJwoKICAgICAgICB0ID0gRXRPcmJpLnBhcnNlKCd0b21vcnJvdycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC56b25lLm5hbWUsICdVVEMnCiAgICAgICAgYXNzZXJ0IHQuc3RyZnRpbWUoJyVZLSVtLSVkJyksIHQxLnN0cmZ0aW1lKCclWS0lbS0lZCcpCiAgICAgICAgYXNzZXJ0IHQuc3RyZnRpbWUoJyVIOiVNOiVTJyksICcxMjowMDowMCcKICAgICAgZW5kCgogICAgICB0ZXN0ICdwaWNrcyB0aGUgUmFpbHMgVGltZS56b25lIChBc2lhL1NoYW5naGFpKSBpZiBhdmFpbGFibGUnIGRvCgogICAgICAgIFRpbWUuYWN0aXZlX3N1cHBvcnRfem9uZSA9ICdBc2lhL1NoYW5naGFpJwoKICAgICAgICB0ID0gRXRPcmJpLnBhcnNlKCd0b21vcnJvdycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC56b25lLm5hbWUsICdBc2lhL1NoYW5naGFpJwogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclWS0lbS0lZCcpLCB0MS5zdHJmdGltZSgnJVktJW0tJWQnKQogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclSDolTTolUycpLCAnMTI6MDA6MDAnCiAgICAgIGVuZAoKICAgICAgZ3JvdXAgJ3doZW4gcGFyc2UoeCwgZW5hYmxlX2Nocm9uaWM6IGZhbHNlKScgZG8KCiAgICAgICAgdGVzdCAnZG9lcyBub3QgcHJlLXBhcnNlIHdpdGggQ2hyb25pYycgZG8KCiAgICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnQW1lcmljYS9DaGljYWdvJwoKICAgICAgICAgIGFzc2VydCggIyB3b3JrcwogICAgICAgICAgICBFdE9yYmkucGFyc2UoJ3RvbW9ycm93JykuY2xhc3MsCiAgICAgICAgICAgIEV0T3JiaTo6RW9UaW1lKQoKICAgICAgICAgIGFzc2VydF9lcnJvciggIyBkb2Vzbid0IHdvcmsKICAgICAgICAgICAgbGFtYmRhIHsgRXRPcmJpLnBhcnNlKCd0b21vcnJvdycsIGVuYWJsZV9jaHJvbmljOiBmYWxzZSkgfSwKICAgICAgICAgICAgQXJndW1lbnRFcnJvciwgJ05vIHRpbWUgaW5mb3JtYXRpb24gaW4gInRvbW9ycm93IicpCiAgICAgICAgZW5kCiAgICAgIGVuZAoKICAgICAgZ3JvdXAgJ2FuZCBFdC1PcmJpLmNocm9uaWNfZW5hYmxlZD8gaXMgZmFsc2UnIGRvCgogICAgICAgIGJlZm9yZSBkbwogICAgICAgICAgRXRPcmJpLmNocm9uaWNfZW5hYmxlZCA9IGZhbHNlCiAgICAgICAgZW5kCiAgICAgICAgYWZ0ZXIgZG8KICAgICAgICAgIEV0T3JiaS5jaHJvbmljX2VuYWJsZWQgPSB0cnVlCiAgICAgICAgZW5kCgogICAgICAgIHRlc3QgJ2RvZXMgbm90IHByZS1wYXJzZSB3aXRoIENocm9uaWMnIGRvCgogICAgICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgICAgICBsYW1iZGEgeyBFdE9yYmkucGFyc2UoJ3RvbW9ycm93JykgfSwKICAgICAgICAgICAgQXJndW1lbnRFcnJvciwgJ05vIHRpbWUgaW5mb3JtYXRpb24gaW4gInRvbW9ycm93IicpCiAgICAgICAgZW5kCgogICAgICAgIHRlc3QgJ3ByZS1wYXJzZXMgd2l0aCBDaHJvbmljIHdoZW4gZW5hYmxlX2Nocm9uaWM6IHRydWUnIGRvCgogICAgICAgICAgYXNzZXJ0X25vdF9lcnJvciB7IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cnLCBlbmFibGVfY2hyb25pYzogdHJ1ZSkgfQogICAgICAgIGVuZAogICAgICBlbmQKCiAgICAgIHRlc3QgJ2ZpbHRlcnMgb3B0aW9ucyBnaXZlbiB0byBDaHJvbmljJyBkbwoKICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnVVRDJwoKICAgICAgICAjYXNzZXJ0IDo6S2hyb25pYykKICAgICAgICAjICAudG8gcmVjZWl2ZSg6cGFyc2UpCiAgICAgICAgIyAgLndpdGgoJ3RvbW9ycm93JywgeyBjb250ZXh0OiA6ZnV0dXJlIH0pCiAgICAgICAgIyAgLmFuZF9jYWxsX29yaWdpbmFsCgogICAgICAgIHQgPSBFdE9yYmkucGFyc2UoCiAgICAgICAgICAndG9tb3Jyb3cnLCB6b25lOiA6OlRaSW5mbzo6VGltZXpvbmUuZ2V0KCdVVEMnKSwgY29udGV4dDogOmZ1dHVyZSkKICAgICAgICB0MSA9IFRpbWUubm93ICsgMjQgKiAzNjAwCgogICAgICAgIGFzc2VydCB0LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICAgIGFzc2VydCB0LnpvbmUubmFtZSwgJ1VUQycKICAgICAgICBhc3NlcnQgdC5zdHJmdGltZSgnJVktJW0tJWQnKSwgdDEuc3RyZnRpbWUoJyVZLSVtLSVkJykKICAgICAgICBhc3NlcnQgdC5zdHJmdGltZSgnJUg6JU06JVMnKSwgJzEyOjAwOjAwJwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnLm9zeF90eicgZG8KCiAgICB0ZXN0ICdleHRyYWN0cyB0aGUgdGltZXpvbmUgcmVsYXRpdmUgdG8gdGhlIHpvbmVpbmZvIGRpcmVjdG9yeScgZG8KCiAgICAgIHN5bWxpbmsgPSBGaWxlLm1ldGhvZCg6c3ltbGluaz8pCiAgICAgIHJlYWRsaW5rID0gRmlsZS5tZXRob2QoOnJlYWRsaW5rKQogICAgICBsaW5rcyA9IFsKICAgICAgICAnL3Zhci9kYi90aW1lem9uZS96b25laW5mby9FdXJvcGUvSXN0YW5idWwnLAogICAgICAgICcvdXNyL3NoYXJlL3pvbmVpbmZvL0FzaWEvVG9reW8nLAogICAgICAgICcvdmFyL2RiL3RpbWV6b25lL2xvY2FsdGltZScKICAgICAgXQoKICAgICAgRmlsZS5kZWZpbmVfc2luZ2xldG9uX21ldGhvZCg6c3ltbGluaz8pIHsgfHBhdGh8IHBhdGggPT0gJy9ldGMvbG9jYWx0aW1lJyB9CiAgICAgIEZpbGUuZGVmaW5lX3NpbmdsZXRvbl9tZXRob2QoOnJlYWRsaW5rKSB7IHxffCBsaW5rcy5zaGlmdCB9CgogICAgICBhc3NlcnQgRXRPcmJpLm9zeF90eiwgJ0V1cm9wZS9Jc3RhbmJ1bCcKICAgICAgYXNzZXJ0IEV0T3JiaS5vc3hfdHosICdBc2lhL1Rva3lvJwogICAgICBhc3NlcnRfbmlsIEV0T3JiaS5vc3hfdHoKCiAgICBlbnN1cmUKCiAgICAgIEZpbGUuZGVmaW5lX3NpbmdsZXRvbl9tZXRob2QoOnN5bWxpbms/LCBzeW1saW5rKQogICAgICBGaWxlLmRlZmluZV9zaW5nbGV0b25fbWV0aG9kKDpyZWFkbGluaywgcmVhZGxpbmspCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy5nZXRfdHpvbmUnIGRvCgogICAgewoKICAgICAgJ0dCJyA9PiAnR0InLAogICAgICAnVVRDJyA9PiAnVVRDJywKICAgICAgJ0dNVCcgPT4gJ0dNVCcsCiAgICAgICdadWx1JyA9PiAnWnVsdScsCiAgICAgICdKYXBhbicgPT4gJ0phcGFuJywKICAgICAgJ1R1cmtleScgPT4gJ1R1cmtleScsCiAgICAgICdBc2lhL1Rva3lvJyA9PiAnQXNpYS9Ub2t5bycsCiAgICAgICdFdXJvcGUvUGFyaXMnID0+ICdFdXJvcGUvUGFyaXMnLAogICAgICAnRXVyb3BlL1p1cmljaCcgPT4gJ0V1cm9wZS9adXJpY2gnLAogICAgICAnVy1TVScgPT4gJ1ctU1UnLAoKICAgICAgJ1onID0+ICdVVEMnLAoKICAgICAgJyswOTowMCcgPT4gJyswOTowMCcsCiAgICAgICctMDE6MzAnID0+ICctMDE6MzAnLAoKICAgICAgJ19BVC0zOjMwQXNpYS9UZWhyYW4nID0+ICdBc2lhL1RlaHJhbicsCiAgICAgICdfQVQtNEFzaWEvVGJpbGlzaScgPT4gJ0FzaWEvVGJpbGlzaScsCgogICAgICAnKzA4OjAwJyA9PiAnKzA4OjAwJywKICAgICAgJyswODAwJyA9PiAnKzA4MDAnLCAjIG5vIG5vcm1hbGl6YXRpb24gdG8gIiswODowMCIKCiAgICAgICctMDEnID0+ICctMDEnLAoKICAgICAgMzYwMCA9PiAnKzAxOjAwJywKCiAgICAgICdUb2t5byBTdGFuZGFyZCBUaW1lJyA9PiAnQXNpYS9Ub2t5bycsCiAgICAgICdDb29yZGluYXRlZCBVbml2ZXJzYWwgVGltZScgPT4gJ1VUQycsCgogICAgICAnRWFzdGVybiBTdGFuZGFyZCBUaW1lJyA9PiAnQW1lcmljYS9OZXdfWW9yaycsCiAgICAgICdFYXN0ZXJuIERheWxpZ2h0IFRpbWUnID0+ICdBbWVyaWNhL05ld19Zb3JrJywKCiAgICAgICdDU1QrMDgwMCcgPT4gJ0FzaWEvQ2hvbmdxaW5nJywKICAgICAgJ0NTVCswODowMCcgPT4gJ0FzaWEvQ2hvbmdxaW5nJywKICAgICAgJ0NTVCswOCcgPT4gJ0FzaWEvQ2hvbmdxaW5nJywKICAgICAgJ0NTVCs4JyA9PiAnQXNpYS9DaG9uZ3FpbmcnLAogICAgICAnRURULTA0MDAnID0+ICdBbWVyaWNhL0RldHJvaXQnLAoKICAgICAgJ1dFVC0xV0VTVCcgPT4gJ1dFVCcsCiAgICAgICdDRVQtMkNFU1QnID0+ICdDRVQnLAoKICAgIH0uZWFjaCBkbyB8YSwgYnwKCiAgICAgIHRlc3QgInJldHVybnMgI3tiLmluc3BlY3R9IGZvciAje2EuaW5zcGVjdH0iIGRvCgogICAgICAgIHogPSBFdE9yYmkuZ2V0X3R6b25lKGEpCgogICAgICAgIGFzc2VydF9ub3RfbmlsIHoKICAgICAgICBhc3NlcnQgei5uYW1lLCBiCiAgICAgIGVuZAogICAgZW5kCgojICAgIHRlc3QgJ3JldHVybnMgYSB0aW1lem9uZSBmb3Igd2VsbC1rbm93biBhYmJyZXZpYXRpb25zJyBkbwojCiMgICAgICBhc3NlcnQgZ3R6KCdKU1QnKSwgJ0phcGFuJwojICAgICAgYXNzZXJ0IGd0eignUFNUJyksICdBbWVyaWNhL0Rhd3NvbicKIyAgICAgIGFzc2VydCBndHooJ0NFU1QnKSwgJ0FmcmljYS9DZXV0YScKIyAgICBlbmQKCiAgICBbCiAgICAgICdBc2lhL1BhcmlzJywgJ05hZGEvTmFkYScsICc3JywgJzA2JywgJ3N1biMzJywgJ01hemRhIFpvb20gWm9vbSBTdGFkaXVtJwogICAgXS5lYWNoIGRvIHxzfAoKICAgICAgdGVzdCAicmV0dXJucyBuaWwgZm9yICN7cy5pbnNwZWN0fSIgZG8KCiAgICAgICAgYXNzZXJ0X25pbCBFdE9yYmkuZ2V0X3R6b25lKHMpCiAgICAgIGVuZAogICAgZW5kCgogICAgIyBydWZ1cy1zY2hlZHVsZXIgZ2gtMjIyCiAgICAjCiAgICB0ZXN0ICJmYWxscyBiYWNrIHRvIEVOVlsnVFonXSBpZiB0ZXN0IGRvZXNuJ3Qga25vdyBUaW1lLm5vdy56b25lIiBkbwoKICAgICAgYmVnaW4KCiAgICAgICAgY3VycmVudCA9IEV0T3JiaS5nZXRfdHpvbmUoOmxvY2FsKQoKICAgICAgICBUaW1lLl96b25lID0gJ+S4reWbveagh+WHhuaXtumXtCcKCiAgICAgICAgI2Fzc2VydF9uaWwgRXRPcmJpLmdldF90em9uZSg6Y3VycmVudCkKICAgICAgICAjYXNzZXJ0IEV0T3JiaS5nZXRfdHpvbmUoOmN1cnJlbnQpLCBFdE9yYmkuZ2V0X3R6b25lKFRpbWUubm93LnpvbmUpCiAgICAgICAgICAjCiAgICAgICAgICAjIGdoLTI0MCBpbnRyb2R1Y2VzIGEgd2F5IG9mIGZpbmRpbmcgdGhlIHRpbWV6b25lIGJ5IGFza2luZyBkaXJlY3RseQogICAgICAgICAgIyB0byB0aGUgc3lzdGVtLCBzbyB0aG9zZSBkbyByZXR1cm4gYSB0aW1lem9uZS4uLgoKICAgICAgICBpbl96b25lICdBc2lhL1NoYW5naGFpJyBkbwoKICAgICAgICAgIGFzc2VydF9pbmNsdWRlKAogICAgICAgICAgICBFdE9yYmkuZ2V0X3R6b25lKDpsb2NhbCksCiAgICAgICAgICAgIFsgRXRPcmJpLmdldF90em9uZSgnQXNpYS9TaGFuZ2hhaScpLAogICAgICAgICAgICAgIEV0T3JiaS5nZXRfdHpvbmUoJ0FzaWEvQ2hvbmdxaW5nJykgXSkKICAgICAgICBlbmQKCiAgICAgIGVuc3VyZQoKICAgICAgICBUaW1lLl96b25lID0gbmlsCiAgICAgIGVuZAoKICAgICAgYXNzZXJ0IEV0T3JiaS5nZXRfdHpvbmUoOmxvY2FsKSwgY3VycmVudAogICAgZW5kCgogICAgeyAjIGZvciBydWZ1cy1zY2hlZHVsZXIgZ2gtMjI4CgogICAgICAnQXNpYS9Ub2t5bycgPT4gJXdbCiAgICAgICAgQXNpYS9Ub2t5byBdLAogICAgICAnQXNpYS9TaGFuZ2hhaScgPT4gJXdbCiAgICAgICAgQXNpYS9TaGFuZ2hhaSBBc2lhL0Nob25ncWluZyBdLAogICAgICAnRXVyb3BlL1p1cmljaCcgPT4gJXdbCiAgICAgICAgRXVyb3BlL1p1cmljaCBBZnJpY2EvQ2V1dGEgd2luZG93czpDRVQgXSwKICAgICAgJ0V1cm9wZS9Mb25kb24nID0+ICV3WwogICAgICAgIEV1cm9wZS9Mb25kb24gRXVyb3BlL0JlbGZhc3Qgd2luZG93czpHTVQgd2luZG93czpHTVQtMCBdLAoKICAgIH0uZWFjaCBkbyB8em9uZSwgdGFyZ2V0c3wKCiAgICAgIHRlc3QgInJldHVybnMgdGhlIGN1cnJlbnQgdGltZXpvbmUgZm9yIDpjdXJyZW50IGluICN7em9uZX0iIGRvCgogICAgICAgIGluX3pvbmUoem9uZSkgZG8KCiAgICAgICAgICBhc3NlcnRfaW5jbHVkZSBFdE9yYmkuZ2V0X3R6b25lKDpsb2NhbCksIHNlbGVjdF96b25lcyh0YXJnZXRzKQogICAgICAgIGVuZAogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgImRvZXNuJ3QgbWluZCBiZWluZyBnaXZlbiBhIFRaSW5mbzo6VGltZXpvbmUiIGRvCgogICAgICB0eiA9IDo6VFpJbmZvOjpUaW1lem9uZS5nZXQoJ1p1bHUnKQogICAgICBjbGFzcyA8PCB0egogICAgICAgIGRlZiA8PT4odHopCiAgICAgICAgICAjcmV0dXJuIG5pbCB1bmxlc3MgdHouaXNfYT8oVGltZXpvbmUpCiAgICAgICAgICBpZGVudGlmaWVyIDw9PiB0ei5pZGVudGlmaWVyCiAgICAgICAgZW5kCiAgICAgIGVuZAogICAgICAgICMgc2ltdWxhdGUgdHppbmZvIDAuMy41MyBpc3N1ZQoKICAgICAgYXNzZXJ0IEV0T3JiaS5nZXRfdHpvbmUodHopLCA6OlRaSW5mbzo6VGltZXpvbmUuZ2V0KCdadWx1JykKICAgIGVuZAogIGVuZAoKICBncm91cCAnLmRldGVybWluZV9sb2NhbF90em9uZScgZG8KCiAgICB0ZXN0ICdmYXZvdXJzIHRoZSBsb2NhbCB0aW1lem9uZScgZG8KCiAgICAgIGluX3pvbmUoOm5vX2Vudl90eikgZG8KCiAgICAgICAgVGltZS5fem9uZSA9ICdDYXBlIFZlcmRlIFN0YW5kYXJkIFRpbWUnCiAgICAgICAgRXRPcmJpLl9vc196b25lID0gJycgIyBmb3JjZSAjb3NfdHogdG8gcmV0dXJuIG5pbAoKICAgICAgICBhc3NlcnQgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZS5uYW1lLCAnQXRsYW50aWMvQ2FwZV9WZXJkZScKICAgICAgZW5kCiAgICBlbmQKCiAgICB0ZXN0ICdmYXZvdXJzIHRoZSBsb2NhbCB0aW1lem9uZScgZG8KCiAgICAgIGluX3pvbmUoJ0V1cm9wZS9CZXJsaW4nKSBkbwoKICAgICAgICBhc3NlcnRfaW5jbHVkZSgKICAgICAgICAgIEV0T3JiaS5kZXRlcm1pbmVfbG9jYWxfdHpvbmUsCiAgICAgICAgICBzZWxlY3Rfem9uZXMoJXdbIEV1cm9wZS9CZXJsaW4gQWZyaWNhL0NldXRhIHdpbmRvd3M6Q0VUIF0pKQogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ2Zhdm91cnMgdGhlIGxvY2FsIHRpbWV6b25lIChNaXR0ZWxldXJvcGFlaXNjaGUgU29tbWVyemVpdCknIGRvCgogICAgICBpbl96b25lKDpub19lbnZfdHopIGRvCgogICAgICAgIFRpbWUuX3pvbmUgPSAiTWl0dGVsZXVyb3BceEU0aXNjaGUgU29tbWVyemVpdCIKICAgICAgICBFdE9yYmkuX29zX3pvbmUgPSAnJyAjIGZvcmNlICNvc190eiB0byByZXR1cm4gbmlsCgogICAgICAgIGFzc2VydF9ub3RfZXJyb3IgeyBFdE9yYmkuZGV0ZXJtaW5lX2xvY2FsX3R6b25lIH0KICAgICAgZW5kCiAgICBlbmQKCiAgICB0ZXN0ICdyZXR1cm5zIHRoZSBsb2NhbCB0aW1lem9uZScgZG8KCiAgICAgIGluX3pvbmUoJ0FtZXJpY2EvSmFtYWljYScpIGRvCgogICAgICAgIGFzc2VydF9pbmNsdWRlKAogICAgICAgICAgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZS5uYW1lLAogICAgICAgICAgWyAnQW1lcmljYS9KYW1haWNhJywgJ0FtZXJpY2EvQXRpa29rYW4nLCAnRVNUJyBdKQogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ3JldHVybnMgdGhlIFJhaWxzLXByb3ZpZGVkIFRpbWUuem9uZS50emluZm8gaWYgYXZhaWxhYmxlJyBkbwoKICAgICAgIyBodHRwOi8vYXBpLnJ1YnlvbnJhaWxzLm9yZy9jbGFzc2VzL0FjdGl2ZVN1cHBvcnQvVGltZVpvbmUuaHRtbAoKICAgICAgVGltZS5hY3RpdmVfc3VwcG9ydF96b25lID0gJ0V1cm9wZS9WaWxuaXVzJwoKICAgICAgaW5fem9uZSg6bm9fZW52X3R6KSBkbwogICAgICAgIGFzc2VydCBFdE9yYmkuZGV0ZXJtaW5lX2xvY2FsX3R6b25lLmNsYXNzLCA6OlRaSW5mbzo6RGF0YVRpbWV6b25lCiAgICAgICAgYXNzZXJ0IEV0T3JiaS5kZXRlcm1pbmVfbG9jYWxfdHpvbmUubmFtZSwgJ0V1cm9wZS9WaWxuaXVzJwogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgImdpdmVzIHByZWNlZGVuY2UgdG8gRU5WWydUWiddIG92ZXIgUmFpbHMgVGltZS56b25lLnR6aW5mbyIgZG8KCiAgICAgICMgaHR0cDovL2FwaS5ydWJ5b25yYWlscy5vcmcvY2xhc3Nlcy9BY3RpdmVTdXBwb3J0L1RpbWVab25lLmh0bWwKCiAgICAgIFRpbWUuYWN0aXZlX3N1cHBvcnRfem9uZSA9ICdFdXJvcGUvVmlsbml1cycKCiAgICAgIGluX3pvbmUoJ0FzaWEvVGVocmFuJykgZG8KCiAgICAgICAgYXNzZXJ0IEV0T3JiaS5kZXRlcm1pbmVfbG9jYWxfdHpvbmUuY2xhc3MsIDo6VFpJbmZvOjpEYXRhVGltZXpvbmUKICAgICAgICBhc3NlcnQgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZS5uYW1lLCAnQXNpYS9UZWhyYW4nCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcuem9uZScgZG8KCiAgICB0ZXN0ICdpcyBhbiBhbGlhcyB0byAuZGV0ZXJtaW5lX2xvY2FsX3R6b25lJyBkbwoKICAgICAgaW5fem9uZSg6bm9fZW52X3R6KSBkbwoKICAgICAgICBUaW1lLl96b25lID0gJ0NhcGUgVmVyZGUgU3RhbmRhcmQgVGltZScKICAgICAgICBFdE9yYmkuX29zX3pvbmUgPSAnJyAjIGZvcmNlICNvc190eiB0byByZXR1cm4gbmlsCgogICAgICAgIGFzc2VydCBFdE9yYmkuem9uZS5uYW1lLCAnQXRsYW50aWMvQ2FwZV9WZXJkZScKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy5ub3cnIGRvCgogICAgdGVzdCAncmV0dXJucyBhIGN1cnJlbnQsIGxvY2FsIEVvVGltZSBpbnN0YW5jZScgZG8KCiAgICAgIGluX3pvbmUgJ0FzaWEvU2hhbmdoYWknIGRvCgogICAgICAgIHQgPSBFdE9yYmkubm93CiAgICAgICAgbiA9IFRpbWUubm93CgogICAgICAgIGFzc2VydCB0LnNlY29uZHMgPiAobiAtIDEpLnRvX2YKICAgICAgICBhc3NlcnQgdC5zZWNvbmRzIDwgKG4gKyAxKS50b19mCgogICAgICAgIGFzc2VydF9pbmNsdWRlIHQuem9uZS5uYW1lLCBbICdBc2lhL1NoYW5naGFpJywgJ0FzaWEvQ2hvbmdxaW5nJyBdCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcubWFrZV90aW1lJyBkbwoKICAgIHRlc3QgJ3JldHVybnMgYW4gRW9UaW1lIGluc3RhbmNlIGFzIGlzJyBkbwoKICAgICAgdDAgPSBFdE9yYmkucGFyc2UoJzIwMTctMDMtMjEgMTI6MDA6MzQgQXNpYS9VbGFuX0JhdG9yJykKICAgICAgdDEgPSBFdE9yYmkubWFrZV90aW1lKHQwKQoKICAgICAgYXNzZXJ0IHQxLmNsYXNzLCA6OkV0T3JiaTo6RW9UaW1lCiAgICAgIGFzc2VydCB0MSwgdDAKICAgICAgYXNzZXJ0IHQxLm9iamVjdF9pZCwgdDAub2JqZWN0X2lkCiAgICBlbmQKCiAgICB0ZXN0ICdyZXR1cm5zIGFuIEVvVGltZSBpbnN0YW5jZSBhcyBpcycgZG8KCiAgICAgIHQwID0gRXRPcmJpLnBhcnNlKCcyMDE3LTAzLTIxIDEyOjAwOjM0IEFzaWEvVWxhbl9CYXRvcicpCiAgICAgIHQxID0gRXRPcmJpLm1ha2VfdGltZSh0MCwgdDAuem9uZSkKCiAgICAgIGFzc2VydCB0MS5jbGFzcywgOjpFdE9yYmk6OkVvVGltZQogICAgICBhc3NlcnQgdDEsIHQwCiAgICAgIGFzc2VydCB0MS5vYmplY3RfaWQsIHQwLm9iamVjdF9pZAogICAgZW5kCgogICAgWwogICAgICBbICdhbiBFb1RpbWUgaW5zdGFuY2UnLAogICAgICAgIG5pbCwKICAgICAgICBsYW1iZGEgeyBFdE9yYmkucGFyc2UoJzIwMTctMDMtMjEgMTI6MDA6MzQgQXNpYS9VbGFuX0JhdG9yJykgfSwKICAgICAgICAnb3QgMjAxNy0wMy0yMSAxMjowMDozNCArMDg6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYSBsb2NhbCB0aW1lJywKICAgICAgICAnQXNpYS9UYmlsaXNpJywKICAgICAgICBUaW1lLnBhcnNlKCcyMDE2LTExLTAxIDEyOjMwOjA5ICswNDowMCcpLCAjIHVzZSBjdXN0b20gdHogOi0oCiAgICAgICAgJ290IDIwMTYtMTEtMDEgMTI6MzA6MDkgKzA0OjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2FuIFVUQyB0aW1lJywKICAgICAgICBuaWwsCiAgICAgICAgVGltZS51dGMoMjAxNiwgMTEsIDAxLCAxMiwgMzAsIDkpLAogICAgICAgICdvdCAyMDE2LTExLTAxIDEyOjMwOjA5ICswMDowMCBkc3Q6ZmFsc2UnIF0sCgogICAgICBbICdhIERhdGUgaW5zdGFuY2UnLAogICAgICAgIG5pbCwKICAgICAgICBEYXRlLm5ldygyMDE2LCAxMSwgMDEpLAogICAgICAgIGxhbWJkYSB7CiAgICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoCiAgICAgICAgICAgIFRpbWUubG9jYWwoMjAxNiwgMTEsIDAxKS50b19mLCBuaWwKICAgICAgICAgICkudG9fZGVidWdfcyB9IF0sCgogICAgICBbICdhIFN0cmluZycsCiAgICAgICAgbmlsLAogICAgICAgICcyMDE2LTExLTAxIDEyOjMwOjA5JywKICAgICAgICBsYW1iZGEgewogICAgICAgICAgRXRPcmJpOjpFb1RpbWUubmV3KAogICAgICAgICAgICBUaW1lLmxvY2FsKDIwMTYsIDExLCAwMSwgMTIsIDMwLCA5KS50b19mLCBuaWwpIH0gXSwKCiAgICAgIFsgJ2EgU3RyaW5nJywKICAgICAgICAnQW1lcmljYS9DaGljYWdvJywKICAgICAgICAnMjAxNi0wOC0wMSAxMjozMDoxMCcsCiAgICAgICAgJ290IDIwMTYtMDgtMDEgMTI6MzA6MTAgLTA1OjAwIGRzdDp0cnVlJyBdLAogICAgICAjWyAnYSBTdHJpbmcnLAogICAgICAjICAnQW1lcmljYS9DaGljYWdvJywKICAgICAgIyAgJzIwMTYtMTEtMDEgMTI6MzA6MDknLAogICAgICAjICAnb3QgMjAxNi0xMS0wMSAxMjozMDowOSAtMDU6MDAgZHN0OnRydWUnIF0sICMgZmFpbHMgb24gQXBwdmV5b3IgOi0oCiAgICAgIFsgJ2EgU3RyaW5nJywKICAgICAgICAnQW1lcmljYS9DaGljYWdvJywKICAgICAgICAnMjAxNi0xMS0wNiAxMjozMDowOCcsCiAgICAgICAgJ290IDIwMTYtMTEtMDYgMTI6MzA6MDggLTA2OjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2EgU3RyaW5nJywKICAgICAgICAnRXVyb3BlL1BhcmlzJywKICAgICAgICAnMjAxNi0xLTEgMTI6MzA6MDcnLAogICAgICAgICdvdCAyMDE2LTAxLTAxIDEyOjMwOjA3ICswMTowMCBkc3Q6ZmFsc2UnIF0sCiAgICAgIFsgJ2EgU3RyaW5nJywKICAgICAgICAnRXVyb3BlL1BhcmlzJywKICAgICAgICAnMjAxNi04LTEgMTI6MzA6MDcnLAogICAgICAgICdvdCAyMDE2LTA4LTAxIDEyOjMwOjA3ICswMjowMCBkc3Q6dHJ1ZScgXSwKCiAgICAgIFsgJ2EgWnVsdSBTdHJpbmcnLAogICAgICAgIG5pbCwKICAgICAgICAnMjAxNi0xMS0wMSAxMjozMDowOVonLAogICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldyhUaW1lLnV0YygyMDE2LCAxMSwgMDEsIDEyLCAzMCwgOSkudG9fZiwgJ1p1bHUnKSBdLAoKICAgICAgWyAnYSBzcyswMTowMCBTdHJpbmcnLAogICAgICAgIG5pbCwKICAgICAgICAnMjAxNi0xMS0wMSAxMjozMDowOSswMTowMCcsCiAgICAgICAgJ290IDIwMTYtMTEtMDEgMTI6MzA6MDkgKzAxOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2Egc3MtMDEgU3RyaW5nJywKICAgICAgICBuaWwsCiAgICAgICAgJzIwMTYtMTEtMDEgMTI6MzA6MDktMDEnLAogICAgICAgICdvdCAyMDE2LTExLTAxIDEyOjMwOjA5IC0wMTowMCBkc3Q6ZmFsc2UnIF0sCgogICAgICBbICdhIFN0cmluZyB3aXRoIGFuIGV4cGxpY2l0IHRpbWUgem9uZScsCiAgICAgICAgbmlsLAogICAgICAgICcyMDE2LTA1LTAxIDEyOjMwOjA5IEFtZXJpY2EvTmV3X1lvcmsnLAogICAgICAgICdvdCAyMDE2LTA1LTAxIDEyOjMwOjA5IC0wNDowMCBkc3Q6dHJ1ZScgXSwKCiAgICAgIFsgJ2EgTnVtZXJpYycsCiAgICAgICAgbmlsLAogICAgICAgIDM2MDAsCiAgICAgICAgbGFtYmRhIHsgWyBUaW1lLm5vdyArIDM2MDAgLSAwLjM1LCBUaW1lLm5vdyArIDM2MDAgKyAwLjM1IF0gfSBdLAoKICAgICAgWyAnYW4gYXJyYXkgWyB5LCBtLCBkLCAuLi4gXScsCiAgICAgICAgJ0V1cm9wZS9Nb3Njb3cnLAogICAgICAgIFsgWyAyMDE3LCAyLCAyOCBdIF0sCiAgICAgICAgJ290IDIwMTctMDItMjggMDA6MDA6MDAgKzAzOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2FuIGFycmF5IG9mIGFyZ3MgKHksIG0sIGQsIC4uLiknLAogICAgICAgICdFdXJvcGUvTW9zY293JywKICAgICAgICBbIDIwMTcsIDEsIDMxLCAxMCBdLAogICAgICAgICdvdCAyMDE3LTAxLTMxIDEwOjAwOjAwICswMzowMCBkc3Q6ZmFsc2UnIF0sCgogICAgICBbICdhbiBhcnJheSBvZiBhcmdzIGFuZCBhIHpvbmUgYXMgbGFzdCBhcmcnLAogICAgICAgIG5pbCwKICAgICAgICBbIDIwMTcsIDEsIDMxLCAxMiwgJ0V1cm9wZS9Nb3Njb3cnIF0sCiAgICAgICAgJ290IDIwMTctMDEtMzEgMTI6MDA6MDAgKzAzOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2Egc3RyaW5nIGFuZCBhIHpvbmUgYXMgbGFzdCBhcmcnLAogICAgICAgIG5pbCwKICAgICAgICBbICcyMDE2LTA1LTAxIDEyOjMwOjA5JywgJ0FtZXJpY2EvQ2hpY2FnbycgXSwKICAgICAgICAnb3QgMjAxNi0wNS0wMSAxMjozMDowOSAtMDU6MDAgZHN0OnRydWUnIF0sCgogICAgICBbICdhIHN0cmluZyBhbmQgYW4gb3ZlcnJpZGluZyB6b25lIGFzIGxhc3QgYXJnJywKICAgICAgICBuaWwsCiAgICAgICAgWyAnMjAxNi0wNS0wMSAxMTozMDowOSBBbWVyaWNhL05ld19Zb3JrJywgJ0FtZXJpY2EvQ2hpY2FnbycgXSwKICAgICAgICAnb3QgMjAxNi0wNS0wMSAxMTozMDowOSAtMDU6MDAgZHN0OnRydWUnIF0sCgogICAgICBbICdhbiBhcnJheSBvZiBhcmdzIGFuZCBhIFRaSW5mbyB6b25lIGFzIGxhc3QgYXJnJywKICAgICAgICBuaWwsCiAgICAgICAgWyAyMDE3LCAxLCAzMSwgRXRPcmJpLmdldF90em9uZSgnRXVyb3BlL09zbG8nKSBdLAogICAgICAgICdvdCAyMDE3LTAxLTMxIDAwOjAwOjAwICswMTowMCBkc3Q6ZmFsc2UnIF0sCgogICAgICBbICdhIHN0cmluZyBhbmQgYSBUWkluZm8gem9uZSBhcyBsYXN0IGFyZycsCiAgICAgICAgbmlsLAogICAgICAgIFsgJzIwMTctMDEtMzEgMTI6MzAnLCBFdE9yYmkuZ2V0X3R6b25lKCdFdXJvcGUvT3NsbycpIF0sCiAgICAgICAgJ290IDIwMTctMDEtMzEgMTI6MzA6MDAgKzAxOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2Egc3RyaW5nJywKICAgICAgICAnQW1lcmljYS9DaGljYWdvJywKICAgICAgICBsYW1iZGEgeyBbIFRpbWUucGFyc2UoJzIwMjEtMDMtMTEnKSBdIH0sCiAgICAgICAgd2luZG93cz8gPyAnQ1NUNkNEVCcgOiAnQW1lcmljYSAtIENoaWNhZ28nLAogICAgICAgIGxhbWJkYSB7IHx0fCB0LnpvbmUudG9fcyB9IF0sCgogICAgICBbICdhIHN0cmluZycsCiAgICAgICAgJ0FtZXJpY2EvTmV3X1lvcmsnLAogICAgICAgIGxhbWJkYSB7IFsgVGltZS5wYXJzZSgnMjAyMS0wMy0xMScpIF0gfSwKICAgICAgICB3aW5kb3dzPyA/ICV3WyBFU1Q1RURUIEVTVCBdIDogJ0FtZXJpY2EgLSBOZXcgWW9yaycsCiAgICAgICAgI3dpbmRvd3M/ID8gJ0VTVCcgOiAnQW1lcmljYSAtIE5ldyBZb3JrJywKICAgICAgICBsYW1iZGEgeyB8dHwgdC56b25lLnRvX3MgfSBdLAoKICAgIF0uZWFjaCBkbyB8bmFtZSwgem9uZSwgYXJncywgYXNzZXJ0ZWQsIHRyYW5zZm9ybWVyfAoKICAgICAgdGl0bGUgPSAidHVybnMgI3tuYW1lfSBpbnRvIGFuIEVvVGltZSBpbnN0YW5jZSIKICAgICAgdGl0bGUgKz0gIiBpbiAje3pvbmV9IiBpZiB6b25lCgogICAgICB0ZXN0KHRpdGxlKSBkbwoKICAgICAgICBlb3QsIGV4cCA9CiAgICAgICAgICBpbl96b25lKHpvbmUpIGRvCgogICAgICAgICAgICBhcyA9IGFyZ3MuaXNfYT8oUHJvYykgPyBhcmdzLmNhbGwgOiBhcmdzCgogICAgICAgICAgICB0ID0gYXMuaXNfYT8oQXJyYXkpID8KICAgICAgICAgICAgICBFdE9yYmkubWFrZV90aW1lKCphcykgOgogICAgICAgICAgICAgIEV0T3JiaS5tYWtlX3RpbWUoYXMpCiAgICAgICAgICAgIHggPSBhc3NlcnRlZC5pc19hPyhQcm9jKSA/CiAgICAgICAgICAgICAgYXNzZXJ0ZWQuY2FsbCA6CiAgICAgICAgICAgICAgYXNzZXJ0ZWQKCiAgICAgICAgICAgIHQgPSB0cmFuc2Zvcm1lci5pc19hPyhQcm9jKSA/IHRyYW5zZm9ybWVyW3RdIDogdAoKICAgICAgICAgICAgWyB0LCB4IF0KICAgICAgICAgIGVuZAojcCBlb3QudG9fZGVidWdfcwojcCBlb3QudXRjX29mZnNldAojcCBlb3Quc2VuZCg6dG9fdGltZSkudG9fcwojcCBlb3Quc2VuZCg6dG9fdGltZSkudXRjX29mZnNldAoKICAgICAgICBjYXNlIGV4cAogICAgICAgIHdoZW4gU3RyaW5nCiAgICAgICAgICBhc3NlcnQgZW90LnJlc3BvbmRfdG8/KDp0b19kZWJ1Z19zKSA/IGVvdC50b19kZWJ1Z19zIDogZW90LCBleHAKICAgICAgICB3aGVuIEFycmF5CiAgICAgICAgICBpZiBleHAuY29sbGVjdCgmOmNsYXNzKS51bmlxID09IFsgU3RyaW5nIF0KICAgICAgICAgICAgYXNzZXJ0IGV4cC5pbmNsdWRlPyhlb3QpCiAgICAgICAgICBlbHNlCiAgICAgICAgICAgIGFzc2VydCBlb3QgPiBleHBbMF0KICAgICAgICAgICAgYXNzZXJ0IGVvdCA8IGV4cFsxXQogICAgICAgICAgZW5kCiAgICAgICAgZWxzZQogICAgICAgICAgYXNzZXJ0IGVvdCwgZXhwCiAgICAgICAgZW5kCiAgICAgIGVuZAogICAgZW5kCgojICAgIHRlc3QgJ2FjY2VwdHMgYSBkdXJhdGlvbiBTdHJpbmcnIyBkbwojIwojIyAgICAgIGV4cGVjdCgKIyMgICAgICAgIEV0T3JiaS5tYWtlX3RpbWUoJzFoJykKIyMgICAgICApLnRvIGJlX2JldHdlZW4oCiMjICAgICAgICBUaW1lLm5vdyArIDM2MDAgLSAxLCBUaW1lLm5vdyArIDM2MDAgKyAxCiMjICAgICAgKQojIyAgICBlbmQKIyAgIwojICAjIFN0cmluZyBwYXJzaW5nIGlzIGZ1Z2l0J3Mgam9iLiBFdC1vcmJpIHNob3VsZCBiZSBhIGRlcGVuZGVuY3kgb2YKIyAgIyBmdWdpdCwgbm90IHRoZSBvdGhlciB3YXkgYXJvdW5kLiBXaGVuIGZ1Z2l0IGlzIHByZXNlbnQsIHRoaXMKIyAgIyBzcGVjIHNob3VsZCBzdWNjZWVkLCBlbHNlIHRlc3Qgc2hvdWxkIG5vdC4KCiAgICB0ZXN0ICdhY2NlcHRzIGEgUmFpbHMgVGltZVdpdGhab25lJyBkbwoKICAgICAgIyBodHRwOi8vYXBpLnJ1YnlvbnJhaWxzLm9yZy9jbGFzc2VzL0FjdGl2ZVN1cHBvcnQvVGltZVdpdGhab25lLmh0bWwKCiAgICAgIG4gPSBUaW1lLm5vdwogICAgICB6ID0gRXRPcmJpLmdldF90em9uZSgnUGFjaWZpYy9FYXN0ZXInKQoKICAgICAgI3QgPSBBY3RpdmVTdXBwb3J0OjpUaW1lV2l0aFpvbmUubmV3KG4sIHopCiAgICAgICAgIwogICAgICB0ID0gbgogICAgICB0Lmluc3RhbmNlX2V2YWwgeyBAeiA9IHogfQogICAgICBjbGFzcyA8PCB0OyBkZWYgdGltZV96b25lOyBAejsgZW5kOyBlbmQKICAgICAgICAjCiAgICAgICAgIyBmYWtlIGFuIEFjdGl2ZVN1cHBvcnQ6OlRpbWVXaXRoWm9uZSBpbnN0YW5jZSB3aXRoIGEgI3RpbWVfem9uZQoKICAgICAgZW90ID0gRXRPcmJpLm1ha2VfdGltZSh0KQoKICAgICAgYXNzZXJ0IGVvdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgYXNzZXJ0IGVvdC5zZWNvbmRzLCB0LnRvX2YKICAgICAgYXNzZXJ0IGVvdC56b25lLCB0LnRpbWVfem9uZQogICAgZW5kCgogICAgdGVzdCAncmVqZWN0cyBhIFRpbWUgaW4gYSBub24tbG9jYWwgYW1iaWd1b3VzIHRpbWV6b25lJyBkbwoKICAgICAgdCA9IFRpbWUubG9jYWwoMjAxNiwgMTEsIDAxLCAxMiwgMzAsIDkpCiAgICAgIGNsYXNzIDw8IHQ7IGRlZiB6b25lOyAnRUNUJzsgZW5kOyBlbmQKCiAgICAgIGluX3pvbmUgJ0FzaWEvVGJpbGlzaScgZG8KCiAgICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgICAgbGFtYmRhIHsgRXRPcmJpLm1ha2VfdGltZSh0KSB9LAogICAgICAgICAgQXJndW1lbnRFcnJvciwgL1xBQ2Fubm90IGRldGVybWluZSB0aW1lem9uZSBmcm9tICJFQ1QiLykKICAgICAgZW5kCiAgICBlbmQKCiAgICB0ZXN0ICdyZWplY3RzIHVucGFyc2FibGUgaW5wdXQnIGRvCgogICAgICBhc3NlcnRfZXJyb3IoCiAgICAgICAgbGFtYmRhIHsgRXRPcmJpLm1ha2VfdGltZSgneHh4JykgfSwKICAgICAgICBBcmd1bWVudEVycm9yLCAnTm8gdGltZSBpbmZvcm1hdGlvbiBpbiAieHh4IicpCiAgICAgICAgICAjCiAgICAgICAgICAjIHN0cmFpZ2h0IG91dCBvZiBUaW1lLnBhcnNlKCkKCiAgICAgIGFzc2VydF9lcnJvcigKICAgICAgICBsYW1iZGEgeyBFdE9yYmkubWFrZV90aW1lKE9iamVjdC5uZXcpIH0sCiAgICAgICAgQXJndW1lbnRFcnJvciwgL1xBQ2Fubm90IHR1cm4gLykKICAgIGVuZAogIGVuZAoKICBncm91cCAnLndpbmRvd3Nfem9uZV9uYW1lJyBkbwoKICAgIHsgWyAnQXNpYS9Ub2t5bycsICcyMDE4LTA1LTIzJyBdID0+ICdKU1QtOScsCiAgICAgIFsgJ0FzaWEvS29sa2F0YScsICcyMDE4LTA3LTAxJyBdID0+ICdJU1QtNTozMCcsCiAgICAgIFsgJ0FzaWEvVGVocmFuJywgJzIwMTktMDEtMDknIF0gPT4gJ19BVC0zOjMwQXNpYS9UZWhyYW4nLCAjJ0lSVC0zOjMwJywKICAgICAgWyAnQXNpYS9UZWhyYW4nLCAnMjAxOS0wNy0wOScgXSA9PiAnX0FULTQ6MzBBc2lhL1RlaHJhbicsICMnSVJULTQ6MzAnLAogICAgICBbICdBc2lhL1RiaWxpc2knLCAnMjAxOS0wMS0wOScgXSA9PiAnX0FULTQ6MDBBc2lhL1RiaWxpc2knLAogICAgICBbICdBc2lhL1NhbWFya2FuZCcsICcyMDE3LTExLTAxJyBdID0+ICdfQVMtNTowMEFzaWEvU2FtYXJrYW5kJywKICAgICAgWyAnRXVyb3BlL0JlcmxpbicsICcyMDE4LTAxLTAxJyBdID0+ICdDRVQtMUNFU1QnLAogICAgICBbICdFdXJvcGUvQmVybGluJywgJzIwMTgtMDctMDEnIF0gPT4gJ0NFVC0yQ0VTVCcsCiAgICAgIFsgJ0FtZXJpY2EvTmV3X1lvcmsnLCAnMjAxOC0wMS0wMScgXSA9PiAnRVNUNUVEVCcsCiAgICAgIFsgJ0FtZXJpY2EvTmV3X1lvcmsnLCAnMjAxOC0wNy0wMScgXSA9PiAnRVNUNEVEVCcsCiAgICAgIFsgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnLCAnMjAxNy0xMC0zMCcgXSA9PiAnUFNUN1BEVCcsCiAgICAgIFsgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnLCAnMjAxOS0wMS0wMScgXSA9PiAnUFNUOFBEVCcsCgogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTA3LTI4JyBdID0+ICdFRVQtM0VFU1QnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4JyBdID0+ICdFRVQtM0VFU1QnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEyLTI4JyBdID0+ICdFRVQtMkVFU1QnLAoKICAgIH0uZWFjaCBkbyB8KHpvbmUsIHRpbWUpLCB2fAoKICAgICAgdGVzdCAicmV0dXJucyAje3YuaW5zcGVjdH0gZm9yICN7em9uZS5pbnNwZWN0fSBhdCAje3RpbWV9IiBkbwoKICAgICAgICBhc3NlcnQgRXRPcmJpLndpbmRvd3Nfem9uZV9uYW1lKHpvbmUsIFRpbWUucGFyc2UodGltZSkpLCB2CiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcuem9uZV9hYmJyZXZpYXRpb24nIGRvCgogICAgeyBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTA3LTI4JyBdID0+ICdFRVNUJywKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0xMC0yOCcgXSA9PiAnRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTItMjgnIF0gPT4gJ0VFVCcsCgogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTAzLTI1IDAyOjAwJyBdID0+ICdFRVQnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTAzLTI1IDAzOjAwJyBdID0+ICdFRVNUJywKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0wMy0yNSAwNDowMCcgXSA9PiAnRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMDMtMjUgMDU6MDAnIF0gPT4gJ0VFU1QnLAoKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0xMC0yOCAwMDowMCcgXSA9PiAnRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTAtMjggMDI6NTknIF0gPT4gJ0VFU1QnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4IDAzOjAwJyBdID0+ICdFRVQnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4IDAzOjMwJyBdID0+ICdFRVQnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4IDA0OjMwJyBdID0+ICdFRVQnLAoKICAgICAgIyBpbiBjYXNlIG9mIGFtYmlndWl0eSwgLnpvbmVfYWJicmV2aWF0aW9uIGdvZXMgMiBob3VycyBmb3J3YXJkLi4uCgogICAgfS5lYWNoIGRvIHwoem9uZSwgdGltZSksIHZ8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7di5pbnNwZWN0fSBmb3IgI3t6b25lLmluc3BlY3R9IGF0ICN7dGltZX0iIGRvCgogICAgICAgIGFzc2VydCBFdE9yYmkuem9uZV9hYmJyZXZpYXRpb24oem9uZSwgVGltZS5wYXJzZSh0aW1lKSksIHYKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy50d2Vha196b25lX25hbWUnIGRvCgogICAgeyAnRVNUNUVEVCcgPT4gJ0VTVDVFRFQnLAogICAgICAnVVRDKzEyJyA9PiAnRXRjL0dNVC0xMicsCiAgICAgICdLb3JlYSBTdGFuZGFyZCBUaW1lJyA9PiAnQXNpYS9TZW91bCcsCgogICAgfS5lYWNoIGRvIHxuMCwgbjF8CgogICAgICB0ZXN0ICJ0dXJucyAje24wLmluc3BlY3R9IGludG8gI3tuMS5pbnNwZWN0fSIgZG8KCiAgICAgICAgYXNzZXJ0IEV0T3JiaS50d2Vha196b25lX25hbWUobjApLCBuMQogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnLmNocm9uaWNfZW5hYmxlZD8nIGRvCgogICAgdGVzdCAncmV0dXJucyB0cnVlIGJ5IGRlZmF1bHQnIGRvCgogICAgICBhc3NlcnQgRXRPcmJpLmNocm9uaWNfZW5hYmxlZD8KICAgIGVuZAogIGVuZAoKICBncm91cCAnLmNocm9uaWNfZW5hYmxlZD0nIGRvCgogICAgYWZ0ZXIgZG8KICAgICAgRXRPcmJpLmNocm9uaWNfZW5hYmxlZCA9IHRydWUKICAgIGVuZAoKICAgIHRlc3QgJ2xldHMgZGlzYWJsZSBjaHJvbmljIHBhcnNpbmcnIGRvCgogICAgICBFdE9yYmkuY2hyb25pY19lbmFibGVkID0gZmFsc2UKCiAgICAgIGFzc2VydF9mYWxzZSBFdE9yYmkuY2hyb25pY19lbmFibGVkPwogICAgZW5kCiAgZW5kCgojICBncm91cCAnLmFiYnJldmlhdGVfem9uZV9uYW1lJyBkbwojCiMgICAgewojCiMgICAgICAnRVNUNUVEVCcgPT4gJ0VTVDVFRFQnLAojICAgICAgJ0JTVCcgPT4gJXdbIEdCLUVpcmUgR0IgXSwKIyAgICAgICdVVEMrMTInID0+IG5pbCwKIwojICAgIH0uZWFjaCBkbyB8bjAsIG4xfAojCiMgICAgICBuMXMgPSBuMS5pbnNwZWN0CiMgICAgICBuMXMgPSBuMS5jb2xsZWN0KCY6aW5zcGVjdCkuam9pbignIG9yICcpIGlmIG4xLmlzX2E/KEFycmF5KQojCiMgICAgICB0ZXN0ICJ0dXJucyAje24wLmluc3BlY3R9IGludG8gI3tuMXN9IiBkbwojCiMgICAgICAgIGlmIG4xLmlzX2E/KEFycmF5KQojICAgICAgICAgIGFzc2VydCBuMSkudG8gaW5jbHVkZShFdE9yYmkuYWJicmV2aWF0ZV96b25lX25hbWUobjApKQojICAgICAgICBlbHNlCiMgICAgICAgICAgYXNzZXJ0IEV0T3JiaS5hYmJyZXZpYXRlX3pvbmVfbmFtZShuMCksIG4xKQojICAgICAgICBlbmQKIyAgICAgIGVuZAojICAgIGVuZAojICBlbmQKZW5kCgo= \ No newline at end of file From 8357f9c79316e1b9a31d16658d325990bc6d1f65 Mon Sep 17 00:00:00 2001 From: Oskar Eichler <62393985+OskarEichler@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:42:54 +0300 Subject: [PATCH 3/3] Store plain-text regression test for et-orbi PR #56 --- test/module_test.rb | 1035 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1034 insertions(+), 1 deletion(-) diff --git a/test/module_test.rb b/test/module_test.rb index 8de29d0..193fa14 100644 --- a/test/module_test.rb +++ b/test/module_test.rb @@ -1 +1,1034 @@ -IyBlbmNvZGluZzogVVRGLTgKCiMKIyBUZXN0aW5nIEV0T3JiaQojCiMgV2VkIE1hciAxMSAyMToxNzozNiBKU1QgMjAxNSwgcXVhdHJlIGFucy4uLiAocnVmdXMtc2NoZWR1bGVyKQojIFN1biBNYXIgMTkgMDU6MTY6MjggSlNUIDIwMTcKIyBGcmkgTWFyIDI0IDA0OjU1OjI1IEpTVCAyMDE3IOWck+OBleOCk+OBruWutgojCgoKZ3JvdXAgRXRPcmJpIGRvCgogIGJlZm9yZSBkbwoKICAgIEVOVlsnVFonXSA9IEVOVlsnX1RaJ10KICAgIFRpbWUuX3pvbmUgPSBuaWwKICAgIEV0T3JiaS5fb3Nfem9uZSA9IG5pbAogICAgVGltZS5hY3RpdmVfc3VwcG9ydF96b25lID0gbmlsCiAgZW5kCgogIGdyb3VwICcubGlzdF9pc284NjAxX3pvbmVzJyBkbwoKICAgIFsKCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDktMDEnLCAld1sgLTAxIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0wMTowMCcsICV3WyAtMDE6MDAgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5IC0wMScsICV3WyAtMDEgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5IC0wMTowMCcsICV3WyAtMDE6MDAgXSBdLAoKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0wMTozMCcsICV3WyAtMDE6MzAgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5IC0wMTozMCcsICV3WyAtMDE6MzAgXSBdLAoKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsIFtdIF0sCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDktMjUnLCBbXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTI1OjAwJywgW10gXSwKCiAgICBdLmVhY2ggZG8gfHN0cmluZywgem9uZXN8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7em9uZXMuaW5zcGVjdH0gZm9yICN7c3RyaW5nLmluc3BlY3R9IiBkbwoKICAgICAgICBhc3NlcnQgRXRPcmJpLmxpc3RfaXNvODYwMV96b25lcyhzdHJpbmcpLCB6b25lcwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKIyAgZ3JvdXAgJy5saXN0X29sc29uX3pvbmVzJyBkbwojCiMgICAgWwojCiMgICAgICBbICcxMS8wOS8yMDAyIEFtZXJpY2EvTmV3X1lvcmsnLAojICAgICAgICAld1sgQW1lcmljYS9OZXdfWW9yayBdIF0sCiMgICAgICBbICcxMS8wOS8yMDAyIEFtZXJpY2EvTmV3X1lvcmsgQXNpYS9TaGFuZ2hhaScsCiMgICAgICAgICV3WyBBbWVyaWNhL05ld19Zb3JrIEFzaWEvU2hhbmdoYWkgXSBdLAojICAgICAgWyAnQW1lcmljYS9OZXdfWW9yayBBc2lhL1NoYW5naGFpJywKIyAgICAgICAgJXdbIEFtZXJpY2EvTmV3X1lvcmsgQXNpYS9TaGFuZ2hhaSBdIF0sCiMgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4IEV0Yy9HTVQtMTEnLAojICAgICAgICAld1sgRXRjL0dNVC0xMSBdIF0sCiMKIyAgICAgIFsgJzIwMTgtMDktMDQgMDc6NTQ6NTggVVRDKzExJywKIyAgICAgICAgJXdbIFVUQysxMSBdIF0sCiMgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4IFVUQysxMSBFdGMvR01ULTExJywKIyAgICAgICAgJXdbIEV0Yy9HTVQtMTEgVVRDKzExIF0gXSwKIyAgICAgICAgICAjCiMgICAgICAgICAgIyBodHRwczovL2dpdGh1Yi5jb20vZmxvcmFpc29uL2Z1Z2l0L2lzc3Vlcy85CiMKIyAgICAgIFsgJzExLzA5LzIwMDIgMnV0b3BpYU5hZGE/M05hZGEnLAojICAgICAgICBbXSBdCiMKIyAgICBdLmVhY2ggZG8gfHN0cmluZywgem9uZXN8CiMKIyAgICAgIHRlc3QgInJldHVybnMgI3t6b25lcy5pbnNwZWN0fSBmb3IgI3tzdHJpbmcuaW5zcGVjdH0iIGRvCiMKIyAgICAgICAgYXNzZXJ0IEV0T3JiaS5saXN0X29sc29uX3pvbmVzKHN0cmluZyksIHpvbmVzKQojICAgICAgZW5kCiMgICAgZW5kCiMgIGVuZAoKICBncm91cCAnLmV4dHJhY3Rfem9uZScgZG8KCiAgICBbCgogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTAxJywgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsICctMDEnIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0wMTowMCcsIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDknLCAnLTAxOjAwJyBdIF0sCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDkgLTAxJywgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsICctMDEnIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOSAtMDE6MDAnLCBbICcyMDE2LTExLTAxIDEyOjMwOjA5JywgJy0wMTowMCcgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTAxOjMwJywgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsICctMDE6MzAnIF0gXSwKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOSAtMDE6MzAnLCBbICcyMDE2LTExLTAxIDEyOjMwOjA5JywgJy0wMTozMCcgXSBdLAoKICAgICAgWyAnMTEvMDkvMjAwMiBBbWVyaWNhL05ld19Zb3JrJywKICAgICAgICBbICcxMS8wOS8yMDAyJywgJ0FtZXJpY2EvTmV3X1lvcmsnIF0gXSwKICAgICAgWyAnMTEvMDkvMjAwMiBBbWVyaWNhL05ld19Zb3JrIEFzaWEvU2hhbmdoYWknLAogICAgICAgIFsgJzExLzA5LzIwMDInLCAnQXNpYS9TaGFuZ2hhaScgXSBdLAogICAgICBbICdBbWVyaWNhL05ld19Zb3JrIEFzaWEvU2hhbmdoYWknLAogICAgICAgIFsgJycsICdBc2lhL1NoYW5naGFpJyBdIF0sCiAgICAgIFsgJzIwMTgtMDktMDQgMDc6NTQ6NTggRXRjL0dNVC0xMScsCiAgICAgICAgWyAnMjAxOC0wOS0wNCAwNzo1NDo1OCcsICdFdGMvR01ULTExJyBdIF0sCiAgICAgIFsgJzIwMTgtMDktMDQgMDc6NTQ6NTggVVRDKzExJywKICAgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4JywgJ1VUQysxMScgXSBdLAogICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4IFVUQysxMSBFdGMvR01ULTExJywKICAgICAgICBbICcyMDE4LTA5LTA0IDA3OjU0OjU4JywgJ0V0Yy9HTVQtMTEnIF0gXSwKCiAgICAgIFsgJ1N1biBOb3YgMTggMTY6MDE6MDAgQXNpYS9TaW5nYXBvcmUgMjAxMicsCiAgICAgICAgWyAnU3VuIE5vdiAxOCAxNjowMTowMCAgMjAxMicsICdBc2lhL1NpbmdhcG9yZScgXSBdLAoKICAgICAgWyAnMjAxNi0xMS0wMSAxMjozMDowOScsIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDknLCBuaWwgXSBdLAogICAgICBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTI1JywgWyAnMjAxNi0xMS0wMSAxMjozMDowOS0yNScsIG5pbCBdIF0sCiAgICAgIFsgJzIwMTYtMTEtMDEgMTI6MzA6MDktMjU6MDAnLCBbICcyMDE2LTExLTAxIDEyOjMwOjA5LTI1OjAwJywgbmlsIF0gXSwKCiAgICAgIFsgJzExLzA5LzIwMDIgMnV0b3BpYU5hZGE/M05hZGEnLAogICAgICAgIFsgJzExLzA5LzIwMDIgMnV0b3BpYU5hZGE/M05hZGEnLCBuaWwgXSBdLAoKICAgICAgWyAnMjAxMi0xMC0yOCAwMzowMDowMCBFRVQnLAogICAgICAgIFsgJzIwMTItMTAtMjggMDM6MDA6MDAnLCAnRUVUJyBdIF0sCiAgICAgICNbICcyMDEyLTEwLTI4IDAzOjAwOjAwIEVFU1QnLAogICAgICAjICBbICcyMDEyLTEwLTI4IDAzOjAwOjAwJywgJ0VFU1QnIF0gXSwKICAgICAgWyAnMjAxMi0xMC0yOCAwMzowMDowMCBFdXJvcGUvVGFsbGlubicsCiAgICAgICAgWyAnMjAxMi0xMC0yOCAwMzowMDowMCcsICdFdXJvcGUvVGFsbGlubicgXSBdLAoKICAgIF0uZWFjaCB7IHxzdHIwLCAoc3RyMSwgem9uZSl8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7WyBzdHIxLCB6b25lIF0uaW5zcGVjdH0gZm9yICN7c3RyMC5pbnNwZWN0fSIgZG8KCiAgICAgICAgczEsIHogPSBFdE9yYmkuZXh0cmFjdF96b25lKHN0cjApCgogICAgICAgIGFzc2VydCBbIHMxLCB6IF0sIFsgc3RyMSwgem9uZSBdCiAgICAgIGVuZAogICAgfQogIGVuZAoKICBncm91cCAnLnBhcnNlJyBkbwoKICAgIHRlc3QgJ3BhcnNlcyBhIHRpbWUgc3RyaW5nIHdpdGhvdXQgYSB0aW1lem9uZScgZG8KCiAgICAgIG90ID0gaW5fem9uZSgnRXVyb3BlL01vc2NvdycpIHsgRXRPcmJpLnBhcnNlKCcyMDE1LzAzLzA4IDAxOjU5OjU5JykgfQoKICAgICAgdCA9IG90CiAgICAgIHUgPSBvdC51dGMKCiAgICAgIGFzc2VydCB0LnRvX2ksIDE0MjU3NjkxOTkKICAgICAgYXNzZXJ0IHUudG9faSwgMTQyNTc2OTE5OQoKICAgICAgYXNzZXJ0KAogICAgICAgIHQuc3RyZnRpbWUoJyVZLyVtLyVkICVIOiVNOiVTICVaICV6JykgKyAiICN7dC5pc2RzdH0iLAogICAgICAgICcyMDE1LzAzLzA4IDAxOjU5OjU5IE1TSyArMDMwMCBmYWxzZScpCgogICAgICBhc3NlcnQgdS50b19kZWJ1Z19zLCAndCAyMDE1LTAzLTA3IDIyOjU5OjU5ICswMDowMCBkc3Q6ZmFsc2UnCiAgICBlbmQKCiAgICB0ZXN0ICdwYXJzZXMgYSB0aW1lIHN0cmluZyB3aXRoIGEgZnVsbCBuYW1lIHRpbWV6b25lJyBkbwoKICAgICAgb3QgPSBFdE9yYmkucGFyc2UoJzIwMTUvMDMvMDggMDE6NTk6NTkgQW1lcmljYS9Mb3NfQW5nZWxlcycpCgogICAgICB0ID0gb3QKICAgICAgdSA9IG90LnV0YwoKICAgICAgYXNzZXJ0IHQudG9faSwgMTQyNTgwODc5OQogICAgICBhc3NlcnQgdS50b19pLCAxNDI1ODA4Nzk5CgogICAgICBhc3NlcnQgdC50b19kZWJ1Z19zLCAnb3QgMjAxNS0wMy0wOCAwMTo1OTo1OSAtMDg6MDAgZHN0OmZhbHNlJwogICAgICBhc3NlcnQgdS50b19kZWJ1Z19zLCAndCAyMDE1LTAzLTA4IDA5OjU5OjU5ICswMDowMCBkc3Q6ZmFsc2UnCiAgICBlbmQKCiAgICB0ZXN0ICdwYXJzZXMgYSB0aW1lIHN0cmluZyB3aXRoIGEgZGVsdGEgdGltZXpvbmUnIGRvCgogICAgICBvdCA9IGluX3pvbmUoJ0V1cm9wZS9CZXJsaW4nKSB7IEV0T3JiaS5wYXJzZSgnMjAxNS0xMi0xMyAxMjozMCAtMDIwMCcpIH0KCiAgICAgIHQgPSBvdAogICAgICB1ID0gb3QudXRjCgogICAgICBhc3NlcnQgdC50b19pLCAxNDUwMDE3MDAwCiAgICAgIGFzc2VydCB1LnRvX2ksIDE0NTAwMTcwMDAKCiAgICAgIGFzc2VydCB0LnRvX2RlYnVnX3MsICdvdCAyMDE1LTEyLTEzIDEyOjMwOjAwIC0wMjowMCBkc3Q6ZmFsc2UnCiAgICAgIGFzc2VydCB1LnRvX2RlYnVnX3MsICd0IDIwMTUtMTItMTMgMTQ6MzA6MDAgKzAwOjAwIGRzdDpmYWxzZScKICAgIGVuZAoKICAgIHRlc3QgJ3BhcnNlcyBhIHRpbWUgc3RyaW5nIHdpdGggYSBkZWx0YSAoOikgdGltZXpvbmUnIGRvCgogICAgICBvdCA9IGluX3pvbmUoJ0V1cm9wZS9CZXJsaW4nKSB7IEV0T3JiaS5wYXJzZSgnMjAxNS0xMi0xMyAxMjozMCAtMDI6MDAnKSB9CgogICAgICB0ID0gb3QKICAgICAgdSA9IG90LnV0YwoKICAgICAgYXNzZXJ0IHQudG9faSwgMTQ1MDAxNzAwMAogICAgICBhc3NlcnQgdS50b19pLCAxNDUwMDE3MDAwCgogICAgICBhc3NlcnQgdC50b19kZWJ1Z19zLCAnb3QgMjAxNS0xMi0xMyAxMjozMDowMCAtMDI6MDAgZHN0OmZhbHNlJwogICAgICBhc3NlcnQgdS50b19kZWJ1Z19zLCAndCAyMDE1LTEyLTEzIDE0OjMwOjAwICswMDowMCBkc3Q6ZmFsc2UnCiAgICBlbmQKCiAgICB0ZXN0ICd0YWtlcyB0aGUgbG9jYWwgVFogd2hlbiB0ZXN0IGRvZXMgbm90IGtub3cgdGhlIHRpbWV6b25lJyBkbwoKICAgICAgaW5fem9uZSAnRXVyb3BlL01vc2NvdycgZG8KCiAgICAgICAgb3QgPSBFdE9yYmkucGFyc2UoJzIwMTUvMDMvMDggMDE6NTk6NTkgTmFkYS9OYWRhJykKCiAgICAgICAgYXNzZXJ0X2luY2x1ZGUgb3Quem9uZS5uYW1lLCAld1sgRXVyb3BlL01vc2NvdyBFdXJvcGUvS2lyb3YgXQogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ3BhcnNlcyBldmVuIHdoZW4gdGhlIHR6IGlzIG91dCBvZiBwbGFjZScgZG8KCiAgICAgIGFzc2VydCgKICAgICAgICBFdE9yYmkucGFyc2UoJ1N1biBOb3YgMTggMTY6MDE6MDAgQXNpYS9TaW5nYXBvcmUgMjAxMicpCiAgICAgICAgICAudG9fZGVidWdfcywKICAgICAgICAib3QgMjAxMi0xMS0xOCAxNjowMTowMCArMDg6MDAgZHN0OmZhbHNlIikKICAgIGVuZAoKICAgIHRlc3QgJ2ZhaWxzIG9uIGludmFsaWQgc3RyaW5ncycgZG8KCiAgICAgIGFzc2VydF9lcnJvcigKICAgICAgICBsYW1iZGEgeyBFdE9yYmkucGFyc2UoJ3h4eCcpIH0sCiAgICAgICAgQXJndW1lbnRFcnJvciwgJ05vIHRpbWUgaW5mb3JtYXRpb24gaW4gInh4eCInKQogICAgZW5kCgogICAgdGVzdCAncGFyc2VzIGluIHRoZSBSYWlscy1wcm92aWRlZCBUaW1lLnpvbmUgKFVUQyknIGRvCgogICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnVVRDJwoKICAgICAgdCA9IEV0T3JiaS5wYXJzZSgnMjAxOS0wMS0wMSAxMjoxMCcpCgogICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgYXNzZXJ0IHQuem9uZSwgOjpUWkluZm86OlRpbWV6b25lLmdldCgnVVRDJykKICAgICAgYXNzZXJ0IHQudG9fcywgJzIwMTktMDEtMDEgMTI6MTA6MDAgWicKICAgICAgYXNzZXJ0IHQudG9fenMsICcyMDE5LTAxLTAxIDEyOjEwOjAwIFVUQycKICAgIGVuZAoKICAgIHRlc3QgJ3BhcnNlcyBpbiB0aGUgUmFpbHMtcHJvdmlkZWQgVGltZS56b25lIChBc2lhL1NoYW5naGFpKScgZG8KCiAgICAgIFRpbWUuYWN0aXZlX3N1cHBvcnRfem9uZSA9ICdBc2lhL1NoYW5naGFpJwoKICAgICAgdCA9IEV0T3JiaS5wYXJzZSgnMjAxOS0wMS0wMSAxMjoxMCcpCgogICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgYXNzZXJ0IHQuem9uZSwgOjpUWkluZm86OlRpbWV6b25lLmdldCgnQXNpYS9TaGFuZ2hhaScpCiAgICAgIGFzc2VydCB0LnRvX3MsICcyMDE5LTAxLTAxIDEyOjEwOjAwICswODAwJwogICAgICBhc3NlcnQgdC50b196cywgJzIwMTktMDEtMDEgMTI6MTA6MDAgQXNpYS9TaGFuZ2hhaScKICAgIGVuZAoKIyAgICAjIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfdGltZV96b25lX2FiYnJldmlhdGlvbnMKIyAgICAjIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfdHpfZGF0YWJhc2VfdGltZV96b25lcwojCiMgICAgY29udGV4dCAnVFogYWJicmV2aWF0aW9ucycgZG8KIwojICAgICAgewojCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBBbWVyaWNhL0xvc19BbmdlbGVzJyA9PiAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDgwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBBbWVyaWNhL0xvc19BbmdlbGVzJyA9PiAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBQU1QnID0+ICAgICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDgwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBQRFQnID0+ICAgICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSAtMDc6MDAnID0+ICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSAtMDc6MDAnID0+ICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBHTVQtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBHTVQtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBVVEMtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBVVEMtNycgPT4gICAgICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTAxLTAxIDdBTSBFdGMvR01ULTcnID0+ICAgICAgICAgICAnMjAyMS0wMS0wMSAwNzowMDowMCAtMDcwMCcsCiMgICAgICAgICcyMDIxLTA3LTAxIDdBTSBFdGMvR01ULTcnID0+ICAgICAgICAgICAnMjAyMS0wNy0wMSAwNzowMDowMCAtMDcwMCcsCiMKIyAgICAgICAgJzIwMjEtMDEtMDEgN0FNIEFDRFQnID0+ICcyMDIxLTAxLTAxIDA3OjAwOjAwICswOTMwJywKIyAgICAgICAgJzIwMjEtMDctMDEgN0FNIEFDU1QnID0+ICcyMDIxLTAxLTAxIDA3OjAwOjAwICsxMDMwJywKIwojICAgICAgfS5lYWNoIGRvIHxrLCB2fAojCiMgICAgICAgIHRlc3QgInBhcnNlcyAje2t9IHRvICN7dn0iIGRvCiMKIyAgICAgICAgICBvdCA9IEV0T3JiaS5wYXJzZShrKQojdCA9IFRpbWUucGFyc2UoaykKI3AgWyBrIF0KI3AgWyBEYXRlLl9wYXJzZShrKSBdCiNwIFsgdC50b19zLCB0LnpvbmUsIDp0IF0KI3AgWyBvdC50b19zLCBvdC56b25lLCA6b3QgXQojcCBbIHYgXQojICAjcCBbIGssIHYsIG90IF0KIwojICAgICAgICAgIGFzc2VydCBvdC50b19zLCB2KQojICAgICAgICBlbmQKIyAgICAgIGVuZAojICAgIGVuZAoKICAgIGdyb3VwICd3aGVuIENocm9uaWMgaXMgZGVmaW5lZCcgZG8KCiAgICAgIGJlZm9yZSBkbwogICAgICAgIHJlcXVpcmVfY2hyb25pYwogICAgICBlbmQKICAgICAgYWZ0ZXIgZG8KICAgICAgICB1bnJlcXVpcmVfY2hyb25pYwogICAgICBlbmQKCiAgICAgIHRlc3QgJ2xldmVyYWdlcyBpdCcgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cgYXQgMXBtJykKICAgICAgICB0MSA9IFRpbWUubm93ICsgMjQgKiAzNjAwCgogICAgICAgIGFzc2VydCB0LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclRiAlVCcpLCB0MS5zdHJmdGltZSgnJUYgMTM6MDA6MDAnKQogICAgICBlbmQKCiAgICAgIHRlc3QgJ2xldmVyYWdlcyB0ZXN0IGluIGEgc3BlY2lmaWVkIHRpbWUgem9uZScgZG8KCiAgICAgICAgdCA9IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cgYXQgMjI6MDAgQW1lcmljYS9OZXdfWW9yaycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC50b196cywgdDEuc3RyZnRpbWUoJyVGIDIyOjAwOjAwIEFtZXJpY2EvTmV3X1lvcmsnKQogICAgICBlbmQKCiAgICAgIHRlc3QgJ3BpY2tzIHRoZSBSYWlscyBUaW1lLnpvbmUgKFVUQykgaWYgYXZhaWxhYmxlJyBkbwoKICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnVVRDJwoKICAgICAgICB0ID0gRXRPcmJpLnBhcnNlKCd0b21vcnJvdycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC56b25lLm5hbWUsICdVVEMnCiAgICAgICAgYXNzZXJ0IHQuc3RyZnRpbWUoJyVZLSVtLSVkJyksIHQxLnN0cmZ0aW1lKCclWS0lbS0lZCcpCiAgICAgICAgYXNzZXJ0IHQuc3RyZnRpbWUoJyVIOiVNOiVTJyksICcxMjowMDowMCcKICAgICAgZW5kCgogICAgICB0ZXN0ICdwaWNrcyB0aGUgUmFpbHMgVGltZS56b25lIChBc2lhL1NoYW5naGFpKSBpZiBhdmFpbGFibGUnIGRvCgogICAgICAgIFRpbWUuYWN0aXZlX3N1cHBvcnRfem9uZSA9ICdBc2lhL1NoYW5naGFpJwoKICAgICAgICB0ID0gRXRPcmJpLnBhcnNlKCd0b21vcnJvdycpCiAgICAgICAgdDEgPSBUaW1lLm5vdyArIDI0ICogMzYwMAoKICAgICAgICBhc3NlcnQgdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgICBhc3NlcnQgdC56b25lLm5hbWUsICdBc2lhL1NoYW5naGFpJwogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclWS0lbS0lZCcpLCB0MS5zdHJmdGltZSgnJVktJW0tJWQnKQogICAgICAgIGFzc2VydCB0LnN0cmZ0aW1lKCclSDolTTolUycpLCAnMTI6MDA6MDAnCiAgICAgIGVuZAoKICAgICAgZ3JvdXAgJ3doZW4gcGFyc2UoeCwgZW5hYmxlX2Nocm9uaWM6IGZhbHNlKScgZG8KCiAgICAgICAgdGVzdCAnZG9lcyBub3QgcHJlLXBhcnNlIHdpdGggQ2hyb25pYycgZG8KCiAgICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnQW1lcmljYS9DaGljYWdvJwoKICAgICAgICAgIGFzc2VydCggIyB3b3JrcwogICAgICAgICAgICBFdE9yYmkucGFyc2UoJ3RvbW9ycm93JykuY2xhc3MsCiAgICAgICAgICAgIEV0T3JiaTo6RW9UaW1lKQoKICAgICAgICAgIGFzc2VydF9lcnJvciggIyBkb2Vzbid0IHdvcmsKICAgICAgICAgICAgbGFtYmRhIHsgRXRPcmJpLnBhcnNlKCd0b21vcnJvdycsIGVuYWJsZV9jaHJvbmljOiBmYWxzZSkgfSwKICAgICAgICAgICAgQXJndW1lbnRFcnJvciwgJ05vIHRpbWUgaW5mb3JtYXRpb24gaW4gInRvbW9ycm93IicpCiAgICAgICAgZW5kCiAgICAgIGVuZAoKICAgICAgZ3JvdXAgJ2FuZCBFdC1PcmJpLmNocm9uaWNfZW5hYmxlZD8gaXMgZmFsc2UnIGRvCgogICAgICAgIGJlZm9yZSBkbwogICAgICAgICAgRXRPcmJpLmNocm9uaWNfZW5hYmxlZCA9IGZhbHNlCiAgICAgICAgZW5kCiAgICAgICAgYWZ0ZXIgZG8KICAgICAgICAgIEV0T3JiaS5jaHJvbmljX2VuYWJsZWQgPSB0cnVlCiAgICAgICAgZW5kCgogICAgICAgIHRlc3QgJ2RvZXMgbm90IHByZS1wYXJzZSB3aXRoIENocm9uaWMnIGRvCgogICAgICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgICAgICBsYW1iZGEgeyBFdE9yYmkucGFyc2UoJ3RvbW9ycm93JykgfSwKICAgICAgICAgICAgQXJndW1lbnRFcnJvciwgJ05vIHRpbWUgaW5mb3JtYXRpb24gaW4gInRvbW9ycm93IicpCiAgICAgICAgZW5kCgogICAgICAgIHRlc3QgJ3ByZS1wYXJzZXMgd2l0aCBDaHJvbmljIHdoZW4gZW5hYmxlX2Nocm9uaWM6IHRydWUnIGRvCgogICAgICAgICAgYXNzZXJ0X25vdF9lcnJvciB7IEV0T3JiaS5wYXJzZSgndG9tb3Jyb3cnLCBlbmFibGVfY2hyb25pYzogdHJ1ZSkgfQogICAgICAgIGVuZAogICAgICBlbmQKCiAgICAgIHRlc3QgJ2ZpbHRlcnMgb3B0aW9ucyBnaXZlbiB0byBDaHJvbmljJyBkbwoKICAgICAgICBUaW1lLmFjdGl2ZV9zdXBwb3J0X3pvbmUgPSAnVVRDJwoKICAgICAgICAjYXNzZXJ0IDo6S2hyb25pYykKICAgICAgICAjICAudG8gcmVjZWl2ZSg6cGFyc2UpCiAgICAgICAgIyAgLndpdGgoJ3RvbW9ycm93JywgeyBjb250ZXh0OiA6ZnV0dXJlIH0pCiAgICAgICAgIyAgLmFuZF9jYWxsX29yaWdpbmFsCgogICAgICAgIHQgPSBFdE9yYmkucGFyc2UoCiAgICAgICAgICAndG9tb3Jyb3cnLCB6b25lOiA6OlRaSW5mbzo6VGltZXpvbmUuZ2V0KCdVVEMnKSwgY29udGV4dDogOmZ1dHVyZSkKICAgICAgICB0MSA9IFRpbWUubm93ICsgMjQgKiAzNjAwCgogICAgICAgIGFzc2VydCB0LmNsYXNzLCBFdE9yYmk6OkVvVGltZQogICAgICAgIGFzc2VydCB0LnpvbmUubmFtZSwgJ1VUQycKICAgICAgICBhc3NlcnQgdC5zdHJmdGltZSgnJVktJW0tJWQnKSwgdDEuc3RyZnRpbWUoJyVZLSVtLSVkJykKICAgICAgICBhc3NlcnQgdC5zdHJmdGltZSgnJUg6JU06JVMnKSwgJzEyOjAwOjAwJwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnLm9zeF90eicgZG8KCiAgICB0ZXN0ICdleHRyYWN0cyB0aGUgdGltZXpvbmUgcmVsYXRpdmUgdG8gdGhlIHpvbmVpbmZvIGRpcmVjdG9yeScgZG8KCiAgICAgIHN5bWxpbmsgPSBGaWxlLm1ldGhvZCg6c3ltbGluaz8pCiAgICAgIHJlYWRsaW5rID0gRmlsZS5tZXRob2QoOnJlYWRsaW5rKQogICAgICBsaW5rcyA9IFsKICAgICAgICAnL3Zhci9kYi90aW1lem9uZS96b25laW5mby9FdXJvcGUvSXN0YW5idWwnLAogICAgICAgICcvdXNyL3NoYXJlL3pvbmVpbmZvL0FzaWEvVG9reW8nLAogICAgICAgICcvdmFyL2RiL3RpbWV6b25lL2xvY2FsdGltZScKICAgICAgXQoKICAgICAgRmlsZS5kZWZpbmVfc2luZ2xldG9uX21ldGhvZCg6c3ltbGluaz8pIHsgfHBhdGh8IHBhdGggPT0gJy9ldGMvbG9jYWx0aW1lJyB9CiAgICAgIEZpbGUuZGVmaW5lX3NpbmdsZXRvbl9tZXRob2QoOnJlYWRsaW5rKSB7IHxffCBsaW5rcy5zaGlmdCB9CgogICAgICBhc3NlcnQgRXRPcmJpLm9zeF90eiwgJ0V1cm9wZS9Jc3RhbmJ1bCcKICAgICAgYXNzZXJ0IEV0T3JiaS5vc3hfdHosICdBc2lhL1Rva3lvJwogICAgICBhc3NlcnRfbmlsIEV0T3JiaS5vc3hfdHoKCiAgICBlbnN1cmUKCiAgICAgIEZpbGUuZGVmaW5lX3NpbmdsZXRvbl9tZXRob2QoOnN5bWxpbms/LCBzeW1saW5rKQogICAgICBGaWxlLmRlZmluZV9zaW5nbGV0b25fbWV0aG9kKDpyZWFkbGluaywgcmVhZGxpbmspCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy5nZXRfdHpvbmUnIGRvCgogICAgewoKICAgICAgJ0dCJyA9PiAnR0InLAogICAgICAnVVRDJyA9PiAnVVRDJywKICAgICAgJ0dNVCcgPT4gJ0dNVCcsCiAgICAgICdadWx1JyA9PiAnWnVsdScsCiAgICAgICdKYXBhbicgPT4gJ0phcGFuJywKICAgICAgJ1R1cmtleScgPT4gJ1R1cmtleScsCiAgICAgICdBc2lhL1Rva3lvJyA9PiAnQXNpYS9Ub2t5bycsCiAgICAgICdFdXJvcGUvUGFyaXMnID0+ICdFdXJvcGUvUGFyaXMnLAogICAgICAnRXVyb3BlL1p1cmljaCcgPT4gJ0V1cm9wZS9adXJpY2gnLAogICAgICAnVy1TVScgPT4gJ1ctU1UnLAoKICAgICAgJ1onID0+ICdVVEMnLAoKICAgICAgJyswOTowMCcgPT4gJyswOTowMCcsCiAgICAgICctMDE6MzAnID0+ICctMDE6MzAnLAoKICAgICAgJ19BVC0zOjMwQXNpYS9UZWhyYW4nID0+ICdBc2lhL1RlaHJhbicsCiAgICAgICdfQVQtNEFzaWEvVGJpbGlzaScgPT4gJ0FzaWEvVGJpbGlzaScsCgogICAgICAnKzA4OjAwJyA9PiAnKzA4OjAwJywKICAgICAgJyswODAwJyA9PiAnKzA4MDAnLCAjIG5vIG5vcm1hbGl6YXRpb24gdG8gIiswODowMCIKCiAgICAgICctMDEnID0+ICctMDEnLAoKICAgICAgMzYwMCA9PiAnKzAxOjAwJywKCiAgICAgICdUb2t5byBTdGFuZGFyZCBUaW1lJyA9PiAnQXNpYS9Ub2t5bycsCiAgICAgICdDb29yZGluYXRlZCBVbml2ZXJzYWwgVGltZScgPT4gJ1VUQycsCgogICAgICAnRWFzdGVybiBTdGFuZGFyZCBUaW1lJyA9PiAnQW1lcmljYS9OZXdfWW9yaycsCiAgICAgICdFYXN0ZXJuIERheWxpZ2h0IFRpbWUnID0+ICdBbWVyaWNhL05ld19Zb3JrJywKCiAgICAgICdDU1QrMDgwMCcgPT4gJ0FzaWEvQ2hvbmdxaW5nJywKICAgICAgJ0NTVCswODowMCcgPT4gJ0FzaWEvQ2hvbmdxaW5nJywKICAgICAgJ0NTVCswOCcgPT4gJ0FzaWEvQ2hvbmdxaW5nJywKICAgICAgJ0NTVCs4JyA9PiAnQXNpYS9DaG9uZ3FpbmcnLAogICAgICAnRURULTA0MDAnID0+ICdBbWVyaWNhL0RldHJvaXQnLAoKICAgICAgJ1dFVC0xV0VTVCcgPT4gJ1dFVCcsCiAgICAgICdDRVQtMkNFU1QnID0+ICdDRVQnLAoKICAgIH0uZWFjaCBkbyB8YSwgYnwKCiAgICAgIHRlc3QgInJldHVybnMgI3tiLmluc3BlY3R9IGZvciAje2EuaW5zcGVjdH0iIGRvCgogICAgICAgIHogPSBFdE9yYmkuZ2V0X3R6b25lKGEpCgogICAgICAgIGFzc2VydF9ub3RfbmlsIHoKICAgICAgICBhc3NlcnQgei5uYW1lLCBiCiAgICAgIGVuZAogICAgZW5kCgojICAgIHRlc3QgJ3JldHVybnMgYSB0aW1lem9uZSBmb3Igd2VsbC1rbm93biBhYmJyZXZpYXRpb25zJyBkbwojCiMgICAgICBhc3NlcnQgZ3R6KCdKU1QnKSwgJ0phcGFuJwojICAgICAgYXNzZXJ0IGd0eignUFNUJyksICdBbWVyaWNhL0Rhd3NvbicKIyAgICAgIGFzc2VydCBndHooJ0NFU1QnKSwgJ0FmcmljYS9DZXV0YScKIyAgICBlbmQKCiAgICBbCiAgICAgICdBc2lhL1BhcmlzJywgJ05hZGEvTmFkYScsICc3JywgJzA2JywgJ3N1biMzJywgJ01hemRhIFpvb20gWm9vbSBTdGFkaXVtJwogICAgXS5lYWNoIGRvIHxzfAoKICAgICAgdGVzdCAicmV0dXJucyBuaWwgZm9yICN7cy5pbnNwZWN0fSIgZG8KCiAgICAgICAgYXNzZXJ0X25pbCBFdE9yYmkuZ2V0X3R6b25lKHMpCiAgICAgIGVuZAogICAgZW5kCgogICAgIyBydWZ1cy1zY2hlZHVsZXIgZ2gtMjIyCiAgICAjCiAgICB0ZXN0ICJmYWxscyBiYWNrIHRvIEVOVlsnVFonXSBpZiB0ZXN0IGRvZXNuJ3Qga25vdyBUaW1lLm5vdy56b25lIiBkbwoKICAgICAgYmVnaW4KCiAgICAgICAgY3VycmVudCA9IEV0T3JiaS5nZXRfdHpvbmUoOmxvY2FsKQoKICAgICAgICBUaW1lLl96b25lID0gJ+S4reWbveagh+WHhuaXtumXtCcKCiAgICAgICAgI2Fzc2VydF9uaWwgRXRPcmJpLmdldF90em9uZSg6Y3VycmVudCkKICAgICAgICAjYXNzZXJ0IEV0T3JiaS5nZXRfdHpvbmUoOmN1cnJlbnQpLCBFdE9yYmkuZ2V0X3R6b25lKFRpbWUubm93LnpvbmUpCiAgICAgICAgICAjCiAgICAgICAgICAjIGdoLTI0MCBpbnRyb2R1Y2VzIGEgd2F5IG9mIGZpbmRpbmcgdGhlIHRpbWV6b25lIGJ5IGFza2luZyBkaXJlY3RseQogICAgICAgICAgIyB0byB0aGUgc3lzdGVtLCBzbyB0aG9zZSBkbyByZXR1cm4gYSB0aW1lem9uZS4uLgoKICAgICAgICBpbl96b25lICdBc2lhL1NoYW5naGFpJyBkbwoKICAgICAgICAgIGFzc2VydF9pbmNsdWRlKAogICAgICAgICAgICBFdE9yYmkuZ2V0X3R6b25lKDpsb2NhbCksCiAgICAgICAgICAgIFsgRXRPcmJpLmdldF90em9uZSgnQXNpYS9TaGFuZ2hhaScpLAogICAgICAgICAgICAgIEV0T3JiaS5nZXRfdHpvbmUoJ0FzaWEvQ2hvbmdxaW5nJykgXSkKICAgICAgICBlbmQKCiAgICAgIGVuc3VyZQoKICAgICAgICBUaW1lLl96b25lID0gbmlsCiAgICAgIGVuZAoKICAgICAgYXNzZXJ0IEV0T3JiaS5nZXRfdHpvbmUoOmxvY2FsKSwgY3VycmVudAogICAgZW5kCgogICAgeyAjIGZvciBydWZ1cy1zY2hlZHVsZXIgZ2gtMjI4CgogICAgICAnQXNpYS9Ub2t5bycgPT4gJXdbCiAgICAgICAgQXNpYS9Ub2t5byBdLAogICAgICAnQXNpYS9TaGFuZ2hhaScgPT4gJXdbCiAgICAgICAgQXNpYS9TaGFuZ2hhaSBBc2lhL0Nob25ncWluZyBdLAogICAgICAnRXVyb3BlL1p1cmljaCcgPT4gJXdbCiAgICAgICAgRXVyb3BlL1p1cmljaCBBZnJpY2EvQ2V1dGEgd2luZG93czpDRVQgXSwKICAgICAgJ0V1cm9wZS9Mb25kb24nID0+ICV3WwogICAgICAgIEV1cm9wZS9Mb25kb24gRXVyb3BlL0JlbGZhc3Qgd2luZG93czpHTVQgd2luZG93czpHTVQtMCBdLAoKICAgIH0uZWFjaCBkbyB8em9uZSwgdGFyZ2V0c3wKCiAgICAgIHRlc3QgInJldHVybnMgdGhlIGN1cnJlbnQgdGltZXpvbmUgZm9yIDpjdXJyZW50IGluICN7em9uZX0iIGRvCgogICAgICAgIGluX3pvbmUoem9uZSkgZG8KCiAgICAgICAgICBhc3NlcnRfaW5jbHVkZSBFdE9yYmkuZ2V0X3R6b25lKDpsb2NhbCksIHNlbGVjdF96b25lcyh0YXJnZXRzKQogICAgICAgIGVuZAogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgImRvZXNuJ3QgbWluZCBiZWluZyBnaXZlbiBhIFRaSW5mbzo6VGltZXpvbmUiIGRvCgogICAgICB0eiA9IDo6VFpJbmZvOjpUaW1lem9uZS5nZXQoJ1p1bHUnKQogICAgICBjbGFzcyA8PCB0egogICAgICAgIGRlZiA8PT4odHopCiAgICAgICAgICAjcmV0dXJuIG5pbCB1bmxlc3MgdHouaXNfYT8oVGltZXpvbmUpCiAgICAgICAgICBpZGVudGlmaWVyIDw9PiB0ei5pZGVudGlmaWVyCiAgICAgICAgZW5kCiAgICAgIGVuZAogICAgICAgICMgc2ltdWxhdGUgdHppbmZvIDAuMy41MyBpc3N1ZQoKICAgICAgYXNzZXJ0IEV0T3JiaS5nZXRfdHpvbmUodHopLCA6OlRaSW5mbzo6VGltZXpvbmUuZ2V0KCdadWx1JykKICAgIGVuZAogIGVuZAoKICBncm91cCAnLmRldGVybWluZV9sb2NhbF90em9uZScgZG8KCiAgICB0ZXN0ICdmYXZvdXJzIHRoZSBsb2NhbCB0aW1lem9uZScgZG8KCiAgICAgIGluX3pvbmUoOm5vX2Vudl90eikgZG8KCiAgICAgICAgVGltZS5fem9uZSA9ICdDYXBlIFZlcmRlIFN0YW5kYXJkIFRpbWUnCiAgICAgICAgRXRPcmJpLl9vc196b25lID0gJycgIyBmb3JjZSAjb3NfdHogdG8gcmV0dXJuIG5pbAoKICAgICAgICBhc3NlcnQgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZS5uYW1lLCAnQXRsYW50aWMvQ2FwZV9WZXJkZScKICAgICAgZW5kCiAgICBlbmQKCiAgICB0ZXN0ICdmYXZvdXJzIHRoZSBsb2NhbCB0aW1lem9uZScgZG8KCiAgICAgIGluX3pvbmUoJ0V1cm9wZS9CZXJsaW4nKSBkbwoKICAgICAgICBhc3NlcnRfaW5jbHVkZSgKICAgICAgICAgIEV0T3JiaS5kZXRlcm1pbmVfbG9jYWxfdHpvbmUsCiAgICAgICAgICBzZWxlY3Rfem9uZXMoJXdbIEV1cm9wZS9CZXJsaW4gQWZyaWNhL0NldXRhIHdpbmRvd3M6Q0VUIF0pKQogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ2Zhdm91cnMgdGhlIGxvY2FsIHRpbWV6b25lIChNaXR0ZWxldXJvcGFlaXNjaGUgU29tbWVyemVpdCknIGRvCgogICAgICBpbl96b25lKDpub19lbnZfdHopIGRvCgogICAgICAgIFRpbWUuX3pvbmUgPSAiTWl0dGVsZXVyb3BceEU0aXNjaGUgU29tbWVyemVpdCIKICAgICAgICBFdE9yYmkuX29zX3pvbmUgPSAnJyAjIGZvcmNlICNvc190eiB0byByZXR1cm4gbmlsCgogICAgICAgIGFzc2VydF9ub3RfZXJyb3IgeyBFdE9yYmkuZGV0ZXJtaW5lX2xvY2FsX3R6b25lIH0KICAgICAgZW5kCiAgICBlbmQKCiAgICB0ZXN0ICdyZXR1cm5zIHRoZSBsb2NhbCB0aW1lem9uZScgZG8KCiAgICAgIGluX3pvbmUoJ0FtZXJpY2EvSmFtYWljYScpIGRvCgogICAgICAgIGFzc2VydF9pbmNsdWRlKAogICAgICAgICAgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZS5uYW1lLAogICAgICAgICAgWyAnQW1lcmljYS9KYW1haWNhJywgJ0FtZXJpY2EvQXRpa29rYW4nLCAnRVNUJyBdKQogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgJ3JldHVybnMgdGhlIFJhaWxzLXByb3ZpZGVkIFRpbWUuem9uZS50emluZm8gaWYgYXZhaWxhYmxlJyBkbwoKICAgICAgIyBodHRwOi8vYXBpLnJ1YnlvbnJhaWxzLm9yZy9jbGFzc2VzL0FjdGl2ZVN1cHBvcnQvVGltZVpvbmUuaHRtbAoKICAgICAgVGltZS5hY3RpdmVfc3VwcG9ydF96b25lID0gJ0V1cm9wZS9WaWxuaXVzJwoKICAgICAgaW5fem9uZSg6bm9fZW52X3R6KSBkbwogICAgICAgIGFzc2VydCBFdE9yYmkuZGV0ZXJtaW5lX2xvY2FsX3R6b25lLmNsYXNzLCA6OlRaSW5mbzo6RGF0YVRpbWV6b25lCiAgICAgICAgYXNzZXJ0IEV0T3JiaS5kZXRlcm1pbmVfbG9jYWxfdHpvbmUubmFtZSwgJ0V1cm9wZS9WaWxuaXVzJwogICAgICBlbmQKICAgIGVuZAoKICAgIHRlc3QgImdpdmVzIHByZWNlZGVuY2UgdG8gRU5WWydUWiddIG92ZXIgUmFpbHMgVGltZS56b25lLnR6aW5mbyIgZG8KCiAgICAgICMgaHR0cDovL2FwaS5ydWJ5b25yYWlscy5vcmcvY2xhc3Nlcy9BY3RpdmVTdXBwb3J0L1RpbWVab25lLmh0bWwKCiAgICAgIFRpbWUuYWN0aXZlX3N1cHBvcnRfem9uZSA9ICdFdXJvcGUvVmlsbml1cycKCiAgICAgIGluX3pvbmUoJ0FzaWEvVGVocmFuJykgZG8KCiAgICAgICAgYXNzZXJ0IEV0T3JiaS5kZXRlcm1pbmVfbG9jYWxfdHpvbmUuY2xhc3MsIDo6VFpJbmZvOjpEYXRhVGltZXpvbmUKICAgICAgICBhc3NlcnQgRXRPcmJpLmRldGVybWluZV9sb2NhbF90em9uZS5uYW1lLCAnQXNpYS9UZWhyYW4nCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcuem9uZScgZG8KCiAgICB0ZXN0ICdpcyBhbiBhbGlhcyB0byAuZGV0ZXJtaW5lX2xvY2FsX3R6b25lJyBkbwoKICAgICAgaW5fem9uZSg6bm9fZW52X3R6KSBkbwoKICAgICAgICBUaW1lLl96b25lID0gJ0NhcGUgVmVyZGUgU3RhbmRhcmQgVGltZScKICAgICAgICBFdE9yYmkuX29zX3pvbmUgPSAnJyAjIGZvcmNlICNvc190eiB0byByZXR1cm4gbmlsCgogICAgICAgIGFzc2VydCBFdE9yYmkuem9uZS5uYW1lLCAnQXRsYW50aWMvQ2FwZV9WZXJkZScKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy5ub3cnIGRvCgogICAgdGVzdCAncmV0dXJucyBhIGN1cnJlbnQsIGxvY2FsIEVvVGltZSBpbnN0YW5jZScgZG8KCiAgICAgIGluX3pvbmUgJ0FzaWEvU2hhbmdoYWknIGRvCgogICAgICAgIHQgPSBFdE9yYmkubm93CiAgICAgICAgbiA9IFRpbWUubm93CgogICAgICAgIGFzc2VydCB0LnNlY29uZHMgPiAobiAtIDEpLnRvX2YKICAgICAgICBhc3NlcnQgdC5zZWNvbmRzIDwgKG4gKyAxKS50b19mCgogICAgICAgIGFzc2VydF9pbmNsdWRlIHQuem9uZS5uYW1lLCBbICdBc2lhL1NoYW5naGFpJywgJ0FzaWEvQ2hvbmdxaW5nJyBdCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcubWFrZV90aW1lJyBkbwoKICAgIHRlc3QgJ3JldHVybnMgYW4gRW9UaW1lIGluc3RhbmNlIGFzIGlzJyBkbwoKICAgICAgdDAgPSBFdE9yYmkucGFyc2UoJzIwMTctMDMtMjEgMTI6MDA6MzQgQXNpYS9VbGFuX0JhdG9yJykKICAgICAgdDEgPSBFdE9yYmkubWFrZV90aW1lKHQwKQoKICAgICAgYXNzZXJ0IHQxLmNsYXNzLCA6OkV0T3JiaTo6RW9UaW1lCiAgICAgIGFzc2VydCB0MSwgdDAKICAgICAgYXNzZXJ0IHQxLm9iamVjdF9pZCwgdDAub2JqZWN0X2lkCiAgICBlbmQKCiAgICB0ZXN0ICdyZXR1cm5zIGFuIEVvVGltZSBpbnN0YW5jZSBhcyBpcycgZG8KCiAgICAgIHQwID0gRXRPcmJpLnBhcnNlKCcyMDE3LTAzLTIxIDEyOjAwOjM0IEFzaWEvVWxhbl9CYXRvcicpCiAgICAgIHQxID0gRXRPcmJpLm1ha2VfdGltZSh0MCwgdDAuem9uZSkKCiAgICAgIGFzc2VydCB0MS5jbGFzcywgOjpFdE9yYmk6OkVvVGltZQogICAgICBhc3NlcnQgdDEsIHQwCiAgICAgIGFzc2VydCB0MS5vYmplY3RfaWQsIHQwLm9iamVjdF9pZAogICAgZW5kCgogICAgWwogICAgICBbICdhbiBFb1RpbWUgaW5zdGFuY2UnLAogICAgICAgIG5pbCwKICAgICAgICBsYW1iZGEgeyBFdE9yYmkucGFyc2UoJzIwMTctMDMtMjEgMTI6MDA6MzQgQXNpYS9VbGFuX0JhdG9yJykgfSwKICAgICAgICAnb3QgMjAxNy0wMy0yMSAxMjowMDozNCArMDg6MDAgZHN0OmZhbHNlJyBdLAoKICAgICAgWyAnYSBsb2NhbCB0aW1lJywKICAgICAgICAnQXNpYS9UYmlsaXNpJywKICAgICAgICBUaW1lLnBhcnNlKCcyMDE2LTExLTAxIDEyOjMwOjA5ICswNDowMCcpLCAjIHVzZSBjdXN0b20gdHogOi0oCiAgICAgICAgJ290IDIwMTYtMTEtMDEgMTI6MzA6MDkgKzA0OjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2FuIFVUQyB0aW1lJywKICAgICAgICBuaWwsCiAgICAgICAgVGltZS51dGMoMjAxNiwgMTEsIDAxLCAxMiwgMzAsIDkpLAogICAgICAgICdvdCAyMDE2LTExLTAxIDEyOjMwOjA5ICswMDowMCBkc3Q6ZmFsc2UnIF0sCgogICAgICBbICdhIERhdGUgaW5zdGFuY2UnLAogICAgICAgIG5pbCwKICAgICAgICBEYXRlLm5ldygyMDE2LCAxMSwgMDEpLAogICAgICAgIGxhbWJkYSB7CiAgICAgICAgICBFdE9yYmk6OkVvVGltZS5uZXcoCiAgICAgICAgICAgIFRpbWUubG9jYWwoMjAxNiwgMTEsIDAxKS50b19mLCBuaWwKICAgICAgICAgICkudG9fZGVidWdfcyB9IF0sCgogICAgICBbICdhIFN0cmluZycsCiAgICAgICAgbmlsLAogICAgICAgICcyMDE2LTExLTAxIDEyOjMwOjA5JywKICAgICAgICBsYW1iZGEgewogICAgICAgICAgRXRPcmJpOjpFb1RpbWUubmV3KAogICAgICAgICAgICBUaW1lLmxvY2FsKDIwMTYsIDExLCAwMSwgMTIsIDMwLCA5KS50b19mLCBuaWwpIH0gXSwKCiAgICAgIFsgJ2EgU3RyaW5nJywKICAgICAgICAnQW1lcmljYS9DaGljYWdvJywKICAgICAgICAnMjAxNi0wOC0wMSAxMjozMDoxMCcsCiAgICAgICAgJ290IDIwMTYtMDgtMDEgMTI6MzA6MTAgLTA1OjAwIGRzdDp0cnVlJyBdLAogICAgICAjWyAnYSBTdHJpbmcnLAogICAgICAjICAnQW1lcmljYS9DaGljYWdvJywKICAgICAgIyAgJzIwMTYtMTEtMDEgMTI6MzA6MDknLAogICAgICAjICAnb3QgMjAxNi0xMS0wMSAxMjozMDowOSAtMDU6MDAgZHN0OnRydWUnIF0sICMgZmFpbHMgb24gQXBwdmV5b3IgOi0oCiAgICAgIFsgJ2EgU3RyaW5nJywKICAgICAgICAnQW1lcmljYS9DaGljYWdvJywKICAgICAgICAnMjAxNi0xMS0wNiAxMjozMDowOCcsCiAgICAgICAgJ290IDIwMTYtMTEtMDYgMTI6MzA6MDggLTA2OjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2EgU3RyaW5nJywKICAgICAgICAnRXVyb3BlL1BhcmlzJywKICAgICAgICAnMjAxNi0xLTEgMTI6MzA6MDcnLAogICAgICAgICdvdCAyMDE2LTAxLTAxIDEyOjMwOjA3ICswMTowMCBkc3Q6ZmFsc2UnIF0sCiAgICAgIFsgJ2EgU3RyaW5nJywKICAgICAgICAnRXVyb3BlL1BhcmlzJywKICAgICAgICAnMjAxNi04LTEgMTI6MzA6MDcnLAogICAgICAgICdvdCAyMDE2LTA4LTAxIDEyOjMwOjA3ICswMjowMCBkc3Q6dHJ1ZScgXSwKCiAgICAgIFsgJ2EgWnVsdSBTdHJpbmcnLAogICAgICAgIG5pbCwKICAgICAgICAnMjAxNi0xMS0wMSAxMjozMDowOVonLAogICAgICAgIEV0T3JiaTo6RW9UaW1lLm5ldyhUaW1lLnV0YygyMDE2LCAxMSwgMDEsIDEyLCAzMCwgOSkudG9fZiwgJ1p1bHUnKSBdLAoKICAgICAgWyAnYSBzcyswMTowMCBTdHJpbmcnLAogICAgICAgIG5pbCwKICAgICAgICAnMjAxNi0xMS0wMSAxMjozMDowOSswMTowMCcsCiAgICAgICAgJ290IDIwMTYtMTEtMDEgMTI6MzA6MDkgKzAxOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2Egc3MtMDEgU3RyaW5nJywKICAgICAgICBuaWwsCiAgICAgICAgJzIwMTYtMTEtMDEgMTI6MzA6MDktMDEnLAogICAgICAgICdvdCAyMDE2LTExLTAxIDEyOjMwOjA5IC0wMTowMCBkc3Q6ZmFsc2UnIF0sCgogICAgICBbICdhIFN0cmluZyB3aXRoIGFuIGV4cGxpY2l0IHRpbWUgem9uZScsCiAgICAgICAgbmlsLAogICAgICAgICcyMDE2LTA1LTAxIDEyOjMwOjA5IEFtZXJpY2EvTmV3X1lvcmsnLAogICAgICAgICdvdCAyMDE2LTA1LTAxIDEyOjMwOjA5IC0wNDowMCBkc3Q6dHJ1ZScgXSwKCiAgICAgIFsgJ2EgTnVtZXJpYycsCiAgICAgICAgbmlsLAogICAgICAgIDM2MDAsCiAgICAgICAgbGFtYmRhIHsgWyBUaW1lLm5vdyArIDM2MDAgLSAwLjM1LCBUaW1lLm5vdyArIDM2MDAgKyAwLjM1IF0gfSBdLAoKICAgICAgWyAnYW4gYXJyYXkgWyB5LCBtLCBkLCAuLi4gXScsCiAgICAgICAgJ0V1cm9wZS9Nb3Njb3cnLAogICAgICAgIFsgWyAyMDE3LCAyLCAyOCBdIF0sCiAgICAgICAgJ290IDIwMTctMDItMjggMDA6MDA6MDAgKzAzOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2FuIGFycmF5IG9mIGFyZ3MgKHksIG0sIGQsIC4uLiknLAogICAgICAgICdFdXJvcGUvTW9zY293JywKICAgICAgICBbIDIwMTcsIDEsIDMxLCAxMCBdLAogICAgICAgICdvdCAyMDE3LTAxLTMxIDEwOjAwOjAwICswMzowMCBkc3Q6ZmFsc2UnIF0sCgogICAgICBbICdhbiBhcnJheSBvZiBhcmdzIGFuZCBhIHpvbmUgYXMgbGFzdCBhcmcnLAogICAgICAgIG5pbCwKICAgICAgICBbIDIwMTcsIDEsIDMxLCAxMiwgJ0V1cm9wZS9Nb3Njb3cnIF0sCiAgICAgICAgJ290IDIwMTctMDEtMzEgMTI6MDA6MDAgKzAzOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2Egc3RyaW5nIGFuZCBhIHpvbmUgYXMgbGFzdCBhcmcnLAogICAgICAgIG5pbCwKICAgICAgICBbICcyMDE2LTA1LTAxIDEyOjMwOjA5JywgJ0FtZXJpY2EvQ2hpY2FnbycgXSwKICAgICAgICAnb3QgMjAxNi0wNS0wMSAxMjozMDowOSAtMDU6MDAgZHN0OnRydWUnIF0sCgogICAgICBbICdhIHN0cmluZyBhbmQgYW4gb3ZlcnJpZGluZyB6b25lIGFzIGxhc3QgYXJnJywKICAgICAgICBuaWwsCiAgICAgICAgWyAnMjAxNi0wNS0wMSAxMTozMDowOSBBbWVyaWNhL05ld19Zb3JrJywgJ0FtZXJpY2EvQ2hpY2FnbycgXSwKICAgICAgICAnb3QgMjAxNi0wNS0wMSAxMTozMDowOSAtMDU6MDAgZHN0OnRydWUnIF0sCgogICAgICBbICdhbiBhcnJheSBvZiBhcmdzIGFuZCBhIFRaSW5mbyB6b25lIGFzIGxhc3QgYXJnJywKICAgICAgICBuaWwsCiAgICAgICAgWyAyMDE3LCAxLCAzMSwgRXRPcmJpLmdldF90em9uZSgnRXVyb3BlL09zbG8nKSBdLAogICAgICAgICdvdCAyMDE3LTAxLTMxIDAwOjAwOjAwICswMTowMCBkc3Q6ZmFsc2UnIF0sCgogICAgICBbICdhIHN0cmluZyBhbmQgYSBUWkluZm8gem9uZSBhcyBsYXN0IGFyZycsCiAgICAgICAgbmlsLAogICAgICAgIFsgJzIwMTctMDEtMzEgMTI6MzAnLCBFdE9yYmkuZ2V0X3R6b25lKCdFdXJvcGUvT3NsbycpIF0sCiAgICAgICAgJ290IDIwMTctMDEtMzEgMTI6MzA6MDAgKzAxOjAwIGRzdDpmYWxzZScgXSwKCiAgICAgIFsgJ2Egc3RyaW5nJywKICAgICAgICAnQW1lcmljYS9DaGljYWdvJywKICAgICAgICBsYW1iZGEgeyBbIFRpbWUucGFyc2UoJzIwMjEtMDMtMTEnKSBdIH0sCiAgICAgICAgd2luZG93cz8gPyAnQ1NUNkNEVCcgOiAnQW1lcmljYSAtIENoaWNhZ28nLAogICAgICAgIGxhbWJkYSB7IHx0fCB0LnpvbmUudG9fcyB9IF0sCgogICAgICBbICdhIHN0cmluZycsCiAgICAgICAgJ0FtZXJpY2EvTmV3X1lvcmsnLAogICAgICAgIGxhbWJkYSB7IFsgVGltZS5wYXJzZSgnMjAyMS0wMy0xMScpIF0gfSwKICAgICAgICB3aW5kb3dzPyA/ICV3WyBFU1Q1RURUIEVTVCBdIDogJ0FtZXJpY2EgLSBOZXcgWW9yaycsCiAgICAgICAgI3dpbmRvd3M/ID8gJ0VTVCcgOiAnQW1lcmljYSAtIE5ldyBZb3JrJywKICAgICAgICBsYW1iZGEgeyB8dHwgdC56b25lLnRvX3MgfSBdLAoKICAgIF0uZWFjaCBkbyB8bmFtZSwgem9uZSwgYXJncywgYXNzZXJ0ZWQsIHRyYW5zZm9ybWVyfAoKICAgICAgdGl0bGUgPSAidHVybnMgI3tuYW1lfSBpbnRvIGFuIEVvVGltZSBpbnN0YW5jZSIKICAgICAgdGl0bGUgKz0gIiBpbiAje3pvbmV9IiBpZiB6b25lCgogICAgICB0ZXN0KHRpdGxlKSBkbwoKICAgICAgICBlb3QsIGV4cCA9CiAgICAgICAgICBpbl96b25lKHpvbmUpIGRvCgogICAgICAgICAgICBhcyA9IGFyZ3MuaXNfYT8oUHJvYykgPyBhcmdzLmNhbGwgOiBhcmdzCgogICAgICAgICAgICB0ID0gYXMuaXNfYT8oQXJyYXkpID8KICAgICAgICAgICAgICBFdE9yYmkubWFrZV90aW1lKCphcykgOgogICAgICAgICAgICAgIEV0T3JiaS5tYWtlX3RpbWUoYXMpCiAgICAgICAgICAgIHggPSBhc3NlcnRlZC5pc19hPyhQcm9jKSA/CiAgICAgICAgICAgICAgYXNzZXJ0ZWQuY2FsbCA6CiAgICAgICAgICAgICAgYXNzZXJ0ZWQKCiAgICAgICAgICAgIHQgPSB0cmFuc2Zvcm1lci5pc19hPyhQcm9jKSA/IHRyYW5zZm9ybWVyW3RdIDogdAoKICAgICAgICAgICAgWyB0LCB4IF0KICAgICAgICAgIGVuZAojcCBlb3QudG9fZGVidWdfcwojcCBlb3QudXRjX29mZnNldAojcCBlb3Quc2VuZCg6dG9fdGltZSkudG9fcwojcCBlb3Quc2VuZCg6dG9fdGltZSkudXRjX29mZnNldAoKICAgICAgICBjYXNlIGV4cAogICAgICAgIHdoZW4gU3RyaW5nCiAgICAgICAgICBhc3NlcnQgZW90LnJlc3BvbmRfdG8/KDp0b19kZWJ1Z19zKSA/IGVvdC50b19kZWJ1Z19zIDogZW90LCBleHAKICAgICAgICB3aGVuIEFycmF5CiAgICAgICAgICBpZiBleHAuY29sbGVjdCgmOmNsYXNzKS51bmlxID09IFsgU3RyaW5nIF0KICAgICAgICAgICAgYXNzZXJ0IGV4cC5pbmNsdWRlPyhlb3QpCiAgICAgICAgICBlbHNlCiAgICAgICAgICAgIGFzc2VydCBlb3QgPiBleHBbMF0KICAgICAgICAgICAgYXNzZXJ0IGVvdCA8IGV4cFsxXQogICAgICAgICAgZW5kCiAgICAgICAgZWxzZQogICAgICAgICAgYXNzZXJ0IGVvdCwgZXhwCiAgICAgICAgZW5kCiAgICAgIGVuZAogICAgZW5kCgojICAgIHRlc3QgJ2FjY2VwdHMgYSBkdXJhdGlvbiBTdHJpbmcnIyBkbwojIwojIyAgICAgIGV4cGVjdCgKIyMgICAgICAgIEV0T3JiaS5tYWtlX3RpbWUoJzFoJykKIyMgICAgICApLnRvIGJlX2JldHdlZW4oCiMjICAgICAgICBUaW1lLm5vdyArIDM2MDAgLSAxLCBUaW1lLm5vdyArIDM2MDAgKyAxCiMjICAgICAgKQojIyAgICBlbmQKIyAgIwojICAjIFN0cmluZyBwYXJzaW5nIGlzIGZ1Z2l0J3Mgam9iLiBFdC1vcmJpIHNob3VsZCBiZSBhIGRlcGVuZGVuY3kgb2YKIyAgIyBmdWdpdCwgbm90IHRoZSBvdGhlciB3YXkgYXJvdW5kLiBXaGVuIGZ1Z2l0IGlzIHByZXNlbnQsIHRoaXMKIyAgIyBzcGVjIHNob3VsZCBzdWNjZWVkLCBlbHNlIHRlc3Qgc2hvdWxkIG5vdC4KCiAgICB0ZXN0ICdhY2NlcHRzIGEgUmFpbHMgVGltZVdpdGhab25lJyBkbwoKICAgICAgIyBodHRwOi8vYXBpLnJ1YnlvbnJhaWxzLm9yZy9jbGFzc2VzL0FjdGl2ZVN1cHBvcnQvVGltZVdpdGhab25lLmh0bWwKCiAgICAgIG4gPSBUaW1lLm5vdwogICAgICB6ID0gRXRPcmJpLmdldF90em9uZSgnUGFjaWZpYy9FYXN0ZXInKQoKICAgICAgI3QgPSBBY3RpdmVTdXBwb3J0OjpUaW1lV2l0aFpvbmUubmV3KG4sIHopCiAgICAgICAgIwogICAgICB0ID0gbgogICAgICB0Lmluc3RhbmNlX2V2YWwgeyBAeiA9IHogfQogICAgICBjbGFzcyA8PCB0OyBkZWYgdGltZV96b25lOyBAejsgZW5kOyBlbmQKICAgICAgICAjCiAgICAgICAgIyBmYWtlIGFuIEFjdGl2ZVN1cHBvcnQ6OlRpbWVXaXRoWm9uZSBpbnN0YW5jZSB3aXRoIGEgI3RpbWVfem9uZQoKICAgICAgZW90ID0gRXRPcmJpLm1ha2VfdGltZSh0KQoKICAgICAgYXNzZXJ0IGVvdC5jbGFzcywgRXRPcmJpOjpFb1RpbWUKICAgICAgYXNzZXJ0IGVvdC5zZWNvbmRzLCB0LnRvX2YKICAgICAgYXNzZXJ0IGVvdC56b25lLCB0LnRpbWVfem9uZQogICAgZW5kCgogICAgdGVzdCAncmVqZWN0cyBhIFRpbWUgaW4gYSBub24tbG9jYWwgYW1iaWd1b3VzIHRpbWV6b25lJyBkbwoKICAgICAgdCA9IFRpbWUubG9jYWwoMjAxNiwgMTEsIDAxLCAxMiwgMzAsIDkpCiAgICAgIGNsYXNzIDw8IHQ7IGRlZiB6b25lOyAnRUNUJzsgZW5kOyBlbmQKCiAgICAgIGluX3pvbmUgJ0FzaWEvVGJpbGlzaScgZG8KCiAgICAgICAgYXNzZXJ0X2Vycm9yKAogICAgICAgICAgbGFtYmRhIHsgRXRPcmJpLm1ha2VfdGltZSh0KSB9LAogICAgICAgICAgQXJndW1lbnRFcnJvciwgL1xBQ2Fubm90IGRldGVybWluZSB0aW1lem9uZSBmcm9tICJFQ1QiLykKICAgICAgZW5kCiAgICBlbmQKCiAgICB0ZXN0ICdyZWplY3RzIHVucGFyc2FibGUgaW5wdXQnIGRvCgogICAgICBhc3NlcnRfZXJyb3IoCiAgICAgICAgbGFtYmRhIHsgRXRPcmJpLm1ha2VfdGltZSgneHh4JykgfSwKICAgICAgICBBcmd1bWVudEVycm9yLCAnTm8gdGltZSBpbmZvcm1hdGlvbiBpbiAieHh4IicpCiAgICAgICAgICAjCiAgICAgICAgICAjIHN0cmFpZ2h0IG91dCBvZiBUaW1lLnBhcnNlKCkKCiAgICAgIGFzc2VydF9lcnJvcigKICAgICAgICBsYW1iZGEgeyBFdE9yYmkubWFrZV90aW1lKE9iamVjdC5uZXcpIH0sCiAgICAgICAgQXJndW1lbnRFcnJvciwgL1xBQ2Fubm90IHR1cm4gLykKICAgIGVuZAogIGVuZAoKICBncm91cCAnLndpbmRvd3Nfem9uZV9uYW1lJyBkbwoKICAgIHsgWyAnQXNpYS9Ub2t5bycsICcyMDE4LTA1LTIzJyBdID0+ICdKU1QtOScsCiAgICAgIFsgJ0FzaWEvS29sa2F0YScsICcyMDE4LTA3LTAxJyBdID0+ICdJU1QtNTozMCcsCiAgICAgIFsgJ0FzaWEvVGVocmFuJywgJzIwMTktMDEtMDknIF0gPT4gJ19BVC0zOjMwQXNpYS9UZWhyYW4nLCAjJ0lSVC0zOjMwJywKICAgICAgWyAnQXNpYS9UZWhyYW4nLCAnMjAxOS0wNy0wOScgXSA9PiAnX0FULTQ6MzBBc2lhL1RlaHJhbicsICMnSVJULTQ6MzAnLAogICAgICBbICdBc2lhL1RiaWxpc2knLCAnMjAxOS0wMS0wOScgXSA9PiAnX0FULTQ6MDBBc2lhL1RiaWxpc2knLAogICAgICBbICdBc2lhL1NhbWFya2FuZCcsICcyMDE3LTExLTAxJyBdID0+ICdfQVMtNTowMEFzaWEvU2FtYXJrYW5kJywKICAgICAgWyAnRXVyb3BlL0JlcmxpbicsICcyMDE4LTAxLTAxJyBdID0+ICdDRVQtMUNFU1QnLAogICAgICBbICdFdXJvcGUvQmVybGluJywgJzIwMTgtMDctMDEnIF0gPT4gJ0NFVC0yQ0VTVCcsCiAgICAgIFsgJ0FtZXJpY2EvTmV3X1lvcmsnLCAnMjAxOC0wMS0wMScgXSA9PiAnRVNUNUVEVCcsCiAgICAgIFsgJ0FtZXJpY2EvTmV3X1lvcmsnLCAnMjAxOC0wNy0wMScgXSA9PiAnRVNUNEVEVCcsCiAgICAgIFsgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnLCAnMjAxNy0xMC0zMCcgXSA9PiAnUFNUN1BEVCcsCiAgICAgIFsgJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnLCAnMjAxOS0wMS0wMScgXSA9PiAnUFNUOFBEVCcsCgogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTA3LTI4JyBdID0+ICdFRVQtM0VFU1QnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4JyBdID0+ICdFRVQtM0VFU1QnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEyLTI4JyBdID0+ICdFRVQtMkVFU1QnLAoKICAgIH0uZWFjaCBkbyB8KHpvbmUsIHRpbWUpLCB2fAoKICAgICAgdGVzdCAicmV0dXJucyAje3YuaW5zcGVjdH0gZm9yICN7em9uZS5pbnNwZWN0fSBhdCAje3RpbWV9IiBkbwoKICAgICAgICBhc3NlcnQgRXRPcmJpLndpbmRvd3Nfem9uZV9uYW1lKHpvbmUsIFRpbWUucGFyc2UodGltZSkpLCB2CiAgICAgIGVuZAogICAgZW5kCiAgZW5kCgogIGdyb3VwICcuem9uZV9hYmJyZXZpYXRpb24nIGRvCgogICAgeyBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTA3LTI4JyBdID0+ICdFRVNUJywKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0xMC0yOCcgXSA9PiAnRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTItMjgnIF0gPT4gJ0VFVCcsCgogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTAzLTI1IDAyOjAwJyBdID0+ICdFRVQnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTAzLTI1IDAzOjAwJyBdID0+ICdFRVNUJywKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0wMy0yNSAwNDowMCcgXSA9PiAnRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMDMtMjUgMDU6MDAnIF0gPT4gJ0VFU1QnLAoKICAgICAgWyAnRXVyb3BlL1RhbGxpbm4nLCAnMjAxMi0xMC0yOCAwMDowMCcgXSA9PiAnRUVTVCcsCiAgICAgIFsgJ0V1cm9wZS9UYWxsaW5uJywgJzIwMTItMTAtMjggMDI6NTknIF0gPT4gJ0VFU1QnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4IDAzOjAwJyBdID0+ICdFRVQnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4IDAzOjMwJyBdID0+ICdFRVQnLAogICAgICBbICdFdXJvcGUvVGFsbGlubicsICcyMDEyLTEwLTI4IDA0OjMwJyBdID0+ICdFRVQnLAoKICAgICAgIyBpbiBjYXNlIG9mIGFtYmlndWl0eSwgLnpvbmVfYWJicmV2aWF0aW9uIGdvZXMgMiBob3VycyBmb3J3YXJkLi4uCgogICAgfS5lYWNoIGRvIHwoem9uZSwgdGltZSksIHZ8CgogICAgICB0ZXN0ICJyZXR1cm5zICN7di5pbnNwZWN0fSBmb3IgI3t6b25lLmluc3BlY3R9IGF0ICN7dGltZX0iIGRvCgogICAgICAgIGFzc2VydCBFdE9yYmkuem9uZV9hYmJyZXZpYXRpb24oem9uZSwgVGltZS5wYXJzZSh0aW1lKSksIHYKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgZ3JvdXAgJy50d2Vha196b25lX25hbWUnIGRvCgogICAgeyAnRVNUNUVEVCcgPT4gJ0VTVDVFRFQnLAogICAgICAnVVRDKzEyJyA9PiAnRXRjL0dNVC0xMicsCiAgICAgICdLb3JlYSBTdGFuZGFyZCBUaW1lJyA9PiAnQXNpYS9TZW91bCcsCgogICAgfS5lYWNoIGRvIHxuMCwgbjF8CgogICAgICB0ZXN0ICJ0dXJucyAje24wLmluc3BlY3R9IGludG8gI3tuMS5pbnNwZWN0fSIgZG8KCiAgICAgICAgYXNzZXJ0IEV0T3JiaS50d2Vha196b25lX25hbWUobjApLCBuMQogICAgICBlbmQKICAgIGVuZAogIGVuZAoKICBncm91cCAnLmNocm9uaWNfZW5hYmxlZD8nIGRvCgogICAgdGVzdCAncmV0dXJucyB0cnVlIGJ5IGRlZmF1bHQnIGRvCgogICAgICBhc3NlcnQgRXRPcmJpLmNocm9uaWNfZW5hYmxlZD8KICAgIGVuZAogIGVuZAoKICBncm91cCAnLmNocm9uaWNfZW5hYmxlZD0nIGRvCgogICAgYWZ0ZXIgZG8KICAgICAgRXRPcmJpLmNocm9uaWNfZW5hYmxlZCA9IHRydWUKICAgIGVuZAoKICAgIHRlc3QgJ2xldHMgZGlzYWJsZSBjaHJvbmljIHBhcnNpbmcnIGRvCgogICAgICBFdE9yYmkuY2hyb25pY19lbmFibGVkID0gZmFsc2UKCiAgICAgIGFzc2VydF9mYWxzZSBFdE9yYmkuY2hyb25pY19lbmFibGVkPwogICAgZW5kCiAgZW5kCgojICBncm91cCAnLmFiYnJldmlhdGVfem9uZV9uYW1lJyBkbwojCiMgICAgewojCiMgICAgICAnRVNUNUVEVCcgPT4gJ0VTVDVFRFQnLAojICAgICAgJ0JTVCcgPT4gJXdbIEdCLUVpcmUgR0IgXSwKIyAgICAgICdVVEMrMTInID0+IG5pbCwKIwojICAgIH0uZWFjaCBkbyB8bjAsIG4xfAojCiMgICAgICBuMXMgPSBuMS5pbnNwZWN0CiMgICAgICBuMXMgPSBuMS5jb2xsZWN0KCY6aW5zcGVjdCkuam9pbignIG9yICcpIGlmIG4xLmlzX2E/KEFycmF5KQojCiMgICAgICB0ZXN0ICJ0dXJucyAje24wLmluc3BlY3R9IGludG8gI3tuMXN9IiBkbwojCiMgICAgICAgIGlmIG4xLmlzX2E/KEFycmF5KQojICAgICAgICAgIGFzc2VydCBuMSkudG8gaW5jbHVkZShFdE9yYmkuYWJicmV2aWF0ZV96b25lX25hbWUobjApKQojICAgICAgICBlbHNlCiMgICAgICAgICAgYXNzZXJ0IEV0T3JiaS5hYmJyZXZpYXRlX3pvbmVfbmFtZShuMCksIG4xKQojICAgICAgICBlbmQKIyAgICAgIGVuZAojICAgIGVuZAojICBlbmQKZW5kCgo= \ 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 '.osx_tz' do + + test 'extracts the timezone relative to the zoneinfo directory' do + + symlink = File.method(:symlink?) + readlink = File.method(:readlink) + links = [ + '/var/db/timezone/zoneinfo/Europe/Istanbul', + '/usr/share/zoneinfo/Asia/Tokyo', + '/var/db/timezone/localtime' + ] + + File.define_singleton_method(:symlink?) { |path| path == '/etc/localtime' } + File.define_singleton_method(:readlink) { |_| links.shift } + + assert EtOrbi.osx_tz, 'Europe/Istanbul' + assert EtOrbi.osx_tz, 'Asia/Tokyo' + assert_nil EtOrbi.osx_tz + + ensure + + File.define_singleton_method(:symlink?, symlink) + File.define_singleton_method(:readlink, readlink) + 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 +