Test-Without-Module-0.23/0000755000175000017500000000000014644737741014626 5ustar corioncorionTest-Without-Module-0.23/README0000755000175000017500000000245314644737741015515 0ustar corioncorionTest::Without::Module - Test fallback behaviour in absence of modules DESCRIPTION This module allows you to deliberately hide modules from a program even though they are installed. This is mostly useful for testing modules that have a fallback when a certain dependency module is not installed. This document describes version 0.23. INSTALLATION This is a Perl module distribution. It should be installed with whichever tool you use to manage your installation of Perl, e.g. any of cpanm . cpan . cpanp -i . Consult https://www.cpan.org/modules/INSTALL.html for further instruction. Should you wish to install this module manually, the procedure is perl Makefile.PL make make test make install REPOSITORY The public repository of this module is L. SUPPORT The public support forum of this module is L. BUG TRACKER Please report bugs in this module via the RT CPAN bug queue at L or via mail to L. SEE ALSO L, L, L, L AUTHOR Copyright (c) 2003-2024 Max Maischein, Ecorion@cpan.orgE LICENSE This module is released under the same terms as Perl itself. Test-Without-Module-0.23/t/0000755000175000017500000000000014644737741015071 5ustar corioncorionTest-Without-Module-0.23/t/01-api.t0000755000175000017500000000012314644737741016244 0ustar corioncorion#!/usr/bin/perl -w use Test::More tests => 1; use_ok( 'Test::Without::Module' ); Test-Without-Module-0.23/t/embedded-Test-Without-Module.t0000755000175000017500000000331614644737741022616 0ustar corioncorion#!D:\Programme\indigoperl-5.6\bin\perl.exe -w use Test::More 'no_plan'; package Catch; sub TIEHANDLE { my($class, $var) = @_; return bless { var => $var }, $class; } sub PRINT { my($self) = shift; ${'main::'.$self->{var}} .= join '', @_; } sub OPEN {} # XXX Hackery in case the user redirects sub CLOSE {} # XXX STDERR/STDOUT. This is not the behavior we want. sub READ {} sub READLINE {} sub GETC {} sub BINMODE {} my $Original_File = 'D:lib\Test\Without\Module.pm'; package main; # pre-5.8.0's warns aren't caught by a tied STDERR. $SIG{__WARN__} = sub { $main::_STDERR_ .= join '', @_; }; tie *STDOUT, 'Catch', '_STDOUT_' or die $!; tie *STDERR, 'Catch', '_STDERR_' or die $!; SKIP: { # A header testing whether we find all prerequisites : # Check for module My::Module eval { require My::Module }; skip "Need module My::Module to run this test", 1 if $@; # Check for module Test::Without::Module eval { require Test::Without::Module }; skip "Need module Test::Without::Module to run this test", 1 if $@; # The original POD test undef $main::_STDOUT_; undef $main::_STDERR_; eval q{ my $example = sub { local $^W = 0; #line 109 lib/Test/Without/Module.pm use Test::Without::Module qw( My::Module ); # Now, loading of My::Module fails : eval { require My::Module; }; warn $@ if $@; # Now it works again eval q{ no Test::Without::Module qw( My::Module ) }; eval { require My::Module; }; print "Found My::Module" unless $@; ; } }; is($@, '', "example from line 109"); }; SKIP: { # A header testing whether we find all prerequisites : # The original POD test undef $main::_STDOUT_; undef $main::_STDERR_; }; Test-Without-Module-0.23/t/06-missing-hidden-modules.t0000644000175000017500000000227314644737741022055 0ustar corioncorion use Test::Without::Module; use Test::More tests => 5; sub tryload { my $module = shift; my $failed = !eval "require $module; 1"; my $error = $@; $error =~ s/(\(\@INC contains: ).*\)/$1...)/; $error =~ s/\n+\z//; my $inc_status = !exists $INC{"$module.pm"} ? 'missing' : !defined $INC{"$module.pm"} ? 'undef' : !$INC{"$module.pm"} ? 'false' : '"'.$INC{"$module.pm"}.'"' ; return $failed, $error, $inc_status; } my ($failed,$error,$inc) = tryload( 'Nonexisting::Module' ); is $failed, 1, "Self-test, a non-existing module fails to load"; like $error, qr!^Can't locate Nonexisting/Module.pm in \@INC( \(you may need to install the Nonexisting::Module module\))? \(\@INC !, 'Self-test, error message shows @INC'; #diag $error; # Now, hide a module that has not been loaded: ok !$INC{'IO/Socket.pm'}, "Module 'IO/Socket.pm' has not been loaded yet"; Test::Without::Module->import('IO::Socket'); ($failed,$error,$inc) = tryload( 'IO::Socket' ); is $failed, 1, "a non-existing module fails to load"; like $error, qr!Can't locate IO/Socket.pm in \@INC( \(you may need to install the IO::Socket module\))? \(\@INC !, 'error message for hidden module shows @INC'; #diag $error; Test-Without-Module-0.23/t/02-block-use-module.t0000755000175000017500000000064414644737741020653 0ustar corioncorion#!/usr/bin/perl -w use strict; use Test::More tests => 4; BEGIN{ use_ok( "Test::Without::Module", qw( Digest::MD5 )); }; is_deeply( [sort keys %{Test::Without::Module::get_forbidden_list()}],[ qw[ Digest/MD5.pm ]],"Module list" ); eval q{ use Digest::MD5 }; ok( $@ ne '', 'Importing raises an error' ); like( $@, qr!^(Can't locate Digest/MD5.pm in \@INC|Digest/MD5.pm did not return a true value at)!, "Hid module"); Test-Without-Module-0.23/t/03-block-require-module.t0000755000175000017500000000150414644737741021530 0ustar corioncorion#!/usr/bin/perl -w use strict; use Symbol qw( delete_package ); use Test::More tests => 6; BEGIN { use_ok( "Test::Without::Module", qw( Digest::MD5 )); }; { use Test::Without::Module qw( Digest::MD5 ); eval { require Digest::MD5 }; use Test::Without::Module qw( Digest::MD5 ); ok( $@ ne '', "Loading raised error"); like( $@, qr!^(Can't locate Digest/MD5.pm in \@INC|Digest/MD5.pm did not return a true value at)!, "Hid module"); is_deeply( [sort keys %{Test::Without::Module::get_forbidden_list()}],[ qw[ Digest/MD5.pm ]],"Module list" ); delete_package( 'Digest::MD5' ); }; TODO: { local $TODO = 'Implement lexical scoping'; eval { require 'Digest::MD5' }; is( $@, '', "Local (require) confinement"); delete_package( 'Digest::MD5' ); eval q{ use Digest::MD5 }; is( $@, '', "Local (use) confinement"); }; Test-Without-Module-0.23/t/rt122551.t0000644000175000017500000000135414644737741016366 0ustar corioncorion#!/usr/bin/perl -w use strict; use Test::More tests => 2; BEGIN { if( ! eval { require Module::Load::Conditional; 1 }) { SKIP: { skip "Module::Load::Conditional not installed: $@", 2; }; }; }; use Test::Without::Module qw(Test::More); local $TODO = 'Module::Load::Conditional doesn\'t guard against failures in @INC hook'; my $res; my $lived = eval { $res = Module::Load::Conditional::can_load( modules => { 'Test::More' => undef, } ); 1; }; ok $lived or diag "Caught error $@"; ok !$res, "We don't load Test::More"; diag "Test::Without::Module: $Test::Without::Module::VERSION"; diag "Module::Load::Conditional: $Module::Load::Conditional::VERSION"; done_testing; Test-Without-Module-0.23/t/05-redefine.t0000644000175000017500000000076714644737741017273 0ustar corioncorion#!/usr/bin/perl -w use strict; use Test::More tests => 1; use File::Find; my @warnings; BEGIN { $SIG{__WARN__} = sub { push @warnings, @_; }; }; use Data::Dumper; #BEGIN { diag $INC{"File/Find.pm"}; }; use Test::Without::Module qw(File::Find); #BEGIN { diag $INC{"File/Find.pm"}; }; no Test::Without::Module qw(File::Find); #diag $INC{"File/Find.pm"}; require File::Find; # diag Dumper \%INC; is_deeply "@warnings", "", "No warnings were issued upon re-allowing a module"; __END__ Test-Without-Module-0.23/t/00-load.t0000644000175000017500000000057214644737741016416 0ustar corioncorion#!perl use strict; use warnings; use Test::More tests => 1; require './Makefile.PL'; my %module = get_module_info(); my $module = $module{ NAME }; require_ok( $module ); diag( sprintf "Testing %s %s, Perl %s", $module, $module->VERSION, $] ); for (sort grep /\.pm\z/, keys %INC) { s/\.pm\z//; s!/!::!g; eval { diag(join(' ', $_, $_->VERSION || '')) }; } Test-Without-Module-0.23/t/04-import-export.t0000755000175000017500000000051314644737741020332 0ustar corioncorion#!/usr/bin/perl -w use Test::More tests => 3; use_ok( 'Test::Without::Module' ); use Test::Without::Module qw( File::Temp ); no Test::Without::Module qw( File::Temp ); is_deeply( [keys %{Test::Without::Module::get_forbidden_list()}],[],"Module list is empty" ); eval { $^W = 0; require File::Temp; }; is( $@, '', "unimport" ); Test-Without-Module-0.23/Makefile.PL0000755000175000017500000001620514644737741016607 0ustar corioncorion# -*- mode: perl; c-basic-offset: 4; indent-tabs-mode: nil; -*- use strict; use ExtUtils::MakeMaker qw(WriteMakefile); # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. # Normalize version strings like 6.30_02 to 6.3002, # so that we can do numerical comparisons on it. my $eumm_version = $ExtUtils::MakeMaker::VERSION; $eumm_version =~ s/_//; my $module = 'Test::Without::Module'; (my $main_file = "lib/$module.pm" ) =~ s!::!/!g; (my $distbase = $module) =~ s!::!-!g; my $distlink = lc $distbase; my @tests = map { glob $_ } 't/*.t', 't/*/*.t'; my %module = ( NAME => $module, AUTHOR => q{Max Maischein }, VERSION_FROM => $main_file, ABSTRACT_FROM => $main_file, META_MERGE => { "meta-spec" => { version => 2 }, resources => { repository => { web => "https://github.com/Corion/$distlink", url => "git://github.com/Corion/$distlink.git", type => 'git', }, bugtracker => { web => "https://github.com/Corion/$distbase/issues", # mailto => 'meta-bugs@example.com', }, license => "https://dev.perl.org/licenses/", }, dynamic_config => 0, # we promise to keep META.* up-to-date x_static_install => 1, # we are pure Perl and don't do anything fancy }, MIN_PERL_VERSION => '5.006', 'LICENSE'=> 'artistic_2', PL_FILES => {}, BUILD_REQUIRES => { 'File::Find' => 0, 'File::Spec' => 0, 'ExtUtils::MakeMaker' => 0, }, PREREQ_PM => { 'Carp' => 0, }, TEST_REQUIRES => { 'Test::More' => 0, }, dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, clean => { FILES => "$distbase-*" }, test => { TESTS => join( ' ', @tests ) }, ); # This is so that we can do # require 'Makefile.PL' # and then call get_module_info sub get_module_info { %module } if( ! caller ) { # I should maybe use something like Shipwright... my $mm = WriteMakefile1(get_module_info); my $version = $mm->parse_version($main_file); regen_README($main_file, $version); regen_EXAMPLES() if -d 'examples'; }; 1; sub WriteMakefile1 { #Written by Alexandr Ciornii, version 0.21. Added by eumm-upgrade. my %params=@_; my $eumm_version=$ExtUtils::MakeMaker::VERSION; $eumm_version=eval $eumm_version; die "EXTRA_META is deprecated" if exists $params{EXTRA_META}; die "License not specified" if not exists $params{LICENSE}; if ($params{BUILD_REQUIRES} and $eumm_version < 6.5503) { #EUMM 6.5502 has problems with BUILD_REQUIRES $params{PREREQ_PM}={ %{$params{PREREQ_PM} || {}} , %{$params{BUILD_REQUIRES}} }; delete $params{BUILD_REQUIRES}; } if ($params{TEST_REQUIRES} and $eumm_version < 6.64) { $params{PREREQ_PM}={ %{$params{PREREQ_PM} || {}} , %{$params{TEST_REQUIRES}} }; delete $params{TEST_REQUIRES}; } delete $params{CONFIGURE_REQUIRES} if $eumm_version < 6.52; delete $params{MIN_PERL_VERSION} if $eumm_version < 6.48; delete $params{META_MERGE} if $eumm_version < 6.46; delete $params{META_ADD} if $eumm_version < 6.46; delete $params{LICENSE} if $eumm_version < 6.31; delete $params{AUTHOR} if $] < 5.005; delete $params{ABSTRACT_FROM} if $] < 5.005; delete $params{BINARY_LOCATION} if $] < 5.005; WriteMakefile(%params); } sub regen_README { # README is the short version that just tells people what this is # and how to install it my( $file, $version ) = @_; eval { # Get description my $readme = join "\n", pod_section($file, 'NAME', 'no heading' ), pod_section($file, 'DESCRIPTION' ), <new(); # Read POD from Module.pm and write to README $parser->parse_from_file($_[0]); my $readme_mkdn = <as_markdown; [![Windows](https://github.com/Corion/$distbase/workflows/windows/badge.svg)](https://github.com/Corion/$distbase/actions?query=workflow%3Awindows) [![MacOS](https://github.com/Corion/$distbase/workflows/macos/badge.svg)](https://github.com/Corion/$distbase/actions?query=workflow%3Amacos) [![Linux](https://github.com/Corion/$distbase/workflows/linux/badge.svg)](https://github.com/Corion/$distbase/actions?query=workflow%3Alinux) STATUS update_file( 'README.mkdn', $readme_mkdn ); }; } sub pod_section { my( $filename, $section, $remove_heading ) = @_; open my $fh, '<', $filename or die "Couldn't read '$filename': $!"; my @section = grep { /^=head1\s+$section/.../^=/ } <$fh>; # Trim the section if( @section ) { pop @section if $section[-1] =~ /^=/; shift @section if $remove_heading; pop @section while @section and $section[-1] =~ /^\s*$/; shift @section while @section and $section[0] =~ /^\s*$/; }; @section = map { $_ =~ s!^=\w+\s+!!; $_ } @section; return join "", @section; } sub regen_EXAMPLES { my $perl = $^X; if ($perl =~/\s/) { $perl = qq{"$perl"}; }; (my $example_file = $main_file) =~ s!\.pm$!/Examples.pm!; my $examples = `$perl -w examples/gen_examples_pod.pl`; if ($examples) { warn "(Re)Creating $example_file\n"; $examples =~ s/\r\n/\n/g; update_file( $example_file, $examples ); }; }; sub update_file { my( $filename, $new_content ) = @_; my $content; if( -f $filename ) { open my $fh, '<:raw:encoding(UTF-8)', $filename or die "Couldn't read '$filename': $!"; local $/; $content = <$fh>; }; if( $content ne $new_content ) { if( open my $fh, '>:raw:encoding(UTF-8)', $filename ) { print $fh $new_content; } else { warn "Couldn't (re)write '$filename': $!"; }; }; } Test-Without-Module-0.23/META.json0000644000175000017500000000302114644737741016243 0ustar corioncorion{ "abstract" : "Test fallback behaviour in absence of modules", "author" : [ "Max Maischein " ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010", "license" : [ "artistic_2" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Test-Without-Module", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0", "File::Find" : "0", "File::Spec" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Carp" : "0", "perl" : "5.006" } }, "test" : { "requires" : { "Test::More" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/Corion/Test-Without-Module/issues" }, "license" : [ "https://dev.perl.org/licenses/" ], "repository" : { "type" : "git", "url" : "git://github.com/Corion/test-without-module.git", "web" : "https://github.com/Corion/test-without-module" } }, "version" : "0.23", "x_serialization_backend" : "JSON::PP version 4.07", "x_static_install" : 1 } Test-Without-Module-0.23/lib/0000755000175000017500000000000014644737741015374 5ustar corioncorionTest-Without-Module-0.23/lib/Test/0000755000175000017500000000000014644737741016313 5ustar corioncorionTest-Without-Module-0.23/lib/Test/Without/0000755000175000017500000000000014644737741017756 5ustar corioncorionTest-Without-Module-0.23/lib/Test/Without/Module.pm0000755000175000017500000001016114644737741021543 0ustar corioncorionpackage Test::Without::Module; use strict; use Carp qw( croak ); use vars qw( $VERSION ); $VERSION = '0.23'; use vars qw(%forbidden); sub get_forbidden_list { \%forbidden }; sub import { my ($self,@forbidden_modules) = @_; my $forbidden = get_forbidden_list; for (@forbidden_modules) { my $file = module2file($_); $forbidden->{$file} = delete $INC{$file}; }; # Scrub %INC, so that loaded modules disappear for my $module (@forbidden_modules) { scrub( $module ); }; @INC = (\&fake_module, grep { !ref || $_ != \&fake_module } @INC); }; sub fake_module { my ($self,$module_file,$member_only) = @_; # Don't touch $@, or .al files will not load anymore???? if (exists get_forbidden_list->{$module_file}) { my $module_name = file2module($module_file); croak "Can't locate $module_file in \@INC (you may need to install the $module_name module) (\@INC contains: @INC)"; }; }; sub unimport { my ($self,@list) = @_; my $module; my $forbidden = get_forbidden_list; for $module (@list) { my $file = module2file($module); if (exists $forbidden->{$file}) { my $path = delete $forbidden->{$file}; if (defined $path) { $INC{ $file } = $path; } } else { croak "Can't allow non-forbidden module $module"; }; }; }; sub file2module { my ($mod) = @_; $mod =~ s!/!::!g; $mod =~ s!\.pm$!!; $mod; }; sub module2file { my ($mod) = @_; $mod =~ s!::|'!/!g; $mod .= ".pm"; $mod; }; sub scrub { my ($module) = @_; delete $INC{module2file($module)}; }; 1; =head1 NAME Test::Without::Module - Test fallback behaviour in absence of modules =head1 SYNOPSIS use Test::Without::Module qw( My::Module ); # Now, loading of My::Module fails : eval { require My::Module; }; warn $@ if $@; # Now it works again eval q{ no Test::Without::Module qw( My::Module ) }; eval { require My::Module; }; print "Found My::Module" unless $@; =head1 DESCRIPTION This module allows you to deliberately hide modules from a program even though they are installed. This is mostly useful for testing modules that have a fallback when a certain dependency module is not installed. =head2 EXPORT None. All magic is done via C and C. =head2 Test::Without::Module::get_forbidden_list This function returns a reference to a copy of the current hash of forbidden modules or an empty hash if none are currently forbidden. This is convenient if you are testing and/or debugging this module. =cut =head1 ONE LINER A neat trick for using this module from the command line was mentioned to me by NUFFIN and by Jerrad Pierce: perl -MTest::Without::Module=Some::Module -w -Iblib/lib t/SomeModule.t That way, you can easily see how your module or test file behaves when a certain module is unavailable. =head1 BUGS =over 4 =item * There is no lexical scoping =back =head1 CREDITS Much improvement must be thanked to Aristotle from PerlMonks, he pointed me to a much less convoluted way to fake a module at L. I also discussed with him an even more elegant way of overriding CORE::GLOBAL::require, but the parsing of the overridden subroutine didn't work out the way I wanted it - CORE::require didn't recognize barewords as such anymore. NUFFIN and Jerrad Pierce pointed out the convenient use from the command line to interactively watch the behaviour of the test suite and module in absence of a module. =head1 AUTHOR Copyright (c) 2003-2024 Max Maischein, Ecorion@cpan.orgE =head1 LICENSE This module is released under the same terms as Perl itself. =head1 REPOSITORY The public repository of this module is L. =head1 SUPPORT The public support forum of this module is L. =head1 BUG TRACKER Please report bugs in this module via the RT CPAN bug queue at L or via mail to L. =head1 SEE ALSO L, L, L, L =cut __END__ Test-Without-Module-0.23/testrules.yml0000644000175000017500000000012114644737741017375 0ustar corioncorion--- # This test suite can be run fully in parallel par: - t/*.t - xt/*.t Test-Without-Module-0.23/MANIFEST0000644000175000017500000000100314644737741015751 0ustar corioncorionChanges lib/Test/Without/Module.pm LICENSE Makefile.PL MANIFEST MANIFEST.SKIP META.json META.yml README README.mkdn t/00-load.t t/01-api.t t/02-block-use-module.t t/03-block-require-module.t t/04-import-export.t t/05-redefine.t t/06-missing-hidden-modules.t t/embedded-Test-Without-Module.t t/rt122551.t testrules.yml Texts/article.txt xt/99-changes.t xt/99-compile.t xt/99-manifest.t xt/99-minimumversion.t xt/99-pod.t xt/99-synopsis.t xt/99-todo.t xt/99-unix-text.t xt/99-versions.t xt/copyright.t xt/meta-lint.t Test-Without-Module-0.23/README.mkdn0000644000175000017500000000646714644737741016453 0ustar corioncorion [![Windows](https://github.com/Corion/Test-Without-Module/workflows/windows/badge.svg)](https://github.com/Corion/Test-Without-Module/actions?query=workflow%3Awindows) [![MacOS](https://github.com/Corion/Test-Without-Module/workflows/macos/badge.svg)](https://github.com/Corion/Test-Without-Module/actions?query=workflow%3Amacos) [![Linux](https://github.com/Corion/Test-Without-Module/workflows/linux/badge.svg)](https://github.com/Corion/Test-Without-Module/actions?query=workflow%3Alinux) # NAME Test::Without::Module - Test fallback behaviour in absence of modules # SYNOPSIS use Test::Without::Module qw( My::Module ); # Now, loading of My::Module fails : eval { require My::Module; }; warn $@ if $@; # Now it works again eval q{ no Test::Without::Module qw( My::Module ) }; eval { require My::Module; }; print "Found My::Module" unless $@; # DESCRIPTION This module allows you to deliberately hide modules from a program even though they are installed. This is mostly useful for testing modules that have a fallback when a certain dependency module is not installed. ## EXPORT None. All magic is done via `use Test::Without::Module LIST` and `no Test::Without::Module LIST`. ## Test::Without::Module::get\_forbidden\_list This function returns a reference to a copy of the current hash of forbidden modules or an empty hash if none are currently forbidden. This is convenient if you are testing and/or debugging this module. # ONE LINER A neat trick for using this module from the command line was mentioned to me by NUFFIN and by Jerrad Pierce: perl -MTest::Without::Module=Some::Module -w -Iblib/lib t/SomeModule.t That way, you can easily see how your module or test file behaves when a certain module is unavailable. # BUGS - There is no lexical scoping # CREDITS Much improvement must be thanked to Aristotle from PerlMonks, he pointed me to a much less convoluted way to fake a module at [https://perlmonks.org?node=192635](https://perlmonks.org?node=192635). I also discussed with him an even more elegant way of overriding CORE::GLOBAL::require, but the parsing of the overridden subroutine didn't work out the way I wanted it - CORE::require didn't recognize barewords as such anymore. NUFFIN and Jerrad Pierce pointed out the convenient use from the command line to interactively watch the behaviour of the test suite and module in absence of a module. # AUTHOR Copyright (c) 2003-2024 Max Maischein, # LICENSE This module is released under the same terms as Perl itself. # REPOSITORY The public repository of this module is [https://github.com/Corion/test-without-module](https://github.com/Corion/test-without-module). # SUPPORT The public support forum of this module is [https://perlmonks.org/](https://perlmonks.org/). # BUG TRACKER Please report bugs in this module via the RT CPAN bug queue at [https://rt.cpan.org/Public/Dist/Display.html?Name=Test-Without-Module](https://rt.cpan.org/Public/Dist/Display.html?Name=Test-Without-Module) or via mail to [test-without-module-Bugs@rt.cpan.org](https://metacpan.org/pod/test-without-module-Bugs%40rt.cpan.org). # SEE ALSO [Devel::Hide](https://metacpan.org/pod/Devel%3A%3AHide), [Acme::Intraweb](https://metacpan.org/pod/Acme%3A%3AIntraweb), [PAR](https://metacpan.org/pod/PAR), [perlfunc](https://metacpan.org/pod/perlfunc) Test-Without-Module-0.23/META.yml0000644000175000017500000000152314644737741016100 0ustar corioncorion--- abstract: 'Test fallback behaviour in absence of modules' author: - 'Max Maischein ' build_requires: ExtUtils::MakeMaker: '0' File::Find: '0' File::Spec: '0' Test::More: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010' license: artistic_2 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Test-Without-Module no_index: directory: - t - inc requires: Carp: '0' perl: '5.006' resources: bugtracker: https://github.com/Corion/Test-Without-Module/issues license: https://dev.perl.org/licenses/ repository: git://github.com/Corion/test-without-module.git version: '0.23' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' x_static_install: 1 Test-Without-Module-0.23/Changes0000755000175000017500000000606014644737741016126 0ustar corioncorionRevision history for Perl extension Test::Without::Module. Todo: - Add way to allow only core modules (suggested by SREZIC) 0.23 2024-07-14 - Lower minimum required Perl version back to 5.006, this addresses GH #5 0.22 2024-07-05 - Fix test suite to not skip a failing test 0.21 2022-12-10 - Harden test suite against changed error message coming with 5.38 0.20 2017-04-09 - Make tests more resilient against old versions of Carp.pm (Paul Howarth) Also addresses https://rt.cpan.org/Public/Bug/Display.html?id=121002 - Typo fixes (Paul Howarth) - Rework test suite, move author tests below xt/ 0.19 2017-03-30 - Make error message more like the original Perl error message (haarg) - Makefile.PL overhaul (by Abzal Serekov) - Generate README.md 0.18 2014-08-30 - Eliminate segfault on Perl 5.8 (by Graham Knop, Karen Etheridge, RT 98207) - Only add the @INC hook once (by Graham Knop, RT #91857) - Updated documentation 0.17 2009-01-18 - Made license explicit in the metadata (for those versions of EU:MM that support it) ! Changed module comparison from using a regular expression to string equality. That means that ugly actions at a distance by matching substrings shouldn't happen. 0.16 2008-10-20 - Add a test that exhibits the "redefined" warnings upon disallowing and then reallowing a module - Fixed the code so no more warnings get issued. [RT#40065] 0.15 2007-10-21 - Now really restored compatibility of the tests with Perl 5.005, using lexical filehandles does not work under 5.005... . No functional changes, no upgrade is necessary 0.14 2007-10-20 - Hopefully restored compatibility of the tests with Perl 5.005 this time, reported by SREZIC . No functional changes, no upgrade is necessary 0.13 2007-10-20 - Restored compatibility of the tests with Perl 5.005, reported by SREZIC . No functional changes, no upgrade is necessary 0.12 2007-10-20 - Fixed RT [rt.cpan.org #24735], reported by Slaven Rezic This seems to have been some really weird interaction or maybe eating of $@. 0.11 2007-08-19 - File::Slurp is now no prerequisite anymore - the test requiring it is now optional 0.10 2007-08-04 - Added missing File::Slurp prerequisite. Thanks to ANDK and the CPAN smoke testers to alert me to this - No functional changes. No upgrade from 0.09 is necessary. 0.09 2006-12-31 - Added more consistency tests - Added Jerrad Pierce to the list of those who mentioned the command line use. I forgot the usage NUFFIN had given, so both were genuinely new to me. 0.08 2006-12-29 - Removed superfluous File::Temp usage from module 0.07 2006-12-29 - No more inline pod tests - No code changes since 0.06 0.06 2003-03-20 - fixed the inline test pod - Now needs Test::Pod 0.95 (or skips the pod test) 0.03 2003-02-17 - Released on CPAN - Fixes so the pod for the fake module doesn't appear in the main pod. 0.02 2003-02-16 - Released on CPAN 0.01 Sat Feb 15 21:14:45 2003 - original version; created by h2xs 1.21 with options -Xn Test::Without::Module Test-Without-Module-0.23/xt/0000755000175000017500000000000014644737741015261 5ustar corioncorionTest-Without-Module-0.23/xt/99-minimumversion.t0000644000175000017500000000047114644737741020770 0ustar corioncorion#!perl -w use strict; use Test::More; eval { #require Test::MinimumVersion::Fast; require Test::MinimumVersion; Test::MinimumVersion->import; }; my @files; if ($@) { plan skip_all => "Test::MinimumVersion required for testing minimum Perl version"; } else { all_minimum_version_from_metajson_ok(); } Test-Without-Module-0.23/xt/99-compile.t0000644000175000017500000000202514644737741017334 0ustar corioncorion#!perl use warnings; use strict; use File::Find; use Test::More; BEGIN { eval 'use Capture::Tiny ":all"; 1'; if ($@) { plan skip_all => "Capture::Tiny needed for testing"; exit 0; }; }; plan 'no_plan'; require './Makefile.PL'; # Loaded from Makefile.PL our %module = get_module_info(); my $last_version = undef; sub check { #return if (! m{(\.pm|\.pl) \z}xmsi); my ($stdout, $stderr, $exit) = capture(sub { system( $^X, '-Mblib', '-c', $_ ); }); s!\s*\z!! for ($stdout, $stderr); if( $exit ) { diag $stderr; diag "Exit code: ", $exit; fail($_); } elsif( $stderr ne "$_ syntax OK") { diag $stderr; fail($_); } else { pass($_); }; } my @files; find({wanted => \&wanted, no_chdir => 1}, grep { -d $_ } 'blib/lib', 'examples', 'lib' ); if( my $exe = $module{EXE_FILES}) { push @files, @$exe; }; for (@files) { check($_) } sub wanted { push @files, $File::Find::name if /\.p(l|m|od)$/; } Test-Without-Module-0.23/xt/99-changes.t0000644000175000017500000000133714644737741017321 0ustar corioncorion#!perl -w use warnings; use strict; use File::Find; use Test::More tests => 2; =head1 PURPOSE This test ensures that the Changes file mentions the current version and that a release date is mentioned as well =cut require './Makefile.PL'; # Loaded from Makefile.PL our %module = get_module_info(); my $module = $module{NAME}; (my $file = $module) =~ s!::!/!g; require "$file.pm"; my $version = sprintf '%0.2f', $module->VERSION; my $changes = do { local $/; open my $fh, 'Changes' or die $!; <$fh> }; ok $changes =~ /^(.*$version.*)$/m, "We find version $version for $module"; my $changes_line = $1; ok $changes_line =~ /$version\s+20\d\d-[01]\d-[0123]\d\b/, "We find a release date on the same line" or diag $changes_line; Test-Without-Module-0.23/xt/99-todo.t0000644000175000017500000000216614644737741016657 0ustar corioncorionuse Test::More; use File::Spec; use File::Find; use strict; # Check that all files do not contain any # lines with "XXX" - such markers should # either have been converted into Todo-stuff # or have been resolved. # The test was provided by Andy Lester. require './Makefile.PL'; # Loaded from Makefile.PL our %module = get_module_info(); my @files; my $blib = File::Spec->catfile(qw(blib lib)); find(\&wanted, grep { -d } ($blib)); if( my $exe = $module{EXE_FILES}) { push @files, @$exe; }; plan tests => 2* @files; foreach my $file (@files) { source_file_ok($file); } sub wanted { push @files, $File::Find::name if /\.p(l|m|od)$/; } sub source_file_ok { my $file = shift; open( my $fh, '<', $file ) or die "Can't open $file: $!"; my @lines = <$fh>; close $fh; my $n = 0; for ( @lines ) { ++$n; s/^/$file ($n): /; } my @x = grep /XXX/, @lines; if ( !is( scalar @x, 0, "Looking for XXXes in $file" ) ) { diag( $_ ) for @x; } @x = grep /<<<|>>>/, @lines; if ( !is( scalar @x, 0, "Looking for <<<<|>>>> in $file" ) ) { diag( $_ ) for @x; } } Test-Without-Module-0.23/xt/99-pod.t0000644000175000017500000000145514644737741016474 0ustar corioncorionuse Test::More; # Check our Pod # The test was provided by Andy Lester, # who stole it from Brian D. Foy # Thanks to both ! use File::Spec; use File::Find; use strict; eval { require Test::Pod; Test::Pod->import; }; require './Makefile.PL'; # Loaded from Makefile.PL our %module = get_module_info(); my @files; if ($@) { plan skip_all => "Test::Pod required for testing POD"; } elsif ($Test::Pod::VERSION < 0.95) { plan skip_all => "Test::Pod 0.95 required for testing POD"; } else { my $blib = File::Spec->catfile(qw(blib lib)); find(\&wanted, grep { -d } ($blib)); if( my $exe = $module{EXE_FILES}) { push @files, @$exe; }; plan tests => scalar @files; foreach my $file (@files) { pod_file_ok($file); } } sub wanted { push @files, $File::Find::name if /\.p(l|m|od)$/; } Test-Without-Module-0.23/xt/99-synopsis.t0000644000175000017500000000301114644737741017567 0ustar corioncorionuse strict; use Test::More; use File::Spec; use File::Find; use File::Temp 'tempfile'; require './Makefile.PL'; # Loaded from Makefile.PL our %module = get_module_info(); my @files; my $blib = File::Spec->catfile(qw(blib lib)); find(\&wanted, grep { -d } ($blib)); #if( my $exe = $module{EXE_FILES}) { # push @files, @$exe; #}; plan tests => scalar @files; foreach my $file (@files) { synopsis_file_ok($file); } sub wanted { push @files, $File::Find::name if /\.p(l|m|od)$/ and $_ !~ /\bDSL\.pm$/; # we skip that one as it initializes immediately } sub synopsis_file_ok { my( $file ) = @_; my $name = "SYNOPSIS in $file compiles"; open my $fh, '<', $file or die "Couldn't read '$file': $!"; my @synopsis = map { s!^\s\s!!; $_ } # outdent all code for here-docs grep { /^\s\s/ } # extract all verbatim (=code) stuff grep { /^=head1\s+SYNOPSIS$/.../^=/ } # extract Pod synopsis <$fh>; if( @synopsis ) { my($tmpfh,$tempname) = tempfile(); print {$tmpfh} join '', @synopsis; close $tmpfh; # flush it my $output = `$^X -Ilib -c $tempname 2>&1`; if( $output =~ /\ssyntax OK$/ ) { pass $name; } else { fail $name; diag $output; diag $_ for @synopsis; }; unlink $tempname or warn "Couldn't clean up $tempname: $!"; } else { SKIP: { skip "$file has no SYNOPSIS section", 1; }; }; } Test-Without-Module-0.23/xt/99-unix-text.t0000644000175000017500000000174514644737741017661 0ustar corioncorionuse Test::More; # Check that all released module files are in # UNIX text format use File::Spec; use File::Find; use strict; my @files = ('Makefile.PL', 'MANIFEST', 'MANIFEST.SKIP', glob 't/*.t'); require './Makefile.PL'; # Loaded from Makefile.PL our %module = get_module_info(); my @files; my $blib = File::Spec->catfile(qw(blib lib)); find(\&wanted, grep { -d } ($blib)); if( my $exe = $module{EXE_FILES}) { push @files, @$exe; }; plan tests => scalar @files; foreach my $file (@files) { unix_file_ok($file); } sub wanted { push @files, $File::Find::name if /\.p(l|m|od)$/; } sub unix_file_ok { my ($filename) = @_; local $/; open my $fh, '<', $filename or die "Couldn't open '$filename' : $!\n"; binmode $fh; my $content = <$fh>; my $i; my @lines = grep { /\x0D\x0A$/sm } map { sprintf "%s: %s\x0A", $i++, $_ } split /\x0A/, $content; unless (is(scalar @lines, 0,"'$filename' contains no windows newlines")) { diag $_ for @lines; }; close $fh; }; Test-Without-Module-0.23/xt/meta-lint.t0000644000175000017500000000215714644737741017345 0ustar corioncorion#!perl -w # Stolen from ChrisDolan on use.perl.org # http://use.perl.org/comments.pl?sid=29264&cid=44309 use warnings; use strict; use File::Find; use Test::More; eval { #require Test::MinimumVersion::Fast; require Parse::CPAN::Meta; Parse::CPAN::Meta->import(); require CPAN::Meta::Validator; CPAN::Meta::Validator->VERSION(2.15); }; if ($@) { plan skip_all => "CPAN::Meta::Validator version 2.15 required for testing META files"; } else { plan tests => 4; } use lib '.'; our %module; require 'Makefile.PL'; # Loaded from Makefile.PL %module = get_module_info(); my $module = $module{NAME}; (my $file = $module) =~ s!::!/!g; require "$file.pm"; my $version = sprintf '%0.2f', $module->VERSION; for my $meta_file ('META.yml', 'META.json') { my $meta = Parse::CPAN::Meta->load_file($meta_file); my $cmv = CPAN::Meta::Validator->new( $meta ); if(! ok $cmv->is_valid, "$meta_file is valid" ) { diag $_ for $cmv->errors; }; # Also check that the declared version matches the version in META.* is $meta->{version}, $version, "$meta_file version matches module version ($version)"; }; Test-Without-Module-0.23/xt/99-manifest.t0000644000175000017500000000204414644737741017513 0ustar corioncorionuse strict; use Test::More; # Check that MANIFEST and MANIFEST.skip are sane : use File::Find; use File::Spec; my @files = qw( MANIFEST MANIFEST.SKIP ); plan tests => scalar @files * 4 +1 # MANIFEST existence check +1 # MYMETA.* non-existence check ; for my $file (@files) { ok(-f $file, "$file exists"); open my $fh, '<', $file or die "Couldn't open $file : $!"; my @lines = <$fh>; is_deeply([grep(/^$/, @lines)],[], "No empty lines in $file"); is_deeply([grep(/^\s+$/, @lines)],[], "No whitespace-only lines in $file"); is_deeply([grep(/^\s*\S\s+$/, @lines)],[],"No trailing whitespace on lines in $file"); if ($file eq 'MANIFEST') { chomp @lines; is_deeply([grep { s/\s.*//; ! -f } @lines], [], "All files in $file exist") or do { diag "$_ is mentioned in $file but doesn't exist on disk" for grep { ! -f } @lines }; # Exclude some files from shipping is_deeply([grep(/^MYMETA\.(yml|json)$/, @lines)],[],"We don't try to ship MYMETA.* $file"); }; close $fh; }; Test-Without-Module-0.23/xt/copyright.t0000644000175000017500000000465014644737741017463 0ustar corioncorion#!perl use warnings; use strict; use File::Find; use Test::More tests => 1; use POSIX 'strftime'; my $this_year = strftime '%Y', localtime; my $last_modified_year = 0; my $is_checkout = -d '.git'; require './Makefile.PL'; # Loaded from Makefile.PL our %module = get_module_info(); my @files; #my $blib = File::Spec->catfile(qw(blib lib)); find(\&wanted, grep { -d } ('lib')); if( my $exe = $module{EXE_FILES}) { push @files, @$exe; }; sub wanted { push @files, $File::Find::name if /\.p(l|m|od)$/; } sub collect { my( $file ) = @_; note $file; my $modified_ts; if( $is_checkout ) { # diag `git log -1 --pretty="format:%ct" "$file"`; $modified_ts = `git log -1 --pretty="format:%ct" "$file"`; } else { $modified_ts = (stat($_))[9]; } my $modified_year; if( $modified_ts ) { $modified_year = strftime('%Y', localtime($modified_ts)); } else { $modified_year = 1970; }; open my $fh, '<', $file or die "Couldn't read $file: $!"; my @copyright = map { /\bcopyright\b.*?\d{4}-(\d{4})\b/i ? [ $_ => $1 ] : () } <$fh>; my $copyright = 0; for (@copyright) { $copyright = $_->[1] > $copyright ? $_->[1] : $copyright; }; return { file => $file, copyright_lines => \@copyright, copyright => $copyright, modified => $modified_year, }; }; my @results; for my $file (@files) { push @results, collect($file); }; for my $file (@results) { $last_modified_year = $last_modified_year < $file->{modified} ? $file->{modified} : $last_modified_year; }; note "Distribution was last modified in $last_modified_year"; my @out_of_date = grep { $_->{copyright} and $_->{copyright} < $last_modified_year } @results; if(! is 0+@out_of_date, 0, "All files have a current copyright year ($last_modified_year)") { for my $file (@out_of_date) { diag sprintf "%s modified %d, but copyright is %d", $file->{file}, $file->{modified}, $file->{copyright}; diag $_ for map {@$_} @{ $file->{copyright_lines}}; }; diag q{To fix (in a rough way, please review) run}; diag sprintf q{ perl -i -ple 's!(\bcopyright\b.*?\d{4}-)(\d{4})\b!${1}%s!i' %s}, $this_year, join ' ', map { $_->{file} } @out_of_date; }; Test-Without-Module-0.23/xt/99-versions.t0000644000175000017500000000315714644737741017563 0ustar corioncorion#!perl -w # Stolen from ChrisDolan on use.perl.org # http://use.perl.org/comments.pl?sid=29264&cid=44309 use warnings; use strict; use File::Find; use Test::More; require './Makefile.PL'; # Loaded from Makefile.PL our %module = get_module_info(); my @files; my $blib = File::Spec->catfile(qw(blib lib)); find(\&wanted, grep { -d } ($blib)); if( my $exe = $module{EXE_FILES}) { push @files, @$exe; }; sub read_file { open my $fh, '<', $_[0] or die "Couldn't read '$_[0]': $!"; binmode $fh; local $/; <$fh> } sub wanted { push @files, $File::Find::name if /\.p(l|m|od)$/; } plan tests => 0+@files; my $last_version = undef; sub check { my $content = read_file($_); # only look at perl scripts, not sh scripts return if (m{blib/script/}xms && $content !~ m/\A \#![^\r\n]+?perl/xms); # what my version numbers look like my $version = qr/\d+\.\d+/; my @version_lines = grep { defined } $content =~ m/ [^\n]* \$VERSION \s* = \s* ["']($version)['"] | package \s+ \S+ \s+ ($version) \s* ; /gxms; if (@version_lines == 0) { fail($_); } for my $line (@version_lines) { $line =~ s/^\s+//; $line =~ s/\s+$//; if (!defined $last_version) { $last_version = shift @version_lines; diag "Checking for $last_version"; pass($_); } else { is($line, $last_version, $_); } } } for (@files) { check(); }; if (! defined $last_version) { fail('Failed to find any files with $VERSION'); } Test-Without-Module-0.23/LICENSE0000644000175000017500000002127514644737741015642 0ustar corioncorion The Artistic License 2.0 Copyright (c) 2000-2006, The Perl Foundation. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble This license establishes the terms under which a given free software Package may be copied, modified, distributed, and/or redistributed. The intent is that the Copyright Holder maintains some artistic control over the development of that Package while still keeping the Package available as open source and free software. You are always permitted to make arrangements wholly outside of this license directly with the Copyright Holder of a given Package. If the terms of this license do not permit the full use that you propose to make of the Package, you should contact the Copyright Holder and seek a different licensing arrangement. Definitions "Copyright Holder" means the individual(s) or organization(s) named in the copyright notice for the entire Package. "Contributor" means any party that has contributed code or other material to the Package, in accordance with the Copyright Holder's procedures. "You" and "your" means any person who would like to copy, distribute, or modify the Package. "Package" means the collection of files distributed by the Copyright Holder, and derivatives of that collection and/or of those files. A given Package may consist of either the Standard Version, or a Modified Version. "Distribute" means providing a copy of the Package or making it accessible to anyone else, or in the case of a company or organization, to others outside of your company or organization. "Distributor Fee" means any fee that you charge for Distributing this Package or providing support for this Package to another party. It does not mean licensing fees. "Standard Version" refers to the Package if it has not been modified, or has been modified only in ways explicitly requested by the Copyright Holder. "Modified Version" means the Package, if it has been changed, and such changes were not explicitly requested by the Copyright Holder. "Original License" means this Artistic License as Distributed with the Standard Version of the Package, in its current version or as it may be modified by The Perl Foundation in the future. "Source" form means the source code, documentation source, and configuration files for the Package. "Compiled" form means the compiled bytecode, object code, binary, or any other form resulting from mechanical transformation or translation of the Source form. Permission for Use and Modification Without Distribution (1) You are permitted to use the Standard Version and create and use Modified Versions for any purpose without restriction, provided that you do not Distribute the Modified Version. Permissions for Redistribution of the Standard Version (2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers. At your discretion, such verbatim copies may or may not include a Compiled form of the Package. (3) You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder. The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License. Distribution of Modified Versions of the Package as Source (4) You may Distribute your Modified Version as Source (either gratis or for a Distributor Fee, and with or without a Compiled form of the Modified Version) provided that you clearly document how it differs from the Standard Version, including, but not limited to, documenting any non-standard features, executables, or modules, and provided that you do at least ONE of the following: (a) make the Modified Version available to the Copyright Holder of the Standard Version, under the Original License, so that the Copyright Holder may include your modifications in the Standard Version. (b) ensure that installation of your Modified Version does not prevent the user installing or running the Standard Version. In addition, the Modified Version must bear a name that is different from the name of the Standard Version. (c) allow anyone who receives a copy of the Modified Version to make the Source form of the Modified Version available to others under (i) the Original License or (ii) a license that permits the licensee to freely copy, modify and redistribute the Modified Version using the same licensing terms that apply to the copy that the licensee received, and requires that the Source form of the Modified Version, and of any works derived from it, be made freely available in that license fees are prohibited but Distributor Fees are allowed. Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source (5) You may Distribute Compiled forms of the Standard Version without the Source, provided that you include complete instructions on how to get the Source of the Standard Version. Such instructions must be valid at the time of your distribution. If these instructions, at any time while you are carrying out such distribution, become invalid, you must provide new instructions on demand or cease further distribution. If you provide valid instructions or cease distribution within thirty days after you become aware that the instructions are invalid, then you do not forfeit any of your rights under this license. (6) You may Distribute a Modified Version in Compiled form without the Source, provided that you comply with Section 4 with respect to the Source of the Modified Version. Aggregating or Linking the Package (7) You may aggregate the Package (either the Standard Version or Modified Version) with other packages and Distribute the resulting aggregation provided that you do not charge a licensing fee for the Package. Distributor Fees are permitted, and licensing fees for other components in the aggregation are permitted. The terms of this license apply to the use and Distribution of the Standard or Modified Versions as included in the aggregation. (8) You are permitted to link Modified and Standard Versions with other works, to embed the Package in a larger work of your own, or to build stand-alone binary or bytecode versions of applications that include the Package, and Distribute the result without restriction, provided the result does not expose a direct interface to the Package. Items That are Not Considered Part of a Modified Version (9) Works (including, but not limited to, modules and scripts) that merely extend or make use of the Package, do not, by themselves, cause the Package to be a Modified Version. In addition, such works are not considered parts of the Package itself, and are not subject to the terms of this license. General Provisions (10) Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license. (11) If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license. (12) This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder. (13) This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed. (14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.Test-Without-Module-0.23/MANIFEST.SKIP0000755000175000017500000000025614644737741016532 0ustar corioncorion^.cvsignore ^.prove ^.lwpcookies ^.releaserc ^blib/ ^Test-Without-Module-.* CVS/ ^pm_to_blib .tar.gz$ .old$ ^Makefile$ ^Releases/ ^MANIFEST.bak$ ^MYMETA.* ^.git ^.travis.yml Test-Without-Module-0.23/Texts/0000755000175000017500000000000014644737741015735 5ustar corioncorionTest-Without-Module-0.23/Texts/article.txt0000755000175000017500000001630614644737741020132 0ustar corioncorionTitle: Preventing a module from loading

I like modules that provide a dynamic fallback and degrade gracefully if some prerequisites are not available instead of requiring modules when they can do well without them.

But there is a problem - on my development machine, I have all these optional modules installed, but I want to test the behaviour of my code without the optional modules. So I want to set up tests where the optional modules seem not available. My preferred syntax for this is a pragma-like syntax :

use Test::Without::Module qw( HTML::Template ); use Test::Without::Module qr/^POE::/;

So, most of the magic will have to be installed in a sub called "import()" within my (to be written) module.

When you want to muck around with module loading, the only way in Perl seems to be to add a code reference into @INC. That code reference either returns a filehandle, from which the text will be loaded, or undef, which means that the next entry in @INC will be tried.

Things that didn't work :

BEGIN { @SAVED_INC = @INC; }; sub import { @INC = sub { # return undef if it's a blocked module # Look if the module is in @SAVED_INC # Return a filehandle to it }; };

This first variant worked quite well, until I came up to [cpan://Digest::MD5], which wants to load XS code. And the XS code loader looks through @INC, it dosen't respect coderefs in @INC, and thus, the loading of Digest::MD5 fails. Or rather, Digest::MD5 has a fallback to [cpan://Digest::Perl::MD5], which I didn't have installed. So this way will not work as soon as we use any module which uses XS code.

So I had to keep all existing directories in @INC, but there was no way to prevent Perl to look through the rest of @INC if my handler returned undef for a blocked module :

BEGIN { @SAVED_INC = @INC; }; sub import { @INC = sub { # return undef if it's a blocked module }; };

[demerphq] then suggested that I forget about a handler in @INC and muck instead with %INC and a custom import method, that would die whenever that module was imported into a new namespace.

sub import { $INC{$module} = 1; *{$module."::import"} = sub { die 'ugh'; }; };

But this version didn't work, because one could still require the module, and most checks whether a module is available rely on the meme

eval { require Optional::Module }; if ($@) { # module is not available };

But this put me on the right track, I would simply create a faked module on the fly, and return this faked module whenever I want to prevent a module from loading. I don't need to handle the case that a module is allowed, as the rest of @INC will take care of that.

sub import { unshift @INC, sub { # return dummy module filehandle if it's a blocked module }; };

There are now some technical pitfalls. First, [cpan://IO::String] does not work in an @INC-handler, seemingly Perl wants a real filehandle (or at least, [cpan://Acme::Intraweb] and [cpan://PAR] do it that way as well), so I have to create a tempfile for every faked module. That's not a real concern as my module is intended for testing anyway - efficiency is of no importance.

Second, what if a module has already been loaded? Then Perl won't go through @INC at all. So we have to scrub %INC as well and clean it of the unwanted modules, in case they have already been loaded.

After these tries, the algorithm to prevent a module from loading now looks like the following :

use vars qw( %forbidden ); sub import { my ($self,@forbidden_modules) = @_; scrub $module for @forbidden_modules; unshift @INC, sub { my (undef,$filename,undef) = @_; if (exists $forbidden{$filename}) { # return faked, failing module }; }; };

The complete module is appended below. If you have suggestions about the naming convention or the usage interface, I'd like to hear about them. If you have any hint on how to make my module into a lexical pragma (warnings.pm and strict.pm didn't offer a hint to me), I'll be even more interested.

package Test::Without::Module; use strict; use File::Temp; use Carp qw( croak ); use vars qw( %forbidden $VERSION ); $VERSION = 0.01; sub import { my ($self,@forbidden_modules) = @_; $forbidden{$_} = $_ for @forbidden_modules; # Scrub %INC, so that loaded modules disappear my ($module); for $module (@forbidden_modules) { scrub $module; }; # Move our handler to the front of the list @INC = grep { $_ ne \&fake_module } @INC; unshift @INC, \&fake_module; }; sub fake_module { my ($self,$module_file,$member_only) = @_; warn $@ if $@; my $modulename = file2module($module_file); # Deliver a faked, nonworking module if (grep { $modulename =~ $_ } keys %forbidden) { my $fh = File::Temp::tmpfile(); print $fh < and C. =begin testing no warnings 'once'; eval 'use Test::Without::Module qw( File::Temp )'; eval 'no Test::Without::Module qw( File::Temp )'; is_deeply( [keys %Test::Without::Module::forbidden],[],"Module list" ); eval { require File::Temp; }; is( $@, '', "unimport" ); =end testing =head1 BUGS =over 4 =item * There is no lexicalic scoping (yet) =back =head1 AUTHOR Max Maischein, Ecorion@cpan.orgE =head1 SEE ALSO L, L, L =cut