Spaces:
Running
Running
use strict; | |
package Test::Tester; | |
BEGIN | |
{ | |
if (*Test::Builder::new{CODE}) | |
{ | |
warn "You should load Test::Tester before Test::Builder (or anything that loads Test::Builder)" | |
} | |
} | |
use Test::Builder; | |
use Test::Tester::CaptureRunner; | |
use Test::Tester::Delegate; | |
require Exporter; | |
use vars qw( @ISA @EXPORT ); | |
our $VERSION = '1.302183'; | |
@EXPORT = qw( run_tests check_tests check_test cmp_results show_space ); | |
@ISA = qw( Exporter ); | |
my $Test = Test::Builder->new; | |
my $Capture = Test::Tester::Capture->new; | |
my $Delegator = Test::Tester::Delegate->new; | |
$Delegator->{Object} = $Test; | |
my $runner = Test::Tester::CaptureRunner->new; | |
my $want_space = $ENV{TESTTESTERSPACE}; | |
sub show_space | |
{ | |
$want_space = 1; | |
} | |
my $colour = ''; | |
my $reset = ''; | |
if (my $want_colour = $ENV{TESTTESTERCOLOUR} || $ENV{TESTTESTERCOLOR}) | |
{ | |
if (eval { require Term::ANSIColor; 1 }) | |
{ | |
eval { require Win32::Console::ANSI } if 'MSWin32' eq $^O; # support color on windows platforms | |
my ($f, $b) = split(",", $want_colour); | |
$colour = Term::ANSIColor::color($f).Term::ANSIColor::color("on_$b"); | |
$reset = Term::ANSIColor::color("reset"); | |
} | |
} | |
sub new_new | |
{ | |
return $Delegator; | |
} | |
sub capture | |
{ | |
return Test::Tester::Capture->new; | |
} | |
sub fh | |
{ | |
# experiment with capturing output, I don't like it | |
$runner = Test::Tester::FHRunner->new; | |
return $Test; | |
} | |
sub find_run_tests | |
{ | |
my $d = 1; | |
my $found = 0; | |
while ((not $found) and (my ($sub) = (caller($d))[3]) ) | |
{ | |
# print "$d: $sub\n"; | |
$found = ($sub eq "Test::Tester::run_tests"); | |
$d++; | |
} | |
# die "Didn't find 'run_tests' in caller stack" unless $found; | |
return $d; | |
} | |
sub run_tests | |
{ | |
local($Delegator->{Object}) = $Capture; | |
$runner->run_tests(@_); | |
return ($runner->get_premature, $runner->get_results); | |
} | |
sub check_test | |
{ | |
my $test = shift; | |
my $expect = shift; | |
my $name = shift; | |
$name = "" unless defined($name); | |
@_ = ($test, [$expect], $name); | |
goto &check_tests; | |
} | |
sub check_tests | |
{ | |
my $test = shift; | |
my $expects = shift; | |
my $name = shift; | |
$name = "" unless defined($name); | |
my ($prem, @results) = eval { run_tests($test, $name) }; | |
$Test->ok(! $@, "Test '$name' completed") || $Test->diag($@); | |
$Test->ok(! length($prem), "Test '$name' no premature diagnostication") || | |
$Test->diag("Before any testing anything, your tests said\n$prem"); | |
local $Test::Builder::Level = $Test::Builder::Level + 1; | |
cmp_results(\@results, $expects, $name); | |
return ($prem, @results); | |
} | |
sub cmp_field | |
{ | |
my ($result, $expect, $field, $desc) = @_; | |
if (defined $expect->{$field}) | |
{ | |
$Test->is_eq($result->{$field}, $expect->{$field}, | |
"$desc compare $field"); | |
} | |
} | |
sub cmp_result | |
{ | |
my ($result, $expect, $name) = @_; | |
my $sub_name = $result->{name}; | |
$sub_name = "" unless defined($name); | |
my $desc = "subtest '$sub_name' of '$name'"; | |
{ | |
local $Test::Builder::Level = $Test::Builder::Level + 1; | |
cmp_field($result, $expect, "ok", $desc); | |
cmp_field($result, $expect, "actual_ok", $desc); | |
cmp_field($result, $expect, "type", $desc); | |
cmp_field($result, $expect, "reason", $desc); | |
cmp_field($result, $expect, "name", $desc); | |
} | |
# if we got no depth then default to 1 | |
my $depth = 1; | |
if (exists $expect->{depth}) | |
{ | |
$depth = $expect->{depth}; | |
} | |
# if depth was explicitly undef then don't test it | |
if (defined $depth) | |
{ | |
$Test->is_eq($result->{depth}, $depth, "checking depth") || | |
$Test->diag('You need to change $Test::Builder::Level'); | |
} | |
if (defined(my $exp = $expect->{diag})) | |
{ | |
my $got = ''; | |
if (ref $exp eq 'Regexp') { | |
if (not $Test->like($result->{diag}, $exp, | |
"subtest '$sub_name' of '$name' compare diag")) | |
{ | |
$got = $result->{diag}; | |
} | |
} else { | |
# if there actually is some diag then put a \n on the end if it's not | |
# there already | |
$exp .= "\n" if (length($exp) and $exp !~ /\n$/); | |
if (not $Test->ok($result->{diag} eq $exp, | |
"subtest '$sub_name' of '$name' compare diag")) | |
{ | |
$got = $result->{diag}; | |
} | |
} | |
if ($got) { | |
my $glen = length($got); | |
my $elen = length($exp); | |
for ($got, $exp) | |
{ | |
my @lines = split("\n", $_); | |
$_ = join("\n", map { | |
if ($want_space) | |
{ | |
$_ = $colour.escape($_).$reset; | |
} | |
else | |
{ | |
"'$colour$_$reset'" | |
} | |
} @lines); | |
} | |
$Test->diag(<<EOM); | |
Got diag ($glen bytes): | |
$got | |
Expected diag ($elen bytes): | |
$exp | |
EOM | |
} | |
} | |
} | |
sub escape | |
{ | |
my $str = shift; | |
my $res = ''; | |
for my $char (split("", $str)) | |
{ | |
my $c = ord($char); | |
if(($c>32 and $c<125) or $c == 10) | |
{ | |
$res .= $char; | |
} | |
else | |
{ | |
$res .= sprintf('\x{%x}', $c) | |
} | |
} | |
return $res; | |
} | |
sub cmp_results | |
{ | |
my ($results, $expects, $name) = @_; | |
$Test->is_num(scalar @$results, scalar @$expects, "Test '$name' result count"); | |
for (my $i = 0; $i < @$expects; $i++) | |
{ | |
my $expect = $expects->[$i]; | |
my $result = $results->[$i]; | |
local $Test::Builder::Level = $Test::Builder::Level + 1; | |
cmp_result($result, $expect, $name); | |
} | |
} | |
######## nicked from Test::More | |
sub plan { | |
my(@plan) = @_; | |
my $caller = caller; | |
$Test->exported_to($caller); | |
my @imports = (); | |
foreach my $idx (0..$#plan) { | |
if( $plan[$idx] eq 'import' ) { | |
my($tag, $imports) = splice @plan, $idx, 2; | |
@imports = @$imports; | |
last; | |
} | |
} | |
$Test->plan(@plan); | |
__PACKAGE__->_export_to_level(1, __PACKAGE__, @imports); | |
} | |
sub import { | |
my($class) = shift; | |
{ | |
no warnings 'redefine'; | |
*Test::Builder::new = \&new_new; | |
} | |
goto &plan; | |
} | |
sub _export_to_level | |
{ | |
my $pkg = shift; | |
my $level = shift; | |
(undef) = shift; # redundant arg | |
my $callpkg = caller($level); | |
$pkg->export($callpkg, @_); | |
} | |
############ | |
1; | |
__END__ | |
=head1 NAME | |
Test::Tester - Ease testing test modules built with Test::Builder | |
=head1 SYNOPSIS | |
use Test::Tester tests => 6; | |
use Test::MyStyle; | |
check_test( | |
sub { | |
is_mystyle_eq("this", "that", "not eq"); | |
}, | |
{ | |
ok => 0, # expect this to fail | |
name => "not eq", | |
diag => "Expected: 'this'\nGot: 'that'", | |
} | |
); | |
or | |
use Test::Tester tests => 6; | |
use Test::MyStyle; | |
check_test( | |
sub { | |
is_mystyle_qr("this", "that", "not matching"); | |
}, | |
{ | |
ok => 0, # expect this to fail | |
name => "not matching", | |
diag => qr/Expected: 'this'\s+Got: 'that'/, | |
} | |
); | |
or | |
use Test::Tester; | |
use Test::More tests => 3; | |
use Test::MyStyle; | |
my ($premature, @results) = run_tests( | |
sub { | |
is_database_alive("dbname"); | |
} | |
); | |
# now use Test::More::like to check the diagnostic output | |
like($results[0]->{diag}, "/^Database ping took \\d+ seconds$"/, "diag"); | |
=head1 DESCRIPTION | |
If you have written a test module based on Test::Builder then Test::Tester | |
allows you to test it with the minimum of effort. | |
=head1 HOW TO USE (THE EASY WAY) | |
From version 0.08 Test::Tester no longer requires you to included anything | |
special in your test modules. All you need to do is | |
use Test::Tester; | |
in your test script B<before> any other Test::Builder based modules and away | |
you go. | |
Other modules based on Test::Builder can be used to help with the | |
testing. In fact you can even use functions from your module to test | |
other functions from the same module (while this is possible it is | |
probably not a good idea, if your module has bugs, then | |
using it to test itself may give the wrong answers). | |
The easiest way to test is to do something like | |
check_test( | |
sub { is_mystyle_eq("this", "that", "not eq") }, | |
{ | |
ok => 0, # we expect the test to fail | |
name => "not eq", | |
diag => "Expected: 'this'\nGot: 'that'", | |
} | |
); | |
this will execute the is_mystyle_eq test, capturing its results and | |
checking that they are what was expected. | |
You may need to examine the test results in a more flexible way, for | |
example, the diagnostic output may be quite long or complex or it may involve | |
something that you cannot predict in advance like a timestamp. In this case | |
you can get direct access to the test results: | |
my ($premature, @results) = run_tests( | |
sub { | |
is_database_alive("dbname"); | |
} | |
); | |
like($result[0]->{diag}, "/^Database ping took \\d+ seconds$"/, "diag"); | |
or | |
check_test( | |
sub { is_mystyle_qr("this", "that", "not matching") }, | |
{ | |
ok => 0, # we expect the test to fail | |
name => "not matching", | |
diag => qr/Expected: 'this'\s+Got: 'that'/, | |
} | |
); | |
We cannot predict how long the database ping will take so we use | |
Test::More's like() test to check that the diagnostic string is of the right | |
form. | |
=head1 HOW TO USE (THE HARD WAY) | |
I<This is here for backwards compatibility only> | |
Make your module use the Test::Tester::Capture object instead of the | |
Test::Builder one. How to do this depends on your module but assuming that | |
your module holds the Test::Builder object in $Test and that all your test | |
routines access it through $Test then providing a function something like this | |
sub set_builder | |
{ | |
$Test = shift; | |
} | |
should allow your test scripts to do | |
Test::YourModule::set_builder(Test::Tester->capture); | |
and after that any tests inside your module will captured. | |
=head1 TEST RESULTS | |
The result of each test is captured in a hash. These hashes are the same as | |
the hashes returned by Test::Builder->details but with a couple of extra | |
fields. | |
These fields are documented in L<Test::Builder> in the details() function | |
=over 2 | |
=item ok | |
Did the test pass? | |
=item actual_ok | |
Did the test really pass? That is, did the pass come from | |
Test::Builder->ok() or did it pass because it was a TODO test? | |
=item name | |
The name supplied for the test. | |
=item type | |
What kind of test? Possibilities include, skip, todo etc. See | |
L<Test::Builder> for more details. | |
=item reason | |
The reason for the skip, todo etc. See L<Test::Builder> for more details. | |
=back | |
These fields are exclusive to Test::Tester. | |
=over 2 | |
=item diag | |
Any diagnostics that were output for the test. This only includes | |
diagnostics output B<after> the test result is declared. | |
Note that Test::Builder ensures that any diagnostics end in a \n and | |
it in earlier versions of Test::Tester it was essential that you have | |
the final \n in your expected diagnostics. From version 0.10 onward, | |
Test::Tester will add the \n if you forgot it. It will not add a \n if | |
you are expecting no diagnostics. See below for help tracking down | |
hard to find space and tab related problems. | |
=item depth | |
This allows you to check that your test module is setting the correct value | |
for $Test::Builder::Level and thus giving the correct file and line number | |
when a test fails. It is calculated by looking at caller() and | |
$Test::Builder::Level. It should count how many subroutines there are before | |
jumping into the function you are testing. So for example in | |
run_tests( sub { my_test_function("a", "b") } ); | |
the depth should be 1 and in | |
sub deeper { my_test_function("a", "b") } | |
run_tests(sub { deeper() }); | |
depth should be 2, that is 1 for the sub {} and one for deeper(). This | |
might seem a little complex but if your tests look like the simple | |
examples in this doc then you don't need to worry as the depth will | |
always be 1 and that's what Test::Tester expects by default. | |
B<Note>: if you do not specify a value for depth in check_test() then it | |
automatically compares it against 1, if you really want to skip the depth | |
test then pass in undef. | |
B<Note>: depth will not be correctly calculated for tests that run from a | |
signal handler or an END block or anywhere else that hides the call stack. | |
=back | |
Some of Test::Tester's functions return arrays of these hashes, just | |
like Test::Builder->details. That is, the hash for the first test will | |
be array element 1 (not 0). Element 0 will not be a hash it will be a | |
string which contains any diagnostic output that came before the first | |
test. This should usually be empty, if it's not, it means something | |
output diagnostics before any test results showed up. | |
=head1 SPACES AND TABS | |
Appearances can be deceptive, especially when it comes to emptiness. If you | |
are scratching your head trying to work out why Test::Tester is saying that | |
your diagnostics are wrong when they look perfectly right then the answer is | |
probably whitespace. From version 0.10 on, Test::Tester surrounds the | |
expected and got diag values with single quotes to make it easier to spot | |
trailing whitespace. So in this example | |
# Got diag (5 bytes): | |
# 'abcd ' | |
# Expected diag (4 bytes): | |
# 'abcd' | |
it is quite clear that there is a space at the end of the first string. | |
Another way to solve this problem is to use colour and inverse video on an | |
ANSI terminal, see below COLOUR below if you want this. | |
Unfortunately this is sometimes not enough, neither colour nor quotes will | |
help you with problems involving tabs, other non-printing characters and | |
certain kinds of problems inherent in Unicode. To deal with this, you can | |
switch Test::Tester into a mode whereby all "tricky" characters are shown as | |
\{xx}. Tricky characters are those with ASCII code less than 33 or higher | |
than 126. This makes the output more difficult to read but much easier to | |
find subtle differences between strings. To turn on this mode either call | |
C<show_space()> in your test script or set the C<TESTTESTERSPACE> environment | |
variable to be a true value. The example above would then look like | |
# Got diag (5 bytes): | |
# abcd\x{20} | |
# Expected diag (4 bytes): | |
# abcd | |
=head1 COLOUR | |
If you prefer to use colour as a means of finding tricky whitespace | |
characters then you can set the C<TESTTESTCOLOUR> environment variable to a | |
comma separated pair of colours, the first for the foreground, the second | |
for the background. For example "white,red" will print white text on a red | |
background. This requires the Term::ANSIColor module. You can specify any | |
colour that would be acceptable to the Term::ANSIColor::color function. | |
If you spell colour differently, that's no problem. The C<TESTTESTERCOLOR> | |
variable also works (if both are set then the British spelling wins out). | |
=head1 EXPORTED FUNCTIONS | |
=head3 ($premature, @results) = run_tests(\&test_sub) | |
\&test_sub is a reference to a subroutine. | |
run_tests runs the subroutine in $test_sub and captures the results of any | |
tests inside it. You can run more than 1 test inside this subroutine if you | |
like. | |
$premature is a string containing any diagnostic output from before | |
the first test. | |
@results is an array of test result hashes. | |
=head3 cmp_result(\%result, \%expect, $name) | |
\%result is a ref to a test result hash. | |
\%expect is a ref to a hash of expected values for the test result. | |
cmp_result compares the result with the expected values. If any differences | |
are found it outputs diagnostics. You may leave out any field from the | |
expected result and cmp_result will not do the comparison of that field. | |
=head3 cmp_results(\@results, \@expects, $name) | |
\@results is a ref to an array of test results. | |
\@expects is a ref to an array of hash refs. | |
cmp_results checks that the results match the expected results and if any | |
differences are found it outputs diagnostics. It first checks that the | |
number of elements in \@results and \@expects is the same. Then it goes | |
through each result checking it against the expected result as in | |
cmp_result() above. | |
=head3 ($premature, @results) = check_tests(\&test_sub, \@expects, $name) | |
\&test_sub is a reference to a subroutine. | |
\@expect is a ref to an array of hash refs which are expected test results. | |
check_tests combines run_tests and cmp_tests into a single call. It also | |
checks if the tests died at any stage. | |
It returns the same values as run_tests, so you can further examine the test | |
results if you need to. | |
=head3 ($premature, @results) = check_test(\&test_sub, \%expect, $name) | |
\&test_sub is a reference to a subroutine. | |
\%expect is a ref to an hash of expected values for the test result. | |
check_test is a wrapper around check_tests. It combines run_tests and | |
cmp_tests into a single call, checking if the test died. It assumes | |
that only a single test is run inside \&test_sub and include a test to | |
make sure this is true. | |
It returns the same values as run_tests, so you can further examine the test | |
results if you need to. | |
=head3 show_space() | |
Turn on the escaping of characters as described in the SPACES AND TABS | |
section. | |
=head1 HOW IT WORKS | |
Normally, a test module (let's call it Test:MyStyle) calls | |
Test::Builder->new to get the Test::Builder object. Test::MyStyle calls | |
methods on this object to record information about test results. When | |
Test::Tester is loaded, it replaces Test::Builder's new() method with one | |
which returns a Test::Tester::Delegate object. Most of the time this object | |
behaves as the real Test::Builder object. Any methods that are called are | |
delegated to the real Test::Builder object so everything works perfectly. | |
However once we go into test mode, the method calls are no longer passed to | |
the real Test::Builder object, instead they go to the Test::Tester::Capture | |
object. This object seems exactly like the real Test::Builder object, | |
except, instead of outputting test results and diagnostics, it just records | |
all the information for later analysis. | |
=head1 CAVEATS | |
Support for calling Test::Builder->note is minimal. It's implemented | |
as an empty stub, so modules that use it will not crash but the calls | |
are not recorded for testing purposes like the others. Patches | |
welcome. | |
=head1 SEE ALSO | |
L<Test::Builder> the source of testing goodness. L<Test::Builder::Tester> | |
for an alternative approach to the problem tackled by Test::Tester - | |
captures the strings output by Test::Builder. This means you cannot get | |
separate access to the individual pieces of information and you must predict | |
B<exactly> what your test will output. | |
=head1 AUTHOR | |
This module is copyright 2005 Fergal Daly <[email protected]>, some parts | |
are based on other people's work. | |
Plan handling lifted from Test::More. written by Michael G Schwern | |
<[email protected]>. | |
Test::Tester::Capture is a cut down and hacked up version of Test::Builder. | |
Test::Builder was written by chromatic <[email protected]> and Michael G | |
Schwern <[email protected]>. | |
=head1 LICENSE | |
Under the same license as Perl itself | |
See http://www.perl.com/perl/misc/Artistic.html | |
=cut | |