From 7e7ddbe33ca34359d711aee1e0ddebaeb40c2a18 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Thu, 25 Jun 2015 04:47:55 +0000 Subject: Class-Load-0.23 --- t/00-report-prereqs.dd | 126 ++++++++++++++++++ t/00-report-prereqs.t | 183 ++++++++++++++++++++++++++ t/000-load.t | 15 +++ t/001-is-class-loaded.t | 108 +++++++++++++++ t/002-try-load-class.t | 38 ++++++ t/003-load-class.t | 96 ++++++++++++++ t/004-load-double.t | 28 ++++ t/005-load-optional.t | 42 ++++++ t/006-returned-error.t | 48 +++++++ t/007-first-existing.t | 123 +++++++++++++++++ t/008-gvstash-bug.t | 23 ++++ t/009-invalid-module-name.t | 25 ++++ t/010-isa-false-positive.t | 67 ++++++++++ t/011-without-xs.t | 22 ++++ t/012-without-implementation.t | 20 +++ t/013-errors.t | 123 +++++++++++++++++ t/014-weird-constants.t | 34 +++++ t/lib/Class/Load/Error/DieAfterBeginIsa.pm | 11 ++ t/lib/Class/Load/Error/DieAfterIsa.pm | 14 ++ t/lib/Class/Load/Error/SyntaxErrorAfterIsa.pm | 11 ++ t/lib/Class/Load/OK.pm | 9 ++ t/lib/Class/Load/Stash.pm | 7 + t/lib/Class/Load/Stash/Sub.pm | 7 + t/lib/Class/Load/SyntaxError.pm | 7 + t/lib/Class/Load/VersionCheck.pm | 11 ++ t/lib/Class/Load/VersionCheck2.pm | 11 ++ t/lib/Test/Class/Load.pm | 17 +++ 27 files changed, 1226 insertions(+) create mode 100644 t/00-report-prereqs.dd create mode 100644 t/00-report-prereqs.t create mode 100644 t/000-load.t create mode 100644 t/001-is-class-loaded.t create mode 100644 t/002-try-load-class.t create mode 100644 t/003-load-class.t create mode 100644 t/004-load-double.t create mode 100644 t/005-load-optional.t create mode 100644 t/006-returned-error.t create mode 100644 t/007-first-existing.t create mode 100644 t/008-gvstash-bug.t create mode 100644 t/009-invalid-module-name.t create mode 100644 t/010-isa-false-positive.t create mode 100644 t/011-without-xs.t create mode 100644 t/012-without-implementation.t create mode 100644 t/013-errors.t create mode 100644 t/014-weird-constants.t create mode 100644 t/lib/Class/Load/Error/DieAfterBeginIsa.pm create mode 100644 t/lib/Class/Load/Error/DieAfterIsa.pm create mode 100644 t/lib/Class/Load/Error/SyntaxErrorAfterIsa.pm create mode 100644 t/lib/Class/Load/OK.pm create mode 100644 t/lib/Class/Load/Stash.pm create mode 100644 t/lib/Class/Load/Stash/Sub.pm create mode 100644 t/lib/Class/Load/SyntaxError.pm create mode 100644 t/lib/Class/Load/VersionCheck.pm create mode 100644 t/lib/Class/Load/VersionCheck2.pm create mode 100644 t/lib/Test/Class/Load.pm (limited to 't') diff --git a/t/00-report-prereqs.dd b/t/00-report-prereqs.dd new file mode 100644 index 0000000..9f1bab8 --- /dev/null +++ b/t/00-report-prereqs.dd @@ -0,0 +1,126 @@ +do { my $x = { + 'configure' => { + 'requires' => { + 'ExtUtils::MakeMaker' => '0', + 'perl' => '5.006' + } + }, + 'develop' => { + 'recommends' => { + 'Dist::Zilla::PluginBundle::Author::ETHER' => '0.097' + }, + 'requires' => { + 'Dist::Zilla' => '5', + 'Dist::Zilla::Plugin::Authority' => '1.009', + 'Dist::Zilla::Plugin::AuthorityFromModule' => '0.002', + 'Dist::Zilla::Plugin::AutoMetaResources' => '0', + 'Dist::Zilla::Plugin::AutoPrereqs' => '0', + 'Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional' => '0.004', + 'Dist::Zilla::Plugin::CheckIssues' => '0', + 'Dist::Zilla::Plugin::CheckPrereqsIndexed' => '0', + 'Dist::Zilla::Plugin::CheckSelfDependency' => '0', + 'Dist::Zilla::Plugin::CheckStrictVersion' => '0', + 'Dist::Zilla::Plugin::ConfirmRelease' => '0', + 'Dist::Zilla::Plugin::CopyFilesFromRelease' => '0', + 'Dist::Zilla::Plugin::FileFinder::ByName' => '0', + 'Dist::Zilla::Plugin::GenerateFile::ShareDir' => '0', + 'Dist::Zilla::Plugin::Git::Check' => '0', + 'Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch' => '0.004', + 'Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts' => '0', + 'Dist::Zilla::Plugin::Git::Commit' => '2.020', + 'Dist::Zilla::Plugin::Git::Contributors' => '0.004', + 'Dist::Zilla::Plugin::Git::Describe' => '0.004', + 'Dist::Zilla::Plugin::Git::GatherDir' => '2.016', + 'Dist::Zilla::Plugin::Git::Push' => '0', + 'Dist::Zilla::Plugin::Git::Remote::Check' => '0', + 'Dist::Zilla::Plugin::Git::Tag' => '0', + 'Dist::Zilla::Plugin::GitHub::Update' => '0.40', + 'Dist::Zilla::Plugin::GithubMeta' => '0', + 'Dist::Zilla::Plugin::InstallGuide' => '0', + 'Dist::Zilla::Plugin::Keywords' => '0.004', + 'Dist::Zilla::Plugin::License' => '0', + 'Dist::Zilla::Plugin::MakeMaker' => '0', + 'Dist::Zilla::Plugin::Manifest' => '0', + 'Dist::Zilla::Plugin::MetaConfig' => '0', + 'Dist::Zilla::Plugin::MetaJSON' => '0', + 'Dist::Zilla::Plugin::MetaNoIndex' => '0', + 'Dist::Zilla::Plugin::MetaProvides::Package' => '1.15000002', + 'Dist::Zilla::Plugin::MetaResources' => '0', + 'Dist::Zilla::Plugin::MetaTests' => '0', + 'Dist::Zilla::Plugin::MetaYAML' => '0', + 'Dist::Zilla::Plugin::MinimumPerl' => '1.006', + 'Dist::Zilla::Plugin::MojibakeTests' => '0', + 'Dist::Zilla::Plugin::NextRelease' => '5.033', + 'Dist::Zilla::Plugin::PodSyntaxTests' => '0', + 'Dist::Zilla::Plugin::Prereqs' => '0', + 'Dist::Zilla::Plugin::Prereqs::AuthorDeps' => '0', + 'Dist::Zilla::Plugin::PromptIfStale' => '0', + 'Dist::Zilla::Plugin::Readme' => '0', + 'Dist::Zilla::Plugin::ReadmeAnyFromPod' => '0.142180', + 'Dist::Zilla::Plugin::RewriteVersion::Transitional' => '0.004', + 'Dist::Zilla::Plugin::Run::AfterBuild' => '0.038', + 'Dist::Zilla::Plugin::Run::AfterRelease' => '0.038', + 'Dist::Zilla::Plugin::RunExtraTests' => '0.024', + 'Dist::Zilla::Plugin::SurgicalPodWeaver' => '0', + 'Dist::Zilla::Plugin::Test::CPAN::Changes' => '0.008', + 'Dist::Zilla::Plugin::Test::ChangesHasContent' => '0', + 'Dist::Zilla::Plugin::Test::Compile' => '2.039', + 'Dist::Zilla::Plugin::Test::EOL' => '0.17', + 'Dist::Zilla::Plugin::Test::Kwalitee' => '2.06', + 'Dist::Zilla::Plugin::Test::MinimumVersion' => '2.000003', + 'Dist::Zilla::Plugin::Test::NoTabs' => '0.08', + 'Dist::Zilla::Plugin::Test::PodSpelling' => '2.006001', + 'Dist::Zilla::Plugin::Test::Portability' => '0', + 'Dist::Zilla::Plugin::Test::ReportPrereqs' => '0.019', + 'Dist::Zilla::Plugin::TestRelease' => '0', + 'Dist::Zilla::Plugin::UploadToCPAN' => '0', + 'Dist::Zilla::PluginBundle::Author::ETHER' => '0.094', + 'File::Spec' => '0', + 'IO::Handle' => '0', + 'IPC::Open3' => '0', + 'Test::CPAN::Changes' => '0.19', + 'Test::CPAN::Meta' => '0', + 'Test::EOL' => '0', + 'Test::Kwalitee' => '1.21', + 'Test::More' => '0.94', + 'Test::NoTabs' => '0', + 'Test::Pod' => '1.41', + 'Test::Spelling' => '0.12', + 'Test::Without::Module' => '0' + } + }, + 'runtime' => { + 'requires' => { + 'Carp' => '0', + 'Data::OptList' => '0', + 'Exporter' => '0', + 'Module::Implementation' => '0.04', + 'Module::Runtime' => '0.012', + 'Package::Stash' => '0.14', + 'Scalar::Util' => '0', + 'Try::Tiny' => '0', + 'base' => '0', + 'perl' => '5.006', + 'strict' => '0', + 'warnings' => '0' + } + }, + 'test' => { + 'recommends' => { + 'CPAN::Meta' => '2.120900' + }, + 'requires' => { + 'ExtUtils::MakeMaker' => '0', + 'File::Spec' => '0', + 'Test::Fatal' => '0', + 'Test::More' => '0.88', + 'Test::Requires' => '0', + 'constant' => '0', + 'lib' => '0', + 'perl' => '5.006', + 'version' => '0' + } + } + }; + $x; + } \ No newline at end of file diff --git a/t/00-report-prereqs.t b/t/00-report-prereqs.t new file mode 100644 index 0000000..d8d15ba --- /dev/null +++ b/t/00-report-prereqs.t @@ -0,0 +1,183 @@ +#!perl + +use strict; +use warnings; + +# This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.021 + +use Test::More tests => 1; + +use ExtUtils::MakeMaker; +use File::Spec; + +# from $version::LAX +my $lax_version_re = + qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? + | + (?:\.[0-9]+) (?:_[0-9]+)? + ) | (?: + v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? + | + (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? + ) + )/x; + +# hide optional CPAN::Meta modules from prereq scanner +# and check if they are available +my $cpan_meta = "CPAN::Meta"; +my $cpan_meta_pre = "CPAN::Meta::Prereqs"; +my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic + +# Verify requirements? +my $DO_VERIFY_PREREQS = 1; + +sub _max { + my $max = shift; + $max = ( $_ > $max ) ? $_ : $max for @_; + return $max; +} + +sub _merge_prereqs { + my ($collector, $prereqs) = @_; + + # CPAN::Meta::Prereqs object + if (ref $collector eq $cpan_meta_pre) { + return $collector->with_merged_prereqs( + CPAN::Meta::Prereqs->new( $prereqs ) + ); + } + + # Raw hashrefs + for my $phase ( keys %$prereqs ) { + for my $type ( keys %{ $prereqs->{$phase} } ) { + for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { + $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; + } + } + } + + return $collector; +} + +my @include = qw( + +); + +my @exclude = qw( + +); + +# Add static prereqs to the included modules list +my $static_prereqs = do 't/00-report-prereqs.dd'; + +# Merge all prereqs (either with ::Prereqs or a hashref) +my $full_prereqs = _merge_prereqs( + ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), + $static_prereqs +); + +# Add dynamic prereqs to the included modules list (if we can) +my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; +if ( $source && $HAS_CPAN_META ) { + if ( my $meta = eval { CPAN::Meta->load_file($source) } ) { + $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); + } +} +else { + $source = 'static metadata'; +} + +my @full_reports; +my @dep_errors; +my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; + +# Add static includes into a fake section +for my $mod (@include) { + $req_hash->{other}{modules}{$mod} = 0; +} + +for my $phase ( qw(configure build test runtime develop other) ) { + next unless $req_hash->{$phase}; + next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); + + for my $type ( qw(requires recommends suggests conflicts modules) ) { + next unless $req_hash->{$phase}{$type}; + + my $title = ucfirst($phase).' '.ucfirst($type); + my @reports = [qw/Module Want Have/]; + + for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { + next if $mod eq 'perl'; + next if grep { $_ eq $mod } @exclude; + + my $file = $mod; + $file =~ s{::}{/}g; + $file .= ".pm"; + my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; + + my $want = $req_hash->{$phase}{$type}{$mod}; + $want = "undef" unless defined $want; + $want = "any" if !$want && $want == 0; + + my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; + + if ($prefix) { + my $have = MM->parse_version( File::Spec->catfile($prefix, $file) ); + $have = "undef" unless defined $have; + push @reports, [$mod, $want, $have]; + + if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { + if ( $have !~ /\A$lax_version_re\z/ ) { + push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; + } + elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { + push @dep_errors, "$mod version '$have' is not in required range '$want'"; + } + } + } + else { + push @reports, [$mod, $want, "missing"]; + + if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { + push @dep_errors, "$mod is not installed ($req_string)"; + } + } + } + + if ( @reports ) { + push @full_reports, "=== $title ===\n\n"; + + my $ml = _max( map { length $_->[0] } @reports ); + my $wl = _max( map { length $_->[1] } @reports ); + my $hl = _max( map { length $_->[2] } @reports ); + + if ($type eq 'modules') { + splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; + push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; + } + else { + splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; + push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; + } + + push @full_reports, "\n"; + } + } +} + +if ( @full_reports ) { + diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; +} + +if ( @dep_errors ) { + diag join("\n", + "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n", + "The following REQUIRED prerequisites were not satisfied:\n", + @dep_errors, + "\n" + ); +} + +pass; + +# vim: ts=4 sts=4 sw=4 et: diff --git a/t/000-load.t b/t/000-load.t new file mode 100644 index 0000000..e9e073d --- /dev/null +++ b/t/000-load.t @@ -0,0 +1,15 @@ +use strict; +use warnings; +use Test::More 0.88; + +use lib 't/lib'; + +use Module::Implementation 0.04 (); + +use_ok 'Test::Class::Load'; + +diag( 'Using ' + . Module::Implementation::implementation_for('Class::Load') + . ' implementation' ); + +done_testing; diff --git a/t/001-is-class-loaded.t b/t/001-is-class-loaded.t new file mode 100644 index 0000000..e92c841 --- /dev/null +++ b/t/001-is-class-loaded.t @@ -0,0 +1,108 @@ +use strict; +use warnings; +use Test::More 0.88; + +use version; + +use lib 't/lib'; +use Test::Class::Load 'is_class_loaded'; + +# basic {{{ +ok(is_class_loaded('Class::Load'), "Class::Load is loaded"); +ok(!is_class_loaded('Class::Load::NONEXISTENT'), "nonexistent class is NOT loaded"); +# }}} + +# @ISA (yes) {{{ +do { + package Class::Load::WithISA; + our @ISA = 'Class::Load'; +}; +ok(is_class_loaded('Class::Load::WithISA'), "class that defines \@ISA is loaded"); +# }}} +# $ISA (no) {{{ +do { + package Class::Load::WithScalarISA; + our $ISA = 'Class::Load'; +}; +ok(!is_class_loaded('Class::Load::WithScalarISA'), "class that defines \$ISA is not loaded"); +# }}} +# $VERSION (yes) {{{ +do { + package Class::Load::WithVERSION; + our $VERSION = '1.0'; +}; +ok(is_class_loaded('Class::Load::WithVERSION'), "class that defines \$VERSION is loaded"); +# }}} +# $VERSION is a version object (yes) {{{ +do { + package Class::Load::WithVersionObject; + our $VERSION = version->new(1); +}; +ok(is_class_loaded('Class::Load::WithVersionObject'), 'when $VERSION contains a version object, we still return true'); +# }}} +# method (yes) {{{ +do { + package Class::Load::WithMethod; + sub foo { } +}; +ok(is_class_loaded('Class::Load::WithMethod'), "class that defines any method is loaded"); +# }}} +# global scalar (no) {{{ +do { + package Class::Load::WithScalar; + our $FOO = 1; +}; +ok(!is_class_loaded('Class::Load::WithScalar'), "class that defines just a scalar is not loaded"); +# }}} +# subpackage (no) {{{ +do { + package Class::Load::Foo::Bar; + sub bar {} +}; +ok(!is_class_loaded('Class::Load::Foo'), "even if Foo::Bar is loaded, Foo is not"); +# }}} +# superstring (no) {{{ +do { + package Class::Load::Quuxquux; + sub quux {} +}; +ok(!is_class_loaded('Class::Load::Quux'), "Quuxquux does not imply the existence of Quux"); +# }}} +# use constant (yes) {{{ +do { + package Class::Load::WithConstant; + use constant PI => 3; +}; +ok(is_class_loaded('Class::Load::WithConstant'), "defining a constant means the class is loaded"); +# }}} +# use constant with reference (yes) {{{ +do { + package Class::Load::WithRefConstant; + use constant PI => \3; +}; +ok(is_class_loaded('Class::Load::WithRefConstant'), "defining a constant as a reference means the class is loaded"); +# }}} +# stub (yes) {{{ +do { + package Class::Load::WithStub; + sub foo; +}; +ok(is_class_loaded('Class::Load::WithStub'), "defining a stub means the class is loaded"); +# }}} +# stub with prototype (yes) {{{ +do { + package Class::Load::WithPrototypedStub; + sub foo (&); +}; +ok(is_class_loaded('Class::Load::WithPrototypedStub'), "defining a stub with a prototype means the class is loaded"); +# }}} + +ok(!is_class_loaded('Class::Load::VersionCheck'), 'Class::Load::VersionCheck has not been loaded yet'); +require Class::Load::VersionCheck; +ok(is_class_loaded('Class::Load::VersionCheck'), 'Class::Load::VersionCheck has been loaded'); +ok(!is_class_loaded('Class::Load::VersionCheck', {-version => 43}), + 'Class::Load::VersionCheck has been loaded but the version check failed'); +ok(is_class_loaded('Class::Load::VersionCheck', {-version => 41}), + 'Class::Load::VersionCheck has been loaded and the version check passed'); + +done_testing; diff --git a/t/002-try-load-class.t b/t/002-try-load-class.t new file mode 100644 index 0000000..a2f46c8 --- /dev/null +++ b/t/002-try-load-class.t @@ -0,0 +1,38 @@ +use strict; +use warnings; +use Test::More 0.88; +use lib 't/lib'; +use Test::Class::Load ':all'; + +ok(try_load_class('Class::Load::OK'), "loaded class OK"); +is($Class::Load::ERROR, undef); + +ok(!try_load_class('Class::Load::Nonexistent'), "didn't load class Nonexistent"); +like($Class::Load::ERROR, qr{^Can't locate Class/Load/Nonexistent.pm in \@INC}); + +ok(try_load_class('Class::Load::OK'), "loaded class OK"); +is($Class::Load::ERROR, undef); + +ok(!try_load_class('Class::Load::SyntaxError'), "didn't load class SyntaxError"); +like($Class::Load::ERROR, qr{^Missing right curly or square bracket at }); + +ok(is_class_loaded('Class::Load::OK')); +ok(!is_class_loaded('Class::Load::Nonexistent')); +ok(!is_class_loaded('Class::Load::SyntaxError')); + +do { + package Class::Load::Inlined; + sub inlined { 1 } +}; + +ok(try_load_class('Class::Load::Inlined'), "loaded class Inlined"); +is($Class::Load::ERROR, undef); +ok(is_class_loaded('Class::Load::Inlined')); + +ok(!try_load_class('Class::Load::VersionCheck', { -version => 43 })); +ok(try_load_class('Class::Load::VersionCheck', { -version => 41 })); + +ok(try_load_class('Class::Load::VersionCheck2', { -version => 41 })); +ok(!try_load_class('Class::Load::VersionCheck2', { -version => 43 })); + +done_testing; diff --git a/t/003-load-class.t b/t/003-load-class.t new file mode 100644 index 0000000..325b63e --- /dev/null +++ b/t/003-load-class.t @@ -0,0 +1,96 @@ +use strict; +use warnings; +use Test::More 0.88; +use lib 't/lib'; +use Test::Class::Load ':all'; +use Test::Fatal; + +is( load_class('Class::Load::OK'), 'Class::Load::OK', 'loaded class OK' ); +is( $Class::Load::ERROR, undef, 'ERROR is undef' ); + +like( + exception { + load_class('Class::Load::Nonexistent'); + }, + qr{^Can't locate Class/Load/Nonexistent.pm in \@INC}, + 'threw exception for nonexistent class' +); + +like( + $Class::Load::ERROR, + qr{^Can't locate Class/Load/Nonexistent.pm in \@INC}, + 'ERROR message for nonexistent class', +); + +ok( load_class('Class::Load::OK'), 'loaded class OK' ); +is( $Class::Load::ERROR, undef, 'ERROR is undef' ); + +like( + exception { + load_class('Class::Load::SyntaxError'); + }, + qr{^Missing right curly or square bracket at }, + 'exception contains syntax error message' +); + +like( + $Class::Load::ERROR, + qr{^Missing right curly or square bracket at }, + 'ERROR contains syntax error message' +); + +ok( is_class_loaded('Class::Load::OK') ); +ok( !is_class_loaded('Class::Load::Nonexistent') ); +ok( !is_class_loaded('Class::Load::SyntaxError') ); + +do { + + package Class::Load::Inlined; + sub inlined { 1 } +}; + +is( + load_class('Class::Load::Inlined'), + 'Class::Load::Inlined', + 'loaded class Inlined' +); +is( $Class::Load::ERROR, undef ); +ok( is_class_loaded('Class::Load::Inlined') ); + +like( + exception { + load_class( 'Class::Load::VersionCheck', { -version => 43 } ); + }, + qr/^Class::Load::VersionCheck version 43 required/, + 'got expected error for load_class with explicit version' +); + +is( + load_class( 'Class::Load::VersionCheck', { -version => 41 } ), + 'Class::Load::VersionCheck', + 'loaded class with version check' +); + +is( + load_class( 'Class::Load::VersionCheck2', { -version => 41 } ), + 'Class::Load::VersionCheck2', + 'loaded class with version check' +); + +like( + exception { + load_class( 'Class::Load::VersionCheck2', { -version => 43 } ); + }, + qr/^Class::Load::VersionCheck2 version 43 required/, + 'got expected error for load_class with explicit version (after class has been loaded into memory)' +); + +like( + exception { + load_class('__PACKAGE__'); + }, + qr/__PACKAGE__\.pm.*\@INC/, + 'errors sanely on __PACKAGE__.pm' +); + +done_testing; diff --git a/t/004-load-double.t b/t/004-load-double.t new file mode 100644 index 0000000..40204bd --- /dev/null +++ b/t/004-load-double.t @@ -0,0 +1,28 @@ +use strict; +use warnings; + +use Test::More 0.88; +use lib 't/lib'; +use Test::Class::Load ':all'; +use Test::Fatal; + +# This test does 2 things. +# Firstly, confirm that on 5.8, load_class will +# still throw an exception , even if its been loaded before: +# +# eval { require Foo; }; require Foo; # doesn't error on 5.8 +# +# Secondly, to ensure errors thrown are useful. +# ( As without the code in load_class to delete $INC{file} +# it will just die with "COMPILATION ERROR", which is +# not useful ) +# +like( exception { + load_class('Class::Load::SyntaxError'); +}, qr/syntax error/ ); + +like( exception { + load_class('Class::Load::SyntaxError'); +}, qr/syntax error/ ); + +done_testing; diff --git a/t/005-load-optional.t b/t/005-load-optional.t new file mode 100644 index 0000000..560287a --- /dev/null +++ b/t/005-load-optional.t @@ -0,0 +1,42 @@ +use strict; +use warnings; + +use Test::More 0.88; +use Test::Fatal; +use lib 't/lib'; +use Test::Class::Load qw( :all ); + +is( + exception { + load_optional_class('Class::Load::OK'); + }, + undef, + 'No failure loading a good class' +); + +is( + exception { + load_optional_class('Class::Load::IDONOTEXIST'); + }, + undef, + 'No failure loading a missing class' +); + +isnt( + exception { + load_optional_class('Class::Load::SyntaxError'); + }, + undef, + 'Loading a broken class breaks' +); + +is( load_optional_class('Class::Load::OK'), 1 , 'Existing Class => 1'); +is( load_optional_class('Class::Load::IDONOTEXIST'), 0, 'Missing Class => 0'); + +is( load_optional_class('Class::Load::VersionCheck'), 1, 'VersionCheck => 1'); +is( load_optional_class('Class::Load::VersionCheck', {-version => 43}), 0, + 'VersionCheck (with too-high version) => 0'); +is( load_optional_class('Class::Load::VersionCheck', {-version => 41}), 1, + 'VersionCheck (with ok version) => 1'); + +done_testing; diff --git a/t/006-returned-error.t b/t/006-returned-error.t new file mode 100644 index 0000000..9601517 --- /dev/null +++ b/t/006-returned-error.t @@ -0,0 +1,48 @@ +use strict; +use warnings; +use Test::More 0.88; +use lib 't/lib'; +use Test::Class::Load ':all'; + +{ + ok(try_load_class('Class::Load::OK'), "loaded class OK"); + my ($r, $e) = try_load_class('Class::Load::OK'); + is($e, undef); +} + +{ + ok(!try_load_class('Class::Load::Nonexistent'), "didn't load class Nonexistent"); + my ($r, $e) = try_load_class('Class::Load::Nonexistent'); + like($e, qr{^Can't locate Class/Load/Nonexistent.pm in \@INC}); +} + +{ + ok(try_load_class('Class::Load::OK'), "loaded class OK"); + my ($r, $e) = try_load_class('Class::Load::OK'); + is($e, undef); +} + +{ + ok(!try_load_class('Class::Load::SyntaxError'), "didn't load class SyntaxError"); + delete $INC{'Class/Load/SyntaxError.pm'}; + my ($r, $e) = try_load_class('Class::Load::SyntaxError'); + like($e, qr{^Missing right curly or square bracket at }); +} + +ok(is_class_loaded('Class::Load::OK')); +ok(!is_class_loaded('Class::Load::Nonexistent')); +ok(!is_class_loaded('Class::Load::SyntaxError')); + +{ + $@ = "foo"; + ok(try_load_class('Class::Load::OK'), "loaded class OK"); + is($@, "foo"); +} + +{ + $@ = "foo"; + ok(!try_load_class('Class::Load::Nonexistent'), "didn't load class Nonexistent"); + is($@, "foo"); +} + +done_testing; diff --git a/t/007-first-existing.t b/t/007-first-existing.t new file mode 100644 index 0000000..9332369 --- /dev/null +++ b/t/007-first-existing.t @@ -0,0 +1,123 @@ +use strict; +use warnings; +use Test::Fatal; +use Test::More 0.88; +use lib 't/lib'; +use Test::Class::Load 'load_first_existing_class'; + +is( + load_first_existing_class( + 'Class::Load::Nonexistent', 'Class::Load::OK' + ), + 'Class::Load::OK', + 'load_first_existing_class ignore nonexistent class' +); + +is( + load_first_existing_class( + 'Class::Load::Nonexistent', 'Class::Load::OK' + ), + 'Class::Load::OK', + 'load_first_existing_class ignore nonexistent class - works when good class is already loaded' +); + +like( + exception { + load_first_existing_class( 'Foo', 'bad name' ); + }, + qr/^\Q`bad name' is not a module name/, + 'load_first_existing_class balks on bad class name' +); + +like( + exception { + load_first_existing_class( 'Class::Load::Nonexistent', 'Class::Load::Nonexistent2' ); + }, + qr/^\QCan't locate Class::Load::Nonexistent or Class::Load::Nonexistent2 in \E\@INC/, + 'load_first_existing_class throws an error when no classes can be loaded' +); + +like( + exception { + load_first_existing_class( + 'Class::Load::Nonexistent', + 'Class::Load::Nonexistent2', + 'Class::Load::Nonexistent3' + ); + }, + qr/^\QCan't locate Class::Load::Nonexistent, Class::Load::Nonexistent2, or Class::Load::Nonexistent3 in \E\@INC/, + 'load_first_existing_class throws an error when no classes can be loaded' +); + +like( + exception { + load_first_existing_class( 'Class::Load::Nonexistent' ); + }, + qr/^\QCan't locate Class::Load::Nonexistent in \E\@INC/, + 'load_first_existing_class throws an error when given one class which it cannot load' +); + +like( + exception { + load_first_existing_class( + 'Class::Load::VersionCheck', { -version => 43 }, + 'Class::Load::VersionCheck2', { -version => 43 }, + ); + }, + qr/^\QCan't locate Class::Load::VersionCheck (version >= 43) or Class::Load::VersionCheck2 (version >= 43) in \E\@INC/, + 'load_first_existing_class throws an error when given multiple classes which it cannot load because of version checks' +); + +like( + exception { + load_first_existing_class( + 'Class::Load::VersionCheck', { -version => 43 }, + 'Class::Load::VersionCheck2', { -version => 43 }, + 'Class::Load::Nonexistent' + ); + }, + qr/^\QCan't locate Class::Load::VersionCheck (version >= 43), Class::Load::VersionCheck2 (version >= 43), or Class::Load::Nonexistent in \E\@INC/, + 'load_first_existing_class throws an error when given multiple classes which it cannot load, some because of version checks' +); + +like( + exception { + load_first_existing_class( 'Class::Load::VersionCheck', {-version => 43} ); + }, + qr/^\QCan't locate Class::Load::VersionCheck (version >= 43) in \E\@INC/, + 'load_first_existing_class throws an error when given one class which it cannot load because of version checks' +); + +like( + exception { + load_first_existing_class( + 'Class::Load::VersionCheck2', { -version => 43 }, + 'Class::Load::SyntaxError', { -version => 43 }, + 'Class::Load::Nonexistent' + ); + }, + qr/^\QCouldn't load class (Class::Load::SyntaxError) because: Missing right curly or square bracket/, + 'load_first_existing_class throws an error when a class fails to load because of a syntax error' +); + +is( + load_first_existing_class( + 'Class::Load::VersionCheck', { -version => 43 }, + 'Class::Load::VersionCheck2', { -version => 43 }, + 'Class::Load::OK' + ), + 'Class::Load::OK', + 'load_first_existing_class returns loadable class when two classes fail version checks' +); + +is( + load_first_existing_class( + 'Class::Load::VersionCheck', { -version => 43 }, + 'Class::Load::VersionCheck2', { -version => 41 }, + 'Class::Load::OK' + ), + 'Class::Load::VersionCheck2', + 'load_first_existing_class returns loadable class when a class passes the version check' +); + +done_testing; diff --git a/t/008-gvstash-bug.t b/t/008-gvstash-bug.t new file mode 100644 index 0000000..fac7a7d --- /dev/null +++ b/t/008-gvstash-bug.t @@ -0,0 +1,23 @@ +use strict; +use warnings; +use Test::Fatal; +use Test::More 0.88; +use lib 't/lib'; +use Test::Class::Load 'load_class'; + +is( exception { + load_class('Class::Load::Stash::Sub'); +}, undef, 'Loaded Class::Load::Stash::Sub' ); + +Class::Load::Stash->can('a_method'); + +is( exception { + load_class('Class::Load::Stash'); +}, undef, 'Loaded Class::Load::Stash' ); + +is( exception { + Class::Load::Stash->a_method; +}, undef, +'Actually loaded Class::Load::Stash - we were not fooled by mention of this stash in Class::Load::Stash::Sub' ); + +done_testing; diff --git a/t/009-invalid-module-name.t b/t/009-invalid-module-name.t new file mode 100644 index 0000000..6f9babc --- /dev/null +++ b/t/009-invalid-module-name.t @@ -0,0 +1,25 @@ +use strict; +use warnings; +use Test::Fatal; +use Test::More 0.88; +use lib 't/lib'; +use Test::Class::Load 'load_class'; + +my @bad = qw( + Foo:Bar + 123 + Foo::..::..::tmp::bad.pl + ::..::tmp::bad + ''tmp + 'tmp +); + +for my $name (@bad) { + like( + exception { load_class($name) }, + qr/^\Q`$name' is not a module name/, + "invalid module name - $name" + ); +} + +done_testing; diff --git a/t/010-isa-false-positive.t b/t/010-isa-false-positive.t new file mode 100644 index 0000000..7136fb6 --- /dev/null +++ b/t/010-isa-false-positive.t @@ -0,0 +1,67 @@ +use strict; +use warnings; +use Test::More 0.88; +use Test::Fatal; + +use lib 't/lib'; +use Test::Class::Load 'load_optional_class'; + +isnt( + exception { + load_optional_class('Class::Load::Error::DieAfterIsa'); + }, + undef, + 'Class which calls die is reported as an error' +); + +{ + local $TODO + = q{I'm not sure this is fixable as it's really an interpreter issue.}; + + isnt( + exception { + load_optional_class('Class::Load::Error::DieAfterIsa'); + }, + undef, + 'Class which calls die is reported as an error (second attempt)' + ); +} + +isnt( + exception { + load_optional_class('Class::Load::Error::DieAfterBeginIsa'); + }, + undef, + 'Class populates @ISA in BEGIN then dies - error on load' +); + +{ + local $TODO + = q{I'm not sure this is fixable as it's really an interpreter issue.}; + + isnt( + exception { + load_optional_class('Class::Load::Error::DieAfterBeginIsa'); + }, + undef, + 'Class populates @ISA in BEGIN then dies - error on load (second attempt)' + ); +} + +isnt( + exception { + load_optional_class('Class::Load::Error::SyntaxErrorAfterIsa'); + }, + undef, + 'Class with a syntax error causes an error' +); + +isnt( + exception { + load_optional_class('Class::Load::Error::SyntaxErrorAfterIsa'); + }, + undef, + 'Class with a syntax error causes an error (second attempt)' +); + +done_testing; diff --git a/t/011-without-xs.t b/t/011-without-xs.t new file mode 100644 index 0000000..e1d1e43 --- /dev/null +++ b/t/011-without-xs.t @@ -0,0 +1,22 @@ +use strict; +use warnings; +use Test::More 0.88; + +use Test::Requires { + 'Test::Without::Module' => 0, +}; + +use Test::Without::Module 'Class::Load::XS'; + +{ + my @warnings; + local $SIG{__WARN__} = sub { push @warnings, @_ }; + require Class::Load; + + is_deeply( + \@warnings, [], + 'no warning from Class::Load when Class::Load::XS is not available' + ); +} + +done_testing(); diff --git a/t/012-without-implementation.t b/t/012-without-implementation.t new file mode 100644 index 0000000..9f47c52 --- /dev/null +++ b/t/012-without-implementation.t @@ -0,0 +1,20 @@ +use strict; +use warnings; +use Test::More 0.88; +use Test::Fatal; + +use Test::Requires { + 'Test::Without::Module' => 0, +}; + +use Test::Without::Module qw( Class::Load::PP Class::Load::XS ); + +{ + like( + exception { require Class::Load }, + qr/Class.Load.PP\.pm did not return a true value/, + 'error when loading Class::Load and no implementation is available includes errors from trying to load modules' + ); +} + +done_testing(); diff --git a/t/013-errors.t b/t/013-errors.t new file mode 100644 index 0000000..38fd4a1 --- /dev/null +++ b/t/013-errors.t @@ -0,0 +1,123 @@ +use strict; +use warnings; + +use Test::More 0.88; + +use lib 't/lib'; +use Test::Class::Load ':all'; + +my $file = __FILE__; + +{ +# line 1 + eval { load_class('Class::NonExistent') }; + my $e = $@; + + unlike( + $e, + qr/at .+Load\.pm line \d+/, + 'load_class exception does not refer to Class::Load internals' + ); + + unlike( + $e, + qr/at .+Runtime\.pm line \d+/, + 'load_class exception does not refer to Module::Runtime internals' + ); + + like( + $e, + qr/Can't locate [^\n]+ at \Q$file\E line 1/, + 'error appears from the spot that called load_class' + ); +} + +{ + my ( $ok, $e ) = try_load_class('Class::NonExistent::Take2'); + + unlike( + $e, + qr/at .+Load\.pm line \d+/, + 'try_load_class exception does not refer to Class::Load internals' + ); + + unlike( + $e, + qr/at .+Runtime\.pm line \d+/, + 'try_load_class exception does not refer to Module::Runtime internals' + ); +} + +{ +# line 2 + eval { load_first_existing_class('Class::NonExistent::Take3') }; + my $e = $@; + + unlike( + $e, + qr/at .+Load\.pm line \d+/, + 'load_first_existing_class exception does not refer to Class::Load internals' + ); + + unlike( + $e, + qr/at .+Runtime\.pm line \d+/, + 'load_first_existing_class exception does not refer to Module::Runtime internals' + ); + + like( + $e, + qr/Can't locate [^\n]+ at \Q$file\E line 2/, + 'error appears from the spot that called load_first_existing_class' + ); +} + +{ +# line 3 + eval { load_first_existing_class('Class::Load::SyntaxError') }; + my $e = $@; + + unlike( + $e, + qr/at .+Load\.pm line \d+/, + 'load_first_existing_class exception does not refer to Class::Load internals' + ); + + unlike( + $e, + qr/at .+Runtime\.pm line \d+/, + 'load_first_existing_class exception does not refer to Module::Runtime internals' + ); + + like( + $e, + qr/Compilation failed .+? at \Q$file\E line 3/s, + 'error appears from the spot that called load_first_existing_class' + ); +} + +{ +# line 4 + eval { load_optional_class('Class::Load::SyntaxError') }; + my $e = $@; + + unlike( + $e, + qr/at .+Load\.pm line \d+/, + 'load_optional_class exception does not refer to Class::Load internals' + ); + + unlike( + $e, + qr/at .+Runtime\.pm line \d+/, + 'load_optional_class exception does not refer to Module::Runtime internals' + ); + + like( + $e, + qr/Compilation failed .+? at \Q$file\E line 4/s, + 'error appears from the spot that called load_optional_class' + ); +} + +done_testing(); diff --git a/t/014-weird-constants.t b/t/014-weird-constants.t new file mode 100644 index 0000000..d3ad760 --- /dev/null +++ b/t/014-weird-constants.t @@ -0,0 +1,34 @@ +use strict; +use warnings; + +use Test::Fatal; +use Test::More 0.88; + +use lib 't/lib'; +use Test::Class::Load ':all'; + +{ + package ConstantISA; + + use constant ISA => 1; +} + +is( + exception { is_class_loaded('ConstantISA') }, + undef, + 'no error checking whether class with ISA constant is loaded' +); + +{ + package ConstantVERSION; + + use constant VERSION => 1; +} + +is( + exception { is_class_loaded('ConstantVERSION') }, + undef, + 'no error checking whether class with VERSION constant is loaded' +); + +done_testing(); diff --git a/t/lib/Class/Load/Error/DieAfterBeginIsa.pm b/t/lib/Class/Load/Error/DieAfterBeginIsa.pm new file mode 100644 index 0000000..5ef3384 --- /dev/null +++ b/t/lib/Class/Load/Error/DieAfterBeginIsa.pm @@ -0,0 +1,11 @@ +package Class::Load::Error::DieAfterBeginIsa; + +use strict; +use warnings; + +BEGIN { + our @ISA = qw( UNIVERSAL ); +} + +die "Not a syntax error"; + diff --git a/t/lib/Class/Load/Error/DieAfterIsa.pm b/t/lib/Class/Load/Error/DieAfterIsa.pm new file mode 100644 index 0000000..0a021cd --- /dev/null +++ b/t/lib/Class/Load/Error/DieAfterIsa.pm @@ -0,0 +1,14 @@ +package Class::Load::Error::DieAfterIsa; + +use strict; +use warnings; + +# This library emulates a bug that can occur under App::Cmd, +# +# A broken library use's another library ( App::Cmd::Setup ), and that library +# injects @ISA during import->() + +our @ISA = qw( UNIVERSAL ); + +die "Not a syntax error"; + diff --git a/t/lib/Class/Load/Error/SyntaxErrorAfterIsa.pm b/t/lib/Class/Load/Error/SyntaxErrorAfterIsa.pm new file mode 100644 index 0000000..a0ae426 --- /dev/null +++ b/t/lib/Class/Load/Error/SyntaxErrorAfterIsa.pm @@ -0,0 +1,11 @@ +package Class::Load::Error::SyntaxErrorAfterIsa; +use strict; +use warnings; + +# This library emulates a bug that can occur under App::Cmd, +# +# A Broken library "use"'s annother library ( App::Cmd::Setup ), and +# that library injects @ISA during import->() +our @ISA = qw( UNIVERSAL ); +#} +sub { diff --git a/t/lib/Class/Load/OK.pm b/t/lib/Class/Load/OK.pm new file mode 100644 index 0000000..230f9c9 --- /dev/null +++ b/t/lib/Class/Load/OK.pm @@ -0,0 +1,9 @@ +#!/usr/bin/env perl +package Class::Load::OK; +use strict; +use warnings; + +sub ok { 1 } + +1; + diff --git a/t/lib/Class/Load/Stash.pm b/t/lib/Class/Load/Stash.pm new file mode 100644 index 0000000..22f7280 --- /dev/null +++ b/t/lib/Class/Load/Stash.pm @@ -0,0 +1,7 @@ +package Class::Load::Stash; +use strict; +use warnings; + +sub a_method { 'a_method' } + +1; diff --git a/t/lib/Class/Load/Stash/Sub.pm b/t/lib/Class/Load/Stash/Sub.pm new file mode 100644 index 0000000..95cfdf2 --- /dev/null +++ b/t/lib/Class/Load/Stash/Sub.pm @@ -0,0 +1,7 @@ +package Class::Load::Stash::Sub; +use strict; +use warnings; + +sub ver_test { return "Class::Load::Stash ver $Class::Load::Stash::VERSION" } + +1; diff --git a/t/lib/Class/Load/SyntaxError.pm b/t/lib/Class/Load/SyntaxError.pm new file mode 100644 index 0000000..54c3ab6 --- /dev/null +++ b/t/lib/Class/Load/SyntaxError.pm @@ -0,0 +1,7 @@ +#!/usr/bin/env perl +package Class::Load::SyntaxError; +use strict; +use warnings; + +sub { + diff --git a/t/lib/Class/Load/VersionCheck.pm b/t/lib/Class/Load/VersionCheck.pm new file mode 100644 index 0000000..2e900f3 --- /dev/null +++ b/t/lib/Class/Load/VersionCheck.pm @@ -0,0 +1,11 @@ +#!/usr/bin/env perl +package Class::Load::VersionCheck; +use strict; +use warnings; + +our $VERSION = 42; + +sub ok { 1 } + +1; + diff --git a/t/lib/Class/Load/VersionCheck2.pm b/t/lib/Class/Load/VersionCheck2.pm new file mode 100644 index 0000000..aee87e0 --- /dev/null +++ b/t/lib/Class/Load/VersionCheck2.pm @@ -0,0 +1,11 @@ +#!/usr/bin/env perl +package Class::Load::VersionCheck2; +use strict; +use warnings; + +our $VERSION = 42; + +sub ok { 1 } + +1; + diff --git a/t/lib/Test/Class/Load.pm b/t/lib/Test/Class/Load.pm new file mode 100644 index 0000000..e87e286 --- /dev/null +++ b/t/lib/Test/Class/Load.pm @@ -0,0 +1,17 @@ +package # hide from PAUSE + Test::Class::Load; + +use strict; +use warnings; + +use Class::Load::PP; + +$ENV{CLASS_LOAD_IMPLEMENTATION} = 'PP'; + +require Class::Load; + +sub import { + Class::Load->export_to_level(1, @_); +} + +1; -- cgit v1.2.1