File Coverage

blib/lib/Test/Needs.pm
Criterion Covered Total %
statement 47 123 38.2
branch 33 84 39.2
condition 5 24 20.8
subroutine 10 18 55.5
pod 1 1 100.0
total 96 250 38.4


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