File Coverage

blib/lib/Test/Warnings.pm
Criterion Covered Total %
statement 93 94 98.9
branch 31 36 86.1
condition 31 38 81.5
subroutine 23 23 100.0
pod 7 8 87.5
total 185 199 92.9


line stmt bran cond sub pod time code
1 27     27   4819620 use strict;
  27         78  
  27         1118  
2 27     27   281 use warnings;
  27         80  
  27         2481  
3             package Test::Warnings; # git description: v0.037-4-gdc90508
4             # vim: set ts=8 sts=2 sw=2 tw=100 et :
5             # ABSTRACT: Test for warnings and the lack of them
6             # KEYWORDS: testing tests warnings
7              
8             our $VERSION = '0.038';
9              
10 27     27   10840 use parent 'Exporter';
  27         8234  
  27         168  
11 27     27   3291 use Test::Builder;
  27         131535  
  27         6051  
12              
13             our @EXPORT_OK = qw(
14             allow_warnings allowing_warnings
15             had_no_warnings
16             warnings warning
17             allow_patterns
18             disallow_patterns
19             );
20             our @EXPORT = qw(done_testing);
21             our %EXPORT_TAGS = ( all => [ @EXPORT_OK, @EXPORT ] );
22              
23             my $warnings_allowed;
24             my $forbidden_warnings_found;
25             my $done_testing_called;
26             my $no_end_test;
27             my $fail_on_warning;
28             my $report_warnings;
29             my @collected_warnings;
30             my @allowed_patterns;
31              
32             sub import {
33 29     29   334 my $class = shift @_;
34              
35 29         60 my %names; @names{@_} = ();
  29         111  
36             # END block will check for this status
37 29         71 $no_end_test = exists $names{':no_end_test'};
38             # __WARN__ handler will check for this status
39 29         64 $fail_on_warning = exists $names{':fail_on_warning'};
40             # Collect and report warnings at the end
41 29         112 $report_warnings = exists $names{':report_warnings'};
42              
43 29         175 delete @names{qw(:no_end_test :fail_on_warning :report_warnings)};
44              
45 29 100       128 if (not $no_end_test) {
46 13         34 $names{done_testing} = ();
47 13         39 my $callpkg = caller(0);
48 27     27   237 no strict 'refs';
  27         74  
  27         1135  
49 27     27   183 no warnings 'once';
  27         73  
  27         24569  
50 13 100       23 undef *{$callpkg.'::done_testing'} if *{$callpkg.'::done_testing'}{CODE};
  11         58  
  13         75  
51             }
52              
53 29         29669 __PACKAGE__->export_to_level(1, $class, keys %names);
54             }
55              
56             # swap this out for testing this module only!
57             my $tb;
58             sub _builder(;$) {
59 81 100   81   2301659 if (not @_) {
60 77   66     327 $tb ||= Test::Builder->new;
61 77         460 return $tb;
62             }
63              
64 4         15 $tb = shift;
65             }
66              
67             my $_orig_warn_handler = $SIG{__WARN__};
68             $SIG{__WARN__} = sub {
69             if ($warnings_allowed or grep +($_[0] =~ $_), @allowed_patterns) {
70             Test::Builder->new->note($_[0]);
71             }
72             else {
73             $forbidden_warnings_found++;
74             push @collected_warnings, $_[0] if $report_warnings;
75              
76             # TODO: this doesn't handle blessed coderefs... does anyone care?
77             goto &$_orig_warn_handler if $_orig_warn_handler
78             and ( (ref $_orig_warn_handler eq 'CODE')
79             or ($_orig_warn_handler ne 'DEFAULT'
80             and $_orig_warn_handler ne 'IGNORE'
81             and defined &$_orig_warn_handler));
82              
83             if ($_[0] =~ /\n$/) {
84             warn $_[0];
85             } else {
86             require Carp;
87             Carp::carp($_[0]);
88             }
89             _builder->ok(0, 'unexpected warning') if $fail_on_warning;
90             }
91             };
92              
93             sub warnings(;&) {
94             # if someone manually does warnings->import in the same namespace this is
95             # imported into, this sub will be called. in that case, just return the
96             # string "warnings" so it calls the correct method.
97 12 100   12 1 1945798 if (!@_) {
98 1         107 return 'warnings';
99             }
100 11         21 my $code = shift;
101 11         18 my @warnings;
102             local $SIG{__WARN__} = sub {
103 10     10   194 push @warnings, shift;
104 11         74 };
105 11         34 $code->();
106 11         2664 @warnings;
107             }
108              
109             sub warning(&) {
110 4     4 1 2312 my @warnings = &warnings(@_);
111 4 100       20 return @warnings == 1 ? $warnings[0] : \@warnings;
112             }
113              
114             # check for any forbidden warnings, and record that we have done so
115             # so we do not check again via END
116             sub done_testing {
117 17 100   17 0 2550327 if (Test2::Tools::Basic->can('done_testing')) {
    50          
118 1 50 33     10 if (not $no_end_test and not $done_testing_called) {
119             # we could use $ctx to create the test, which means not having to adjust Level,
120             # but then we need to make _builder Test2-compatible, which seems like a PITA.
121 1         3 local $Test::Builder::Level = $Test::Builder::Level + 3;
122 1         7 had_no_warnings('no (unexpected) warnings (via done_testing)');
123 1         3 $done_testing_called = 1;
124             }
125              
126 1         5 Test2::Tools::Basic::done_testing(@_);
127             }
128             elsif (Test::Builder->can('done_testing')) {
129             # only do this at the end of all tests, not at the end of a subtest
130 16         75 my $builder = _builder;
131 16         104 my $in_subtest_sub = $builder->can('in_subtest');
132 16 50 66     116 if (not $no_end_test and not $done_testing_called
    100 66        
133             and not ($in_subtest_sub ? $builder->$in_subtest_sub : $builder->parent)) {
134 6         1092 local $Test::Builder::Level = $Test::Builder::Level + 3;
135 6         32 had_no_warnings('no (unexpected) warnings (via done_testing)');
136 6         16 $done_testing_called = 1;
137             }
138              
139 16         316 _builder->done_testing(@_);
140             }
141             else {
142 0         0 die 'no done_testing available via a Test module';
143             }
144             }
145              
146             # we also monkey-patch Test::Builder::done_testing (which is called by Test::More::done_testing),
147             # in case Test::More was loaded after Test::Warnings and therefore its version of done_testing was
148             # imported into the test rather than ours.
149             if (Test::Builder->can('done_testing')) {
150 27     27   246 no strict 'refs';
  27         49  
  27         1834  
151             my $orig = *{'Test::Builder::done_testing'}{CODE};
152 27     27   250 no warnings 'redefine';
  27         96  
  27         22341  
153             *{'Test::Builder::done_testing'} = sub {
154             # only do this at the end of all tests, not at the end of a subtest
155 19     19   673914 my $builder = _builder;
156 19         100 my $in_subtest_sub = $builder->can('in_subtest');
157 19 50 100     92 if (not $no_end_test and not $done_testing_called
    100 100        
158             and not ($in_subtest_sub ? $builder->$in_subtest_sub : $builder->parent)) {
159 1         128 local $Test::Builder::Level = $Test::Builder::Level + 3;
160 1         5 had_no_warnings('no (unexpected) warnings (via done_testing)');
161 1         2 $done_testing_called = 1;
162             }
163              
164 19         219 $orig->(@_);
165             };
166             }
167              
168             END {
169 24 100 100 24   1970494 if (not $no_end_test
      100        
      100        
170             and not $done_testing_called
171             # skip this if there is no plan and no tests have been run (e.g.
172             # compilation tests of this module!)
173             and (_builder->expected_tests or _builder->current_test > 0)
174             ) {
175 2         390 local $Test::Builder::Level = $Test::Builder::Level + 1;
176 2         9 had_no_warnings('no (unexpected) warnings (via END block)');
177             }
178             }
179              
180             # setter
181             sub allow_warnings(;$) {
182 3 100 66 3 1 24 $warnings_allowed = @_ || defined $_[0] ? $_[0] : 1;
183             }
184              
185             # getter
186 3     3 1 18 sub allowing_warnings() { $warnings_allowed }
187              
188             # call at any time to assert no (unexpected) warnings so far
189             sub had_no_warnings(;$) {
190 14 100   14 1 568 if ($ENV{PERL_TEST_WARNINGS_ONLY_REPORT_WARNINGS}) {
191 1 50       4 $forbidden_warnings_found
192             and _builder->diag("Found $forbidden_warnings_found warnings but allowing them because PERL_TEST_WARNINGS_ONLY_REPORT_WARNINGS is set");
193             }
194             else {
195 13   100     43 _builder->ok(!$forbidden_warnings_found, shift || 'no (unexpected) warnings');
196             }
197 14 100 66     4693 if (($report_warnings or $ENV{PERL_TEST_WARNINGS_ONLY_REPORT_WARNINGS})
      100        
198             and $forbidden_warnings_found) {
199 2         8 _builder->diag("Got the following unexpected warnings:");
200 2         360 for my $i (1 .. @collected_warnings) {
201 2         9 _builder->diag(" $i: $collected_warnings[ $i - 1 ]");
202             }
203             }
204             }
205              
206             # pass one or more regexes (in qr format)
207             # when called in void context, lasting effect is universal
208             # otherwise, returns objects: when they go out of scope, the effect is removed
209             # (warning disallowed again).
210             sub allow_patterns(@) {
211 2     2 1 682066 push @allowed_patterns, @_;
212 2 100       7 return if not defined wantarray;
213 1         8 return [ map +Test::Warnings::TemporaryWarning->new($_), @_ ];
214             }
215              
216             sub disallow_patterns(@) {
217 2     2 1 320 foreach my $pattern (@_) {
218 2         10 @allowed_patterns = grep +($_ ne $pattern), @allowed_patterns;
219             }
220             }
221              
222             package # hide from PAUSE
223             Test::Warnings::TemporaryWarning;
224              
225             sub new {
226 1     1   3 my ($class, $pattern) = @_;
227 1         4 bless \$pattern, $class;
228             }
229              
230             sub DESTROY {
231 1     1   330 Test::Warnings::disallow_patterns(${$_[0]});
  1         7  
232             }
233              
234             1;
235              
236             __END__