File Coverage

blib/lib/Test/Modern.pm
Criterion Covered Total %
statement 200 277 72.2
branch 52 116 44.8
condition 9 25 36.0
subroutine 37 44 84.0
pod 5 5 100.0
total 303 467 64.8


line stmt bran cond sub pod time code
1 16     16   34784 use 5.006001;
  16         50  
  16         601  
2 16     16   75 use strict;
  16         27  
  16         616  
3 16     16   80 use warnings;
  16         126  
  16         1409  
4              
5             package Test::Modern;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.011';
9              
10             our $VERBOSE;
11              
12 16     16   87 use Cwd 0 qw();
  16         304  
  16         423  
13 16     16   15375 use Exporter::Tiny 0.030 qw();
  16         53895  
  16         414  
14 16     16   127 use File::Spec 0 qw();
  16         301  
  16         336  
15 16     16   16110 use IO::File 1.08 qw();
  16         199702  
  16         510  
16 16     16   141 use IO::Handle 1.21 qw();
  16         302  
  16         331  
17 16     16   14460 use Import::Into 1.002 qw();
  16         56624  
  16         543  
18 16     16   372 use Module::Runtime 0.012 qw( require_module module_notional_filename );
  16         272  
  16         91  
19 16     16   17175 use Test::More 0.96;
  16         317388  
  16         166  
20 16     16   20034 use Test::API 0.004;
  16         41533  
  16         152  
21 16     16   17946 use Test::Fatal 0.007;
  16         58200  
  16         1114  
22 16 50   16   13777 use Test::Warnings 0.009 qw( warning warnings ), ($ENV{PERL_TEST_MODERN_ALLOW_WARNINGS} ? ':no_end_test' : ());
  16         34054  
  16         160  
23 16     16   19411 use Test::Deep 0.111 qw( :v1 );
  16         218911  
  16         6758  
24 16     16   165 use Try::Tiny 0.15 qw( try catch );
  16         304  
  16         23205  
25              
26             my %HINTS;
27              
28             $HINTS{ extended } = sub
29             {
30             return if $ENV{EXTENDED_TESTING};
31             plan skip_all => 'Not running extended tests';
32             };
33              
34             $HINTS{ author } = sub
35             {
36             return if $ENV{AUTHOR_TESTING};
37             plan skip_all => 'Not running author tests';
38             };
39              
40             $HINTS{ release } = sub
41             {
42             return if $ENV{RELEASE_TESTING};
43             plan skip_all => 'Not running release tests';
44             };
45              
46             $HINTS{ interactive } = sub
47             {
48             return unless $ENV{AUTOMATED_TESTING};
49             plan skip_all => 'Not running interactive tests';
50             };
51              
52             $HINTS{ requires } = sub
53             {
54             my %requires = %{ $_[2] };
55             for my $module (sort keys %requires)
56             {
57             if ($module eq 'perl')
58             {
59             next if !defined($requires{$module});
60             next if $] >= $requires{$module};
61             return plan skip_all => sprintf(
62             "Test requires Perl %s",
63             $requires{$module},
64             );
65             }
66            
67             try {
68             &require_module(
69             $module,
70             defined($requires{$module}) ? $requires{$module} : (),
71             );
72             }
73             catch {
74             plan skip_all => sprintf(
75             "Test requires %s%s",
76             $module,
77             defined($requires{$module}) ? " $requires{$module}" : "",
78             );
79             }
80             }
81             return;
82             };
83              
84             $HINTS{ lib } = sub { $_[3]{lib}++; () };
85              
86             {
87             my ($installed, %hide) = 0;
88            
89             # This implementation stolen from Devel::Hide. Keep the
90             # Perl-5.6 compatible implementation, because one day it
91             # might be nice if this module could support Perl 5.6.
92             my $_scalar_as_io = ($] >= 5.008)
93             ? sub {
94 1     1   14 open(my($io), '<', \$_[0])
  1         2  
  1         11  
95             or die("Cannot open scalarref for IO?!");
96             return $io;
97             }
98             : sub {
99             my $scalar = shift;
100             require File::Temp;
101             my $io = File::Temp::tempfile();
102             print {$io} $scalar;
103             seek $io, 0, 0; # rewind the handle
104             return $io;
105             };
106            
107             my $inc = sub
108             {
109             my (undef, $file) = @_;
110             if ($hide{$file})
111             {
112             my $oops = sprintf(
113             qq{die "Can't locate %s (hidden by %s)\\n";},
114             $file,
115             __PACKAGE__,
116             );
117             return $_scalar_as_io->($oops);
118             }
119             return;
120             };
121            
122             $HINTS{ without } = sub
123             {
124             unless ($installed)
125             {
126             unshift(@INC, $inc);
127             ++$installed;
128             }
129            
130             my @without = @{ $_[2] };
131             for my $module (@without)
132             {
133             my $file = module_notional_filename($module);
134             exists($INC{$file})
135             ? plan(skip_all => sprintf("cannot prevent $module from loading (it is already loaded)"))
136             : ++$hide{$file};
137             }
138             return;
139             };
140             }
141              
142             {
143             my $checker;
144             $checker = sub {
145             $checker = eval q{
146             package #
147             Dummy::Test::RequiresInternet;
148             use Socket;
149             sub {
150             my ($host, $port) = @_;
151             my $portnum = ($port =~ /[^0-9]/) ? getservbyname($port, "tcp") : $port;
152             die "Could not find a port number for $port\n" if not $portnum;
153             my $iaddr = inet_aton($host) or die "no host: $host\n";
154             my $paddr = sockaddr_in($portnum, $iaddr);
155             my $proto = getprotobyname("tcp");
156             socket(my $sock, PF_INET, SOCK_STREAM, $proto) or die "socket: $!\n";
157             connect($sock, $paddr) or die "connect: $!\n";
158             close($sock) or die "close: $!\n";
159             !!1;
160             }
161             };
162             goto($checker);
163             };
164            
165             $HINTS{ internet } = sub
166             {
167             plan skip_all => 'Not running network tests'
168             if $ENV{NO_NETWORK_TESTING};
169            
170             my $arg = $_[2];
171             my @hosts =
172             ref($arg) eq q(HASH) ? %$arg :
173             ref($arg) eq q(ARRAY) ? @$arg :
174             ref($arg) eq q(SCALAR) ? ( $$arg => 80 ) :
175             ( 'www.google.com' => 80 );
176            
177             while (@hosts) {
178             my ($host, $port) = splice(@hosts, 0, 2);
179             defined($host) && defined($port)
180             or BAIL_OUT("Expected host+port pair (not undef)");
181             eval { $checker->($host, $port); 1 } or plan skip_all => "$@";
182             }
183            
184             return;
185             };
186             }
187              
188             $HINTS{ benchmark } = sub
189             {
190             return qw(is_fastest)
191             if $ENV{RELEASE_TESTING} || $ENV{EXTENDED_TESTING};
192             plan skip_all => 'Not running benchmarks';
193             };
194              
195             $HINTS{ verbose } = sub { $VERBOSE = 1; () };
196              
197             our @ISA = qw(Exporter::Tiny);
198             our %EXPORT_TAGS = (
199             more => [qw(
200             ok is isnt like unlike is_deeply cmp_ok new_ok isa_ok can_ok
201             pass fail
202             diag note explain
203             subtest
204             skip todo_skip plan done_testing BAIL_OUT
205             )],
206             fatal => [qw( exception )],
207             warnings => [qw( warnings warning )],
208             api => [qw( public_ok import_ok class_api_ok )],
209             moose => [qw( does_ok )],
210             pod => [qw(
211             pod_file_ok all_pod_files_ok
212             pod_coverage_ok all_pod_coverage_ok
213             )],
214             versions => [qw( version_ok version_all_ok version_all_same )],
215             strings => [qw(
216             is_string is_string_nows like_string unlike_string
217             contains_string lacks_string
218             )],
219             clean => [qw( namespaces_clean )],
220             deep => [qw( cmp_deeply TD )],
221             deeper => [qw(
222             cmp_deeply TD
223             ignore methods listmethods shallow noclass useclass
224             re superhashof subhashof bag superbagof subbagof
225             set supersetof subsetof all any obj_isa array_each
226             str num bool code
227             )],
228             deprecated => [qw( use_ok require_ok eq_array eq_hash eq_set )],
229             %HINTS,
230             );
231              
232             our @EXPORT_OK = (
233             'object_ok', 'shouldnt_warn', 'is_fastest',
234             map(@$_, grep { ref($_) eq 'ARRAY' } values(%EXPORT_TAGS)),
235             );
236              
237             our @EXPORT = (
238             'object_ok',
239             map(@{$EXPORT_TAGS{$_}}, qw(more fatal warnings api moose strings deep clean)),
240             );
241              
242             # Here we check to see if the import list consists
243             # only of hints. If so, we add @EXPORT to the list.
244             # This means that `use Test::Modern -extended;`
245             # won't result in the standard exports getting
246             # suppressed.
247             #
248             sub import
249             {
250 20     20   5562 my $me = shift;
251 38 100 100     373 my $symbols = grep {
    100          
252 20         47 ref($_) ? 0 : # refs are not symbols
253             /\A[:-](\w+)\z/ && $HINTS{$1} ? 0 : # hints are not symbols
254             1; # everything else is
255             } @_;
256            
257 20 100       160 push @_, @EXPORT if $symbols == 0;
258            
259 20 100       81 my $globals = ref($_[0]) eq 'HASH' ? shift() : {};
260 20 100       151 $globals->{into_file} = (caller)[1] unless ref($globals->{into});
261            
262 20         65 unshift @_, $me, $globals;
263 20         143 goto \&Exporter::Tiny::import;
264             }
265              
266             sub _exporter_validate_opts
267             {
268 15     15   4984 my $me = shift;
269 15         25 my ($opts) = @_;
270 15         34 my $caller = $opts->{into};
271            
272             # Exporter::Tiny can't handle exporting variables
273             # at the moment. :-(
274             #
275             {
276 16     16   125 no strict qw(refs);
  16         34  
  16         29015  
  15         23  
277 15 100       51 (ref($caller) ? $caller->{'$TODO'} : *{"$caller\::TODO"})
  9         55  
278             = \$Test::More::TODO;
279             }
280            
281 15 100       53 return if ref $caller;
282 9         76 'strict'->import::into($caller);
283 9         2322 'warnings'->import::into($caller);
284 9         1384 $me->_setup_inc($opts);
285             }
286              
287             sub _setup_inc
288             {
289 9     9   19 shift;
290 9         19 my ($opts) = @_;
291            
292 9 50       40 return unless exists($opts->{into_file});
293            
294 9         13 my $dir = do {
295 9         194 my ($v, $d) = 'File::Spec'->splitpath($opts->{into_file});
296 9         182 'File::Spec'->catpath($v, $d, '');
297             };
298            
299 9         16 my $found;
300 9         33 LEVEL: for my $i (0..5)
301             {
302 18         209 my $t_dir = 'File::Spec'->catdir($dir, (('File::Spec'->updir) x $i), 't');
303 18         132 my $xt_dir = 'File::Spec'->catdir($dir, (('File::Spec'->updir) x $i), 'xt');
304            
305 18 100 66     954 -d $t_dir or -d $xt_dir or next LEVEL;
306            
307 9         61 my $tlib_dir = 'File::Spec'->catdir($t_dir, 'lib');
308            
309 9 50       152 if (-d $tlib_dir)
310             {
311 9         11036 require lib;
312 9         7070 'lib'->import(Cwd::abs_path $tlib_dir);
313 9         1076 $found++;
314             }
315            
316 9 50       53 last LEVEL if $found;
317             }
318            
319 9 50 66     94 if ($opts->{lib} and not $found)
320             {
321 0         0 BAIL_OUT("Expected to find directory t/lib!");
322             }
323             }
324              
325             # Additional exports
326             #
327              
328             sub does_ok ($$;$) # just a copy of Test::More::isa_ok
329             {
330 2     2 1 5 my( $thing, $class, $thing_name ) = @_;
331 2         59 my $tb = Test::More->builder;
332            
333 2         14 my $whatami;
334 2 50       7 if( !defined $thing ) {
    50          
335 0         0 $whatami = 'undef';
336             }
337             elsif( ref $thing ) {
338 2         3 $whatami = 'reference';
339            
340 2         9 local($@,$!);
341 2         15 require Scalar::Util;
342 2 50       8 if( Scalar::Util::blessed($thing) ) {
343 2         4 $whatami = 'object';
344             }
345             }
346             else {
347 0         0 $whatami = 'class';
348             }
349            
350 2     2   12 my( $rslt, $error ) = $tb->_try( sub { $thing->DOES($class) } );
  2         33  
351            
352 2 50       25 if ($error) {
353 0 0       0 die <
354             WHOA! I tried to call ->DOES on your $whatami and got some weird error.
355             Here's the error.
356             $error
357             WHOA
358             }
359            
360             # Special case for isa_ok( [], "ARRAY" ) and like
361 2 50       8 if( $whatami eq 'reference' ) {
362 0         0 $rslt = UNIVERSAL::DOES($thing, $class);
363             }
364            
365 2         3 my($diag, $name);
366 2 50       5 if( defined $thing_name ) {
    0          
    0          
    0          
    0          
367 2         5 $name = "'$thing_name' does '$class'";
368 2 50       8 $diag = defined $thing ? "'$thing_name' doesn't '$class'" : "'$thing_name' isn't defined";
369             }
370             elsif( $whatami eq 'object' ) {
371 0         0 my $my_class = ref $thing;
372 0         0 $thing_name = qq[An object of class '$my_class'];
373 0         0 $name = "$thing_name does '$class'";
374 0         0 $diag = "The object of class '$my_class' doesn't '$class'";
375             }
376             elsif( $whatami eq 'reference' ) {
377 0         0 my $type = ref $thing;
378 0         0 $thing_name = qq[A reference of type '$type'];
379 0         0 $name = "$thing_name does '$class'";
380 0         0 $diag = "The reference of type '$type' doesn't '$class'";
381             }
382             elsif( $whatami eq 'undef' ) {
383 0         0 $thing_name = 'undef';
384 0         0 $name = "$thing_name does '$class'";
385 0         0 $diag = "$thing_name isn't defined";
386             }
387             elsif( $whatami eq 'class' ) {
388 0         0 $thing_name = qq[The class (or class-like) '$thing'];
389 0         0 $name = "$thing_name does '$class'";
390 0         0 $diag = "$thing_name doesn't '$class'";
391             }
392             else {
393 0         0 die;
394             }
395            
396 2         1 my $ok;
397 2 50       5 if($rslt) {
398 2         6 $ok = $tb->ok( 1, $name );
399             }
400             else {
401 0         0 $ok = $tb->ok( 0, $name );
402 0         0 $tb->diag(" $diag\n");
403             }
404            
405 2         653 return $ok;
406             }
407              
408             sub object_ok
409             {
410 1     1 1 23 local $Test::Builder::Level = $Test::Builder::Level + 1;
411            
412 1         2 my $object = shift;
413 1 50       7 my $name = (@_%2) ? shift : '$object';
414 1         8 my %tests = @_;
415 1         3 my $bail = !!0;
416            
417             my $result = &subtest("$name ok", sub
418             {
419 1 50   1   1009 if (ref($object) eq q(CODE))
420             {
421             try {
422 1         43 my $tmp = $object->();
423 1 50       8 die 'coderef did not return an object'
424             unless ref($tmp);
425 1         2 $object = $tmp;
426 1         6 pass("instantiate $name");
427             }
428             catch {
429 0         0 fail("instantiate $name");
430 0         0 diag("instantiating $name threw an exception: $_");
431             }
432 1         13 }
433            
434 1 50       425 ok(Scalar::Util::blessed($object), "$name is blessed")
435             or return;
436            
437 1 50       336 if (exists($tests{isa}))
438             {
439 1 50       7 my @classes = ref($tests{isa}) eq q(ARRAY) ? @{$tests{isa}} : $tests{isa};
  0         0  
440 1         6 isa_ok($object, $_, $name) for @classes;
441 1         353 delete $tests{isa};
442             }
443            
444 1 50       4 if (exists($tests{does}))
445             {
446 1 50       7 my @roles = ref($tests{does}) eq q(ARRAY) ? @{$tests{does}} : $tests{does};
  1         4  
447 1         5 does_ok($object, $_, $name) for @roles;
448 1         4 delete $tests{does};
449             }
450            
451 1 50       4 if (exists($tests{can}))
452             {
453 1 50       6 my @methods = ref($tests{can}) eq q(ARRAY) ? @{$tests{can}} : $tests{can};
  1         4  
454 1         5 can_ok($object, @methods);
455 1         458 delete $tests{can};
456             }
457            
458 1 50       4 if (exists($tests{api}))
459             {
460 1 50       11 my @methods = ref($tests{api}) eq q(ARRAY) ? @{$tests{api}} : $tests{api};
  1         3  
461 1         7 class_api_ok(ref($object), @methods);
462 1         426 delete $tests{api};
463             }
464            
465 1 50       5 if (delete($tests{clean}))
466             {
467 1         4 namespaces_clean(ref($object));
468             }
469            
470 1 50       4 if (exists($tests{more}))
471             {
472 1         3 my $more = delete $tests{more};
473             &subtest("more tests for $name", sub
474             {
475 1         551 my $exception = exception { $object->$more };
  1         48  
476 1         1009 is($exception, undef, "no exception thrown by additional tests");
477 1         333 done_testing;
478 1         10 });
479             }
480            
481 1         6750 $bail = !!keys %tests;
482            
483 1         4 done_testing;
484 1         16 });
485            
486 1 50       804 if ($bail)
487             {
488 0         0 my $huh = join q[, ], sort keys %tests;
489 0         0 BAIL_OUT("object_ok cannot understand: $huh");
490             }
491            
492             # return $object
493 1 50       6 $result ? $object : ();
494             }
495              
496             {
497             my $wrap = sub {
498             my @alts = @_;
499             my $chosen;
500             return sub {
501 0 0   0   0 unless ($chosen)
502             {
503 0         0 ALT: for (@alts)
504             {
505 0 0 0     0 ref($_) and ($chosen = $_) and last ALT;
506 0         0 my ($module, $sub) = /^(.+)::(\w+)$/;
507             try {
508 16     16   131 no strict qw(refs);
  16         39  
  16         670  
509 16     16   91 no warnings qw(exiting);
  16         28  
  16         10269  
510 0     0   0 require_module($module);
511 0 0 0     0 exists(&$_)
512             ? (($chosen = \&$_) and last ALT)
513             : die("no such sub $_");
514             }
515             catch {
516 0     0   0 diag("could not load $module: $_");
517 0         0 };
518             }
519             }
520             $chosen
521 0 0       0 ? goto($chosen)
522             : fail("$alts[0] not available - failing test!")
523             };
524             };
525            
526             *is_string = $wrap->(
527             'Test::LongString::is_string',
528             'Test::More::is',
529             );
530             *is_string_nows = $wrap->(
531             'Test::LongString::is_string_nows',
532             sub {
533             my $got = shift;
534             my $expected = shift;
535             s/\s+//g for $got, $expected;
536             unshift @_, $got, $expected;
537             goto \&Test::More::is;
538             },
539             );
540             *like_string = $wrap->(
541             'Test::LongString::like_string',
542             'Test::More::like',
543             );
544             *unlike_string = $wrap->(
545             'Test::LongString::unlike_string',
546             'Test::More::unlike',
547             );
548             *contains_string = $wrap->(
549             'Test::LongString::contains_string',
550             sub {
551             my $got = shift;
552             my $expected = shift;
553             unshift @_, $got, qr/\Q$expected\E/;
554             goto \&Test::More::like;
555             },
556             );
557             *lacks_string = $wrap->(
558             'Test::LongString::lacks_string',
559             sub {
560             my $got = shift;
561             my $expected = shift;
562             unshift @_, $got, qr/\Q$expected\E/;
563             goto \&Test::More::unlike;
564             },
565             );
566             }
567              
568             sub _generate_TD
569             {
570 7     7   20595 my $_td = bless(do { my $x = 1; \$x }, 'Test::Modern::_TD');
  7         17  
  7         26  
571 7     0   106 return sub () { $_td };
  0         0  
572             }
573              
574             sub Test::Modern::_TD::AUTOLOAD
575             {
576 2     2   21425 shift;
577 2         20 my ($method) = ($Test::Modern::_TD::AUTOLOAD =~ /(\w+)\z/);
578 2 50       10 return if $method eq 'DESTROY';
579 2 50       30 my $coderef = 'Test::Deep'->can($method)
580             or die("Test::Deep::$method not found");
581 2         11 $coderef->(@_);
582             }
583              
584             # Stolen from Test::CleanNamespaces; eliminated Package::Stash and
585             # Sub::Name dependencies; massively cleaned up; don't use Test::Builder
586             # directly; instead just call Test::More's exported functions.
587             #
588             {
589             my $_dirt = sub
590             {
591             my $ns = shift;
592             require_module($ns);
593            
594             my %symbols = do {
595 16     16   107 no strict qw(refs);
  16         30  
  16         3751  
596             map { /([^:]+)$/; $1 => $_; }
597             grep { eval { *$_{CODE} } }
598             values %{"$ns\::"};
599             };
600            
601             my @imports;
602             my $meta;
603             if ($INC{ module_notional_filename('Moose::Util') }
604             and $meta = Moose::Util::find_meta($ns))
605             {
606             my %subs = %symbols;
607             delete @subs{ $meta->get_method_list };
608             @imports = keys %subs;
609             }
610             elsif ($INC{ module_notional_filename('Mouse::Util') }
611             and $meta = Mouse::Util::class_of($ns))
612             {
613             my %subs = %symbols;
614             delete @subs{ $meta->get_method_list };
615             @imports = keys %subs;
616             }
617             else
618             {
619             require B;
620 16     16   87 no strict qw(refs);
  16         41  
  16         15210  
621             @imports = grep {
622             my $stash = B::svref_2object(\&{"$ns\::$_"})->GV->STASH->NAME;
623             $stash ne $ns
624             and $stash ne 'Role::Tiny'
625             and not eval { require Role::Tiny; Role::Tiny->is_role($stash) }
626             } keys %symbols;
627             }
628            
629             my %imports; @imports{@imports} = map substr("$symbols{$_}", 1), @imports;
630            
631             # But really it's better to inherit these rather than import them. :-)
632             if (keys %imports) {
633             delete @imports{qw(import unimport)};
634             }
635            
636             if (keys %imports) {
637             my @overloads = grep {
638             /^\(/ or $imports{$_} eq 'overload::nil'
639             } keys %imports;
640             delete @imports{@overloads} if @overloads;
641             }
642            
643             if (keys %imports and $] < 5.010) {
644             my @constants = grep { $imports{$_} eq 'constant::__ANON__' } keys %imports;
645             delete @imports{@constants} if @constants;
646             }
647            
648             @imports = sort keys(%imports);
649             my %sources;
650             @sources{@imports} = map {
651             B::svref_2object(\&{"$ns\::$_"})->GV->STASH->NAME;
652             } @imports;
653            
654             my %does;
655             for my $func (keys %sources) {
656             my $role = $sources{$func};
657             $does{$role} = !!eval { $ns->DOES($role) }
658             unless exists $does{$role};
659             delete $imports{$func}
660             if $does{$role};
661             }
662            
663             sort keys(%imports);
664             };
665            
666             my $_diag_dirt = sub
667             {
668             require B;
669            
670             my $ns = shift;
671             my @imports = @_;
672            
673             my %imports;
674             @imports{@imports} = map {
675             B::svref_2object(\&{"$ns\::$_"})->GV->STASH->NAME . "::$_";
676             } @imports;
677             diag explain('remaining imports: ' => \%imports);
678             };
679            
680             my $_test_or_skip = sub
681             {
682             my $ns = shift;
683             my $rv;
684             try {
685             my @imports = $ns->$_dirt;
686             $rv = ok(!@imports, "${ns} contains no imported functions")
687             or $ns->$_diag_dirt(@imports);
688             }
689             catch {
690             SKIP: {
691             skip "failed to load $ns: $_", 1;
692             fail("failed to load module");
693             };
694             };
695             return $rv;
696             };
697            
698             sub namespaces_clean
699             {
700 1     1 1 1 local $Test::Builder::Level = $Test::Builder::Level + 1;
701            
702             # special case a single namespace
703 1 50       7 return shift->$_test_or_skip if @_ == 1;
704            
705 0         0 my @namespaces = @_;
706             return &subtest(
707             sprintf("namespaces_clean: %s", join q(, ), @namespaces),
708             sub {
709 0     0   0 $_->$_test_or_skip for @namespaces;
710 0         0 done_testing;
711             },
712 0         0 );
713             }
714             }
715              
716             # Release tests...
717             {
718             sub _should_extended_test ()
719             {
720 7 50 33 7   148 $ENV{RELEASE_TESTING} || $ENV{AUTHOR_TESTING} || $ENV{EXTENDED_TESTING};
721             }
722            
723             sub _wrap
724             {
725 16     16   103 no strict qw(refs);
  16         62  
  16         22478  
726 112     112   293 my ($module, $function, %opt) = @_;
727            
728 112         104 my $code;
729 112 100       294 ($function, $code) = each(%$function)
730             if ref($function) eq q(HASH);
731            
732             *$function = sub
733             {
734 7 50 33 7   78 if ($opt{extended} and not _should_extended_test)
735             {
736 7         32 SKIP: {
737 7         8 skip 'Not running extended tests', 1;
738 0         0 pass("skipped");
739             }
740 7         3381 return 1;
741             }
742            
743 0 0       0 if (eval "require $module")
744             {
745 0   0     0 $code ||= \&{"$module\::$function"};
  0         0  
746 0 0       0 if ($opt{multi})
747             {
748 0         0 my @args = @_;
749             @_ = ($function, sub {
750 0     0   0 @_ = @args;
751 0         0 goto $code;
752 0         0 });
753 0         0 goto \&Test::More::subtest;
754             }
755             else
756             {
757 0         0 goto $code;
758             }
759             }
760            
761 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
762 0 0       0 SKIP: {
763 0         0 skip "$module only required for release testing", 1
764             unless $ENV{RELEASE_TESTING};
765 0         0 fail("$function");
766 0         0 diag("$module not installed");
767             }
768 0         0 return;
769 112         948 };
770             }
771            
772             my $_VAS = sub
773             {
774             my ($dir, $name) = @_;
775             $dir
776             = defined $dir ? $dir
777             : -d 'blib' ? 'blib'
778             : 'lib';
779             return fail("$dir does not exist, or is not a directory")
780             unless -d $dir;
781            
782             my @files = File::Find::Rule->perl_module->in($dir);
783             $name ||= "all modules in $dir have the same version number";
784            
785             local $Test::Builder::Level = $Test::Builder::Level + 1;
786            
787             &subtest($name => sub
788             {
789             my %versions;
790             for my $file (@files)
791             {
792             Test::Version::version_ok($file) or next;
793             my $info = Module::Metadata->new_from_file($file);
794             my $ver = $info->version;
795             $ver = '(undef)' unless defined $ver;
796             push @{$versions{$ver}}, $file;
797             }
798             my $ok = keys(%versions) < 2;
799             ok($ok, "single version number found");
800             if (!$ok)
801             {
802             diag("Files with version $_: @{$versions{$_}}")
803             for sort keys(%versions);
804             }
805             done_testing;
806             });
807             };
808            
809             _wrap("Test::Pod", "pod_file_ok", extended => 1);
810             _wrap("Test::Pod", "all_pod_files_ok", extended => 1, multi => 1);
811             _wrap("Test::Pod::Coverage", "pod_coverage_ok", extended => 1);
812             _wrap("Test::Pod::Coverage", "all_pod_coverage_ok", extended => 1, multi => 1);
813             _wrap("Test::Version", "version_ok", extended => 1);
814             _wrap("Test::Version", "version_all_ok", extended => 1, multi => 1);
815             _wrap("Test::Version", { "version_all_same" => $_VAS }, extended => 1);
816             }
817              
818             sub shouldnt_warn (&)
819             {
820 1     1 1 16 my @warnings = do {
821 1         5 local $Test::Builder::Level = $Test::Builder::Level + 3;
822 1         9 &Test::Warnings::warnings(@_);
823             };
824            
825 1         3244 my $old = $TODO;
826 1         3 $TODO = "shouldn't warn block";
827 1         3 local $Test::Builder::Level = $Test::Builder::Level + 1;
828 1         6 ok(scalar(@warnings)==0, "no (unexpected) warnings");
829 1         1303 diag("Saw warning: $_") for @warnings;
830 1         290 $TODO = $old;
831             }
832              
833             {
834             # When passed a string of code, Benchmark::timethis() evaluates
835             # it in the caller package; need to fake that.
836             my $_do = sub {
837             my $caller = shift;
838             my $sub = shift;
839             my $doer = eval qq[ package $caller; sub { ${sub}(\@_) } ];
840             $doer->(@_);
841             };
842            
843             my $get_res = sub {
844             require Benchmark;
845             require Scalar::Util;
846             my ($caller, $times, $sub) = @_;
847             Scalar::Util::blessed($sub) && $sub->isa("Benchmark")
848             ? $sub
849             : $caller->$_do('Benchmark::timethis', $times, $sub, "", "none");
850             };
851            
852             sub is_fastest
853             {
854 0     0 1 0 require Benchmark;
855            
856 0         0 my $caller = caller;
857 0         0 my ($which, $times, $marks, $desc) = @_;
858 0 0       0 $desc = "$which is fastest" unless defined $desc;
859            
860 0         0 my @marks;
861 0         0 while (my ($name, $sub) = each %$marks)
862             {
863 0         0 my $res = $get_res->($caller, $times, $sub);
864 0         0 my ($r, $pu, $ps, $cu, $cs, $n) = @$res;
865            
866 0         0 push(@marks, {
867             name => $name,
868             res => $res,
869             n => $n,
870             s => ($pu+$ps),
871             });
872             }
873            
874 0         0 @marks = sort {$b->{n} * $a->{s} <=> $a->{n} * $b->{s}} @marks;
  0         0  
875            
876 0         0 my $ok = $marks[0]->{name} eq $which;
877            
878 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
879            
880 0         0 ok($ok, $desc);
881 0 0       0 diag("$which was not the fastest") unless $ok;
882            
883 0 0 0     0 if ($VERBOSE or not $ok)
884             {
885 0         0 foreach my $mark (@marks)
886             {
887 0         0 diag("$mark->{name} - " . Benchmark::timestr($mark->{res}));
888             }
889             }
890            
891 0         0 $ok;
892             }
893             }
894              
895             1;
896              
897             ## no Test::Tabs
898              
899             __END__