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   476584 use 5.008001;
  8         103  
3 8     8   36 use warnings;
  8         11  
  8         175  
4 8     8   35 use strict;
  8         13  
  8         141  
5 8     8   1041 use utf8;
  8         41  
  8         51  
6 8     8   190 use Carp;
  8         13  
  8         547  
7              
8             our $VERSION = 'v0.2.0';
9              
10 8     8   3173 use Export::Attrs;
  8         58019  
  8         43  
11 8     8   589 use List::Util qw( any );
  8         16  
  8         779  
12 8     8   56 use Scalar::Util qw( weaken );
  8         15  
  8         348  
13 8     8   3297 use Test::MockModule;
  8         35295  
  8         59  
14              
15 8     8   304 use constant TIME_HIRES_CLOCK_NOT_SUPPORTED => -1;
  8         15  
  8         617  
16 8     8   44 use constant MICROSECONDS => 1_000_000;
  8         13  
  8         308  
17 8     8   35 use constant NANOSECONDS => 1_000_000_000;
  8         13  
  8         359  
18              
19 8     8   44 use constant DEFAULT_WAIT_ONE_TICK => 0.05;
  8         15  
  8         3792  
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 572 my ($dur) = @_;
52              
53             @Timers = sort {
54 51         142 $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       228 my $next_at = @Timers ? $Timers[0]{start}+$Timers[0]{after} : 0;
58 51         387 $next_at = sprintf '%.6f', $next_at;
59              
60 51 100       153 if (!defined $dur) {
61 23 50       152 $dur = $next_at > $Relative ? $next_at - $Relative : 0;
62 23         93 $dur = sprintf '%.6f', $dur;
63             }
64 51 50       157 croak "ff($dur): negative time not invented yet" if $dur < 0;
65              
66 51 100 100     251 if ($next_at == 0 || $next_at > $Relative+$dur) {
67 28         67 $Relative += $dur;
68 28         104 $Relative = sprintf '%.6f', $Relative;
69 28         86 return;
70             }
71              
72 23 50       72 if ($next_at > $Relative) {
73 23         43 $dur -= $next_at - $Relative;
74 23         113 $dur = sprintf '%.6f', $dur;
75 23         47 $Relative = $next_at;
76             }
77 23         59 my $cb = $Timers[0]{cb};
78 23 100       98 if ($Timers[0]{repeat} == 0) {
79 7 100       20 if ($Timers[0]{watcher}) {
80 3         12 _stop_timer($Timers[0]{watcher});
81             }
82             else {
83 4         6 shift @Timers;
84             }
85             }
86             else {
87 16         42 $Timers[0]{after} = $Timers[0]{repeat};
88 16         45 $Timers[0]{start} = $Relative;
89             }
90 23         107 $cb->();
91 23         308 @_ = ($dur);
92 23         128 goto &ff;
93 8     8   56 }
  8         12  
  8         77  
94              
95             {
96             my $next_id = 0;
97             sub _add_timer {
98 20     20   56 my ($loop, $after, $repeat, $cb, $watcher) = @_;
99 20         78 my $id = sprintf 'fake_%05d', $next_id++;
100 20 50       283 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       82 if ($watcher) {
110 13         44 weaken($Timers[-1]{watcher});
111             }
112 20         46 return $id;
113             }
114             }
115              
116             sub _start_timer {
117 2     2   6 my ($watcher) = @_;
118 2 50       5 my ($timer) = grep { $_->{watcher} && $_->{watcher} eq $watcher } @Timers_ns;
  4         17  
119 2 50       6 if ($timer) {
120 2 50       4 @Timers_ns = grep { !$_->{watcher} || $_->{watcher} ne $watcher } @Timers_ns;
  4         19  
121 2         4 push @Timers, $timer;
122             }
123 2         4 return;
124             }
125              
126             sub _stop_timer {
127 13     13   27 my ($watcher) = @_;
128 13 50       32 my ($timer) = grep { $_->{watcher} && $_->{watcher} eq $watcher } @Timers;
  6         49  
129 13 100       34 if ($timer) {
130 6 50       13 @Timers = grep { !$_->{watcher} || $_->{watcher} ne $watcher } @Timers;
  6         96  
131 6         15 push @Timers_ns, $timer;
132             }
133 13         45 return;
134             }
135              
136             sub _mock_core_global {
137 8     8   70 $Module{'CORE::GLOBAL'} = Test::MockModule->new('CORE::GLOBAL', no_auto=>1);
138             $Module{'CORE::GLOBAL'}->mock(time => sub () {
139 46     46   709 return int($Absolute + $Relative);
140 8         385 });
141             $Module{'CORE::GLOBAL'}->mock(localtime => sub (;$) {
142 12 50   12   2589579 my $time = defined $_[0] ? $_[0] : int($Absolute + $Relative);
143 12         475 return CORE::localtime($time);
144 8         1063 });
145             $Module{'CORE::GLOBAL'}->mock(gmtime => sub (;$) {
146 13 100   13   853 my $time = defined $_[0] ? $_[0] : int($Absolute + $Relative);
147 13         112 return CORE::gmtime($time);
148 8         699 });
149             $Module{'CORE::GLOBAL'}->mock(sleep => sub ($) {
150 16     16   173 my $dur = int $_[0];
151 16 100       65 croak 'sleep with negative value is not supported' if $dur < 0;
152 14         28 $Relative += $dur;
153 14         66 $Relative = sprintf '%.6f', $Relative;
154 14         41 return $dur;
155 8         649 });
156 8         606 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   111 $Monotonic = Time::HiRes::clock_gettime(CLOCK_MONOTONIC());
164              
165 8         356 $Module{'Time::HiRes'} = Test::MockModule->new('Time::HiRes');
166             $Module{'Time::HiRes'}->mock(time => sub () {
167 8     8   1102586 return 0+sprintf '%.6f', $Absolute + $Relative;
168 8         296 });
169             $Module{'Time::HiRes'}->mock(gettimeofday => sub () {
170 4     4   455 my $t = sprintf '%.6f', $Absolute + $Relative;
171 4 100       31 return wantarray ? (map {0+$_} split qr/[.]/ms, $t) : 0+$t;
  4         21  
172 8         768 });
173             $Module{'Time::HiRes'}->mock(clock_gettime => sub (;$) {
174 51     51   1008959 my ($which) = @_;
175 51 100       158 if ($which == CLOCK_REALTIME()) {
    100          
176 1         18 return 0+sprintf '%.6f', $Absolute + $Relative;
177             }
178             elsif ($which == CLOCK_MONOTONIC()) {
179 49         2158 return 0+sprintf '%.6f', $Monotonic + $Relative;
180             }
181 1         9 return TIME_HIRES_CLOCK_NOT_SUPPORTED;
182 8         713 });
183             $Module{'Time::HiRes'}->mock(clock_getres => sub (;$) {
184 3     3   1376 my ($which) = @_;
185 3 100 100     9 if ($which == CLOCK_REALTIME() || $which == CLOCK_MONOTONIC()) {
186 2         25 return $Module{'Time::HiRes'}->original('clock_getres')->(@_);
187             }
188 1         9 return TIME_HIRES_CLOCK_NOT_SUPPORTED;
189 8         660 });
190             $Module{'Time::HiRes'}->mock(sleep => sub (;@) {
191 3     3   125 my ($seconds) = @_;
192 3 100       16 croak 'sleep without arg is not supported' if !@_;
193 2 100       27 croak "Time::HiRes::sleep($seconds): negative time not invented yet" if $seconds < 0;
194 1         1 $Relative += $seconds;
195 1         7 $Relative = sprintf '%.6f', $Relative;
196 1         3 return $seconds;
197 8         667 });
198             $Module{'Time::HiRes'}->mock(usleep => sub ($) {
199 10     10   114 my ($useconds) = @_;
200 10 100       35 croak "Time::HiRes::usleep($useconds): negative time not invented yet" if $useconds < 0;
201 9         17 $Relative += $useconds / MICROSECONDS;
202 9         33 $Relative = sprintf '%.6f', $Relative;
203 9         16 return $useconds;
204 8         718 });
205             $Module{'Time::HiRes'}->mock(nanosleep => sub ($) {
206 2     2   47 my ($nanoseconds) = @_;
207 2 100       15 croak "Time::HiRes::nanosleep($nanoseconds): negative time not invented yet" if $nanoseconds < 0;
208 1         3 $Relative += $nanoseconds / NANOSECONDS;
209 1         7 $Relative = sprintf '%.6f', $Relative;
210 1         3 return $nanoseconds;
211 8         685 });
212             $Module{'Time::HiRes'}->mock(clock_nanosleep => sub ($$;$) {
213 5     5   140 my ($which, $nanoseconds, $flags) = @_;
214 5 100       20 croak "Time::HiRes::clock_nanosleep(..., $nanoseconds): negative time not invented yet" if $nanoseconds < 0;
215 4 100 100     11 croak 'only CLOCK_REALTIME and CLOCK_MONOTONIC are supported' if $which != CLOCK_REALTIME() && $which != CLOCK_MONOTONIC();
216 3 100       24 croak 'only flags=0 is supported' if $flags;
217 2         4 $Relative += $nanoseconds / NANOSECONDS;
218 2         10 $Relative = sprintf '%.6f', $Relative;
219 2         5 return $nanoseconds;
220 8         636 });
221 8         660 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   65 $Module{'EV'} = Test::MockModule->new('EV');
229 8         288 $Module{'EV::Watcher'} = Test::MockModule->new('EV::Watcher', no_auto=>1);
230 8         187 $Module{'EV::Timer'} = Test::MockModule->new('EV::Timer', no_auto=>1);
231 8         164 $Module{'EV::Periodic'} = Test::MockModule->new('EV::Periodic', no_auto=>1);
232             $Module{'EV'}->mock(time => sub () {
233 7     7   83 return 0+sprintf '%.6f', $Absolute + $Relative;
234 8         196 });
235             $Module{'EV'}->mock(now => sub () {
236 21     21   459 return 0+sprintf '%.6f', $Absolute + $Relative;
237 8         807 });
238             $Module{'EV'}->mock(sleep => sub ($) {
239 4     4   10 my ($seconds) = @_;
240 4 100       12 if ($seconds < 0) {
241 1         3 $seconds = 0;
242             }
243 4         7 $Relative += $seconds;
244 4         24 $Relative = sprintf '%.6f', $Relative;
245 4         6 return;
246 8         649 });
247             $Module{'EV'}->mock(run => sub (;$) {
248 23     23   422 my ($flags) = @_;
249 23         38 my $tick = 0;
250 23         40 my $w;
251 23 100       68 if (@Timers) {
252             $w = $Module{'EV'}->original('timer')->(
253             $WAIT_ONE_TICK, $WAIT_ONE_TICK, sub {
254 23     23   1088168 my $me = shift;
255 23         85 my $k;
256 23 100 66     194 if (!$tick++ || !$flags) {
257 15         160 $k = $me->keepalive(0);
258 15         65 ff();
259             }
260 23 100 33     251 if (!@Timers) {
    100 66        
261 7         35 $me->stop;
262             }
263             elsif ($k && ($flags || any {$_->{watcher} && $_->{watcher}->keepalive} @Timers)) {
264 8         45 $me->keepalive(1);
265             }
266             }
267 15         142 );
268 15 50 66 2   200 if (!($flags || any {$_->{watcher} && $_->{watcher}->keepalive} @Timers)) {
  2 50       35  
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     124 if ($flags && $flags == EV::RUN_ONCE()) {
281 18         60 $Module{'EV'}->original('run')->(@_);
282             }
283 23         210 return $Module{'EV'}->original('run')->(@_);
284 8         621 });
285             $Module{'EV'}->mock(timer => sub ($$$) {
286 11     11   168 my ($after, $repeat, $cb) = @_;
287 11         55 my $w = $Module{'EV'}->original('timer_ns')->(@_);
288 11         163 weaken(my $weakw = $w);
289 11 50   13   60 _add_timer('EV', $after, $repeat, sub { $weakw && $weakw->invoke(EV::TIMER()) }, $w);
  13         172  
290 11         43 return $w;
291 8         615 });
292             $Module{'EV'}->mock(timer_ns => sub ($$$) {
293 1     1   4 my ($after, $repeat, $cb) = @_;
294 1         4 my $w = EV::timer($after, $repeat, $cb);
295 1         4 _stop_timer($w);
296 1         3 return $w;
297 8         607 });
298             $Module{'EV'}->mock(periodic => sub ($$$$) {
299 2     2   9 my ($at, $repeat, $reschedule_cb, $cb) = @_;
300 2 50       7 croak 'reschedule_cb is not supported yet' if $reschedule_cb;
301 2 50       15 $at = sprintf '%.6f', $at < 0 ? 0 : $at;
302 2 50       12 $repeat = sprintf '%.6f', $repeat < 0 ? 0 : $repeat;
303 2         10 my $now = sprintf '%.6f', $Absolute + $Relative;
304 2 100 66     15 if ($repeat > 0 && $at < $now) {
305 8     8   22743 use bignum;
  8         52989  
  8         42  
306 1         63 $at += $repeat * int(($now - $at) / $repeat + 1);
307 1         2693 $at = sprintf '%.6f', $at;
308             }
309 2 50       113 my $after = $at > $now ? $at - $now : 0;
310 2         10 $after = sprintf '%.6f', $after;
311 2         10 my $w = $Module{'EV'}->original('periodic_ns')->(@_);
312 2         31 weaken(my $weakw = $w);
313 2 50   2   15 _add_timer('EV', $after, $repeat, sub { $weakw && $weakw->invoke(EV::TIMER()) }, $w);
  2         28  
314 2         5 return $w;
315 8         664 });
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         680 });
322             $Module{'EV::Watcher'}->mock(is_active => sub {
323 2     2   6 my ($w) = @_;
324 2 50       5 my ($active) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers;
  2         17  
325 2 50       3 my ($inactive) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers_ns;
  2         14  
326 2 100       8 if ($active) {
    50          
327 1         6 return 1;
328             }
329             elsif ($inactive) {
330 1         5 return;
331             }
332 0         0 return $Module{'EV::Watcher'}->original('is_active')->(@_);
333 8         652 });
334             $Module{'EV::Timer'}->mock(DESTROY => sub {
335 26     26   440 my ($w) = @_;
336 26 50       58 @Timers = grep { !$_->{watcher} || $_->{watcher} ne $w } @Timers;
  18         264  
337 26 50       57 @Timers_ns = grep { !$_->{watcher} || $_->{watcher} ne $w } @Timers_ns;
  11         87  
338 26         150 return $Module{'EV::Timer'}->original('DESTROY')->(@_);
339 8         672 });
340             $Module{'EV::Timer'}->mock(start => sub {
341 1     1   4 return _start_timer(@_);
342 8         643 });
343             $Module{'EV::Timer'}->mock(stop => sub {
344 9     9   35 return _stop_timer(@_);
345 8         615 });
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         600 });
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         614 });
367             $Module{'EV::Timer'}->mock(again => sub {
368 3     3   45 my ($w, $repeat) = @_;
369 3 50 33     18 if (defined $repeat && $repeat < 0) {
370 0         0 $repeat = 0;
371             }
372 3 50       27 my ($active) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers;
  2         18  
373 3 50       8 my ($inactive) = grep { $_->{watcher} && $_->{watcher} eq $w } @Timers_ns;
  2         12  
374 3 100       9 if ($active) {
    50          
375 2 50       22 $active->{repeat} = sprintf '%.6f', defined $repeat ? $repeat : $active->{repeat};
376 2 50       9 if ($active->{repeat} > 0) {
377 2         5 $active->{after} = $active->{repeat};
378 2         5 $active->{start} = $Relative;
379             }
380             else {
381 0         0 _stop_timer($active->{watcher});
382             }
383             }
384             elsif ($inactive) {
385 1 50       12 $inactive->{repeat} = sprintf '%.6f', defined $repeat ? $repeat : $inactive->{repeat};
386 1 50       6 if ($inactive->{repeat} > 0) {
387 1         3 $inactive->{after} = $inactive->{repeat};
388 1         3 $inactive->{start} = $Relative;
389 1         4 _start_timer($inactive->{watcher});
390             }
391             }
392 3         8 return;
393 8         652 });
394             $Module{'EV::Periodic'}->mock(DESTROY => sub {
395 2     2   755 my ($w) = @_;
396 2 50       5 @Timers = grep { !$_->{watcher} || $_->{watcher} ne $w } @Timers;
  1         16  
397 2 50       5 @Timers_ns = grep { !$_->{watcher} || $_->{watcher} ne $w } @Timers_ns;
  2         16  
398 2         7 return $Module{'EV::Periodic'}->original('DESTROY')->(@_);
399 8         749 });
400             $Module{'EV::Periodic'}->mock(start => sub {
401 0     0   0 return _start_timer(@_);
402 8         754 });
403             $Module{'EV::Periodic'}->mock(stop => sub {
404 0     0   0 return _stop_timer(@_);
405 8         674 });
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   558102 use bigint;
  8         19  
  8         67  
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         972 });
427             $Module{'EV::Periodic'}->mock(again => sub {
428 0     0   0 return _start_timer(@_);
429 8         700 });
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         668 });
438 8         621 return;
439             }
440              
441             sub _mock_mojolicious {
442 8     8   87 $Module{'Mojo::Reactor::Poll'} = Test::MockModule->new('Mojo::Reactor::Poll');
443             $Module{'Mojo::Reactor::Poll'}->mock(one_tick => sub {
444 10     10   222 my ($self) = @_;
445 10 100       24 if (!@Timers) {
446 2         9 return $Module{'Mojo::Reactor::Poll'}->original('one_tick')->(@_);
447             }
448             my $id = $Module{'Mojo::Reactor::Poll'}->original('timer')->(
449 8     8   71 $self, $WAIT_ONE_TICK, sub { ff() }
450 8         43 );
451 8         38 $Module{'Mojo::Reactor::Poll'}->original('one_tick')->(@_);
452 8         63 $Module{'Mojo::Reactor::Poll'}->original('remove')->($self, $id);
453 8         80 return;
454 8         564 });
455             $Module{'Mojo::Reactor::Poll'}->mock(timer => sub {
456 5     5   853 my ($self, $delay, $cb) = @_;
457 5 50       11 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   22 return _add_timer($self, $delay, 0, sub { $cb->($self) });
  4         10  
461 8         1076 });
462             $Module{'Mojo::Reactor::Poll'}->mock(recurring => sub {
463 2     2   39 my ($self, $delay, $cb) = @_;
464 2     4   11 return _add_timer($self, $delay, $delay, sub { $cb->($self) });
  4         8  
465 8         686 });
466             $Module{'Mojo::Reactor::Poll'}->mock(again => sub {
467 2     2   18 my ($self, $id) = @_;
468 2 50       12 if ($id !~ /\Afake_\d+\z/ms) {
469 0         0 $Module{'Mojo::Reactor::Poll'}->original('again')->(@_);
470             }
471             else {
472 2         6 my ($timer) = grep { $_->{id} eq $id } @Timers;
  2         7  
473 2 50       6 if ($timer) {
474 2         5 $timer->{start} = $Relative;
475             }
476             }
477 2         4 return;
478 8         667 });
479             $Module{'Mojo::Reactor::Poll'}->mock(remove => sub {
480 12     12   191 my ($self, $id) = @_;
481 12 100       41 if ($id !~ /\Afake_\d+\z/ms) {
482 10         24 $Module{'Mojo::Reactor::Poll'}->original('remove')->(@_);
483             }
484             else {
485 2 50       3 @Timers = grep { $_->{loop} ne $self || $_->{id} ne $id } @Timers;
  2         24  
486             }
487 12         120 return;
488 8         641 });
489             $Module{'Mojo::Reactor::Poll'}->mock(reset => sub {
490 2     2   118 my ($self) = @_;
491 2         8 @Timers = grep { $_->{loop} ne $self } @Timers;
  2         14  
492 2         18 return $Module{'Mojo::Reactor::Poll'}->original('reset')->(@_);
493 8         708 });
494 8         597 return;
495             }
496              
497              
498             1;
499             __END__