The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package main;

use 5.006002;

use strict;
use warnings;

use lib qw{ mock };

use Scalar::Util qw{ blessed };
use Test::More 0.88;	# Because of done_testing();

use CPAN::Access::AdHoc;
use CPAN::Access::AdHoc::Archive;
use CPAN::Access::AdHoc::Util qw{ :all };

sub exception ($$$$);
sub init (@);
sub warning ($$$$);

init;

warning   \&__whinge, 'Awww', qr{\AAwww\b}, 'Check __whinge';

exception \&__wail, 'Pfui', qr{\APfui\b}, 'Check __wail';

exception \&__weep, 'Fubar', qr{\AProgramming Error - Fubar},
    'Check __weep';

exception \&__load, '1module', qr{\AMalformed module name '1module'},
    'Check __load';

exception new => [ fubar => 'bazzle' ],
    qr{\A\QUnknown attribute(s): fubar},
    'New with invalid arguments.';

exception fetch => 'fubar/bazzle',
    qr{/fubar/bazzle: 404\b},
    'Fetch a non-existant file.';

warning http_error_handler => sub {
    my ( $self, $url, $resp ) = @_;
    $url =~ m{ /modules/02packages_details [.] txt [.] gz \z }smx
	and return;
    die "$url not found\n"
}, undef,
    'Set an alternate HTTP error handler';

exception fetch => 'fubar/bazzle',
    qr{\Afubar/bazzle not found\b},
    'Fetch a non-existant file and get alternate warning.';

eval {
    warning fetch => 'fubar/modules/02packages.details.txt.gz', undef,
	'Can supress exception with HTTP error handler';
    1;
} or fail "HTTP error handler allowed unexpected error $@";

warning http_error_handler => undef, undef,
    'Restore the original HTTP error handler';

exception fetch => 'fubar/bazzle',
    qr{/fubar/bazzle: 404\b},
    'Fetch a non-existant file and get original warning.';


SKIP: {
    my $tests = 1;

    eval {
	require LWP::UserAgent;
	1;
    } or skip 'LWP::UserAgent can not be loaded', $tests;

    eval {
	require HTTP::Response;
	1;
    } or skip 'HTTP::Response can not be loaded', $tests;

    no warnings qw{ redefine };

    local *LWP::MediaTypes::guess_media_type = sub {
	my ( $url, $rslt ) = @_;
	$rslt->header( 'Content-Type' => 'something/awful' );
	return;
    };

    exception fetch => 'authors/01mailrc.txt.gz',
	qr{Unsupported Content-Type 'something/awful'},
	'Unexpected Content-Type';
}

SKIP: {
    my $tests = 3;

    eval {
	require Errno;
	Errno->can( 'ENOENT' );
	1;
    } or skip 'Errno can not be loaded, or does not support ENOENT';

    no warnings qw{ redefine };

    local *IO::File::new = sub {
	$! = Errno::ENOENT();
	return;
    };

    exception fetch_author_index => [],
	qr{\AUnable to open string reference:},
	'Failure to open a string reference in fetch_author_index()';

    exception fetch_module_index => [],
	qr{\AUnable to open string reference:},
	'Failure to open a string reference in fetch_module_index()';

    exception fetch_registered_module_index => [],
	qr{\AUnable to open string reference:},
	'Failure to open a string reference in fetch_registered_module_index()';
}

exception fetch_distribution_checksums => 'fubar',
    qr{\AFailed to get file:},
    'Fetch checksums for an invalid distribution name';

SKIP: {
    my $tests = 1;

    eval {
	require Digest::SHA;
	1;
    } or skip 'Unable to load Digest::SHA', $tests;

    no warnings qw{ redefine };

    local *Digest::SHA::sha256_hex = sub {
	return 'impossible checksum';
    };

    exception fetch_distribution_archive => 'BACH/Johann-0.001.tar.bz2',
	qr{\AChecksum failure on},
	'Checksum failure';
}

exception config => {},
    qr{\AAttribute 'config' must be a file name or a Config::Tiny reference},
    'Set config() to invalid configuration';

exception default_cpan_source => 'fubar',
    qr{\AUnknown default_cpan_source 'fubar'},
    'Set default_cpan_source() to bad value';

exception cpan => 'fubar://bazzle/',
    qr{\AURL scheme fubar: is unsupported},
    'Set cpan() to invalid URL.';

init bless {}, 'CPAN::Access::AdHoc::Archive';

exception base_directory => [],
    qr{\A\QProgramming Error - The base_directory() method must be overridden},
    'Must override the CPAN::Access::AdHoc::Archive base_directory method';

exception extract => [],
    qr{\A\QProgramming Error - The extract() method must be overridden},
    'Must override the CPAN::Access::AdHoc::Archive extract method';

exception get_item_content => [],
    qr{\A\QProgramming Error - The get_item_content() method must be overridden},
    'Must override the CPAN::Access::AdHoc::Archive get_item_content method';

exception get_item_mtime => [],
    qr{\A\QProgramming Error - The get_item_mtime() method must be overridden},
    'Must override the CPAN::Access::AdHoc::Archive get_item_mtime method';

exception item_present => [],
    qr{\A\QProgramming Error - The item_present() method must be overridden},
    'Must override the CPAN::Access::AdHoc::Archive item_present method';

exception list_contents => [],
    qr{\A\QProgramming Error - The list_contents() method must be overridden},
    'Must override the CPAN::Access::AdHoc::Archive list_contents method';

exception wrap_archive => [
	{ author => 'NOBODY', directory => 'modules/' },
	'mock/repos/modules/02packages.details.txt.gz',
    ],
    qr{\ASpecifying both 'author' and 'directory' is ambiguous\b},
    q{Can not specify both 'author and 'directory' to wrap_archive};

done_testing;

{

    my $cad;

    my %instantiator;

    BEGIN {
	%instantiator = map { $_ => 1 } qw{ new };
    }

    sub object {
	return $cad;
    }

    sub _xqt {
	my ( $method, $args ) = @_;
	'ARRAY' eq ref $args
	    or $args = [ $args ];
	my $rslt = eval {
	    if ( 'CODE' eq ref $method ) {
		$method->( @{ $args } );
	    } elsif ( $instantiator{$method} ) {
		CPAN::Access::AdHoc->$method( @{ $args } );
	    } else {
		$cad->$method( @{ $args } );
	    }
	    1;
	};
	return $rslt;
    }

    sub exception ($$$$) {
	my ( $method, $args, $exception, $title ) = @_;
	_xqt( $method, $args ) or do {
	    if ( defined( my $err = $@ ) ) {
		@_ = ( $err, $exception, $title );
		'Regexp' eq ref $exception
		    and goto &like;
		goto &is;
	    } else {
		@_ = "$method() failed, but \$@ not set";
		goto &fail;
	    }
	};
	@_ = "$method() unexpectedly succeeded";
	goto &fail;
    }

    sub init (@) {
	my ( $class, @args ) = @_;

	if ( blessed( $class ) ) {
	    $cad = $class;
	    return;
	}

	$class ||= 'CPAN::Access::AdHoc';
	$cad = undef;
	eval {
	    $cad = $class->new( @args );
	    1;
	} and $cad or do {
	    @_ = 'Failed to instantiate CPAN::Access::AdHoc';
	    goto &fail;
	};
	@_ = 'Instantiated CPAN::Access::AdHoc';
	goto &pass;
    }

    sub warning ($$$$) {
	my ( $method, $args, $warning, $title ) = @_;
	my $err;
	{
	    local $SIG{__WARN__} = sub { $err = $_[0] };
	    _xqt( $method, $args );
	}
	if ( defined $err ) {
	    if ( defined $warning ) {
		@_ = ( $err, $warning, $title );
		'Regexp' eq ref $warning
		    and goto &like;
		goto &is;
	    } else {
		@_ = ( "$title gave warning '$err'" );
		goto &fail;
	    }
	} elsif ( defined $warning ) {
	    @_ = "$method() did not generate a warning";
	    goto &fail;
	} else {
	    @_ = ( $title );
	    goto &pass;
	}
    }

}

1;

# ex: set textwidth=72 :