File Coverage

blib/lib/Test/Warn.pm
Criterion Covered Total %
statement 107 142 75.3
branch 35 48 72.9
condition 16 18 88.8
subroutine 20 24 83.3
pod 3 3 100.0
total 181 235 77.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Test::Warn - Perl extension to test methods for warnings
4              
5             =head1 SYNOPSIS
6              
7             use Test::Warn;
8              
9             warning_is {foo(-dri => "/")} "Unknown Parameter 'dri'", "dri != dir gives warning";
10             warnings_are {bar(1,1)} ["Width very small", "Height very small"];
11              
12             warning_is {add(2,2)} undef, "No warnings for calc 2+2"; # or
13             warnings_are {add(2,2)} [], "No warnings for calc 2+2"; # whichever reads better :-)
14              
15             warning_like {foo(-dri => "/")} qr/unknown param/i, "an unknown parameter test";
16             warnings_like {bar(1,1)} [qr/width.*small/i, qr/height.*small/i];
17              
18             warning_is {foo()} {carped => "didn't find the right parameters"};
19             warnings_like {foo()} [qr/undefined/,qr/undefined/,{carped => qr/no result/i}];
20              
21             warning_like {foo(undef)} 'uninitialized';
22             warning_like {bar(file => '/etc/passwd')} 'io';
23              
24             warning_like {eval q/"$x"; $x;/}
25             [qw/void uninitialized/],
26             "some warnings at compile time";
27              
28             warnings_exist {...} [qr/expected warning/], "Expected warning is thrown";
29              
30             =head1 DESCRIPTION
31              
32             A good style of Perl programming calls for a lot of diverse regression tests.
33              
34             This module provides a few convenience methods for testing warning based-code.
35              
36             If you are not already familiar with the L manpage
37             now would be the time to go take a look.
38              
39             =head2 FUNCTIONS
40              
41             =over 4
42              
43             =item B I
44              
45             Tests that BLOCK gives the specified warning exactly once.
46              
47             The test fails if the BLOCK warns more than once or does not warn at all.
48             If the string is undef, then the test succeeds if the BLOCK doesn't
49             give any warning.
50              
51             Another way to say that there are no warnings in the block
52             is:
53              
54             warnings_are {foo()} [], "no warnings"
55              
56             If you want to test for a warning given by Carp
57             you have to write something like:
58              
59             warning_is {carp "msg"} {carped => 'msg'}, "Test for a carped warning";
60              
61             The test will fail if a "normal" warning is found instead of a "carped" one.
62              
63             Note: C would print something like C.
64             This method ignores everything after the "at". Thus to match this warning
65             you would have to call C<< warning_is {warn "foo"} "foo", "Foo succeeded" >>.
66             If you need to test for a warning at an exact line,
67             try something like:
68              
69             warning_like {warn "foo"} qr/at XYZ.dat line 5/
70              
71             Warn messages with a trailing newline (like C) don't produce the C message by Perl.
72             Up to Test::Warn 0.30 such warning weren't supported by C<< warning_is {warn "foo\n"} "foo\n" >>.
73             Starting with version 0.31 they are supported, but also marked as experimental.
74              
75             L|/warning_is-BLOCK-STRING-TEST_NAME> and L|/warnings_are-BLOCK-ARRAYREF-TEST_NAME>
76             are only aliases to the same method. So you also could write
77             C<< warning_is {foo()} [], "no warning" >> or something similar.
78              
79             I decided to give two methods the same name to improve readability.
80              
81             A true value is returned if the test succeeds, false otherwise.
82              
83             The test name is optional, but recommended.
84              
85             =item B I
86              
87             Tests to see that BLOCK gives exactly the specified warnings.
88             The test fails if the warnings from BLOCK are not exactly the ones in ARRAYREF.
89             If the ARRAYREF is equal to C<< [] >>,
90             then the test succeeds if the BLOCK doesn't give any warning.
91              
92             Please read also the notes to
93             L|/warning_is-BLOCK-STRING-TEST_NAME>
94             as these methods are only aliases.
95              
96             If you want more than one test for carped warnings, try this:
97              
98             warnings_are {carp "c1"; carp "c2"} {carped => ['c1','c2'];
99              
100             or
101              
102             warnings_are {foo()} ["Warning 1", {carped => ["Carp 1", "Carp 2"]}, "Warning 2"];
103              
104             Note that C<< {carped => ...} >> must always be a hash ref.
105              
106             =item B I
107              
108             Tests that BLOCK gives exactly one warning and it can be matched by
109             the given regexp.
110              
111             If the string is undef, then the tests succeeds if the BLOCK doesn't
112             give any warning.
113              
114             The REGEXP is matched against the whole warning line,
115             which in general has the form C<< "WARNING at __FILE__ line __LINE__" >>.
116             So you can check for a warning in the file C on line 5 with:
117              
118             warning_like {bar()} qr/at Foo.pm line 5/, "Testname"
119              
120             I don't know whether it makes sense to do such a test :-(
121              
122             However, you should be prepared as a matching with C<'at'>, C<'file'>, C<'\d'>
123             or similar will always pass.
124              
125             Consider C<< qr/^foo/ >> if you want to test for warning C<"foo something"> in file F.
126              
127             You can also write the regexp in a string as C<"/.../">
128             instead of using the C<< qr/.../ >> syntax.
129              
130             Note that the slashes are important in the string,
131             as strings without slashes are reserved for warning categories
132             (to match warning categories as can be seen in the perllexwarn man page).
133              
134             Similar to
135             L<< C|/warning_is-BLOCK-STRING-TEST_NAME >> and
136             L<< C|/warnings_are-BLOCK-ARRAYREF-TEST_NAME >>
137             you can test for warnings via C with:
138              
139             warning_like {bar()} {carped => qr/bar called too early/i};
140              
141             Similar to
142             L<< C|/warning_is-BLOCK-STRING-TEST_NAME >> and
143             L<< C|/warnings_are-BLOCK-ARRAYREF-TEST_NAME >>,
144              
145             L<< C|/warning_like-BLOCK-REGEXP-TEST_NAME >> and
146             L<< C|/warnings_like-BLOCK-ARRAYREF-TEST_NAME >>
147             are only aliases to the same methods.
148              
149             A true value is returned if the test succeeds, false otherwise.
150              
151             The test name is optional, but recommended.
152              
153             =item B I
154              
155             Tests whether a BLOCK gives exactly one warning of the passed category.
156              
157             The categories are grouped in a tree,
158             like it is expressed in L.
159             Also see L.
160              
161              
162             Thanks to the grouping in a tree,
163             it's possible to test simply for an 'io' warning,
164             instead of testing for a 'closed|exec|layer|newline|pipe|unopened' warning.
165              
166             Note, that warnings occurring at compile time
167             can only be caught in an eval block. So
168              
169             warning_like {eval q/"$x"; $x;/}
170             [qw/void uninitialized/],
171             "some warnings at compile time";
172              
173             will work, while it wouldn't work without the eval.
174              
175             Note, that it isn't possible yet,
176             to test for own categories,
177             created with L.
178              
179             =item B I
180              
181             Tests to see that BLOCK gives exactly the number of the specified
182             warnings, in the defined order.
183              
184             Please read also the notes to
185             L<< C|/warning_like-BLOCK-REGEXP-TEST_NAME >>
186             as these methods are only aliases.
187              
188             Similar to
189             L<< C|/warnings_are-BLOCK-ARRAYREF-TEST_NAME >>,
190             you can test for multiple warnings via C
191             and for warning categories, too:
192              
193             warnings_like {foo()}
194             [qr/bar warning/,
195             qr/bar warning/,
196             {carped => qr/bar warning/i},
197             'io'
198             ],
199             "I hope you'll never have to write a test for so many warnings :-)";
200              
201             =item B I
202              
203             Same as warning_like, but will C<< warn() >> all warnings that do not match the supplied regex/category,
204             instead of registering an error. Use this test when you just want to make sure that specific
205             warnings were generated, and couldn't care less if other warnings happened in the same block
206             of code.
207              
208             warnings_exist {...} [qr/expected warning/], "Expected warning is thrown";
209              
210             warnings_exist {...} ['uninitialized'], "Expected warning is thrown";
211              
212             =back
213              
214             =head2 EXPORT
215              
216             C,
217             C,
218             C,
219             C,
220             C by default.
221              
222             =head1 BUGS AND LIMITATIONS
223              
224             Category check is done as C<< qr/category_name/ >>. In some case this works, like for
225             category C<'uninitialized'>. For C<'utf8'> it does not work. Perl does not have a list
226             of warnings, so it is not possible to generate one for C.
227              
228             If you want to add a warning to a category, send a pull request. Modifications
229             should be done to C<< %warnings_in_category >>. You should look into perl source to check
230             how warning is looking exactly.
231              
232             Please note that warnings with newlines inside are very awkward.
233             The only sensible way to handle them is to use the C or
234             C methods. The background is that there is no
235             really safe way to distinguish between warnings with newlines and a
236             stacktrace.
237              
238             If a method has its own warn handler,
239             overwriting C<$SIG{__WARN__}>,
240             my test warning methods won't get these warnings.
241              
242             The C method isn't fully tested.
243             Please take note if you use this this calling style,
244             and report any bugs you find.
245              
246             =head2 XS warnings
247              
248             As described in https://rt.cpan.org/Ticket/Display.html?id=42070&results=3c71d1b101a730e185691657f3b02f21 or https://github.com/hanfried/test-warn/issues/1 XS warnings might not be caught.
249              
250             =head1 SEE ALSO
251              
252             Have a look to the similar L module. L
253              
254             =head1 THANKS
255              
256             Many thanks to Adrian Howard, chromatic and Michael G. Schwern,
257             who have given me a lot of ideas.
258              
259             =head1 AUTHOR
260              
261             Janek Schleicher, Ebigj AT kamelfreund.deE
262              
263             =head1 COPYRIGHT AND LICENSE
264              
265             Copyright 2002 by Janek Schleicher
266              
267             Copyright 2007-2014 by Alexandr Ciornii, L
268              
269             Copyright 2015-2018 by Janek Schleicher
270              
271             This library is free software; you can redistribute it and/or modify
272             it under the same terms as Perl itself.
273              
274             =cut
275              
276              
277             package Test::Warn;
278              
279 6     6   335713 use 5.006;
  6         52  
280 6     6   44 use strict;
  6         8  
  6         160  
281 6     6   30 use warnings;
  6         14  
  6         306  
282              
283 6     6   2984 use Sub::Uplevel 0.12;
  6         7230  
  6         31  
284              
285             our $VERSION = '0.36';
286              
287             require Exporter;
288              
289             our @ISA = qw(Exporter);
290              
291             our %EXPORT_TAGS = ( 'all' => [ qw(
292             @EXPORT
293             ) ] );
294              
295             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
296              
297             our @EXPORT = qw(
298             warning_is warnings_are
299             warning_like warnings_like
300             warnings_exist
301             );
302              
303 6     6   1205 use Test::Builder;
  6         50217  
  6         220  
304             my $Tester = Test::Builder->new;
305              
306             {
307 6     6   40 no warnings 'once';
  6         15  
  6         8715  
308             *warning_is = *warnings_are;
309             *warning_like = *warnings_like;
310             }
311              
312             sub warnings_are (&$;$) {
313 281     281 1 821017 my $block = shift;
314 281   100     1145 my @exp_warning = map {_canonical_exp_warning($_)}
  602         957  
315             _to_array_if_necessary( shift() || [] );
316 281         521 my $testname = shift;
317 281         425 my @got_warning = ();
318             local $SIG{__WARN__} = sub {
319 940     940   827920 my ($called_from) = caller(0); # to find out Carping methods
320 940         28706 push @got_warning, _canonical_got_warning($called_from, shift());
321 281         1915 };
322 281         1129 uplevel 1,$block;
323 281         2554 my $ok = _cmp_is( \@got_warning, \@exp_warning );
324 281         1308 $Tester->ok( $ok, $testname );
325 281 100       183712 $ok or _diag_found_warning(@got_warning),
326             _diag_exp_warning(@exp_warning);
327 281         7864 return $ok;
328             }
329              
330              
331             sub warnings_like (&$;$) {
332 547     547 1 1464073 my $block = shift;
333 547   100     1773 my @exp_warning = map {_canonical_exp_warning($_)}
  1184         1899  
334             _to_array_if_necessary( shift() || [] );
335 547         896 my $testname = shift;
336 547         674 my @got_warning = ();
337             local $SIG{__WARN__} = sub {
338 1856     1856   1516304 my ($called_from) = caller(0); # to find out Carping methods
339 1856         52337 push @got_warning, _canonical_got_warning($called_from, shift());
340 547         3088 };
341 547         1706 uplevel 1,$block;
342 547         4321 my $ok = _cmp_like( \@got_warning, \@exp_warning );
343 547         1726 $Tester->ok( $ok, $testname );
344 547 100       302141 $ok or _diag_found_warning(@got_warning),
345             _diag_exp_warning(@exp_warning);
346 547         13575 return $ok;
347             }
348              
349             sub warnings_exist (&$;$) {
350 0     0 1 0 my $block = shift;
351 0   0     0 my @exp_warning = map {_canonical_exp_warning($_)}
  0         0  
352             _to_array_if_necessary( shift() || [] );
353 0         0 my $testname = shift;
354 0         0 my @got_warning = ();
355             local $SIG{__WARN__} = sub {
356 0     0   0 my ($called_from) = caller(0); # to find out Carping methods
357 0         0 my $wrn_text=shift;
358 0         0 my $wrn_rec=_canonical_got_warning($called_from, $wrn_text);
359 0         0 foreach my $wrn (@exp_warning) {
360 0 0       0 if (_cmp_got_to_exp_warning_like($wrn_rec,$wrn)) {
361 0         0 push @got_warning, $wrn_rec;
362 0         0 return;
363             }
364             }
365 0         0 warn $wrn_text;
366 0         0 };
367 0         0 uplevel 1,$block;
368 0         0 my $ok = _cmp_like( \@got_warning, \@exp_warning );
369 0         0 $Tester->ok( $ok, $testname );
370 0 0       0 $ok or _diag_found_warning(@got_warning),
371             _diag_exp_warning(@exp_warning);
372 0         0 return $ok;
373             }
374              
375              
376             sub _to_array_if_necessary {
377 828 100   828   2240 return (ref($_[0]) eq 'ARRAY') ? @{$_[0]} : ($_[0]);
  521         1141  
378             }
379              
380             sub _canonical_got_warning {
381 2796     2796   4352 my ($called_from, $msg) = @_;
382 2796 100       5135 my $warn_kind = $called_from eq 'Carp' ? 'carped' : 'warn';
383 2796         7530 my @warning_stack = split /\n/, $msg; # some stuff of uplevel is included
384 2796         13206 return {$warn_kind => $warning_stack[0]}; # return only the real message
385             }
386              
387             sub _canonical_exp_warning {
388 1786     1786   2172 my ($exp) = @_;
389 1786 100       3014 if (ref($exp) eq 'HASH') { # could be {carped => ...}
390 1088 100       1960 my $to_carp = $exp->{carped} or return; # undefined message are ignored
391             return (ref($to_carp) eq 'ARRAY') # is {carped => [ ..., ...] }
392 1076 100       2869 ? map({ {carped => $_} } grep {defined $_} @$to_carp)
  1346         2490  
  1358         1984  
393             : +{carped => $to_carp};
394             }
395 698         1366 return {warn => $exp};
396             }
397              
398             sub _cmp_got_to_exp_warning {
399 503     503   593 my ($got_kind, $got_msg) = %{ shift() };
  503         1372  
400 503         658 my ($exp_kind, $exp_msg) = %{ shift() };
  503         1027  
401 503 100 100     1267 return 0 if ($got_kind eq 'warn') && ($exp_kind eq 'carped');
402 502         590 my $cmp;
403 502 100       977 if ($exp_msg =~ /\n$/s) {
404 6         23 $cmp = "$got_msg\n" eq $exp_msg;
405             } else {
406 496         5436 $cmp = $got_msg =~ /^\Q$exp_msg\E at .+ line \d+\.?$/s;
407             }
408 502         2195 return $cmp;
409             }
410              
411             sub _cmp_got_to_exp_warning_like {
412 985     985   1337 my ($got_kind, $got_msg) = %{ shift() };
  985         2130  
413 985         1120 my ($exp_kind, $exp_msg) = %{ shift() };
  985         1795  
414 985 100 100     1999 return 0 if ($got_kind eq 'warn') && ($exp_kind eq 'carped');
415 984 50       1876 if (my $re = $Tester->maybe_regex($exp_msg)) { #qr// or '//'
416 984         15967 my $cmp = $got_msg =~ /$re/;
417 984         3467 return $cmp;
418             } else {
419 0         0 return Test::Warn::Categorization::warning_like_category($got_msg,$exp_msg);
420             }
421             }
422              
423              
424             sub _cmp_is {
425 281     281   435 my @got = @{ shift() };
  281         602  
426 281         465 my @exp = @{ shift() };
  281         548  
427 281 100       831 scalar @got == scalar @exp or return 0;
428 183         317 my $cmp = 1;
429 183   100     991 $cmp &&= _cmp_got_to_exp_warning($got[$_],$exp[$_]) for (0 .. $#got);
430 183         474 return $cmp;
431             }
432              
433             sub _cmp_like {
434 547     547   641 my @got = @{ shift() };
  547         1058  
435 547         771 my @exp = @{ shift() };
  547         849  
436 547 100       1362 scalar @got == scalar @exp or return 0;
437 351         478 my $cmp = 1;
438 351   100     1488 $cmp &&= _cmp_got_to_exp_warning_like($got[$_],$exp[$_]) for (0 .. $#got);
439 351         1023 return $cmp;
440             }
441              
442             sub _diag_found_warning {
443 476     476   1006 foreach (@_) {
444 1490 50       227731 if (ref($_) eq 'HASH') {
445 1490 100       1686 ${$_}{carped} ? $Tester->diag("found carped warning: ${$_}{carped}")
  1490         3118  
  1108         2930  
446 382         944 : $Tester->diag("found warning: ${$_}{warn}");
447             } else {
448 0         0 $Tester->diag( "found warning: $_" );
449             }
450             }
451 476 100       89958 $Tester->diag( "didn't find a warning" ) unless @_;
452             }
453              
454             sub _diag_exp_warning {
455 476     476   15297 foreach (@_) {
456 1424 50       203719 if (ref($_) eq 'HASH') {
457 1424 100       1499 ${$_}{carped} ? $Tester->diag("expected to find carped warning: ${$_}{carped}")
  1424         2997  
  1064         2736  
458 360         914 : $Tester->diag("expected to find warning: ${$_}{warn}");
459             } else {
460 0         0 $Tester->diag( "expected to find warning: $_" );
461             }
462             }
463 476 100       90759 $Tester->diag( "didn't expect to find a warning" ) unless @_;
464             }
465              
466             package Test::Warn::Categorization;
467              
468 6     6   59 use Carp;
  6         12  
  6         2095  
469              
470             my $bits = \%warnings::Bits;
471             my @warnings = sort grep {
472             my $warn_bits = $bits->{$_};
473             #!grep { $_ ne $warn_bits && ($_ & $warn_bits) eq $_ } values %$bits;
474             } keys %$bits;
475              
476             # Create a warning name category (e.g. 'utf8') to map to a list of warnings.
477             # The warnings are strings that will be OR'ed together into a
478             # regular expression: qr/...|...|.../.
479             my %warnings_in_category = (
480             'utf8' => ['Wide character in \w+\b',],
481             );
482              
483             sub _warning_category_regexp {
484 0     0     my $category = shift;
485 0 0         my $category_bits = $bits->{$category} or return;
486             my @category_warnings
487 0           = grep { ($bits->{$_} & $category_bits) eq $bits->{$_} } @warnings;
  0            
488              
489             my @list =
490 0 0         map { exists $warnings_in_category{$_}? (@{ $warnings_in_category{$_}}) : ($_) }
  0            
  0            
491             @category_warnings;
492 0           my $re = join "|", @list;
493 0           return qr/$re/;
494             }
495              
496             sub warning_like_category {
497 0     0     my ($warning, $category) = @_;
498 0 0         my $re = _warning_category_regexp($category) or
499             carp("Unknown warning category '$category'"),return;
500 0           my $ok = $warning =~ /$re/;
501 0           return $ok;
502             }
503              
504             1;