File Coverage

blib/lib/Test/TestCoverage.pm
Criterion Covered Total %
statement 97 115 84.3
branch 18 38 47.3
condition 8 21 38.1
subroutine 16 19 84.2
pod 6 6 100.0
total 145 199 72.8


line stmt bran cond sub pod time code
1             package Test::TestCoverage;
2              
3             # ABSTRACT: Test if your test covers all "public" subroutines of the package
4              
5 5     5   151472 use strict;
  5         12  
  5         219  
6 5     5   29 use warnings;
  5         8  
  5         184  
7 5     5   31 use Devel::Symdump;
  5         13  
  5         145  
8 5     5   24 use Test::Builder;
  5         7  
  5         116  
9 5     5   26 use B;
  5         6  
  5         326  
10 5     5   30 use base qw(Exporter);
  5         7  
  5         1926  
11              
12             our @EXPORT = qw(
13             test_coverage
14             ok_test_coverage
15             all_test_coverage_ok
16             reset_test_coverage
17             reset_all_test_coverage
18             test_coverage_except
19             );
20             our $VERSION = '0.12';
21              
22             my $self = {};
23             my $test = Test::Builder->new();
24             my $invokes = {};
25             my $last = '';
26              
27              
28             sub test_coverage {
29 3     3 1 1187 my ($package) = @_;
30 3 50       20 return unless defined $package;
31 3         8 $last = $package;
32 3         14 _get_subroutines($package);
33            
34 3         10 $invokes->{$package} = {};
35            
36 3 50       16 my $moosified = $INC{"Moose.pm"} ? 1 : 0;
37            
38 3         5 for my $subref(@{$self->{subs}->{$package}}){
  3         13  
39 6         13 my $sub = $subref->[0];
40            
41 6         14 my $sub_with = $package . '::' . $sub;
42 6 50       19 unless(exists $invokes->{$package}->{$sub}){
43 6         14 $invokes->{$package}->{$sub} = 0;
44             }
45            
46 5     5   39 no strict 'refs';
  5         7  
  5         228  
47 5     5   30 no warnings 'redefine';
  5         8  
  5         6726  
48            
49 6         47 my $old = $package->can( $sub );
50 6         8 my $mopped = 0;
51              
52 6 50       16 if ( $moosified ) {
53 0         0 require Class::MOP;
54 0 0       0 my $meta
55             = $package->can('add_before_method_modifier')
56             ? $package
57             : Class::MOP::class_of( $package );
58              
59 0 0       0 if ( defined $meta ) {
60 0         0 $mopped++;
61             $meta->add_after_method_modifier( $sub, sub {
62 0     0   0 $invokes->{$package}->{$sub}++;
63 0         0 } );
64             }
65             }
66              
67 6 50       12 if ( !$mopped ) {
68 6         36 *{ $package . '::' . $sub } = sub {
69 4     4   1068 $invokes->{$package}->{$sub}++;
70 4         15 $old->( @_ );
71 6         77 };
72             }
73             }
74            
75 3         9 1;
76             }
77              
78             sub test_coverage_except {
79 2     2 1 16 my ($package,@subroutines) = @_;
80            
81 2         4 for my $subname(@subroutines){
82 2 50 33     25 if(exists $invokes->{$package} and
      33        
83             exists $invokes->{$package}->{$subname} and
84             exists $self->{subs}->{$package}){
85 2         3 @{$self->{subs}->{$package}} = grep{$_->[0] ne $subname}@{$self->{subs}->{$package}};
  2         10  
  4         12  
  2         6  
86 2         11 delete $invokes->{$package}->{$subname};
87             }
88             }
89             }
90              
91             sub all_test_coverage_ok {
92 1     1 1 6 my ($msg) = @_;
93            
94 1         5 for my $package(keys %$invokes){
95 1         3 ok_test_coverage($package,$msg);
96             }
97 1         64 1;
98             }
99              
100             sub ok_test_coverage {
101 6     6 1 30 my ($package,$msg) = @_;
102            
103 6 50 66     54 if(!$package or (!exists $invokes->{$package})
      66        
104             and $package !~ /^(?:\w+(?:::)?)+$/){
105 2         4 $package = $last;
106             }
107            
108 6 50       21 unless(exists $invokes->{$package}){
109 0         0 warn $package.' was not tested';
110 0         0 return;
111             }
112            
113 6 100       18 my $bool_msg = defined $msg ? 1 : 0;
114 6         9 my $title = 'Test test-coverage ';
115 6         7 my $missing;
116            
117 6         10 my $bool_coverage = 1;
118 6         9 for my $sub(map{$_->[0]}@{$self->{subs}->{$package}}){
  10         32  
  6         21  
119 10 50 33     76 if(!exists $invokes->{$package}->{$sub} or $invokes->{$package}->{$sub} == 0){
120 0 0 0     0 $missing = defined $missing && !$bool_msg ? $missing . $sub . ' ' : $sub . ' ';
121 0         0 $bool_coverage = 0;
122             }
123             }
124            
125 6 100       19 if(!$bool_msg){
126 5         6 $msg = $title;
127 5 50       18 $msg .= $missing.' are missing' if(defined $missing);
128             }
129            
130 6         53 $test->cmp_ok($bool_coverage,"==",1,$msg);
131 6         4287 1;
132             }
133              
134             sub reset_test_coverage{
135 0     0 1 0 my ($self,$pkg) = @_;
136 0         0 for my $key(keys %{$invokes->{$pkg}}){
  0         0  
137 0         0 $invokes->{$pkg}->{$key} = 0;
138             }
139             }
140              
141             sub reset_all_test_coverage{
142 0     0 1 0 my ($self) = @_;
143 0         0 for my $pkg(keys %{$invokes}){
  0         0  
144 0         0 $self->reset_test_coverage($pkg);
145             }
146             }
147              
148             sub _get_subroutines{
149 3     3   7 my ($pkg,$test) = @_;
150            
151 3         322 eval qq{ require $pkg };
152 3 50       512 print STDERR $@ if $@;
153 3 50       20 return if $@;
154            
155 3   33     23 $test ||= $pkg;
156              
157 3         326 my $symdump = Devel::Symdump->new($pkg);
158              
159 3         7 my @symbols;
160 3         149 for my $func ($symdump->functions ) {
161 6         15 my $owner = _get_sub(\&{$func});
  6         31  
162 6         91 $owner =~ s/^\*(.*)::.*?$/$1/;
163 6 50       23 next if $owner ne $test;
164              
165             # check if it's on the whitelist
166 6         654 $func =~ s/${pkg}:://;
167              
168 6 50       497 push @symbols, [$func,$owner] unless $func =~ /^_/;
169             }
170            
171 3         811 $self->{subs}->{$pkg} = \@symbols;
172            
173 3         77 1;
174             }
175              
176             sub _get_sub {
177 6     6   11 my ($svref) = @_;
178 6         36 my $b_cv = B::svref_2object($svref);
179 5     5   52 no strict 'refs';
  5         9  
  5         447  
180 6         12 return *{ $b_cv->GV->STASH->NAME . "::" . $b_cv->GV->NAME };
  6         129  
181             }
182              
183             1;
184              
185             __END__