File Coverage

blib/lib/Test/Effects.pm
Criterion Covered Total %
statement 245 307 79.8
branch 103 170 60.5
condition 26 68 38.2
subroutine 38 43 88.3
pod 4 5 80.0
total 416 593 70.1


line stmt bran cond sub pod time code
1             package Test::Effects;
2              
3 22     22   80416 use warnings;
  22         161  
  22         785  
4 22     22   122 use strict;
  22         59  
  22         439  
5 22     22   512 use 5.014;
  22         90  
6              
7             our $VERSION = '0.002000';
8              
9 22     22   13352 use Test::More;
  22         1410435  
  22         180  
10 22     22   17159 use Test::Trap;
  22         1041532  
  22         109  
11 22     22   2836 use base 'Test::Builder::Module';
  22         79  
  22         3971  
12              
13 22     22   10335 use match::smart 'match';
  22         126796  
  22         213  
14              
15             # Export the modules interface (and that of Test::More)...
16             our @EXPORT = (
17             qw( effects_ok ),
18             qw( ONLY VERBOSE TIME ),
19             @Test::More::EXPORT,
20             );
21              
22             our @EXPORT_OK = (
23             @Test::More::Export_OK,
24             );
25              
26             our %EXPORT_TAGS = (
27             'minimal' => [ 'effects_ok' ],
28             'more' => [ 'effects_ok', @Test::More::EXPORT],
29             );
30              
31             # Magic number tells Test::More how many stack levels to go up when reporting errors
32             # (Unfortunately, this depends on the internals of Test::More)
33             # [TODO: Send a patch for Test::More that autoskips a named class when reporting]
34             my $LEVEL_OFFSET = 6;
35             my $LEVEL_OFFSET_NESTED = 2 * $LEVEL_OFFSET + 1;
36              
37              
38             # Adjust tests used in the module to account for nesting...
39             sub _subtest {
40 44     44   167 my ($desc) = @_;
41              
42             # Report problems as being in the appropriate place...
43 44         122 local $Test::Builder::Level = $Test::Builder::Level + $LEVEL_OFFSET;
44              
45 44         220 &subtest(@_);
46             }
47              
48             sub _fail {
49             # Report problems as being in the appropriate place...
50 0     0   0 local $Test::Builder::Level = $Test::Builder::Level + $LEVEL_OFFSET_NESTED;
51              
52 0         0 &fail(@_);
53             }
54              
55 22     22   7825 use Scalar::Util 'looks_like_number';
  22         55  
  22         2952  
56              
57 7     7 0 115 sub is_num { Test::Effects->builder->is_num(@_) }
58              
59             sub _is_or_like {
60 186     186   1131 my ($got, $expected, $desc) = @_;
61              
62             # Report problems as being in the appropriate place...
63 186         398 local $Test::Builder::Level = $Test::Builder::Level + $LEVEL_OFFSET_NESTED;
64              
65 186         462 for my $expected_type (ref $expected) {
66 186 50       891 if ($expected_type eq 'CODE') {
    100          
    50          
67 22     22   204 no warnings;
  22         60  
  22         5315  
68 0         0 my $ok = \&Test::Builder::ok;
69 0 0   0   0 local *Test::Builder::ok = sub { $_[2] = $desc unless defined $_[2]; $ok->(@_); };
  0         0  
  0         0  
70 0         0 ok($expected->($got, $desc), $desc);
71             }
72 72         238 elsif ($expected_type eq 'Regexp') { &like(@_); }
73 0         0 elsif (looks_like_number($expected)) { &is_num(@_); }
74 114         433 else { &is(@_); }
75             }
76             }
77              
78             sub _is_deeply {
79 4     4   20 my ($got, $expected, $desc) = @_;
80              
81             # Report problems as being in the appropriate place...
82 4         42 local $Test::Builder::Level = $Test::Builder::Level + $LEVEL_OFFSET_NESTED;
83              
84 4         24 for my $expected_type (ref $expected) {
85 4 50       22 if ($expected_type eq 'CODE') {
86 22     22   209 no warnings;
  22         52  
  22         4289  
87 0         0 my $ok = \&Test::Builder::ok;
88 0 0   0   0 local *Test::Builder::ok = sub { $_[2] = $desc unless defined $_[2]; $ok->(@_); };
  0         0  
  0         0  
89 0         0 ok($expected->($got, $desc), $desc);
90             }
91             else {
92 4         31 &is_deeply(@_);
93             }
94             }
95             }
96              
97             sub _is_like_or_deeply {
98 12     12   56 my ($got, $expected, $desc) = @_;
99              
100             # Report problems as being in the appropriate place...
101 12         73 local $Test::Builder::Level = $Test::Builder::Level + $LEVEL_OFFSET_NESTED;
102              
103 12         53 for my $expected_type (ref $expected) {
104 12 100       107 if ($expected_type eq 'CODE') {
    50          
    50          
105 22     22   216 no warnings;
  22         47  
  22         6079  
106 4         15 my $ok = \&Test::Builder::ok;
107 4 50   4   30 local *Test::Builder::ok = sub { $_[2] = $desc unless defined $_[2]; $ok->(@_); };
  4         127  
  4         18  
108 4         19 ok($expected->($got, $desc), $desc);
109             }
110             elsif ($expected_type eq 'Regexp') {
111 0 0 0     0 my $got_val = ref($got) eq 'ARRAY' && @{$got} == 1 ? $got->[0] : $got;
112 0         0 like($got_val, $expected, $desc);
113             }
114             elsif ($expected_type eq q{}) {
115 8 100       61 if (looks_like_number($expected)) { &is_num(@_) }
  7         42  
116 1         5 else { &is(@_) }
117             }
118             else {
119 0         0 &is_deeply(@_);
120             }
121             }
122             }
123              
124             sub _is_like_or_list {
125 42     42   430 my ($got, $expected, $desc) = @_;
126              
127             # Report problems as being in the appropriate place...
128 42         118 local $Test::Builder::Level = $Test::Builder::Level + $LEVEL_OFFSET_NESTED;
129              
130 42         157 for my $expected_type (ref $expected) {
131 42 50 33     433 if ($expected_type eq 'CODE') {
    50          
    50          
    50          
    50          
132 22     22   223 no warnings;
  22         52  
  22         6623  
133 0         0 my $ok = \&Test::Builder::ok;
134 0 0   0   0 local *Test::Builder::ok = sub { $_[2] = $desc unless defined $_[2]; $ok->(@_); };
  0         0  
  0         0  
135 0         0 ok($expected->($got, $desc), $desc);
136             }
137             elsif ($expected_type eq 'Regexp') {
138 0 0 0     0 my $got_val = ref($got) eq 'ARRAY' && @{$got} == 1 ? $got->[0] : $got;
139 0         0 like($got_val, $expected, $desc);
140             }
141             elsif ($expected_type eq q{} && looks_like_number($expected)) {
142 0         0 &is_num(@_);
143             }
144             elsif ($expected_type eq q{}) {
145 0         0 &is(@_);
146             }
147             elsif ($expected_type eq q{ARRAY}) {
148 42         96 for my $n (0..$#{$expected}) {
  42         205  
149 22         146 _is_or_like($got->[$n], $expected->[$n], "$desc [warning $n]");
150             }
151             }
152             else {
153 0         0 &is_deeply(@_);
154             }
155             }
156             }
157              
158              
159             # Utility sub: dump values...
160              
161             sub _explain {
162 22     22   14411 use Data::Dumper 'Dumper';
  22         155267  
  22         9420  
163 0     0   0 local $Data::Dumper::Terse = 1;
164 0         0 local $Data::Dumper::Indent = 0;
165 0         0 return Dumper(shift) =~ s/\s+/ /gr
166             =~ s/\s+$//gr;
167             }
168              
169              
170             # Utility subs: build subs that append the right verb, when requested....
171              
172             sub _was_were_sub {
173 88     88   227 my ($desc) = @_;
174              
175             return sub {
176 164     164   6075 my (undef, $was_were) = @_;
177              
178 164 100       659 return $desc if !$was_were;
179 71         355 return "$desc was";
180 88         35993 };
181             }
182              
183             sub _was_were_warn_sub {
184             return sub {
185 42     42   1556 my ($expected, $was_were) = @_;
186              
187 42   50     109 my $count = eval{ @{$expected} } // 1;
  42         99  
  42         202  
188              
189 42 50       299 return $count == 1 ? 'warning' . ($was_were ? ' was' : q{})
    50          
    100          
190             : 'warnings' . ($was_were ? ' were' : q{});
191             }
192 22     22   163 }
193              
194              
195             # Utility sub: load carp() and croak() only on demand...
196             sub _croak {
197 4 50   4   10 if (eval{ require Carp }) { Carp::croak(@_); }
  4         41  
  4         851  
198 0         0 else { die @_; }
199             }
200              
201              
202             # Module is largely table-driven (from these tables)...
203              
204             my (%TEST_FOR, %NULL_VALUE_FOR, %BAD_NULL_VALUE_FOR, %DESCRIBE);
205              
206             BEGIN {
207 22     22   179 %TEST_FOR = (
208             'stdout' => \&_is_or_like,
209             'stderr' => \&_is_or_like,
210             'warn' => \&_is_like_or_list,
211             'die' => \&_is_or_like,
212             'exit' => \&_is_or_like,
213             );
214              
215 22         144 %NULL_VALUE_FOR = (
216             'stdout' => q{},
217             'stderr' => q{},
218             'warn' => [],
219             'die' => undef,
220             'exit' => undef,
221             );
222              
223 22         106 %BAD_NULL_VALUE_FOR = (
224             'stdout' => undef,
225             'stderr' => undef,
226             'warn' => undef,
227             'die' => q{},
228             'exit' => q{},
229             );
230              
231 22         73 %DESCRIBE = (
232             'stdout' => _was_were_sub( 'output to STDOUT' ),
233             'stderr' => _was_were_sub( 'output to STDERR' ),
234             'warn' => _was_were_warn_sub(),
235             'die' => _was_were_sub( 'exception' ),
236             'exit' => _was_were_sub( 'call to exit()' ),
237             );
238             }
239              
240              
241             # Make a copy of a hash with an appropriate flag added...
242 5     5 1 3210 sub ONLY (+) { return { ONLY => 1, %{shift()} } }
  5         33  
243 18     18 1 3247 sub VERBOSE (+) { return { VERBOSE => 1, %{shift()} } }
  18         174  
244 9     9 1 2944 sub TIME (+) { return { TIME => 1, %{shift()} } }
  9         81  
245              
246             my $MS_THRESHHOLD = 0.1; # seconds (anything less reported in ms)
247              
248             # Test all trapped info, as requested...
249             sub effects_ok (&;+$) {
250 46     46 1 10182 my ($block, $expected, $desc) = @_;
251 46         139 my $expected_ref = ref $expected;
252              
253             # Handle case where hash is missing, but description isn't...
254 46 50 33     230 if (@_ == 2 && !$expected_ref) {
255 0         0 $desc = "$expected";
256 0         0 $expected = undef;
257             }
258              
259             # Expectations are passed in a hash...
260 46   50     134 $expected //= {};
261 46 100       173 if (ref($expected) ne 'HASH') {
262 1   50     9 _croak 'Second argument to effects_ok() must be hash or hash reference, not '
263             . lc(ref($expected) || 'scalar value');
264             }
265              
266             # If there's a timing request, the value has to make sense...
267 45         88 my $timing;
268 45 100       195 if (exists $expected->{'timing'}) {
269 1         3 my $spec = $expected->{'timing'};
270 1   33     13 my $valid_time = ref($spec) =~ m{ \A (?: HASH | ARRAY ) \Z}xms
271             || !ref($spec) && looks_like_number($spec);
272 1 50       3 if (!$valid_time) {
273 1         4 _croak("Invalid timing specification: timing => '$spec'");
274             }
275             }
276              
277             # Get lexical hints...
278 44   100     94 my %lexical_hint = %{ (caller 0)[10] // {} };
  44         521  
279              
280             # Fill in default tests, unless requested not to...
281             my $is_only
282             = exists $expected->{'ONLY'} ? $expected->{'ONLY'}
283 44 100       215 : $lexical_hint{'Test::Effects::ONLY'};
284              
285             # Time the test, if requested
286             my $timed_test
287             = exists $expected->{'TIME'} ? $expected->{'TIME'}
288 44 100       152 : $lexical_hint{'Test::Effects::TIME'};
289              
290 44 100       128 if (!$is_only) {
291 37         78 my $warn = $expected->{'warn'};
292             $expected = {
293             %NULL_VALUE_FOR,
294 17         58 'stderr' => (ref $warn eq 'ARRAY' ? join(q{}, @{$warn}) : $warn),
295 37 100       188 %{$expected},
  37         233  
296             };
297             }
298              
299             # Correct common mispecifications...
300 44         223 for my $option (keys %BAD_NULL_VALUE_FOR) {
301 220 100       3545 next if !exists $expected->{$option};
302 206 100       532 if (match( $expected->{$option}, $BAD_NULL_VALUE_FOR{$option} )) {
303 20         172 $expected->{$option} = $NULL_VALUE_FOR{$option};
304             }
305             }
306              
307             # Ensure there's a description...
308 44   33     424 $desc //= sprintf "Testing effects_ok() at %s line %d", (caller)[1,2];
309              
310             # Are we echoing this test???
311             my $is_terse
312             = exists $expected->{'VERBOSE'} ? !$expected->{'VERBOSE'}
313 44 100       169 : !$lexical_hint{'Test::Effects::VERBOSE'};
314              
315             # Show the description...
316 44   66     200 my $preview_desc = !$is_terse || exists $expected->{'timing'};
317 44 100       180 if ($preview_desc) {
318 25         170 note '_' x (3 + length $desc);
319 25 50       9357 note exists $expected->{'timing'}
320             ? "$desc (being timed)..."
321             : "$desc...";
322             }
323              
324             # Redirect test output, so we can retrospectively de-terse on errors...
325 44         7986 my $tests_output;
326 44         207 for my $builder (Test::Builder->new()) {
327 44         427 $builder->output(\$tests_output);
328 44         8780 $builder->failure_output(\$tests_output);
329 44         8084 $builder->todo_output(\$tests_output);
330             }
331              
332             # Preview description under terse too, in case of failures...
333 44 100       7372 if (!$preview_desc) {
334 19         150 note '_' x (3 + length $desc);
335 19         5960 note "$desc...";
336             }
337              
338             # Are we WITHOUT any modules in this test???
339 44         5022 my @real_INC = @INC;
340 44         200 local @INC = @INC;
341 44         5778 local %INC = %INC;
342 44 100       434 if (exists $expected->{'WITHOUT'}) {
343 4         13 my $without_list = $expected->{'WITHOUT'};
344              
345             # Normalize list...
346 4 50       21 if (ref $without_list ne 'ARRAY') {
347 4         10 $without_list = [ $without_list ];
348             }
349              
350             # Translate list to filepaths...
351 4         12 for my $module_name ( @{$without_list} ) {
  4         11  
352             # Classify the argument...
353 4         10 my $is_pattern = ref $module_name eq 'Regexp';
354 4         18 my $is_libpath = $module_name =~ m{/};
355              
356             # Modules get translated to paths...
357 4 100 66     20 if (!$is_libpath) {
    100          
358 2 50       26 if (not $module_name =~ s{::}{/}gxms) {
359 0 0       0 diag "WARNING: ambiguous WITHOUT => "
    0          
    0          
360             . ($is_pattern ? "qr{$module_name}" : "'$module_name'")
361             . "\ntreated as module name (not library path)"
362             . "\n(use "
363             . ($is_pattern ? "qr{::$module_name}" : "'::$module_name'")
364             . " or "
365             . ($is_pattern ? "qr{$module_name/}" : "'$module_name/'")
366             . " to silence this warning)";
367             }
368 2 100       7 if (!$is_pattern) {
369 1         3 $module_name .= '.pm';
370             }
371             else {
372 1         18 $module_name = qr{$module_name};
373             }
374             }
375             # Libpaths winnow @INC directly...
376             elsif (!$is_pattern && $is_libpath) {
377 1         7 $module_name =~ s{/\Z}{}xms;
378 1 50       4 if ($module_name =~ m{\A /}x) {
379 0         0 @INC = grep { !m{\A $module_name /? \Z}x } @INC;
  0         0  
380             }
381             else {
382 1         3 @INC = grep { !m{\A (?: [.]/ )? $module_name /? \Z}x } @INC;
  10         63  
383             }
384             }
385             else { # Pattern spec for libpath
386 1         3 @INC = grep { !m{$module_name} } @INC;
  11         44  
387             }
388              
389             # Libpaths then don't need to be checked with @INC...
390 4 100       26 if ($is_libpath) {
391 2         6 $module_name = undef;
392             }
393             }
394              
395             # Put an interceptor sub at the start of @INC...
396             unshift @INC, sub {
397 4     4   69 my ($self, $target) = @_;
398              
399             # If what you're looking for is WITHOUT'd, pretend to fail...
400 4 100 66     26 if (match($target, $without_list) || match("/$target", $without_list)) {
401 2         261 _croak "Can't locate $target in \@INC (\@INC contains: @real_INC)";
402             }
403 2         417 return;
404 4         35 };
405             }
406              
407             # Test in a subtest...
408             my $failed = _subtest $desc => sub {
409             # Find the specified return value (if any)...
410 44     44   37668 my @return_specs = grep /return/, keys %{$expected};
  44         416  
411 44 50       221 if (@return_specs > 1) {
412 0         0 _fail "Ambiguous specification for testing of return value.";
413             diag "ERROR: Found request for " . scalar(@return_specs),
414             " mutually exclusive tests:\n",
415             " {\n",
416 0         0 (map { " '$_' => " . _explain($expected->{$_}) . ",\n" } @return_specs),
  0         0  
417             " }\n",
418             " Call to effects_ok() terminated without testing anything.";
419 0         0 return;
420             }
421              
422             # Infer context, if necessary...
423 44 100       178 if (exists $expected->{'return'}) {
424 1         3 for my $return_type (ref $expected->{'return'}) {
425 1 50       3 if ($return_type eq 'ARRAY') {
426 0         0 $expected->{'list_return'} = delete $expected->{'return'};
427             }
428             else {
429 1         4 $expected->{'scalar_return'} = delete $expected->{'return'};
430             }
431             }
432             }
433              
434             # Call the block and test the return value in the appropriate context...
435             # 1. Explicit void context...
436 44 100       361 if (exists $expected->{'void_return'}) {
    100          
    100          
437 3 50       7 if (defined $expected->{'void_return'}) {
438 0         0 note "WARNING: Meaningless option {void_return => '$expected->{void_return}'} ignored.\n"
439             . " To silence this warning, either remove the option entirely\n"
440             . " or replace it with: {void_return => undef})";
441             }
442 3 50       9 if ($timed_test) {
443 3         11 _time_calls_to($block, $timed_test => $timing);
444             }
445             else {
446 0         0 trap { $block->() };
  0         0  
447             }
448 3         35 pass 'Tested in void context, so ignored return value'
449             }
450             # 2. Explicit scalar context...
451             elsif (exists $expected->{'scalar_return'}) {
452 12         25 my $return_val = do {
453 12 100       43 if ($timed_test) {
454 7         20 _time_calls_to($block, $timed_test => $timing);
455             }
456             else {
457 5         64 trap { $block->() };
  5         8243  
458             }
459             };
460 12         2058 _is_like_or_deeply $return_val, $expected->{'scalar_return'}
461             => 'Scalar context return value was as expected';
462             }
463             # 3. Explicit list context...
464             elsif (exists $expected->{'list_return'}) {
465 4         7 my @return_vals = do {
466 4 50       11 if ($timed_test) {
467 4         16 _time_calls_to($block, $timed_test => $timing);
468             }
469             else {
470 0         0 trap { $block->() };
  0         0  
471             }
472             };
473 4         31 _is_deeply \@return_vals, $expected->{'list_return'}
474             => 'List context return value was as expected';
475             }
476             # 4. Implied void context...
477             else {
478 25 100       127 if ($timed_test) {
479 3         21 _time_calls_to($block, $timed_test => $timing);
480             }
481             else {
482 22         205 trap { $block->() };
  22         35119  
483             }
484 25         3037588 pass 'No return value specified, so tested in void context';
485             }
486              
487             # Test side-effects...
488 44         27871 for my $info (qw< stdout stderr warn die exit>) {
489 220 100       95631 if (exists $expected->{$info}) {
490 22     22   247 no strict 'refs';
  22         81  
  22         16612  
491             my $desc = match($expected->{$info}, $NULL_VALUE_FOR{$info})
492             ? 'No ' . $DESCRIBE{$info}->($expected->{$info}) . ' (as expected)'
493 206 100       904 : ucfirst $DESCRIBE{$info}->($expected->{$info},'was') . ' as expected';
494              
495 206         1219 $TEST_FOR{$info}->($trap->$info, $expected->{$info}, $desc);
496             }
497             }
498              
499             # Do timing, if requested...
500              
501 44 50       16082 if (exists $expected->{'timing'}) {
502              
503             # Work out the parameters...
504 0         0 my $time_spec = $expected->{'timing'};
505 0         0 my $spec_type = ref $time_spec;
506              
507 0 0 0     0 my $min = $spec_type eq 'HASH' ? $time_spec->{'min'} // 0
    0 0        
508             : $spec_type eq 'ARRAY' ? $time_spec->[0] // 0
509             : 0;
510              
511 0         0 state $INF = 0 + 'inf';
512 0 0 0     0 my $max = $spec_type eq 'HASH' ? $time_spec->{'max'} // $INF
    0 0        
513             : $spec_type eq 'ARRAY' ? $time_spec->[-1] // $INF
514             : 0+$time_spec;
515              
516             # Run the test...
517 0         0 my $duration = _time_calls_to($block);
518              
519             # Compute a handy alternate measure of performance...
520 0 0       0 my $speed = $duration ? 1/$duration : 0;
521 0 0       0 $speed = $speed > 1 ? sprintf('(%1.0lf/sec)', $speed)
    0          
522             : $speed > 0 ? sprintf('(%0.4g/sec)', $speed)
523             : '(unmeasurably fast)';
524              
525             # Was the result acceptable???
526 0   0     0 my $in_range = $min <= $duration && $duration <= $max;
527              
528             # Report outcome...
529 0 0       0 ok $in_range => $duration > $MS_THRESHHOLD
530             ? sprintf('Ran in %0.3f sec %s', $duration, $speed)
531             : sprintf('Ran in %dms %s', 1000*$duration, $speed);
532              
533             # Clean up report...
534 0 0       0 if (!$in_range) {
535 0         0 $tests_output =~ s{\N*\n\N*\n\z}{}xms;
536             }
537              
538             # Report any problems (as being in the appropriate place)...
539 0         0 local $Test::Builder::Level = $Test::Builder::Level + $LEVEL_OFFSET_NESTED - 1;
540 0 0       0 if (!$in_range) {
541 0         0 diag sprintf " Expected to run in: $min to $max sec", $min, $max;
542             }
543             }
544              
545 44         544 };
546              
547             # Clean up...
548 44         62461 my $builder = Test::Builder->new;
549 44         652 $builder->reset_outputs;
550              
551             # Report outcomes...
552 44         6763 my $passed = ($builder->summary)[-1];
553             # If passed in terse mode, just print the summary (i.e. last line)...
554 44 100 66     6605 if ( $is_terse && $passed ) {
    50          
555 19         228 $tests_output =~ s{ .* \n (?= .*\n )}{}xms;
556             }
557             # Otherwise print the probems...
558             elsif ( $is_terse ) {
559 0         0 $tests_output =~ s{^ \s*+ (?! not | [#] ) [^\n]* \n}{}gxms;
560             }
561              
562             # Add the timing info, if requested...
563 44 100       244 if ($timed_test) {
564 17         258 $tests_output =~ s{^ ( (?:not|ok) \s \N*) }{$1\t$timing}xms;
565             }
566              
567 44         122 print {$builder->output} $tests_output;
  44         266  
568              
569 44         15773 return $passed;
570             }
571              
572              
573 22 50   22   128 BEGIN { eval{ require Time::HiRes and Time::HiRes->import('time') } }
  22         12169  
574              
575             sub _time_calls_to {
576 17     17   63 my ($block, $time_one_call) = @_;
577              
578 17         37 state $MAX_CPU_TIME = 1;
579 17         44 state $MIN_CREDIBLE_UTILIZATION = 0.1;
580              
581 17         68 my ($cpu_time, $wall_time) = (0,0);
582 17         35 my $count = 0;
583              
584 17         46 my (@start, @end);
585 17         34 my $wantarray = wantarray;
586 17         36 my (@result, $result);
587              
588 17         33 while (1) {
589 17 100 66     144 if ($time_one_call && $wantarray) {
    100 66        
590 4         50 @start = (time, times);
591 4     4   51 @result = trap { $block->() };
  4         6208  
592 4         1004165 @end = (time, times);
593             }
594             elsif ($time_one_call && defined $wantarray) {
595 7         102 @start = (time, times);
596 7     7   89 $result = trap { $block->() };
  7         11135  
597 7         1757893 @end = (time, times);
598             }
599             else {
600 6         124 @start = (time, times);
601 6     6   83 trap { $block->() };
  6         10148  
602 6         2756049 @end = (time, times);
603             }
604              
605 17         140 $wall_time += $end[0] - $start[0];
606 17         201 $cpu_time += $end[$_] - $start[$_] for 1..4;
607              
608 17         92 $count++;
609              
610 17 50 33     336 last if $cpu_time > $MAX_CPU_TIME
      33        
611             || $wall_time > 2 * $MAX_CPU_TIME
612             || $time_one_call;
613             }
614              
615 17   50     109 my $utilization = $cpu_time / ($wall_time||1);
616 17 50 66     145 my $timing = !$cpu_time || $utilization < $MIN_CREDIBLE_UTILIZATION
617             ? $wall_time / $count
618             : $cpu_time / $count;
619              
620 17 50       72 if ($time_one_call) {
621 17 100       133 if ($timing < $MS_THRESHHOLD) {
622 1         5 $_[2] = '[' . int($timing * 1000) . 'ms]';
623             }
624             else {
625 16         360 $_[2] = sprintf '[%0.2lf sec]', $timing;
626             }
627 17 100       145 return $wantarray ? @result : $result;
628             }
629             else {
630 0           return $timing;
631             }
632             }
633              
634              
635             1; # Magic true value required at end of module
636             __END__