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

NAME

Test::Prereq::Meta - Test distribution prerequisites against CPAN meta data file.

SYNOPSIS

 use Test::More 0.88; # For done_testing();
 use Test::Prereq::Meta qw{ prereq_ok };
 
 prereq_ok();
 
 done_testing();

DESCRIPTION

This Perl module tests whether a Perl module or file's prerequisites are all accounted for in the meta data for its distribution. It was inspired by Brian D. Foy's Test::Prereq, and like it uses Module::Extract::Use to determine what modules a given Perl script/module needs. But unlike Test::Prereq this module loads prerequisites from the distribution's meta data file (hence this module's name) using CPAN::Meta, and is thus independent of the distribution's build mechanism.

Each file tested has a test generated for each distinct module used. If a file uses no modules, a skipped test is generated.

Note that this package requires Perl 5.10, a requirement it inherits from Module::Extract::Use. If you are writing a test that might be run under an older Perl, you would need to do something like the following, which works if the prerequisite test is in its own file:

 use Test::More 0.88; # For done_testing();
 
 "$]" >= 5.010
   or plan skip_all => 'Perl 5.10 or higher required';
 require Test::Prereq::Meta;
 Test::Prereq::Meta->import( 'prereq_ok' );
 
 prereq_ok();
 
 done_testing();

There are no exports by default, but anything so documented can be exported, and export tag :all exports everything exportable.

METHODS

This class supports the following public methods:

new

 my $tpm = Test::Prereq::Meta->new();

This static method instantiates the test object and reads in the meta data that contain the prerequisites.

Caveat: the resultant object should not be used to test more than one distribution.

This method accepts the following arguments as name/value pairs:

accept

This argument is the name of a module, or a reference to an array of module names. These modules will be passed even if they are not listed as prerequisites.

If the verbose attribute is true, a diagnostic will be emitted for any modules listed here which also appear in the prerequisites or the uses attribute.

The default is [], that is, a reference to an empty array.

file_error

This argument specifies the template for the name of the failing test that is generated if some error is encountered by Module::Extract::Use.

See below for the defined substitutions into the template. The '%m' substitution is not relevant to this template.

The default value is 'Failed to analyze %f: %e'.

meta_file

This argument specifies the name of the file that contains the meta data, or a reference to an array of file names. In the latter case the first file that is readable will be used.

The default is

 [ qw{ MYMETA.json MYMETA.yml META.json META.yml } ]

An exception will be thrown if none of the specified files is readable.

This argument can also be a CPAN::Meta object.

Caveat: I am unsure what the correct search order should be among the meta files. For the purposes of this module .json and .yml should provide equivalent information, but MYMETA.* may not be equivalent to META.*. I have placed MYMETA.* first because it is easier to regenerate, but this may change if some compelling reason to change emerges.

name

This argument specifies the template for the name of the tests generated.

See below for the defined substitutions into the template. The '%e' substitution is not relevant to this template.

The default value is 'Prereq test: %f uses %m'.

per_file_note

This argument specifies the template for the note to be inserted before the tests of each file. This note will be indented so as to align with the names of subsequent tests, if any. A value of '' suppresses the note.

See below for the defined substitutions into the template. The '%m' and '%e' substitutions are not relevant to this template.

The default value is '%f'.

perl_version

This argument specifies the version of Perl whose core modules are to be accepted even if they are not listed as prerequisites. This version should be specified as it appears in $] or (more to the point) as it is expected by Module::CoreList.

The following special-case versions are also provided:

none

This has the effect of requiring core modules to be included in the prerequisites unless otherwise exempted (e.g. by being included in the 'accept' list).

this

This specifies the version of Perl that is running the test, and is equivalent to specifying the value of $].

The default is 'none'.

prune

This argument should be considered experimental. There are obvious portability issues, and VMS is currently unsupported because I have no such platform on which to develop or test. It may become necessary to change this in incompatible ways with little (or no) notice, or retract it completely. Caveat coder.

This argument specifies the names of files to prune from the scan done by all_prereq_ok(). The specification is in POSIX form, relative to the distribution directory. In the case of directories (which is the anticipated use) all files in the directory will also be ignored. A single file can be specified as a scalar; otherwise the value is a reference to an array of file names.

The specifications are matched against the file names reported by File::Find (normalized to POSIX form).

The default is [], i.e. prune nothing.

skip_name

This argument specifies the template for the name of any skipped tests.

See below for the defined substitutions into the template. The '%m' and '%e' substitutions are not relevant to this template.

The default value is 'Prereq test: %f does not use any modules'.

uses

This argument is the name of a module, or a reference to an array of module names. The all_prereqs_used() test will count these as having been used, even if no use of them is found.

If the verbose attribute is true, a diagnostic will be emitted for any modules listed here which also appear in the accept attribute.

The default is [], that is, a reference to an empty array.

verbose

This Boolean argument specifies whether diagnostics are generated on redundant accept and uses specifications.

On the presumption that these are more likely to be of use to a module author than a module user, the default is true if and only if at least one of the following directories exists at the distribution's top level:

    .bzr .cdv .git .hg .svn CVS

The use of arguments other than the above will result in an exception.

Arguments 'file_error', 'name', 'per_file_note', and 'skip_name' are templates for generating the actual text to be emitted. Selected data can be substituted into the template. Substitutions are introduced by the '%' character. The following substitutions are defined:

'%e' substitutes the most-recent Module::Extract::Use error.
'%f' substitutes the name of the file being tested;
'%m' substitutes the name of the module being required;
'%%' substitutes a literal '%'.

All other substitutions are undefined in the formal sense that the author makes no commitment as to what they do, and whatever they do the author reserves the right to change it without notice.

Not all defined substitutions are relevant in all cases. In such cases you will generally get '' substituted, though the author may change this if someone makes a case for it.

all_prereq_ok

 $tpm->all_prereq_ok()

This method takes as arguments one or more file names, which are searched by File::Find. Any Perl files found are passed to file_prereq_ok(). Perl files are defined as text files whose names end in .PL (case-insensitive), .pm (case-sensitive), or .t (case-sensitive), or text files having a shebang line which contains the string 'perl'.

This method returns a true value if all tests either passed or skipped, or a false value if any test failed.

If no arguments are specified, the arguments default to

 ( qw{ blib/arch blib/lib blib/script t } )

This method can also be exported and called as a subroutine, in which case it functions as though its invocant were the default object, i.e. one instantiated with no arguments.

all_prereqs_used

 $tpm->all_prereqs_used()

This method tests whether all prerequisites have been used. If it fails, it emits a diagnostic saying which prerequisites are unused.

This method will not work as desired unless the invocant was also used to test all relevant files in the distribution using all_prereq_ok() or file_prereq_ok().

If the verbose attribute is true, a diagnostic will be emitted for any modules listed in the uses attribute which were actually used by the code being tested.

file_prereq_ok

 $tpm->file_prereq_ok( $file_name );

This method takes as argument exactly one file name. This is assumed to be a Perl file, and all modules required by it (by require(), use(), use base, or use parent) are checked against the requirements specified in the meta data, in ASCIIbetical order. A passing test is generated if the module meets one of the following criteria:

it is listed in the distribution's prerequisites
it is provided by a .pm file listed in the distribution's MANIFEST.
it is listed in the accept argument to new().
it is a core module in the specified version of Perl, if any.

Otherwise a failing test will be generated. If the file does not require any modules, a skipped test is generated.

This method returns a true value if all tests either passed or skipped, or a false value if any test failed.

This method can also be exported and called as a subroutine, in which case it functions as though its invocant were the default object, i.e. one instantiated with no arguments.

prereq_ok

 prereq_ok( $perl_version, $name, $accept );

This subroutine (not method) is intended to correspond to the same-named subroutine in Test::Prereq|Test::Prereq, and takes the same arguments. Unlike new(), $perl_version defaults to $] for compatibility with Test::Prereq.

This subroutine returns a true value if all tests either passed or skipped, or a false value if any test failed.

BUGS/RESTRICTIONS

This module relies on Brian D. Foy's Module::Extract::Use to determine what modules a given Perl source file requires. This in turn relies on the PPI parse of the file, that is, on a static analysis.

This means that things like require $some_file or eval "require $some_module" will be missed.

It also means that this module relies on Module::Extract::Use's idea of what statements cause a module to be loaded. As of this writing this appears to be anything that parses as a PPI::Statement::Include (with special-case code for use base and use parent), plus a good effort to find require() calls that are embedded in other statements.

This module uses the meta data 'provides' information to determine what modules are provided by the distribution. If this is absent, it uses Module::Metadata to determine provided modules directly from blib/lib/.

It is a personal crotchet of mine that if a distribution provides both Makefile.PL and Build.PL, that neither ExtUtils::MakeMaker nor Module::Build should be a prerequisite. The actual situation is that one or the other is required, but both are not. But the Perl dependency system appears to have no way to represent this. So in the presence of both Makefile.PL and Build.PL, I have indulged my whim.

SEE ALSO

Test::Dependencies by Erik Huelsmann, which expects to receive dependencies in a CPAN::Meta object, and scans a list of files provided by the user. Internally, it uses Pod::Strip to remove POD, and then regular expressions to find use(), with(), extends(), and use base statements.

Test::Prereq by Brian D. Foy, which intercepts the ExtUtils::MakeMaker WriteMakefile(), or Test::Prereq::Build (same distribution) which intercepts Module::Build new() calls to figure out what dependencies have been declared. It uses Module::Extract::Use (and ultimately PPI) to find use(), no(), require(), use base and use parent statements.

SUPPORT

Support is by the author. Please file bug reports at https://rt.cpan.org/Public/Dist/Display.html?Name=Test-Prereq-Meta, https://github.com/trwyant/perl-Test-Prereq-Meta/issues, or in electronic mail to the author.

AUTHOR

Thomas R. Wyant, III wyant at cpan dot org

COPYRIGHT AND LICENSE

Copyright (C) 2021 by Thomas R. Wyant, III

This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.