Test-Kit

 view release on metacpan or  search on metacpan

lib/Test/Kit.pm  view on Meta::CPAN


use Import::Into;
use Module::Runtime 'use_module', 'module_notional_filename';
use Sub::Delete;
use Test::Builder ();
use Test::More ();
use Scalar::Util qw(refaddr);
use Hook::LexWrap qw(wrap);

use parent 'Exporter';
our @EXPORT = ('include');
# my %test_kits_cache = (
#     'MyTest::Awesome' => {
#         'ok' => { source => [ 'Test::More' ], refaddr => 0x1234, },
#         'pass' => { source => [ 'Test::Simple', 'Test::More' ], refaddr => 0xbeef, },
#         'warnings_are' => { source => [ 'Test::Warn' ], refaddr => 0xbead, },
#         ...
#     },
#     ...
# )
#
my %test_kits_cache;

sub include {
    my @to_include = @_;

    my $class = __PACKAGE__;

    my $include_hashref;
    if (grep { ref($_) } @to_include) {
        $include_hashref = { @to_include };
    }
    else {
        $include_hashref = { map { $_ => {} } @to_include };
    }

    return $class->_include($include_hashref);
}

sub _include {
    my $class = shift;
    my $include_hashref = shift;

    my $target = $class->_get_package_to_import_into();

    $class->_make_target_a_test_more_like_exporter($target);

    for my $package (sort keys %$include_hashref) {
        # special cases for strict and warnings on pre-1.3 Test::Builder
        #
        # The logic here is copied from Moose which always causes strict and
        # warnings to be enabled when it is used.
        #
        # A comment in Moose::Exporter states:
        #
        # "this works because both pragmas set $^H (see perldoc perlvar) which
        # affects the current compilation - i.e. the file who use'd us - which
        # is why we don't need to do anything special to make it affect that
        # file rather than this one (which is already compiled)"
        #
        # In the Moose code the author simply calls strict->import() in the
        # appropriate import() method and that does the trick. For us working
        # at a bit more of a distance we have to be a bit trickier - adding
        # strict->import() or warnings->import() to the import method on the
        # target class. We do that by wrapping it with Hook::LexWrap::wrap().
        #
        if ($Test::Builder::VERSION < 1.3 && ($package eq 'strict' || $package eq 'warnings')) {
            wrap "${target}::import", post => sub { $package->import(); };
        }
        else {
            my $fake_package = $class->_create_fake_package($package, $include_hashref->{$package}, $target);
            $fake_package->import::into($target);
        }
    }

    $class->_update_target_exports($target);

    return;
}

sub _get_package_to_import_into {
    my $class = shift;

    # so, as far as I can tell, on Perl 5.14 and 5.16 at least, we have the
    # following callstack...
    #
    # 1. Test::Kit
    # 2. MyTest
    # 3. main
    # 4. main
    # 5. main
    #
    # ... and we want to get the package name "MyTest" out of there.
    # So let's look for the first non-Test::Kit result

    for my $i (1 .. 20) {
        my $caller_package = (caller($i))[0];
        if ($caller_package ne $class) {
            return $caller_package;
        }
    }

    die "Unable to find package to import into";
}

sub _make_target_a_test_more_like_exporter {
    my $class = shift;
    my $target = shift;

    return if $test_kits_cache{$target};

    $class->_check_target_does_not_import($target);

    {
        no strict 'refs';
        push @{ "${target}::ISA" }, 'Test::Builder::Module';

        # need to explicitly do this so that if we need to wrap import()
        # for strict and warnings includes it already exists at the right
        # point.
        *{ "${target}::import" } = \&Test::Builder::Module::import;
    }

    $test_kits_cache{$target} = {};



( run in 0.522 second using v1.01-cache-2.11-cpan-39bf76dae61 )