File Coverage

blib/lib/Dist/Zilla/Plugin/Test/CheckBreaks.pm
Criterion Covered Total %
statement 62 62 100.0
branch 10 10 100.0
condition 2 3 66.6
subroutine 18 18 100.0
pod 0 5 0.0
total 92 98 93.8


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