File Coverage

blib/lib/Test/Mock/Time.pm
Criterion Covered Total %
statement 314 367 85.5
branch 100 172 58.1
condition 21 33 63.6
subroutine 62 70 88.5
pod 1 1 100.0
total 498 643 77.4


line stmt bran cond sub pod time code
1             package Test::Mock::Time;
2 8     8   1255652 use 5.008001;
  8         35  
3 8     8   54 use warnings;
  8         55  
  8         490  
4 8     8   56 use strict;
  8         15  
  8         241  
5 8     8   1077 use utf8;
  8         565  
  8         70  
6 8     8   338 use Carp;
  8         15  
  8         980  
7              
8             our $VERSION = 'v0.2.1';
9              
10 8     8   5151 use Export::Attrs;
  8         148092  
  8         64  
11 8     8   936 use List::Util qw( any );
  8         14  
  8         672  
12 8     8   61 use Scalar::Util qw( weaken );
  8         16  
  8         449  
13 8     8   5978 use Test::MockModule;
  8         70125  
  8         71  
14              
15 8     8   1541 use constant TIME_HIRES_CLOCK_NOT_SUPPORTED => -1;
  8         68  
  8         916  
16 8     8   67 use constant MICROSECONDS => 1_000_000;
  8         18  
  8         489  
17 8     8   65 use constant NANOSECONDS => 1_000_000_000;
  8         48  
  8         444  
18              
19 8     8   74 use constant DEFAULT_WAIT_ONE_TICK => 0.05;
  8         36  
  8         8442  
20             our $WAIT_ONE_TICK = DEFAULT_WAIT_ONE_TICK;
21              
22             my $Absolute = time; # usual time starts at current actual time
23             my $Monotonic = 0; # monotonic time starts at 0 if not available
24             my $Relative = 0; # how many deterministic time passed since start
25             my @Timers; # active timers
26             my @Timers_ns; # inactive timers
27             my %Module; # keep module mocks
28              
29              
30             _mock_core_global();
31             ## no critic (RequireCheckingReturnValueOfEval)
32             eval {
33             require Time::HiRes;
34             Time::HiRes->import(qw( CLOCK_REALTIME CLOCK_MONOTONIC ));
35             _mock_time_hires();
36             };
37             eval {
38             require EV;
39             _mock_ev();
40             };
41             eval {
42             require Mojolicious;
43             Mojolicious->VERSION('6'); # may be compatible with older ones, needs testing
44             require Mojo::Reactor::Poll;
45             _mock_mojolicious();
46             };
47              
48              
49             # FIXME make ff() reentrant
50             sub ff :Export(:DEFAULT) {
51 51     51 1 1087 my ($dur) = @_;
52              
53             @Timers = sort {
54 51         171 $a->{start}+$a->{after} <=> $b->{start}+$b->{after} or
55             $a->{id} cmp $b->{id} # preserve order to simplify tests
56 0 0       0 } @Timers;
57 51 100       296 my $next_at = @Timers ? $Timers[0]{start}+$Timers[0]{after} : 0;
58 51         431 $next_at = sprintf '%.6f', $next_at;
59              
60 51 100       169 if (!defined $dur) {
61 23 50       135 $dur = $next_at > $Relative ? $next_at - $Relative : 0;
62 23         103 $dur = sprintf '%.6f', $dur;
63             }
64 51 50       219 croak "ff($dur): negative time not invented yet" if $dur < 0;
65              
66 51 100 100     300 if ($next_at == 0 || $next_at > $Relative+$dur) {
67 28         59 $Relative += $dur;
68 28         119 $Relative = sprintf '%.6f', $Relative;
69 28         100 return;
70             }
71              
72 23 50       75 if ($next_at > $Relative) {
73 23         53 $dur -= $next_at - $Relative;
74 23         85 $dur = sprintf '%.6f', $dur;
75 23         52 $Relative = $next_at;
76             }
77 23         78 my $cb = $Timers[0]{cb};
78 23 100       101 if ($Timers[0]{repeat} == 0) {
79 7 100       27 if ($Timers[0]{watcher}) {
80 3         15 _stop_timer($Timers[0]{watcher});
81             }
82             else {
83 4         9 shift @Timers;
84             }
85             }
86             else {
87 16         52 $Timers[0]{after} = $Timers[0]{repeat};
88 16         47 $Timers[0]{start} = $Relative;
89             }
90 23         157 $cb->();
91 23         536 @_ = ($dur);
92 23         162 goto &ff;
93 8     8   80 }
  8         21  
  8         69  
94              
95             {
96             my $next_id = 0;
97             sub _add_timer {
98 20     20   69 my ($loop, $after, $repeat, $cb, $watcher) = @_;
99 20         162 my $id = sprintf 'fake_%05d', $next_id++;
100 20 50       480 push @Timers, {
    50          
101             id => $id,
102             start => $Relative,
103             loop => $loop,
104             after => sprintf('%.6f', $after < 0 ? 0 : $after),
105             repeat => sprintf('%.6f', $repeat < 0 ? 0 : $repeat),
106             cb => $cb,
107             watcher => $watcher,
108             };
109 20 100       99 if ($watcher) {
110 13         46 weaken($Timers[-1]{watcher});
111             }
112 20         68 return $id;
113             }
114             }
115              
116             sub _start_timer {
117 2     2   6 my ($watcher) = @_;
118 2 50       75 my ($timer) = grep { $_->{watcher} && $_->{watcher} eq $watcher } @Timers_ns;
  4         62  
119 2 50       10 if ($timer) {
120 2 50       5 @Timers_ns = grep { !$_->{watcher} || $_->{watcher} ne $watcher } @Timers_ns;
  4         23  
121 2         5 push @Timers, $timer;
122             }
123 2         8 return;
124             }
125              
126             sub _stop_timer {
127 13     13   39 my ($watcher) = @_;
128 13 50       37 my ($timer) = grep { $_->{watcher} && $_->{watcher} eq $watcher } @Timers;
  6         87  
129 13 100       42 if ($timer) {
130 6 50       34 @Timers = grep { !$_->{watcher} || $_->{watcher} ne $watcher } @Timers;
  6         53  
131 6         18 push @Timers_ns, $timer;
132             }
133 13         59 return;
134             }
135              
136             sub _mock_core_global {
137 8     8   94 $Module{'CORE::GLOBAL'} = Test::MockModule->new('CORE::GLOBAL', no_auto=>1);
138             $Module{'CORE::GLOBAL'}->mock(time => sub () {
139 46     46   1292253 return int($Absolute + $Relative);
140 8         478 });
141             $Module{'CORE::GLOBAL'}->mock(localtime => sub (;$) {
142 12 50   12   4502373 my $time = defined $_[0] ? $_[0] : int($Absolute + $Relative);
143 12         446 return CORE::localtime($time);
144 8         1384 });
145             $Module{'CORE::GLOBAL'}->mock(gmtime => sub (;$) {
146 13 100   13   1038 my $time = defined $_[0] ? $_[0] : int($Absolute + $Relative);
147 13         105 return CORE::gmtime($time);
148 8         898 });
149             $Module{'CORE::GLOBAL'}->mock(sleep => sub ($) {
150 16     16   249 my $dur = int $_[0];
151 16 100       96 croak 'sleep with negative value is not supported' if $dur < 0;
152 14         31 $Relative += $dur;
153 14         88 $Relative = sprintf '%.6f', $Relative;
154 14         48 return $dur;
155 8         881 });
156 8         846 return;
157             }
158              
159             sub _mock_time_hires {
160             # Do not improve precision of current actual time to simplify tests.
161             #$Absolute = Time::HiRes::time();
162             # Use current actual monotonic time.
163 8     8   95 $Monotonic = Time::HiRes::clock_gettime(CLOCK_MONOTONIC());
164              
165 8         330 $Module{'Time::HiRes'} = Test::MockModule->new('Time::HiRes');
166             $Module{'Time::HiRes'}->mock(time => sub () {
167 8     8   1825199 return 0+sprintf '%.6f', $Absolute + $Relative;
168 8         518 });
169             $Module{'Time::HiRes'}->mock(gettimeofday => sub () {
170 4     4   4992 my $t = sprintf '%.6f', $Absolute + $Relative;
171 4 100       66 return wantarray ? (map {0+$_} split qr/[.]/ms, $t) : 0+$t;
  4         35  
172 8         986 });
173             $Module{'Time::HiRes'}->mock(clock_gettime => sub (;$) {
174 51     51   1985697 my ($which) = @_;
175 51 100       236 if ($which == CLOCK_REALTIME()) {
    100          
176 1         25 return 0+sprintf '%.6f', $Absolute + $Relative;
177             }
178             elsif ($which == CLOCK_MONOTONIC()) {
179 49         2401 return 0+sprintf '%.6f', $Monotonic + $Relative;
180             }
181 1         16 return TIME_HIRES_CLOCK_NOT_SUPPORTED;
182 8         869 });
183             $Module{'Time::HiRes'}->mock(clock_getres => sub (;$) {
184 3     3   11634 my ($which) = @_;
185 3 100 100     17 if ($which == CLOCK_REALTIME() || $which == CLOCK_MONOTONIC()) {
186 2         44 return $Module{'Time::HiRes'}->original('clock_getres')->(@_);
187             }
188 1         27 return TIME_HIRES_CLOCK_NOT_SUPPORTED;
189 8         886 });
190             $Module{'Time::HiRes'}->mock(sleep => sub (;@) {
191 3     3   225 my ($seconds) = @_;
192 3 100       29 croak 'sleep without arg is not supported' if !@_;
193 2 100       41 croak "Time::HiRes::sleep($seconds): negative time not invented yet" if $seconds < 0;
194 1         5 $Relative += $seconds;
195 1         15 $Relative = sprintf '%.6f', $Relative;
196 1         5 return $seconds;
197 8         843 });
198             $Module{'Time::HiRes'}->mock(usleep => sub ($) {
199 10     10   229 my ($useconds) = @_;
200 10 100       4103 croak "Time::HiRes::usleep($useconds): negative time not invented yet" if $useconds < 0;
201 9         41 $Relative += $useconds / MICROSECONDS;
202 9         56 $Relative = sprintf '%.6f', $Relative;
203 9         28 return $useconds;
204 8         829 });
205             $Module{'Time::HiRes'}->mock(nanosleep => sub ($) {
206 2     2   104 my ($nanoseconds) = @_;
207 2 100       31 croak "Time::HiRes::nanosleep($nanoseconds): negative time not invented yet" if $nanoseconds < 0;
208 1         4 $Relative += $nanoseconds / NANOSECONDS;
209 1         45 $Relative = sprintf '%.6f', $Relative;
210 1         4 return $nanoseconds;
211 8         798 });
212             $Module{'Time::HiRes'}->mock(clock_nanosleep => sub ($$;$) {
213 5     5   698 my ($which, $nanoseconds, $flags) = @_;
214 5 100       42 croak "Time::HiRes::clock_nanosleep(..., $nanoseconds): negative time not invented yet" if $nanoseconds < 0;
215 4 100 100     15 croak 'only CLOCK_REALTIME and CLOCK_MONOTONIC are supported' if $which != CLOCK_REALTIME() && $which != CLOCK_MONOTONIC();
216 3 100       40 croak 'only flags=0 is supported' if $flags;
217 2         41 $Relative += $nanoseconds / NANOSECONDS;
218 2         18 $Relative = sprintf '%.6f', $Relative;
219 2         6 return $nanoseconds;
220 8         821 });
221 8         949 return;
222             }
223              
224             # TODO Distinguish timers set on different event loops / Mojo reactor
225             # objects while one_tick?
226              
227             sub _mock_ev { ## no critic (ProhibitExcessComplexity)
228 8     8   84 $Module{'EV'} = Test::MockModule->new('EV');
229 8         1167 $Module{'EV::Watcher'} = Test::MockModule->new('EV::Watcher', no_auto=>1);
230 8         298 $Module{'EV::Timer'} = Test::MockModule->new('EV::Timer', no_auto=>1);
231 8         242 $Module{'EV::Periodic'} = Test::MockModule->new('EV::Periodic', no_auto=>1);
232             $Module{'EV'}->mock(time => sub () {
233 7     7   140 return 0+sprintf '%.6f', $Absolute + $Relative;
234 8         274 });
235             $Module{'EV'}->mock(now => sub () {
236 21     21   747 return 0+sprintf '%.6f', $Absolute + $Relative;
237 8         1083 });
238             $Module{'EV'}->mock(sleep => sub ($) {
239 4     4   14 my ($seconds) = @_;
240 4 100       18 if ($seconds < 0) {
241 1         3 $seconds = 0;
242             }
243 4         12 $Relative += $seconds;
244 4         33 $Relative = sprintf '%.6f', $Relative;
245 4         10 return;
246 8         897 });
247             $Module{'EV'}->mock(run => sub (;$) {
248 23     23   666 my ($flags) = @_;
249 23         52 my $tick = 0;
250 23         52 my $w;
251 23 100       83 if (@Timers) {
252             $w = $Module{'EV'}->original('timer')->(
253             $WAIT_ONE_TICK, $WAIT_ONE_TICK, sub {
254 23     23   1080908 my $me = shift;
255 23         62 my $k;
256 23 100 66     236 if (!$tick++ || !$flags) {
257 15         197 $k = $me->keepalive(0);
258 15         81 ff();
259             }
260 23 100 33     232 if (!@Timers) {
    100 66        
261 7         65 $me->stop;
262             }
263             elsif ($k && ($flags || any {$_->{watcher} && $_->{watcher}->keepalive} @Timers)) {
264 8         64 $me->keepalive(1);
265             }
266             }
267 15         153 );
268 15 50 66 2   443 if (!($flags || any {$_->{watcher} && $_->{watcher}->keepalive} @Timers)) {
  2 50       37  
269 0         0 $w->keepalive(0);
270             }
271             }
272             # $tick above and this second RUN_ONCE is work around bug in EV-4.10+
273             # http://lists.schmorp.de/pipermail/libev/2016q1/002656.html
274             # FIXME I believe this workaround isn't correct with EV-4.03 - calling
275             # RUN_ONCE twice must have side effect in processing two events
276             # (at least one of them must be a non-timer event) instead of one.
277             # To make it correct we probably need to mock all watcher types
278             # to intercept invoking their callbacks and thus make it possible
279             # to find out is first RUN_ONCE has actually called any callbacks.
280 23 100 66     150 if ($flags && $flags == EV::RUN_ONCE()) {
281 18         75 $Module{'EV'}->original('run')->(@_);
282             }
283 23         304 return $Module{'EV'}->original('run')->(@_);
284 8         850 });
285             $Module{'EV'}->mock(timer => sub ($$$) {
286 11     11   180 my ($after, $repeat, $cb) = @_;
287 11         78 my $w = $Module{'EV'}->original('timer_ns')->(@_);
288 11         374 weaken(my $weakw = $w);
289 11 50   13   141 _add_timer('EV', $after, $repeat, sub { $weakw && $weakw->invoke(EV::TIMER()) }, $w);
  13         226  
290 11         56 return $w;
291 8         3017 });
292             $Module{'EV'}->mock(timer_ns => sub ($$$) {
293 1     1   5 my ($after, $repeat, $cb) = @_;
294 1         5 my $w = EV::timer($after, $repeat, $cb);
295 1         6 _stop_timer($w);
296 1         5 return $w;
297 8         878 });
298             $Module{'EV'}->mock(periodic => sub ($$$$) {
299 2     2   9 my ($at, $repeat, $reschedule_cb, $cb) = @_;
300 2 50       12 croak 'reschedule_cb is not supported yet' if $reschedule_cb;
301 2 50       23 $at = sprintf '%.6f', $at < 0 ? 0 : $at;
302 2 50       16 $repeat = sprintf '%.6f', $repeat < 0 ? 0 : $repeat;
303 2         14 my $now = sprintf '%.6f', $Absolute + $Relative;
304 2 100 66     21 if ($repeat > 0 && $at < $now) {
305 8     8   42560 use bignum;
  8         78592  
  8         35  
306 1         75 $at += $repeat * int(($now - $at) / $repeat + 1);
307 1         4419 $at = sprintf '%.6f', $at;
308             }
309 2 50       92 my $after = $at > $now ? $at - $now : 0;
310 2         13 $after = sprintf '%.6f', $after;
311 2         16 my $w = $Module{'EV'}->original('periodic_ns')->(@_);
312 2         60 weaken(my $weakw = $w);
313 2 50   2   41 _add_timer('EV', $after, $repeat, sub { $weakw && $weakw->invoke(EV::TIMER()) }, $w);
  2         46  
314 2         9 return $w;
315 8         861 });
316             $Module{'EV'}->mock(periodic_ns => sub ($$$$) {
317 0     0   0 my ($at, $repeat, $reschedule_cb, $cb) = @_;
318 0         0 my $w = EV::periodic($at, $repeat, $reschedule_cb, $cb);
319 0         0 _stop_timer($w);
320 0         0 return $w;
321 8         839 });
322             $Module{'EV::Watcher'}->mock(is_active => sub {
323 2     2   8 my ($w) = @_;
324 2 50       7 my ($active) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers;
  2         23  
325 2 50       5 my ($inactive) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers_ns;
  2         16  
326 2 100       9 if ($active) {
    50          
327 1         7 return 1;
328             }
329             elsif ($inactive) {
330 1         7 return;
331             }
332 0         0 return $Module{'EV::Watcher'}->original('is_active')->(@_);
333 8         851 });
334             $Module{'EV::Timer'}->mock(DESTROY => sub {
335 26     26   590 my ($w) = @_;
336 26 50       120 @Timers = grep { !$_->{watcher} || $_->{watcher} ne $w } @Timers;
  18         350  
337 26 50       66 @Timers_ns = grep { !$_->{watcher} || $_->{watcher} ne $w } @Timers_ns;
  11         159  
338 26         200 return $Module{'EV::Timer'}->original('DESTROY')->(@_);
339 8         860 });
340             $Module{'EV::Timer'}->mock(start => sub {
341 1     1   6 return _start_timer(@_);
342 8         820 });
343             $Module{'EV::Timer'}->mock(stop => sub {
344 9     9   54 return _stop_timer(@_);
345 8         1016 });
346             $Module{'EV::Timer'}->mock(set => sub {
347 0     0   0 my ($w, $after, $repeat) = @_;
348 0 0       0 if (!defined $repeat) {
349 0         0 $repeat = 0;
350             }
351 0 0       0 my ($timer) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers, @Timers_ns;
  0         0  
352 0 0       0 if ($timer) {
353 0         0 $timer->{start} = $Relative;
354 0 0       0 $timer->{after} = sprintf '%.6f', $after < 0 ? 0 : $after;
355 0 0       0 $timer->{repeat}= sprintf '%.6f', $repeat < 0 ? 0 : $repeat;
356             }
357 0         0 return;
358 8         827 });
359             $Module{'EV::Timer'}->mock(remaining => sub {
360 0     0   0 my ($w) = @_;
361 0 0       0 my ($timer) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers, @Timers_ns;
  0         0  
362 0 0       0 if ($timer) {
363 0         0 return 0+sprintf '%.6f', $timer->{start} + $timer->{after} - $Relative;
364             }
365 0         0 return;
366 8         819 });
367             $Module{'EV::Timer'}->mock(again => sub {
368 3     3   62 my ($w, $repeat) = @_;
369 3 50 33     16 if (defined $repeat && $repeat < 0) {
370 0         0 $repeat = 0;
371             }
372 3 50       11 my ($active) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers;
  2         26  
373 3 50       9 my ($inactive) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers_ns;
  2         22  
374 3 100       16 if ($active) {
    50          
375 2 50       30 $active->{repeat} = sprintf '%.6f', defined $repeat ? $repeat : $active->{repeat};
376 2 50       13 if ($active->{repeat} > 0) {
377 2         9 $active->{after} = $active->{repeat};
378 2         8 $active->{start} = $Relative;
379             }
380             else {
381 0         0 _stop_timer($active->{watcher});
382             }
383             }
384             elsif ($inactive) {
385 1 50       22 $inactive->{repeat} = sprintf '%.6f', defined $repeat ? $repeat : $inactive->{repeat};
386 1 50       7 if ($inactive->{repeat} > 0) {
387 1         5 $inactive->{after} = $inactive->{repeat};
388 1         4 $inactive->{start} = $Relative;
389 1         6 _start_timer($inactive->{watcher});
390             }
391             }
392 3         15 return;
393 8         897 });
394             $Module{'EV::Periodic'}->mock(DESTROY => sub {
395 2     2   3256 my ($w) = @_;
396 2 50       6 @Timers = grep { !$_->{watcher} || $_->{watcher} ne $w } @Timers;
  1         24  
397 2 50       7 @Timers_ns = grep { !$_->{watcher} || $_->{watcher} ne $w } @Timers_ns;
  2         22  
398 2         13 return $Module{'EV::Periodic'}->original('DESTROY')->(@_);
399 8         854 });
400             $Module{'EV::Periodic'}->mock(start => sub {
401 0     0   0 return _start_timer(@_);
402 8         844 });
403             $Module{'EV::Periodic'}->mock(stop => sub {
404 0     0   0 return _stop_timer(@_);
405 8         849 });
406             $Module{'EV::Periodic'}->mock(set => sub {
407 0     0   0 my ($w, $at, $repeat, $reschedule_cb, $cb) = @_;
408 0 0       0 croak 'reschedule_cb is not supported yet' if $reschedule_cb;
409 0 0       0 $at = sprintf '%.6f', $at < 0 ? 0 : $at;
410 0 0       0 $repeat = sprintf '%.6f', $repeat < 0 ? 0 : $repeat;
411 0         0 my $now = sprintf '%.6f', $Absolute + $Relative;
412 0 0 0     0 if ($repeat > 0 && $at < $now) {
413 8     8   1411697 use bignum;
  8         22  
  8         48  
414 0         0 $at += $repeat * int(($now - $at) / $repeat + 1);
415 0         0 $at = sprintf '%.6f', $at;
416             }
417 0 0       0 my $after = $at > $now ? $at - $now : 0;
418 0         0 $after = sprintf '%.6f', $after;
419 0 0       0 my ($timer) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers, @Timers_ns;
  0         0  
420 0 0       0 if ($timer) {
421 0         0 $timer->{start} = $Relative;
422 0         0 $timer->{after} = $after;
423 0         0 $timer->{repeat}= $repeat;
424             }
425 0         0 return;
426 8         915 });
427             $Module{'EV::Periodic'}->mock(again => sub {
428 0     0   0 return _start_timer(@_);
429 8         838 });
430             $Module{'EV::Periodic'}->mock(at => sub {
431 0     0   0 my ($w) = @_;
432 0 0       0 my ($timer) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers, @Timers_ns;
  0         0  
433 0 0       0 if ($timer) {
434 0         0 return 0+sprintf '%.6f', $timer->{start} + $timer->{after};
435             }
436 0         0 return;
437 8         793 });
438 8         777 return;
439             }
440              
441             sub _mock_mojolicious {
442 8     8   119 $Module{'Mojo::Reactor::Poll'} = Test::MockModule->new('Mojo::Reactor::Poll');
443             $Module{'Mojo::Reactor::Poll'}->mock(one_tick => sub {
444 10     10   339 my ($self) = @_;
445 10 100       33 if (!@Timers) {
446 2         13 return $Module{'Mojo::Reactor::Poll'}->original('one_tick')->(@_);
447             }
448             my $id = $Module{'Mojo::Reactor::Poll'}->original('timer')->(
449 8     8   103 $self, $WAIT_ONE_TICK, sub { ff() }
450 8         74 );
451 8         65 $Module{'Mojo::Reactor::Poll'}->original('one_tick')->(@_);
452 8         109 $Module{'Mojo::Reactor::Poll'}->original('remove')->($self, $id);
453 8         222 return;
454 8         757 });
455             $Module{'Mojo::Reactor::Poll'}->mock(timer => sub {
456 5     5   1542 my ($self, $delay, $cb) = @_;
457 5 50       22 if ($delay == 0) { # do not fake timer for 0 seconds to avoid hang
458 0         0 return $Module{'Mojo::Reactor::Poll'}->original('timer')->(@_);
459             }
460 5     4   65 return _add_timer($self, $delay, 0, sub { $cb->($self) });
  4         36  
461 8         1296 });
462             $Module{'Mojo::Reactor::Poll'}->mock(recurring => sub {
463 2     2   57 my ($self, $delay, $cb) = @_;
464 2     4   35 return _add_timer($self, $delay, $delay, sub { $cb->($self) });
  4         16  
465 8         940 });
466             $Module{'Mojo::Reactor::Poll'}->mock(again => sub {
467 2     2   28 my ($self, $id) = @_;
468 2 50       21 if ($id !~ /\Afake_\d+\z/ms) {
469 0         0 $Module{'Mojo::Reactor::Poll'}->original('again')->(@_);
470             }
471             else {
472 2         5 my ($timer) = grep { $_->{id} eq $id } @Timers;
  2         10  
473 2 50       8 if ($timer) {
474 2         7 $timer->{start} = $Relative;
475             }
476             }
477 2         5 return;
478 8         998 });
479             $Module{'Mojo::Reactor::Poll'}->mock(remove => sub {
480 12     12   316 my ($self, $id) = @_;
481 12 100       57 if ($id !~ /\Afake_\d+\z/ms) {
482 10         42 $Module{'Mojo::Reactor::Poll'}->original('remove')->(@_);
483             }
484             else {
485 2 50       7 @Timers = grep { $_->{loop} ne $self || $_->{id} ne $id } @Timers;
  2         39  
486             }
487 12         262 return;
488 8         942 });
489             $Module{'Mojo::Reactor::Poll'}->mock(reset => sub {
490 2     2   179 my ($self) = @_;
491 2         10 @Timers = grep { $_->{loop} ne $self } @Timers;
  2         22  
492 2         14 return $Module{'Mojo::Reactor::Poll'}->original('reset')->(@_);
493 8         842 });
494 8         791 return;
495             }
496              
497              
498             1;
499             __END__