Badger
view release on metacpan or search on metacpan
lib/Badger/Test/Manager.pm view on Meta::CPAN
package Badger::Test::Manager;
use Badger::Class
version => 0.01,
debug => 1,
base => 'Badger::Prototype',
import => 'class';
use Badger::Rainbow
'ANSI_colours ANSI_escape';
eval "use Algorithm::Diff qw( diff )";
our $CAN_DIFF = $@ ? 0 : 1;
our $ESCAPES = qr/\e\[(.*?)m/; # remove ANSI escapes
our $REASON = 'No reason given';
our $MESSAGES = {
no_plan => "You haven't called plan() yet!\n",
dup_plan => "You called plan() twice!\n",
plan => "1..%s\n",
skip_all => "1..0 # skip %s\n",
skip_one => "ok %s # skip %s\n",
name => "test %s at %s line %s",
ok => "ok %s - %s\n",
not_ok => "not ok %s - %s\n%s",
not_eq => "# expect: [%s]\n# result: [%s]\n",
not_ne => "# unexpected match: [%s]\n",
not_like => "# expect: /%s/\n# result: [%s]\n",
not_unlike => "# expect: ! /%s/\n# result: [%s]\n",
too_few => "# Looks like you planned %s tests but only ran %s.\n",
too_many => "# Looks like you planned only %s tests but ran %s.\n",
no_result => "# result is undefined\n",
pass => "# PASS: All %d tests passed\n",
fail => "# FAIL: %d tests failed\n",
mess => "# FAIL: Inconsistent test results\n",
summary => "# %d/%d tests run, %d passed, %d failed, %d skipped\n",
hunk => "# -- diffs %s of %s --\n",
delta => "# %s %3d %s\n",
};
our $SCHEME = {
green => 'ok pass',
red => 'not_ok too_few too_many fail mess',
cyan => 'skip_one skip_all hunk delta',
yellow => 'plan not_eq not_ne not_like not_unlike summary',
};
# Sorry, English and American/Spanish only, no couleur, colori, farbe, etc.
*color = \&colour;
# for nice cleanup in END block
our $INSTANCES = { };
#-----------------------------------------------------------------------
# constructor method
#-----------------------------------------------------------------------
sub init {
my ($self, $config) = @_;
$self->{ plan } = $config->{ plan } || 0;
$self->{ count } = $config->{ count } || 1;
$self->{ results } = $config->{ results } || [ ];
$self->{ summary } = $config->{ summary } || 0;
$self->{ reason } = $config->{ reason } || $REASON;
$self->{ colour } = $config->{ colour } || $config->{ color } || 0;
$INSTANCES->{ $self } = $self;
return $self;
}
#------------------------------------------------------------------------
# plan($n)
#
# Declare how many (more) tests are expected to come. If ok() is called
# before plan() then the results are cached instead of being printed up
# front. When plan() is called, the total number of tests (including any
# cached) is known and the "1..$n" line can be printed along with any
# cached results. After that, calls to ok() generated output immediately.
#------------------------------------------------------------------------
sub plan ($$;$) {
my $self = shift->prototype;
my ($tests, $reason) = @_;
# calling plan() twice would be ambiguous
return $self->error_msg('dup_plan')
if $self->{ plan };
# if $tests == 0 then skip all
return $self->skip_all($reason)
unless $tests;
# update the plan to account for any tests that have already been run
my $results = $self->{ results };
$tests += @$results;
$self->test_msg( plan => $tests );
$self->{ plan } = $tests;
$self->{ tested } = 0;
$self->{ passed } = 0;
$self->{ failed } = 0;
$self->{ skipped } = 0;
# now flush any cached test results
while (@$results) {
lib/Badger/Test/Manager.pm view on Meta::CPAN
return $ok;
}
sub pass ($;$) {
shift->ok(1, @_);
}
sub fail ($;$) {
shift->ok(0, @_);
}
sub is ($$$;$) {
my $self = shift->prototype;
my ($result, $expect, $msg) = @_;
$msg ||= $self->test_name();
if (! defined $result) {
return $self->fail($msg, $self->message('no_result'));
}
# force stringification of $result to avoid 'no eq method' overload errors
$result = "$result" if ref $result;
# if we have coloured output enabled then the result might not match
# the expected because of embedded ANSI escapes, so we strip them out
my ($r, $e) = map {
s/$ESCAPES//g if $self->{ colour };
$_
} ($result, $expect);
if ($r eq $e) {
return $self->pass($msg);
}
else {
return $self->fail($msg, $self->different($expect, $result));
}
}
sub isnt ($$$;$) {
my $self = shift->prototype;
my ($result, $expect, $msg) = @_;
$msg ||= $self->test_name();
# force stringification of $result to avoid 'no eq method' overload errors
$result = "$result" if ref $result;
# if we have coloured output enabled then the result might not match
# the expected because of embedded ANSI escapes, so we strip them out
my ($r, $e) = map {
s/$ESCAPES//g if $self->{ colour };
$_
} ($result, $expect);
if ($r ne $e) {
return $self->pass($msg);
}
else {
for ($expect, $result) {
s/\n/\n |/g;
}
return $self->fail($msg, $self->message( not_eq => $expect, $result ));
}
}
sub like ($$$;$) {
my $self = shift->prototype;
my ($result, $expect, $name) = @_;
$name ||= $self->test_name();
# strip ANSI escapes if necessary
my $r = $result;
$r =~ s/$ESCAPES//g if $self->{ colour };
if ($r =~ $expect) {
$self->pass($name);
}
else {
return $self->fail($name, $self->message( not_like => $expect, $result ));
}
}
sub unlike ($$$;$) {
my $self = shift->prototype;
my ($result, $expect, $name) = @_;
$name ||= $self->test_name();
# strip ANSI escapes if necessary
my $r = $result;
$r =~ s/$ESCAPES//g if $self->{ colour };
if ($r !~ $expect) {
$self->pass($name);
}
else {
return $self->fail($name, $self->message( not_unlike => $expect, $result ));
}
}
sub skip ($;$) {
my $self = shift->prototype;
my $msg = shift || $self->test_name;
return $self->error_msg('no_plan')
unless $self->{ plan };
$self->{ tested }++;
$self->{ skipped }++;
return $self->test_msg( skip_one => $self->{ count }++, $msg );
}
sub skip_some {
my ($self, $n, $msg) = @_;
$n = int $n;
return unless $n > 0;
while ($n--) {
$self->skip($msg);
}
}
sub skip_rest {
my $self = shift->prototype;
my $msg = shift;
my $plan = $self->{ plan };
while ($self->{ tested } < $plan) {
$self->skip($msg);
}
exit;
}
sub skip_all ($;$) {
my $self = shift->prototype;
$self->test_msg( skip_all => shift || $self->{ reason } );
exit;
}
sub result {
my $self = shift->prototype;
my $ok = shift;
return $self->error_msg('no_plan')
unless $self->{ plan };
if ($ok) {
$self->{ passed }++;
return $self->test_msg( ok => @_ );
}
else {
$self->{ failed }++;
return $self->test_msg( not_ok => @_ );
}
}
sub test_msg {
my $self = shift;
print $self->message(@_);
}
sub test_name ($) {
my $self = shift->prototype;
my ($pkg, $file, $line) = caller(2);
$self->message( name => $self->{ count }, $file, $line );
}
sub different {
my ($self, $expect, $result) = @_;
my ($pad_exp, $pad_res) = ($expect, $result);
for ($pad_exp, $pad_res) {
s/\n/\n# |/g;
}
my $msg = $self->message( not_eq => $pad_exp, $pad_res );
return $msg
unless $CAN_DIFF;
my $diffs = diff( map { [ split(/\n/) ] } $expect, $result );
my $n = 0;
my $m = scalar @$diffs;
foreach my $hunk (@$diffs) {
$msg .= $self->message( hunk => ++$n, $m );
foreach my $delta (@$hunk) {
$msg .= $self->message( delta => @$delta );
}
# $msg .= "\n";
}
return $msg;
}
sub colour {
my $self = shift->prototype;
my $ansi = ANSI_colours;
# enable colour mode by inserting ANSI escapes into $MESSAGES
if (@_ && ($self->{ colour } = shift)) {
foreach my $col (keys %$SCHEME) {
my $code = $ansi->{ $col }
|| $self->error("Invalid colour name in \$SCHEME: $col\n");
$MESSAGES->{ $_ } = ANSI_escape($code, $MESSAGES->{ $_ })
for split(/\s+/, $SCHEME->{ $col });
}
Badger::Debug->enable_colour;
}
return $self->{ colour };
}
sub flush {
my $self = shift->prototype;
my $results = shift || $self->{ results };
return unless @$results;
$self->{ plan } ||= @$results;
while (@$results) {
my $test = shift @$results;
$self->result(@$test);
}
}
sub summary {
my $self = shift->prototype;
return @_
? ($self->{ summary } = shift)
: $self->{ summary };
}
sub finish {
my $self = shift->prototype;
$self->flush; # output any cached results
my ($plan, $ran, $pass, $fail, $skip)
( run in 0.561 second using v1.01-cache-2.11-cpan-98e64b0badf )