File Coverage

blib/lib/Test/Modern.pm
Criterion Covered Total %
statement 201 279 72.0
branch 52 116 44.8
condition 9 25 36.0
subroutine 37 44 84.0
pod 5 5 100.0
total 304 469 64.8


line stmt bran cond sub pod time code
1 16     16   26718 use 5.006001;
  16         48  
  16         662  
2 16     16   70 use strict;
  16         19  
  16         494  
3 16     16   74 use warnings;
  16         40  
  16         1151  
4              
5             package Test::Modern;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.012';
9              
10             our $VERBOSE;
11              
12 16     16   82 use Cwd 0 qw();
  16         244  
  16         441  
13 16     16   9295 use Exporter::Tiny 0.030 qw();
  16         36183  
  16         451  
14 16     16   99 use File::Spec 0 qw();
  16         286  
  16         367  
15 16     16   8926 use IO::File 1.08 qw();
  16         140543  
  16         530  
16 16     16   116 use IO::Handle 1.21 qw();
  16         241  
  16         359  
17 16     16   8735 use Import::Into 1.002 qw();
  16         42610  
  16         543  
18 16     16   259 use Module::Runtime 0.012 qw( require_module module_notional_filename );
  16         300  
  16         81  
19 16     16   11759 use Test::More 0.96;
  16         239611  
  16         134  
20 16     16   12885 use Test::API 0.004;
  16         26363  
  16         121  
21 16     16   11843 use Test::Fatal 0.007;
  16         39792  
  16         1112  
22 16 50   16   7944 use Test::Warnings 0.009 qw( warning warnings ), ($ENV{PERL_TEST_MODERN_ALLOW_WARNINGS} ? ':no_end_test' : ());
  16         25248  
  16         131  
23 16     16   12979 use Test::Deep 0.111 qw( :v1 );
  16         148866  
  16         5136  
24 16     16   130 use Try::Tiny 0.15 qw( try catch );
  16         265  
  16         19040  
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   7 open(my($io), '<', \$_[0])
  1         1  
  1         7  
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   8069 my $me = shift;
251 38 100 100     328 my $symbols = grep {
    100          
252 20         44 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       155 push @_, @EXPORT if $symbols == 0;
258            
259 20 100       78 my $globals = ref($_[0]) eq 'HASH' ? shift() : {};
260 20 100       116 $globals->{into_file} = (caller)[1] unless ref($globals->{into});
261            
262 20         63 unshift @_, $me, $globals;
263 20         116 goto \&Exporter::Tiny::import;
264             }
265              
266             sub _exporter_validate_opts
267             {
268 15     15   4483 my $me = shift;
269 15         25 my ($opts) = @_;
270 15         31 my $caller = $opts->{into};
271            
272             # Exporter::Tiny can't handle exporting variables
273             # at the moment. :-(
274             #
275             {
276 16     16   132 no strict qw(refs);
  16         23  
  16         23442  
  15         22  
277 15 100       51 (ref($caller) ? $caller->{'$TODO'} : *{"$caller\::TODO"})
  9         42  
278             = \$Test::More::TODO;
279             }
280            
281 15 100       58 return if ref $caller;
282 9         63 'strict'->import::into($caller);
283 9         2038 'warnings'->import::into($caller);
284 9         1430 $me->_setup_inc($opts);
285             }
286              
287             sub _setup_inc
288             {
289 9     9   15 shift;
290 9         16 my ($opts) = @_;
291            
292 9 50       35 return unless exists($opts->{into_file});
293            
294 9         10 my $dir = do {
295 9         174 my ($v, $d) = 'File::Spec'->splitpath($opts->{into_file});
296 9         147 'File::Spec'->catpath($v, $d, '');
297             };
298            
299 9         17 my $found;
300 9         22 LEVEL: for my $i (0..5)
301             {
302 18         163 my $t_dir = 'File::Spec'->catdir($dir, (('File::Spec'->updir) x $i), 't');
303 18         92 my $xt_dir = 'File::Spec'->catdir($dir, (('File::Spec'->updir) x $i), 'xt');
304            
305 18 100 66     301 -d $t_dir or -d $xt_dir or next LEVEL;
306            
307 9         46 my $tlib_dir = 'File::Spec'->catdir($t_dir, 'lib');
308            
309 9 50       97 if (-d $tlib_dir)
310             {
311 9         4663 require lib;
312 9         5483 'lib'->import(Cwd::abs_path $tlib_dir);
313 9         743 $found++;
314             }
315            
316 9 50       41 last LEVEL if $found;
317             }
318            
319 9 50 66     68 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 2 my( $thing, $class, $thing_name ) = @_;
331 2         7 my $tb = Test::More->builder;
332            
333 2         36 my $whatami;
334 2 50       7 if( !defined $thing ) {
    50          
335 0         0 $whatami = 'undef';
336             }
337             elsif( ref $thing ) {
338 2         4 $whatami = 'reference';
339            
340 2         8 local($@,$!);
341 2         9 require Scalar::Util;
342 2 50       7 if( Scalar::Util::blessed($thing) ) {
343 2         5 $whatami = 'object';
344             }
345             }
346             else {
347 0         0 $whatami = 'class';
348             }
349            
350 2         3 my ($rslt, $error);
351 2     2   14 try { $rslt = $thing->DOES($class) } catch { $error = $_ };
  2         66  
  0         0  
352            
353 2 50       26 if ($error) {
354 0 0       0 die <
355             WHOA! I tried to call ->DOES on your $whatami and got some weird error.
356             Here's the error.
357             $error
358             WHOA
359             }
360            
361             # Special case for isa_ok( [], "ARRAY" ) and like
362 2 50       6 if( $whatami eq 'reference' ) {
363 0         0 $rslt = UNIVERSAL::DOES($thing, $class);
364             }
365            
366 2         2 my($diag, $name);
367 2 50       5 if( defined $thing_name ) {
    0          
    0          
    0          
    0          
368 2         5 $name = "'$thing_name' does '$class'";
369 2 50       8 $diag = defined $thing ? "'$thing_name' doesn't '$class'" : "'$thing_name' isn't defined";
370             }
371             elsif( $whatami eq 'object' ) {
372 0         0 my $my_class = ref $thing;
373 0         0 $thing_name = qq[An object of class '$my_class'];
374 0         0 $name = "$thing_name does '$class'";
375 0         0 $diag = "The object of class '$my_class' doesn't '$class'";
376             }
377             elsif( $whatami eq 'reference' ) {
378 0         0 my $type = ref $thing;
379 0         0 $thing_name = qq[A reference of type '$type'];
380 0         0 $name = "$thing_name does '$class'";
381 0         0 $diag = "The reference of type '$type' doesn't '$class'";
382             }
383             elsif( $whatami eq 'undef' ) {
384 0         0 $thing_name = 'undef';
385 0         0 $name = "$thing_name does '$class'";
386 0         0 $diag = "$thing_name isn't defined";
387             }
388             elsif( $whatami eq 'class' ) {
389 0         0 $thing_name = qq[The class (or class-like) '$thing'];
390 0         0 $name = "$thing_name does '$class'";
391 0         0 $diag = "$thing_name doesn't '$class'";
392             }
393             else {
394 0         0 die;
395             }
396            
397 2         3 my $ok;
398 2 50       3 if($rslt) {
399 2         7 $ok = $tb->ok( 1, $name );
400             }
401             else {
402 0         0 $ok = $tb->ok( 0, $name );
403 0         0 $tb->diag(" $diag\n");
404             }
405            
406 2         832 return $ok;
407             }
408              
409             sub object_ok
410             {
411 1     1 1 19 local $Test::Builder::Level = $Test::Builder::Level + 1;
412            
413 1         1 my $object = shift;
414 1 50       4 my $name = (@_%2) ? shift : '$object';
415 1         5 my %tests = @_;
416 1         2 my $bail = !!0;
417            
418             my $result = &subtest("$name ok", sub
419             {
420 1 50   1   904 if (ref($object) eq q(CODE))
421             {
422             try {
423 1         58 my $tmp = $object->();
424 1 50       11 die 'coderef did not return an object'
425             unless ref($tmp);
426 1         2 $object = $tmp;
427 1         8 pass("instantiate $name");
428             }
429             catch {
430 0         0 fail("instantiate $name");
431 0         0 diag("instantiating $name threw an exception: $_");
432             }
433 1         12 }
434            
435 1 50       449 ok(Scalar::Util::blessed($object), "$name is blessed")
436             or return;
437            
438 1 50       342 if (exists($tests{isa}))
439             {
440 1 50       7 my @classes = ref($tests{isa}) eq q(ARRAY) ? @{$tests{isa}} : $tests{isa};
  0         0  
441 1         7 isa_ok($object, $_, $name) for @classes;
442 1         367 delete $tests{isa};
443             }
444            
445 1 50       5 if (exists($tests{does}))
446             {
447 1 50       6 my @roles = ref($tests{does}) eq q(ARRAY) ? @{$tests{does}} : $tests{does};
  1         3  
448 1         5 does_ok($object, $_, $name) for @roles;
449 1         4 delete $tests{does};
450             }
451            
452 1 50       40 if (exists($tests{can}))
453             {
454 1 50       5 my @methods = ref($tests{can}) eq q(ARRAY) ? @{$tests{can}} : $tests{can};
  1         4  
455 1         7 can_ok($object, @methods);
456 1         588 delete $tests{can};
457             }
458            
459 1 50       4 if (exists($tests{api}))
460             {
461 1 50       4 my @methods = ref($tests{api}) eq q(ARRAY) ? @{$tests{api}} : $tests{api};
  1         3  
462 1         4 class_api_ok(ref($object), @methods);
463 1         430 delete $tests{api};
464             }
465            
466 1 50       4 if (delete($tests{clean}))
467             {
468 1         3 namespaces_clean(ref($object));
469             }
470            
471 1 50       5 if (exists($tests{more}))
472             {
473 1         2 my $more = delete $tests{more};
474             &subtest("more tests for $name", sub
475             {
476 1         547 my $exception = exception { $object->$more };
  1         38  
477 1         861 is($exception, undef, "no exception thrown by additional tests");
478 1         385 done_testing;
479 1         9 });
480             }
481            
482 1         642 $bail = !!keys %tests;
483            
484 1         3 done_testing;
485 1         15 });
486            
487 1 50       785 if ($bail)
488             {
489 0         0 my $huh = join q[, ], sort keys %tests;
490 0         0 BAIL_OUT("object_ok cannot understand: $huh");
491             }
492            
493             # return $object
494 1 50       5 $result ? $object : ();
495             }
496              
497             {
498             my $wrap = sub {
499             my @alts = @_;
500             my $chosen;
501             return sub {
502 0 0   0   0 unless ($chosen)
503             {
504 0         0 ALT: for (@alts)
505             {
506 0 0 0     0 ref($_) and ($chosen = $_) and last ALT;
507 0         0 my ($module, $sub) = /^(.+)::(\w+)$/;
508             try {
509 16     16   103 no strict qw(refs);
  16         29  
  16         681  
510 16     16   79 no warnings qw(exiting);
  16         23  
  16         9050  
511 0     0   0 require_module($module);
512 0 0 0     0 exists(&$_)
513             ? (($chosen = \&$_) and last ALT)
514             : die("no such sub $_");
515             }
516             catch {
517 0     0   0 diag("could not load $module: $_");
518 0         0 };
519             }
520             }
521             $chosen
522 0 0       0 ? goto($chosen)
523             : fail("$alts[0] not available - failing test!")
524             };
525             };
526            
527             *is_string = $wrap->(
528             'Test::LongString::is_string',
529             'Test::More::is',
530             );
531             *is_string_nows = $wrap->(
532             'Test::LongString::is_string_nows',
533             sub {
534             my $got = shift;
535             my $expected = shift;
536             s/\s+//g for $got, $expected;
537             unshift @_, $got, $expected;
538             goto \&Test::More::is;
539             },
540             );
541             *like_string = $wrap->(
542             'Test::LongString::like_string',
543             'Test::More::like',
544             );
545             *unlike_string = $wrap->(
546             'Test::LongString::unlike_string',
547             'Test::More::unlike',
548             );
549             *contains_string = $wrap->(
550             'Test::LongString::contains_string',
551             sub {
552             my $got = shift;
553             my $expected = shift;
554             unshift @_, $got, qr/\Q$expected\E/;
555             goto \&Test::More::like;
556             },
557             );
558             *lacks_string = $wrap->(
559             'Test::LongString::lacks_string',
560             sub {
561             my $got = shift;
562             my $expected = shift;
563             unshift @_, $got, qr/\Q$expected\E/;
564             goto \&Test::More::unlike;
565             },
566             );
567             }
568              
569             sub _generate_TD
570             {
571 7     7   17075 my $_td = bless(do { my $x = 1; \$x }, 'Test::Modern::_TD');
  7         27  
  7         25  
572 7     0   86 return sub () { $_td };
  0         0  
573             }
574              
575             sub Test::Modern::_TD::AUTOLOAD
576             {
577 2     2   507348 shift;
578 2         15 my ($method) = ($Test::Modern::_TD::AUTOLOAD =~ /(\w+)\z/);
579 2 50       13 return if $method eq 'DESTROY';
580 2 50       19 my $coderef = 'Test::Deep'->can($method)
581             or die("Test::Deep::$method not found");
582 2         9 $coderef->(@_);
583             }
584              
585             # Stolen from Test::CleanNamespaces; eliminated Package::Stash and
586             # Sub::Name dependencies; massively cleaned up; don't use Test::Builder
587             # directly; instead just call Test::More's exported functions.
588             #
589             {
590             my $_dirt = sub
591             {
592             my $ns = shift;
593             require_module($ns);
594            
595             my %symbols = do {
596 16     16   89 no strict qw(refs);
  16         23  
  16         3433  
597             map { /([^:]+)$/; $1 => $_; }
598             grep { eval { *$_{CODE} } }
599             values %{"$ns\::"};
600             };
601            
602             my @imports;
603             my $meta;
604             if ($INC{ module_notional_filename('Moose::Util') }
605             and $meta = Moose::Util::find_meta($ns))
606             {
607             my %subs = %symbols;
608             delete @subs{ $meta->get_method_list };
609             @imports = keys %subs;
610             }
611             elsif ($INC{ module_notional_filename('Mouse::Util') }
612             and $meta = Mouse::Util::class_of($ns))
613             {
614             my %subs = %symbols;
615             delete @subs{ $meta->get_method_list };
616             @imports = keys %subs;
617             }
618             else
619             {
620             require B;
621 16     16   79 no strict qw(refs);
  16         23  
  16         13945  
622             @imports = grep {
623             my $stash = B::svref_2object(\&{"$ns\::$_"})->GV->STASH->NAME;
624             $stash ne $ns
625             and $stash ne 'Role::Tiny'
626             and not eval { require Role::Tiny; Role::Tiny->is_role($stash) }
627             } keys %symbols;
628             }
629            
630             my %imports; @imports{@imports} = map substr("$symbols{$_}", 1), @imports;
631            
632             # But really it's better to inherit these rather than import them. :-)
633             if (keys %imports) {
634             delete @imports{qw(import unimport)};
635             }
636            
637             if (keys %imports) {
638             my @overloads = grep {
639             /^\(/ or $imports{$_} eq 'overload::nil'
640             } keys %imports;
641             delete @imports{@overloads} if @overloads;
642             }
643            
644             if (keys %imports and $] < 5.010) {
645             my @constants = grep { $imports{$_} eq 'constant::__ANON__' } keys %imports;
646             delete @imports{@constants} if @constants;
647             }
648            
649             @imports = sort keys(%imports);
650             my %sources;
651             @sources{@imports} = map {
652             B::svref_2object(\&{"$ns\::$_"})->GV->STASH->NAME;
653             } @imports;
654            
655             my %does;
656             for my $func (keys %sources) {
657             my $role = $sources{$func};
658             $does{$role} = !!eval { $ns->DOES($role) }
659             unless exists $does{$role};
660             delete $imports{$func}
661             if $does{$role};
662             }
663            
664             sort keys(%imports);
665             };
666            
667             my $_diag_dirt = sub
668             {
669             require B;
670            
671             my $ns = shift;
672             my @imports = @_;
673            
674             my %imports;
675             @imports{@imports} = map {
676             B::svref_2object(\&{"$ns\::$_"})->GV->STASH->NAME . "::$_";
677             } @imports;
678             diag explain('remaining imports: ' => \%imports);
679             };
680            
681             my $_test_or_skip = sub
682             {
683             my $ns = shift;
684             my $rv;
685             try {
686             my @imports = $ns->$_dirt;
687             $rv = ok(!@imports, "${ns} contains no imported functions")
688             or $ns->$_diag_dirt(@imports);
689             }
690             catch {
691             SKIP: {
692             skip "failed to load $ns: $_", 1;
693             fail("failed to load module");
694             };
695             };
696             return $rv;
697             };
698            
699             sub namespaces_clean
700             {
701 1     1 1 4 local $Test::Builder::Level = $Test::Builder::Level + 1;
702            
703             # special case a single namespace
704 1 50       5 return shift->$_test_or_skip if @_ == 1;
705            
706 0         0 my @namespaces = @_;
707             return &subtest(
708             sprintf("namespaces_clean: %s", join q(, ), @namespaces),
709             sub {
710 0     0   0 $_->$_test_or_skip for @namespaces;
711 0         0 done_testing;
712             },
713 0         0 );
714             }
715             }
716              
717             # Release tests...
718             {
719             sub _should_extended_test ()
720             {
721 7 50 33 7   56 $ENV{RELEASE_TESTING} || $ENV{AUTHOR_TESTING} || $ENV{EXTENDED_TESTING};
722             }
723            
724             sub _wrap
725             {
726 16     16   106 no strict qw(refs);
  16         25  
  16         19222  
727 112     112   309 my ($module, $function, %opt) = @_;
728            
729 112         102 my $code;
730 112 100       272 ($function, $code) = each(%$function)
731             if ref($function) eq q(HASH);
732            
733             *$function = sub
734             {
735 7 50 33 7   50 if ($opt{extended} and not _should_extended_test)
736             {
737 7         18 SKIP: {
738 7         7 skip 'Not running extended tests', 1;
739 0         0 pass("skipped");
740             }
741 7         1136 return 1;
742             }
743            
744 0 0       0 if (eval "require $module")
745             {
746 0   0     0 $code ||= \&{"$module\::$function"};
  0         0  
747 0 0       0 if ($opt{multi})
748             {
749 0         0 my @args = @_;
750             @_ = ($function, sub {
751 0     0   0 @_ = @args;
752 0         0 goto $code;
753 0         0 });
754 0         0 goto \&Test::More::subtest;
755             }
756             else
757             {
758 0         0 goto $code;
759             }
760             }
761            
762 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
763 0 0       0 SKIP: {
764 0         0 skip "$module only required for release testing", 1
765             unless $ENV{RELEASE_TESTING};
766 0         0 fail("$function");
767 0         0 diag("$module not installed");
768             }
769 0         0 return;
770 112         611 };
771             }
772            
773             my $_VAS = sub
774             {
775             my ($dir, $name) = @_;
776             $dir
777             = defined $dir ? $dir
778             : -d 'blib' ? 'blib'
779             : 'lib';
780             return fail("$dir does not exist, or is not a directory")
781             unless -d $dir;
782            
783             my @files = File::Find::Rule->perl_module->in($dir);
784             $name ||= "all modules in $dir have the same version number";
785            
786             local $Test::Builder::Level = $Test::Builder::Level + 1;
787            
788             &subtest($name => sub
789             {
790             my %versions;
791             for my $file (@files)
792             {
793             Test::Version::version_ok($file) or next;
794             my $info = Module::Metadata->new_from_file($file);
795             my $ver = $info->version;
796             $ver = '(undef)' unless defined $ver;
797             push @{$versions{$ver}}, $file;
798             }
799             my $ok = keys(%versions) < 2;
800             ok($ok, "single version number found");
801             if (!$ok)
802             {
803             diag("Files with version $_: @{$versions{$_}}")
804             for sort keys(%versions);
805             }
806             done_testing;
807             });
808             };
809            
810             _wrap("Test::Pod", "pod_file_ok", extended => 1);
811             _wrap("Test::Pod", "all_pod_files_ok", extended => 1, multi => 1);
812             _wrap("Test::Pod::Coverage", "pod_coverage_ok", extended => 1);
813             _wrap("Test::Pod::Coverage", "all_pod_coverage_ok", extended => 1, multi => 1);
814             _wrap("Test::Version", "version_ok", extended => 1);
815             _wrap("Test::Version", "version_all_ok", extended => 1, multi => 1);
816             _wrap("Test::Version", { "version_all_same" => $_VAS }, extended => 1);
817             }
818              
819             sub shouldnt_warn (&)
820             {
821 1     1 1 10 my @warnings = do {
822 1         3 local $Test::Builder::Level = $Test::Builder::Level + 3;
823 1         6 &Test::Warnings::warnings(@_);
824             };
825            
826 1         1953 my $old = $TODO;
827 1         3 $TODO = "shouldn't warn block";
828 1         3 local $Test::Builder::Level = $Test::Builder::Level + 1;
829 1         5 ok(scalar(@warnings)==0, "no (unexpected) warnings");
830 1         799 diag("Saw warning: $_") for @warnings;
831 1         168 $TODO = $old;
832             }
833              
834             {
835             # When passed a string of code, Benchmark::timethis() evaluates
836             # it in the caller package; need to fake that.
837             my $_do = sub {
838             my $caller = shift;
839             my $sub = shift;
840             my $doer = eval qq[ package $caller; sub { ${sub}(\@_) } ];
841             $doer->(@_);
842             };
843            
844             my $get_res = sub {
845             require Benchmark;
846             require Scalar::Util;
847             my ($caller, $times, $sub) = @_;
848             Scalar::Util::blessed($sub) && $sub->isa("Benchmark")
849             ? $sub
850             : $caller->$_do('Benchmark::timethis', $times, $sub, "", "none");
851             };
852            
853             sub is_fastest
854             {
855 0     0 1 0 require Benchmark;
856            
857 0         0 my $caller = caller;
858 0         0 my ($which, $times, $marks, $desc) = @_;
859 0 0       0 $desc = "$which is fastest" unless defined $desc;
860            
861 0         0 my @marks;
862 0         0 while (my ($name, $sub) = each %$marks)
863             {
864 0         0 my $res = $get_res->($caller, $times, $sub);
865 0         0 my ($r, $pu, $ps, $cu, $cs, $n) = @$res;
866            
867 0         0 push(@marks, {
868             name => $name,
869             res => $res,
870             n => $n,
871             s => ($pu+$ps),
872             });
873             }
874            
875 0         0 @marks = sort {$b->{n} * $a->{s} <=> $a->{n} * $b->{s}} @marks;
  0         0  
876            
877 0         0 my $ok = $marks[0]->{name} eq $which;
878            
879 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
880            
881 0         0 ok($ok, $desc);
882 0 0       0 diag("$which was not the fastest") unless $ok;
883            
884 0 0 0     0 if ($VERBOSE or not $ok)
885             {
886 0         0 foreach my $mark (@marks)
887             {
888 0         0 diag("$mark->{name} - " . Benchmark::timestr($mark->{res}));
889             }
890             }
891            
892 0         0 $ok;
893             }
894             }
895              
896             1;
897              
898             ## no Test::Tabs
899              
900             __END__