App-makefilepl2cpanfile
view release on metacpan or search on metacpan
t/integration.t view on Meta::CPAN
use strict;
use warnings;
# Integration tests for App::makefilepl2cpanfile.
#
# These tests validate end-to-end workflows that cross function boundaries:
# parse_prereqs() feeding generate(), config loading interacting with the
# develop block, develop-merge semantics, format invariants across scenarios,
# and statelessness under repeated or interleaved calls.
#
# NOTE ON OPTIONAL DEPENDENCIES
# All CPAN dependencies in this module are hard 'use' requirements â there are
# no soft/optional require paths. Test::Without::Module is therefore not
# applicable. The closest analog is the OPTIONAL CONFIG FILE at
# ~/.config/makefilepl2cpanfile.yml; its absent/present/malformed paths are
# exercised in dedicated subtests below.
use Test::Most;
use Test::Mockingbird;
use File::Temp qw(tempdir);
use Path::Tiny;
use Readonly;
use YAML::Tiny;
use_ok('App::makefilepl2cpanfile');
# -----------------------------------------------------------------------
# Fixture constants â shared across all subtests.
# -----------------------------------------------------------------------
# Makefile.PL with only PREREQ_PM entries; no version constraints, no comments.
Readonly my $MF_SIMPLE => <<'END';
use ExtUtils::MakeMaker;
WriteMakefile(
PREREQ_PM => {
'Moo' => 0,
'Try::Tiny' => 0,
},
);
END
# Makefile.PL with all four simple key types.
Readonly my $MF_ALL_PHASES => <<'END';
use ExtUtils::MakeMaker;
WriteMakefile(
CONFIGURE_REQUIRES => { 'ExtUtils::MakeMaker' => '6.64' },
BUILD_REQUIRES => { 'Module::Build' => '0.42' },
TEST_REQUIRES => { 'Test::More' => '0.98' },
PREREQ_PM => { 'Scalar::Util' => 0 },
);
END
# Makefile.PL with a MIN_PERL_VERSION declaration.
Readonly my $MF_MIN_PERL => <<'END';
use ExtUtils::MakeMaker;
WriteMakefile(
MIN_PERL_VERSION => '5.010',
PREREQ_PM => { 'Moo' => 0 },
);
END
# Makefile.PL with inline comments on dependency lines.
Readonly my $MF_COMMENTS => <<'END';
use ExtUtils::MakeMaker;
WriteMakefile(
PREREQ_PM => {
'HTTP::Tiny' => 0, # used for HTTP calls
'JSON::PP' => '2.00', # decode API responses
},
);
END
# Makefile.PL with a CPAN Meta Spec structured prereqs block.
Readonly my $MF_STRUCTURED => <<'END';
use ExtUtils::MakeMaker;
WriteMakefile(
prereqs => {
runtime => {
requires => { 'Moo' => '2.00' },
recommends => { 'Moo::XS' => 0 },
suggests => { 'MooX::TypeTiny' => 0 },
},
test => {
requires => { 'Test::More' => '0.98' },
},
},
);
END
# Makefile.PL with a META_MERGE nested prereqs block (recommends).
Readonly my $MF_META_MERGE => <<'END';
use ExtUtils::MakeMaker;
WriteMakefile(
PREREQ_PM => { 'Carp' => 0 },
META_MERGE => {
prereqs => {
runtime => {
t/integration.t view on Meta::CPAN
ok $out !~ /\n\n\n/,
"[$name] output has no triple (or more) consecutive newlines";
# Runtime deps must NOT be inside an 'on' block.
# (Simple test: if requires 'Moo' appears, it must not be inside on 'runtime')
ok $out !~ /on 'runtime' => sub/,
"[$name] no 'on runtime' block (runtime emitted at top level)";
}
};
# -----------------------------------------------------------------------
# 14. Alphabetical sort invariant within phase blocks
#
# Strategy: verify that within each relationship group, modules are emitted
# in alphabetical order regardless of the order they appear in Makefile.PL.
# -----------------------------------------------------------------------
subtest 'output format: modules sorted alphabetically within each phase' => sub {
my $g = empty_home();
# Declare modules in reverse alphabetical order in the Makefile.PL.
my $mf = make_mf(<<'END');
WriteMakefile(
PREREQ_PM => {
'Zeta::Module' => 0,
'Alpha::First' => 0,
'Mu::Middle' => 0,
},
);
END
my $out = App::makefilepl2cpanfile::generate(
makefile => "$mf",
with_develop => 0,
);
diag "Output:\n$out" if $ENV{TEST_VERBOSE};
# Extract the order of the three modules in the output.
my @order;
while ($out =~ /requires '(Alpha::First|Mu::Middle|Zeta::Module)'/g) {
push @order, $1;
}
is_deeply \@order, [qw(Alpha::First Mu::Middle Zeta::Module)],
'modules emitted in alphabetical order regardless of Makefile.PL order';
};
# -----------------------------------------------------------------------
# 15. Statelessness: independent generate() calls do not interfere
#
# Strategy: call generate() with two different Makefile.PLs (A and B),
# then call generate() for A again. The first and third outputs must be
# identical, and A must differ from B. This confirms no shared mutable
# state exists between calls.
# -----------------------------------------------------------------------
subtest 'statelessness: independent generate() calls do not interfere' => sub {
my $g = empty_home();
my $mf_a = make_mf($MF_SIMPLE); # Moo + Try::Tiny
my $mf_b = make_mf($MF_ALL_PHASES); # completely different modules
my $out_a1 = App::makefilepl2cpanfile::generate(
makefile => "$mf_a", with_develop => 0
);
my $out_b = App::makefilepl2cpanfile::generate(
makefile => "$mf_b", with_develop => 0
);
my $out_a2 = App::makefilepl2cpanfile::generate(
makefile => "$mf_a", with_develop => 0
);
is $out_a2, $out_a1, 'same Makefile.PL produces identical output on second call';
isnt $out_b, $out_a1, 'different Makefile.PLs produce different outputs';
diag "A1:\n$out_a1\nB:\n$out_b" if $ENV{TEST_VERBOSE};
};
# -----------------------------------------------------------------------
# 16. Statelessness: interleaved parse_prereqs() calls
#
# Strategy: call parse_prereqs() with content A, then with empty content,
# then with content A again. The first and third results must be deeply
# equal, and the second (empty) must return an empty hashref.
# This checks that the function holds no mutable state between calls.
# -----------------------------------------------------------------------
subtest 'statelessness: interleaved parse_prereqs() calls' => sub {
my $content_a = $MF_ALL_PHASES;
my $result_a1 = App::makefilepl2cpanfile::parse_prereqs($content_a);
my $result_empty = App::makefilepl2cpanfile::parse_prereqs('');
my $result_a2 = App::makefilepl2cpanfile::parse_prereqs($content_a);
is_deeply $result_a2, $result_a1,
'parse_prereqs() returns same result for same content on repeated calls';
is_deeply $result_empty, {},
'parse_prereqs() returns empty hashref for empty content';
};
# -----------------------------------------------------------------------
# 17. Version constraint emission: versioned and unversioned deps
#
# Strategy: a version of 0 must produce no version constraint in the
# cpanfile output; a non-zero version must produce a quoted version
# argument. Both parse_prereqs and generate are checked end-to-end.
# -----------------------------------------------------------------------
subtest 'pipeline: versioned and unversioned deps emitted correctly' => sub {
my $g = empty_home();
my $mf = make_mf(<<'END');
WriteMakefile(
PREREQ_PM => {
'Unversioned' => 0,
'Versioned' => '1.23',
},
);
END
( run in 1.512 second using v1.01-cache-2.11-cpan-6de40a662fe )