File Coverage

blib/lib/App/ArchiveDevelCover.pm
Criterion Covered Total %
statement 155 160 96.8
branch 20 26 76.9
condition 6 12 50.0
subroutine 24 24 100.0
pod 0 7 0.0
total 205 229 89.5


line stmt bran cond sub pod time code
1             use 5.010;
2 3     3   517748 use Moose;
  3         36  
3 3     3   1385 use MooseX::Types::Path::Class;
  3         1290955  
  3         21  
4 3     3   22051 use DateTime;
  3         1167617  
  3         33  
5 3     3   5839 use File::Copy;
  3         1327425  
  3         142  
6 3     3   536 use HTML::TableExtract;
  3         2034  
  3         195  
7 3     3   1766 use experimental qw(switch);
  3         37621  
  3         18  
8 3     3   1307  
  3         2975  
  3         16  
9             # ABSTRACT: Archive Devel::Cover reports
10             our $VERSION = '1.004'; # VERSION
11              
12             with 'MooseX::Getopt';
13              
14             has [qw(from to)] => (is=>'ro',isa=>'Path::Class::Dir',coerce=>1,required=>1,);
15             has 'project' => (is => 'ro', isa=>'Str', lazy_build=>1);
16             my $self = shift;
17             my @list = $self->from->parent->dir_list;
18 2     2   5 return $list[-1] || 'unknown project';
19 2         51 }
20 2   50     280 has 'coverage_html' => (is=>'ro',isa=>'Path::Class::File',lazy_build=>1,traits=> ['NoGetopt']);
21             my $self = shift;
22             if (-e $self->from->file('coverage.html')) {
23             return $self->from->file('coverage.html');
24 4     4   7 }
25 4 100       92 else {
26 3         404 say "Cannot find 'coverage.html' in ".$self->from.'. Aborting';
27             exit;
28             }
29 1         142 }
30 1         51 has 'runtime' => (is=>'ro',isa=>'DateTime',lazy_build=>1,traits=> ['NoGetopt'],);
31             my $self = shift;
32             return DateTime->from_epoch(epoch=>$self->coverage_html->stat->mtime);
33             }
34             has 'archive_html' => (is=>'ro',isa=>'Path::Class::File',lazy_build=>1,traits=> ['NoGetopt']);
35 4     4   8 my $self = shift;
36 4         105 unless (-e $self->to->file('index.html')) {
37             my $tpl = $self->_archive_template;
38             my $fh = $self->to->file('index.html')->openw;
39             print $fh $tpl;
40 2     2   5 close $fh;
41 2 100       57 }
42 1         173 return $self->to->file('index.html');
43 1         29 }
44 1         265 has 'archive_db' => (is=>'ro',isa=>'Path::Class::File',lazy_build=>1,traits=> ['NoGetopt']);
45 1         60 my $self = shift;
46             return $self->to->file('archive_db');
47 2         180 }
48             has 'previous_stats' => (is=>'ro',isa=>'ArrayRef',lazy_build=>1,traits=>['NoGetopt']);
49             my $self = shift;
50             if (-e $self->archive_db) {
51 2     2   5 my $dbr = $self->archive_db->openr;
52 2         49 my @data = <$dbr>; # probably better to just get last line...
53             my @prev = split(/;/,$data[-1]);
54             return \@prev;
55             }
56 2     2   5 else {
57 2 100       72 return [undef,0,0,0];
58 1         81 }
59 1         167 }
60 1         7 has 'diff_html' => (is=>'ro',isa=>'Path::Class::File',lazy_build=>1,traits=> ['NoGetopt']);
61 1         42 my $self = shift;
62             return $self->to->subdir($self->runtime->iso8601)->file('diff.html');
63             }
64 1         90  
65             my $self = shift;
66             $self->archive;
67             $self->generate_diff;
68             $self->update_index;
69 1     1   4 }
70 1         46  
71             my $self = shift;
72              
73             my $from = $self->from;
74 4     4 0 5903 my $target = $self->to->subdir($self->runtime->iso8601);
75 4         14  
76 2         17 if (-e $target) {
77 2         12 say "This coverage report has already been archived.";
78             exit;
79             }
80              
81 4     4 0 11 $target->mkpath;
82             my $target_string = $target->stringify;
83 4         138  
84 4         96 while (my $f = $from->next) {
85             next unless $f=~/\.(html|css)$/;
86 3 100       214 copy($f->stringify,$target_string) || die "Cannot copy $from to $target_string: $!";
87 1         58 }
88 1         7  
89             say "archived coverage reports at $target_string";
90             }
91 2         84  
92 2         470 my $self = shift;
93              
94 2         87 my $te = HTML::TableExtract->new( headers => [qw(stm sub total)] );
95 22 100       6689 $te->parse(scalar $self->coverage_html->slurp);
96 12 50       337 my $rows =$te->rows;
97             my $last_row = $rows->[-1];
98              
99 2         927 $self->update_archive_html($last_row);
100             $self->update_archive_db($last_row);
101             }
102              
103 2     2 0 4 my ($self, $last_row) = @_;
104              
105 2         40 my $prev_stats = $self->previous_stats;
106 2         338 my $runtime = $self->runtime;
107 2         9453 my $date = $runtime->ymd('-').' '.$runtime->hms;
108 2         417 my $link = "./".$runtime->iso8601."/coverage.html";
109             my $diff = "./".$runtime->iso8601."/diff.html";
110 2         9  
111 2         926 my $new_stat = qq{\n<tr><td><a href="$link">$date</a></td><td><a href="$diff">diff</a></td>};
112             foreach my $val (@$last_row) {
113             $new_stat.=$self->td_style($val);
114             }
115 2     2 0 6 my $prev_total = $prev_stats->[3];
116             my $this_total = $last_row->[-1];
117 2         69 if ($this_total == $prev_total) {
118 2         50 $new_stat.=qq{<td class="c3">=</td>};
119 2         12 }
120 2         51 elsif ($this_total > $prev_total) {
121 2         47 $new_stat.=qq{<td class="c3">+</td>};
122             }
123 2         41 else {
124 2         6 $new_stat.=qq{<td class="c0">-</td>};
125 6         16 }
126              
127 2         6 $new_stat.="</tr>\n";
128 2         4  
129 2 50       11 my $archive = $self->archive_html->slurp;
    50          
130 0         0 $archive =~ s/(<!-- INSERT -->)/$1 . $new_stat/e;
131              
132             my $fh = $self->archive_html->openw;
133 2         14 print $fh $archive;
134             close $fh;
135              
136 0         0 unless (-e $self->to->file('cover.css')) {
137             copy($self->from->file('cover.css'),$self->to->file('cover.css')) || warn "Cannot copy cover.css: $!";
138             }
139 2         11 }
140              
141 2         65 my ($self, $last_row) = @_;
142 2         393 my $dbw = $self->archive_db->open(">>") || warn "Can't write archive.db: $!";
  2         62  
143             say $dbw join(';',$self->runtime->iso8601,@$last_row);
144 2         65 close $dbw;
145 2         355 }
146 2         212  
147             my $self = shift;
148 2 100       72  
149 1 50       144 my $prev = $self->previous_stats;
150             return unless $prev->[0];
151              
152             my $te_new = HTML::TableExtract->new( headers => [qw(file stm sub total)] );
153             $te_new->parse(scalar $self->coverage_html->slurp);
154 2     2 0 7 my $new_rows =$te_new->rows;
155 2   33     65 my $te_old = HTML::TableExtract->new( headers => [qw(file stm sub total)] );
156 2         378 $te_old->parse(scalar $self->to->subdir($prev->[0])->file('coverage.html')->slurp);
157 2         207 my $old_rows =$te_old->rows;
158              
159             my %diff;
160             foreach my $row (@$new_rows) {
161 2     2 0 5 my $file =shift(@$row);
162             $diff{$file}=$row;
163 2         77 }
164 2 100       8  
165             foreach my $row (@$old_rows) {
166 1         14 my $file =shift(@$row);
167 1         198 push(@{$diff{$file}},@$row);
168 1         4987 }
169 1         283  
170 1         160 my @output;
171 1         4809 foreach my $file (sort keys %diff) {
172             my $data = $diff{$file};
173 1         207  
174 1         4 my $line = qq{\n<tr><td>$file</td>};
175 2         4 foreach my $i (0,1,2) {
176 2         6 my $nv = $data->[$i] || 0;
177             my $ov = $data->[$i+3] || 0;
178             my $display = "$ov&nbsp;-&gt;&nbsp;$nv";
179 1         4 if ($nv == $ov) {
180 2         6 $line.=qq{<td>$display</td>};
181 2         4 }
  2         6  
182             elsif ($nv > $ov) {
183             $line.=$self->td_style(100,$display);
184 1         2 }
185 1         5 else {
186 2         12 $line.=$self->td_style(0,$display);
187             }
188 2         10 }
189 2         4 $line.="</tr>";
190 6   50     16 push(@output,$line);
191 6   50     9 }
192 6         12 my $table = join("\n",@output);
193 6 50       20 my $tpl = $self->_diff_template;
    50          
194 0         0 $tpl=~s/DATA/$table/;
195              
196             my $fh = $self->diff_html->openw;
197 6         12 print $fh $tpl;
198             close $fh;
199             }
200 0         0  
201             my ($self, $val, $display) = @_;
202             $display //=$val;
203 2         5 my $style;
204 2         5 given ($val) {
205             when ($_ < 75) { $style = 'c0' }
206 1         16 when ($_ < 90) { $style = 'c1' }
207 1         5 when ($_ < 100) { $style = 'c2' }
208 1         15 when ($_ >= 100) { $style = 'c3' }
209             }
210 1         42 return qq{<td class="$style">$display</td>};
211 1         216 }
212 1         120  
213             my $self = shift;
214             my $name = $self->project;
215             $self->_page_template(
216 12     12 0 20 "Test Coverage Archive for $name",
217 12   66     37 q{
218 12         13 <table>
219 12         15 <tr><th>Coverage Report</th><th>diff</th><th>stmt</th><th>sub</th><th>total</th><th>Trend</th></tr>
220 12         24 <!-- INSERT -->
  3         5  
221 9         33 </table>
  2         7  
222 7         24 });
  0         0  
223 7         8 }
  7         21  
224              
225 12         47 my $self = shift;
226             my $name = $self->project;
227             $self->_page_template(
228             "Test Coverage Diff for $name",
229 1     1   3 q{
230 1         42 <table>
231 1         6 <tr><th>File</th><th>stmt</th><th>sub</th><th>total</th></tr>
232             DATA
233             </table>
234             });
235             }
236              
237             my ($self, $title, $content) = @_;
238              
239             my $name = $self->project;
240             my $class = ref($self);
241             my $version = $class->VERSION;
242 1     1   2 return <<"EOTMPL";
243 1         37 <!DOCTYPE html
244 1         7 PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
245             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
246             <html xmlns="http://www.w3.org/1999/xhtml">
247             <!-- This file was generated by $class version $version -->
248             <head>
249             <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
250             <meta http-equiv="Content-Language" content="en-us"></meta>
251             <link rel="stylesheet" type="text/css" href="cover.css"></link>
252             <title>Test Coverage Archive for $name</title>
253             </head>
254             <body>
255 2     2   7  
256             <body>
257 2         57 <h1>$title</h1>
258 2         5  
259 2         43 $content
260 2         27  
261             <p>Generated by <a href="http://metacpan.org/module/$class">$class</a> version $version.</p>
262              
263             </body>
264             </html>
265             EOTMPL
266              
267             }
268              
269             __PACKAGE__->meta->make_immutable;
270             1;
271              
272              
273             =pod
274              
275             =encoding UTF-8
276              
277             =head1 NAME
278              
279             App::ArchiveDevelCover - Archive Devel::Cover reports
280              
281             =head1 VERSION
282              
283             version 1.004
284              
285             =head1 SYNOPSIS
286              
287             Backend for the C<archive_devel_cover.pl> command. See L<archive_devel_cover.pl> and/or C<perldoc archive_devel_cover.pl> for details.
288              
289             =head1 AUTHOR
290              
291             Thomas Klausner <domm@plix.at>
292              
293             =head1 COPYRIGHT AND LICENSE
294              
295             This software is copyright (c) 2012 - 2022 by Thomas Klausner.
296              
297             This is free software; you can redistribute it and/or modify it under
298             the same terms as the Perl 5 programming language system itself.
299              
300             =cut