File Coverage

inc/My/Module/Test.pm
Criterion Covered Total %
statement 256 285 89.8
branch 92 126 73.0
condition 33 68 48.5
subroutine 41 42 97.6
pod 22 24 91.6
total 444 545 81.4


line stmt bran cond sub pod time code
1             package My::Module::Test;
2              
3 5     5   262766 use strict;
  5         41  
  5         144  
4 5     5   30 use warnings;
  5         8  
  5         137  
5              
6 5     5   24 use Exporter;
  5         12  
  5         365  
7              
8             our @ISA = ( qw{ Exporter } );
9              
10 5     5   2781 use PPIx::Regexp;
  5         18  
  5         207  
11 5     5   30 use PPIx::Regexp::Constant qw{ INFINITY };
  5         10  
  5         222  
12 5     5   2590 use PPIx::Regexp::Dumper;
  5         13  
  5         164  
13 5     5   38 use PPIx::Regexp::Element;
  5         8  
  5         123  
14 5     5   26 use PPIx::Regexp::Tokenizer;
  5         16  
  5         120  
15 5     5   81 use PPIx::Regexp::Util qw{ __choose_tokenizer_class __instance };
  5         11  
  5         333  
16 5     5   34 use Scalar::Util qw{ looks_like_number refaddr };
  5         10  
  5         248  
17 5     5   2453 use Test::More 0.88;
  5         194266  
  5         42  
18              
19             our $VERSION = '0.088';
20              
21 5     5   1542 use constant ARRAY_REF => ref [];
  5         27  
  5         20105  
22              
23             our @EXPORT_OK = qw{
24             builder
25             cache_count
26             choose
27             klass
28             cmp_ok
29             content
30             count
31             diag
32             different
33             done_testing
34             dump_result
35             equals
36             error
37             fail
38             false
39             finis
40             format_want
41             invocant
42             is
43             navigate
44             note
45             ok
46             parse
47             pass
48             plan
49             ppi
50             raw_width
51             result
52             replace_characters
53             skip
54             tokenize
55             true
56             value
57             width
58             INFINITY
59             };
60              
61             our @EXPORT = @EXPORT_OK; ## no critic (ProhibitAutomaticExportation)
62              
63             push @EXPORT_OK, qw{ __quote };
64              
65             my (
66             $initial_class, # For static methods; set by parse() or tokenize()
67             $kind, # of thing; set by parse() or tokenize()
68             $nav, # Navigation used to get to current object, as a
69             # string.
70             $obj, # Current object:
71             # PPIx::Regexp::Tokenizer if set by tokenize(),
72             # PPIx::Regexp if set by parse(), or
73             # PPIx::Regexp::Element if set by navigate().
74             $parse, # Result of parse:
75             # array ref if set by tokenize(), or
76             # PPIx::Regexp object if set by parse()
77             %replace_characters, # Troublesome characters replaced in output
78             # before testing
79             $result, # Operation result.
80             );
81              
82             sub builder {
83 1     1 1 139 return Test::More->builder();
84             }
85              
86             sub cache_count {
87 8     8 1 1146 my ( $expect ) = @_;
88 8 100       49 defined $expect or $expect = 0;
89 8         19 $obj = undef;
90 8         19 $parse = undef;
91 8         41 _pause();
92 8         156 $result = PPIx::Regexp->__cache_size();
93             # cperl does not seem to like goto &xxx; it throws a deep recursion
94             # error if you do it enough times.
95 8         56 $Test::Builder::Level = $Test::Builder::Level + 1;
96 8         102 return is( $result, $expect,
97             "Should be $expect leftover cache contents" );
98             }
99              
100             sub choose {
101 4683     4683 1 19811066 my @args = @_;
102 4683         9205 $obj = $parse;
103 4683         10522 return navigate( @args );
104             }
105              
106             sub klass {
107 4381     4381 1 904949 my ( $class ) = @_;
108 4381   66     12135 $result = ref $obj || $obj;
109             # cperl does not seem to like goto &xxx; it throws a deep recursion
110             # error if you do it enough times.
111 4381         6032 $Test::Builder::Level = $Test::Builder::Level + 1;
112 4381 100       7488 if ( defined $class ) {
113 4319 50       15027 my $rslt = isa_ok( $obj, $class, "$kind $nav" )
114             or diag " Instead, $kind $nav isa $result";
115 4319         6756385 return $rslt;
116             } else {
117 62   50     599 return is( ref $obj || undef, $class, "Class of $kind $nav" );
118             }
119             }
120              
121             sub content { ## no critic (RequireArgUnpacking)
122             # For some reason cperl seems to have no problem with this
123 3591     3591 1 230668 unshift @_, 'content';
124 3591         11736 goto &_method_result;
125             }
126              
127             sub count {
128 1382     1382 1 323854 my ( @args ) = @_;
129 1382         2900 my $expect = pop @args;
130             # cperl does not seem to like goto &xxx; it throws a deep recursion
131             # error if you do it enough times.
132 1382         2463 $Test::Builder::Level = $Test::Builder::Level + 1;
133 1382 100       7358 if ( ARRAY_REF eq ref $parse ) {
    100          
    50          
134 202         365 $result = @{ $parse };
  202         487  
135 202         979 return is( $result, $expect, "Expect $expect tokens" );
136             } elsif ( ARRAY_REF eq ref $obj ) {
137 559         806 $result = @{ $obj };
  559         933  
138 559         2269 return is( $result, $expect, "Expect $expect tokens" );
139             } elsif ( $obj->can( 'children' ) ) {
140 621         2175 $result = $obj->children();
141 621         2744 return is( $result, $expect, "Expect $expect children" );
142             } else {
143 0         0 $result = $obj->can( 'children' );
144 0         0 return ok( $result, ref( $obj ) . "->can( 'children')" );
145             }
146             }
147              
148             sub different {
149 1     1 1 9 my @args = @_;
150 1 50       5 @args < 3 and unshift @args, $obj;
151 1         3 my ( $left, $right, $name ) = @args;
152             # cperl does not seem to like goto &xxx; it throws a deep recursion
153             # error if you do it enough times.
154 1         3 $Test::Builder::Level = $Test::Builder::Level + 1;
155 1 50 33     20 if ( ! defined $left && ! defined $right ) {
    50 33        
    50 33        
    0 0        
    0 0        
156 0         0 return ok( undef, $name );
157             } elsif ( ! defined $left || ! defined $right ) {
158 0         0 return ok( 1, $name );
159             } elsif ( ref $left && ref $right ) {
160 1         10 return ok( refaddr( $left ) != refaddr( $right ), $name );
161             } elsif ( ref $left || ref $right ) {
162 0         0 return ok( 1, $name );
163             } elsif ( looks_like_number( $left ) && looks_like_number( $right ) ) {
164 0         0 return ok( $left != $right, $name );
165             } else {
166 0         0 return ok( $left ne $right, $name );
167             }
168             }
169              
170             sub dump_result {
171 5     5 1 4945 my ( $opt, @args ) = _parse_constructor_args( { test => 1 }, @_ );
172 5 50 0     14 if ( $opt->{test} ) {
    0          
    0          
    0          
173 5         14 my ( $expect, $name ) = splice @args, -2;
174 5         29 my $got = PPIx::Regexp::Dumper->new( $obj, @args )->string();
175             # cperl does not seem to like goto &xxx; it throws a deep
176             # recursion error if you do it enough times.
177 5         40 $Test::Builder::Level = $Test::Builder::Level + 1;
178 5         17 return is( $got, $expect, $name );
179             } elsif ( __instance( $result, 'PPIx::Regexp::Tokenizer' ) ||
180             __instance( $result, 'PPIx::Regexp::Element' ) ) {
181 0         0 diag( PPIx::Regexp::Dumper->new( $obj, @args )->string() );
182 0         0 } elsif ( eval { require YAML; 1; } ) {
  0         0  
183 0         0 diag( "Result dump:\n", YAML::Dump( $result ) );
184 0         0 } elsif ( eval { require Data::Dumper; 1 } ) {
  0         0  
185 0         0 diag( "Result dump:\n", Data::Dumper::Dumper( $result ) );
186             } else {
187 0         0 diag( "Result dump unavailable.\n" );
188             }
189 0         0 return;
190             }
191              
192             sub equals {
193 19     19 1 115 my @args = @_;
194 19 100       56 @args < 3 and unshift @args, $obj;
195 19         34 my ( $left, $right, $name ) = @args;
196             # cperl does not seem to like goto &xxx; it throws a deep recursion
197             # error if you do it enough times.
198 19         27 $Test::Builder::Level = $Test::Builder::Level + 1;
199 19 100 66     106 if ( ! defined $left && ! defined $right ) {
    50 33        
    50 33        
    0 0        
    0 0        
200 11         30 return ok( 1, $name );
201             } elsif ( ! defined $left || ! defined $right ) {
202 0         0 return ok( undef, $name );
203             } elsif ( ref $left && ref $right ) {
204 8         31 return ok( refaddr( $left ) == refaddr( $right ), $name );
205             } elsif ( ref $left || ref $right ) {
206 0         0 return ok( undef, $name );
207             } elsif ( looks_like_number( $left ) && looks_like_number( $right ) ) {
208 0         0 return ok( $left == $right, $name );
209             } else {
210 0         0 return ok( $left eq $right, $name );
211             }
212             }
213              
214             sub error { ## no critic (RequireArgUnpacking)
215 19     19 0 80807 unshift @_, 'error';
216 19         73 goto &_method_result;
217             }
218              
219             sub false {
220 52     52 1 45325 my ( $method, $args ) = @_;
221 52 100       179 ARRAY_REF eq ref $args
222             or $args = [ $args ];
223 52         111 my $class = ref $obj;
224             # cperl does not seem to like goto &xxx; it throws a deep recursion
225             # error if you do it enough times.
226 52         79 $Test::Builder::Level = $Test::Builder::Level + 1;
227 52 50       273 if ( $obj->can( $method ) ) {
228 52         66 $result = $obj->$method( @{ $args } );
  52         216  
229 52         148 my $fmtd = _format_args( $args );
230 52         255 return ok( ! $result, "$class->$method$fmtd is false" );
231             } else {
232 0         0 $result = undef;
233 0         0 return ok( undef, "$class->$method() exists" );
234             }
235             }
236              
237             sub finis {
238 2     2 1 10120 $obj = $parse = $result = undef;
239 2         10 _pause();
240 2         45 $result = PPIx::Regexp::Element->__parent_keys();
241             # cperl does not seem to like goto &xxx; it throws a deep recursion
242             # error if you do it enough times.
243 2         13 $Test::Builder::Level = $Test::Builder::Level + 1;
244 2         19 return is( $result, 0, 'Should be no leftover objects' );
245             }
246              
247             sub format_want {
248 1078     1078 1 1895 my ( $want ) = @_;
249 1078 100       2356 return _format_args( $want, bare => ref $want ? 0 : 1 );
250             }
251              
252             sub invocant {
253 0     0 1 0 return $obj;
254             }
255              
256             {
257              
258             my %array = map { $_ => 1 } qw{
259             children delimiters finish schildren start tokens type
260             };
261              
262             sub navigate {
263 4738     4738 1 84564 my @args = @_;
264 4738         5817 my $scalar = 1;
265             @args > 1
266             and ARRAY_REF eq ref $args[-1]
267 595         3530 and @{ $args[-1] } == 0
268 4738 100 100     22953 and $array{$args[-2]}
      100        
      100        
269             and $scalar = 0;
270 4738         7671 my @nav = ();
271 4738         9370 while ( @args ) {
272 8443 100       19710 if ( __instance( $args[0], 'PPIx::Regexp::Element' ) ) {
    100          
273 9         36 $obj = shift @args;
274             } elsif ( ARRAY_REF eq ref $obj ) {
275 1562         2835 my $inx = shift @args;
276 1562         2518 push @nav, $inx;
277 1562         3840 $obj = $obj->[$inx];
278             } else {
279 6872         10073 my $method = shift @args;
280 6872         8259 my $args = shift @args;
281 6872 100       14767 ARRAY_REF eq ref $args
282             or $args = [ $args ];
283 6872         10590 push @nav, $method, $args;
284 6872 50       22948 $obj->can( $method ) or return;
285 6872 100 100     18151 if ( @args || $scalar ) {
286 6316 100       6535 $obj = $obj->$method( @{ $args } ) or return;
  6316         17165  
287             } else {
288 556         817 $obj = [ $obj->$method( @{ $args } ) ];
  556         2315  
289             }
290             }
291             }
292 4665         9665 $nav = __quote( @nav );
293 4665         35781 $nav =~ s/ ' ( \w+ ) ' , /$1 =>/smxg;
294 4665         12267 $nav =~ s/ \[ \s+ \] /[]/smxg;
295 4665         7012 $result = $obj;
296 4665         14043 return $obj;
297             }
298              
299             }
300              
301             sub parse { ## no critic (RequireArgUnpacking)
302 305     305 1 1031836 my ( $opt, $regexp, @args ) = _parse_constructor_args(
303             { test => 1 }, @_ );
304 305         788 $initial_class = 'PPIx::Regexp';
305 305         618 $kind = 'element';
306 305         1833 $result = $obj = $parse = PPIx::Regexp->new( $regexp, @args );
307 305         1187 $nav = '';
308 305 100       1082 $opt->{test} or return;
309             # cperl does not seem to like goto &xxx; it throws a deep recursion
310             # error if you do it enough times.
311 304         763 $Test::Builder::Level = $Test::Builder::Level + 1;
312 304         802 return isa_ok( $parse, 'PPIx::Regexp',
313             _replace_characters( $regexp ) );
314             }
315              
316             sub ppi { ## no critic (RequireArgUnpacking)
317 1     1 1 1724 my @args = @_;
318 1         3 my $expect = pop @args;
319 1         2 $result = undef;
320 1 50       8 defined $obj and $result = $obj->ppi()->content();
321 1         19 my $safe;
322 1 50       3 if ( defined $result ) {
323 1         5 ($safe = $result) =~ s/([\\'])/\\$1/smxg;
324             } else {
325 0         0 $safe = 'undef';
326             }
327             # cperl does not seem to like goto &xxx; it throws a deep recursion
328             # error if you do it enough times.
329 1         3 $Test::Builder::Level = $Test::Builder::Level + 1;
330 1         6 return is( $result, $expect, "$kind $nav ppi() content '$safe'" );
331             }
332              
333             sub raw_width {
334 282     282 1 465222 my ( $min, $max, $name ) = @_;
335 282 50       1532 defined $name
336             or $name = sprintf q<%s '%s'>, ref $obj, $obj->content();
337 282         752 $Test::Builder::Level = $Test::Builder::Level + 1;
338 282         1220 my @width = $obj->raw_width();
339 282   33     1115 return is( $width[0], $min, "$name raw minimum witdh" ) && is(
340             $width[1], $max, "$name raw maximum width" );
341             }
342              
343             sub replace_characters {
344 2     2 0 198045 %replace_characters = @_;
345 2         6 return;
346             }
347              
348             sub result {
349 2     2 1 4103 return $result;
350             }
351              
352             sub tokenize { ## no critic (RequireArgUnpacking)
353 205     205 1 780381 my ( $opt, $regexp, @args ) = _parse_constructor_args(
354             { test => 1, tokens => 1 }, @_ );
355 205         655 my %args = @args;
356 205         914 $initial_class = __choose_tokenizer_class( $regexp, \%args );
357 205         550 $kind = 'token';
358 205         1365 $obj = $initial_class->new( $regexp, @args );
359 205 100 100     1266 if ( $obj && $opt->{tokens} ) {
360 202         837 $parse = [ $obj->tokens() ];
361             } else {
362 3         8 $parse = [];
363             }
364 205         605 $result = $parse;
365 205         606 $nav = '';
366 205 100       791 $opt->{test} or return;
367 204         520 $Test::Builder::Level = $Test::Builder::Level + 1;
368 204         699 return isa_ok( $obj, 'PPIx::Regexp::Tokenizer',
369             _replace_characters( $regexp ) );
370             }
371              
372             sub true { ## no critic (RequireArgUnpacking)
373 47     47 1 38950 my ( $method, $args ) = @_;
374 47 100       156 ARRAY_REF eq ref $args
375             or $args = [ $args ];
376 47         88 my $class = ref $obj;
377             # cperl does not seem to like goto &xxx; it throws a deep recursion
378             # error if you do it enough times.
379 47         70 $Test::Builder::Level = $Test::Builder::Level + 1;
380 47 50       277 if ( $obj->can( $method ) ) {
381 47         64 $result = $obj->$method( @{ $args } );
  47         155  
382 47         114 my $fmtd = _format_args( $args );
383 47         207 return ok( $result, "$class->$method$fmtd is true" );
384             } else {
385 0         0 $result = undef;
386 0         0 return ok( undef, "$class->$method() exists" );
387             }
388             }
389              
390             sub value { ## no critic (RequireArgUnpacking)
391 1078     1078 1 1723033 my ( $method, $args, $want, $name ) = @_;
392 1078 100       3793 ARRAY_REF eq ref $args
393             or $args = [ $args ];
394              
395 1078   66     2733 my $invocant = $obj || $initial_class;
396 1078   66     3221 my $class = ref $obj || $obj || $initial_class;
397             # cperl does not seem to like goto &xxx; it throws a deep recursion
398             # error if you do it enough times.
399 1078         1599 $Test::Builder::Level = $Test::Builder::Level + 1;
400 1078 50       5184 if ( ! $invocant->can( $method ) ) {
401 0         0 return ok( undef, "$class->$method() exists" );
402             }
403              
404             $result = ARRAY_REF eq ref $want ?
405 3         12 [ $invocant->$method( @{ $args } ) ] :
406 1078 100       2674 $invocant->$method( @{ $args } );
  1075         4427  
407              
408 1078         15432 my $fmtd = _format_args( $args );
409 1078 100       3135 my $answer = format_want( $want, bare => ref $want ? 0 : 1 );
410 1078 50       3676 defined $name
411             or $name = "${class}->$method$fmtd is $answer";
412 1078 100       2050 if ( ref $result ) {
413 5         22 return is_deeply( $result, $want, $name );
414             } else {
415 1073         2664 return is( $result, $want, $name );
416             }
417             }
418              
419             sub width {
420 282     282 1 1145488 my ( $min, $max, $name ) = @_;
421 282 50       1510 defined $name
422             or $name = sprintf q<%s '%s'>, ref $obj, $obj->content();
423 282         565 $Test::Builder::Level = $Test::Builder::Level + 1;
424 282         905 my @width = $obj->width();
425 282   33     1165 return is( $width[0], $min, "$name minimum witdh" ) && is(
426             $width[1], $max, "$name maximum width" );
427             }
428              
429             sub _format_args {
430 2255     2255   4305 my ( $args, %opt ) = @_;
431 2255 100       5161 ARRAY_REF eq ref $args
432             or $args = [ $args ];
433 2255         2848 my @rslt;
434 2255         2406 foreach my $arg ( @{ $args } ) {
  2255         4060  
435 1142 100       3909 if ( ! defined $arg ) {
    100          
436 277         662 push @rslt, 'undef';
437             } elsif ( looks_like_number( $arg ) ) {
438 629         1697 push @rslt, $arg;
439             } else {
440 236         440 push @rslt, $arg;
441 236         505 $rslt[-1] =~ s/ ' /\\'/smxg;
442 236         604 $rslt[-1] = "'$rslt[-1]'";
443             }
444             }
445 2255         4556 my $string = join ', ', @rslt;
446 2255 100       6477 $opt{bare} and return $string;
447 1182 100       3561 @rslt or return '()';
448 64         205 return "( $string )";
449             }
450              
451             sub _method_result { ## no critic (RequireArgUnpacking)
452 3610     3610   8433 my ( $method, @args ) = @_;
453 3610         5244 my $expect = pop @args;
454 3610         5642 $result = undef;
455 3610 100       18693 defined $obj and $result = $obj->$method();
456 3610         4610 my $safe;
457 3610 100       6657 if ( defined $result ) {
458 3546         10419 ($safe = $result) =~ s/([\\'])/\\$1/smxg;
459 3546         7387 $safe = "'$safe'";
460             } else {
461 64         144 $safe = 'undef';
462             }
463 3610         11989 @_ = _replace_characters( $result, $expect, "$kind $nav $method $safe" );
464 3610         11527 goto &is;
465             }
466              
467             sub _parse_constructor_args {
468 515     515   1606 my ( $opt, @args ) = @_;
469 515         1231 my @rslt = ( $opt );
470 515         1335 foreach my $arg ( @args ) {
471 578 100 66     3529 if ( $arg =~ m/ \A - -? (no)? (\w+) \z /smx &&
472             exists $opt->{$2} ) {
473 4         16 $opt->{$2} = !$1;
474             } else {
475 574         1636 push @rslt, $arg;
476             }
477             }
478 515         2028 return @rslt;
479             }
480              
481             sub _pause {
482 10 50   10   26 if ( eval { require Time::HiRes; 1 } ) { # Cargo cult programming.
  10         1685  
  10         3287  
483 10         1001845 Time::HiRes::sleep( 0.1 ); # Something like this is
484             } else { # in PPI's
485 0         0 sleep 1; # t/08_regression.t, and
486             } # who am I to argue?
487 10         132 return;
488             }
489              
490             # quote a string.
491             sub __quote {
492 11387     11387   18197 my @args = @_;
493 11387         12393 my @rslt;
494 11387         15184 foreach my $item ( @args ) {
495 21135 50       33281 if ( __instance( $item, 'PPIx::Regexp::Element' ) ) {
496 0         0 $item = $item->content();
497             }
498 21135 100       55144 if ( ! defined $item ) {
    100          
    100          
499 24         45 push @rslt, 'undef';
500             } elsif ( ARRAY_REF eq ref $item ) {
501 6720         8194 push @rslt, join( ' ', '[', __quote( @{ $item } ), ']' );
  6720         11224  
502             } elsif ( looks_like_number( $item ) ) {
503 7663         13102 push @rslt, $item;
504             } else {
505 6728         13336 $item =~ s/ ( [\\'] ) /\\$1/smxg;
506 6728         15299 push @rslt, "'$item'";
507             }
508             }
509 11387         39671 return join( ', ', @rslt );
510             }
511              
512             sub _replace_characters {
513 4118     4118   8975 my @arg = @_;
514 4118 100       9406 if ( keys %replace_characters ) {
515 14         33 foreach ( @arg ) {
516             $_ = join '',
517             # The following assumes I will never want to replace 0.
518 38 100       226 map { $replace_characters{$_} || $_ }
  403         999  
519             split qr<>;
520             }
521             }
522             wantarray
523 4118 100       10105 or return join '', @arg;
524 3610         9923 return @arg;
525             }
526              
527             1;
528              
529             __END__