File Coverage

lib/Dist/Zilla/Plugin/Test/ReportMetadata.pm
Criterion Covered Total %
statement 61 61 100.0
branch 3 6 50.0
condition 1 3 33.3
subroutine 15 15 100.0
pod 3 3 100.0
total 83 88 94.3


line stmt bran cond sub pod time code
1             package Dist::Zilla::Plugin::Test::ReportMetadata;
2              
3 1     1   1170524 use 5.008;
  1         5  
4 1     1   5 use version; our $VERSION = qv( sprintf '0.4.%d', q$Rev: 1 $ =~ /\d+/gmx );
  1         2  
  1         7  
5              
6 1     1   118 use Data::Dumper;
  1         2  
  1         79  
7 1     1   5 use Dist::Zilla 4 ();
  1         20  
  1         24  
8 1     1   5 use Moose;
  1         2  
  1         6  
9 1     1   6011 use Sub::Exporter::ForMethods;
  1         2  
  1         13  
10              
11             use Data::Section 0.200002 # encoding and bytes
12 1     1   160 { installer => Sub::Exporter::ForMethods::method_installer }, '-setup';
  1         23  
  1         7  
13              
14             with 'Dist::Zilla::Role::FileGatherer', 'Dist::Zilla::Role::PrereqSource';
15              
16             has 'env_vars' => is => 'ro', traits => [ 'Array' ], default => sub {
17             [ qw( AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING
18             NONINTERACTIVE_TESTING PERL_CPAN_REPORTER_CONFIG
19             PERL_CR_SMOKER_CURRENT PERL5_CPAN_IS_RUNNING
20             PERL5_CPANPLUS_IS_VERSION TEST_CRITIC TEST_SPELLING ) ] },
21             handles => { 'display_vars' => 'elements', }, init_arg => 'env_var';
22              
23             has 'excludes' => is => 'ro', traits => [ 'Array' ], default => sub { [] },
24             handles => { 'excluded_modules' => 'elements', }, init_arg => 'exclude';
25              
26             has 'includes' => is => 'ro', traits => [ 'Array' ], default => sub { [] },
27             handles => { 'included_modules' => 'elements', }, init_arg => 'include';
28              
29             has 'verify_prereqs' => is => 'ro', isa => 'Bool', default => 1;
30              
31             sub _dump_filename {
32 2     2   53 return 't/00report-metadata.dd';
33             }
34              
35             sub _dump_prereqs {
36 2     2   5 my $self = shift;
37 2         74 my $prereqs = $self->zilla->prereqs->as_string_hash;
38 2         2021 my $dumper = Data::Dumper->new( [ $prereqs ], [ 'x' ] );
39 2         105 my $dumped = $dumper->Purity( 1 )->Sortkeys( 1 )->Terse( 0 )->Dump();
40              
41 2         390 return "do { my ${dumped} \$x;\n }";
42             }
43              
44             sub _format_list {
45 3     3   8 return join "\n", map { " ${_}" } @_;
  13         64  
46             }
47              
48             sub _munge_test {
49 1     1   13 my ($self, $guts) = @_;
50              
51 1 50       37 $guts =~ s{INSERT_VERSION_HERE}{$self->VERSION || '<self>'}e;
  1         19  
52 1         35 $guts =~ s{INSERT_DD_FILENAME_HERE}{$self->_dump_filename}e;
  1         5  
53 1         34 $guts =~ s{INSERT_DISPLAY_VARS_HERE}{_format_list( $self->display_vars )}e;
  1         61  
54 1         36 $guts =~ s{INSERT_INCLUDED_MODULES_HERE}{_format_list( $self->included_modules )}e;
  1         57  
55 1         36 $guts =~ s{INSERT_EXCLUDED_MODULES_HERE}{_format_list( $self->excluded_modules )}e;
  1         56  
56 1 50       35 $guts =~ s{INSERT_VERIFY_PREREQS_CONFIG}{$self->verify_prereqs ? 1 : 0}e;
  1         47  
57 1         38 return $guts;
58             }
59              
60             sub gather_files {
61 1     1 1 65458 my $self = shift; my $data = $self->merged_section_data;
  1         7  
62              
63 1 50 33     7932 $data and %{ $data } or return;
  1         9  
64              
65 1         11 require Dist::Zilla::File::InMemory;
66              
67 1         3 for my $filename (keys %{ $data }) {
  1         4  
68             $self->add_file
69             ( Dist::Zilla::File::InMemory->new
70             ( { name => $filename,
71 1         3 content => $self->_munge_test( ${ $data->{ $filename } } ) } ) );
  1         6  
72             }
73              
74 1         1537 require Dist::Zilla::File::FromCode;
75              
76             $self->add_file
77             ( Dist::Zilla::File::FromCode->new
78             ( { name => $self->_dump_filename,
79 1     2   60283 code => sub { $self->_dump_prereqs }, } ) );
  2         270586  
80              
81 1         624 return;
82             }
83              
84             sub mvp_multivalue_args {
85 1     1 1 217 return qw( env_var exclude include );
86             }
87              
88             sub register_prereqs {
89 1     1 1 498397 my $self = shift; my $zilla = $self->zilla;
  1         39  
90              
91 1         12 $zilla->register_prereqs
92             ( { phase => 'test', type => 'requires', },
93             'File::Spec' => 0,
94             'Module::Metadata' => 0,
95             'Sys::Hostname' => 0, );
96              
97 1         224 $zilla->register_prereqs
98             ( { phase => 'test', type => 'recommends', },
99             'CPAN::Meta' => '2.120900', );
100              
101 1         194 return;
102             }
103              
104             __PACKAGE__->meta->make_immutable;
105              
106             1;
107              
108             =pod
109              
110             =encoding utf-8
111              
112             =begin html
113              
114             <a href="https://travis-ci.org/pjfl/p5-dist-zilla-plugin-test-reportmetadata"><img src="https://travis-ci.org/pjfl/p5-dist-zilla-plugin-test-reportmetadata.svg?branch=master" alt="Travis CI Badge"></a>
115             <a href="http://badge.fury.io/pl/Dist-Zilla-Plugin-Test-ReportMetadata"><img src="https://badge.fury.io/pl/Dist-Zilla-Plugin-Test-ReportMetadata.svg" alt="CPAN Badge"></a>
116             <a href="http://cpants.cpanauthors.org/dist/Dist-Zilla-Plugin-Test-ReportMetadata"><img src="http://cpants.cpanauthors.org/dist/Dist-Zilla-Plugin-Test-ReportMetadata.png" alt="Kwalitee Badge"></a>
117              
118             =end html
119              
120             =head1 Name
121              
122             Dist::Zilla::Plugin::Test::ReportMetadata - Report on prerequisite versions during automated testing
123              
124             =head1 Synopsis
125              
126             # In dist.ini
127             [Test::ReportMetadata]
128              
129             =head1 Description
130              
131             The is a clone of L<Dist::Zilla::Plugin::Test::ReportPrereqs> but with the
132             dependency on L<ExtUtils::MakeMaker> replaced with one on
133             L<Module::Metadata>. If you are using L<Module::Build> then L<Module::Metadata>
134             is already a dependency
135              
136             Versions are reported based on the result of the C<version> attribute from
137             L<Module::Metadata>
138              
139             Additionally a selection of environment variables are also displayed
140              
141             =head1 Configuration and Environment
142              
143             Defines the following attributes;
144              
145             =over 3
146              
147             =item C<env_vars>
148              
149             An array reference of environment variable names displayed on the test report.
150             Set using the multivalued initialisation argument C<env_var>. If the list
151             has no values then nothing is printed. An empty list can be set with
152              
153             [Test::ReportMetadata]
154             env_var = none
155              
156             =item C<excludes>
157              
158             An array reference of module names to exclude from the test report.
159             Set using the multivalued initialisation argument C<exclude>
160              
161             =item C<includes>
162              
163             An array reference of module names to include in the test report.
164             Set using the multivalued initialisation argument C<include>
165              
166             =item C<verify_prereqs>
167              
168             A boolean defaulting to true. If true emits lots of warnings if prerequisites
169             are not satisfied
170              
171             =back
172              
173             =head1 Subroutines/Methods
174              
175             =head2 C<gather_files>
176              
177             Required by L<Dist::Zilla::Role::FileGatherer>
178              
179             =head2 C<mvp_multivalue_args>
180              
181             Returns a list of configuration attribute names that are treated as
182             multi valued
183              
184             =head2 C<register_prereqs>
185              
186             Required by L<Dist::Zilla::Role::PrereqSource>
187              
188             =head1 Diagnostics
189              
190             None
191              
192             =head1 Dependencies
193              
194             =over 3
195              
196             =item L<Data::Dumper>
197              
198             =item L<Data::Section>
199              
200             =item L<Dist::Zilla>
201              
202             =item L<Moose>
203              
204             =item L<Sub::Exporter::ForMethods>
205              
206             =back
207              
208             =head1 Incompatibilities
209              
210             There are no known incompatibilities in this module
211              
212             =head1 Bugs and Limitations
213              
214             There are no known bugs in this module. Please report problems to
215             http://rt.cpan.org/NoAuth/Bugs.html?Dist=Dist-Zilla-Plugin-Test-ReportMetadata.
216             Patches are welcome
217              
218             =head1 Acknowledgements
219              
220             Larry Wall - For the Perl programming language
221              
222             =head1 Author
223              
224             Peter Flanigan, C<< <pjfl@cpan.org> >>
225              
226             =head1 License and Copyright
227              
228             Copyright (c) 2015 Peter Flanigan. All rights reserved
229              
230             This program is free software; you can redistribute it and/or modify it
231             under the same terms as Perl itself. See L<perlartistic>
232              
233             This program is distributed in the hope that it will be useful,
234             but WITHOUT WARRANTY; without even the implied warranty of
235             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
236              
237             =cut
238              
239             # Local Variables:
240             # mode: perl
241             # tab-width: 3
242             # End:
243             # vim: expandtab shiftwidth=3:
244              
245             __DATA__
246             ___[ t/00report-metadata.t ]___
247             #!perl
248             # Generated by Dist::Zilla::Plugin::Test::ReportMetadata INSERT_VERSION_HERE
249              
250             use strict;
251             use warnings;
252              
253             use File::Spec;
254             use Module::Metadata;
255             use Sys::Hostname;
256              
257             # Instead of importing from Test::More
258             sub diag (;@) {
259             warn @_; return;
260             }
261              
262             sub pass () {
263             print "ok 1\n1..1\n"; return;
264             }
265              
266             # Hide optional CPAN::Meta modules from prereq scanner
267             # and check if they are available
268             my $CPAN_META = 'CPAN::Meta';
269             my $CPAN_META_PRE = 'CPAN::Meta::Prereqs';
270             my @DISPLAY_VARS = grep { $_ ne 'none' } qw( INSERT_DISPLAY_VARS_HERE );
271             my $DO_VERIFY_PREREQS = INSERT_VERIFY_PREREQS_CONFIG; # Verify requirements?
272             my @EXCLUDE = qw( INSERT_EXCLUDED_MODULES_HERE );
273             my $HOST = lc hostname;
274             my @INCLUDE = qw( INSERT_INCLUDED_MODULES_HERE );
275             my $LAX_VERSION_RE = # From $version::LAX
276             qr{(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )?
277             |
278             (?:\.[0-9]+) (?:_[0-9]+)?
279             ) | (?:
280             v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )?
281             |
282             (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)?
283             )
284             )}x;
285             my $OSNAME = lc $^O;
286             # Add static prereqs to the included modules list
287             my $STATIC_PREREQS = do 'INSERT_DD_FILENAME_HERE';
288              
289             my $diag_env = sub {
290             my $k = shift; my $v = exists $ENV{ $k } ? $ENV{ $k } : 'undef';
291              
292             return diag sprintf " \$%-30s %s\n", $k, $v;
293             };
294             my $max = sub {
295             my $v = shift; $v = ( $_ > $v ) ? $_ : $v for @_; return $v;
296             };
297             my $merge_prereqs = sub {
298             my ($collector, $prereqs) = @_;
299              
300             ref $collector eq $CPAN_META_PRE # CPAN::Meta::Prereqs object
301             and return $collector->with_merged_prereqs
302             ( CPAN::Meta::Prereqs->new( $prereqs ) );
303              
304             for my $phase (keys %{ $prereqs }) { # Raw hashrefs
305             for my $type (keys %{ $prereqs->{ $phase } }) {
306             for my $module (keys %{ $prereqs->{ $phase }{ $type } }) {
307             $collector->{ $phase }{ $type }{ $module }
308             = $prereqs->{ $phase }{ $type }{ $module };
309             }
310             }
311             }
312              
313             return $collector;
314             };
315             my $cpan_meta_ver = "${CPAN_META}->VERSION( '2.120900' )";
316             my $has_cpan_meta = eval "require ${CPAN_META}; ${cpan_meta_ver}"
317             && eval "require ${CPAN_META_PRE}";
318             # Merge all prereqs (either with ::Prereqs or a hashref)
319             my $full_prereqs = $merge_prereqs->
320             ( ( $has_cpan_meta ? $CPAN_META_PRE->new : {} ), $STATIC_PREREQS );
321             # Add dynamic prereqs to the included modules list (if we can)
322             my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml';
323              
324             if ($source and $has_cpan_meta) {
325             if (my $meta = eval { CPAN::Meta->load_file( $source ) }) {
326             $full_prereqs = $merge_prereqs->( $full_prereqs, $meta->prereqs );
327             }
328             }
329             else { $source = 'static metadata' }
330              
331             my @full_reports;
332             my @dep_errors;
333             my $req_hash = $has_cpan_meta ? $full_prereqs->as_string_hash : $full_prereqs;
334              
335             for my $mod (@INCLUDE) { # Add static includes into a fake section
336             $req_hash->{other}{modules}{ $mod } = 0;
337             }
338              
339             for my $phase (qw( configure build test runtime develop other )) {
340             $req_hash->{ $phase } or next;
341             $phase eq 'develop' and not $ENV{AUTHOR_TESTING} and next;
342              
343             for my $type ( qw( requires recommends suggests conflicts modules ) ) {
344             $req_hash->{ $phase }{ $type } or next;
345              
346             my $title = (ucfirst $phase).' '.(ucfirst $type);
347             my @reports = [ qw( Module Want Have ) ];
348              
349             for my $mod (sort keys %{ $req_hash->{ $phase }{ $type } }) {
350             $mod eq 'perl' and next; grep { $_ eq $mod } @EXCLUDE and next;
351              
352             my $file = $mod; $file =~ s{ :: }{/}gmx; $file .= '.pm';
353             my ($prefix) = grep { -e File::Spec->catfile( $_, $file ) } @INC;
354             my $want = $req_hash->{ $phase }{ $type }{ $mod };
355              
356             defined $want or $want = 'undef';
357             not $want and $want == 0 and $want = 'any';
358              
359             my $req_string = $want eq 'any'
360             ? 'any version required' : "version '${want}' required";
361              
362             if ($prefix) {
363             my $path = File::Spec->catfile( $prefix, $file );
364             my $info = Module::Metadata->new_from_file( $path );
365             my $have = $info->version;
366              
367             defined $have or $have = 'undef';
368             push @reports, [ $mod, $want, $have ];
369              
370             if ($DO_VERIFY_PREREQS and $has_cpan_meta and $type eq 'requires') {
371             if ($have !~ m{ \A $LAX_VERSION_RE \z }mx) {
372             push @dep_errors,
373             "${mod} version '${have}' cannot be parsed (${req_string})";
374             }
375             elsif ( !$full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) {
376             push @dep_errors,
377             "${mod} version '${have}' is not in required range '${want}'";
378             }
379             }
380             }
381             else {
382             push @reports, [ $mod, $want, 'missing' ];
383              
384             $DO_VERIFY_PREREQS and $type eq 'requires'
385             and push @dep_errors, "${mod} is not installed (${req_string})";
386             }
387             }
388              
389             if (@reports) {
390             push @full_reports, "=== ${title} ===\n\n";
391              
392             my $ml = $max->( map { length $_->[ 0 ] } @reports );
393             my $wl = $max->( map { length $_->[ 1 ] } @reports );
394             my $hl = $max->( map { length $_->[ 2 ] } @reports );
395              
396             if ($type eq 'modules') {
397             splice @reports, 1, 0, [ '-' x $ml, q(), '-' x $hl ];
398             push @full_reports, map { sprintf " %*s %*s\n", -$ml,
399             $_->[ 0 ], $hl, $_->[ 2 ] } @reports;
400             }
401             else {
402             splice @reports, 1, 0, [ '-' x $ml, '-' x $wl, '-' x $hl ];
403             push @full_reports, map { sprintf " %*s %*s %*s\n", -$ml,
404             $_->[ 0 ], $wl, $_->[ 1 ], $hl,
405             $_->[ 2 ] } @reports;
406             }
407              
408             push @full_reports, "\n";
409             }
410             }
411             }
412              
413             if (@DISPLAY_VARS) {
414             diag "\nOS: ${OSNAME}, Host: ${HOST}\n";
415             diag "\n=== Environment variables ===\n\n";
416              
417             $diag_env->( $_ ) for (@DISPLAY_VARS);
418             }
419              
420             if (@full_reports) {
421             diag "\nVersions for all modules listed in ${source} (including optional ones):\n\n", @full_reports;
422             }
423              
424             if (@dep_errors) {
425             diag join "\n",
426             "\n*** WARNING WARNING WARNING WARNING WARNING WARNING ***\n",
427             "The following REQUIRED prerequisites were not satisfied:\n",
428             @dep_errors, "\n";
429             }
430              
431             pass;
432             exit 0;