File Coverage

blib/lib/Dist/Zilla/Plugin/Test/CheckBreaks.pm
Criterion Covered Total %
statement 61 61 100.0
branch 8 8 100.0
condition 3 3 100.0
subroutine 18 18 100.0
pod 0 5 0.0
total 90 95 94.7


line stmt bran cond sub pod time code
1 5     5   17876776 use strict;
  5         12  
  5         149  
2 5     5   25 use warnings;
  5         10  
  5         291  
3             package Dist::Zilla::Plugin::Test::CheckBreaks; # git description: v0.018-2-g6cc62e9
4             # vim: set ts=8 sts=4 sw=4 tw=115 et :
5             # ABSTRACT: Generate a test that shows what modules you are breaking
6             # KEYWORDS: distribution prerequisites upstream dependencies modules conflicts breaks breakages metadata
7              
8             our $VERSION = '0.019';
9              
10 5     5   34 use Moose;
  5         9  
  5         43  
11             with (
12             'Dist::Zilla::Role::FileGatherer',
13             'Dist::Zilla::Role::FileMunger',
14             'Dist::Zilla::Role::TextTemplate',
15             'Dist::Zilla::Role::PrereqSource',
16             'Dist::Zilla::Role::ModuleMetadata',
17             );
18 5     5   30895 use Path::Tiny;
  5         14  
  5         323  
19 5     5   33 use Module::Runtime 'module_notional_filename';
  5         12  
  5         38  
20 5     5   288 use List::Util 1.33 qw(any first);
  5         135  
  5         386  
21 5     5   44 use Sub::Exporter::ForMethods 'method_installer';
  5         12  
  5         77  
22 5     5   1217 use Data::Section 0.004 { installer => method_installer }, '-setup';
  5         104  
  5         32  
23 5     5   6599 use Data::Dumper ();
  5         27557  
  5         153  
24 5     5   55 use namespace::autoclean;
  5         15  
  5         55  
25              
26             has no_forced_deps => (
27             is => 'ro', isa => 'Bool',
28             default => 0,
29             );
30              
31 12     12 0 76 sub filename { path('t', 'zzz-check-breaks.t') }
32              
33             around dump_config => sub
34             {
35             my ($orig, $self) = @_;
36             my $config = $self->$orig;
37              
38             $config->{+__PACKAGE__} = {
39             conflicts_module => [ sort $self->conflicts_module ],
40             no_forced_deps => ($self->no_forced_deps ? 1 : 0),
41             blessed($self) ne __PACKAGE__ ? ( version => $VERSION ) : (),
42             };
43              
44             return $config;
45             };
46              
47             sub gather_files
48             {
49 6     6 0 415214 my $self = shift;
50              
51 6         1936 require Dist::Zilla::File::InMemory;
52              
53             $self->add_file( Dist::Zilla::File::InMemory->new(
54             name => $self->filename->stringify,
55 6         446138 content => ${$self->section_data('test-check-breaks')},
  6         309  
56             ));
57             }
58              
59 6     6 0 115428 sub mvp_multivalue_args { 'conflicts_module' }
60              
61             has conflicts_module => (
62             isa => 'ArrayRef[Str]',
63             traits => ['Array'],
64             handles => { conflicts_module => 'elements' },
65             lazy => 1,
66             default => sub {
67             my $self = shift;
68              
69             $self->log_debug('no conflicts_module provided; looking for one in the dist...');
70              
71             my $mmd = $self->module_metadata_for_file($self->zilla->main_module);
72             my $module = ($mmd->packages_inside)[0] . '::Conflicts';
73              
74             # check that the file exists in the dist (it should never be shipped
75             # separately!)
76             my $conflicts_filename = module_notional_filename($module);
77             if (any { $_->name eq path('lib', $conflicts_filename) } @{ $self->zilla->files })
78             {
79             $self->log_debug([ '%s found', $module ]);
80             return [ $module ];
81             }
82              
83             $self->log_debug([ 'No %s found', $module ]);
84             return [];
85             },
86             );
87              
88 10     10   30745 sub _cmc_prereq { '0.011' }
89              
90             sub munge_files
91             {
92 6     6 0 45141 my $self = shift;
93              
94             # module => filename
95             my $modules = { map {
96 6         290 require Module::Runtime;
  4         51  
97 4         13 $_ => Module::Runtime::module_notional_filename($_)
98             } $self->conflicts_module };
99              
100 6         315 my $breaks_data = $self->_x_breaks_data;
101 6 100 100     184 $self->log_debug('no x_breaks metadata and no conflicts module found to check against: adding no-op test')
102             if not keys %$breaks_data and not $self->conflicts_module;
103              
104 6         463 my $filename = $self->filename;
105 6     19   314 my $file = first { $_->name eq $filename } @{ $self->zilla->files };
  19         999  
  6         198  
106              
107 6         335 my $content = $self->fill_in_string(
108             $file->content,
109             {
110             dist => \($self->zilla),
111             plugin => \$self,
112             modules => \$modules,
113             no_forced_deps => \($self->no_forced_deps),
114             breaks => \$breaks_data,
115             cmc_prereq => \($self->_cmc_prereq),
116             test_count => \($self->_test_count),
117             }
118             );
119              
120 6         17237 $content =~ s/\n\n\z/\n/;
121 6         55 $file->content($content);
122              
123 6         1752 return;
124             }
125              
126             sub register_prereqs
127             {
128 6     6 0 4765 my $self = shift;
129              
130 6         231 $self->zilla->register_prereqs(
131             {
132             phase => 'test',
133             type => 'requires',
134             },
135             'Test::More' => '0',
136             );
137              
138 6 100       3548 return if not keys %{ $self->_x_breaks_data };
  6         282  
139              
140 2 100       65 $self->zilla->register_prereqs(
141             {
142             phase => 'test',
143             type => $self->no_forced_deps ? 'suggests' : 'requires',
144             },
145             'CPAN::Meta::Requirements' => '0',
146             'CPAN::Meta::Check' => $self->_cmc_prereq,
147             );
148             }
149              
150             has _x_breaks_data => (
151             is => 'ro', isa => 'HashRef[Str]',
152             init_arg => undef,
153             lazy => 1,
154             default => sub {
155             my $self = shift;
156             my $breaks_data = $self->zilla->distmeta->{x_breaks};
157             defined $breaks_data ? $breaks_data : {};
158             },
159             );
160              
161             sub _test_count {
162 6     6   16 my $self = shift;
163              
164             # 1 for each conflicts module, or 1 for none
165 6         254 my $test_count = $self->conflicts_module;
166 6 100       23 ++$test_count if not $test_count;
167              
168             # ...and one for the x_breaks section, even if empty
169 6         13 ++$test_count;
170 6         62 return $test_count;
171             }
172              
173             __PACKAGE__->meta->make_immutable;
174              
175             #pod =pod
176             #pod
177             #pod =head1 SYNOPSIS
178             #pod
179             #pod In your F<dist.ini>:
180             #pod
181             #pod [Breaks]
182             #pod Foo = <= 1.1 ; Foo at 1.1 or lower will break when I am installed
183             #pod
184             #pod [Test::CheckBreaks]
185             #pod conflicts_module = Moose::Conflicts
186             #pod
187             #pod =head1 DESCRIPTION
188             #pod
189             #pod This is a L<Dist::Zilla> plugin that runs at the
190             #pod L<gather files|Dist::Zilla::Role::FileGatherer> stage, providing a test file
191             #pod that runs last in your test suite and checks for conflicting modules, as
192             #pod indicated by C<x_breaks> in your distribution metadata.
193             #pod (See the F<t/zzz-check-breaks.t> test in this distribution for an example.)
194             #pod
195             #pod C<x_breaks> entries are expected to be
196             #pod L<version ranges|CPAN::Meta::Spec/Version Ranges>, with one
197             #pod addition, for backwards compatibility with
198             #pod L<[Conflicts]|Dist::Zilla::Plugin::Conflicts>: if a bare version number is
199             #pod specified, it is interpreted as C<< '<= $version' >> (to preserve the intent
200             #pod that versions at or below the version specified are those considered to be
201             #pod broken). It is possible that this interpretation will be removed in the
202             #pod future; almost certainly before C<breaks> becomes a formal part of the meta
203             #pod specification.
204             #pod
205             #pod =head1 CONFIGURATION
206             #pod
207             #pod =head2 C<conflicts_module>
208             #pod
209             #pod The name of the conflicts module to load and upon which to invoke the C<check_conflicts>
210             #pod method. Defaults to the name of the main module with 'C<::Conflicts>'
211             #pod appended, such as what is generated by the
212             #pod L<[Conflicts]|Dist::Zilla::Plugin::Conflicts> plugin.
213             #pod
214             #pod If your distribution uses L<Moose> but does not itself generate a conflicts
215             #pod plugin, then C<Moose::Conflicts> is an excellent choice, as there are numerous
216             #pod interoperability conflicts catalogued in that module.
217             #pod
218             #pod There is no error if the module does not exist. This test does not require
219             #pod L<[Conflicts]|Dist::Zilla::Plugin::Conflicts> to be used in your distribution;
220             #pod this is only a feature added for backwards compatibility.
221             #pod
222             #pod This option can be used more than once starting with version 0.017.
223             #pod
224             #pod =head2 C<no_forced_deps>
225             #pod
226             #pod Suitable for distributions that do not wish to add a C<test requires>
227             #pod prerequisite on L<CPAN::Meta::Requirements> and L<CPAN::Meta::Check> --
228             #pod instead, the dependencies will be added as C<test suggests>, and the generated
229             #pod test will gracefully skip checks if these modules are not available.
230             #pod
231             #pod Available since version 0.015.
232             #pod
233             #pod =for Pod::Coverage mvp_multivalue_args filename gather_files munge_files register_prereqs
234             #pod
235             #pod =head1 BACKGROUND
236             #pod
237             #pod =for stopwords irc
238             #pod
239             #pod I came upon this idea for a test after handling a
240             #pod L<bug report|https://rt.cpan.org/Ticket/Display.html?id=92780>
241             #pod I've seen many times before when dealing with L<Moose> code: "hey, when I
242             #pod updated Moose, my other thing that uses Moose stopped working!" For quite
243             #pod some time Moose has generated breakage information in the form of the
244             #pod F<moose-outdated> executable and a check in F<Makefile.PL> (which uses the
245             #pod generated module C<Moose::Conflicts>), but the output is usually buried in the
246             #pod user's install log or way up in the console buffer, and so doesn't get acted
247             #pod on nearly as often as it should. I realized it would be a simple matter to
248             #pod re-run the executable at the very end of tests by crafting a filename that
249             #pod always sorts (and runs) last, and further that we could generate this test.
250             #pod This coincided nicely with conversations on irc C<#toolchain> about the
251             #pod C<x_breaks> metadata field and plans for its future. Therefore, this
252             #pod distribution, and its sister plugin L<[Breaks]|Dist::Zilla::Plugin::Breaks>
253             #pod were born!
254             #pod
255             #pod =head1 SEE ALSO
256             #pod
257             #pod =for :list
258             #pod * L<Dist::Zilla::Plugin::Breaks>
259             #pod * L<Dist::CheckConflicts>
260             #pod * L<The Annotated Lancaster Consensus|http://www.dagolden.com/index.php/2098/the-annotated-lancaster-consensus/> at "Improving on 'conflicts'"
261             #pod * L<Module::Install::CheckConflicts>
262             #pod
263             #pod =cut
264              
265             =pod
266              
267             =encoding UTF-8
268              
269             =head1 NAME
270              
271             Dist::Zilla::Plugin::Test::CheckBreaks - Generate a test that shows what modules you are breaking
272              
273             =head1 VERSION
274              
275             version 0.019
276              
277             =head1 SYNOPSIS
278              
279             In your F<dist.ini>:
280              
281             [Breaks]
282             Foo = <= 1.1 ; Foo at 1.1 or lower will break when I am installed
283              
284             [Test::CheckBreaks]
285             conflicts_module = Moose::Conflicts
286              
287             =head1 DESCRIPTION
288              
289             This is a L<Dist::Zilla> plugin that runs at the
290             L<gather files|Dist::Zilla::Role::FileGatherer> stage, providing a test file
291             that runs last in your test suite and checks for conflicting modules, as
292             indicated by C<x_breaks> in your distribution metadata.
293             (See the F<t/zzz-check-breaks.t> test in this distribution for an example.)
294              
295             C<x_breaks> entries are expected to be
296             L<version ranges|CPAN::Meta::Spec/Version Ranges>, with one
297             addition, for backwards compatibility with
298             L<[Conflicts]|Dist::Zilla::Plugin::Conflicts>: if a bare version number is
299             specified, it is interpreted as C<< '<= $version' >> (to preserve the intent
300             that versions at or below the version specified are those considered to be
301             broken). It is possible that this interpretation will be removed in the
302             future; almost certainly before C<breaks> becomes a formal part of the meta
303             specification.
304              
305             =head1 CONFIGURATION
306              
307             =head2 C<conflicts_module>
308              
309             The name of the conflicts module to load and upon which to invoke the C<check_conflicts>
310             method. Defaults to the name of the main module with 'C<::Conflicts>'
311             appended, such as what is generated by the
312             L<[Conflicts]|Dist::Zilla::Plugin::Conflicts> plugin.
313              
314             If your distribution uses L<Moose> but does not itself generate a conflicts
315             plugin, then C<Moose::Conflicts> is an excellent choice, as there are numerous
316             interoperability conflicts catalogued in that module.
317              
318             There is no error if the module does not exist. This test does not require
319             L<[Conflicts]|Dist::Zilla::Plugin::Conflicts> to be used in your distribution;
320             this is only a feature added for backwards compatibility.
321              
322             This option can be used more than once starting with version 0.017.
323              
324             =head2 C<no_forced_deps>
325              
326             Suitable for distributions that do not wish to add a C<test requires>
327             prerequisite on L<CPAN::Meta::Requirements> and L<CPAN::Meta::Check> --
328             instead, the dependencies will be added as C<test suggests>, and the generated
329             test will gracefully skip checks if these modules are not available.
330              
331             Available since version 0.015.
332              
333             =for Pod::Coverage mvp_multivalue_args filename gather_files munge_files register_prereqs
334              
335             =head1 BACKGROUND
336              
337             =for stopwords irc
338              
339             I came upon this idea for a test after handling a
340             L<bug report|https://rt.cpan.org/Ticket/Display.html?id=92780>
341             I've seen many times before when dealing with L<Moose> code: "hey, when I
342             updated Moose, my other thing that uses Moose stopped working!" For quite
343             some time Moose has generated breakage information in the form of the
344             F<moose-outdated> executable and a check in F<Makefile.PL> (which uses the
345             generated module C<Moose::Conflicts>), but the output is usually buried in the
346             user's install log or way up in the console buffer, and so doesn't get acted
347             on nearly as often as it should. I realized it would be a simple matter to
348             re-run the executable at the very end of tests by crafting a filename that
349             always sorts (and runs) last, and further that we could generate this test.
350             This coincided nicely with conversations on irc C<#toolchain> about the
351             C<x_breaks> metadata field and plans for its future. Therefore, this
352             distribution, and its sister plugin L<[Breaks]|Dist::Zilla::Plugin::Breaks>
353             were born!
354              
355             =head1 SEE ALSO
356              
357             =over 4
358              
359             =item *
360              
361             L<Dist::Zilla::Plugin::Breaks>
362              
363             =item *
364              
365             L<Dist::CheckConflicts>
366              
367             =item *
368              
369             L<The Annotated Lancaster Consensus|http://www.dagolden.com/index.php/2098/the-annotated-lancaster-consensus/> at "Improving on 'conflicts'"
370              
371             =item *
372              
373             L<Module::Install::CheckConflicts>
374              
375             =back
376              
377             =head1 SUPPORT
378              
379             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Dist-Zilla-Plugin-Test-CheckBreaks>
380             (or L<bug-Dist-Zilla-Plugin-Test-CheckBreaks@rt.cpan.org|mailto:bug-Dist-Zilla-Plugin-Test-CheckBreaks@rt.cpan.org>).
381              
382             There is also a mailing list available for users of this distribution, at
383             L<http://dzil.org/#mailing-list>.
384              
385             There is also an irc channel available for users of this distribution, at
386             L<C<#distzilla> on C<irc.perl.org>|irc://irc.perl.org/#distzilla>.
387              
388             I am also usually active on irc, as 'ether' at C<irc.perl.org>.
389              
390             =head1 AUTHOR
391              
392             Karen Etheridge <ether@cpan.org>
393              
394             =head1 CONTRIBUTOR
395              
396             =for stopwords Olivier Mengué
397              
398             Olivier Mengué <dolmen@cpan.org>
399              
400             =head1 COPYRIGHT AND LICENCE
401              
402             This software is copyright (c) 2014 by Karen Etheridge.
403              
404             This is free software; you can redistribute it and/or modify it under
405             the same terms as the Perl 5 programming language system itself.
406              
407             =cut
408              
409             __DATA__
410             ___[ test-check-breaks ]___
411             use strict;
412             use warnings;
413              
414             # this test was generated with {{ ref $plugin }} {{ $plugin->VERSION }}
415              
416             use Test::More tests => {{ $test_count }};
417              
418             SKIP: {
419             {{
420             keys %$modules
421             ? join("}\n\nSKIP: {\n", map {
422             my $module = $_;
423             my $filename = $modules->{$module};
424             <<"CHECK_CONFLICTS";
425             eval { +require $module; ${module}->check_conflicts };
426             skip('no $module module found', 1) if not \$INC{'$filename'};
427              
428             diag \$@ if \$@;
429             pass 'conflicts checked via $module';
430             CHECK_CONFLICTS
431             } sort keys %$modules)
432             : " skip 'no conflicts module found to check against', 1;\n";
433             }}}
434              
435             {{
436             if (keys %$breaks)
437             {
438             my $dumped = Data::Dumper->new([ $breaks ], [ 'breaks' ])
439             ->Sortkeys(1)
440             ->Indent(1)
441             ->Useqq(1)
442             ->Dump;
443             my $dist_name = $dist->name;
444              
445             my $breaks_content = <<CHECK_BREAKS_header;
446             # this data duplicates x_breaks in META.json
447             my $dumped
448             CHECK_BREAKS_header
449              
450             $breaks_content .= $no_forced_deps ? <<CHECK_BREAKS_prereq_nodeps
451             skip 'This information-only test requires CPAN::Meta::Requirements', 1
452             if not eval { +require CPAN::Meta::Requirements };
453             skip 'This information-only test requires CPAN::Meta::Check $cmc_prereq', 1
454             if not eval { +require CPAN::Meta::Check; CPAN::Meta::Check->VERSION($cmc_prereq) };
455             CHECK_BREAKS_prereq_nodeps
456             : <<CHECK_BREAKS_prereq_deps;
457             use CPAN::Meta::Requirements;
458             use CPAN::Meta::Check $cmc_prereq;
459             CHECK_BREAKS_prereq_deps
460              
461             $breaks_content .= "\n" . <<'CHECK_BREAKS_checks';
462             my $reqs = CPAN::Meta::Requirements->new;
463             $reqs->add_string_requirement($_, $breaks->{$_}) foreach keys %$breaks;
464              
465             our $result = CPAN::Meta::Check::check_requirements($reqs, 'conflicts');
466              
467             if (my @breaks = grep { defined $result->{$_} } keys %$result)
468             {
469             CHECK_BREAKS_checks
470              
471             $breaks_content .= " diag 'Breakages found with $dist_name:';\n"
472             . <<'CHECK_BREAKS_diag';
473             diag "$result->{$_}" for sort @breaks;
474             diag "\n", 'You should now update these modules!';
475             }
476              
477             pass 'checked x_breaks data';
478             CHECK_BREAKS_diag
479              
480             if ($no_forced_deps) {
481             $breaks_content =~ s/^(?=.)/ /mg;
482             $breaks_content = 'SKIP: {' . "\n" . $breaks_content . '}';
483             }
484             $breaks_content;
485             }
486             else { q{pass 'no x_breaks data to check';} . "\n" }
487             }}