File Coverage

blib/lib/Test/Warn.pm
Criterion Covered Total %
statement 107 143 74.8
branch 35 48 72.9
condition 16 21 76.1
subroutine 20 24 83.3
pod 3 3 100.0
total 181 239 75.7


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   293780 use 5.006;
  6         55  
280 6     6   37 use strict;
  6         8  
  6         141  
281 6     6   25 use warnings;
  6         8  
  6         246  
282              
283 6     6   2625 use Sub::Uplevel 0.12;
  6         6678  
  6         27  
284              
285             our $VERSION = '0.37';
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   1373 use Test::Builder;
  6         61719  
  6         244  
304             my $Tester = Test::Builder->new;
305              
306             {
307 6     6   36 no warnings 'once';
  6         10  
  6         7799  
308             *warning_is = *warnings_are;
309             *warning_like = *warnings_like;
310             }
311              
312             sub warnings_are (&$;$) {
313 281     281 1 620739 my $block = shift;
314 281   100     733 my @exp_warning = map {_canonical_exp_warning($_)}
  602         717  
315             _to_array_if_necessary( shift() || [] );
316 281         379 my $testname = shift;
317 281         298 my @got_warning = ();
318             local $SIG{__WARN__} = sub {
319 940     940   726657 my ($called_from) = caller(0); # to find out Carping methods
320 940         25456 push @got_warning, _canonical_got_warning($called_from, shift());
321 281         1267 };
322 281         698 uplevel 1,$block;
323 281         1661 my $ok = _cmp_is( \@got_warning, \@exp_warning );
324 281         785 $Tester->ok( $ok, $testname );
325 281 100       135512 $ok or _diag_found_warning(@got_warning),
326             _diag_exp_warning(@exp_warning);
327 281         5900 return $ok;
328             }
329              
330              
331             sub warnings_like (&$;$) {
332 547     547 1 1331404 my $block = shift;
333 547   100     1469 my @exp_warning = map {_canonical_exp_warning($_)}
  1184         1415  
334             _to_array_if_necessary( shift() || [] );
335 547         703 my $testname = shift;
336 547         545 my @got_warning = ();
337             local $SIG{__WARN__} = sub {
338 1856     1856   1471729 my ($called_from) = caller(0); # to find out Carping methods
339 1856         50412 push @got_warning, _canonical_got_warning($called_from, shift());
340 547         2559 };
341 547         1352 uplevel 1,$block;
342 547         3266 my $ok = _cmp_like( \@got_warning, \@exp_warning );
343 547         1396 $Tester->ok( $ok, $testname );
344 547 100       272133 $ok or _diag_found_warning(@got_warning),
345             _diag_exp_warning(@exp_warning);
346 547         12109 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 0         0 my $exp_idx = 0;
356             local $SIG{__WARN__} = sub {
357 0     0   0 my ($called_from) = caller(0); # to find out Carping methods
358 0         0 my $wrn_text=shift;
359 0         0 my $wrn_rec=_canonical_got_warning($called_from, $wrn_text);
360 0 0 0     0 if (
361             $exp_idx < @exp_warning and
362             _cmp_got_to_exp_warning_like($wrn_rec,$exp_warning[$exp_idx])
363             ) {
364 0         0 push @got_warning, $wrn_rec;
365 0         0 $exp_idx++;
366 0         0 return;
367             }
368 0         0 warn $wrn_text;
369 0         0 };
370 0         0 uplevel 1,$block;
371 0         0 my $ok = _cmp_like( \@got_warning, \@exp_warning );
372 0         0 $Tester->ok( $ok, $testname );
373 0 0       0 $ok or _diag_found_warning(@got_warning),
374             _diag_exp_warning(@exp_warning);
375 0         0 return $ok;
376             }
377              
378              
379             sub _to_array_if_necessary {
380 828 100   828   1767 return (ref($_[0]) eq 'ARRAY') ? @{$_[0]} : ($_[0]);
  521         881  
381             }
382              
383             sub _canonical_got_warning {
384 2796     2796   4033 my ($called_from, $msg) = @_;
385 2796 100       4527 my $warn_kind = $called_from eq 'Carp' ? 'carped' : 'warn';
386 2796         6880 my @warning_stack = split /\n/, $msg; # some stuff of uplevel is included
387 2796         12474 return {$warn_kind => $warning_stack[0]}; # return only the real message
388             }
389              
390             sub _canonical_exp_warning {
391 1786     1786   1813 my ($exp) = @_;
392 1786 100       2514 if (ref($exp) eq 'HASH') { # could be {carped => ...}
393 1088 100       1628 my $to_carp = $exp->{carped} or return; # undefined message are ignored
394             return (ref($to_carp) eq 'ARRAY') # is {carped => [ ..., ...] }
395 1076 100       2319 ? map({ {carped => $_} } grep {defined $_} @$to_carp)
  1346         2202  
  1358         1723  
396             : +{carped => $to_carp};
397             }
398 698         1206 return {warn => $exp};
399             }
400              
401             sub _cmp_got_to_exp_warning {
402 503     503   469 my ($got_kind, $got_msg) = %{ shift() };
  503         989  
403 503         506 my ($exp_kind, $exp_msg) = %{ shift() };
  503         699  
404 503 100 100     908 return 0 if ($got_kind eq 'warn') && ($exp_kind eq 'carped');
405 502         421 my $cmp;
406 502 100       768 if ($exp_msg =~ /\n$/s) {
407 6         37 $cmp = "$got_msg\n" eq $exp_msg;
408             } else {
409 496         4309 $cmp = $got_msg =~ /^\Q$exp_msg\E at .+ line \d+\.?$/s;
410             }
411 502         1651 return $cmp;
412             }
413              
414             sub _cmp_got_to_exp_warning_like {
415 985     985   901 my ($got_kind, $got_msg) = %{ shift() };
  985         1861  
416 985         980 my ($exp_kind, $exp_msg) = %{ shift() };
  985         1347  
417 985 100 100     1796 return 0 if ($got_kind eq 'warn') && ($exp_kind eq 'carped');
418 984 50       1609 if (my $re = $Tester->maybe_regex($exp_msg)) { #qr// or '//'
419 984         15193 my $cmp = $got_msg =~ /$re/;
420 984         3708 return $cmp;
421             } else {
422 0         0 return Test::Warn::Categorization::warning_like_category($got_msg,$exp_msg);
423             }
424             }
425              
426              
427             sub _cmp_is {
428 281     281   271 my @got = @{ shift() };
  281         416  
429 281         281 my @exp = @{ shift() };
  281         343  
430 281 100       601 scalar @got == scalar @exp or return 0;
431 183         194 my $cmp = 1;
432 183   100     581 $cmp &&= _cmp_got_to_exp_warning($got[$_],$exp[$_]) for (0 .. $#got);
433 183         315 return $cmp;
434             }
435              
436             sub _cmp_like {
437 547     547   505 my @got = @{ shift() };
  547         789  
438 547         560 my @exp = @{ shift() };
  547         648  
439 547 100       1154 scalar @got == scalar @exp or return 0;
440 351         366 my $cmp = 1;
441 351   100     1132 $cmp &&= _cmp_got_to_exp_warning_like($got[$_],$exp[$_]) for (0 .. $#got);
442 351         589 return $cmp;
443             }
444              
445             sub _diag_found_warning {
446 476     476   707 foreach (@_) {
447 1490 50       208089 if (ref($_) eq 'HASH') {
448 1490 100       1380 ${$_}{carped} ? $Tester->diag("found carped warning: ${$_}{carped}")
  1490         2528  
  1108         2525  
449 382         891 : $Tester->diag("found warning: ${$_}{warn}");
450             } else {
451 0         0 $Tester->diag( "found warning: $_" );
452             }
453             }
454 476 100       80847 $Tester->diag( "didn't find a warning" ) unless @_;
455             }
456              
457             sub _diag_exp_warning {
458 476     476   13334 foreach (@_) {
459 1424 50       191059 if (ref($_) eq 'HASH') {
460 1424 100       1286 ${$_}{carped} ? $Tester->diag("expected to find carped warning: ${$_}{carped}")
  1424         2387  
  1064         2365  
461 360         834 : $Tester->diag("expected to find warning: ${$_}{warn}");
462             } else {
463 0         0 $Tester->diag( "expected to find warning: $_" );
464             }
465             }
466 476 100       79253 $Tester->diag( "didn't expect to find a warning" ) unless @_;
467             }
468              
469             package Test::Warn::Categorization;
470              
471 6     6   137 use Carp;
  6         9  
  6         1887  
472              
473             my $bits = \%warnings::Bits;
474             my @warnings = sort grep {
475             my $warn_bits = $bits->{$_};
476             #!grep { $_ ne $warn_bits && ($_ & $warn_bits) eq $_ } values %$bits;
477             } keys %$bits;
478              
479             # Create a warning name category (e.g. 'utf8') to map to a list of warnings.
480             # The warnings are strings that will be OR'ed together into a
481             # regular expression: qr/...|...|.../.
482             my %warnings_in_category = (
483             'utf8' => ['Wide character in \w+\b',],
484             );
485              
486             sub _warning_category_regexp {
487 0     0     my $category = shift;
488 0 0         my $category_bits = $bits->{$category} or return;
489             my @category_warnings
490 0           = grep { ($bits->{$_} & $category_bits) eq $bits->{$_} } @warnings;
  0            
491              
492             my @list =
493 0 0         map { exists $warnings_in_category{$_}? (@{ $warnings_in_category{$_}}) : ($_) }
  0            
  0            
494             @category_warnings;
495 0           my $re = join "|", @list;
496 0           return qr/$re/;
497             }
498              
499             sub warning_like_category {
500 0     0     my ($warning, $category) = @_;
501 0 0         my $re = _warning_category_regexp($category) or
502             carp("Unknown warning category '$category'"),return;
503 0           my $ok = $warning =~ /$re/;
504 0           return $ok;
505             }
506              
507             1;