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 )