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.029;
2             # ABSTRACT: tools for testing Dist::Zilla plugins
3              
4 50     50   5255853 use Dist::Zilla::Pragmas;
  50         156  
  50         306  
5              
6 50     50   24479 use Params::Util qw(_HASH0);
  50         268339  
  50         3381  
7 50     50   23517 use JSON::MaybeXS;
  50         286269  
  50         3043  
8 50     50   394 use Scalar::Util qw(blessed);
  50         117  
  50         1981  
9 50     50   25130 use Test::Deep ();
  50         337628  
  50         1366  
10 50     50   29931 use YAML::Tiny;
  50         295409  
  50         4423  
11              
12 50         568 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   31596 };
  50         342010  
24              
25 50     50   52464 use namespace::autoclean -except => 'import';
  50         824365  
  50         255  
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 534528 require Dist::Zilla::Tester;
47 166         1316 Dist::Zilla::Tester::builder();
48             }
49              
50             sub Minter {
51 1     1 1 642 require Dist::Zilla::Tester;
52 1         7 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 350 my ($have, $want, $comment) = @_;
67              
68 37         142 my @want = @$want;
69 37 100 66     129 my @have = map { my $str = (blessed $_ and
  227         3015  
70             $_->DOES('Dist::Zilla::Role::File'))
71             ? $_->name
72             : $_;
73 227         440 $str =~ s{\\}{/}g; $str } @$have;
  227         453  
74              
75 37         107 local $Test::Builder::Level = $Test::Builder::Level + 1;
76 37         456 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 5186 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         2686 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 690 my ($json, $want, $comment) = @_;
109              
110 2 50       15 my $have = JSON::MaybeXS->new(ascii => 1)->decode($json)
111             or die "Cannot decode JSON";
112              
113 2         90 local $Test::Builder::Level = $Test::Builder::Level + 1;
114 2         7 Test::Deep::cmp_deeply($have, $want, $comment);
115             }
116              
117             sub _build_ini_builder {
118 95     95   221 my ($starting_core) = @_;
119 95   100     652 $starting_core ||= {};
120              
121             sub {
122 161     161   1697 my (@arg) = @_;
123 161 100       1197 my $new_core = _HASH0($arg[0]) ? shift(@arg) : {};
124              
125 161         2183 my $core_config = { %$starting_core, %$new_core };
126              
127 161         615 my $config = '';
128              
129 161         1397 for my $key (sort keys %$core_config) {
130             my @values = ref $core_config->{ $key }
131 0         0 ? @{ $core_config->{ $key } }
132 973 50       2890 : $core_config->{ $key };
133              
134 973         1814 $config .= "$key = $_\n" for grep {defined} @values;
  973         3942  
135             }
136              
137 161 50       1075 $config .= "\n" if length $config;
138              
139 161         492 for my $line (@arg) {
140 473 100       1646 my @plugin = ref $line ? @$line : ($line, {});
141 473         909 my $moniker = shift @plugin;
142 473 100       1597 my $name = _HASH0($plugin[0]) ? undef : shift @plugin;
143 473   100     2048 my $payload = shift(@plugin) || {};
144              
145 473 50       1252 Carp::confess("bogus plugin configuration: too many args") if @plugin;
146              
147 473         1171 $config .= '[' . $moniker;
148 473 100       1244 $config .= ' / ' . $name if defined $name;
149 473         826 $config .= "]\n";
150              
151 473         1420 for my $key (sort keys %$payload) {
152             my @values = ref $payload->{ $key }
153 17         47 ? @{ $payload->{ $key } }
154 319 100       1218 : $payload->{ $key };
155              
156 319         638 $config .= "$key = $_\n" for grep {defined} @values;
  336         1505  
157             }
158              
159 473         1241 $config .= "\n";
160             }
161              
162 161         3332 return $config;
163             }
164 95         846 }
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   9556 _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   14448 _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.029
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) 2022 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