App-makefilepl2cpanfile
view release on metacpan or search on metacpan
use strict;
use warnings;
# Logic tests: each subtest is a proof of one rule the code relies on.
#
# The simplifications in lib/App/makefilepl2cpanfile.pm are only correct
# if these premises hold, so they are tested first, against every source
# of data, with deliberately hostile input:
#
# P1 Every stored version is 0, '' (config only), or passes
# _valid_requirement (a version or a version range).
# P2 Every stored comment is undef or a non-empty string.
#
# Then each reduced condition is tested with one case per logical
# partition (never two cases from the same partition), and each guard
# clause is shown to stop the call before any later work happens.
#
# The second half exhausts the truth table of every conditional in the
# module, checks De Morgan's laws row by row, walks all 40 combinations
# of generate()'s inputs against its formal specification, asserts the
# invariants before, during and after a call, and shows that inputs
# contradicting a premise are refused by the first guard able to see them.
use Test::Most;
use lib 't/lib';
use Test::Permissions qw(can_revoke_read can_revoke_search);
use Test::Mockingbird;
use Test::Returns;
use File::Temp qw(tempdir);
use Path::Tiny;
use Readonly;
use YAML::Tiny;
use App::makefilepl2cpanfile;
Readonly my $PKG => 'App::makefilepl2cpanfile';
Readonly my %CFG => (
header => '# Generated from Makefile.PL using makefilepl2cpanfile',
cfg_dir => '.config',
cfg_file => 'makefilepl2cpanfile.yml',
);
# Version tokens chosen to hit every branch of every validator: valid,
# zero, empty, no digit, trailing junk, non-ASCII digits, code, ranges.
Readonly my @VERSION_CORPUS => (
'0', '1', '1.60', 'v1.2.3', '1.23_01', '0.0', 'v0', '', '.', '_', 'v',
'1e3', '1.0-TRIAL', '>= 1.2', "\x{0661}", '1\\', '$x', 'Inf', 'NaN', ' 1',
);
# Comment tails: printable, blank, and made only of stripped characters.
Readonly my @COMMENT_CORPUS => (
'#', '# ', "#\t", '# text', "# \x{202E}", "# \r", "# a\x{202E}b", "# \x{1F680}",
);
sub use_home {
my $data = $_[0];
my $home = path(tempdir(CLEANUP => 1));
my $cfg = $home->child($CFG{cfg_dir}, $CFG{cfg_file});
if(defined $data) {
$cfg->parent->mkpath;
ref $data ? YAML::Tiny->new($data)->write("$cfg") : $cfg->spew_utf8($data);
}
return (mock_scoped('File::HomeDir::my_home' => sub { "$home" }), $cfg);
}
sub make_mf {
my $mf = path(tempdir(CLEANUP => 1))->child('Makefile.PL');
$mf->spew_utf8($_[0]);
return "$mf";
}
sub quiet_warnings {
my $code = $_[0];
my @w;
local $SIG{__WARN__} = sub { push @w, $_[0] };
my $r = $code->();
return ($r, @w);
}
# Every [phase, rel, module, entry] in a deps structure.
sub all_entries {
my $deps = $_[0];
return map {
my $p = $_;
map {
my $r = $_;
map { [ $p, $r, $_, $deps->{$p}{$r}{$_} ] } keys %{ $deps->{$p}{$r} }
} keys %{ $deps->{$p} }
} keys %{$deps};
}
# Captures the structure generate() hands to _emit.
sub capture_emit {
my $slot = $_[0];
my $real = \&App::makefilepl2cpanfile::_emit;
return mock_scoped "${PKG}::_emit" => sub { ${$slot} = $_[0]; $real->(@_) };
}
# -----------------------------------------------------------------------
# P1: every stored version is in the domain _has_version assumes
#
# Premise 1: _has_version only tests for a digit from 1 to 9.
# Premise 2: that is correct only for undef, 0, '' and valid versions.
# So: every source must store nothing else. Feed every corpus token
# through all three sources and check each stored value.
# -----------------------------------------------------------------------
subtest 'P1: all three sources store only in-domain versions' => sub {
( run in 0.798 second using v1.01-cache-2.11-cpan-e7c6538aa59 )