use 5.004;
use strict;
-use Test::Builder;
# Can't use Carp because it might cause use_ok() to accidentally succeed
-require Exporter;
use vars qw($VERSION @ISA @EXPORT %EXPORT_TAGS $TODO);
-$VERSION = '0.54';
+$VERSION = '0.67';
$VERSION = eval $VERSION; # make the alpha version come out as a number
-@ISA = qw(Exporter);
+use Test::Builder::Module;
+@ISA = qw(Test::Builder::Module);
@EXPORT = qw(ok use_ok require_ok
is isnt like unlike is_deeply
cmp_ok
plan
can_ok isa_ok
diag
+ BAIL_OUT
);
-my $Test = Test::Builder->new;
-my $Show_Diag = 1;
-
-
-# 5.004's Exporter doesn't have export_to_level.
-sub _export_to_level
-{
- my $pkg = shift;
- my $level = shift;
- (undef) = shift; # redundant arg
- my $callpkg = caller($level);
- $pkg->export($callpkg, @_);
-}
-
=head1 NAME
=head1 SYNOPSIS
- use Test::More tests => $Num_Tests;
+ use Test::More tests => 23;
# or
use Test::More qw(no_plan);
# or
require_ok( 'Some::Module' );
# Various ways to say "ok"
- ok($this eq $that, $test_name);
+ ok($got eq $expected, $test_name);
- is ($this, $that, $test_name);
- isnt($this, $that, $test_name);
+ is ($got, $exptected, $test_name);
+ isnt($got, $expected, $test_name);
# Rather than print STDERR "# here's what went wrong\n"
diag("here's what went wrong");
- like ($this, qr/that/, $test_name);
- unlike($this, qr/that/, $test_name);
+ like ($got, qr/expected/, $test_name);
+ unlike($got, qr/expected/, $test_name);
- cmp_ok($this, '==', $that, $test_name);
+ cmp_ok($got, '==', $expected, $test_name);
- is_deeply($complex_structure1, $complex_structure2, $test_name);
+ is_deeply($got_complex_structure, $expected_complex_structure, $test_name);
SKIP: {
skip $why, $how_many unless $have_some_feature;
pass($test_name);
fail($test_name);
- # Utility comparison functions.
- eq_array(\@this, \@that);
- eq_hash(\%this, \%that);
- eq_set(\@this, \@that);
+ BAIL_OUT($why);
# UNIMPLEMENTED!!!
my @status = Test::More::status;
- # UNIMPLEMENTED!!!
- BAIL_OUT($why);
-
=head1 DESCRIPTION
The preferred way to do this is to declare a plan when you C<use Test::More>.
- use Test::More tests => $Num_Tests;
+ use Test::More tests => 23;
There are rare cases when you will not know beforehand how many tests
your script is going to run. In this case, you can declare that you
use Test::More qw(no_plan);
B<NOTE>: using no_plan requires a Test::Harness upgrade else it will
-think everything has failed. See L<BUGS and CAVEATS>)
+think everything has failed. See L<CAVEATS and NOTES>).
In some cases, you'll want to completely skip an entire testing script.
=cut
sub plan {
- my(@plan) = @_;
+ my $tb = Test::More->builder;
- my $idx = 0;
- my @cleaned_plan;
- while( $idx <= $#plan ) {
- my $item = $plan[$idx];
-
- if( $item eq 'no_diag' ) {
- $Show_Diag = 0;
- }
- else {
- push @cleaned_plan, $item;
- }
-
- $idx++;
- }
-
- $Test->plan(@cleaned_plan);
+ $tb->plan(@_);
}
-sub import {
- my($class) = shift;
-
- my $caller = caller;
- $Test->exported_to($caller);
+# This implements "use Test::More 'no_diag'" but the behavior is
+# deprecated.
+sub import_extra {
+ my $class = shift;
+ my $list = shift;
+ my @other = ();
my $idx = 0;
- my @plan;
- my @imports;
- while( $idx <= $#_ ) {
- my $item = $_[$idx];
-
- if( $item eq 'import' ) {
- push @imports, @{$_[$idx+1]};
- $idx++;
+ while( $idx <= $#{$list} ) {
+ my $item = $list->[$idx];
+
+ if( defined $item and $item eq 'no_diag' ) {
+ $class->builder->no_diag(1);
}
else {
- push @plan, $item;
+ push @other, $item;
}
$idx++;
}
- plan(@plan);
-
- __PACKAGE__->_export_to_level(1, __PACKAGE__, @imports);
+ @$list = @other;
}
=item B<ok>
- ok($this eq $that, $test_name);
+ ok($got eq $expected, $test_name);
-This simply evaluates any expression (C<$this eq $that> is just a
+This simply evaluates any expression (C<$got eq $expected> is just a
simple example) and uses that to determine if the test succeeded or
failed. A true expression passes, a false one fails. Very simple.
Should an ok() fail, it will produce some diagnostics:
not ok 18 - sufficient mucus
- # Failed test 18 (foo.t at line 42)
+ # Failed test 'sufficient mucus'
+ # in foo.t at line 42.
-This is actually Test::Simple's ok() routine.
+This is the same as Test::Simple's ok() routine.
=cut
sub ok ($;$) {
my($test, $name) = @_;
- $Test->ok($test, $name);
+ my $tb = Test::More->builder;
+
+ $tb->ok($test, $name);
}
=item B<is>
=item B<isnt>
- is ( $this, $that, $test_name );
- isnt( $this, $that, $test_name );
+ is ( $got, $expected, $test_name );
+ isnt( $got, $expected, $test_name );
Similar to ok(), is() and isnt() compare their two arguments
with C<eq> and C<ne> respectively and use the result of that to
Will produce something like this:
not ok 17 - Is foo the same as bar?
- # Failed test (foo.t at line 139)
+ # Failed test 'Is foo the same as bar?'
+ # in foo.t at line 139.
# got: 'waffle'
# expected: 'yarblokos'
=cut
sub is ($$;$) {
- $Test->is_eq(@_);
+ my $tb = Test::More->builder;
+
+ $tb->is_eq(@_);
}
sub isnt ($$;$) {
- $Test->isnt_eq(@_);
+ my $tb = Test::More->builder;
+
+ $tb->isnt_eq(@_);
}
*isn't = \&isnt;
=item B<like>
- like( $this, qr/that/, $test_name );
+ like( $got, qr/expected/, $test_name );
-Similar to ok(), like() matches $this against the regex C<qr/that/>.
+Similar to ok(), like() matches $got against the regex C<qr/expected/>.
So this:
- like($this, qr/that/, 'this is like that');
+ like($got, qr/expected/, 'this is like that');
is similar to:
- ok( $this =~ /that/, 'this is like that');
+ ok( $got =~ /expected/, 'this is like that');
(Mnemonic "This is like that".)
perls) as a string that looks like a regex (alternative delimiters are
currently not supported):
- like( $this, '/that/', 'this is like that' );
+ like( $got, '/expected/', 'this is like that' );
-Regex options may be placed on the end (C<'/that/i'>).
+Regex options may be placed on the end (C<'/expected/i'>).
Its advantages over ok() are similar to that of is() and isnt(). Better
diagnostics on failure.
=cut
sub like ($$;$) {
- $Test->like(@_);
+ my $tb = Test::More->builder;
+
+ $tb->like(@_);
}
=item B<unlike>
- unlike( $this, qr/that/, $test_name );
+ unlike( $got, qr/expected/, $test_name );
-Works exactly as like(), only it checks if $this B<does not> match the
+Works exactly as like(), only it checks if $got B<does not> match the
given pattern.
=cut
sub unlike ($$;$) {
- $Test->unlike(@_);
+ my $tb = Test::More->builder;
+
+ $tb->unlike(@_);
}
=item B<cmp_ok>
- cmp_ok( $this, $op, $that, $test_name );
+ cmp_ok( $got, $op, $expected, $test_name );
Halfway between ok() and is() lies cmp_ok(). This allows you to
compare two arguments using any binary perl operator.
- # ok( $this eq $that );
- cmp_ok( $this, 'eq', $that, 'this eq that' );
+ # ok( $got eq $expected );
+ cmp_ok( $got, 'eq', $expected, 'this eq that' );
- # ok( $this == $that );
- cmp_ok( $this, '==', $that, 'this == that' );
+ # ok( $got == $expected );
+ cmp_ok( $got, '==', $expected, 'this == that' );
- # ok( $this && $that );
- cmp_ok( $this, '&&', $that, 'this && that' );
+ # ok( $got && $expected );
+ cmp_ok( $got, '&&', $expected, 'this && that' );
...etc...
-Its advantage over ok() is when the test fails you'll know what $this
-and $that were:
+Its advantage over ok() is when the test fails you'll know what $got
+and $expected were:
not ok 1
- # Failed test (foo.t at line 12)
+ # Failed test in foo.t at line 12.
# '23'
# &&
# undef
=cut
sub cmp_ok($$$;$) {
- $Test->cmp_ok(@_);
+ my $tb = Test::More->builder;
+
+ $tb->cmp_ok(@_);
}
sub can_ok ($@) {
my($proto, @methods) = @_;
my $class = ref $proto || $proto;
+ my $tb = Test::More->builder;
+
+ unless( $class ) {
+ my $ok = $tb->ok( 0, "->can(...)" );
+ $tb->diag(' can_ok() called with empty class or reference');
+ return $ok;
+ }
unless( @methods ) {
- my $ok = $Test->ok( 0, "$class->can(...)" );
- $Test->diag(' can_ok() called with no methods');
+ my $ok = $tb->ok( 0, "$class->can(...)" );
+ $tb->diag(' can_ok() called with no methods');
return $ok;
}
my $name;
$name = @methods == 1 ? "$class->can('$methods[0]')"
: "$class->can(...)";
-
- my $ok = $Test->ok( !@nok, $name );
- $Test->diag(map " $class->can('$_') failed\n", @nok);
+ my $ok = $tb->ok( !@nok, $name );
+
+ $tb->diag(map " $class->can('$_') failed\n", @nok);
return $ok;
}
sub isa_ok ($$;$) {
my($object, $class, $obj_name) = @_;
+ my $tb = Test::More->builder;
my $diag;
$obj_name = 'The object' unless defined $obj_name;
my $ok;
if( $diag ) {
- $ok = $Test->ok( 0, $name );
- $Test->diag(" $diag\n");
+ $ok = $tb->ok( 0, $name );
+ $tb->diag(" $diag\n");
}
else {
- $ok = $Test->ok( 1, $name );
+ $ok = $tb->ok( 1, $name );
}
return $ok;
=cut
sub pass (;$) {
- $Test->ok(1, @_);
+ my $tb = Test::More->builder;
+ $tb->ok(1, @_);
}
sub fail (;$) {
- $Test->ok(0, @_);
+ my $tb = Test::More->builder;
+ $tb->ok(0, @_);
}
=back
-=head2 Diagnostics
-
-If you pick the right test function, you'll usually get a good idea of
-what went wrong when it failed. But sometimes it doesn't work out
-that way. So here we have ways for you to write your own diagnostic
-messages which are safer than just C<print STDERR>.
-
-=over 4
-
-=item B<diag>
-
- diag(@diagnostic_message);
-
-Prints a diagnostic message which is guaranteed not to interfere with
-test output. Like C<print> @diagnostic_message is simply concatinated
-together.
-
-Handy for this sort of thing:
-
- ok( grep(/foo/, @users), "There's a foo user" ) or
- diag("Since there's no foo, check that /etc/bar is set up right");
-
-which would produce:
-
- not ok 42 - There's a foo user
- # Failed test (foo.t at line 52)
- # Since there's no foo, check that /etc/bar is set up right.
-
-You might remember C<ok() or diag()> with the mnemonic C<open() or
-die()>.
-
-All diag()s can be made silent by passing the "no_diag" option to
-Test::More. C<use Test::More tests => 1, 'no_diag'>. This is useful
-if you have diagnostics for personal testing but then wish to make
-them silent for release without commenting out each individual
-statement.
-
-B<NOTE> The exact formatting of the diagnostic output is still
-changing, but it is guaranteed that whatever you throw at it it won't
-interfere with the test.
-
-=cut
-
-sub diag {
- return unless $Show_Diag;
- $Test->diag(@_);
-}
-
-
-=back
=head2 Module tests
sub use_ok ($;@) {
my($module, @imports) = @_;
@imports = () unless @imports;
+ my $tb = Test::More->builder;
my($pack,$filename,$line) = caller;
USE
}
- my $ok = $Test->ok( !$@, "use $module;" );
+ my $ok = $tb->ok( !$@, "use $module;" );
unless( $ok ) {
chomp $@;
$@ =~ s{^BEGIN failed--compilation aborted at .*$}
{BEGIN failed--compilation aborted at $filename line $line.}m;
- $Test->diag(<<DIAGNOSTIC);
+ $tb->diag(<<DIAGNOSTIC);
Tried to use '$module'.
Error: $@
DIAGNOSTIC
sub require_ok ($) {
my($module) = shift;
+ my $tb = Test::More->builder;
my $pack = caller;
require $module;
REQUIRE
- my $ok = $Test->ok( !$@, "require $module;" );
+ my $ok = $tb->ok( !$@, "require $module;" );
unless( $ok ) {
chomp $@;
- $Test->diag(<<DIAGNOSTIC);
+ $tb->diag(<<DIAGNOSTIC);
Tried to require '$module'.
Error: $@
DIAGNOSTIC
# End with an alphanumeric.
# The rest is an alphanumeric or ::
$module =~ s/\b::\b//g;
- $module =~ /^[a-zA-Z]\w+$/;
+ $module =~ /^[a-zA-Z]\w*$/;
+}
+
+=back
+
+
+=head2 Complex data structures
+
+Not everything is a simple eq check or regex. There are times you
+need to see if two data structures are equivalent. For these
+instances Test::More provides a handful of useful functions.
+
+B<NOTE> I'm not quite sure what will happen with filehandles.
+
+=over 4
+
+=item B<is_deeply>
+
+ is_deeply( $got, $expected, $test_name );
+
+Similar to is(), except that if $got and $expected are references, it
+does a deep comparison walking each data structure to see if they are
+equivalent. If the two structures are different, it will display the
+place where they start differing.
+
+is_deeply() compares the dereferenced values of references, the
+references themselves (except for their type) are ignored. This means
+aspects such as blessing and ties are not considered "different".
+
+is_deeply() current has very limited handling of function reference
+and globs. It merely checks if they have the same referent. This may
+improve in the future.
+
+Test::Differences and Test::Deep provide more in-depth functionality
+along these lines.
+
+=cut
+
+use vars qw(@Data_Stack %Refs_Seen);
+my $DNE = bless [], 'Does::Not::Exist';
+sub is_deeply {
+ my $tb = Test::More->builder;
+
+ unless( @_ == 2 or @_ == 3 ) {
+ my $msg = <<WARNING;
+is_deeply() takes two or three args, you gave %d.
+This usually means you passed an array or hash instead
+of a reference to it
+WARNING
+ chop $msg; # clip off newline so carp() will put in line/file
+
+ _carp sprintf $msg, scalar @_;
+
+ return $tb->ok(0);
+ }
+
+ my($got, $expected, $name) = @_;
+
+ $tb->_unoverload_str(\$expected, \$got);
+
+ my $ok;
+ if( !ref $got and !ref $expected ) { # neither is a reference
+ $ok = $tb->is_eq($got, $expected, $name);
+ }
+ elsif( !ref $got xor !ref $expected ) { # one's a reference, one isn't
+ $ok = $tb->ok(0, $name);
+ $tb->diag( _format_stack({ vals => [ $got, $expected ] }) );
+ }
+ else { # both references
+ local @Data_Stack = ();
+ if( _deep_check($got, $expected) ) {
+ $ok = $tb->ok(1, $name);
+ }
+ else {
+ $ok = $tb->ok(0, $name);
+ $tb->diag(_format_stack(@Data_Stack));
+ }
+ }
+
+ return $ok;
+}
+
+sub _format_stack {
+ my(@Stack) = @_;
+
+ my $var = '$FOO';
+ my $did_arrow = 0;
+ foreach my $entry (@Stack) {
+ my $type = $entry->{type} || '';
+ my $idx = $entry->{'idx'};
+ if( $type eq 'HASH' ) {
+ $var .= "->" unless $did_arrow++;
+ $var .= "{$idx}";
+ }
+ elsif( $type eq 'ARRAY' ) {
+ $var .= "->" unless $did_arrow++;
+ $var .= "[$idx]";
+ }
+ elsif( $type eq 'REF' ) {
+ $var = "\${$var}";
+ }
+ }
+
+ my @vals = @{$Stack[-1]{vals}}[0,1];
+ my @vars = ();
+ ($vars[0] = $var) =~ s/\$FOO/ \$got/;
+ ($vars[1] = $var) =~ s/\$FOO/\$expected/;
+
+ my $out = "Structures begin differing at:\n";
+ foreach my $idx (0..$#vals) {
+ my $val = $vals[$idx];
+ $vals[$idx] = !defined $val ? 'undef' :
+ $val eq $DNE ? "Does not exist" :
+ ref $val ? "$val" :
+ "'$val'";
+ }
+
+ $out .= "$vars[0] = $vals[0]\n";
+ $out .= "$vars[1] = $vals[1]\n";
+
+ $out =~ s/^/ /msg;
+ return $out;
+}
+
+
+sub _type {
+ my $thing = shift;
+
+ return '' if !ref $thing;
+
+ for my $type (qw(ARRAY HASH REF SCALAR GLOB CODE Regexp)) {
+ return $type if UNIVERSAL::isa($thing, $type);
+ }
+
+ return '';
+}
+
+=back
+
+
+=head2 Diagnostics
+
+If you pick the right test function, you'll usually get a good idea of
+what went wrong when it failed. But sometimes it doesn't work out
+that way. So here we have ways for you to write your own diagnostic
+messages which are safer than just C<print STDERR>.
+
+=over 4
+
+=item B<diag>
+
+ diag(@diagnostic_message);
+
+Prints a diagnostic message which is guaranteed not to interfere with
+test output. Like C<print> @diagnostic_message is simply concatenated
+together.
+
+Handy for this sort of thing:
+
+ ok( grep(/foo/, @users), "There's a foo user" ) or
+ diag("Since there's no foo, check that /etc/bar is set up right");
+
+which would produce:
+
+ not ok 42 - There's a foo user
+ # Failed test 'There's a foo user'
+ # in foo.t at line 52.
+ # Since there's no foo, check that /etc/bar is set up right.
+
+You might remember C<ok() or diag()> with the mnemonic C<open() or
+die()>.
+
+B<NOTE> The exact formatting of the diagnostic output is still
+changing, but it is guaranteed that whatever you throw at it it won't
+interfere with the test.
+
+=cut
+
+sub diag {
+ my $tb = Test::More->builder;
+
+ $tb->diag(@_);
}
+
=back
+
=head2 Conditional tests
Sometimes running a test under certain conditions will cause the
#'#
sub skip {
my($why, $how_many) = @_;
+ my $tb = Test::More->builder;
unless( defined $how_many ) {
# $how_many can only be avoided when no_plan is in use.
_carp "skip() needs to know \$how_many tests are in the block"
- unless $Test->has_plan eq 'no_plan';
+ unless $tb->has_plan eq 'no_plan';
+ $how_many = 1;
+ }
+
+ if( defined $how_many and $how_many =~ /\D/ ) {
+ _carp "skip() was passed a non-numeric number of tests. Did you get the arguments backwards?";
$how_many = 1;
}
for( 1..$how_many ) {
- $Test->skip($why);
+ $tb->skip($why);
}
local $^W = 0;
When the block is empty, delete it.
B<NOTE>: TODO tests require a Test::Harness upgrade else it will
-treat it as a normal failure. See L<BUGS and CAVEATS>)
+treat it as a normal failure. See L<CAVEATS and NOTES>).
=item B<todo_skip>
sub todo_skip {
my($why, $how_many) = @_;
+ my $tb = Test::More->builder;
unless( defined $how_many ) {
# $how_many can only be avoided when no_plan is in use.
_carp "todo_skip() needs to know \$how_many tests are in the block"
- unless $Test->has_plan eq 'no_plan';
+ unless $tb->has_plan eq 'no_plan';
$how_many = 1;
}
for( 1..$how_many ) {
- $Test->todo_skip($why);
+ $tb->todo_skip($why);
}
local $^W = 0;
=back
-=head2 Comparison functions
-
-Not everything is a simple eq check or regex. There are times you
-need to see if two arrays are equivalent, for instance. For these
-instances, Test::More provides a handful of useful functions.
-B<NOTE> I'm not quite sure what will happen with filehandles.
+=head2 Test control
=over 4
-=item B<is_deeply>
+=item B<BAIL_OUT>
- is_deeply( $this, $that, $test_name );
+ BAIL_OUT($reason);
-Similar to is(), except that if $this and $that are hash or array
-references, it does a deep comparison walking each data structure to
-see if they are equivalent. If the two structures are different, it
-will display the place where they start differing.
+Indicates to the harness that things are going so badly all testing
+should terminate. This includes the running any additional test scripts.
-Test::Differences and Test::Deep provide more in-depth functionality
-along these lines.
+This is typically used when testing cannot continue such as a critical
+module failing to compile or a necessary external utility not being
+available such as a database connection failing.
-=cut
-
-use vars qw(@Data_Stack %Refs_Seen);
-my $DNE = bless [], 'Does::Not::Exist';
-sub is_deeply {
- unless( @_ == 2 or @_ == 3 ) {
- my $msg = <<WARNING;
-is_deeply() takes two or three args, you gave %d.
-This usually means you passed an array or hash instead
-of a reference to it
-WARNING
- chop $msg; # clip off newline so carp() will put in line/file
+The test will exit with 255.
- _carp sprintf $msg, scalar @_;
- }
+=cut
- my($this, $that, $name) = @_;
+sub BAIL_OUT {
+ my $reason = shift;
+ my $tb = Test::More->builder;
- my $ok;
- if( !ref $this xor !ref $that ) { # one's a reference, one isn't
- $ok = 0;
- }
- if( !ref $this and !ref $that ) {
- $ok = $Test->is_eq($this, $that, $name);
- }
- else {
- local @Data_Stack = ();
- local %Refs_Seen = ();
- if( _deep_check($this, $that) ) {
- $ok = $Test->ok(1, $name);
- }
- else {
- $ok = $Test->ok(0, $name);
- $ok = $Test->diag(_format_stack(@Data_Stack));
- }
- }
-
- return $ok;
+ $tb->BAIL_OUT($reason);
}
-sub _format_stack {
- my(@Stack) = @_;
+=back
- my $var = '$FOO';
- my $did_arrow = 0;
- foreach my $entry (@Stack) {
- my $type = $entry->{type} || '';
- my $idx = $entry->{'idx'};
- if( $type eq 'HASH' ) {
- $var .= "->" unless $did_arrow++;
- $var .= "{$idx}";
- }
- elsif( $type eq 'ARRAY' ) {
- $var .= "->" unless $did_arrow++;
- $var .= "[$idx]";
- }
- elsif( $type eq 'REF' ) {
- $var = "\${$var}";
- }
- }
- my @vals = @{$Stack[-1]{vals}}[0,1];
- my @vars = ();
- ($vars[0] = $var) =~ s/\$FOO/ \$got/;
- ($vars[1] = $var) =~ s/\$FOO/\$expected/;
+=head2 Discouraged comparison functions
- my $out = "Structures begin differing at:\n";
- foreach my $idx (0..$#vals) {
- my $val = $vals[$idx];
- $vals[$idx] = !defined $val ? 'undef' :
- $val eq $DNE ? "Does not exist"
- : "'$val'";
- }
+The use of the following functions is discouraged as they are not
+actually testing functions and produce no diagnostics to help figure
+out what went wrong. They were written before is_deeply() existed
+because I couldn't figure out how to display a useful diff of two
+arbitrary data structures.
- $out .= "$vars[0] = $vals[0]\n";
- $out .= "$vars[1] = $vals[1]\n";
+These functions are usually used inside an ok().
- $out =~ s/^/ /msg;
- return $out;
-}
+ ok( eq_array(\@got, \@expected) );
+C<is_deeply()> can do that better and with diagnostics.
-sub _type {
- my $thing = shift;
+ is_deeply( \@got, \@expected );
- return '' if !ref $thing;
-
- for my $type (qw(ARRAY HASH REF SCALAR GLOB Regexp)) {
- return $type if UNIVERSAL::isa($thing, $type);
- }
-
- return '';
-}
+They may be deprecated in future versions.
+=over 4
=item B<eq_array>
- eq_array(\@this, \@that);
+ my $is_eq = eq_array(\@got, \@expected);
Checks if two arrays are equivalent. This is a deep check, so
multi-level structures are handled correctly.
#'#
sub eq_array {
local @Data_Stack;
- local %Refs_Seen;
- _eq_array(@_);
+ _deep_check(@_);
}
sub _eq_array {
return 1 if $a1 eq $a2;
- if($Refs_Seen{$a1}) {
- return $Refs_Seen{$a1} eq $a2;
- }
- else {
- $Refs_Seen{$a1} = "$a2";
- }
-
my $ok = 1;
my $max = $#$a1 > $#$a2 ? $#$a1 : $#$a2;
for (0..$max) {
sub _deep_check {
my($e1, $e2) = @_;
+ my $tb = Test::More->builder;
+
my $ok = 0;
+ # Effectively turn %Refs_Seen into a stack. This avoids picking up
+ # the same referenced used twice (such as [\$a, \$a]) to be considered
+ # circular.
+ local %Refs_Seen = %Refs_Seen;
+
{
# Quiet uninitialized value warnings when comparing undefs.
local $^W = 0;
- $Test->_unoverload(\$e1, \$e2);
+ $tb->_unoverload_str(\$e1, \$e2);
# Either they're both references or both not.
my $same_ref = !(!ref $e1 xor !ref $e2);
+ my $not_ref = (!ref $e1 and !ref $e2);
if( defined $e1 xor defined $e2 ) {
$ok = 0;
elsif ( $same_ref and ($e1 eq $e2) ) {
$ok = 1;
}
+ elsif ( $not_ref ) {
+ push @Data_Stack, { type => '', vals => [$e1, $e2] };
+ $ok = 0;
+ }
else {
+ if( $Refs_Seen{$e1} ) {
+ return $Refs_Seen{$e1} eq $e2;
+ }
+ else {
+ $Refs_Seen{$e1} = "$e2";
+ }
+
my $type = _type($e1);
- $type = '' unless _type($e2) eq $type;
+ $type = 'DIFFERENT' unless _type($e2) eq $type;
- if( !$type ) {
- push @Data_Stack, { vals => [$e1, $e2] };
+ if( $type eq 'DIFFERENT' ) {
+ push @Data_Stack, { type => $type, vals => [$e1, $e2] };
$ok = 0;
}
elsif( $type eq 'ARRAY' ) {
$ok = _eq_hash($e1, $e2);
}
elsif( $type eq 'REF' ) {
- push @Data_Stack, { type => 'REF', vals => [$e1, $e2] };
+ push @Data_Stack, { type => $type, vals => [$e1, $e2] };
$ok = _deep_check($$e1, $$e2);
pop @Data_Stack if $ok;
}
$ok = _deep_check($$e1, $$e2);
pop @Data_Stack if $ok;
}
+ elsif( $type ) {
+ push @Data_Stack, { type => $type, vals => [$e1, $e2] };
+ $ok = 0;
+ }
+ else {
+ _whoa(1, "No type in _deep_check");
+ }
}
}
}
+sub _whoa {
+ my($check, $desc) = @_;
+ if( $check ) {
+ die <<WHOA;
+WHOA! $desc
+This should never happen! Please contact the author immediately!
+WHOA
+ }
+}
+
+
=item B<eq_hash>
- eq_hash(\%this, \%that);
+ my $is_eq = eq_hash(\%got, \%expected);
Determines if the two hashes contain the same keys and values. This
is a deep check.
sub eq_hash {
local @Data_Stack;
- local %Refs_Seen;
- return _eq_hash(@_);
+ return _deep_check(@_);
}
sub _eq_hash {
return 1 if $a1 eq $a2;
- if( $Refs_Seen{$a1} ) {
- return $Refs_Seen{$a1} eq $a2;
- }
- else {
- $Refs_Seen{$a1} = "$a2";
- }
-
my $ok = 1;
my $bigger = keys %$a1 > keys %$a2 ? $a1 : $a2;
foreach my $k (keys %$bigger) {
=item B<eq_set>
- eq_set(\@this, \@that);
+ my $is_eq = eq_set(\@got, \@expected);
Similar to eq_array(), except the order of the elements is B<not>
important. This is a deep check, but the irrelevancy of order only
applies to the top level.
-B<NOTE> By historical accident, this is not a true set comparision.
+ ok( eq_set(\@got, \@expected) );
+
+Is better written:
+
+ is_deeply( [sort @got], [sort @expected] );
+
+B<NOTE> By historical accident, this is not a true set comparison.
While the order of elements does not matter, duplicate elements do.
+B<NOTE> eq_set() does not know how to deal with references at the top
+level. The following is an example of a comparison which might not work:
+
+ eq_set([\1, \2], [\2, \1]);
+
+Test::Deep contains much better set comparison functions.
+
=cut
sub eq_set {
# There's faster ways to do this, but this is easiest.
local $^W = 0;
- # We must make sure that references are treated neutrally. It really
- # doesn't matter how we sort them, as long as both arrays are sorted
- # with the same algorithm.
+ # It really doesn't matter how we sort them, as long as both arrays are
+ # sorted with the same algorithm.
+ #
+ # Ensure that references are not accidentally treated the same as a
+ # string containing the reference.
+ #
# Have to inline the sort routine due to a threading/sort bug.
# See [rt.cpan.org 6782]
+ #
+ # I don't know how references would be sorted so we just don't sort
+ # them. This means eq_set doesn't really work with refs.
return eq_array(
- [sort { ref $a ? -1 : ref $b ? 1 : $a cmp $b } @$a1],
- [sort { ref $a ? -1 : ref $b ? 1 : $a cmp $b } @$a2]
+ [grep(ref, @$a1), sort( grep(!ref, @$a1) )],
+ [grep(ref, @$a2), sort( grep(!ref, @$a2) )],
);
}
Returns the Test::Builder object underlying Test::More for you to play
with.
-=cut
-
-sub builder {
- return Test::Builder->new;
-}
=back
So the exit codes are...
0 all tests successful
- 255 test died
+ 255 test died or all passed but wrong # of tests run
any other number how many failed (including missing or extras)
If you fail more than 254 tests, it will be reported as 254.
+B<NOTE> This behavior may go away in future versions.
+
=head1 CAVEATS and NOTES
=item Overloaded objects
-String overloaded objects are compared B<as strings>. This prevents
-Test::More from piercing an object's interface allowing better blackbox
-testing. So if a function starts returning overloaded objects instead of
-bare strings your tests won't notice the difference. This is good.
+String overloaded objects are compared B<as strings> (or in cmp_ok()'s
+case, strings or numbers as appropriate to the comparison op). This
+prevents Test::More from piercing an object's interface allowing
+better blackbox testing. So if a function starts returning overloaded
+objects instead of bare strings your tests won't notice the
+difference. This is good.
However, it does mean that functions like is_deeply() cannot be used to
test the internals of string overloaded objects. In this case I would
use Test::More
use threads;
+5.8.1 and above are supported. Anything below that has too many bugs.
+
=item Test::Harness upgrade
=head1 COPYRIGHT
-Copyright 2001, 2002, 2004 by Michael G Schwern E<lt>schwern@pobox.comE<gt>.
+Copyright 2001-2002, 2004-2006 by Michael G Schwern E<lt>schwern@pobox.comE<gt>.
-This program is free software; you can redistribute it and/or
+This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>