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   268143 use 5.006;
  6         37  
280 6     6   26 use strict;
  6         8  
  6         141  
281 6     6   25 use warnings;
  6         14  
  6         199  
282              
283 6     6   2405 use Sub::Uplevel 0.12;
  6         6251  
  6         23  
284              
285             our $VERSION = '0.35';
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   1029 use Test::Builder;
  6         42553  
  6         180  
304             my $Tester = Test::Builder->new;
305              
306             {
307 6     6   28 no warnings 'once';
  6         9  
  6         7313  
308             *warning_is = *warnings_are;
309             *warning_like = *warnings_like;
310             }
311              
312             sub warnings_are (&$;$) {
313 281     281 1 619945 my $block = shift;
314 281   100     747 my @exp_warning = map {_canonical_exp_warning($_)}
  602         748  
315             _to_array_if_necessary( shift() || [] );
316 281         352 my $testname = shift;
317 281         299 my @got_warning = ();
318             local $SIG{__WARN__} = sub {
319 940     940   734717 my ($called_from) = caller(0); # to find out Carping methods
320 940         25104 push @got_warning, _canonical_got_warning($called_from, shift());
321 281         1249 };
322 281         735 uplevel 1,$block;
323 281         1602 my $ok = _cmp_is( \@got_warning, \@exp_warning );
324 281         876 $Tester->ok( $ok, $testname );
325 281 100       138619 $ok or _diag_found_warning(@got_warning),
326             _diag_exp_warning(@exp_warning);
327 281         6001 return $ok;
328             }
329              
330              
331             sub warnings_like (&$;$) {
332 547     547 1 1394203 my $block = shift;
333 547   100     1530 my @exp_warning = map {_canonical_exp_warning($_)}
  1184         1414  
334             _to_array_if_necessary( shift() || [] );
335 547         761 my $testname = shift;
336 547         705 my @got_warning = ();
337             local $SIG{__WARN__} = sub {
338 1856     1856   1490427 my ($called_from) = caller(0); # to find out Carping methods
339 1856         50547 push @got_warning, _canonical_got_warning($called_from, shift());
340 547         2591 };
341 547         1682 uplevel 1,$block;
342 547         3726 my $ok = _cmp_like( \@got_warning, \@exp_warning );
343 547         1614 $Tester->ok( $ok, $testname );
344 547 100       290039 $ok or _diag_found_warning(@got_warning),
345             _diag_exp_warning(@exp_warning);
346 547         12553 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   1759 return (ref($_[0]) eq 'ARRAY') ? @{$_[0]} : ($_[0]);
  521         855  
378             }
379              
380             sub _canonical_got_warning {
381 2796     2796   4132 my ($called_from, $msg) = @_;
382 2796 100       4876 my $warn_kind = $called_from eq 'Carp' ? 'carped' : 'warn';
383 2796         6848 my @warning_stack = split /\n/, $msg; # some stuff of uplevel is included
384 2796         12295 return {$warn_kind => $warning_stack[0]}; # return only the real message
385             }
386              
387             sub _canonical_exp_warning {
388 1786     1786   2305 my ($exp) = @_;
389 1786 100       2581 if (ref($exp) eq 'HASH') { # could be {carped => ...}
390 1088 100       1764 my $to_carp = $exp->{carped} or return; # undefined message are ignored
391             return (ref($to_carp) eq 'ARRAY') # is {carped => [ ..., ...] }
392 1076 100       2377 ? map({ {carped => $_} } grep {defined $_} @$to_carp)
  1346         2381  
  1358         1739  
393             : +{carped => $to_carp};
394             }
395 698         1239 return {warn => $exp};
396             }
397              
398             sub _cmp_got_to_exp_warning {
399 503     503   488 my ($got_kind, $got_msg) = %{ shift() };
  503         1059  
400 503         520 my ($exp_kind, $exp_msg) = %{ shift() };
  503         754  
401 503 100 100     1012 return 0 if ($got_kind eq 'warn') && ($exp_kind eq 'carped');
402 502         469 my $cmp;
403 502 100       807 if ($exp_msg =~ /\n$/s) {
404 6         10 $cmp = "$got_msg\n" eq $exp_msg;
405             } else {
406 496         4516 $cmp = $got_msg =~ /^\Q$exp_msg\E at .+ line \d+\.?$/s;
407             }
408 502         1862 return $cmp;
409             }
410              
411             sub _cmp_got_to_exp_warning_like {
412 985     985   899 my ($got_kind, $got_msg) = %{ shift() };
  985         2047  
413 985         1025 my ($exp_kind, $exp_msg) = %{ shift() };
  985         1631  
414 985 100 100     1941 return 0 if ($got_kind eq 'warn') && ($exp_kind eq 'carped');
415 984 50       1807 if (my $re = $Tester->maybe_regex($exp_msg)) { #qr// or '//'
416 984         15660 my $cmp = $got_msg =~ /$re/;
417 984         3393 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   259 my @got = @{ shift() };
  281         410  
426 281         272 my @exp = @{ shift() };
  281         353  
427 281 100       550 scalar @got == scalar @exp or return 0;
428 183         213 my $cmp = 1;
429 183   100     639 $cmp &&= _cmp_got_to_exp_warning($got[$_],$exp[$_]) for (0 .. $#got);
430 183         303 return $cmp;
431             }
432              
433             sub _cmp_like {
434 547     547   576 my @got = @{ shift() };
  547         959  
435 547         585 my @exp = @{ shift() };
  547         765  
436 547 100       1305 scalar @got == scalar @exp or return 0;
437 351         412 my $cmp = 1;
438 351   100     1312 $cmp &&= _cmp_got_to_exp_warning_like($got[$_],$exp[$_]) for (0 .. $#got);
439 351         683 return $cmp;
440             }
441              
442             sub _diag_found_warning {
443 476     476   881 foreach (@_) {
444 1490 50       210493 if (ref($_) eq 'HASH') {
445 1490 100       1449 ${$_}{carped} ? $Tester->diag("found carped warning: ${$_}{carped}")
  1490         2713  
  1108         2483  
446 382         838 : $Tester->diag("found warning: ${$_}{warn}");
447             } else {
448 0         0 $Tester->diag( "found warning: $_" );
449             }
450             }
451 476 100       82099 $Tester->diag( "didn't find a warning" ) unless @_;
452             }
453              
454             sub _diag_exp_warning {
455 476     476   13563 foreach (@_) {
456 1424 50       191671 if (ref($_) eq 'HASH') {
457 1424 100       1405 ${$_}{carped} ? $Tester->diag("expected to find carped warning: ${$_}{carped}")
  1424         2641  
  1064         2406  
458 360         859 : $Tester->diag("expected to find warning: ${$_}{warn}");
459             } else {
460 0         0 $Tester->diag( "expected to find warning: $_" );
461             }
462             }
463 476 100       79298 $Tester->diag( "didn't expect to find a warning" ) unless @_;
464             }
465              
466             package Test::Warn::Categorization;
467              
468 6     6   45 use Carp;
  6         9  
  6         1625  
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;