summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2014-11-13 06:59:49 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2014-11-13 06:59:49 +0000
commit60626c51d97a7ef06b364481ae4afd9706ef6ece (patch)
tree29f14af72f207da0e57c7ac944a2ab3faddff296 /t
downloadClass-Load-XS-tarball-master.tar.gz
Diffstat (limited to 't')
-rw-r--r--t/00-report-prereqs.dd131
-rw-r--r--t/00-report-prereqs.t176
-rw-r--r--t/000-load.t15
-rw-r--r--t/001-is-class-loaded.t108
-rw-r--r--t/002-try-load-class.t38
-rw-r--r--t/003-load-class.t96
-rw-r--r--t/004-load-double.t28
-rw-r--r--t/005-load-optional.t42
-rw-r--r--t/006-returned-error.t48
-rw-r--r--t/007-first-existing.t123
-rw-r--r--t/008-gvstash-bug.t23
-rw-r--r--t/009-invalid-module-name.t25
-rw-r--r--t/010-isa-false-positive.t67
-rw-r--r--t/011-without-xs.t23
-rw-r--r--t/012-without-implementation.t20
-rw-r--r--t/013-errors.t123
-rw-r--r--t/014-weird-constants.t34
-rw-r--r--t/lib/Class/Load/Error/DieAfterBeginIsa.pm11
-rw-r--r--t/lib/Class/Load/Error/DieAfterIsa.pm14
-rw-r--r--t/lib/Class/Load/Error/SyntaxErrorAfterIsa.pm11
-rw-r--r--t/lib/Class/Load/OK.pm9
-rw-r--r--t/lib/Class/Load/Stash.pm7
-rw-r--r--t/lib/Class/Load/Stash/Sub.pm7
-rw-r--r--t/lib/Class/Load/SyntaxError.pm7
-rw-r--r--t/lib/Class/Load/VersionCheck.pm11
-rw-r--r--t/lib/Class/Load/VersionCheck2.pm11
-rw-r--r--t/lib/Test/Class/Load.pm17
27 files changed, 1225 insertions, 0 deletions
diff --git a/t/00-report-prereqs.dd b/t/00-report-prereqs.dd
new file mode 100644
index 0000000..c623e88
--- /dev/null
+++ b/t/00-report-prereqs.dd
@@ -0,0 +1,131 @@
+do { my $x = {
+ 'configure' => {
+ 'requires' => {
+ 'ExtUtils::MakeMaker' => '0',
+ 'perl' => '5.006'
+ }
+ },
+ 'develop' => {
+ 'recommends' => {
+ 'Dist::Zilla::PluginBundle::Author::ETHER' => '0.078'
+ },
+ 'requires' => {
+ 'Devel::PPPort' => '3.23',
+ 'Dist::Zilla' => '5',
+ 'Dist::Zilla::Plugin::Authority' => '0',
+ 'Dist::Zilla::Plugin::AuthorityFromModule' => '0.002',
+ 'Dist::Zilla::Plugin::AutoMetaResources' => '0',
+ 'Dist::Zilla::Plugin::AutoPrereqs' => '0',
+ '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::ExecDir' => '0',
+ 'Dist::Zilla::Plugin::FileFinder::ByName' => '0',
+ 'Dist::Zilla::Plugin::GenerateFile::ShareDir' => '0',
+ 'Dist::Zilla::Plugin::Git::Check' => '2.025',
+ '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',
+ 'Dist::Zilla::Plugin::Git::GatherDir' => '2.016',
+ 'Dist::Zilla::Plugin::Git::NextVersion' => '0',
+ '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',
+ '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' => '0',
+ 'Dist::Zilla::Plugin::MojibakeTests' => '0',
+ 'Dist::Zilla::Plugin::NextRelease' => '4.300018',
+ 'Dist::Zilla::Plugin::PPPort' => '0',
+ 'Dist::Zilla::Plugin::PkgVersion' => '5.010',
+ 'Dist::Zilla::Plugin::PodCoverageTests' => '0',
+ '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::Run::AfterBuild' => '0.024',
+ 'Dist::Zilla::Plugin::Run::AfterRelease' => '0.024',
+ 'Dist::Zilla::Plugin::RunExtraTests' => '0.019',
+ 'Dist::Zilla::Plugin::ShareDir' => '0',
+ 'Dist::Zilla::Plugin::SurgicalPodWeaver' => '0',
+ 'Dist::Zilla::Plugin::Test::CPAN::Changes' => '0.008',
+ 'Dist::Zilla::Plugin::Test::ChangesHasContent' => '0',
+ 'Dist::Zilla::Plugin::Test::CleanNamespaces' => '0',
+ 'Dist::Zilla::Plugin::Test::Compile' => '2.039',
+ 'Dist::Zilla::Plugin::Test::EOL' => '0.14',
+ 'Dist::Zilla::Plugin::Test::Kwalitee' => '0',
+ 'Dist::Zilla::Plugin::Test::MinimumVersion' => '2.000003',
+ 'Dist::Zilla::Plugin::Test::NoTabs' => '0',
+ 'Dist::Zilla::Plugin::Test::Pod::No404s' => '0',
+ 'Dist::Zilla::Plugin::Test::PodSpelling' => '0',
+ '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.076',
+ 'File::Spec' => '0',
+ 'IO::Handle' => '0',
+ 'IPC::Open3' => '0',
+ 'Pod::Coverage::TrustPod' => '0',
+ 'Pod::Weaver::Section::Contributors' => '0',
+ 'Test::CPAN::Changes' => '0.19',
+ 'Test::CPAN::Meta' => '0',
+ 'Test::CleanNamespaces' => '0.15',
+ 'Test::EOL' => '0',
+ 'Test::Kwalitee' => '1.21',
+ 'Test::More' => '0.94',
+ 'Test::NoTabs' => '0',
+ 'Test::Pod' => '1.41',
+ 'Test::Pod::Coverage' => '1.08',
+ 'Test::Spelling' => '0.12',
+ 'Test::Without::Module' => '0'
+ }
+ },
+ 'runtime' => {
+ 'requires' => {
+ 'Class::Load' => '0.20',
+ 'XSLoader' => '0',
+ 'perl' => '5.006',
+ 'strict' => '0',
+ 'warnings' => '0'
+ }
+ },
+ 'test' => {
+ 'recommends' => {
+ 'CPAN::Meta' => '2.120900'
+ },
+ 'requires' => {
+ 'ExtUtils::MakeMaker' => '0',
+ 'File::Spec' => '0',
+ 'Module::Implementation' => '0.04',
+ '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..402b3d9
--- /dev/null
+++ b/t/00-report-prereqs.t
@@ -0,0 +1,176 @@
+#!perl
+
+use strict;
+use warnings;
+
+# This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.019
+
+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 );
+ 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..1711b51
--- /dev/null
+++ b/t/011-without-xs.t
@@ -0,0 +1,23 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+use Test::Fatal;
+
+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..ab2cd03
--- /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::XS;
+
+$ENV{CLASS_LOAD_IMPLEMENTATION} = 'XS';
+
+require Class::Load;
+
+sub import {
+ Class::Load->export_to_level(1, @_);
+}
+
+1;