File Coverage

blib/lib/Test/Mock/Time.pm
Criterion Covered Total %
statement 314 365 86.0
branch 93 162 57.4
condition 24 47 51.0
subroutine 62 70 88.5
pod 1 1 100.0
total 494 645 76.5


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