File Coverage

blib/lib/Test/Usage.pm
Criterion Covered Total %
statement 218 250 87.2
branch 55 76 72.3
condition 23 38 60.5
subroutine 36 40 90.0
pod 8 15 53.3
total 340 419 81.1


line stmt bran cond sub pod time code
1             package Test::Usage;
2              
3 4     4   91043 use 5.008;
  4         15  
  4         261  
4             our $VERSION = '0.08';
5              
6             =head1 NAME
7              
8             Test::Usage - A different approach to testing: selective, quieter, colorful.
9              
10             =head1 SYNOPSIS
11              
12             package Foo_T;
13             use strict;
14             use warnings;
15             use Test::Usage;
16             use Foo;
17              
18             example('e1', sub { ... ok(...); ... die "Uh oh"; ... });
19             example('a1', sub { ... ok(...); ... });
20             example('a2', sub {
21             my $f = Foo->new();
22             my $got_foo = $f->foo();
23             my $exp_foo = 'FOO';
24             ok(
25             $got_foo eq $exp_foo,
26             "Expecting foo() to return '$exp_foo'.",
27             "But got '$got_foo'."
28             );
29             });
30              
31             Then, from the command line:
32              
33             # Run all examples found in the test module, that is, e1, a1,
34             # and a2.
35             perl -MFoo_T -e test
36              
37             # Run all examples whose label matches glob 'a*': a1, a2.
38             perl -MFoo_T -e 'test(a => "a*")'
39              
40             # Run example 'a2', reporting successes also, but without color.
41             perl -MFoo_T -e 'test(a => "a2", v => 2, c => 0)'
42              
43             # Run and summarize all examples in all "*_T.pm" files found
44             # under current directory.
45             perl -MTest::Usage -e files
46              
47             The module exports some of its methods to the calling package and some
48             to main, to make them easier to use, usually from the shell. When the
49             developer wishes to run a test, he invokes it as shown in the synopsis
50             (perhaps with a coating of shell syntaxic sugar).
51              
52             Bleah. Under cygwin's screen, Win32Console fails, so do this before
53             invoking screen:
54              
55             export USE_ANSI_COLOR=1
56              
57             =cut
58              
59             # --------------------------------------------------------------------
60 4     4   20 use strict;
  4         9  
  4         121  
61 4     4   22 use warnings;
  4         21  
  4         238  
62 4     4   19 use Carp;
  4         8  
  4         327  
63             $Carp::MaxArgLen = 0; # So error messages don't get truncated.
64 4     4   5985 use File::Temp qw(tempfile);
  4         126705  
  4         358  
65 4     4   36 use File::Find;
  4         7  
  4         243  
66 4     4   4311 use File::Slurp;
  4         71097  
  4         639  
67 4     4   1580 use File::Spec;
  4         9  
  4         94  
68 4     4   17334 use IO::File;
  4         4559  
  4         1823  
69              
70             # Main accumulator.
71             my $t;
72              
73             # --------------------------------------------------------------------
74             # Color management.
75              
76             my $gColor = {};
77              
78             # Explicit initializations, for the maintainer's benefit.
79             $gColor->{id} = undef;
80             $gColor->{palette} = undef;
81             # Will be set only for Win32::Console usage.
82             $gColor->{out} = undef;
83              
84             if (! defined($ENV{USE_ANSI_COLOR}) && $^O eq 'MSWin32') {
85             eval "use Win32::Console";
86             if ($@ eq '') {
87             $gColor->{out} = Win32::Console->new(STD_OUTPUT_HANDLE())
88             or die "Couldn't get new Win32::Console instance.";
89             $gColor->{palette} = {
90             cBoldWhite => $Win32::Console::FG_WHITE | $Win32::Console::BG_BLACK,
91             cBoldMagenta => $Win32::Console::FG_LIGHTMAGENTA | $Win32::Console::BG_BLACK,
92             cBoldCyan => $Win32::Console::FG_LIGHTCYAN | $Win32::Console::BG_BLACK,
93             cYellow => $Win32::Console::FG_YELLOW | $Win32::Console::BG_BLACK,
94             cBoldGreen => $Win32::Console::FG_LIGHTGREEN | $Win32::Console::BG_BLACK,
95             cBoldRed => $Win32::Console::FG_LIGHTRED | $Win32::Console::BG_BLACK,
96             cWhite => $Win32::Console::FG_WHITE | $Win32::Console::BG_BLACK,
97             cBlack => $Win32::Console::FG_BLACK | $Win32::Console::BG_BLACK,
98             };
99             $gColor->{id} = 'Win32Console';
100             }
101             }
102             else {
103 4     4   14539 eval "use Term::ANSIColor ()";
  4         42921  
  4         75  
104             if ($@ eq '') {
105             $gColor->{palette} = {
106             cBoldWhite => 'bold white',
107             cBoldMagenta => 'bold magenta',
108             cBoldCyan => 'bold cyan',
109             cYellow => 'yellow',
110             cBoldGreen => 'bold green',
111             cBoldRed => 'bold red',
112             cWhite => 'white',
113             cBlack => 'black',
114             };
115             $gColor->{id} = 'ANSI';
116             }
117             }
118              
119             # --------------------------------------------------------------------
120             # Verbosity level constants.
121              
122 4     4   56 use constant REPORT_NOTHING => 0;
  4         9  
  4         286  
123 4     4   20 use constant REPORT_FAILURES => 1;
  4         6  
  4         169  
124 4     4   21 use constant REPORT_ALL => 2;
  4         7  
  4         16443  
125              
126             # --------------------------------------------------------------------
127             # Default options. Change if necessary.
128             #
129             # Implementation note: all leaf keys of %_D must be different, since
130             # they become keys to $t->{options}.
131              
132             my %_D;
133             # Can be set by test().
134             $_D{t} = {
135             # Accept tests whose label matches this glob.
136             a => '*',
137             # Exclude tests whose label matches this glob.
138             e => '__*',
139             # Print a summary line if true.
140             s => 1,
141             # Verbosity level.
142             v => REPORT_FAILURES,
143             # Fail tests systematically if true.
144             f => 0,
145             };
146             # Can be set by files().
147             $_D{f} = {
148             # Directory in which to look for files.
149             d => '.',
150             # Test files whose name matches this glob.
151             g => '*_T.pm',
152             # Look for files recursively through dir if true.
153             r => 1,
154             # Add to Perl %INC path.
155             i => '',
156             # Option values to pass to test() for each file.
157             t => {},
158             };
159             # Miscellaneous. Can be set by test() or files().
160             $_D{m} = {
161             # Use color if possible.
162             c => 1,
163             };
164             # Color map.
165             $_D{c} = {
166             what => 'cBoldWhite',
167             died => 'cBoldMagenta',
168             warned => 'cBoldCyan',
169             summary => 'cYellow',
170             success => 'cBoldGreen',
171             failure => 'cBoldRed',
172             diag => 'cBoldRed',
173             default => 'cWhite',
174             };
175              
176             # --------------------------------------------------------------------
177              
178             =head1 METHODS AND FUNCTIONS
179              
180             All methods apply to a single instance of Test::Usage, named $t,
181             initialized by import().
182              
183             The module defines the following methods and functions.
184              
185             =cut
186              
187             # --------------------------------------------------------------------
188              
189             =head2 import ($pkg)
190              
191             Sets $t to an empty hash ref, blessed in Test::Usage.
192              
193             Resets $t's counters to 0:
194              
195             Number of 'ok' that failed.
196             Number of 'ok' that succeeded.
197             Number of examples that died.
198             Number of examples that had warnings.
199              
200             Sets $t's default label to '-'.
201              
202             Resets $t's options to default values. Here are the as-shipped
203             values:
204              
205             For the test() method:
206              
207             a => '*' # Accept tests whose label matches this glob.
208             e => '__*' # Exclude tests whose label matches this glob.
209             s => 1 # Print a summary line if true.
210             v => REPORT_FAILURES # Verbosity level.
211             f => 0 # Fail tests systematically if true.
212              
213             For the files() method:
214              
215             d => '.' # Directory in which to look for files.
216             g => '*_T.pm' # Test files whose name matches this glob.
217             r => 1 # Look for files recursively through dir if true.
218             i => '' # Add to Perl %INC path.
219             t => {} # Option values to pass to test() for each file.
220              
221             For both test() and files():
222              
223             c => 1 # Use color if possible.
224              
225             Exports these methods to the calling package:
226              
227             t
228             example
229             ok
230             ok_labeled
231             diag
232              
233             Exports these methods to main:
234              
235             t
236             test
237             files
238             labels
239             plabels
240              
241             =cut
242              
243             sub import {
244 4     4   49 $t = bless {}, __PACKAGE__;
245 4         13 my $caller = caller;
246 4         414 $t->{name} = $caller;
247             # example() will push elements like [$label, $sub_ref]
248             # onto this array ref.
249 4         13 $t->{examples} = [];
250             # Default label.
251 4         12 $t->{label} = '-';
252             # Private counters.
253 4         384 $t->{nb_succ} = 0;
254 4         8 $t->{nb_fail} = 0;
255             # Incremented respectively when a die or a warning occur within an
256             # example().
257 4         9 $t->{died} = 0;
258 4         10 $t->{warned} = 0;
259              
260 4         14 reset_options();
261              
262 4     2   789 eval << "EOT";
  2         215  
  0         0  
  127         47564  
  21         3489  
  18         7874  
263             package $caller;
264             *t = sub { \$t };
265             *example = sub { \$t->example(\@_) };
266             *ok = sub { \$t->ok(\@_) };
267             *ok_labeled = sub { \$t->ok_labeled(\@_) };
268             *diag = sub { \$t->diag(\@_) };
269             EOT
270 4 100   11   194 eval "*main::t = sub { \$t }" unless $caller eq 'main';
  11         34559  
271 4     0   775 eval << "EOT";
  0         0  
  0         0  
  0         0  
  12         2278  
272             package main;
273             *test = sub { \$t->test(\@_) };
274             *files = sub { \$t->files(\@_) };
275             *labels = sub { \$t->labels(\@_) };
276             *plabels = sub { \$t->plabels(\@_) };
277             EOT
278             }
279              
280             # --------------------------------------------------------------------
281              
282             =head2 $pkg::t (), ::t ()
283              
284             Both return $t, effectively giving access to all Test::Usage methods.
285              
286             =cut
287              
288             # --------------------------------------------------------------------
289              
290             =head2 $pkg::example ($label, $sub_ref)
291              
292             Add a test example labeled $label and implemented by $sub_ref to the
293             tests that can be run by $t->test().
294              
295             $label is an arbitrary string that is used to identify the example.
296             The label will be displayed when reporting tests results. Labels can
297             be chosen to make it easy to run selected subsets; for example, you
298             may want to label a bunch of examples that you usually run together
299             with a common prefix.
300              
301             The $sub_ref is a reference to the subroutine implementing the test.
302             It often calls a number of ok(), wrapped in setup/tear-down
303             scaffolding to express the intended usage.
304              
305             Here's a full example:
306              
307             example('t1', sub {
308             my $f = Foo->new();
309             my $exp = 1;
310             my $got = $f->get_val();
311             ok(
312             $got == $exp,
313             "Expected get_val() to return $exp for a new Foo object.",
314             "But got $got.",
315             );
316             });
317              
318             =cut
319              
320             sub example {
321 11     11 1 22 my ($self, $label, $sub_ref) = @_;
322             # We store test examples in an array to guarantee that they will
323             # be executed in the order they appear in the test file.
324 11         15 push @{$self->{examples}},
  11         55  
325             Test::Usage::Example->new($label, $sub_ref);
326             }
327              
328             # --------------------------------------------------------------------
329              
330             =head2 $pkg::ok ($bool, $exp_msg, $got_msg)
331              
332             $bool is an expression that will be evaluated as true or false, and
333             thus determine the return value of the method. Also, if $bool is
334             true, $t will increment the number of successful tests it has seen,
335             else the number of failed tests.
336              
337             Note that $bool will be evaluated in list context; for example, if you
338             want to use a bind operator here, make sure you wrap it with 'scalar'.
339             For example:
340              
341             ok(scalar($x =~ /abc/),
342             "Expected \$x to match /abc/.",
343             "But its value was '$x'."
344             );
345              
346             In that example, if 'scalar' is not used, the bind operator is
347             evaluated in list context, and if there is no match, an empty list is
348             returned, which results in ok() receiving only the last two arguments.
349              
350             If the test() flags are such that the result of the ok() is to be
351             printed, something like one of the following will be printed:
352              
353             ok a1
354             # Expected $x to match /abc/.
355              
356             not ok a1
357             # Expected $x to match /abc/.
358             # But its value was 'def'.
359              
360             =cut
361              
362             sub ok {
363 84     84 1 174 my ($self, $bool, $exp_msg, $got_msg) = @_;
364 84         209 $self->_confirm($self->{label}, $bool, $exp_msg, $got_msg);
365             }
366              
367             # --------------------------------------------------------------------
368              
369             =head2 $pkg::ok_labeled ($sub_label, $bool, $exp_msg, $got_msg)
370              
371             Same as ok(), except that ".$sub_label" is appended to the label in
372             the printed output. This is useful for examples containing many ok()
373             whose labels we want to distinguish.
374              
375             =cut
376              
377             sub ok_labeled {
378 1     1 1 2 my ($self, $sub_label, $bool, $exp_msg, $got_msg) = @_;
379 1         6 $self->_confirm(
380             $self->{label} . '.' . $sub_label,
381             $bool, $exp_msg, $got_msg
382             );
383             }
384              
385             # --------------------------------------------------------------------
386              
387             =begin maintenance $self->_confirm ($label, $bool, $exp_msg, $got_msg)
388              
389             Returns true if argument $bool is true, false otherwise. Also returns
390             false if the 'fail' option is set.
391              
392             =end maintenance
393              
394             =cut
395              
396             sub _confirm {
397 85     85   140 my ($self, $label, $bool, $exp_msg, $got_msg) = @_;
398 85 100       141 $bool = 0 if $self->options()->{f};
399 85 100 66     309 ($bool && ++$self->{nb_succ}) || ++$self->{nb_fail};
400 85         169 my $verbosity = $self->options()->{v};
401 85 100       193 $exp_msg = '' unless defined $exp_msg;
402 85 100       146 $got_msg = '' unless defined $got_msg;
403 85 100 100     346 if ($verbosity == REPORT_ALL || ($verbosity == REPORT_FAILURES && ! $bool)) {
      66        
404 43 100       916 $self->printk('what', $bool ? 'ok ' : 'not ok ');
405 43 100       347 my $printk_type = $bool ? 'success' : 'failure';
406 43         218 $self->printk($printk_type, "$label\n");
407 43 100       266 if ($exp_msg ne '') {
408 38         143 $exp_msg =~ s/^/ # /gm;
409 38         225 $exp_msg =~ s/\n*$/\n/;
410 38         81 $self->printk($printk_type, $exp_msg);
411             }
412 43 100 100     357 if (! $bool && $got_msg ne '') {
413 27         88 $got_msg =~ s/^/ # /gm;
414 27         295 $got_msg =~ s/\n*$/\n/;
415 27         55 $self->printk($printk_type, $got_msg);
416             }
417             }
418 85         765 return $bool;
419             }
420              
421             # --------------------------------------------------------------------
422              
423             =head2 $pkg::diag (@msgs)
424              
425             Prefixes each line of each string in @msgs with ' # ' and displays
426             them using the 'diag' color tag. Returns true (contrary to
427             Test::Builder, Test::More, et al.).
428              
429             =cut
430              
431             sub diag {
432 0     0 1 0 my($self, @msgs) = @_;
433 0 0 0     0 return unless @msgs && $self->options()->{v} > 0;
434             # Prefix each line to make it a comment, to avoid interference
435             # when used with Test::Harness.
436 0         0 foreach (@msgs) {
437 0 0       0 $_ = 'undef' unless defined;
438 0         0 s/^/ # /gm;
439             }
440 0 0       0 push @msgs, "\n" unless $msgs[-1] =~ /\n\Z/;
441 0         0 $self->printk('diag', "@msgs");
442 0         0 return 1;
443             }
444              
445             # --------------------------------------------------------------------
446              
447             =head2 ::labels ()
448              
449             Returns a ref to an array holding the labels of all the examples, in
450             the order they were defined.
451              
452             =cut
453              
454             sub labels {
455 1     1 0 2 [map {$_->{label}} @{$_[0]->{examples}}];
  2         10  
  1         4  
456             }
457              
458             # --------------------------------------------------------------------
459              
460             =head2 ::plabels ()
461              
462             Prints space separated known labels to STDOUT.
463              
464             =cut
465              
466             sub plabels {
467 0     0 0 0 print STDOUT join ' ', @{::labels()};
  0         0  
468             }
469              
470             # --------------------------------------------------------------------
471              
472             =head2 ::test (%options)
473              
474             Clears counters and runs all the examples in the module, subject to
475             the constraints indicated by %options. If %options is undefined or if
476             some of its keys are missing, default values apply.
477              
478             Returns a list containing the following values:
479              
480             Name of the module being tested.
481             Number of seconds it took to run the examples.
482             Number of 'ok' that succeeded.
483             Number of 'ok' that failed.
484             Number of examples that died.
485             Number of examples that had warnings.
486              
487             Here is the meaning and default value of the elements of %options:
488              
489             =over 4
490              
491             =item a => '*' # Accept.
492              
493             The value is a glob. Tests whose label matches this glob will be run.
494             All tests are run when the value is the default.
495              
496             =item e => '__*' # Exclude.
497              
498             The value is a glob. Tests whose label matches this glob will not be
499             run. I use this when I want to keep a test in the test module, but I
500             don't want to run it for some reason. When using the default value,
501             prepending the string '__' to a test label will effectively
502             disactivate it. When you are ready to run those tests, remove the '__'
503             prefix from the label, or pass the 'e => ""' argument.
504              
505             =item v => 1 # Verbosity.
506              
507             Determines the verbosity of the testing mechanism:
508              
509             0: Display no individual results.
510             1: Display individual results for failing tests only.
511             2: Display individual results for all tests.
512              
513             =item s => 1 # Summary.
514              
515             If true, two lines like the following will wrap the test output:
516              
517             # module_name
518             ...
519             # +3 -1 -d +w (00h:00m:02s) module_name
520              
521             That means that of the ok*() calls that were made, 3 succeeded and 1
522             failed, that no dies but some warnings occurred, and it took about 2
523             seconds to run.
524              
525             =item f => 0 # Fail.
526              
527             If true, any ok*() invoked will act as though it failed. When combined
528             with a verbosity of 1 or 2, (to display failures), you will see all
529             the actual messages that would get printed when failures occur.
530              
531             =back
532              
533             =cut
534              
535             {
536             my $tee_hdl;
537              
538             sub _tee_to {
539 0     0   0 my ($self, $file_name) = @_;
540 0 0       0 $tee_hdl = IO::File->new($file_name, O_WRONLY|O_APPEND)
541             or die "Couldn't write to '$file_name'.";
542             }
543              
544             sub test {
545 15     15 1 41 my ($self, %options) = @_;
546 15         30 $self->{nb_succ} = 0;
547 15         24 $self->{nb_fail} = 0;
548 15         28 $self->{died} = 0;
549 15         20 $self->{warned} = 0;
550             # Run examples matching this glob.
551 15 100       58 my $accept = glob_to_regex(
552             defined($options{a})
553             ? $options{a}
554             : $self->{options}{a}
555             );
556             # Don't run examples matching this glob.
557 15 100       61 my $exclude = glob_to_regex(
558             defined($options{e})
559             ? $options{e}
560             : $self->{options}{e}
561             );
562 15 50       43 $self->{options}{c} = $options{c} if defined $options{c};
563 15 100       59 $self->{options}{v} = _adjust_verbosity($options{v}) if defined $options{v};
564 15 100       42 $self->{options}{f} = $options{f} if defined $options{f};
565 15         31 my $start_time = time;
566             # Run the examples.
567 15         65 $self->printk('summary', '# ' . $self->{name} . "\n");
568 15         92 for my $example (@{$self->{examples}}) {
  15         36  
569 107         161 my $label = $example->{label};
570 107         141 my $sub_ref = $example->{sub_ref};
571 107 100       469 next unless $label =~ /$accept/;
572 56 100       156 next if $label =~ /$exclude/;
573 48         73 $self->{label} = $label;
574 48         57 my $warnings = '';
575 48         59 eval {
576             local $SIG{__DIE__} = sub {
577 1     1   314 Carp::confess();
578 48         260 };
579             local $SIG{__WARN__} = sub {
580 1     1   9 $DB::single = 1;
581 1         156 $warnings .= Carp::longmess(@_) . "\n";
582 48         227 };
583 48         141 $sub_ref->($self);
584             };
585 48 100       490 if ($warnings) {
586 1         4 $self->printk('keyword', 'WARNED ');
587 1         7 $self->printk('warned', $warnings);
588 1         6 ++$self->{warned};
589             }
590 48 100       113 if ($@) {
591 1         4 $self->printk('keyword', 'DIED ');
592 1         8 $self->printk('died', $@);
593 1         9 ++$self->{died};
594             }
595             }
596 15         33 my $time_took = time - $start_time;
597 15 100       34 my $summary = join(' ',
    100          
598             $self->sprintk('summary', ' #'),
599             $self->sprintk('success', '+' . $self->{nb_succ}),
600             $self->sprintk('failure', '-' . $self->{nb_fail}),
601             $self->sprintk('died', ($self->{died} ? '+' : '-') . 'd'),
602             $self->sprintk('warned', ($self->{warned} ? '+' : '-') . 'w'),
603             $self->sprintk('summary', '(' . _elapsed_str($time_took) . ') '),
604             $self->sprintk('summary', $self->{name}),
605             ) . "\n";
606 15 50       50 if ($tee_hdl) {
607 0         0 print $tee_hdl
608             ' nb_succ ', $self->{nb_succ},
609             ' nb_fail ', $self->{nb_fail},
610             ' died ', $self->{died},
611             ' warned ', $self->{warned},
612             ' time_took ', _elapsed_str($time_took),
613             "\n"
614             ;
615             }
616 15 100       54 if (defined($options{s}) ? $options{s} : $self->{options}{s}) {
    100          
617 13         43 print $summary;
618             # $self->printk('summary', $summary);
619             }
620 15         99 $self->reset_options();
621             return
622 15         187 $self->{name},
623             $time_took,
624             $self->{nb_succ},
625             $self->{nb_fail},
626             $self->{died},
627             $self->{warned},
628             ;
629             }
630             }
631              
632             # --------------------------------------------------------------------
633              
634             =head2 ::files (%options)
635              
636             After having found all the files that correspond to the criteria
637             defined in %options (for example, directory to look in), for each file
638             calls perl in a subshell to run something like this:
639              
640             perl -M$file -e 'test()'
641              
642             The results of each run are collected, examined and tallied, and a
643             summary line and a '1..n' line are displayed, something like this:
644              
645             # Total +7 -5 0d 1w (00h:00m:00s) in 4 modules
646             1..12
647              
648             Returns a list of:
649              
650             Number of seconds it took to run the examples.
651             Number of 'ok' that succeeded.
652             Number of 'ok' that failed.
653             Number of examples that died.
654             Number of examples that had warnings.
655             Number of modules that were run.
656              
657             All values in %options are optional. Their meaning and default value
658             are as follows:
659              
660             =over 4
661              
662             =item d* => '.' # Glob Directory.
663              
664             All options starting with the letter 'd' designate directories in
665             which to look for files matching the glob specified by option 'g'.
666             These directories should be in perl's current module search path, else
667             add to the path using the 'i' option.
668              
669             =item g => '*_T.pm' # Glob for files to test.
670              
671             Only files matching this glob will be tested.
672              
673             =item r => 1 # Search for files recursively.
674              
675             If set to true, files matching the 'g' glob will be searched for
676             recursively in all subdirs starting from (and including) those
677             specified by the 'd' options. FIXME: Currently, it's always true.
678              
679             =item i* => '' # Directories to add to perl @INC paths.
680              
681             All options starting with the letter 'i' designate directories that
682             you want to add to the @INC path for finding modules. They will be
683             added in the order of the sorted 'i*' keys.
684              
685             =item t => {} # test() options.
686              
687             These options will be passed to the test() method, invoked for each
688             tested file.
689              
690             =item follow => 1 # Follow symlinks when looking for files.
691              
692             This is hard-coded for now, it cannot change. FIXME
693              
694             =back
695              
696             =cut
697              
698             sub files {
699 1     1 1 6 my ($pkg, %options) = @_;
700             defined($options{$_}) || ($options{$_} = $_D{f}{$_})
701 1   66     41 for qw(g r t);
702 1 50       10 $pkg->{options}{c} = defined($options{c}) ? $options{c} : $_D{m}{c};
703 1 50       12 $options{d} = $_D{f}{d}
704             unless grep substr($_, 0, 1) eq 'd', keys %options;
705 1         42 my @dirs = map File::Spec->rel2abs($options{$_}),
706             grep substr($_, 0, 1) eq 'd', keys %options;
707             # Make the options to pass to test() into a string.
708 2         10 my $t_options = join ', ', map {
709 1         5 "$_ => '$options{t}{$_}'"
710 1         4 } keys %{$options{t}};
711             # Use the user supplied -i* options and the current contents of
712             # @INC as the include path (-I) to the perl we will call.
713 1         3 my $libs = '';
714 1         28 $libs = join ' ',
715             map(qq|"-I$options{$_}"|,
716             grep substr($_, 0, 1) eq 'i', sort keys %options),
717             map(qq|"-I$_"|, @INC);
718 1         4 my $tot_nb_succ = 0;
719 1         2 my $tot_nb_fail = 0;
720 1         2 my $tot_died = 0;
721 1         2 my $tot_warned = 0;
722 1         2 my $tot_hrs = 0;
723 1         2 my $tot_mins = 0;
724 1         2 my $tot_secs = 0;
725 1         3 my $nb_modules = 0;
726 1         2 my @found_modules;
727             my $wanted = sub {
728 11     11   15 my $dir = $File::Find::dir;
729 11         14 my $file = $_;
730 11         16 my $spec = "$dir/$file";
731 11 100       435 return if -d $spec;
732 9 100       20 return unless matches_glob($spec, $options{g});
733 4         10 my $module = extract_module_name($spec);
734 4 50       10 return unless defined $module;
735 4         138 push @found_modules, $module;
736 1         6 };
737 1         182 find({wanted => $wanted, follow => 1}, $_) for @dirs;
738 1         7 my $start_time = time;
739 1         5 for my $module (sort @found_modules) {
740 4         70 my (undef, $file_name) = tempfile(UNLINK => 1);
741             # Try to make quotes OS-neutral.
742 4         1744767 my $prog = qq{$^X $libs -w -e "use $module; }
743             . qq{t()->_tee_to(q[$file_name]); test($t_options)"};
744 4         1155166 system "$prog";
745 4         311 my $result = read_file($file_name);
746 4         1581 my ($nb_succ, $nb_fail, $died, $warned, $hrs, $mins, $secs)
747             = $result =~ /
748             nb_succ \s+ (\S+) \s+
749             nb_fail \s+ (\S+) \s+
750             died \s+ (\S+) \s+
751             warned \s+ (\S+) \s+
752             time_took \s+ (..)h:(..)m:(..)
753             /x;
754 4   50     44 $tot_nb_succ += $nb_succ || 0;
755 4   100     53 $tot_nb_fail += $nb_fail || 0;
756 4   100     67 $tot_died += $died || 0;
757 4   100     33 $tot_warned += $warned || 0;
758 4   50     13 $tot_hrs += $hrs || 0;
759 4   50     24 $tot_mins += $mins || 0;
760 4   50     42 $tot_secs += $secs || 0;
761 4         44 ++$nb_modules;
762             };
763 1         45 my $tot_time = _elapsed_str(time - $start_time);
764             # Summary line.
765 1         132 $pkg->printk('summary', '# Total ');
766 1         21 $pkg->printk('success', "+$tot_nb_succ ");
767 1         11 $pkg->printk('failure', "-$tot_nb_fail ");
768 1         27 $pkg->printk('died', "${tot_died}d ");
769 1         17 $pkg->printk('warned', "${tot_warned}w ");
770 1         18 $pkg->printk('summary', "($tot_time) ");
771 1         16 $pkg->printk('summary', "in $nb_modules modules.\n");
772             # '1..n' line.
773 1         15 $pkg->printk('summary', sprintf "1..%d\n", $tot_nb_succ + $tot_nb_fail);
774             return
775 1         116 $tot_time,
776             $tot_nb_succ,
777             $tot_nb_fail,
778             $tot_died,
779             $tot_warned,
780             $nb_modules,
781             ;
782             }
783              
784             # --------------------------------------------------------------------
785             sub glob_to_regex {
786 39     39 0 50 my $glob = shift;
787 39         66 $glob =~ s/\./\\./g;
788 39         79 $glob =~ s/\*/\.*/g;
789 39         51 $glob =~ s/\?/./g;
790             # Insert anchors for start and end of string.
791 39         128 $glob =~ s/^/\^/g;
792 39         123 $glob =~ s/$/\$/g;
793 39         88 return $glob;
794             }
795              
796             # --------------------------------------------------------------------
797             sub _adjust_verbosity {
798 10     10   15 my $val = shift;
799 10 50       53 return ($val =~ /^(0|1|2)$/) ? $val : REPORT_FAILURES;
800             }
801              
802             # --------------------------------------------------------------------
803              
804             =begin maintenance $t->sprintk ($color_tag, $text)
805              
806             $color_tag, which will map into the color table, is one of:
807              
808             what
809             died
810             warned
811             summary
812             success
813             failure
814             diag
815             default
816              
817             =end maintenance
818              
819             =cut
820              
821             sub sprintk {
822 283     283 0 393 my ($self, $color_tag, $text) = @_;
823 283 50 33     2045 return $text unless $gColor->{id} && $self->{options}{c};
824 0   0     0 my $raw_color = $_D{c}{$color_tag} || $_D{c}{default};
825 0         0 my $cooked_color = $gColor->{palette}{$raw_color};
826 0         0 my $ret_str = '';
827 0 0       0 if ($gColor->{id} eq 'Win32Console') {
    0          
828 0         0 my $save_color = $gColor->{out}->Attr();
829 0         0 $gColor->{out}->Attr($cooked_color);
830 0         0 $gColor->{out}->Write($&) while $text =~ /.{1,1000}/gs;
831             # $gColor->{out}->Attr($Win32::Console::FG_GRAY | $Win32::Console::BG_BLACK);
832 0         0 $gColor->{out}->Attr($save_color);
833             }
834             elsif ($gColor->{id} eq 'ANSI') {
835 0         0 $ret_str .= Term::ANSIColor::color($cooked_color);
836             # Make sure the color reset command is part of the last line
837             # (simplifies testing).
838 0         0 my $chomped = chomp $text;
839 0         0 $ret_str .= $text;
840 0         0 $ret_str .= Term::ANSIColor::color('reset');
841 0 0       0 $ret_str .= "\n" if $chomped;
842             }
843             # Unknown color id.
844             else {
845 0         0 $ret_str .= $text;
846             }
847 0         0 return $ret_str;
848             }
849              
850             # --------------------------------------------------------------------
851             sub printk {
852 178     178 0 272 my ($self, $color_tag, $text) = @_;
853 178         340 print $self->sprintk($color_tag, $text);
854             }
855              
856             # --------------------------------------------------------------------
857              
858             =head2 $t->reset_options ()
859              
860             Resets all options to their default values.
861              
862             =cut
863              
864             sub reset_options {
865 39     39 1 94 for my $what (qw(t f m c)) {
866 156         171 $t->{options}{$_} = $_D{$what}{$_} for keys %{$_D{$what}};
  156         3261  
867             }
868             }
869              
870             # --------------------------------------------------------------------
871              
872             =head2 $t->options ()
873              
874             Returns a ref to the hash representing current option settings.
875              
876             =cut
877              
878 216     216 1 554 sub options { $t->{options} }
879              
880             # --------------------------------------------------------------------
881              
882             =begin maintenance $pkg::_elapsed_str ($seconds)
883              
884             Returns a string like '00:00:05' representing a duration of $seconds
885             as a 'hours:minutes:seconds' equivalent.
886              
887             =end maintenance
888              
889             =cut
890              
891             sub _elapsed_str {
892 16     16   199 my $seconds = shift;
893 16         48 my $hr = int($seconds / 3600);
894 16         30 $seconds -= $hr * 3600;
895 16         31 my $mi = int($seconds / 60);
896 16         28 my $se = $seconds - $mi * 60;
897 16         209 sprintf "%02dh:%02dm:%02ds", $hr, $mi, $se;
898             }
899              
900             # --------------------------------------------------------------------
901             sub extract_module_name {
902 4     4 0 4 my $spec = shift;
903             # Extract the module name from the file.
904 4         13 my $contents = read_file($spec);
905 4         323 my ($module) = $contents =~ /^\s*package\s+(\S+);/m;
906 4         9 return $module;
907             }
908              
909             # --------------------------------------------------------------------
910             sub matches_glob {
911 9     9 0 12 my ($file_spec, $glob) = @_;
912             # Strip leading '^' of resulting regex.
913 9         15 my $regex = substr(glob_to_regex($glob), 1);
914 9         209 return $file_spec =~ /$regex/;
915             }
916              
917             # --------------------------------------------------------------------
918             package Test::Usage::Example;
919              
920             sub new {
921 11     11   20 my ($pkg, $label, $sub_ref) = @_;
922 11         26 my $self = bless {}, $pkg;
923 11         14 @{$self}{qw(label sub_ref)} = ($label, $sub_ref);
  11         38  
924 11         39 return $self;
925             }
926              
927             # --------------------------------------------------------------------
928             1;
929             __END__