1#!/usr/bin/perl -w
2
3use strict;
4
5use Test::Harness;
6use Getopt::Long;
7use Pod::Usage 1.12;
8use File::Spec;
9
10use vars qw( $VERSION );
11$VERSION = "1.04";
12
13my @ext = ();
14my $shuffle = 0;
15my $dry = 0;
16my $blib = 0;
17my $lib = 0;
18my $recurse = 0;
19my @includes = ();
20my @switches = ();
21
22# Allow cuddling the paths with the -I
23@ARGV = map { /^(-I)(.+)/ ? ($1,$2) : $_ } @ARGV;
24
25# Stick any default switches at the beginning, so they can be overridden
26# by the command line switches.
27unshift @ARGV, split( " ", $ENV{PROVE_SWITCHES} ) if defined $ENV{PROVE_SWITCHES};
28
29Getopt::Long::Configure( "no_ignore_case" );
30Getopt::Long::Configure( "bundling" );
31GetOptions(
32    'b|blib'        => \$blib,
33    'd|debug'       => \$Test::Harness::debug,
34    'D|dry'         => \$dry,
35    'h|help|?'      => sub {pod2usage({-verbose => 1}); exit},
36    'H|man'         => sub {pod2usage({-verbose => 2}); exit},
37    'I=s@'          => \@includes,
38    'l|lib'         => \$lib,
39    'r|recurse'     => \$recurse,
40    's|shuffle'     => \$shuffle,
41    't'             => sub { unshift @switches, "-t" }, # Always want -t up front
42    'T'             => sub { unshift @switches, "-T" }, # Always want -T up front
43    'timer'         => \$Test::Harness::Timer,
44    'v|verbose'     => \$Test::Harness::verbose,
45    'V|version'     => sub { print_version(); exit; },
46    'ext=s@'        => \@ext,
47) or exit 1;
48
49$ENV{TEST_VERBOSE} = 1 if $Test::Harness::verbose;
50
51# Build up extensions regex
52@ext = map { split /,/ } @ext;
53s/^\.// foreach @ext;
54@ext = ("t") unless @ext;
55my $ext_regex = join( "|", map { quotemeta } @ext );
56$ext_regex = qr/\.($ext_regex)$/;
57
58# Handle blib includes
59if ( $blib ) {
60    my @blibdirs = blibdirs();
61    if ( @blibdirs ) {
62        unshift @includes, @blibdirs;
63    } else {
64        warn "No blib directories found.\n";
65    }
66}
67
68# Handle lib includes
69if ( $lib ) {
70    unshift @includes, "lib";
71}
72
73# Build up TH switches
74push( @switches, map { /\s/ && !/^".*"$/ ? qq["-I$_"] : "-I$_" } @includes );
75$Test::Harness::Switches = join( " ", @switches );
76print "# \$Test::Harness::Switches: $Test::Harness::Switches\n" if $Test::Harness::debug;
77
78my @tests;
79@ARGV = File::Spec->curdir unless @ARGV;
80push( @tests, -d $_ ? all_in( $_ ) : $_ ) for map { glob } @ARGV;
81
82if ( @tests ) {
83    shuffle(@tests) if $shuffle;
84    if ( $dry ) {
85        print join( "\n", @tests, "" );
86    } else {
87        print "# ", scalar @tests, " tests to run\n" if $Test::Harness::debug;
88        runtests(@tests);
89    }
90}
91
92sub all_in {
93    my $start = shift;
94
95    my @hits = ();
96
97    local *DH;
98    if ( opendir( DH, $start ) ) {
99        my @files = sort readdir DH;
100        closedir DH;
101        for my $file ( @files ) {
102            next if $file eq File::Spec->updir || $file eq File::Spec->curdir;
103            next if $file eq ".svn";
104            next if $file eq "CVS";
105
106            my $currfile = File::Spec->catfile( $start, $file );
107            if ( -d $currfile ) {
108                push( @hits, all_in( $currfile ) ) if $recurse;
109            } else {
110                push( @hits, $currfile ) if $currfile =~ $ext_regex;
111            }
112        }
113    } else {
114        warn "$start: $!\n";
115    }
116
117    return @hits;
118}
119
120sub shuffle {
121    # Fisher-Yates shuffle
122    my $i = @_;
123    while ($i) {
124        my $j = rand $i--;
125        @_[$i, $j] = @_[$j, $i];
126    }
127}
128
129sub print_version {
130    printf( "prove v%s, using Test::Harness v%s and Perl v%vd\n",
131        $VERSION, $Test::Harness::VERSION, $^V );
132}
133
134# Stolen directly from blib.pm
135sub blibdirs {
136    my $dir = File::Spec->curdir;
137    if ($^O eq 'VMS') {
138        ($dir = VMS::Filespec::unixify($dir)) =~ s-/\z--;
139    }
140    my $archdir = "arch";
141    if ( $^O eq "MacOS" ) {
142        # Double up the MP::A so that it's not used only once.
143        $archdir = $MacPerl::Architecture = $MacPerl::Architecture;
144    }
145
146    my $i = 5;
147    while ($i--) {
148        my $blib      = File::Spec->catdir( $dir, "blib" );
149        my $blib_lib  = File::Spec->catdir( $blib, "lib" );
150        my $blib_arch = File::Spec->catdir( $blib, $archdir );
151
152        if ( -d $blib && -d $blib_arch && -d $blib_lib ) {
153            return ($blib_arch,$blib_lib);
154        }
155        $dir = File::Spec->catdir($dir, File::Spec->updir);
156    }
157    warn "$0: Cannot find blib\n";
158    return;
159}
160
161__END__
162
163=head1 NAME
164
165prove -- A command-line tool for running tests against Test::Harness
166
167=head1 SYNOPSIS
168
169prove [options] [files/directories]
170
171Options:
172
173    -b, --blib      Adds blib/lib to the path for your tests, a la "use blib".
174    -d, --debug     Includes extra debugging information.
175    -D, --dry       Dry run: Show the tests to run, but don't run them.
176        --ext=x     Extensions (defaults to .t)
177    -h, --help      Display this help
178    -H, --man       Longer manpage for prove
179    -I              Add libraries to @INC, as Perl's -I
180    -l, --lib       Add lib to the path for your tests.
181    -r, --recurse   Recursively descend into directories.
182    -s, --shuffle   Run the tests in a random order.
183    -T              Enable tainting checks
184    -t              Enable tainting warnings
185        --timer     Print elapsed time after each test file
186    -v, --verbose   Display standard output of test scripts while running them.
187    -V, --version   Display version info
188
189Single-character options may be stacked.  Default options may be set by
190specifying the PROVE_SWITCHES environment variable.
191
192=head1 OVERVIEW
193
194F<prove> is a command-line interface to the test-running functionality
195of C<Test::Harness>.  With no arguments, it will run all tests in the
196current directory.
197
198Shell metacharacters may be used with command lines options and will be exanded
199via C<glob>.
200
201=head1 PROVE VS. "MAKE TEST"
202
203F<prove> has a number of advantages over C<make test> when doing development.
204
205=over 4
206
207=item * F<prove> is designed as a development tool
208
209Perl users typically run the test harness through a makefile via
210C<make test>.  That's fine for module distributions, but it's
211suboptimal for a test/code/debug development cycle.
212
213=item * F<prove> is granular
214
215F<prove> lets your run against only the files you want to check.
216Running C<prove t/live/ t/master.t> checks every F<*.t> in F<t/live>,
217plus F<t/master.t>.
218
219=item * F<prove> has an easy verbose mode
220
221F<prove> has a C<-v> option to see the raw output from the tests.
222To do this with C<make test>, you must set C<HARNESS_VERBOSE=1> in
223the environment.
224
225=item * F<prove> can run under taint mode
226
227F<prove>'s C<-T> runs your tests under C<perl -T>, and C<-t> runs them
228under C<perl -t>.
229
230=item * F<prove> can shuffle tests
231
232You can use F<prove>'s C<--shuffle> option to try to excite problems
233that don't show up when tests are run in the same order every time.
234
235=item * F<prove> doesn't rely on a make tool
236
237Not everyone wants to write a makefile, or use L<ExtUtils::MakeMaker>
238to do so.  F<prove> has no external dependencies.
239
240=item * Not everything is a module
241
242More and more users are using Perl's testing tools outside the
243context of a module distribution, and may not even use a makefile
244at all.
245
246=back
247
248=head1 COMMAND LINE OPTIONS
249
250=head2 -b, --blib
251
252Adds blib/lib to the path for your tests, a la "use blib".
253
254=head2 -d, --debug
255
256Include debug information about how F<prove> is being run.  This
257option doesn't show the output from the test scripts.  That's handled
258by -v,--verbose.
259
260=head2 -D, --dry
261
262Dry run: Show the tests to run, but don't run them.
263
264=head2 --ext=extension
265
266Specify extensions of the test files to run.  By default, these are .t,
267but you may have other non-.t test files, most likely .sh shell scripts.
268The --ext is repeatable.
269
270=head2 -I
271
272Add libraries to @INC, as Perl's -I.
273
274=head2 -l, --lib
275
276Add C<lib> to @INC.  Equivalent to C<-Ilib>.
277
278=head2 -r, --recurse
279
280Descends into subdirectories of any directories specified, looking for tests.
281
282=head2 -s, --shuffle
283
284Sometimes tests are accidentally dependent on tests that have been
285run before.  This switch will shuffle the tests to be run prior to
286running them, thus ensuring that hidden dependencies in the test
287order are likely to be revealed.  The author hopes the run the
288algorithm on the preceding sentence to see if he can produce something
289slightly less awkward.
290
291=head2 -t
292
293Runs test programs under perl's -t taint warning mode.
294
295=head2 -T
296
297Runs test programs under perl's -T taint mode.
298
299=head2 --timer
300
301Print elapsed time after each test file
302
303=head2 -v, --verbose
304
305Display standard output of test scripts while running them.  Also sets
306TEST_VERBOSE in case your tests rely on them.
307
308=head2 -V, --version
309
310Display version info.
311
312=head1 BUGS
313
314Please use the CPAN bug ticketing system at L<http://rt.cpan.org/>.
315You can also mail bugs, fixes and enhancements to
316C<< <bug-test-harness@rt.cpan.org> >>.
317
318=head1 TODO
319
320=over 4
321
322=item *
323
324Shuffled tests must be recreatable
325
326=back
327
328=head1 AUTHORS
329
330Andy Lester C<< <andy@petdance.com> >>
331
332=head1 COPYRIGHT
333
334Copyright 2005 by Andy Lester C<< <andy@petdance.com> >>.
335
336This program is free software; you can redistribute it and/or
337modify it under the same terms as Perl itself.
338
339See L<http://www.perl.com/perl/misc/Artistic.html>.
340
341=cut
342