summaryrefslogtreecommitdiff
path: root/ext/Test-Harness/lib/App
diff options
context:
space:
mode:
Diffstat (limited to 'ext/Test-Harness/lib/App')
-rw-r--r--ext/Test-Harness/lib/App/Prove.pm141
-rw-r--r--ext/Test-Harness/lib/App/Prove/State.pm85
-rw-r--r--ext/Test-Harness/lib/App/Prove/State/Result.pm15
-rw-r--r--ext/Test-Harness/lib/App/Prove/State/Result/Test.pm13
4 files changed, 183 insertions, 71 deletions
diff --git a/ext/Test-Harness/lib/App/Prove.pm b/ext/Test-Harness/lib/App/Prove.pm
index 29d2f8fd1f..bc665fac56 100644
--- a/ext/Test-Harness/lib/App/Prove.pm
+++ b/ext/Test-Harness/lib/App/Prove.pm
@@ -11,19 +11,17 @@ use Getopt::Long;
use App::Prove::State;
use Carp;
-@ISA = qw(TAP::Object);
-
=head1 NAME
App::Prove - Implements the C<prove> command.
=head1 VERSION
-Version 3.14
+Version 3.16
=cut
-$VERSION = '3.14';
+$VERSION = '3.16';
=head1 DESCRIPTION
@@ -53,21 +51,16 @@ use constant PLUGINS => 'App::Prove::Plugin';
my @ATTR;
BEGIN {
+ @ISA = qw(TAP::Object);
+
@ATTR = qw(
archive argv blib show_count color directives exec failures fork
formatter harness includes modules plugins jobs lib merge parse quiet
really_quiet recurse backwards shuffle taint_fail taint_warn timer
verbose warnings_fail warnings_warn show_help show_man show_version
- test_args state dry extension ignore_exit rules state_manager
+ state_class test_args state dry extension ignore_exit rules state_manager
);
- for my $attr (@ATTR) {
- no strict 'refs';
- *$attr = sub {
- my $self = shift;
- $self->{$attr} = shift if @_;
- return $self->{$attr};
- };
- }
+ __PACKAGE__->mk_methods(@ATTR);
}
=head1 METHODS
@@ -108,27 +101,22 @@ sub _initialize {
while ( my ( $env, $attr ) = each %env_provides_default ) {
$self->{$attr} = 1 if $ENV{$env};
}
- $self->state_manager(
- $self->state_class->new( { store => STATE_FILE } ) );
-
+ $self->state_class('App::Prove::State');
return $self;
}
=head3 C<state_class>
-Returns the name of the class used for maintaining state. This class should
-either subclass from C<App::Prove::State> or provide an identical interface.
+Getter/setter for the name of the class used for maintaining state. This
+class should either subclass from C<App::Prove::State> or provide an identical
+interface.
=head3 C<state_manager>
-Getter/setter for the an instane of the C<state_class>.
+Getter/setter for the instance of the C<state_class>.
=cut
-sub state_class {
- return 'App::Prove::State';
-}
-
=head3 C<add_rc_file>
$prove->add_rc_file('myproj/.proverc');
@@ -400,19 +388,22 @@ sub _find_module {
}
sub _load_extension {
- my ( $self, $class, @search ) = @_;
+ my ( $self, $name, @search ) = @_;
my @args = ();
- if ( $class =~ /^(.*?)=(.*)/ ) {
- $class = $1;
+ if ( $name =~ /^(.*?)=(.*)/ ) {
+ $name = $1;
@args = split( /,/, $2 );
}
- if ( my $name = $self->_find_module( $class, @search ) ) {
- $name->import(@args);
+ if ( my $class = $self->_find_module( $name, @search ) ) {
+ $class->import(@args);
+ if ( $class->can('load') ) {
+ $class->load( { app_prove => $self, args => [@args] } );
+ }
}
else {
- croak "Can't load module $class";
+ croak "Can't load module $name";
}
}
@@ -437,6 +428,11 @@ command line tool consists of the following code:
sub run {
my $self = shift;
+ unless ( $self->state_manager ) {
+ $self->state_manager(
+ $self->state_class->new( { store => STATE_FILE } ) );
+ }
+
if ( $self->show_help ) {
$self->_help(1);
}
@@ -675,6 +671,8 @@ calling C<run>.
=item C<state>
+=item C<state_class>
+
=item C<taint_fail>
=item C<taint_warn>
@@ -690,3 +688,88 @@ calling C<run>.
=item C<warnings_warn>
=back
+
+=head1 PLUGINS
+
+C<App::Prove> provides support for 3rd-party plugins. These are currently
+loaded at run-time, I<after> arguments have been parsed (so you can not
+change the way arguments are processed, sorry), typically with the
+C<< -PI<plugin> >> switch, eg:
+
+ prove -PMyPlugin
+
+This will search for a module named C<App::Prove::Plugin::MyPlugin>, or failing
+that, C<MyPlugin>. If the plugin can't be found, C<prove> will complain & exit.
+
+You can pass an argument to your plugin by appending an C<=> after the plugin
+name, eg C<-PMyPlugin=foo>. You can pass multiple arguments using commas:
+
+ prove -PMyPlugin=foo,bar,baz
+
+These are passed in to your plugin's C<load()> class method (if it has one),
+along with a reference to the C<App::Prove> object that is invoking your plugin:
+
+ sub load {
+ my ($class, $p) = @_;
+
+ my @args = @{ $p->{args} };
+ # @args will contain ( 'foo', 'bar', 'baz' )
+ $p->{app_prove}->do_something;
+ ...
+ }
+
+Note that the user's arguments are also passed to your plugin's C<import()>
+function as a list, eg:
+
+ sub import {
+ my ($class, @args) = @_;
+ # @args will contain ( 'foo', 'bar', 'baz' )
+ ...
+ }
+
+This is for backwards compatibility, and may be deprecated in the future.
+
+=head2 Sample Plugin
+
+Here's a sample plugin, for your reference:
+
+ package App::Prove::Plugin::Foo;
+
+ # Sample plugin, try running with:
+ # prove -PFoo=bar -r -j3
+ # prove -PFoo -Q
+ # prove -PFoo=bar,My::Formatter
+
+ use strict;
+ use warnings;
+
+ sub load {
+ my ($class, $p) = @_;
+ my @args = @{ $p->{args} };
+ my $app = $p->{app_prove};
+
+ print "loading plugin: $class, args: ", join(', ', @args ), "\n";
+
+ # turn on verbosity
+ $app->verbose( 1 );
+
+ # set the formatter?
+ $app->formatter( $args[1] ) if @args > 1;
+
+ # print some of App::Prove's state:
+ for my $attr (qw( jobs quiet really_quiet recurse verbose )) {
+ my $val = $app->$attr;
+ $val = 'undef' unless defined( $val );
+ print "$attr: $val\n";
+ }
+
+ return 1;
+ }
+
+ 1;
+
+=head1 SEE ALSO
+
+L<prove>, L<TAP::Harness>
+
+=cut
diff --git a/ext/Test-Harness/lib/App/Prove/State.pm b/ext/Test-Harness/lib/App/Prove/State.pm
index 2b284d2074..6eef184a62 100644
--- a/ext/Test-Harness/lib/App/Prove/State.pm
+++ b/ext/Test-Harness/lib/App/Prove/State.pm
@@ -12,7 +12,10 @@ use TAP::Parser::YAMLish::Reader ();
use TAP::Parser::YAMLish::Writer ();
use TAP::Base;
-@ISA = qw( TAP::Base );
+BEGIN {
+ @ISA = qw( TAP::Base );
+ __PACKAGE__->mk_methods('result_class');
+}
use constant IS_WIN32 => ( $^O =~ /^(MS)?Win32$/ );
use constant NEED_GLOB => IS_WIN32;
@@ -23,11 +26,11 @@ App::Prove::State - State storage for the C<prove> command.
=head1 VERSION
-Version 3.14
+Version 3.16
=cut
-$VERSION = '3.14';
+$VERSION = '3.16';
=head1 DESCRIPTION
@@ -48,6 +51,24 @@ and the operations that may be performed on it.
=head3 C<new>
+Accepts a hashref with the following key/value pairs:
+
+=over 4
+
+=item * C<store>
+
+The filename of the data store holding the data that App::Prove::State reads.
+
+=item * C<extension> (optional)
+
+The test name extension. Defaults to C<.t>.
+
+=item * C<result_class> (optional)
+
+The name of the C<result_class>. Defaults to C<App::Prove::State::Result>.
+
+=back
+
=cut
# override TAP::Base::new:
@@ -56,17 +77,19 @@ sub new {
my %args = %{ shift || {} };
my $self = bless {
- _ => $class->result_class->new(
- { tests => {},
- generation => 1,
- }
- ),
select => [],
seq => 1,
store => delete $args{store},
- extension => delete $args{extension} || '.t',
+ extension => ( delete $args{extension} || '.t' ),
+ result_class =>
+ ( delete $args{result_class} || 'App::Prove::State::Result' ),
}, $class;
+ $self->{_} = $self->result_class->new(
+ { tests => {},
+ generation => 1,
+ }
+ );
my $store = $self->{store};
$self->load($store)
if defined $store && -f $store;
@@ -76,16 +99,12 @@ sub new {
=head2 C<result_class>
-Returns the name of the class used for tracking test results. This class
-should either subclass from C<App::Prove::State::Result> or provide an
+Getter/setter for the name of the class used for tracking test results. This
+class should either subclass from C<App::Prove::State::Result> or provide an
identical interface.
=cut
-sub result_class {
- return 'App::Prove::State::Result';
-}
-
=head2 C<extension>
Get or set the extension files must have in order to be considered
@@ -107,7 +126,7 @@ Get the results of the last test run. Returns a C<result_class()> instance.
sub results {
my $self = shift;
- $self->{_} || $self->result_class->new
+ $self->{_} || $self->result_class->new;
}
=head2 C<commit>
@@ -118,8 +137,8 @@ Save the test results. Should be called after all tests have run.
sub commit {
my $self = shift;
- if ( $self->{should_save} && defined( my $store = $self->{store} ) ) {
- $self->save($store);
+ if ( $self->{should_save} ) {
+ $self->save;
}
}
@@ -373,15 +392,6 @@ Store the results of a test.
=cut
-sub observe_test {
- my ( $self, $test, $parser ) = @_;
- $self->_record_test(
- $test->[0],
- scalar( $parser->failed ) + ( $parser->has_problems ? 1 : 0 ),
- scalar( $parser->todo ), $parser->start_time, $parser->end_time,
- );
-}
-
# Store:
# last fail time
# last pass time
@@ -391,10 +401,18 @@ sub observe_test {
# total failures
# total passes
# state generation
+# parser
+
+sub observe_test {
-sub _record_test {
- my ( $self, $name, $fail, $todo, $start_time, $end_time ) = @_;
- my $test = $self->results->test($name);
+ my ( $self, $test_info, $parser ) = @_;
+ my $name = $test_info->[0];
+ my $fail = scalar( $parser->failed ) + ( $parser->has_problems ? 1 : 0 );
+ my $todo = scalar( $parser->todo );
+ my $start_time = $parser->start_time;
+ my $end_time = $parser->end_time,
+
+ my $test = $self->results->test($name);
$test->sequence( $self->{seq}++ );
$test->generation( $self->results->generation );
@@ -404,6 +422,8 @@ sub _record_test {
$test->num_todo($todo);
$test->elapsed( $end_time - $start_time );
+ $test->parser($parser);
+
if ($fail) {
$test->total_failures( $test->total_failures + 1 );
$test->last_fail_time($end_time);
@@ -421,13 +441,14 @@ Write the state to a file.
=cut
sub save {
- my ( $self, $name ) = @_;
+ my ($self) = @_;
+ my $store = $self->{store} or return;
$self->results->last_run_time( $self->get_time );
my $writer = TAP::Parser::YAMLish::Writer->new;
local *FH;
- open FH, ">$name" or croak "Can't write $name ($!)";
+ open FH, ">$store" or croak "Can't write $store ($!)";
$writer->write( $self->results->raw, \*FH );
close FH;
}
diff --git a/ext/Test-Harness/lib/App/Prove/State/Result.pm b/ext/Test-Harness/lib/App/Prove/State/Result.pm
index 37337ea258..a087da4d72 100644
--- a/ext/Test-Harness/lib/App/Prove/State/Result.pm
+++ b/ext/Test-Harness/lib/App/Prove/State/Result.pm
@@ -14,11 +14,11 @@ App::Prove::State::Result - Individual test suite results.
=head1 VERSION
-Version 3.14
+Version 3.16
=cut
-$VERSION = '3.14';
+$VERSION = '3.16';
=head1 DESCRIPTION
@@ -63,10 +63,11 @@ sub _initialize {
my ( $self, $tests ) = @_;
my %tests;
while ( my ( $name, $test ) = each %$tests ) {
- $tests{$name} = $self->test_class->new({
- %$test,
- name => $name
- });
+ $tests{$name} = $self->test_class->new(
+ { %$test,
+ name => $name
+ }
+ );
}
$self->tests( \%tests );
return $self;
@@ -170,7 +171,7 @@ sub test {
return $test;
}
else {
- my $test = $self->test_class->new({name => $name});
+ my $test = $self->test_class->new( { name => $name } );
$self->{tests}->{$name} = $test;
return $test;
}
diff --git a/ext/Test-Harness/lib/App/Prove/State/Result/Test.pm b/ext/Test-Harness/lib/App/Prove/State/Result/Test.pm
index 50e209614f..4744086e9e 100644
--- a/ext/Test-Harness/lib/App/Prove/State/Result/Test.pm
+++ b/ext/Test-Harness/lib/App/Prove/State/Result/Test.pm
@@ -10,11 +10,11 @@ App::Prove::State::Result::Test - Individual test results.
=head1 VERSION
-Version 3.14
+Version 3.16
=cut
-$VERSION = '3.14';
+$VERSION = '3.16';
=head1 DESCRIPTION
@@ -42,6 +42,7 @@ my %methods = (
seq => { method => 'sequence', default => 1 },
total_passes => { method => 'total_passes', default => 0 },
total_failures => { method => 'total_failures', default => 0 },
+ parser => { method => 'parser' },
);
while ( my ( $key, $description ) = each %methods ) {
@@ -132,14 +133,20 @@ The number of times the test has passed.
The number of times the test has failed.
+=head3 C<parser>
+
+The underlying parser object. This is useful if you need the full
+information for the test program.
+
=cut
sub raw {
my $self = shift;
my %raw = %$self;
- # this is backwards-compatibility hack and is not gauranteed.
+ # this is backwards-compatibility hack and is not guaranteed.
delete $raw{name};
+ delete $raw{parser};
return \%raw;
}