File Coverage

blib/lib/Test2/Tools/LoadModule.pm
Criterion Covered Total %
statement 209 213 98.1
branch 65 72 90.2
condition 27 30 90.0
subroutine 44 44 100.0
pod 7 7 100.0
total 352 366 96.1


line stmt bran cond sub pod time code
1             package Test2::Tools::LoadModule;
2              
3 10     10   2090326 use 5.008001;
  10         98  
4              
5 10     10   59 use strict;
  10         20  
  10         283  
6 10     10   57 use warnings;
  10         31  
  10         453  
7              
8             # OK, the following is probably paranoia. But if Perl 7 decides to
9             # change this particular default I'm ready. Unless they eliminate $].
10 10     10   6703 no if $] ge '5.020', feature => qw{ signatures };
  10         141  
  10         57  
11              
12 10     10   1635 use Carp;
  10         23  
  10         653  
13 10     10   65 use Exporter 5.567; # Comes with Perl 5.8.1.
  10         163  
  10         345  
14             # use File::Find ();
15             # use File::Spec ();
16             # use Getopt::Long 2.34; # Comes with Perl 5.8.1.
17 10     10   57 use Test2::API ();
  10         22  
  10         132  
18 10     10   46 use Test2::Util ();
  10         30  
  10         288  
19              
20 10     10   50 use base qw{ Exporter };
  10         18  
  10         2791  
21              
22             our $VERSION = '0.007';
23             $VERSION =~ s/ _ //smxg;
24              
25             {
26             my @test2 = qw{
27             all_modules_tried_ok
28             clear_modules_tried
29             load_module_ok
30             load_module_or_skip
31             load_module_or_skip_all
32             };
33              
34             my @more = qw{
35             require_ok
36             use_ok
37             };
38              
39             my @private = qw{
40             __build_load_eval
41             __get_hint_hash
42             DEFAULT_LOAD_ERROR
43             ERR_IMPORT_BAD
44             ERR_MODULE_UNDEF
45             ERR_OPTION_BAD
46             ERR_SKIP_NUM_BAD
47             ERR_VERSION_BAD
48             HINTS_AVAILABLE
49             TEST_MORE_ERROR_CONTEXT
50             TEST_MORE_LOAD_ERROR
51             };
52              
53             our @EXPORT_OK = ( @test2, @more, @private );
54              
55             our %EXPORT_TAGS = (
56             all => [ @test2, @more ],
57             default => \@test2,
58             more => \@more,
59             private => \@private,
60             test2 => \@test2,
61             );
62              
63             our @EXPORT = @{ $EXPORT_TAGS{default} }; ## no critic (ProhibitAutomaticExportation)
64             }
65              
66 10     10   79 use constant ARRAY_REF => ref [];
  10         23  
  10         971  
67 10     10   68 use constant HASH_REF => ref {};
  10         21  
  10         608  
68              
69 10     10   113 use constant CALLER_HINT_HASH => 10;
  10         21  
  10         586  
70              
71 10     10   70 use constant DEFAULT_LOAD_ERROR => '%s';
  10         20  
  10         569  
72              
73 10         515 use constant ERR_IMPORT_BAD =>
74 10     10   73 'Import list must be an array reference, or undef';
  10         28  
75 10     10   60 use constant ERR_MODULE_UNDEF => 'Module name must be defined';
  10         30  
  10         531  
76 10     10   62 use constant ERR_OPTION_BAD => 'Bad option';
  10         37  
  10         527  
77 10         539 use constant ERR_SKIP_NUM_BAD =>
78 10     10   60 'Number of skipped tests must be an unsigned integer';
  10         19  
79 10     10   64 use constant ERR_VERSION_BAD => q/Version '%s' is invalid/;
  10         20  
  10         629  
80              
81 10     10   63 use constant HINTS_AVAILABLE => $] ge '5.010';
  10         18  
  10         1413  
82              
83             # The following cribbed shamelessly from version::regex 0.9924,
84             # after being munged to suit by tools/version_regex 0.000_010.
85             # This technical debt is incurred to avoid having to require a version
86             # of the version module large enough to export the is_lax() subroutine.
87 10         632 use constant LAX_VERSION => qr/(?x: (?x:
88             v (?-x:[0-9]+) (?-x: (?-x:\.[0-9]+)+ (?-x:_[0-9]+)? )?
89             |
90             (?-x:[0-9]+)? (?-x:\.[0-9]+){2,} (?-x:_[0-9]+)?
91             ) | (?x: (?-x:[0-9]+) (?-x: (?-x:\.[0-9]+) | \. )? (?-x:_[0-9]+)?
92             |
93             (?-x:\.[0-9]+) (?-x:_[0-9]+)?
94 10     10   73 ) )/;
  10         22  
95              
96 10     10   64 use constant TEST_MORE_ERROR_CONTEXT => q/Tried to %s '%s'./;
  10         29  
  10         528  
97 10     10   162 use constant TEST_MORE_LOAD_ERROR => 'Error: %s';
  10         20  
  10         669  
98 10         8496 use constant TEST_MORE_OPT => {
99             load_error => TEST_MORE_LOAD_ERROR,
100             require => 1,
101 10     10   81 };
  10         23  
102              
103             {
104             my %module_tried;
105              
106             sub load_module_ok (@) { ## no critic (RequireArgUnpacking)
107 16     16 1 52945 my @arg = _validate_args( 0, @_ );
108              
109             # We do this now in case _load_module_ok() throws an uncaught
110             # exception, just so we have SOME record we tried.
111 12         37 $module_tried{ $arg[1] } = undef;
112              
113 12         40 my $ctx = Test2::API::context();
114              
115 12         1065 my $rslt = _load_module_ok( @arg );
116              
117 12         2182 $module_tried{ $arg[1] } = $rslt;
118              
119 12         44 $ctx->release();
120              
121 12         308 return $rslt;
122             }
123              
124             sub all_modules_tried_ok (@) {
125 3     3 1 6974 my @where = @_;
126             @where
127 3 50       13 or @where = ( 'blib/lib', 'blib/arch' );
128              
129 3         19 require File::Find;
130 3         11 require File::Spec;
131              
132 3         6 my @not_tried;
133 3         6 foreach my $d ( @where ) {
134             File::Find::find( sub {
135 15 100   15   981 m/ [.] pm \z /smx
136             or return;
137 3         341 my ( undef, $dir, $name ) = File::Spec->splitpath(
138             File::Spec->abs2rel( $File::Find::name, $d ) );
139 3         19 my @dir = File::Spec->splitdir( $dir );
140 3 50       10 $dir[-1]
141             or pop @dir;
142 3         18 ( my $module = join '::', @dir, $name ) =~ s/ [.] pm //smx;
143 3 100       106 exists $module_tried{$module}
144             or push @not_tried, $module;
145 6         413 }, $d );
146             }
147              
148 3 100       18 if ( @not_tried ) {
149              
150 2         7 my $ctx = Test2::API::context();
151              
152 2         243 $ctx->fail( "Module $_ not tried" ) for sort @not_tried;
153              
154 2         384 $ctx->release();
155              
156 2         59 return 0;
157             }
158             }
159              
160             sub clear_modules_tried () {
161 1     1 1 633 %module_tried = ();
162 1         3 return;
163             }
164             }
165              
166             sub _load_module_ok {
167 21     21   81 my ( $opt, $module, $version, $import, $name, @diag ) = @_;
168              
169 21         49 local $@ = undef;
170              
171 21         86 my $eval = __build_load_eval( $opt, $module, $version, $import );
172              
173 21 100       71 defined $name
174             or $name = $eval;
175              
176 21         57 my $ctx = Test2::API::context();
177              
178 21 100       1376 _eval_in_pkg( $eval, $ctx->trace()->call() )
179             and return $ctx->pass_and_release( $name );
180              
181 10         40 chomp $@;
182              
183             $opt->{load_error}
184 10 100       74 and push @diag, sprintf $opt->{load_error}, $@;
185              
186 10         64 return $ctx->fail_and_release( $name, @diag );
187             }
188              
189             sub load_module_or_skip (@) { ## no critic (RequireArgUnpacking,RequireFinalReturn)
190 11     11 1 31850 my ( $opt, $module, $version, $import, $name, $num ) = _validate_args( 5, @_ );
191              
192 6 100       18 _load_module( $opt, $module, $version, $import )
193             and return;
194              
195 4 50       24 defined $name
196             or $name = sprintf 'Unable to %s',
197             __build_load_eval( $opt, $module, $version, $import );
198 4 100 100     141 defined $num
199             and $num =~ m/ [^0-9] /smx
200             and croak ERR_SKIP_NUM_BAD;
201              
202 3         12 my $ctx = Test2::API::context();
203 3   100     282 $num ||= 1;
204 3         16 $ctx->skip( 'skipped test', $name ) for 1 .. $num;
205              
206 3         1080 $ctx->release();
207 10     10   79 no warnings qw{ exiting };
  10         24  
  10         18480  
208 3         81 last SKIP;
209             }
210              
211             sub load_module_or_skip_all (@) { ## no critic (RequireArgUnpacking)
212 10     10 1 28359 my ( $opt, $module, $version, $import, $name ) = _validate_args( 4, @_ );
213              
214 5 100       23 _load_module( $opt, $module, $version, $import )
215             and return;
216              
217 3 50       21 defined $name
218             or $name = sprintf 'Unable to %s',
219             __build_load_eval( $opt, $module, $version, $import );
220              
221 3         15 my $ctx = Test2::API::context();
222 3         339 $ctx->plan( 0, SKIP => $name );
223 0         0 $ctx->release();
224              
225 0         0 return;
226             }
227              
228             sub _load_module {
229 11     11   32 my ( $opt, $module, $version, $import ) = @_;
230              
231 11         23 local $@ = undef;
232              
233 11         35 my $eval = __build_load_eval( $opt, $module, $version, $import );
234              
235 11         36 return _eval_in_pkg( $eval, _get_call_info() )
236             }
237              
238             {
239             my $psr;
240              
241             # Because we want to work with Perl 5.8.1 we are limited to
242             # Getopt::Long 2.34, and therefore getoptions(). So we expect the
243             # arguments to be in a suitably-localized @ARGV. The optional
244             # argument is a reference to a hash into which we place the option
245             # values. If omitted, we create a reference to a new hash. Either
246             # way the hash reference gets returned.
247             sub _parse_opts {
248 50     50   128 my ( $opt ) = @_;
249 50   50     172 $opt ||= {};
250             {
251 50 100       93 unless ( $psr ) {
  50         165  
252 9         7321 require Getopt::Long;
253 9         95763 Getopt::Long->VERSION( 2.34 );
254 9         249 $psr = Getopt::Long::Parser->new();
255 9         269 $psr->configure( qw{ posix_default } );
256             }
257              
258 50         748 my $opt_err;
259 50     3   366 local $SIG{__WARN__} = sub { $opt_err = $_[0] };
  3         796  
260             $psr->getoptions( $opt, qw{
261             load_error=s
262             require|req!
263             },
264 50 100       244 ) or do {
265 3 50       190 if ( defined $opt_err ) {
266 3         6 chomp $opt_err;
267 3         343 croak $opt_err;
268             } else {
269 0         0 croak ERR_OPTION_BAD;
270             }
271             };
272             }
273 47 100       15814 if ( $opt->{load_error} ) {
274             $opt->{load_error} =~ m/ ( %+ ) [ #0+-]* [0-9]* s /smx
275             and length( $1 ) % 2
276 34 100 66     382 or $opt->{load_error} = '%s';
277             }
278 47         131 return $opt;
279             }
280             }
281              
282             sub import { ## no critic (RequireArgUnpacking,ProhibitBuiltinHomonyms)
283 18     18   11508 ( my $class, local @ARGV ) = @_; # See _parse_opts
284 18 100       89 if ( @ARGV ) {
285 13         27 my %opt;
286 13         48 _parse_opts( \%opt );
287 13         26 if ( HINTS_AVAILABLE ) {
288 13         53 $^H{ _make_pragma_key() } = $opt{$_} for keys %opt;
289             } else {
290             keys %opt
291             and carp "Import options ignored under Perl $]";
292             }
293             @ARGV
294 13 100       5231 or return;
295             }
296 14         6138 return $class->export_to_level( 1, $class, @ARGV );
297             }
298              
299             sub require_ok ($) {
300 4     4 1 23482 my ( $module ) = @_;
301 4 100       97 defined $module
302             or croak ERR_MODULE_UNDEF;
303 3         9 my $ctx = Test2::API::context();
304 3         271 my $rslt = _load_module_ok( TEST_MORE_OPT,
305             $module, undef, undef, "require $module;",
306             sprintf( TEST_MORE_ERROR_CONTEXT, require => $module ),
307             );
308 3         572 $ctx->release();
309 3         77 return $rslt;
310             }
311              
312             sub use_ok ($;@) {
313 7     7 1 22442 my ( $module, @arg ) = @_;
314 7 100       114 defined $module
315             or croak ERR_MODULE_UNDEF;
316 6 100 100     72 my $version = ( defined $arg[0] && $arg[0] =~ LAX_VERSION ) ?
317             shift @arg : undef;
318 6         23 my $ctx = Test2::API::context();
319 6         915 my $rslt = _load_module_ok( TEST_MORE_OPT,
320             $module, $version, \@arg, undef,
321             sprintf( TEST_MORE_ERROR_CONTEXT, use => $module ),
322             );
323 6         1231 $ctx->release();
324 6         159 return $rslt;
325             }
326              
327             sub _make_pragma_key {
328 4     4   28 return join '', __PACKAGE__, '/', $_;
329             }
330              
331             sub _caller_class {
332 4     4   9 my ( $lvl ) = @_;
333 4   50     52 my ( $pkg ) = caller( $lvl || 1 );
334 4 100       410 my $code = $pkg->can( 'CLASS' )
335             or croak ERR_MODULE_UNDEF;
336 1         6 return $code->();
337             }
338              
339             {
340              
341             my %default_hint = (
342             load_error => DEFAULT_LOAD_ERROR,
343             );
344              
345             sub __get_hint_hash {
346 42     42   726 my ( $level ) = @_;
347 42   100     148 $level ||= 0;
348 42         290 my $hint_hash = ( caller( $level ) )[ CALLER_HINT_HASH ];
349 42         190 my %rslt = %default_hint;
350 42         82 if ( HINTS_AVAILABLE ) {
351 42         74 foreach ( keys %{ $hint_hash } ) {
  42         155  
352 4         37 my ( $hint_pkg, $hint_key ) = split qr< / >smx;
353             __PACKAGE__ eq $hint_pkg
354 4 50       25 and $rslt{$hint_key} = $hint_hash->{$_};
355             }
356             }
357 42         201 return \%rslt;
358             }
359             }
360              
361             sub __build_load_eval {
362 73     73   30347 my @arg = @_;
363 73 100       293 HASH_REF eq ref $arg[0]
364             or unshift @arg, {};
365 73         199 my ( $opt, $module, $version, $import ) = @arg;
366 73         204 my @eval = "use $module";
367              
368 73 100       204 defined $version
369             and push @eval, $version;
370              
371 73 100 100     429 if ( $import && @{ $import } ) {
  29 100 100     145  
372 19         39 push @eval, "qw{ @{ $import } }";
  19         64  
373             } elsif ( defined $import xor not $opt->{require} ) {
374             # Do nothing.
375             } else {
376 12         31 push @eval, '()';
377             }
378              
379 73         476 return "@eval;";
380             }
381              
382             sub _validate_args {
383 37     37   150 ( my $max_arg, local @ARGV ) = @_;
384 37         108 my $opt = _parse_opts( __get_hint_hash( 2 ) );
385              
386 34 100 100     163 if ( $max_arg && @ARGV > $max_arg ) {
387 2         18 ( my $sub_name = ( caller 1 )[3] ) =~ s/ .* :: //smx;
388 2         241 croak sprintf '%s() takes at most %d arguments', $sub_name, $max_arg;
389             }
390              
391 32         111 my ( $module, $version, $import, $name, @diag ) = @ARGV;
392              
393 32 100       99 defined $module
394             or $module = _caller_class( 2 );
395              
396 29 100       92 if ( defined $version ) {
397 7 100       365 $version =~ LAX_VERSION
398             or croak sprintf ERR_VERSION_BAD, $version;
399             }
400              
401 26 100 100     484 not defined $import
402             or ARRAY_REF eq ref $import
403             or croak ERR_IMPORT_BAD;
404              
405 23         141 return ( $opt, $module, $version, $import, $name, @diag );
406             }
407              
408             sub _eval_in_pkg {
409 32     32   358 my ( $eval, $pkg, $file, $line ) = @_;
410              
411 32         153 my $e = <<"EOD";
412             package $pkg;
413             #line $line "$file"
414             $eval;
415             1;
416             EOD
417              
418             # We need the stringy eval() so we can mess with Perl's concept of
419             # what the current file and line number are for the purpose of
420             # formatting the exception, AND as a convenience to get symbols
421             # imported.
422 32         3654 my $rslt = eval $e; ## no critic (ProhibitStringyEval)
423              
424 32         6319 return $rslt;
425             }
426              
427             sub _get_call_info {
428 11     11   21 my $lvl = 0;
429 11         132 while ( my @info = caller $lvl++ ) {
430 33 100       223 __PACKAGE__ eq $info[0]
431             and next;
432 11 50       76 $info[1] =~ m/ \A [(] eval \b /smx # )
433             or return @info;
434             }
435 0           confess 'Bug - Unable to determine caller';
436             }
437              
438             1;
439              
440             __END__