File Coverage

blib/lib/Test/Inter.pm
Criterion Covered Total %
statement 553 765 72.2
branch 261 480 54.3
condition 61 146 41.7
subroutine 49 64 76.5
pod 24 24 100.0
total 948 1479 64.1


line stmt bran cond sub pod time code
1             package Test::Inter;
2             # Copyright (c) 2010-2019 Sullivan Beck. All rights reserved.
3             # This program is free software; you can redistribute it and/or modify it
4             # under the same terms as Perl itself.
5              
6             ###############################################################################
7              
8             require 5.004;
9              
10 10     10   18004 use warnings;
  10         73  
  10         323  
11 10     10   45 use strict;
  10         23  
  10         305  
12 10     10   73 use File::Basename;
  10         30  
  10         1306  
13 10     10   4511 use IO::File;
  10         83895  
  10         1048  
14 10     10   66 use Cwd 'abs_path';
  10         20  
  10         2303  
15              
16             our($VERSION);
17             $VERSION = '1.09';
18              
19             ###############################################################################
20             # BASE METHODS
21             ###############################################################################
22              
23             sub version {
24 0     0 1 0 my($self) = @_;
25              
26 0         0 return $VERSION;
27             }
28              
29             sub new {
30 10     10 1 385 my($class,@args) = @_;
31 10         119 my($name,%opts);
32              
33 10 50       50 if (@args % 2) {
34 10         28 ($name,%opts) = @args;
35             } else {
36 0         0 $name = $0;
37 0         0 $name =~ s,^\./,,;
38 0         0 %opts = @args;
39             }
40              
41             # The basic structure
42              
43 10         106 my $self = {
44             'name' => $name, # the name of the test script
45             'start' => 1, # the first test to run
46             'end' => 0, # the last test to end
47             'plan' => 0, # the number of tests planned
48             'abort' => 0, # abort on the first failed test
49             'quiet' => 0, # if 1, no output on successes
50             # (this should only be done when
51             # running as an interactive script)
52             'mode' => 'test', # mode to run script in
53             'width' => 80, # width of terminal
54             'features' => {}, # a list of available features
55             'use_lib' => 'on', # whether to run 'use lib' when loading
56             # this module
57              
58             'skipall' => '', # the reason for skipping all
59             # remaining tests
60              
61             'plandone' => 0, # 1 if a plan is done
62             'testsrun' => 0, # 1 if any tests have been run
63              
64             'libdir' => '', # a directory to load modules from
65             'testdir' => '', # the test directory
66             };
67              
68 10         22 bless $self, $class;
69 10         19 $main::TI_NUM = 0;
70              
71             # Handle options, environment variables, global variables
72              
73 10         39 my @opts = qw(start end testnum plan abort quiet mode width skip_all);
74 10         23 my %o = map { $_,1 } @opts;
  90         151  
75              
76 10     10   71 no strict 'refs';
  10         32  
  10         83838  
77 10         40 foreach my $opt (@opts) {
78 90 50       179 if (! exists $o{$opt}) {
79 0         0 $self->_die("Invalid option to new method: $opt");
80             }
81              
82 90         155 my $OPT = uc("ti_$opt");
83              
84 90 50 33     383 if (exists $opts{opt} ||
      33        
85             exists $ENV{$OPT} ||
86 90         450 defined ${ "main::$OPT" }) {
87              
88 0         0 my $val;
89 0 0       0 if (defined ${ "main::$OPT" }) {
  0 0       0  
90 0         0 $val = ${ "main::$OPT" };
  0         0  
91             } elsif (exists $ENV{$OPT}) {
92 0         0 $val = $ENV{$OPT};
93             } else {
94 0         0 $val = $opts{$opt};
95             }
96              
97 0         0 &{ "Test::Inter::$opt" }($self,$val);
  0         0  
98             }
99             }
100              
101 10 50       83 if ($$self{'mode'} ne 'test') {
102 0         0 print "\nRunning $name...\n";
103             }
104              
105             # We assume that the module is distributed in a directory with the correct
106             # hierarchy. This is:
107             # /some/path MODDIR
108             # /t TESTDIR
109             # /lib LIBDIR
110             # We'll find the full path to each.
111              
112 10         31 my($moddir,$testdir,$libdir);
113              
114 10 50       199 if (-f "$0") {
    0          
    0          
115 10         1204 $moddir = dirname(dirname(abs_path($0)));
116             } elsif (-d "./t") {
117 0         0 $moddir = dirname(abs_path('.'));
118             } elsif (-d "../t") {
119 0         0 $moddir = dirname(abs_path('..'));
120             }
121 10 50       177 if (-d "$moddir/t") {
122 10         33 $testdir = "$moddir/t";
123             }
124 10 50       166 if (-d "$moddir/lib") {
125 10         33 $libdir = "$moddir/lib";
126             }
127              
128 10         37 $$self{'moddir'} = $moddir;
129 10         23 $$self{'testdir'} = $testdir;
130 10         20 $$self{'libdir'} = $libdir;
131              
132 10         58 $self->use_lib();
133              
134 10         1174 return $self;
135             }
136              
137             sub use_lib {
138 10     10 1 35 my($self,$val) = @_;
139 10 50       39 if (defined $val) {
140 0         0 $$self{'use_lib'} = $val;
141 0         0 return;
142             }
143              
144 10 50       64 if ($$self{'use_lib'} eq 'on') {
145 10         25 foreach my $dir ($$self{'libdir'},$$self{'testdir'}) {
146 20 50       1315 next if (! defined $dir);
147 10     10   498 eval "use lib '$dir'";
  10     10   677  
  10         56  
  10         64  
  10         16  
  10         34  
  20         1276  
148             }
149             }
150             }
151              
152             sub testdir {
153 0     0 1 0 my($self,$req) = @_;
154 0 0 0     0 if ($req && $req eq 'mod') {
    0 0        
155 0         0 return $$self{'moddir'};
156             } elsif ($req && $req eq 'lib') {
157 0         0 return $$self{'libdir'};
158             }
159 0         0 return $$self{'testdir'};
160             }
161              
162             sub start {
163 0     0 1 0 my($self,$val) = @_;
164 0 0       0 $val = 1 if (! defined($val));
165 0 0       0 $self->_die("start requires an integer value") if ($val !~ /^\d+$/);
166 0         0 $$self{'start'} = $val;
167             }
168              
169             sub end {
170 0     0 1 0 my($self,$val) = @_;
171 0 0       0 $val = 0 if (! $val);
172 0 0       0 $self->_die("end requires an integer value") if ($val !~ /^\d+$/);
173 0         0 $$self{'end'} = $val;
174             }
175              
176             sub testnum {
177 0     0 1 0 my($self,$val) = @_;
178 0 0       0 if (! defined($val)) {
179 0         0 $$self{'start'} = 1;
180 0         0 $$self{'end'} = 0;
181             } else {
182 0 0       0 $self->_die("testnum requires an integer value") if ($val !~ /^\d+$/);
183 0         0 $$self{'start'} = $$self{'end'} = $val;
184             }
185             }
186              
187             sub plan {
188 0     0 1 0 my($self,$val) = @_;
189              
190 0 0       0 if ($$self{'plandone'}) {
191 0         0 $self->_die('Plan/done_testing included twice');
192             }
193 0         0 $$self{'plandone'} = 1;
194              
195 0 0       0 $val = 0 if (! defined($val));
196 0 0       0 $self->_die("plan requires an integer value") if ($val !~ /^\d+$/);
197 0         0 $$self{'plan'} = $val;
198              
199 0 0       0 if ($val != 0) {
200 0         0 $self->_plan($val);
201             }
202             }
203              
204             sub done_testing {
205 8     8 1 64 my($self,$val) = @_;
206              
207 8 50       38 if ($$self{'plandone'}) {
208 0         0 $self->_die('Plan/done_testing included twice');
209             }
210 8         35 $$self{'plandone'} = 1;
211              
212 8 50       29 $val = $main::TI_NUM if (! $val);
213 8 50       50 $self->_die("done_testing requires an integer value") if ($val !~ /^\d+$/);
214 8         38 $self->_plan($val);
215              
216 8 50       0 if ($val != $main::TI_NUM) {
217 0         0 $self->_die("Ran $main::TI_NUM tests, expected $val");
218             }
219             }
220              
221             sub abort {
222 0     0 1 0 my($self,$val) = @_;
223 0 0       0 $val = 0 if (! $val);
224 0         0 $$self{'abort'} = $val;
225             }
226              
227             sub quiet {
228 0     0 1 0 my($self,$val) = @_;
229 0 0       0 $val = 0 if (! $val);
230 0         0 $$self{'quiet'} = $val;
231             }
232              
233             sub mode {
234 0     0 1 0 my($self,$val) = @_;
235 0 0       0 $val = 'test' if (! $val);
236 0         0 $$self{'mode'} = $val;
237             }
238              
239             sub width {
240 0     0 1 0 my($self,$val) = @_;
241 0 0       0 $val = 0 if (! $val);
242 0         0 $$self{'width'} = $val;
243             }
244              
245             sub skip_all {
246 2     2 1 17 my($self,$reason,@features) = @_;
247              
248 2 50       6 if (@features) {
249 0         0 my $skip = 0;
250 0         0 foreach my $feature (@features) {
251 0 0 0     0 if (! exists $$self{'features'}{$feature} ||
252             ! $$self{'features'}{$feature}) {
253 0         0 $skip = 1;
254 0 0       0 $reason = "Required feature ($feature) missing"
255             if (! $reason);
256 0         0 last;
257             }
258             }
259 0 0       0 return if (! $skip);
260             }
261              
262 2 50 33     23 if ($$self{'plandone'} ||
263             $$self{'testsrun'}) {
264 0 0       0 $reason = 'Remaining tests skipped' if (! $reason);
265 0         0 $$self{'skipall'} = $reason;
266              
267             } else {
268 2 50       6 $reason = 'Test script skipped' if (! $reason);
269 2         8 $self->_plan(0,$reason);
270 2         0 exit 0;
271             }
272             }
273              
274             sub _die {
275 0     0   0 my($self,$message) = @_;
276              
277 0         0 print "ERROR: $message\n";
278 0         0 exit 1;
279             }
280              
281             sub feature {
282 4     4 1 23 my($self,$feature,$val) = @_;
283 4         10 $$self{'features'}{$feature} = $val;
284             }
285              
286             sub diag {
287 0     0 1 0 my($self,$message) = @_;
288 0 0       0 return if ($$self{'quiet'} == 2);
289 0         0 $self->_diag($message);
290             }
291              
292             sub note {
293 0     0 1 0 my($self,$message) = @_;
294 0 0       0 return if ($$self{'quiet'});
295 0         0 $self->_diag($message);
296             }
297              
298             ###############################################################################
299             # LOAD METHODS
300             ###############################################################################
301             # The routines were originally from Test::More (though they have been
302             # changed... some to a greater extent than others).
303              
304             sub require_ok {
305 6     6 1 48 my($self,$module,$mode) = @_;
306 6 100       13 $mode = '' if (! $mode);
307 6 100       14 $main::TI_NUM++ unless ($mode eq 'feature');
308              
309 6         9 my $pack = caller;
310 6         17 my @inc = map { "unshift(\@INC,'$_');\n" } ($$self{'libdir'},$$self{'testdir'});
  12         31  
311              
312 6         9 my($desc,$code);
313              
314 6 100       24 if ( $module =~ /^\d+(?:\.\d+)?$/ ) {
315             # A perl version check.
316 2         3 $desc = "require perl $module";
317 2         4 $code = <
318             require $module;
319             1;
320             REQUIRE
321             } else {
322 4 50       9 $module = qq['$module'] unless $self->_is_module_name($module);
323 4         9 $desc = "require $module";
324 4         5 my $p = "package"; # So the following do not get picked up by cpantorpm-depreq
325 4         4 my $r = "require";
326 4         16 $code = <
327             $p $pack;
328             @inc
329             $r $module;
330             1;
331             REQUIRE
332             }
333              
334 6 100       14 $desc .= ' (should not load)' if ($mode eq 'forbid');
335 6 100       11 $desc .= ' (feature)' if ($mode eq 'feature');
336              
337 6         13 my($eval_result,$eval_error) = $self->_eval($code);
338 6         9 chomp($eval_error);
339 6         18 my @eval_error = split(/\n/,$eval_error);
340 6         12 foreach my $err (@eval_error) {
341 3         18 $err =~ s/ \(\@INC contains.*//; # strip out the actual @INC values
342             }
343              
344 6         8 my $ok = 1;
345 6 100       10 if ($eval_result) {
346             # Able to load the module
347 3 50       9 if ($mode eq 'forbid') {
    100          
348 0         0 $$self{'skipall'} = 'Loaded a module not supposed to be present';
349 0         0 $self->_not_ok($desc);
350             $self->_diag('Test required that module not be loadable')
351 0 0       0 unless ($$self{'quiet'} == 2);
352 0         0 $ok = 0;
353             } elsif ($mode eq 'feature') {
354 1         6 $self->feature($module,1);
355 1 50       4 if (! $$self{'quiet'}) {
356 1         2 $self->_diag($desc);
357 1         5 $self->_diag("Feature available: $module");
358             }
359             } else {
360 2         6 $self->_ok($desc);
361             }
362              
363             } else {
364             # Unable to load the module
365 3 100       9 if ($mode eq 'forbid') {
    50          
366 2         5 $self->_ok($desc);
367             } elsif ($mode eq 'feature') {
368 1         4 $self->feature($module,0);
369 1 50       5 if (! $$self{'quiet'}) {
370 1         3 $self->_diag($desc);
371 1         16 $self->_diag("Feature unavailable: $module");
372             }
373             } else {
374 0         0 $$self{'skipall'} = 'Unable to load a required module';
375 0         0 $self->_not_ok($desc);
376 0         0 $ok = 0;
377             }
378             }
379              
380             return
381             if ( ($ok && $$self{'quiet'}) ||
382 6 50 33     33 (! $ok && $$self{'quiet'} == 2) );
      33        
      33        
383              
384 6         16 foreach my $err (@eval_error) {
385 3         7 $self->_diag($err);
386             }
387             }
388              
389             sub use_ok {
390 12     12 1 56 my($self,@args) = @_;
391              
392 12         15 my $mode = '';
393 12 100 100     51 if ($args[$#args] eq 'forbid' ||
394             $args[$#args] eq 'feature') {
395 7         11 $mode = pop(@args);
396             }
397 12 100       34 $main::TI_NUM++ unless ($mode eq 'feature');
398              
399 12         24 my $pack = caller;
400              
401 12         17 my($code,$desc,$module);
402 12 100 100     62 if ( @args == 1 and $args[0] =~ /^\d+(?:\.\d+)?$/ ) {
    50          
403             # A perl version check.
404 3         7 $desc = "require perl $args[0]";
405 3         5 $module = 'perl';
406 3         5 $code = <
407             use $args[0];
408             1;
409             USE
410              
411             } elsif (@args) {
412 9         30 $module = shift(@args);
413              
414 9 50       30 if (! $self->_is_module_name($module)) {
415 0         0 $self->_not_ok("use module: invalid module name: $module");
416 0         0 return;
417             }
418              
419 9         14 my $vers = '';
420 9 100 100     71 if ( @args and $args[0] =~ /^\d+(?:\.\d+)?$/ ) {
421 3         8 $vers = shift(@args);
422             }
423              
424 9 100       30 my $imports = (@args ? 'qw(' . join(' ',@args) . ')' : '');
425 9         24 $desc = "use $module $vers $imports";
426              
427 9         19 my @inc = map { "unshift(\@INC,'$_');\n" } ($$self{'libdir'},$$self{'testdir'});
  18         62  
428              
429 9         13 my $p = "package"; # So the following do not get picked up by cpantorpm-depreq
430 9         39 $code = <
431             $p $pack;
432             @inc
433             use $module $vers $imports;
434             1;
435             USE
436              
437             } else {
438 0         0 $self->_not_ok('use module: no module specified');
439 0         0 return;
440             }
441              
442 12 100       29 $desc .= ' (should not load)' if ($mode eq 'forbid');
443 12 100       23 $desc .= ' (feature)' if ($mode eq 'feature');
444              
445 12         24 my($eval_result,$eval_error) = $self->_eval($code);
446 12         22 chomp($eval_error);
447 12         32 my @eval_error = split(/\n/,$eval_error);
448 12         46 @eval_error = grep(!/^BEGIN failed--compilation aborted/,@eval_error);
449 12         21 foreach my $err (@eval_error) {
450 6         20 $err =~ s/ \(\@INC contains.*//; # strip out the actual @INC values
451             }
452              
453 12         18 my $ok = 1;
454 12 100       29 if ($eval_result) {
455             # Able to load the module
456 6 50       18 if ($mode eq 'forbid') {
    100          
457 0         0 $$self{'skipall'} = 'Loaded a module not supposed to be present';
458 0         0 $self->_not_ok($desc);
459             $self->_diag('Test required that module not be usable')
460 0 0       0 unless ($$self{'quiet'} == 2);
461 0         0 $ok = 0;
462             } elsif ($mode eq 'feature') {
463 1         4 $self->feature($module,1);
464 1 50       3 if (! $$self{'quiet'}) {
465 1         2 $self->_diag($desc);
466 1         3 $self->_diag("Feature available: $module");
467             }
468             } else {
469 5         13 $self->_ok($desc);
470             }
471              
472             } else {
473             # Unable to load the module
474 6 100       16 if ($mode eq 'forbid') {
    50          
475 5         15 $self->_ok($desc);
476             } elsif ($mode eq 'feature') {
477 1         3 $self->feature($module,0);
478 1 50       2 if (! $$self{'quiet'}) {
479 1         3 $self->_diag($desc);
480 1         3 $self->_diag("Feature unavailable: $module");
481             }
482             } else {
483 0         0 $$self{'skipall'} = 'Unable to load a required module';
484 0         0 $self->_not_ok($desc);
485 0         0 $ok = 0;
486             }
487             }
488              
489             return
490             if ( ($ok && $$self{'quiet'}) ||
491 12 50 33     77 (! $ok && $$self{'quiet'} == 2) );
      33        
      33        
492              
493 12         2089 foreach my $err (@eval_error) {
494 6         14 $self->_diag($err);
495             }
496             }
497              
498             sub _is_module_name {
499 13     13   20 my($self,$module) = @_;
500              
501             # Module names start with a letter.
502             # End with an alphanumeric.
503             # The rest is an alphanumeric or ::
504 13         34 $module =~ s/\b::\b//g;
505              
506 13 50       75 return $module =~ /^[a-zA-Z]\w*$/ ? 1 : 0;
507             }
508              
509             sub _eval {
510 18     18   28 my($self,$code) = @_;
511              
512 18         21 my( $sigdie, $eval_result, $eval_error );
513             {
514 18         22 local( $@, $!, $SIG{__DIE__} ); # isolate eval
  18         161  
515 18     3   4393 $eval_result = eval $code;
  3     3   79  
  1     3   3  
  3     1   20  
  3     1   22  
  3     1   98  
  3         1536  
  2         6438  
  1         51  
  1         6  
  1         2  
  1         39  
  1         178  
  0         0  
  0         0  
  1         689  
  1         3149  
  1         38  
516 18         58 $eval_error = $@;
517 18   50     172 $sigdie = $SIG{__DIE__} || undef;
518             }
519             # make sure that $code got a chance to set $SIG{__DIE__}
520 18 50       61 $SIG{__DIE__} = $sigdie if defined $sigdie;
521              
522 18         56 return( $eval_result, $eval_error );
523             }
524              
525             ###############################################################################
526             # OK/IS/ISNT METHODS
527             ###############################################################################
528              
529             sub ok {
530 17     17 1 91 my($self,@args) = @_;
531 17         20 $main::TI_NUM++;
532              
533 17         31 my($op,@ret) = $self->_ok_result(@args);
534 17         23 my($name,@diag);
535 17         18 my $ok = 1;
536              
537 17 50       33 if ($op eq 'skip') {
    50          
538 0         0 my $reason = shift(@ret);
539 0         0 $self->_skip($reason);
540              
541             } elsif ($op eq 'pass') {
542 17         22 ($name,@diag) = @ret;
543 17         28 $self->_ok($name);
544              
545             } else {
546 0         0 ($name,@diag) = @ret;
547 0         0 $self->_not_ok($name);
548 0         0 $ok = 0;
549             }
550              
551             return
552             if ( ($ok && $$self{'quiet'}) ||
553 17 50 33     77 (! $ok && $$self{'quiet'} == 2) );
      33        
      33        
554              
555 17         38 foreach my $diag (@diag) {
556 18         28 $self->_diag($diag);
557             }
558             }
559              
560             sub _ok_result {
561 20     20   38 my($self,@args) = @_;
562              
563             # Test if we're skipping this test
564              
565 20         34 my($skip,$reason) = $self->_skip_test();
566 20 50       34 return ('skip',$reason) if ($skip);
567              
568             # No args == always pass
569              
570 20 100       34 if (@args == 0) {
571 1         3 return ('pass','Empty test');
572             }
573              
574             # Get the result
575              
576 19         38 my($func,$funcargs,$result) = $self->_get_result(\@args);
577              
578             # Get name/expected
579              
580 19         26 my($name,$expected);
581 19 100       40 if (@args == 1) {
    100          
    50          
582 6         10 $name = $args[0];
583             } elsif (@args == 2) {
584 10         17 ($expected,$name) = @args;
585             } elsif (@args > 2) {
586 0         0 return(0,'','Improperly formed test: too many arguments');
587             }
588              
589             # Check the result
590              
591 19         32 my($pass,@diag) = $self->_cmp_result('ok',$func,$funcargs,$result,$expected);
592 19         50 return($pass,$name,@diag);
593             }
594              
595             sub is {
596 2     2 1 15 my($self,@args) = @_;
597 2         5 $self->_is("is",@args);
598             }
599              
600             sub isnt {
601 2     2 1 27 my($self,@args) = @_;
602 2         3 $self->_is("isnt",@args);
603             }
604              
605             sub _is {
606 11     11   26 my($self,$is,@args) = @_;
607 11         15 $main::TI_NUM++;
608              
609 11         20 my($op,@ret) = $self->_is_result($is,@args);
610 11         15 my($name,@diag);
611 11         11 my $ok = 1;
612              
613 11 50       26 if ($op eq 'skip') {
    50          
614 0         0 my $reason = shift(@ret);
615 0         0 $self->_skip($reason);
616              
617             } elsif ($op eq 'pass') {
618 11         16 ($name,@diag) = @ret;
619 11         18 $self->_ok($name);
620              
621             } else {
622 0         0 ($name,@diag) = @ret;
623 0         0 $self->_not_ok($name);
624 0         0 $ok = 0;
625             }
626              
627             return
628             if ( ($ok && $$self{'quiet'}) ||
629 11 50 33     59 (! $ok && $$self{'quiet'} == 2) );
      33        
      33        
630              
631 11         45 foreach my $diag (@diag) {
632 0         0 $self->_diag($diag);
633             }
634             }
635              
636             sub _is_result {
637 24     24   44 my($self,$is,@args) = @_;
638              
639             # Test if we're skipping this test
640              
641 24         43 my($skip,$reason) = $self->_skip_test();
642 24 50       40 return ('skip',$reason) if ($skip);
643              
644             # Test args
645              
646 24 50       44 if (@args < 2) {
647 0         0 return ('fail','','Improperly formed test: too few arguments');
648             }
649              
650 24         52 my($func,$funcargs,$result) = $self->_get_result(\@args);
651              
652 24         32 my($name,$expected);
653 24 50       56 if (@args == 1) {
    50          
654 0         0 ($expected) = @args;
655             } elsif (@args == 2) {
656 24         34 ($expected,$name) = @args;
657             } else {
658 0         0 return(0,'','Improperly formed test: too many arguments');
659             }
660              
661             # Check the result
662              
663 24         51 my($pass,@diag) = $self->_cmp_result($is,$func,$funcargs,$result,$expected);
664 24         80 return($pass,$name,@diag);
665             }
666              
667             # Returns $func,$args and $results. The first two are returned only if
668             # there is a function.
669             #
670             sub _get_result {
671 43     43   57 my($self,$args) = @_;
672 43         50 my($func,@funcargs,@result,$result);
673              
674 43 100       104 if (ref($$args[0]) eq 'CODE') {
    100          
675 21         29 $func = shift(@$args);
676              
677 21 100       71 if (ref($$args[0]) eq 'ARRAY') {
678 17         21 @funcargs = @{ $$args[0] };
  17         37  
679 17         21 shift(@$args);
680             }
681              
682 21         47 @result = &$func(@funcargs);
683 21         176 return ($func,\@funcargs,\@result);
684              
685             } elsif (ref($$args[0]) eq 'ARRAY') {
686 7         9 @result = @{ $$args[0] };
  7         50  
687 7         13 shift(@$args);
688 7         20 return ('','',\@result);
689              
690             } else {
691 15         21 $result = shift(@$args);
692 15         45 return ('','',$result);
693             }
694             }
695              
696             sub _cmp_result {
697 43     43   76 my($self,$type,$func,$funcargs,$result,$expected) = @_;
698 43         45 my $pass = 0;
699 43         45 my $identical = 0;
700 43         45 my @diag;
701              
702 43 100       74 if ($type eq 'ok') {
703 19 100       33 if (ref($result) eq 'ARRAY') {
    100          
704 13         19 foreach my $ele (@$result) {
705 18 50       33 $pass = 1 if (defined($ele));
706             }
707              
708             } elsif (ref($result) eq 'HASH') {
709 2         6 foreach my $key (keys %$result) {
710 4         6 my $val = $$result{$key};
711 4 50       25 $pass = 1 if (defined($val));
712             }
713              
714             } else {
715 4 50       7 $pass = ($result ? 1 : 0);
716             }
717              
718 19 100       29 if (! defined($expected)) {
719             # If no expected result passed in, we don't test the results
720 9         11 $identical = 1;
721             } else {
722             # Results/expected must be the same structure
723 10         19 $identical = $self->_cmp_structure($result,$expected);
724             }
725              
726             } else {
727 24         47 $identical = $self->_cmp_structure($result,$expected);
728 24 100       41 if ($type eq 'is') {
729 22         31 $pass = $identical;
730             } else {
731 2         3 $pass = 1 - $identical;
732             }
733             }
734              
735 43 100 100     103 if (! $identical && $type ne 'isnt') {
736 7 100       11 if ($func) {
737 4         9 push(@diag,"Arguments: " . $self->_stringify($funcargs));
738             }
739 7         15 push(@diag, "Results : " . $self->_stringify($result));
740 7 50 33     32 push(@diag, "Expected : " . $self->_stringify($expected)) unless ($type eq 'ok' &&
741             ! defined($result));
742             }
743              
744 43 50       139 return (($pass ? 'pass' : 'fail'),@diag);
745             }
746              
747             # Turn a data structure into a string (poor-man's Data::Dumper)
748             sub _stringify {
749 18     18   22 my($self,$s) = @_;
750              
751 18         27 my($str) = $self->__stringify($s);
752 18         27 my($width) = $$self{'width'};
753 18 50       29 if ($width) {
754 18         16 $width -= 21; # The leading string
755 18 50       25 $width = 10 if ($width < 10);
756 18 50       27 $str = substr($str,0,$width) if (length($str)>$width);
757             }
758 18         42 return $str;
759             }
760              
761             sub __stringify {
762 38     38   47 my($self,$s) = @_;
763              
764 38 50       93 if (! defined($s)) {
    100          
    100          
    50          
    50          
765 0         0 return '__undef__';
766              
767             } elsif (ref($s) eq 'ARRAY') {
768 10         12 my $str = '[ ';
769 10         12 foreach my $val (@$s) {
770 12         22 $str .= $self->__stringify($val) . ' ';
771             }
772 10         12 $str .= ']';
773 10         26 return $str;
774              
775             } elsif (ref($s) eq 'HASH') {
776 2         3 my $str = '{ ';
777 2         8 foreach my $key (sort keys %$s) {
778 4         6 my $key = $self->__stringify($key);
779 4         6 my $val = $self->__stringify($$s{$key});
780 4         8 $str .= "$key=>$val ";
781             }
782 2         4 $str .= '}';
783 2         3 return $str;
784              
785             } elsif (ref($s)) {
786 0         0 return '<' . ref($s) . '>';
787              
788             } elsif ($s eq '') {
789 0         0 return "''";
790              
791             } else {
792 26 50       48 if ($s =~ /\s/) {
793 0         0 my $q = qr/\'/; # single quote
794 0         0 my $qq = qr/\"/; # double quote
795 0 0       0 if ($s !~ $q) {
796 0         0 return "'$s'";
797             }
798 0 0       0 if ($s !~ $qq) {
799 0         0 return '"' . $s . '"';
800             }
801 0         0 return "<$s>";
802              
803             } else {
804 26         51 return $s;
805             }
806             }
807             }
808              
809             sub _cmp_structure {
810 75     75   105 my($self,$s1,$s2) = @_;
811              
812 75 0 33     137 return 1 if (! defined($s1) && ! defined($s2)); # undef = undef
813 75 50 33     221 return 0 if (! defined($s1) || ! defined($s2)); # undef != def
814 75 100       122 return 0 if (ref($s1) ne ref($s2)); # must be same type
815              
816 71 100       149 if (ref($s1) eq 'ARRAY') {
    100          
    50          
817 17 50       30 return 0 if ($#$s1 != $#$s2); # two lists must be the same length
818 17         38 foreach (my $i=0; $i<=$#$s1; $i++) {
819 35 100       65 return 0 unless $self->_cmp_structure($$s1[$i],$$s2[$i]);
820             }
821 15         31 return 1;
822              
823             } elsif (ref($s1) eq 'HASH') {
824 4         10 my @k1 = keys %$s1;
825 4         17 my @k2 = keys %$s2;
826 4 50       10 return 0 if ($#k1 != $#k2); # two hashes must be the same length
827 4         8 foreach my $key (@k1) {
828 6 50       11 return 0 if (! exists $$s2{$key}); # keys must be the same
829 6 100       14 return 0 unless $self->_cmp_structure($$s1{$key},$$s2{$key});
830             }
831 2         4 return 1;
832              
833             } elsif (ref($s1)) {
834             # Two references (other than ARRAY and HASH are assumed equal.
835 0         0 return 1;
836              
837             } else {
838             # Two scalars are compared stringwise
839 50         172 return ($s1 eq $s2);
840             }
841             }
842              
843             sub _skip_test {
844 44     44   55 my($self) = @_;
845              
846 44 50 33     222 if ($$self{'skipall'}) {
    50 33        
847 0         0 return (1,$$self{'skipall'});
848             } elsif ( $main::TI_NUM < $$self{'start'} ||
849             ($$self{'end'} && $main::TI_NUM > $$self{'end'}) ) {
850 0         0 return (1,'Test not in list of tests specified to run');
851             }
852 44         81 return 0;
853             }
854              
855             ###############################################################################
856             # FILE METHOD
857             ###############################################################################
858              
859             sub file {
860 2     2 1 17 my($self,$func,$input,$outputdir,$expected,$name,@args) = @_;
861 2 50       6 $name = "" if (! $name);
862              
863 2 50       6 if (! ref($func) eq 'CODE') {
864 0         0 $self->_die("file method required a coderef");
865             }
866              
867 2         3 my @funcargs;
868 2         6 my $testdir = $$self{'testdir'};
869              
870             # Input file
871              
872 2 100       6 if ($input) {
873 1 50       36 if (-r $input) {
    50          
874             # Nothing
875              
876             } elsif (-r "$testdir/$input") {
877 1         4 $input = "$testdir/$input";
878              
879             } else {
880 0         0 $self->_die("Input file not readable: $input");
881             }
882 1         3 push(@funcargs,$input);
883             }
884              
885             # Output file and directory
886              
887 2 50       4 if (! $outputdir) {
888 2 50 33     48 if (-d $testdir &&
889             -w $testdir) {
890 2         6 $outputdir = $testdir;
891             } else {
892 0         0 $outputdir = ".";
893             }
894             }
895 2 50       5 if ($outputdir) {
896 2 50 33     41 if (! -d $outputdir ||
897             ! -w $outputdir) {
898 0         0 $self->_die("Output directory not writable: $outputdir");
899             }
900             }
901 2         7 my $output = "$outputdir/tmp_test_inter";
902 2         4 push(@funcargs,$output);
903              
904             # Expected output
905              
906 2 50       72 if (! $expected) {
    50          
    50          
907 0         0 $self->_die("Expected output file not specified");
908              
909             } elsif (-r $expected) {
910             # Nothing
911              
912             } elsif (-r "$testdir/$expected") {
913 2         6 $expected = "$testdir/$expected";
914              
915             } else {
916 0         0 $self->_die("Expected output file not readable: $expected");
917             }
918              
919             # Create the temporary output file.
920              
921 2         10 &$func(@funcargs,@args);
922 2 50       340 if (! -r "$output") {
923 0         0 $self->_die("Output file not created");
924             }
925              
926             # Test each line
927              
928 2         15 my $in = new IO::File;
929 2         88 $in->open($output);
930 2         112 my @out = <$in>;
931 2         16 $in->close();
932 2         30 chomp(@out);
933              
934 2         7 $in->open($expected);
935 2         91 my @exp = <$in>;
936 2         9 $in->close();
937 2         26 chomp(@exp);
938 2 50       108 unlink($output) if (! $ENV{'TI_NOCLEAN'});
939              
940 2         13 while (@out < @exp) {
941 0         0 push(@out,'');
942             }
943 2         5 while (@exp < @out) {
944 0         0 push(@exp,'');
945             }
946              
947 2         10 for (my $i=0; $i<@out; $i++) {
948 7         11 my $line = $i+1;
949 7 50       18 my $n = ($name ? "$name : Line $line" : "Line $line");
950 7         18 $self->_is('is',$out[$i],$exp[$i],$n);
951             }
952             }
953              
954             ###############################################################################
955             # TESTS METHOD
956             ###############################################################################
957              
958             sub tests {
959 4     4 1 26 my($self,%opts) = @_;
960              
961             #
962             # feature => [ FEATURE, FEATURE, ... ]
963             # disable => [ FEATURE, FEATURE, ... ]
964             #
965              
966 4         7 my $skip = '';
967 4 50       8 if (exists $opts{'feature'}) {
968 0         0 foreach my $feature (@{ $opts{'feature'} }) {
  0         0  
969             $skip = "Required feature unavailable: $feature", last
970 0 0       0 if (! exists $$self{'features'}{$feature});
971             }
972             }
973 4 50 33     9 if (exists $opts{'disable'} && ! $skip) {
974 0         0 foreach my $feature (@{ $opts{'disable'} }) {
  0         0  
975             $skip = "Disabled due to feature being available: $feature", last
976 0 0       0 if (exists $$self{'features'}{$feature});
977             }
978             }
979              
980             #
981             # name => NAME
982             # skip => REASON
983             # todo => 0/1
984             #
985              
986 4         5 my $name = '';
987 4 50       6 if (exists $opts{'name'}) {
988 0         0 $name = $opts{'name'};
989             }
990              
991 4 50       6 if (exists $opts{'skip'}) {
992 0         0 $skip = $opts{'skip'};
993             }
994              
995 4         5 my $todo = 0;
996 4 50       21 if (exists $opts{'todo'}) {
997 0         0 $todo = $opts{'todo'};
998             }
999              
1000             #
1001             # tests => STRING OR LISTREF
1002             # func => CODEREF
1003             # expected => STRING OR LISTREF
1004             #
1005              
1006             # tests
1007 4 50       10 if (! exists $opts{'tests'}) {
1008 0         0 $self->_die("invalid test format: tests required");
1009             }
1010 4         7 my $tests = $opts{'tests'};
1011 4         7 my(%tests,$gotexpected);
1012              
1013 4         0 my($ntest,$nexp);
1014 4 50       9 if (ref($tests) eq 'ARRAY') {
1015 0         0 my @results = @$tests;
1016 0         0 $ntest = 0;
1017 0         0 foreach my $result (@results) {
1018 0         0 $ntest++;
1019 0         0 $tests{$ntest}{'err'} = 0;
1020 0 0       0 if (ref($result) eq 'ARRAY') {
1021 0         0 $tests{$ntest}{'args'} = $result;
1022             } else {
1023 0         0 $tests{$ntest}{'args'} = [$result];
1024             }
1025             }
1026 0         0 $gotexpected = 0;
1027              
1028             } else {
1029 4         8 ($ntest,$gotexpected,%tests) = $self->_parse($tests);
1030 4 100       12 $nexp = $ntest if ($gotexpected);
1031             }
1032              
1033             # expected
1034 4 100       11 if (exists $opts{'expected'}) {
1035 2 50       4 if ($gotexpected) {
1036 0         0 $self->_die("invalid test format: expected results included twice");
1037             }
1038 2         4 my $expected = $opts{'expected'};
1039              
1040 2 100       6 if (ref($expected) eq 'ARRAY') {
1041 1         3 my @exp = @$expected;
1042 1         1 $nexp = 0;
1043 1         3 foreach my $exp (@exp) {
1044 1         1 $nexp++;
1045 1 50       2 if (ref($exp) eq 'ARRAY') {
1046 1         3 $tests{$nexp}{'expected'} = $exp;
1047             } else {
1048 0         0 $tests{$nexp}{'expected'} = [$exp];
1049             }
1050             }
1051              
1052             } else {
1053 1         2 my($g,%t);
1054 1         3 ($nexp,$g,%t) = $self->_parse($expected);
1055 1 50       4 if ($g) {
1056 0         0 $self->_die("invalid test format: expected results contain '=>'");
1057             }
1058 1         3 foreach my $t (1..$nexp) {
1059 1         4 $tests{$t}{'expected'} = $t{$t}{'args'};
1060             }
1061             }
1062 2         3 $gotexpected = 1;
1063             }
1064              
1065 4 50 66     13 if ($gotexpected &&
      66        
1066             ($nexp != 1 && $nexp != $ntest)) {
1067 0         0 $self->_die("invalid test format: number expected results differs from number of tests");
1068             }
1069              
1070             # func
1071 4         7 my $func;
1072 4 100       6 if (exists $opts{'func'}) {
1073 3         5 $func = $opts{'func'};
1074 3 50       8 if (ref($func) ne 'CODE') {
1075 0         0 $self->_die("invalid test format: func must be a code reference");
1076             }
1077             }
1078              
1079             #
1080             # Compare results
1081             #
1082              
1083 4         9 foreach my $t (1..$ntest) {
1084 16         18 $main::TI_NUM++;
1085              
1086 16 50       24 if ($skip) {
1087 0         0 $self->_skip($skip,$name);
1088 0         0 next;
1089             }
1090              
1091 16 50       45 if ($tests{$t}{'err'}) {
1092 0         0 $self->_not_ok($name);
1093 0         0 $self->diag($tests{$t}{'err'});
1094 0         0 next;
1095             }
1096              
1097 16         22 my($op,@ret);
1098              
1099             # Test results
1100              
1101 16 100       24 if ($gotexpected) {
1102             # Do an 'is' test
1103              
1104 13         19 my @a = ('is');
1105 13 50       24 push(@a,$func) if ($func);
1106 13         16 push(@a,$tests{$t}{'args'});
1107             push(@a,($nexp == 1 ? $tests{'1'}{'expected'}
1108 13 100       24 : $tests{$t}{'expected'}));
1109 13         13 push(@a,$name);
1110              
1111 13         24 ($op,@ret) = $self->_is_result(@a);
1112              
1113             } else {
1114             # Do an 'ok' test
1115              
1116 3         4 my $result = $tests{$t}{'args'};
1117 3 50       7 if (@$result == 1) {
1118 0         0 $result = $$result[0];
1119             }
1120 3         6 ($op,@ret) = $self->_ok_result($result,$name);
1121             }
1122              
1123             # Print it out
1124              
1125 16         18 my($name,@diag);
1126 16         47 my $ok = 1;
1127              
1128 16 50       35 if ($op eq 'skip') {
    50          
1129 0         0 my $reason = shift(@ret);
1130 0         0 $self->_skip($reason);
1131              
1132             } elsif ($op eq 'pass') {
1133 16         23 ($name,@diag) = @ret;
1134 16         31 $self->_ok($name);
1135              
1136             } else {
1137 0         0 ($name,@diag) = @ret;
1138 0         0 $self->_not_ok($name);
1139 0         0 $ok = 0;
1140             }
1141              
1142             next
1143             if ( ($ok && $$self{'quiet'}) ||
1144 16 50 33     71 (! $ok && $$self{'quiet'} == 2) );
      33        
      33        
1145              
1146 16         60 foreach my $diag (@diag) {
1147 0         0 $self->_diag($diag);
1148             }
1149             }
1150             }
1151              
1152             ###############################################################################
1153             # TAP METHODS
1154             ###############################################################################
1155              
1156             sub _diag {
1157 35     35   53 my($self,$message) = @_;
1158 35         1235 print '#' . ' 'x10 . "$message\n";
1159             }
1160              
1161             sub _plan {
1162 10     10   27 my($self,$n,$reason) = @_;
1163 10 100       32 $reason = '' if (! $reason);
1164              
1165 10 50       36 if ($$self{'mode'} eq 'test') {
1166              
1167             # Test mode
1168              
1169 10 100       57 if (! $n) {
1170 2 50       4 $reason = '' if (! $reason);
1171 2         31 print "1..0 # Skipped $reason\n";
1172 2         7 return;
1173             }
1174              
1175 8         80 print "1..$n\n";
1176              
1177             } else {
1178              
1179 0 0       0 if (! $n) {
1180 0         0 print " All tests skipped: $reason\n";
1181             } else {
1182             print " Epected number of tests: $n\n"
1183 0 0       0 unless ($$self{'quiet'});
1184             }
1185             }
1186             }
1187              
1188             sub _ok {
1189 58     58   90 my($self,$name) = @_;
1190              
1191 58 100       97 $name = '' if (! $name);
1192 58         106 $name =~ s/\#//;
1193              
1194 58         136 $$self{'testsrun'} = 1;
1195              
1196             return if ($$self{'mode'} ne 'test' &&
1197 58 0 33     113 $$self{'quiet'});
1198              
1199 58         315 print "ok $main::TI_NUM" . ' 'x(8-length($main::TI_NUM)) . "$name\n";
1200              
1201 58 50 33     231 if ($name =~ /^\d/ && $$self{'quiet'} != 2) {
1202 0         0 $self->_diag('It is strongly recommended that the name of a test not');
1203 0         0 $self->_diag('begin with a digit so it will not be confused with the');
1204 0         0 $self->_diag('test number.');
1205             }
1206             }
1207              
1208             sub _not_ok {
1209 0     0   0 my($self,$name) = @_;
1210 0 0       0 $name = '' if (! $name);
1211 0         0 $name =~ s/\#//;
1212              
1213 0         0 $$self{'testsrun'} = 1;
1214              
1215 0         0 print "not ok $main::TI_NUM" . ' 'x(4-length($main::TI_NUM)) . "$name\n";
1216              
1217 0 0       0 if ($$self{'abort'} == 2) {
    0          
1218 0         0 exit 1;
1219             } elsif ($$self{'abort'}) {
1220 0         0 $$self{'skipall'} = 'Tests aborted due to failed test';
1221             }
1222             }
1223              
1224             sub _skip {
1225 0     0   0 my($self,$reason,$name) = @_;
1226 0 0       0 $name = '' if (! $name);
1227 0         0 $name =~ s/\#//;
1228              
1229 0         0 $$self{'testsrun'} = 1;
1230              
1231             return if ($$self{'mode'} ne 'test' &&
1232 0 0 0     0 $$self{'quiet'});
1233              
1234 0 0       0 print "ok $main::TI_NUM" . ' 'x(8-length($main::TI_NUM)) .
1235             ($name ? "$name " : "") . "# SKIPPED $reason\n";
1236             }
1237              
1238             ###############################################################################
1239             # TEST PARSING METHODS
1240             ###############################################################################
1241              
1242             {
1243             my $l; # current line number
1244             my $sp_opt = qr/\s*/; # optional whitespace
1245             my $sp = qr/\s+/; # required whitespace
1246             my $lparen = qr/\(/; # opening paren
1247             my $lbrack = qr/\[/; # opening brack
1248             my $lbrace = qr/\{/; # opening brace
1249             my $q = qr/\'/; # single quote
1250             my $qq = qr/\"/; # double quote
1251             my $token = qr/\S+/; # a token of non-whitespace characters
1252             my $min_str = qr/.*?/; # a minimum length string
1253             my $results = qr/=>/; # the string to switch to results
1254              
1255             # We'll also need to match delimiters and other special characters that
1256             # signal the end of a token. The default delimiter is just whitespace,
1257             # both other end-of-token regular expressions will include closing
1258             # parens, delimiters, etc.
1259             #
1260             # The end-of-token regexp will return a match for a special character (if
1261             # any) that terminates the token. If a token ends a whitespace or EOL,
1262             # nothing is matched.
1263             #
1264             my $eot = qr/()(?:\s+|$)/;
1265              
1266             # Allowed delimiters is anything except () [] {} alphanumeric,
1267             # underscore, and whitespace.
1268             #
1269             my $delim = qr/[^\'\"\(\)\[\]\{\}a-zA-Z0-9_ \t]/;
1270              
1271             # This takes a string which may contain a partial or complete
1272             # descritpion of any number of tests, and parses it.
1273             #
1274             # The string is multiline, and tests must be separated from each other
1275             # by one or more blank lines. Lines starting with a pound sign (#)
1276             # are comments.
1277             #
1278             # A test may include arguments (or obtained results), expected results,
1279             # or both.
1280             #
1281             # Returns
1282             # ($n,$gotboth,%tests)
1283             # where
1284             # $n is the number of tests
1285             # $gotboth is 1 if both arguments and expected results are obtained
1286             # $tests{$i} is the i'th test.
1287             #
1288             sub _parse {
1289 5     5   9 my($self,$string) = @_;
1290 5         6 my $t = 0;
1291 5         5 my $gotboth = -1;
1292 5         6 my %tests = ();
1293              
1294             # Split on newlines
1295 5         37 $string = [ split(/\n/s,$string) ];
1296              
1297 5         9 $t = 0;
1298 5         10 while (@$string) {
1299 17         31 my $test = $self->_next_test($string);
1300 17 50       31 last if (! @$test);
1301              
1302             # All tests must contain both args/results OR only one of them.
1303 17         25 my ($err,$both,$args,$results) = $self->_parse_test($test);
1304 17 100       42 if ($gotboth == -1) {
    50          
1305 5         6 $gotboth = $both;
1306             } elsif ($gotboth != $both) {
1307 0         0 $err = "Malformed test [$l]: expected results for some tests, not others";
1308             }
1309              
1310 17         23 $t++;
1311 17         42 $tests{$t}{'err'} = $err;
1312 17         27 $tests{$t}{'args'} = $args;
1313 17 100       49 $tests{$t}{'expected'} = $results if ($gotboth);
1314             }
1315              
1316 5         47 return ($t,$gotboth,%tests);
1317             }
1318              
1319             # Get all lines up to the end of lines or a blank line. Both
1320             # signal the end of a test.
1321             #
1322             sub _next_test {
1323 17     17   23 my($self,$list) = @_;
1324 17         17 my @test;
1325 17         18 my $started = 0;
1326              
1327 17         18 while (1) {
1328 34 100       50 last if (! @$list);
1329 33         40 my $line = shift(@$list);
1330              
1331 33         93 $line =~ s/^\s*//;
1332 33         105 $line =~ s/\s*$//;
1333              
1334             # If it's a blank line, add it to the test. If we've
1335             # already done test lines, then this signals the end
1336             # of the test. Otherwise, this is before the test,
1337             # so keep looking.
1338 33 100       58 if ($line eq '') {
1339 16         25 push(@test,$line);
1340 16 50       23 next if (! $started);
1341 16         21 last;
1342             }
1343              
1344             # Comments are added to the test as a blank line.
1345 17 50       32 if ($line =~ /^#/) {
1346 0         0 push(@test,'');
1347 0         0 next;
1348             }
1349              
1350 17         28 push(@test,$line);
1351 17         23 $started = 1;
1352             }
1353              
1354 17 50       27 return [] if (! $started);
1355 17         24 return \@test;
1356             }
1357              
1358             # Parse an entire test. Look for arguments, =>, and expected results.
1359             #
1360             sub _parse_test {
1361 17     17   33 my($self,$test) = @_;
1362 17         22 my($err,$both,@args,@results);
1363              
1364 17         19 my $curr = 'args';
1365              
1366 17         26 while (@$test) {
1367              
1368 68 100       118 last if (! $self->_test_line($test));
1369              
1370             # Check for '=>'
1371              
1372 51 100       80 if ($self->_parse_begin_results($test)) {
1373 7 50       15 if ($curr eq 'args') {
1374 7         8 $curr = 'results';
1375             } else {
1376 0         0 return ("Malformed test [$l]: '=>' found twice");
1377             }
1378 7         13 next;
1379             }
1380              
1381             # Get the next item(s) to add.
1382              
1383 44         79 my($err,$match,@val) = $self->_parse_token($test,$eot);
1384 44 50       75 return ($err) if ($err);
1385              
1386 44 100       71 if ($curr eq 'args') {
1387 29         68 push(@args,@val);
1388             } else {
1389 15         35 push(@results,@val);
1390             }
1391             }
1392              
1393 17 100       29 $both = ($curr eq 'results' ? 1 : 0);
1394 17         47 return ("",$both,\@args,\@results);
1395             }
1396              
1397             # Makes sure that the first line in the test contains
1398             # something. Blank lines are ignored.
1399             #
1400             sub _test_line {
1401 129     129   154 my($self,$test) = @_;
1402              
1403 129   66     501 while (@$test &&
      100        
1404             (! defined($$test[0]) ||
1405             $$test[0] eq '')) {
1406 33         39 shift(@$test);
1407 33         38 $l++;
1408 33         84 next;
1409             }
1410 129 100       270 return 1 if (@$test);
1411 17         28 return 0;
1412             }
1413              
1414             # Check for '=>'.
1415             #
1416             # Return 1 if found, 0 otherwise.
1417             #
1418             sub _parse_begin_results {
1419 51     51   75 my($self,$test) = @_;
1420              
1421 51 100       246 return 1 if ($$test[0] =~ s/^ $sp_opt $results $eot //x);
1422 44         92 return 0;
1423             }
1424              
1425             # Gets the next item to add to the current list.
1426             #
1427             # Returns ($err,$match,@val) where $match is the character that
1428             # matched the end of the current element (either a delimiter,
1429             # closing character, or nothing if the element ends on
1430             # whitespace/newline).
1431             #
1432             sub _parse_token {
1433 61     61   83 my($self,$test,$EOT) = @_;
1434              
1435 61         63 my($err,$found,$match,@val);
1436              
1437             {
1438 61 50       62 last if (! $self->_test_line($test));
  61         97  
1439              
1440             # Check for quoted
1441              
1442 61         106 ($err,$found,$match,@val) = $self->_parse_quoted($test,$EOT);
1443 61 50       98 last if ($err);
1444 61 100       88 if ($found) {
1445             # '' remains ''
1446 2         3 last;
1447             }
1448              
1449             # Check for open
1450              
1451 59         122 ($err,$found,$match,@val) = $self->_parse_open_close($test,$EOT,$lparen,')');
1452 59 50       111 last if ($err);
1453 59 100       90 if ($found) {
1454             # () is an empty list
1455 7 50 66     18 if (@val == 1 && $val[0] eq '') {
1456 0         0 @val = ();
1457             }
1458 7         8 last;
1459             }
1460              
1461 52         91 ($err,$found,$match,@val) = $self->_parse_open_close($test,$EOT,$lbrack,']');
1462 52 50       93 last if ($err);
1463 52 100       72 if ($found) {
1464             # [] is []
1465 2 50 33     9 if (@val == 1 && $val[0] eq '') {
1466 2         5 @val = ([]);
1467             } else {
1468 0         0 @val = ( [@val] );
1469             }
1470 2         3 last;
1471             }
1472              
1473 50         89 ($err,$found,$match,@val) = $self->_parse_open_close($test,$EOT,$lbrace,'}');
1474 50 50       100 last if ($err);
1475 50 100       81 if ($found) {
1476 2 50 33     10 if (@val == 1 && $val[0] eq '') {
    0 0        
    0          
1477 2         14 @val = ( {} );
1478             } elsif (@val % 2 == 0) {
1479             # Even number of elements
1480 0         0 @val = ( {@val} );
1481             } elsif (! defined $val[$#val] ||
1482             $val[$#val] eq '') {
1483             # Odd number of elements with nothing as the
1484             # last element.
1485 0         0 pop(@val);
1486 0         0 @val = ( {@val} );
1487             } else {
1488             # Odd number of elements not supported for a hash
1489 0         0 $err = "Malformed test [$l]: hash with odd number of elements";
1490             }
1491 2         6 last;
1492             }
1493              
1494             # Check for some other token
1495              
1496 48         81 ($err,$found,$match,@val) = $self->_parse_simple_token($test,$EOT);
1497 48 50       89 last if ($err);
1498              
1499 48         60 last;
1500             }
1501              
1502 61 50       88 return ($err) if ($err);
1503 61 50       85 return ("Malformed test: unable to parse") if (! $found);
1504              
1505 61         90 foreach my $v (@val) {
1506 68 50       100 $v = '' if ($v eq '__blank__');
1507 68 50       94 $v = undef if ($v eq '__undef__');
1508 68 100       143 $v =~ s/__nl__/\n/g if ($v);
1509             }
1510 61 50       198 return (0,$match,@val) if ($found);
1511 0         0 return (0,0);
1512             }
1513              
1514             ###
1515             ### The next few routines parse parts of the test. Each of them
1516             ### take as arguments:
1517             ###
1518             ### $test : the listref containing the unparsed portion of
1519             ### the test
1520             ### $EOT : the end of a token
1521             ###
1522             ### + other args as needed.
1523             ###
1524             ### They all return:
1525             ###
1526             ### $err : a string containing the error (if any)
1527             ### $found : 1 if something matched
1528             ### $match : the character which terminates the current
1529             ### token signaling the start of the next token
1530             ### (this will either be a delimiter, a closing
1531             ### character, or nothing if the string ended on
1532             ### whitespace or a newline)
1533             ### @val : the value (or values) of the token
1534             ###
1535              
1536             # Check for a quoted string
1537             # 'STRING'
1538             # "STRING"
1539             # The string must be on one line, and everything up to the
1540             # closing quote is included (the quotes themselves are
1541             # stripped).
1542             #
1543             sub _parse_quoted {
1544 61     61   77 my($self,$test,$EOT) = @_;
1545              
1546 61 100 66     1263 if ($$test[0] =~ s/^ $sp_opt $q ($min_str) $q $EOT//x ||
    50 33        
1547             $$test[0] =~ s/^ $sp_opt $qq ($min_str) $qq $EOT//x) {
1548 2         8 return (0,1,$2,$1);
1549              
1550             } elsif ($$test[0] =~ /^ $sp_opt $q/x ||
1551             $$test[0] =~ /^ $sp_opt $qq/x) {
1552 0         0 return ("Malformed test [$l]: improper quoting");
1553             }
1554 59         166 return (0,0);
1555             }
1556              
1557             # Parses an open/close section.
1558             #
1559             # ( TOKEN TOKEN ... )
1560             # (, TOKEN, TOKEN, ... )
1561             #
1562             # $open is a regular expression matching the open, $close is the
1563             # actual closing character.
1564             #
1565             # After the closing character must be an $EOT.
1566             #
1567             sub _parse_open_close {
1568 161     161   239 my($self,$test,$EOT,$open,$close) = @_;
1569              
1570             # See if there is an open
1571              
1572 161         175 my($del,$newEOT);
1573 161 100       4751 if ($$test[0] =~ s/^ $sp_opt $open ($delim) $sp_opt //x) {
    100          
1574 2         5 $del = $1;
1575 2         26 $newEOT = qr/ $sp_opt ($|\Q$del\E|\Q$close\E) /x;
1576              
1577             } elsif ($$test[0] =~ s/^ $sp_opt $open $sp_opt //x) {
1578 9         19 $del = '';
1579 9         100 $newEOT = qr/ ($sp_opt $|$sp_opt \Q$close\E|$sp) /x;
1580              
1581             } else {
1582 150         523 return (0,0);
1583             }
1584              
1585             # If there was, then we need to read tokens until either:
1586             # the string is all used up => error
1587             # $close is found
1588              
1589 11         32 my($match,@val);
1590 11         12 while (1) {
1591              
1592             # Get a token. We MUST find something valid even if it is
1593             # an empty list followed by the closing character.
1594 17         57 my($e,$m,@v) = $self->_parse_token($test,$newEOT);
1595 17 50       40 return ($e) if ($e);
1596 17         57 $m =~ s/^$sp//;
1597              
1598             # If we ended on nothing, and $del is something, then we
1599             # ended on a newline with no delimiter. The next line MUST
1600             # start with a delimiter or close character or the test is
1601             # invalid.
1602              
1603 17 50 66     43 if (! $m && $del) {
1604              
1605 0 0       0 if (! $self->_test_line($test)) {
1606 0         0 return ("Malformed test [$l]: premature end of test");
1607             }
1608              
1609 0 0       0 if ($$test[0] =~ s/^ $sp_opt $newEOT //x) {
1610 0         0 $m = $1;
1611             } else {
1612 0         0 return ("Malformed test [$l]: unexpected token (expected '$close' or '$del')");
1613             }
1614             }
1615              
1616             # Figure out what value(s) were returned
1617 17 50 66     51 if ($m eq $close && ! @v) {
1618 0         0 push(@val,'');
1619             } else {
1620 17         29 push(@val,@v);
1621             }
1622              
1623 17 100       44 last if ($m eq $close);
1624              
1625             }
1626              
1627             # Now we need to find out what character ends this token:
1628              
1629 11 100       22 if ($$test[0] eq '') {
1630             # Ended at EOL
1631 2         10 return (0,1,'',@val);
1632             }
1633 9 50       81 if ($$test[0] =~ s/^ $sp_opt $EOT //x) {
1634 9         50 return (0,1,$1,@val);
1635             } else {
1636 0         0 return ("Malformed test [$l]: unexpected token");
1637             }
1638             }
1639              
1640             # Checks for a simple token.
1641             #
1642             sub _parse_simple_token {
1643 48     48   64 my($self,$test,$EOT) = @_;
1644              
1645 48         485 $$test[0] =~ s/^ $sp_opt (.*?) $EOT//x;
1646 48         194 return (0,1,$2,$1);
1647             }
1648             }
1649              
1650             1;
1651             # Local Variables:
1652             # mode: cperl
1653             # indent-tabs-mode: nil
1654             # cperl-indent-level: 3
1655             # cperl-continued-statement-offset: 2
1656             # cperl-continued-brace-offset: 0
1657             # cperl-brace-offset: 0
1658             # cperl-brace-imaginary-offset: 0
1659             # cperl-label-offset: 0
1660             # End: