File Coverage

inc/Test/Warn.pm
Criterion Covered Total %
statement 40 139 28.7
branch 3 46 6.5
condition 1 18 5.5
subroutine 10 24 41.6
pod 3 3 100.0
total 57 230 24.7


line stmt bran cond sub pod time code
1             #line 1
2             #line 241
3              
4              
5             package Test::Warn;
6              
7             use 5.006;
8             use strict;
9             use warnings;
10              
11             #use Array::Compare;
12             use Sub::Uplevel 0.12;
13              
14             our $VERSION = '0.30';
15              
16             require Exporter;
17              
18             our @ISA = qw(Exporter);
19              
20             our %EXPORT_TAGS = ( 'all' => [ qw(
21             @EXPORT
22             ) ] );
23              
24             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
25              
26             our @EXPORT = qw(
27             warning_is warnings_are
28             warning_like warnings_like
29             warnings_exist
30             );
31              
32             use Test::Builder;
33             my $Tester = Test::Builder->new;
34              
35             {
36             no warnings 'once';
37             *warning_is = *warnings_are;
38             *warning_like = *warnings_like;
39             }
40              
41             sub warnings_are (&$;$) {
42             my $block = shift;
43             my @exp_warning = map {_canonical_exp_warning($_)}
44             _to_array_if_necessary( shift() || [] );
45             my $testname = shift;
46             my @got_warning = ();
47             local $SIG{__WARN__} = sub {
48             my ($called_from) = caller(0); # to find out Carping methods
49             push @got_warning, _canonical_got_warning($called_from, shift());
50             };
51             uplevel 1,$block;
52             my $ok = _cmp_is( \@got_warning, \@exp_warning );
53             $Tester->ok( $ok, $testname );
54             $ok or _diag_found_warning(@got_warning),
55             _diag_exp_warning(@exp_warning);
56             return $ok;
57             }
58              
59              
60             sub warnings_like (&$;$) {
61             my $block = shift;
62             my @exp_warning = map {_canonical_exp_warning($_)}
63             _to_array_if_necessary( shift() || [] );
64             my $testname = shift;
65             my @got_warning = ();
66             local $SIG{__WARN__} = sub {
67             my ($called_from) = caller(0); # to find out Carping methods
68             push @got_warning, _canonical_got_warning($called_from, shift());
69             };
70             uplevel 1,$block;
71             my $ok = _cmp_like( \@got_warning, \@exp_warning );
72             $Tester->ok( $ok, $testname );
73             $ok or _diag_found_warning(@got_warning),
74             _diag_exp_warning(@exp_warning);
75             return $ok;
76             }
77              
78             sub warnings_exist (&$;$) {
79             my $block = shift;
80             my @exp_warning = map {_canonical_exp_warning($_)}
81             _to_array_if_necessary( shift() || [] );
82             my $testname = shift;
83             my @got_warning = ();
84             local $SIG{__WARN__} = sub {
85             my ($called_from) = caller(0); # to find out Carping methods
86             my $wrn_text=shift;
87             my $wrn_rec=_canonical_got_warning($called_from, $wrn_text);
88             foreach my $wrn (@exp_warning) {
89             if (_cmp_got_to_exp_warning_like($wrn_rec,$wrn)) {
90             push @got_warning, $wrn_rec;
91             return;
92             }
93             }
94             warn $wrn_text;
95             };
96             uplevel 1,$block;
97             my $ok = _cmp_like( \@got_warning, \@exp_warning );
98             $Tester->ok( $ok, $testname );
99             $ok or _diag_found_warning(@got_warning),
100             _diag_exp_warning(@exp_warning);
101             return $ok;
102             }
103              
104              
105             sub _to_array_if_necessary {
106             return (ref($_[0]) eq 'ARRAY') ? @{$_[0]} : ($_[0]);
107             }
108              
109             sub _canonical_got_warning {
110             my ($called_from, $msg) = @_;
111             my $warn_kind = $called_from eq 'Carp' ? 'carped' : 'warn';
112             my @warning_stack = split /\n/, $msg; # some stuff of uplevel is included
113             return {$warn_kind => $warning_stack[0]}; # return only the real message
114             }
115              
116             sub _canonical_exp_warning {
117             my ($exp) = @_;
118             if (ref($exp) eq 'HASH') { # could be {carped => ...}
119             my $to_carp = $exp->{carped} or return; # undefined message are ignored
120             return (ref($to_carp) eq 'ARRAY') # is {carped => [ ..., ...] }
121             ? map({ {carped => $_} } grep {defined $_} @$to_carp)
122             : +{carped => $to_carp};
123             }
124             return {warn => $exp};
125             }
126              
127             sub _cmp_got_to_exp_warning {
128             my ($got_kind, $got_msg) = %{ shift() };
129             my ($exp_kind, $exp_msg) = %{ shift() };
130             return 0 if ($got_kind eq 'warn') && ($exp_kind eq 'carped');
131             my $cmp = $got_msg =~ /^\Q$exp_msg\E at .+ line \d+\.?$/;
132             return $cmp;
133             }
134              
135             sub _cmp_got_to_exp_warning_like {
136             my ($got_kind, $got_msg) = %{ shift() };
137             my ($exp_kind, $exp_msg) = %{ shift() };
138             return 0 if ($got_kind eq 'warn') && ($exp_kind eq 'carped');
139             if (my $re = $Tester->maybe_regex($exp_msg)) { #qr// or '//'
140             my $cmp = $got_msg =~ /$re/;
141             return $cmp;
142             } else {
143             return Test::Warn::Categorization::warning_like_category($got_msg,$exp_msg);
144             }
145             }
146              
147              
148             sub _cmp_is {
149             my @got = @{ shift() };
150             my @exp = @{ shift() };
151             scalar @got == scalar @exp or return 0;
152             my $cmp = 1;
153             $cmp &&= _cmp_got_to_exp_warning($got[$_],$exp[$_]) for (0 .. $#got);
154             return $cmp;
155             }
156              
157             sub _cmp_like {
158             my @got = @{ shift() };
159             my @exp = @{ shift() };
160             scalar @got == scalar @exp or return 0;
161             my $cmp = 1;
162             $cmp &&= _cmp_got_to_exp_warning_like($got[$_],$exp[$_]) for (0 .. $#got);
163             return $cmp;
164             }
165              
166             sub _diag_found_warning {
167             foreach (@_) {
168             if (ref($_) eq 'HASH') {
169             ${$_}{carped} ? $Tester->diag("found carped warning: ${$_}{carped}")
170             : $Tester->diag("found warning: ${$_}{warn}");
171             } else {
172             $Tester->diag( "found warning: $_" );
173             }
174             }
175             $Tester->diag( "didn't find a warning" ) unless @_;
176             }
177              
178             sub _diag_exp_warning {
179             foreach (@_) {
180             if (ref($_) eq 'HASH') {
181             ${$_}{carped} ? $Tester->diag("expected to find carped warning: ${$_}{carped}")
182             : $Tester->diag("expected to find warning: ${$_}{warn}");
183             } else {
184             $Tester->diag( "expected to find warning: $_" );
185             }
186             }
187             $Tester->diag( "didn't expect to find a warning" ) unless @_;
188             }
189              
190             package Test::Warn::Categorization;
191              
192             use Carp;
193              
194             my $bits = \%warnings::Bits;
195             my @warnings = sort grep {
196             my $warn_bits = $bits->{$_};
197             #!grep { $_ ne $warn_bits && ($_ & $warn_bits) eq $_ } values %$bits;
198             } keys %$bits;
199              
200             my %warnings_in_category = (
201             'utf8' => ['Wide character in \w+\b',],
202             );
203              
204             sub _warning_category_regexp {
205             my $category = shift;
206             my $category_bits = $bits->{$category} or return;
207             my @category_warnings
208             = grep { ($bits->{$_} & $category_bits) eq $bits->{$_} } @warnings;
209              
210             my @list =
211             map { exists $warnings_in_category{$_}? (@{ $warnings_in_category{$_}}) : ($_) }
212             @category_warnings;
213             my $re = join "|", @list;
214             return qr/$re/;
215             }
216              
217             sub warning_like_category {
218             my ($warning, $category) = @_;
219             my $re = _warning_category_regexp($category) or
220             carp("Unknown warning category '$category'"),return;
221             my $ok = $warning =~ /$re/;
222             return $ok;
223             }
224            
225             1;