NAME

XT::Files - standard interface for author tests to find files to check

VERSION

Version 0.001

SYNOPSIS

In your distribution, add a XT::Files configuration file (optional):

[Default]
dirs = 0

[Dirs]
bin = script
module = lib
test = t
test = xt

In a .t file (optional):

use XT::Files;

my $xt = XT::Files->initialize( -config => undef ));
$xt->bin_dir('bin');
$xt->module_dir('lib');
$xt->test_dir('t');

In a Test module (optional):

use Test::XTFiles;

my @files = Test::XTFiles->new->all_perl_files;

DESCRIPTION

Author tests often iterate over your distributions files to check them. Unfortunately, every XT test uses its own code and defaults, to find the files to check, which means they often don't fit the need of your distribution. Common problems are not checking bin or script or, if they do, assuming Perl files in bin or script end in .pl.

The idea of XT::Files is that it's the Tests that know what they want to check (e.g. module files), but it's the distribution that knows where these files can be found (e.g. in the lib directory and in the t/lib directory).

Without XT::Files you are probably adding the same code to multiple .t files under xt that iterate over a check function of the test.

XT::Files is a standard interface that makes it easy for author tests to ask the distribution for the kind of files it would like to test. And it can easily be used for author tests that don't support XT::Files to have the same set of files tested with every test.

Note: This is for author tests only. Your own distributions tests already know which files to test.

USAGE

Usage for distribution authors

The distribution can (and should) use an XT::Files configuration file. The default names for the file is either .xtfilesrc or xtfiles.ini in the root directory of your distribution. Only one of these files must exist. If you put it in a different location or name it differently, you have to load it in every .t file

XT::Files->initialize( -config => 'maint/xt_files.txt' );

The config file contain a global section and a section for every used plugin. Comments start with either # or ;.

The same plugin can be run multiple times by adding multiple sections with the same name. Sections of the same name are not merged.

# require at least this version of XT::Files
:version = 0.001

# XT::Files::Plugin::Default
# the default configuration plugin
[Default]
# add the default directories (this is the default)
dirs = 1
# add the default excludes (this is the default)
excludes = 1

# XT::Files::Plugin::Dirs
# add directories with the bin_dir, module_dir or test_dir method
# from XT::Files
[Dirs]
bin = maint
module = maint/lib
test = maint/t

# XT::Files::Plugin::Files
# add files with the bin_file, module_file or test_file method
# from XT::Files
[Files]
bin = maint/config.pl
pod = maint/contribute.pod
module = maint/config.pm

# XT::Files::Plugin::Excludes
# add exclude patterns
[Excludes]
exclude = [.]old$

# add a directory to @INC to load plugins contained in the distribution
[lib]
lib = maint/plugin

# load a plugin from outside of the XT::Files::Plugin namesapce
# this is most likely used to load a plugin contained in the distribution
[=Local::MyPlugin]

The configuration is used to tell tests which files are what. A file can be of the following types.

  • bin

    These are executable Perl files. For most distributions they live in bin or script. They might, or might not, have a .pl extension. These files might, or might not, contain a Pod documentation.

    You can also add scripts in additional locations, e.g. in maint to the list of files to be tested with your author tests.

  • module

    These are Perl modules. For most distributions they live in the lib directory. Normally they have a .pm extension. These files might, or might not, contain a Pod documentation.

  • pod

    This is for Pod files. Normally they end in .pod. Your scripts or modules which contain Pod documentation are not of type pod.

  • test

    This is for test files. These files normally have a .t extension. Test files are also bin files.

All file names should be in UNIX format (forward slashes as directory separator) as all files found by XT::Files are added in this way. If you add a directory or file with a different directory separator the result is undefined.

Usage for author test authors

Writing an author test with XT::Files support is straightforward. All you have to do is decide what kind of files your author test is going to test and request these files from Test::XTFiles:

use Test::XTFiles;

# all Perl scripts and tests
my @files = Test::XTFiles->new->all_executable_files;

# all modules
my @files = Test::XTFiles->new->all_module_files;

# all perl files (scripts, modules and tests)
my @files = Test::XTFiles->new->all_perl_files;

# all files with Pod in it
use Pod::Simple::Search;
my @files = grep { Pod::Simple::Search->new->contains_pod($_) }
    Test::XTFiles->new->all_files;

Don't try to be clever, that's the distributions job. Ask what makes sense to test - it's the distributions fault if a file is not correctly classified. And it's much easier for a distribution author to fix the distributions config file then it is for the test author to guess correctly.

Methods from XT::Files

All file names passed to methods should be in UNIX format (forward slashes as directory separator) as all files found by XT::Files are added in this way. If you add a directory with a different directory separator the result is undefined.

new( [ -config => CONFIG ] )

Returns a new XT::Files object.

Supports the -config argument which needs one of the following arguments.

  • undef: No configuration is loaded and the object is not initialized. This can be useful if you would like to build up your configuration programmatically.

  • A file name: The file is opened and the configuration is read from this file.

  • A reference to a string: The configuration is read from this string.

Note: This does neither create the XT::Files singleton nor return it. This is probably not what you want. Unless you know exactly why you need a XT::Files object that differs from the singleton you should use initialize or instance which both create and return the singleton.

initialize ( [ -config => CONFIG ] )

Checks if the singleton exists and croaks if it does. Otherwise calls new with the same arguments and saves the XT::Files object returned by new in the singleton, before returning it.

This is most likely the initialization you should use in your .t file if you need the object.

my $xt = XT::Files->initialize;
$xt->bin_file('maint/cleanup.pl');

instance

Checks if the XT::Files singleton exists and calls initialize without arguments if it does not. Then returns the singleton.

This method silently discards all arguments. If the singleton does not exist, it will always use the default configuration which is the XT::Files config file or, if that does not exist, the XT::Files::Plugin::Default plugin.

This is the method that is called by Test::XTFiles's new method.

files

Returns all files to be tested as XT::Files::File objects.

You should probably use one or multiple of the methods of Test::XTFiles if you need to obtain a list of files to be tested, either in a Test test or in a .t test file.

exclude( PATTERN )

Adds an exclude pattern. The files method tries to match the basename of every file against these patterns and skips the file if it matches.

Use this to exclude temporary or backup files you have in your workspace.

bin_dir( DIRECTORY )

Scans the directory for files and adds them all as executable files. Files that already have an entry are skipped.

There are no further checks that every file in the directory is a Perl script. Use this method to add directories like bin or script.

If you have a directory that contains Perl scripts and other files, add them selectively with bin_file from within your .t test file or use the XT::Files::Plugin::Files plugin from your configuration file.

module_dir( DIRECTORY )

Scans the directory for files and adds all files ending in .pm as module file and every file ending in .pod as pod file. Other files are skipped. Files that already have an entry are skipped.

test_dir( DIRECTORY )

Scans the directory for files and adds all files anding in .t as test file. Other files are skipped. Files that already have an entry are skipped.

bin_file( FILENAME )

Adds the file FILENAME to the list of files to be tested and marks it as a Perl script file. If there is already an entry for FILENAME, the existing entry is replaced with a new entry.

ignore_file( FILENAME )

Ignores a file from being tested. This method adds an undef entry for FILENAME. Use this to e.g. remove a single file from a directory:

$xt->bin_dir('maint');
$xt->ignore_file('maint/bugs.csv');

module_file( FILENAME )

Adds the file FILENAME to the list of files to be tested and marks it as a Perl module file. If there is already an entry for FILENAME, the existing entry is replaced with a new entry.

pod_file( FILENAME )

Adds the file FILENAME to the list of files to be tested and marks it as a Pod file. If there is already an entry for FILENAME, the existing entry is replaced with a new entry.

A Pod file is a file which typically ends in .pod. This is not for other files (e.g. modules or scripts) that also contain Pod.

remove_file( FILENAME )

Removes the entry for FILENAME from the list of files to be tested.

This differs from ignore_file in that later calls to the *_dir methods can add a new file for a removed file, but not for an ignored file.

test_file( FILENAME )

Adds the file FILENAME to the list of files to be tested and marks it as a test file. If there is already an entry for FILENAME, the existing entry is replaced with a new entry.

file( FILENAME, [ FILE OBJECT ] )

Returns the file entry for FILENAME when called with a single argument.

With two arguments, the FILE OBJECT must either be undef or a XT::Files::File object.

You should probably use one of the existing *_file methods to add new files but this method can be used to e.g. add a modulino.

my $file = XT::Files::File->new(
    name => 'bin/my_modulino',
    is_module => 1,
    is_script => 1
);
$xt->file($file->name, $file);

plugin( NAME, VERSION, KEYVALS_REF )

Loads and runs a plugin. All plugins must have a new and a run method.

If the name starts with a =, the leading = is removed and the remaining string is used as package name. Otherwise XT::Files::Plugin:: is prepended to the string and this is used as package name.

The plugin method uses Module::Load to load the plugin. If a VERSION is defined it checks that versions parse of the VERSION isn't lower then the plugins version. VERSION can be undef which means every version is accepted.

Then it calls the plugins new method and passes $self as the xtf argument and expects an object of the plugin in return.

my $plugin_object = NAME->new( xtf => $self );

After that it calls the plugins run method and passes it the KEYVALS_REF.

EXAMPLES

Example 1 Use a test that supports XT::Files with default config

Because the Test::Pod::Links supports XT::Files we can just use the following two lines for our author test .t file.

use Test::Pod::Links 0.003;

all_pod_files_ok();

When Test::Pod::Links asks XT::Files for all the pod files to check, XT::Files checks if the distribution has an XT::Files config file. If the config file exists it is parsed and processed, otherwise the XT::Files::Plugin::Default is loaded to load the default XT::Files configuration.

Example 2 Use a test that supports XT::Files with default config files

In your distribution's .xtfilesrc or xtfiles.ini file you can configure the structure of your distribution.

[Default]
dirs=0

[Dirs]
bin = bin
module = lib
test = t
test = xt

The run the test the same as in Example 1

use Test::Pod::Links

all_pod_files_ok();

But this time the file list is generated depending on your config file and not on the defaults from the XT::Files::Plugin::Default plugin.

Example 3 Use a test that supports XT::Files but ignore default config file

The following example lets you programmatically configure the XT::Files file list omitting a config file, if it exists and only loading the excludes config from the XT::Files::Plugin::Default plugin.

We recommend that you always configure XT::Files with a config file but this example could be used if some special logic is required.

use XT::Files;
use Test::Pod::Links

my $xt = XT::Files->initialize( -config => undef );

$xt->plugin( 'Default', undef, [ dirs => 0 ] );

$xt->bin_dir('bin');
$xt->lib_dir('lib');
$xt->test_dir('t');
$xt->test_dir('xt');

all_pod_files_ok();

Example 4 Use a test that supports XT::Files with the config file but add test directory

This initializes the config, either from the config file or, if no config file exists, with the XT::Files::Plugin::Default plugin. Then it adds an additional two directories. This can be used if you want to check some files with only some author tests.

use XT::Files;
use Test::Pod::Links

my $xt = XT::Files->instance;
$xt->test_dir('t');
$xt->test_dir('xt');

all_pod_files_ok();

Example 5 Use a test that does not support XT::Files

If a test does not support XT::Files we have to fall back to the old iterating over the files and call the files_ok (or similar) function. This allows us to use the same logic to generate the file list as all tests that support XT::Files use.

use Test::More 0.88;
use Test::XTFiles;
use Test::Something;

for my $file (Test::XTFiles->new->all_files()) {
  files_ok($file);
}

done_testing();

SEE ALSO

Test::XTFiles, XT::Files::File, XT::Files::Plugin::Default, XT::Files::Plugin::Dirs, XT::Files::Plugin::Excludes, XT::Files::Plugin::Files, XT::Files::Plugin::lib, XT::Files::Plugin, XT::Files::Role::Logger

SUPPORT

Bugs / Feature Requests

Please report any bugs or feature requests through the issue tracker at https://github.com/skirmess/XT-Files/issues. You will be notified automatically of any progress on your issue.

Source Code

This is open source software. The code repository is available for public review and contribution under the terms of the license.

https://github.com/skirmess/XT-Files

git clone https://github.com/skirmess/XT-Files.git

AUTHOR

Sven Kirmess <sven.kirmess@kzone.ch>

COPYRIGHT AND LICENSE

This software is Copyright (c) 2018-2019 by Sven Kirmess.

This is free software, licensed under:

The (two-clause) FreeBSD License