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   281820 use strict;
  5         44  
  5         171  
4 5     5   31 use warnings;
  5         7  
  5         153  
5              
6 5     5   30 use Exporter;
  5         9  
  5         348  
7              
8             our @ISA = ( qw{ Exporter } );
9              
10 5     5   3025 use PPIx::Regexp;
  5         22  
  5         250  
11 5     5   33 use PPIx::Regexp::Constant qw{ INFINITY };
  5         8  
  5         285  
12 5     5   2854 use PPIx::Regexp::Dumper;
  5         13  
  5         166  
13 5     5   38 use PPIx::Regexp::Element;
  5         10  
  5         165  
14 5     5   29 use PPIx::Regexp::Tokenizer;
  5         14  
  5         168  
15 5     5   29 use PPIx::Regexp::Util qw{ __choose_tokenizer_class __instance };
  5         10  
  5         340  
16 5     5   32 use Scalar::Util qw{ looks_like_number refaddr };
  5         10  
  5         282  
17 5     5   2253 use Test::More 0.88;
  5         209445  
  5         49  
18              
19             our $VERSION = '0.087';
20              
21 5     5   1581 use constant ARRAY_REF => ref [];
  5         10  
  5         21423  
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 117 return Test::More->builder();
84             }
85              
86             sub cache_count {
87 8     8 1 1197 my ( $expect ) = @_;
88 8 100       40 defined $expect or $expect = 0;
89 8         21 $obj = undef;
90 8         19 $parse = undef;
91 8         58 _pause();
92 8         149 $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         48 $Test::Builder::Level = $Test::Builder::Level + 1;
96 8         117 return is( $result, $expect,
97             "Should be $expect leftover cache contents" );
98             }
99              
100             sub choose {
101 4683     4683 1 19453613 my @args = @_;
102 4683         10162 $obj = $parse;
103 4683         11704 return navigate( @args );
104             }
105              
106             sub klass {
107 4381     4381 1 893179 my ( $class ) = @_;
108 4381   66     13197 $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         6995 $Test::Builder::Level = $Test::Builder::Level + 1;
112 4381 100       8414 if ( defined $class ) {
113 4319 50       15226 my $rslt = isa_ok( $obj, $class, "$kind $nav" )
114             or diag " Instead, $kind $nav isa $result";
115 4319         6678570 return $rslt;
116             } else {
117 62   50     555 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 229644 unshift @_, 'content';
124 3591         12585 goto &_method_result;
125             }
126              
127             sub count {
128 1382     1382 1 320681 my ( @args ) = @_;
129 1382         2785 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         2748 $Test::Builder::Level = $Test::Builder::Level + 1;
133 1382 100       8270 if ( ARRAY_REF eq ref $parse ) {
    100          
    50          
134 202         427 $result = @{ $parse };
  202         363  
135 202         970 return is( $result, $expect, "Expect $expect tokens" );
136             } elsif ( ARRAY_REF eq ref $obj ) {
137 559         757 $result = @{ $obj };
  559         1094  
138 559         2236 return is( $result, $expect, "Expect $expect tokens" );
139             } elsif ( $obj->can( 'children' ) ) {
140 621         2204 $result = $obj->children();
141 621         2954 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 22 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         4 $Test::Builder::Level = $Test::Builder::Level + 1;
155 1 50 33     17 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         14 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 4987 my ( $opt, @args ) = _parse_constructor_args( { test => 1 }, @_ );
172 5 50 0     13 if ( $opt->{test} ) {
    0          
    0          
    0          
173 5         14 my ( $expect, $name ) = splice @args, -2;
174 5         33 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         45 $Test::Builder::Level = $Test::Builder::Level + 1;
178 5         24 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 135 my @args = @_;
194 19 100       82 @args < 3 and unshift @args, $obj;
195 19         33 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         42 $Test::Builder::Level = $Test::Builder::Level + 1;
199 19 100 66     124 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         36 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 81010 unshift @_, 'error';
216 19         88 goto &_method_result;
217             }
218              
219             sub false {
220 52     52 1 45457 my ( $method, $args ) = @_;
221 52 100       240 ARRAY_REF eq ref $args
222             or $args = [ $args ];
223 52         106 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         95 $Test::Builder::Level = $Test::Builder::Level + 1;
227 52 50       280 if ( $obj->can( $method ) ) {
228 52         92 $result = $obj->$method( @{ $args } );
  52         194  
229 52         156 my $fmtd = _format_args( $args );
230 52         288 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 9964 $obj = $parse = $result = undef;
239 2         11 _pause();
240 2         82 $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         20 $Test::Builder::Level = $Test::Builder::Level + 1;
244 2         20 return is( $result, 0, 'Should be no leftover objects' );
245             }
246              
247             sub format_want {
248 1078     1078 1 1898 my ( $want ) = @_;
249 1078 100       2422 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 84448 my @args = @_;
264 4738         5957 my $scalar = 1;
265             @args > 1
266             and ARRAY_REF eq ref $args[-1]
267 595         4006 and @{ $args[-1] } == 0
268 4738 100 100     23091 and $array{$args[-2]}
      100        
      100        
269             and $scalar = 0;
270 4738         8647 my @nav = ();
271 4738         9687 while ( @args ) {
272 8443 100       21633 if ( __instance( $args[0], 'PPIx::Regexp::Element' ) ) {
    100          
273 9         22 $obj = shift @args;
274             } elsif ( ARRAY_REF eq ref $obj ) {
275 1562         2608 my $inx = shift @args;
276 1562         2626 push @nav, $inx;
277 1562         4422 $obj = $obj->[$inx];
278             } else {
279 6872         10330 my $method = shift @args;
280 6872         8404 my $args = shift @args;
281 6872 100       15712 ARRAY_REF eq ref $args
282             or $args = [ $args ];
283 6872         9999 push @nav, $method, $args;
284 6872 50       24740 $obj->can( $method ) or return;
285 6872 100 100     18750 if ( @args || $scalar ) {
286 6316 100       6699 $obj = $obj->$method( @{ $args } ) or return;
  6316         16987  
287             } else {
288 556         982 $obj = [ $obj->$method( @{ $args } ) ];
  556         2416  
289             }
290             }
291             }
292 4665         9826 $nav = __quote( @nav );
293 4665         37684 $nav =~ s/ ' ( \w+ ) ' , /$1 =>/smxg;
294 4665         12440 $nav =~ s/ \[ \s+ \] /[]/smxg;
295 4665         6697 $result = $obj;
296 4665         14847 return $obj;
297             }
298              
299             }
300              
301             sub parse { ## no critic (RequireArgUnpacking)
302 305     305 1 1026116 my ( $opt, $regexp, @args ) = _parse_constructor_args(
303             { test => 1 }, @_ );
304 305         806 $initial_class = 'PPIx::Regexp';
305 305         729 $kind = 'element';
306 305         2202 $result = $obj = $parse = PPIx::Regexp->new( $regexp, @args );
307 305         1342 $nav = '';
308 305 100       1106 $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         867 $Test::Builder::Level = $Test::Builder::Level + 1;
312 304         879 return isa_ok( $parse, 'PPIx::Regexp',
313             _replace_characters( $regexp ) );
314             }
315              
316             sub ppi { ## no critic (RequireArgUnpacking)
317 1     1 1 1699 my @args = @_;
318 1         3 my $expect = pop @args;
319 1         3 $result = undef;
320 1 50       8 defined $obj and $result = $obj->ppi()->content();
321 1         20 my $safe;
322 1 50       4 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         8 return is( $result, $expect, "$kind $nav ppi() content '$safe'" );
331             }
332              
333             sub raw_width {
334 282     282 1 492047 my ( $min, $max, $name ) = @_;
335 282 50       1557 defined $name
336             or $name = sprintf q<%s '%s'>, ref $obj, $obj->content();
337 282         535 $Test::Builder::Level = $Test::Builder::Level + 1;
338 282         1220 my @width = $obj->raw_width();
339 282   33     1131 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 192227 %replace_characters = @_;
345 2         8 return;
346             }
347              
348             sub result {
349 2     2 1 4179 return $result;
350             }
351              
352             sub tokenize { ## no critic (RequireArgUnpacking)
353 205     205 1 762252 my ( $opt, $regexp, @args ) = _parse_constructor_args(
354             { test => 1, tokens => 1 }, @_ );
355 205         780 my %args = @args;
356 205         1032 $initial_class = __choose_tokenizer_class( $regexp, \%args );
357 205         468 $kind = 'token';
358 205         1527 $obj = $initial_class->new( $regexp, @args );
359 205 100 100     1235 if ( $obj && $opt->{tokens} ) {
360 202         963 $parse = [ $obj->tokens() ];
361             } else {
362 3         8 $parse = [];
363             }
364 205         744 $result = $parse;
365 205         805 $nav = '';
366 205 100       834 $opt->{test} or return;
367 204         427 $Test::Builder::Level = $Test::Builder::Level + 1;
368 204         680 return isa_ok( $obj, 'PPIx::Regexp::Tokenizer',
369             _replace_characters( $regexp ) );
370             }
371              
372             sub true { ## no critic (RequireArgUnpacking)
373 47     47 1 39717 my ( $method, $args ) = @_;
374 47 100       177 ARRAY_REF eq ref $args
375             or $args = [ $args ];
376 47         139 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         90 $Test::Builder::Level = $Test::Builder::Level + 1;
380 47 50       288 if ( $obj->can( $method ) ) {
381 47         69 $result = $obj->$method( @{ $args } );
  47         183  
382 47         129 my $fmtd = _format_args( $args );
383 47         230 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 1765953 my ( $method, $args, $want, $name ) = @_;
392 1078 100       4100 ARRAY_REF eq ref $args
393             or $args = [ $args ];
394              
395 1078   66     3019 my $invocant = $obj || $initial_class;
396 1078   66     3639 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         1820 $Test::Builder::Level = $Test::Builder::Level + 1;
400 1078 50       5472 if ( ! $invocant->can( $method ) ) {
401 0         0 return ok( undef, "$class->$method() exists" );
402             }
403              
404             $result = ARRAY_REF eq ref $want ?
405 3         16 [ $invocant->$method( @{ $args } ) ] :
406 1078 100       2713 $invocant->$method( @{ $args } );
  1075         4532  
407              
408 1078         17030 my $fmtd = _format_args( $args );
409 1078 100       3649 my $answer = format_want( $want, bare => ref $want ? 0 : 1 );
410 1078 50       3542 defined $name
411             or $name = "${class}->$method$fmtd is $answer";
412 1078 100       1916 if ( ref $result ) {
413 5         28 return is_deeply( $result, $want, $name );
414             } else {
415 1073         2821 return is( $result, $want, $name );
416             }
417             }
418              
419             sub width {
420 282     282 1 1204801 my ( $min, $max, $name ) = @_;
421 282 50       1615 defined $name
422             or $name = sprintf q<%s '%s'>, ref $obj, $obj->content();
423 282         656 $Test::Builder::Level = $Test::Builder::Level + 1;
424 282         998 my @width = $obj->width();
425 282   33     1133 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   4425 my ( $args, %opt ) = @_;
431 2255 100       5166 ARRAY_REF eq ref $args
432             or $args = [ $args ];
433 2255         2895 my @rslt;
434 2255         2495 foreach my $arg ( @{ $args } ) {
  2255         4012  
435 1142 100       4322 if ( ! defined $arg ) {
    100          
436 277         618 push @rslt, 'undef';
437             } elsif ( looks_like_number( $arg ) ) {
438 629         1950 push @rslt, $arg;
439             } else {
440 236         409 push @rslt, $arg;
441 236         507 $rslt[-1] =~ s/ ' /\\'/smxg;
442 236         632 $rslt[-1] = "'$rslt[-1]'";
443             }
444             }
445 2255         4783 my $string = join ', ', @rslt;
446 2255 100       6790 $opt{bare} and return $string;
447 1182 100       3889 @rslt or return '()';
448 64         214 return "( $string )";
449             }
450              
451             sub _method_result { ## no critic (RequireArgUnpacking)
452 3610     3610   9446 my ( $method, @args ) = @_;
453 3610         5992 my $expect = pop @args;
454 3610         5438 $result = undef;
455 3610 100       18918 defined $obj and $result = $obj->$method();
456 3610         4663 my $safe;
457 3610 100       6538 if ( defined $result ) {
458 3546         11395 ($safe = $result) =~ s/([\\'])/\\$1/smxg;
459 3546         7418 $safe = "'$safe'";
460             } else {
461 64         249 $safe = 'undef';
462             }
463 3610         11563 @_ = _replace_characters( $result, $expect, "$kind $nav $method $safe" );
464 3610         11442 goto &is;
465             }
466              
467             sub _parse_constructor_args {
468 515     515   1995 my ( $opt, @args ) = @_;
469 515         1343 my @rslt = ( $opt );
470 515         1325 foreach my $arg ( @args ) {
471 578 100 66     3174 if ( $arg =~ m/ \A - -? (no)? (\w+) \z /smx &&
472             exists $opt->{$2} ) {
473 4         15 $opt->{$2} = !$1;
474             } else {
475 574         1540 push @rslt, $arg;
476             }
477             }
478 515         1888 return @rslt;
479             }
480              
481             sub _pause {
482 10 50   10   27 if ( eval { require Time::HiRes; 1 } ) { # Cargo cult programming.
  10         1982  
  10         3893  
483 10         1001755 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         184 return;
488             }
489              
490             # quote a string.
491             sub __quote {
492 11387     11387   17987 my @args = @_;
493 11387         11756 my @rslt;
494 11387         16264 foreach my $item ( @args ) {
495 21135 50       33577 if ( __instance( $item, 'PPIx::Regexp::Element' ) ) {
496 0         0 $item = $item->content();
497             }
498 21135 100       56866 if ( ! defined $item ) {
    100          
    100          
499 24         42 push @rslt, 'undef';
500             } elsif ( ARRAY_REF eq ref $item ) {
501 6720         7904 push @rslt, join( ' ', '[', __quote( @{ $item } ), ']' );
  6720         10922  
502             } elsif ( looks_like_number( $item ) ) {
503 7663         12743 push @rslt, $item;
504             } else {
505 6728         13927 $item =~ s/ ( [\\'] ) /\\$1/smxg;
506 6728         14750 push @rslt, "'$item'";
507             }
508             }
509 11387         39745 return join( ', ', @rslt );
510             }
511              
512             sub _replace_characters {
513 4118     4118   8975 my @arg = @_;
514 4118 100       9557 if ( keys %replace_characters ) {
515 14         29 foreach ( @arg ) {
516             $_ = join '',
517             # The following assumes I will never want to replace 0.
518 38 100       210 map { $replace_characters{$_} || $_ }
  403         1019  
519             split qr<>;
520             }
521             }
522             wantarray
523 4118 100       9699 or return join '', @arg;
524 3610         10016 return @arg;
525             }
526              
527             1;
528              
529             __END__