1#!/usr/bin/perl 2 3use strict; 4use warnings; 5 6use IO::Async::Test; 7 8use Test::More; 9use Test::Fatal; 10use Test::Refcount; 11 12use lib "."; 13use t::TimeAbout; 14 15use Time::HiRes qw( time ); 16 17use IO::Async::Timer::Countdown; 18 19use IO::Async::Loop; 20 21use constant AUT => $ENV{TEST_QUICK_TIMERS} ? 0.1 : 1; 22 23my $loop = IO::Async::Loop->new_builtin; 24 25testing_loop( $loop ); 26 27{ 28 my $expired; 29 my @eargs; 30 31 my $timer = IO::Async::Timer::Countdown->new( 32 delay => 2 * AUT, 33 34 on_expire => sub { @eargs = @_; $expired = 1 }, 35 ); 36 37 ok( defined $timer, '$timer defined' ); 38 isa_ok( $timer, "IO::Async::Timer", '$timer isa IO::Async::Timer' ); 39 40 is_oneref( $timer, '$timer has refcount 1 initially' ); 41 42 $loop->add( $timer ); 43 44 is_refcount( $timer, 2, '$timer has refcount 2 after adding to Loop' ); 45 46 ok( !$timer->is_running, 'New Timer is no yet running' ); 47 ok( !$timer->is_expired, 'New Timer is no yet expired' ); 48 49 is( $timer->start, $timer, '$timer->start returns $timer' ); 50 51 is_refcount( $timer, 2, '$timer has refcount 2 after starting' ); 52 53 ok( $timer->is_running, 'Started Timer is running' ); 54 ok( !$timer->is_expired, 'Started Timer not yet expired' ); 55 56 time_about( sub { wait_for { $expired } }, 2, 'Timer works' ); 57 is_deeply( \@eargs, [ $timer ], 'on_expire args' ); 58 59 ok( !$timer->is_running, 'Expired Timer is no longer running' ); 60 ok( $timer->is_expired, 'Expired Timer now expired' ); 61 62 undef @eargs; 63 64 is_refcount( $timer, 2, '$timer has refcount 2 before removing from Loop' ); 65 66 $loop->remove( $timer ); 67 68 is_oneref( $timer, '$timer has refcount 1 after removing from Loop' ); 69 70 undef $expired; 71 72 is( $timer->start, $timer, '$timer->start out of a Loop returns $timer' ); 73 74 $loop->add( $timer ); 75 76 ok( $timer->is_running, 'Re-started Timer is running' ); 77 ok( !$timer->is_expired, 'Re-started Timer not yet expired' ); 78 79 time_about( sub { wait_for { $expired } }, 2, 'Timer works a second time' ); 80 81 ok( !$timer->is_running, '2nd-time expired Timer is no longer running' ); 82 ok( $timer->is_expired, '2nd-time expired Timer now expired' ); 83 84 undef $expired; 85 $timer->start; 86 87 $loop->loop_once( 1 * AUT ); 88 89 $timer->stop; 90 91 $timer->stop; 92 93 ok( 1, "Timer can be stopped a second time" ); 94 95 $loop->loop_once( 2 * AUT ); 96 97 ok( !$expired, "Stopped timer doesn't expire" ); 98 99 undef $expired; 100 $timer->start; 101 102 $loop->loop_once( 1 * AUT ); 103 104 my $now = time; 105 $timer->reset; 106 107 $loop->loop_once( 1.5 * AUT ); 108 109 ok( !$expired, "Reset Timer hasn't expired yet" ); 110 111 wait_for { $expired }; 112 my $took = (time - $now) / AUT; 113 114 cmp_ok( $took, '>', 1.5, "Timer has now expired took at least 1.5" ); 115 cmp_ok( $took, '<', 2.5, "Timer has now expired took no more than 2.5" ); 116 117 $loop->remove( $timer ); 118 119 undef @eargs; 120 121 is_oneref( $timer, 'Timer has refcount 1 finally' ); 122} 123 124{ 125 my $timer = IO::Async::Timer::Countdown->new( 126 delay => 2 * AUT, 127 on_expire => sub { }, 128 ); 129 130 $loop->add( $timer ); 131 132 $timer->start; 133 134 $loop->remove( $timer ); 135 136 $loop->loop_once( 3 * AUT ); 137 138 ok( !$timer->is_expired, "Removed Timer does not expire" ); 139} 140 141{ 142 my $timer = IO::Async::Timer::Countdown->new( 143 delay => 2 * AUT, 144 on_expire => sub { }, 145 ); 146 147 $timer->start; 148 149 $loop->add( $timer ); 150 151 ok( $timer->is_running, 'Pre-started Timer is running after adding' ); 152 153 time_about( sub { wait_for { $timer->is_expired } }, 2, 'Pre-started Timer works' ); 154 155 $loop->remove( $timer ); 156} 157 158{ 159 my $timer = IO::Async::Timer::Countdown->new( 160 delay => 2 * AUT, 161 on_expire => sub { }, 162 ); 163 164 $timer->start; 165 $timer->stop; 166 167 $loop->add( $timer ); 168 169 $loop->loop_once( 3 * AUT ); 170 171 ok( !$timer->is_expired, "start/stopped Timer doesn't expire" ); 172 173 $loop->remove( $timer ); 174} 175 176{ 177 my $timer = IO::Async::Timer::Countdown->new( 178 delay => 2 * AUT, 179 on_expire => sub { }, 180 ); 181 182 $loop->add( $timer ); 183 184 $timer->configure( delay => 1 * AUT ); 185 186 $timer->start; 187 188 time_about( sub { wait_for { $timer->is_expired } }, 1, 'Reconfigured timer delay works' ); 189 190 my $expired; 191 $timer->configure( on_expire => sub { $expired = 1 } ); 192 193 $timer->start; 194 195 time_about( sub { wait_for { $expired } }, 1, 'Reconfigured timer on_expire works' ); 196 197 $timer->start; 198 ok( exception { $timer->configure( delay => 5 ); }, 199 'Configure a running timer fails' ); 200 201 $loop->remove( $timer ); 202} 203 204{ 205 my $timer = IO::Async::Timer::Countdown->new( 206 delay => 1 * AUT, 207 remove_on_expire => 1, 208 209 on_expire => sub { }, 210 ); 211 212 $loop->add( $timer ); 213 $timer->start; 214 215 time_about( sub { wait_for { $timer->is_expired } }, 1, 'remove_on_expire Timer' ); 216 217 is( $timer->loop, undef, 'remove_on_expire Timer removed from Loop after expire' ); 218} 219 220## Subclass 221 222my $sub_expired; 223{ 224 my $timer = TestTimer->new( 225 delay => 2 * AUT, 226 ); 227 228 ok( defined $timer, 'subclass $timer defined' ); 229 isa_ok( $timer, "IO::Async::Timer", 'subclass $timer isa IO::Async::Timer' ); 230 231 is_oneref( $timer, 'subclass $timer has refcount 1 initially' ); 232 233 $loop->add( $timer ); 234 235 is_refcount( $timer, 2, 'subclass $timer has refcount 2 after adding to Loop' ); 236 237 $timer->start; 238 239 is_refcount( $timer, 2, 'subclass $timer has refcount 2 after starting' ); 240 241 ok( $timer->is_running, 'Started subclass Timer is running' ); 242 243 time_about( sub { wait_for { $sub_expired } }, 2, 'subclass Timer works' ); 244 245 ok( !$timer->is_running, 'Expired subclass Timer is no longer running' ); 246 247 is_refcount( $timer, 2, 'subclass $timer has refcount 2 before removing from Loop' ); 248 249 $loop->remove( $timer ); 250 251 is_oneref( $timer, 'subclass $timer has refcount 1 after removing from Loop' ); 252} 253 254done_testing; 255 256package TestTimer; 257use base qw( IO::Async::Timer::Countdown ); 258 259sub on_expire { $sub_expired = 1 } 260