File Coverage

blib/lib/Test/Inter.pm
Criterion Covered Total %
statement 553 765 72.2
branch 261 480 54.3
condition 61 152 40.1
subroutine 49 64 76.5
pod 24 24 100.0
total 948 1485 63.8


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