File Coverage

blib/lib/Test/DZil.pm
Criterion Covered Total %
statement 74 75 98.6
branch 17 22 77.2
condition 6 7 85.7
subroutine 17 17 100.0
pod 5 5 100.0
total 119 126 94.4


line stmt bran cond sub pod time code
1             package Test::DZil 6.030;
2             # ABSTRACT: tools for testing Dist::Zilla plugins
3              
4 50     50   5330369 use Dist::Zilla::Pragmas;
  50         168  
  50         350  
5              
6 50     50   24212 use Params::Util qw(_HASH0);
  50         272707  
  50         3421  
7 50     50   23864 use JSON::MaybeXS;
  50         286993  
  50         3145  
8 50     50   420 use Scalar::Util qw(blessed);
  50         143  
  50         2036  
9 50     50   32336 use Test::Deep ();
  50         352010  
  50         1454  
10 50     50   30039 use YAML::Tiny;
  50         299894  
  50         4514  
11              
12 50         628 use Sub::Exporter -setup => {
13             exports => [
14             is_filelist =>
15             is_yaml =>
16             is_json =>
17             dist_ini => \'_dist_ini',
18             simple_ini => \'_simple_ini',
19             Builder =>
20             Minter =>
21             ],
22             groups => [ default => [ qw(-all) ] ],
23 50     50   32333 };
  50         349053  
24              
25 50     50   52261 use namespace::autoclean -except => 'import';
  50         836478  
  50         264  
26              
27             #pod =head1 DESCRIPTION
28             #pod
29             #pod Test::DZil provides routines for writing tests for Dist::Zilla plugins.
30             #pod
31             #pod =cut
32              
33             #pod =func Builder
34             #pod
35             #pod =func Minter
36             #pod
37             #pod my $tzil = Builder->from_config(...);
38             #pod
39             #pod These return class names that subclass L<Dist::Zilla::Dist::Builder> or
40             #pod L<Dist::Zilla::Dist::Minter>, respectively, with the L<Dist::Zilla::Tester>
41             #pod behavior added.
42             #pod
43             #pod =cut
44              
45             sub Builder {
46 166     166 1 536973 require Dist::Zilla::Tester;
47 166         1375 Dist::Zilla::Tester::builder();
48             }
49              
50             sub Minter {
51 1     1 1 593 require Dist::Zilla::Tester;
52 1         8 Dist::Zilla::Tester::minter();
53             }
54              
55             #pod =func is_filelist
56             #pod
57             #pod is_filelist( \@files_we_have, \@files_we_want, $desc );
58             #pod
59             #pod This test assertion compares two arrayrefs of filenames, taking care of slash
60             #pod normalization and sorting. C<@files_we_have> may also contain objects that
61             #pod do L<Dist::Zilla::Role::File>.
62             #pod
63             #pod =cut
64              
65             sub is_filelist {
66 37     37 1 452 my ($have, $want, $comment) = @_;
67              
68 37         141 my @want = @$want;
69 37 100 66     109 my @have = map { my $str = (blessed $_ and
  227         3112  
70             $_->DOES('Dist::Zilla::Role::File'))
71             ? $_->name
72             : $_;
73 227         463 $str =~ s{\\}{/}g; $str } @$have;
  227         504  
74              
75 37         128 local $Test::Builder::Level = $Test::Builder::Level + 1;
76 37         493 Test::Deep::cmp_bag(\@have, \@want, $comment);
77             }
78              
79             #pod =func is_yaml
80             #pod
81             #pod is_yaml( $yaml_string, $want_struct, $comment );
82             #pod
83             #pod This test assertion deserializes the given YAML string and does a
84             #pod C<L<cmp_deeply|Test::Deep/cmp_deeply>>.
85             #pod
86             #pod =cut
87              
88             sub is_yaml {
89 2     2 1 5735 my ($yaml, $want, $comment) = @_;
90              
91 2 50       12 my $have = YAML::Tiny->read_string($yaml)
92             or die "Cannot decode YAML";
93              
94 2         2665 local $Test::Builder::Level = $Test::Builder::Level + 1;
95 2         11 Test::Deep::cmp_deeply($have->[0], $want, $comment);
96             }
97              
98             #pod =func is_json
99             #pod
100             #pod is_json( $json_string, $want_struct, $comment );
101             #pod
102             #pod This test assertion deserializes the given JSON string and does a
103             #pod C<L<cmp_deeply|Test::Deep/cmp_deeply>>.
104             #pod
105             #pod =cut
106              
107             sub is_json {
108 2     2 1 721 my ($json, $want, $comment) = @_;
109              
110 2 50       18 my $have = JSON::MaybeXS->new(ascii => 1)->decode($json)
111             or die "Cannot decode JSON";
112              
113 2         89 local $Test::Builder::Level = $Test::Builder::Level + 1;
114 2         10 Test::Deep::cmp_deeply($have, $want, $comment);
115             }
116              
117             sub _build_ini_builder {
118 95     95   218 my ($starting_core) = @_;
119 95   100     775 $starting_core ||= {};
120              
121             sub {
122 161     161   1574 my (@arg) = @_;
123 161 100       1202 my $new_core = _HASH0($arg[0]) ? shift(@arg) : {};
124              
125 161         2045 my $core_config = { %$starting_core, %$new_core };
126              
127 161         647 my $config = '';
128              
129 161         1428 for my $key (sort keys %$core_config) {
130             my @values = ref $core_config->{ $key }
131 0         0 ? @{ $core_config->{ $key } }
132 973 50       2789 : $core_config->{ $key };
133              
134 973         1628 $config .= "$key = $_\n" for grep {defined} @values;
  973         3968  
135             }
136              
137 161 50       1000 $config .= "\n" if length $config;
138              
139 161         491 for my $line (@arg) {
140 473 100       1567 my @plugin = ref $line ? @$line : ($line, {});
141 473         952 my $moniker = shift @plugin;
142 473 100       1527 my $name = _HASH0($plugin[0]) ? undef : shift @plugin;
143 473   100     1435 my $payload = shift(@plugin) || {};
144              
145 473 50       1163 Carp::confess("bogus plugin configuration: too many args") if @plugin;
146              
147 473         1156 $config .= '[' . $moniker;
148 473 100       1241 $config .= ' / ' . $name if defined $name;
149 473         798 $config .= "]\n";
150              
151 473         1476 for my $key (sort keys %$payload) {
152             my @values = ref $payload->{ $key }
153 17         49 ? @{ $payload->{ $key } }
154 319 100       1063 : $payload->{ $key };
155              
156 319         591 $config .= "$key = $_\n" for grep {defined} @values;
  336         1459  
157             }
158              
159 473         1273 $config .= "\n";
160             }
161              
162 161         2908 return $config;
163             }
164 95         802 }
165              
166             #pod =func dist_ini
167             #pod
168             #pod my $ini_text = dist_ini(\%root_config, @plugins);
169             #pod
170             #pod This routine returns a string that could be used to populate a simple
171             #pod F<dist.ini> file. The C<%root_config> gives data for the "root" section of the
172             #pod configuration. To provide a line multiple times, provide an arrayref. For
173             #pod example, the root section could read:
174             #pod
175             #pod {
176             #pod name => 'Dist-Sample',
177             #pod author => [
178             #pod 'J. Smith <jsmith@example.com>',
179             #pod 'Q. Smith <qsmith@example.com>',
180             #pod ],
181             #pod }
182             #pod
183             #pod The root section is optional.
184             #pod
185             #pod Plugins can be given in a few ways:
186             #pod
187             #pod =begin :list
188             #pod
189             #pod = C<"PluginMoniker">
190             #pod
191             #pod = C<[ "PluginMoniker" ]>
192             #pod
193             #pod These become C<[PluginMoniker]>
194             #pod
195             #pod = C<[ "PluginMoniker", "PluginName" ]>
196             #pod
197             #pod This becomes C<[PluginMoniker / PluginName]>
198             #pod
199             #pod = C<[ "PluginMoniker", { ... } ]>
200             #pod
201             #pod = C<[ "PluginMoniker", "PluginName", { ... } ]>
202             #pod
203             #pod These use the given hashref as the parameters inside the section, with the same
204             #pod semantics as the root section.
205             #pod
206             #pod =end :list
207             #pod
208             #pod =cut
209              
210             sub _dist_ini {
211 46     46   11004 _build_ini_builder;
212             }
213              
214             #pod =func simple_ini
215             #pod
216             #pod This behaves exactly like C<dist_ini>, but it merges any given root config into
217             #pod a starter config, which means that you can often skip any explicit root config.
218             #pod The starter config may change slightly over time, but is something like this:
219             #pod
220             #pod {
221             #pod name => 'DZT-Sample',
222             #pod abstract => 'Sample DZ Dist',
223             #pod version => '0.001',
224             #pod author => 'E. Xavier Ample <example@example.org>',
225             #pod license => 'Perl_5',
226             #pod copyright_holder => 'E. Xavier Ample',
227             #pod }
228             #pod
229             #pod =cut
230              
231             sub _simple_ini {
232 49     49   13424 _build_ini_builder({
233             name => 'DZT-Sample',
234             abstract => 'Sample DZ Dist',
235             version => '0.001',
236             author => 'E. Xavier Ample <example@example.org>',
237             license => 'Perl_5',
238             copyright_holder => 'E. Xavier Ample',
239             });
240             }
241              
242             1;
243              
244             __END__
245              
246             =pod
247              
248             =encoding UTF-8
249              
250             =head1 NAME
251              
252             Test::DZil - tools for testing Dist::Zilla plugins
253              
254             =head1 VERSION
255              
256             version 6.030
257              
258             =head1 DESCRIPTION
259              
260             Test::DZil provides routines for writing tests for Dist::Zilla plugins.
261              
262             =head1 PERL VERSION
263              
264             This module should work on any version of perl still receiving updates from
265             the Perl 5 Porters. This means it should work on any version of perl released
266             in the last two to three years. (That is, if the most recently released
267             version is v5.40, then this module should work on both v5.40 and v5.38.)
268              
269             Although it may work on older versions of perl, no guarantee is made that the
270             minimum required version will not be increased. The version may be increased
271             for any reason, and there is no promise that patches will be accepted to lower
272             the minimum required perl.
273              
274             =head1 FUNCTIONS
275              
276             =head2 Builder
277              
278             =head2 Minter
279              
280             my $tzil = Builder->from_config(...);
281              
282             These return class names that subclass L<Dist::Zilla::Dist::Builder> or
283             L<Dist::Zilla::Dist::Minter>, respectively, with the L<Dist::Zilla::Tester>
284             behavior added.
285              
286             =head2 is_filelist
287              
288             is_filelist( \@files_we_have, \@files_we_want, $desc );
289              
290             This test assertion compares two arrayrefs of filenames, taking care of slash
291             normalization and sorting. C<@files_we_have> may also contain objects that
292             do L<Dist::Zilla::Role::File>.
293              
294             =head2 is_yaml
295              
296             is_yaml( $yaml_string, $want_struct, $comment );
297              
298             This test assertion deserializes the given YAML string and does a
299             C<L<cmp_deeply|Test::Deep/cmp_deeply>>.
300              
301             =head2 is_json
302              
303             is_json( $json_string, $want_struct, $comment );
304              
305             This test assertion deserializes the given JSON string and does a
306             C<L<cmp_deeply|Test::Deep/cmp_deeply>>.
307              
308             =head2 dist_ini
309              
310             my $ini_text = dist_ini(\%root_config, @plugins);
311              
312             This routine returns a string that could be used to populate a simple
313             F<dist.ini> file. The C<%root_config> gives data for the "root" section of the
314             configuration. To provide a line multiple times, provide an arrayref. For
315             example, the root section could read:
316              
317             {
318             name => 'Dist-Sample',
319             author => [
320             'J. Smith <jsmith@example.com>',
321             'Q. Smith <qsmith@example.com>',
322             ],
323             }
324              
325             The root section is optional.
326              
327             Plugins can be given in a few ways:
328              
329             =over 4
330              
331             =item C<"PluginMoniker">
332              
333             =item C<[ "PluginMoniker" ]>
334              
335             These become C<[PluginMoniker]>
336              
337             =item C<[ "PluginMoniker", "PluginName" ]>
338              
339             This becomes C<[PluginMoniker / PluginName]>
340              
341             =item C<[ "PluginMoniker", { ... } ]>
342              
343             =item C<[ "PluginMoniker", "PluginName", { ... } ]>
344              
345             These use the given hashref as the parameters inside the section, with the same
346             semantics as the root section.
347              
348             =back
349              
350             =head2 simple_ini
351              
352             This behaves exactly like C<dist_ini>, but it merges any given root config into
353             a starter config, which means that you can often skip any explicit root config.
354             The starter config may change slightly over time, but is something like this:
355              
356             {
357             name => 'DZT-Sample',
358             abstract => 'Sample DZ Dist',
359             version => '0.001',
360             author => 'E. Xavier Ample <example@example.org>',
361             license => 'Perl_5',
362             copyright_holder => 'E. Xavier Ample',
363             }
364              
365             =head1 AUTHOR
366              
367             Ricardo SIGNES 😏 <cpan@semiotic.systems>
368              
369             =head1 COPYRIGHT AND LICENSE
370              
371             This software is copyright (c) 2023 by Ricardo SIGNES.
372              
373             This is free software; you can redistribute it and/or modify it under
374             the same terms as the Perl 5 programming language system itself.
375              
376             =cut