The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

BEGIN
{
	chdir 't' if -d 't';
	use lib '../lib', '../blib/lib';
}

use strict;
use IO::String;
use File::Spec::Functions 'catfile';

use Test::More tests => 35;
use Test::Exception;

my $module = 'Module::Build::TestReporter';
use_ok( $module ) or exit;

can_ok( $module, 'new' );

my %args = ( module_name => 'Some::Module', dist_version => '1.0' );
my $tr   = $module->new( %args );

isa_ok( $tr, 'Module::Build' );
isa_ok( $tr, $module );
is( $tr->notes( 'report_file' ), 'test_failures.txt',
	'new() should set report_file to test_failures.txt by default' );

$args{report_file} = 'report.txt';
$tr                = $module->new( %args );
is( $tr->notes( 'report_file' ), 'report.txt',
	'... but should set if it passed' );

can_ok( $tr, 'ACTION_test' );
my $io = IO::String->new();
{
	my @args;

	local *Module::Build::ACTION_test;
	*Module::Build::ACTION_test = sub { @args = @_ };

	my $oldfh = select( $io );
	$tr->ACTION_test( qw( 1 2 3 ) );
	is( $io->getline, undef, 'ACTION_test() should not write to selected fh' );
	is_deeply (\@args, [ $tr, 1 .. 3 ], '... calling SUPER with args' );
	my $newfh = select( $oldfh );
	is( $newfh, $io, '... and should restore selected fh' );
}

my $fail_filename;

can_ok( $tr, 'find_test_files' );
{
	local $ENV{TEST_VERBOSE} = 0;
	local *Module::Build::find_test_files;
	*Module::Build::find_test_files = sub
	{
		[ map { catfile( 'fake_tests', $_ ) } qw( fail.t pass.t ) ]
	};

	my $rf;

	local *Module::Build::TestReporter::report_failures;
	*Module::Build::TestReporter::report_failures = sub
	{
		$rf++;
	};

	$tr->notes( test_failures => [ 1 .. 10 ] );

	my $outfh = IO::String->new();
	$tr->notes( test_oldfh => $outfh );

	my $result = $tr->find_test_files();
	is_deeply( $result, [], 'find_test_files() should return empty arrayref' );
	my $text = $outfh->string_ref();
	is( $$text, '', '... writing no output by default' );
	$outfh->pos( 0 );
	is( $rf, 1, '... reporting failures' );
	is( @{ $tr->notes( 'test_failures' ) }, 1,
		'... having cleared out any existing failures' );

	$ENV{TEST_VERBOSE} = 1;
	$tr->find_test_files();
	($fail_filename, my $pass_filename) = @{Module::Build::find_test_files()};
	is( $$text, "${pass_filename}...ok\n", '... writing no output by default' );
	is( $rf, 2, '... yet still reporting failures' );
}

can_ok( $tr, 'save_failure_details' );

my $failures = $tr->notes( 'test_failures' );
is( @$failures, 1,
	'save_failure_details() should save results of all failures' );
is_deeply( $failures->[0],
	{
		file => $fail_filename,
		passing =>  0,
		max     => 10,
		bonus   =>  0,
		ok      =>  9,
		seen    => 10,
		skip    =>  0,
		todo    =>  0,
		failures => [
			{
				number      => 10,
				description => 'no, it is not',
				diagnostics => "\n    Failed test ($fail_filename at line 9)\n"
				            .  "         got: 'foo'\n    expected: 'bar'\n",
			}
		],
	}, '... saving failure information' );

can_ok( $tr, 'report_failures' );
{
	my $outfh    = IO::String->new();
	my $failures = $tr->notes( 'test_failures' );
	$tr->notes( test_failures =>     [] );
	$tr->notes( test_oldfh    => $outfh );

	$tr->report_failures();
	my $text = $outfh->string_ref();
	is( $$text, "All tests passed...\n",
		'report_failures() should report success with no failures' );

	$tr->notes( test_failures => $failures );

	# this report has no actual failures, so skip it
	push @$failures,
	{
		file    => 'another_file.t', 
		ok      =>  6,
		seen    =>  6,
	};

	my ($full_report, $version, $fail_report);

	local *Module::Build::TestReporter::write_report;
	*Module::Build::TestReporter::write_report = sub 
	{
		($full_report, $version) = splice( @_, 1, 2 );
	};

	local *Module::Build::TestReporter::write_failure_results;
	*Module::Build::TestReporter::write_failure_results = sub
	{
		((my $self), $fail_report) = @_;
	};

	$tr->report_failures();

	my $fail_header  = qr/Test failures in '$fail_filename' \(1\/10\):/;
	my $fail_details = qr/fail.t.+line 9.+got: 'foo'.+expected: 'bar'/s;

	like( $full_report, $fail_header,
		'report_failures() should write a full report for all failed tests' );
	like( $full_report, $fail_details,
		'... with test failure information' );

	like( $version, qr/Summary of my perl.+Characteristics of this binary/s,
		'... and the full -V information of this perl' );

	like( $fail_report, $fail_header, '... and a failure report' );
	like( $fail_report, $fail_header, '... with failure details' );
}

can_ok( $tr, 'write_report' );
$tr->write_report( 'my report', '+version' );
ok( -e 'report.txt', 'write_report() should write its report' );
my $text = do { local (@ARGV, $/) = 'report.txt'; <> };
is( $text, 'my report+version', '... from the report passed' );

$tr->notes( report_file => '' );
throws_ok
	{ $tr->write_report( 'my report' ) }
	qr/Can't write/, 
	'... throwing an exception if it cannot write test data';

can_ok( $tr, 'write_failure_results' );
{
	local $ENV{TEST_VERBOSE} = 0;
	my $outfh                = IO::String->new();
	$tr->notes( test_oldfh => $outfh );
	my $text                 = $outfh->string_ref();

	# no contact specified
	$tr->notes( report_address => '' );
	$tr->write_failure_results( 'my report' );
	is( $$text, "Tests failed!\n",
		'write_failure_results() should only warn of failure without contact' );
	$outfh->pos( 0 );

	$tr->notes( report_file    => 'test_failures.txt'    );
	$tr->notes( report_address => 'failures@example.com' );
	$tr->write_failure_results( 'my report' );
	like( $$text, qr/e-mail 'test_failures.txt' to failures\@/,
		'... or giving e-mail directions with a contact' );
	$outfh->pos( 0 );

	$ENV{TEST_VERBOSE}       = 1;
	$tr->write_failure_results( 'my report' );
	like( $$text, qr/my report/, '... adding the report in verbose mode' );
}

END
{
	1 while unlink 'report.txt';
}