File Coverage

blib/lib/Test/Needs.pm
Criterion Covered Total %
statement 46 116 39.6
branch 32 82 39.0
condition 4 25 16.0
subroutine 10 18 55.5
pod 1 1 100.0
total 93 242 38.4


line stmt bran cond sub pod time code
1             package Test::Needs;
2 1     1   55102 use strict;
  1         9  
  1         26  
3 1     1   4 use warnings;
  1         2  
  1         24  
4 1     1   4 no warnings 'once';
  1         9  
  1         153  
5             our $VERSION = '0.002_008';
6             $VERSION =~ tr/_//d;
7              
8             BEGIN {
9             *_WORK_AROUND_HINT_LEAKAGE
10             = "$]" < 5.011 && !("$]" >= 5.009004 && "$]" < 5.010001)
11 1 50 33 1   10 ? sub(){1} : sub(){0};
12             *_WORK_AROUND_BROKEN_MODULE_STATE
13             = "$]" < 5.009
14 1 50       806 ? sub(){1} : sub(){0};
15             }
16              
17             our @EXPORT = qw(test_needs);
18              
19             sub _try_require {
20 9     9   11 local %^H
21             if _WORK_AROUND_HINT_LEAKAGE;
22 9         13 my ($module) = @_;
23 9         45 (my $file = "$module.pm") =~ s{::|'}{/}g;
24 9         13 my $err;
25             {
26 9         9 local $@;
  9         9  
27 9 100       20 eval { require $file }
  9         754  
28             or $err = $@;
29             }
30 9 100       40 if (defined $err) {
31 6         7 delete $INC{$file}
32             if _WORK_AROUND_BROKEN_MODULE_STATE;
33 6 100       53 die $err
34             unless $err =~ /\ACan't locate \Q$file\E/;
35 4         25 return !1;
36             }
37 3         16 !0;
38             }
39              
40             sub _croak {
41 0     0   0 my $message = join '', @_;
42 0         0 my $i = 1;
43 0         0 while (my ($p, $f, $l) = caller($i++)) {
44             next
45 0 0       0 if $p =~ /\ATest::Needs(?:::|\z)/;
46 0         0 die "$message at $f line $l.\n";
47             }
48 0         0 die $message;
49             }
50              
51             sub _try_version {
52 2     2   5 my ($module, $version) = @_;
53 2         3 local $@;
54 2         3 !!eval { $module->VERSION($version); 1 };
  2         40  
  1         7  
55             }
56              
57             sub _numify_version {
58 15     15   25 for ($_[0]) {
59             return
60 15 50       187 !$_ ? 0
    100          
    100          
    50          
61             : /^[0-9]+\.[0-9]+$/ ? sprintf('%.6f', $_)
62             : /^v?([0-9]+(?:\.[0-9]+)+)$/
63             ? sprintf('%d.%03d%03d', ((split /\./, $1), 0, 0)[0..2])
64             : /^(\x05)(.*)$/s
65             ? sprintf('%d.%03d%03d', map ord, $1, split //, $2)
66             : _croak qq{version "$_" does not look like a number};
67             }
68             }
69              
70             sub _find_missing {
71             my @bad = map {
72 24     24   8407 my ($module, $version) = @$_;
  24         46  
73 24 50 100     128 $module eq 'perl' ? do {
    100          
    100          
    100          
    50          
    100          
74 15         23 $version = _numify_version($version);
75 15 100       82 "$]" < $version ? (sprintf "perl %s (have %.6f)", $version, $]) : ()
76             }
77             : $module =~ /^\d|[^\w:]|:::|[^:]:[^:]|^:|:$/
78             ? _croak sprintf qq{"%s" does not look like a module name}, $module
79             : _try_require($module) ? (
80             defined $version && !_try_version($module, $version)
81             ? "$module $version (have ".(defined $module->VERSION ? $module->VERSION : 'undef').')'
82             : ()
83             )
84             : $version ? "$module $version"
85             : $module;
86             }
87             _pairs(@_);
88 22 100       103 @bad ? "Need " . join(', ', @bad) : undef;
89             }
90              
91             sub import {
92 0     0   0 my $class = shift;
93 0         0 my $target = caller;
94 0 0       0 if (@_) {
95 0   0     0 local $Test::Builder::Level = ($Test::Builder::Level||0) + 1;
96 0         0 test_needs(@_);
97             }
98 1     1   7 no strict 'refs';
  1         1  
  1         822  
99 0         0 *{"${target}::$_"} = \&{"${class}::$_"}
  0         0  
100 0         0 for @{"${class}::EXPORT"};
  0         0  
101             }
102              
103             sub test_needs {
104 0     0 1 0 my $missing = _find_missing(@_);
105 0   0     0 local $Test::Builder::Level = ($Test::Builder::Level||0) + 1;
106 0 0       0 if ($missing) {
107 0 0       0 if ($ENV{RELEASE_TESTING}) {
108 0         0 _fail("$missing due to RELEASE_TESTING");
109             }
110             else {
111 0         0 _skip($missing);
112             }
113             }
114              
115             }
116              
117 0     0   0 sub _skip { _fail_or_skip($_[0], 0) }
118 0     0   0 sub _fail { _fail_or_skip($_[0], 1) }
119              
120             sub _pairs {
121             map +(
122             ref eq 'HASH' ? do {
123 18         22 my $arg = $_;
124 18         90 map [ $_ => $arg->{$_} ], sort keys %$arg;
125             }
126 24 100   24   77 : ref eq 'ARRAY' ? do {
    100          
127 2         4 my $arg = $_;
128 2         10 map [ @{$arg}[$_*2,$_*2+1] ], 0 .. int($#$arg / 2);
  2         7  
129             }
130             : [ $_ ]
131             ), @_;
132             }
133              
134             sub _fail_or_skip {
135 0     0     my ($message, $fail) = @_;
136 0 0         if ($INC{'Test2/API.pm'}) {
    0          
137 0           my $ctx = Test2::API::context();
138 0           my $hub = $ctx->hub;
139 0 0         if ($fail) {
140 0           $ctx->ok(0, "Test::Needs modules available", [$message]);
141             }
142             else {
143 0           my $plan = $hub->plan;
144 0           my $tests = $hub->count;
145 0 0 0       if ($plan || $tests) {
146 0 0 0       my $skips
147             = $plan && $plan ne 'NO PLAN' ? $plan - $tests : 1;
148 0           $ctx->skip("Test::Needs modules not available") for 1 .. $skips;
149 0           $ctx->note($message);
150             }
151             else {
152 0           $ctx->plan(0, 'SKIP', $message);
153             }
154             }
155 0           $ctx->done_testing;
156 0 0         $ctx->release if $Test2::API::VERSION < 1.302053;
157 0           $ctx->send_event('+'._t2_terminate_event());
158             }
159             elsif ($INC{'Test/Builder.pm'}) {
160 0           my $tb = Test::Builder->new;
161             my $has_plan = Test::Builder->can('has_plan') ? 'has_plan'
162 0 0   0     : sub { $_[0]->expected_tests || eval { $_[0]->current_test($_[0]->current_test); 'no_plan' } };
  0 0          
  0            
  0            
163 0 0         if ($fail) {
164 0 0         $tb->plan(tests => 1)
165             unless $tb->$has_plan;
166 0           $tb->ok(0, "Test::Needs modules available");
167 0           $tb->diag($message);
168             }
169             else {
170 0           my $plan = $tb->$has_plan;
171 0           my $tests = $tb->current_test;
172 0 0 0       if ($plan || $tests) {
173 0 0 0       my $skips
174             = $plan && $plan ne 'no_plan' ? $plan - $tests : 1;
175             $tb->skip("Test::Needs modules not available")
176 0           for 1 .. $skips;
177 0 0         Test::Builder->can('note') ? $tb->note($message) : print "# $message\n";
178             }
179             else {
180 0           $tb->skip_all($message);
181             }
182             }
183 0 0         $tb->done_testing
184             if Test::Builder->can('done_testing');
185 0 0 0       die bless {} => 'Test::Builder::Exception'
186             if Test::Builder->can('parent') && $tb->parent;
187             }
188             else {
189 0 0         if ($fail) {
190 0           print "1..1\n";
191 0           print "not ok 1 - Test::Needs modules available\n";
192 0           print STDERR "# $message\n";
193 0           exit 1;
194             }
195             else {
196 0           print "1..0 # SKIP $message\n";
197             }
198             }
199 0           exit 0;
200             }
201              
202             my $terminate_event;
203             sub _t2_terminate_event () {
204 0 0   0     return $terminate_event
205             if $terminate_event;
206 0           local $@;
207 0 0         $terminate_event = eval sprintf <<'END_CODE', __LINE__+2, __FILE__ or die "$@";
208             #line %d "%s"
209             package # hide
210             Test::Needs::Event::Terminate;
211             use Test2::Event ();
212             our @ISA = qw(Test2::Event);
213             sub no_display { 1 }
214             sub terminate { 0 }
215             __PACKAGE__;
216             END_CODE
217 0           (my $pm = "$terminate_event.pm") =~ s{::}{/}g;
218 0           $INC{$pm} = __FILE__;
219 0           $terminate_event;
220             }
221              
222             1;
223             __END__