package Fennec::Assert::Interceptor;
BEGIN {
  $Fennec::Assert::Interceptor::VERSION = '0.030';
}
use strict;
use warnings;

use Fennec::Assert;

use Fennec::Util::Alias qw/
    Fennec::Collector::Interceptor
    Fennec::Runner
/;

util( "capture", "codeblock", sub(&) {
    my ( $code ) = @_;
    my $collector = Interceptor->new;
    Runner->run_with_collector( $collector, $code );
    return $collector->intercepted;
});

util ln => sub {
    my ( $diff ) = @_;
    my ( undef, undef, $line ) = caller;
    return $line + $diff;
};

tester 'result_line_numbers_are';
sub result_line_numbers_are {
    my ( $results, @numbers ) = @_;
    result(
        pass => 0,
        name => "result+line counts match",
        stderr => "Number of results, and number of line numbers do not match"
    ) unless @$results == @numbers;

    my $count = 0;
    for my $result ( @$results ) {
        result_line_number_is(
            $result,
            $numbers[$count],
            "Line number for result #$count is " . $numbers[$count]
        );
        $count++;
    }
};

tester 'result_line_number_is';
sub result_line_number_is {
    my ( $result, $number, $name ) = @_;
    my $pass = $number == $result->line ? 1 : 0;
    result(
        pass => $pass,
        name => $name,
        $pass ? () : (stderr => [ "Got: " . $result->line, "Wanted: $number" ]),
    );
};

1;

=head1 NAME

Fennec::Assert::Interceptor - Intercept results generated by asserts in order to test them.

=head1 DESCRIPTION

When writing custom assertion libraries it helps to be able to test the results
they generate. L<Test::Builder> does this using L<Test::Builder::Tester> which
captures output and requires you to generate the same output seperetly and
compare them. Frankly I hate Test::Builders tester.

Fennec's interceptor assertion library provides a function that lets you
capture results generated within a codeblock. All output is captured and
returned as an arrayref of L<Fennec::Output> objects. You can then check the
result and diag objects themselves. This provides additional tools for working
with the returned results.

=head1 SYNOPSIS

    use Fennec asserts => [ 'Core', 'Interceptor' ];

    tests verify_results => sub {
        my $results = capture {
            ok( 1, "pass" );
            ok( 0, "fail" );
            diag( "a diag message" );
        };
        my @lines = ( ln(-4), ln(-3) );

        ok( $results->[0]->pass, "First result passed" );
        is( $results->[0]->name, "pass", "First result name" );
        is( $results->[0]->line, $lines[0], "Correct result line number" );
        isa_ok( $results->[0], 'Fennec::Output::Result' );

        ok( !$results->[1]->pass, "Second result failed" );
        is( $results->[1]->name, "fail", "Second result name" );
        is( $results->[1]->line, $lines[1], "Correct result line number" );
        isa_ok( $results->[1], 'Fennec::Output::Result' );

        is_deeply(
            $results->[2]->stderr,
            [ "a diag message" ],
            "diag message"
        );
        isa_ok( $results->[2], 'Fennec::Output::Diag' );
    };

    1;

=head1 FUNCTIONS

=over 4

=item $results = capture { ... }

Capture and return all the results generated within a codeblock.

=item $line = ln( $int )

Return the current line number plus $int. Useful for determining what line
number a result should have.

=item result_line_number_is( $result, $line; $name )

Check the line number of a result.

=item result_line_numbers_are( \@results, @lines )

Check the line numbers of several results.

=back

=head1 MANUAL

=over 2

=item L<Fennec::Manual::Quickstart>

The quick guide to using Fennec.

=item L<Fennec::Manual::User>

The extended guide to using Fennec.

=item L<Fennec::Manual::Developer>

The guide to developing and extending Fennec.

=item L<Fennec::Manual>

Documentation guide.

=back

=head1 AUTHORS

Chad Granum L<exodist7@gmail.com>

=head1 COPYRIGHT

Copyright (C) 2010 Chad Granum

Fennec is free software; Standard perl licence.

Fennec is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
