File Coverage

blib/lib/Test/Needs.pm
Criterion Covered Total %
statement 47 125 37.6
branch 33 84 39.2
condition 5 24 20.8
subroutine 10 18 55.5
pod 1 1 100.0
total 96 252 38.1


line stmt bran cond sub pod time code
1             package Test::Needs;
2 2     2   127372 use strict;
  2         12  
  2         57  
3 2     2   9 use warnings;
  2         3  
  2         51  
4 2     2   8 no warnings 'once';
  2         4  
  2         407  
5             our $VERSION = '0.002010';
6             $VERSION =~ tr/_//d;
7              
8             BEGIN {
9             *_WORK_AROUND_HINT_LEAKAGE
10             = "$]" < 5.011 && !("$]" >= 5.009004 && "$]" < 5.010001)
11 2 50 33 2   21 ? sub(){1} : sub(){0};
12             *_WORK_AROUND_BROKEN_MODULE_STATE
13             = "$]" < 5.009
14 2 50       7 ? sub(){1} : sub(){0};
15              
16             # this allows regexes to match wide characters in vstrings
17 2 50 33     1717 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   11 local %^H
29             if _WORK_AROUND_HINT_LEAKAGE;
30 9         13 my ($module) = @_;
31 9         50 (my $file = "$module.pm") =~ s{::|'}{/}g;
32 9         11 my $err;
33             {
34 9         11 local $@;
  9         12  
35 9 100       11 eval { require $file }
  9         2053  
36             or $err = $@;
37             }
38 9 100       47 if (defined $err) {
39 6         8 delete $INC{$file}
40             if _WORK_AROUND_BROKEN_MODULE_STATE;
41 6 100       66 die $err
42             unless $err =~ /\ACan't locate \Q$file\E/;
43 4         46 return !1;
44             }
45 3         16 !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   6 my ($module, $version) = @_;
61 2         3 local $@;
62 2         14 !!eval { $module->VERSION($version); 1 };
  2         53  
  1         6  
63             }
64              
65             sub _numify_version {
66 50     50   17131 for ($_[0]) {
67             return
68 50 50       666 !$_ ? 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   8659 my ($module, $version) = @$_;
  24         44  
81 24 50 100     129 $module eq 'perl' ? do {
    100          
    100          
    100          
    50          
    100          
82 15         24 $version = _numify_version($version);
83 15 100       85 "$]" < $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       105 @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   14 no strict 'refs';
  2         3  
  2         1795  
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         21 my $arg = $_;
138 18         93 map [ $_ => $arg->{$_} ], sort keys %$arg;
139             }
140 24 100   24   79 : ref eq 'ARRAY' ? do {
    100          
141 2         3 my $arg = $_;
142 2         9 map [ @{$arg}[$_*2,$_*2+1] ], 0 .. int($#$arg / 2);
  2         8  
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           my $tests = $tb->current_test;
179 0 0         if ($fail) {
180 0 0         $tb->plan(tests => 1)
181             unless $tb->$has_plan;
182 0           $tests++;
183 0           $tb->ok(0, "Test::Needs modules available");
184 0           $tb->diag($message);
185             }
186             else {
187 0           my $plan = $tb->$has_plan;
188 0 0 0       if ($plan || $tests) {
189 0 0 0       my $skips
190             = $plan && $plan ne 'no_plan' ? $plan - $tests : 1;
191             $tb->skip("Test::Needs modules not available")
192 0           for 1 .. $skips;
193 0           $tests += $skips;
194 0 0         Test::Builder->can('note') ? $tb->note($message) : print "# $message\n";
195             }
196             else {
197 0           $tb->skip_all($message);
198             }
199             }
200 0 0         $tb->done_testing($tests)
201             if Test::Builder->can('done_testing');
202 0 0 0       die bless {} => 'Test::Builder::Exception'
203             if Test::Builder->can('parent') && $tb->parent;
204             }
205             else {
206 0 0         if ($fail) {
207 0           print "1..1\n";
208 0           print "not ok 1 - Test::Needs modules available\n";
209 0           print STDERR "# $message\n";
210 0           exit 1;
211             }
212             else {
213 0           print "1..0 # SKIP $message\n";
214             }
215             }
216 0           exit 0;
217             }
218              
219             my $terminate_event;
220             sub _t2_terminate_event () {
221 0 0   0     return $terminate_event
222             if $terminate_event;
223 0           local $@;
224 0 0         $terminate_event = eval sprintf <<'END_CODE', __LINE__+2, __FILE__ or die "$@";
225             #line %d "%s"
226             package # hide
227             Test::Needs::Event::Terminate;
228             use Test2::Event ();
229             our @ISA = qw(Test2::Event);
230             sub no_display { 1 }
231             sub terminate { 0 }
232             __PACKAGE__;
233             END_CODE
234 0           (my $pm = "$terminate_event.pm") =~ s{::}{/}g;
235 0           $INC{$pm} = __FILE__;
236 0           $terminate_event;
237             }
238              
239             1;
240             __END__