File Coverage

lib/TAP/Formatter/HTML/Session.pm
Criterion Covered Total %
statement 98 104 94.2
branch 43 50 86.0
condition 23 34 67.6
subroutine 13 13 100.0
pod 0 7 0.0
total 177 208 85.1


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             TAP::Formatter::HTML::Session - TAP Test Harness output delegate for html output
4              
5             =head1 SYNOPSIS
6              
7             # see TAP::Formatter::HTML
8              
9             =cut
10              
11             package TAP::Formatter::HTML::Session;
12              
13 14     14   114 use strict;
  14         29  
  14         438  
14 14     14   66 use warnings;
  14         38  
  14         578  
15              
16             # DEBUG:
17             #use Data::Dumper 'Dumper';
18              
19 14     14   96 use base qw( TAP::Base );
  14         24  
  14         1329  
20 14     14   6731 use accessors qw( test formatter parser results html_id meta closed );
  14         13234  
  14         66  
21              
22             our $VERSION = '0.13';
23              
24             sub _initialize {
25 38     38   2988 my ($self, $args) = @_;
26              
27 38   50     297 $args ||= {};
28 38         742 $self->SUPER::_initialize($args);
29              
30 38         1437 $self->results([])->meta({})->closed(0);
31 38         1118 foreach my $arg (qw( test parser formatter )) {
32 114 50       1349 $self->$arg($args->{$arg}) if defined $args->{$arg};
33             }
34              
35             # make referring to it in HTML easy:
36 38         372 my $html_id = $self->test;
37 38         821 $html_id =~ s/[^a-zA-Z\d-]/-/g;
38 38         308 $self->html_id( $html_id );
39              
40 38         424 $self->info( $self->test, ':' );
41              
42 38         326 return $self;
43             }
44              
45             # Called by TAP::Parser to create a result after a session is opened
46             # TODO: override TAP::Parser::ResultFactory and add html-aware results?
47             # OR: mixin some methods to the results.
48             # this logic is getting cumbersome. :-/
49             sub result {
50 1775     1775 0 3257961 my ($self, $result) = @_;
51             #warn ref($self) . "->result called with args: " . Dumper( $result );
52              
53 1775         3559 my $iter = $self->html_id_iterator;
54 1775 100       11694 if ($result->is_test) {
55 1206         7257 $self->log( $result->as_string );
56             # make referring to it in HTML easy:
57 1206 50       5658 $result->{html_id} = $iter ? $iter->() : $self->html_id . '-' . $result->number;
58              
59             # set test status to avoid the hassle of recalculating it in the template:
60 1206 100       2696 $result->{test_status} = $result->has_todo ? 'todo-' : '';
61 1206 100       6327 $result->{test_status} .= $result->has_skip ? 'skip-' : '';
62 1206 100       5782 $result->{test_status} .= $result->is_actual_ok ? 'ok' : 'not-ok';
63              
64             # also provide a 'short' status name to reduce size of html:
65 1206         5973 my $short;
66 1206 100       2184 if ($result->has_todo) {
    100          
    100          
67 64 100       283 if ($result->is_actual_ok) {
68 4         51 $short = 'u'; # todo-ok = "unexpected" ok
69             } else {
70 60         391 $short = 't'; # todo-not-ok
71             }
72             } elsif ($result->has_skip) {
73 30         252 $short = 's'; # skip-ok
74             } elsif ($result->is_actual_ok) {
75 999         9952 $short = 'k'; # ok
76             } else {
77 113         1294 $short = 'n'; # not-ok
78             }
79 1206         2333 $result->{short_test_status} = $short;
80              
81             # keep track of passes for percent_passed calcs:
82 1206 100       2191 if ($result->is_ok) {
83 1091         15729 $self->meta->{passed}++;
84             }
85              
86             # keep track of passes (including unplanned!) for actual_percent_passed calcs:
87 1206 100 66     6019 if ($result->is_ok || $result->is_unplanned && $result->is_actual_ok) {
      100        
88 1093         15014 $self->meta->{passed_including_unplanned}++;
89             }
90              
91             # mark passed todo tests for easy reference:
92 1206 100 100     6368 if ($result->has_todo && $result->is_actual_ok) {
93 4         55 $result->{todo_passed} = 1;
94             }
95             } else {
96 569         3915 $self->info( $result->as_string );
97             }
98              
99 1775         9898 $self->set_result_css_type( $result );
100              
101 1775         2305 push @{ $self->results }, $result;
  1775         3215  
102 1775         7481 return;
103             }
104              
105             # TODO: inheritance was created for a reason... use it
106 14         9435 use constant result_css_type_map =>
107             {
108             plan => 'pln',
109             pragma => 'prg',
110             test => 'tst',
111             comment => 'cmt',
112             bailout => 'blt',
113             version => 'ver',
114             unknown => 'unk',
115             yaml => 'yml',
116 14     14   9962 };
  14         30  
117              
118             sub set_result_css_type {
119 1775     1775 0 2730 my ($self, $result) = @_;
120 1775   50     3129 my $type = $result->type || 'unknown';
121 1775   50     8911 my $css_type = $self->result_css_type_map->{$type} || 'unk';
122 1775         3275 $result->{css_type} = $css_type;
123 1775         2712 return $self;
124             }
125              
126             # Called by TAP::?? to indicate there are no more test results coming
127             sub close_test {
128 38     38 0 134426 my ($self, @args) = @_;
129             # warn ref($self) . "->close_test called with args: " . Dumper( [@args] );
130             #print STDERR 'end of: ', $self->test, "\n\n";
131 38         213 $self->closed(1);
132 38         275 return;
133             }
134              
135             sub as_report {
136 38     38 0 98 my ($self) = @_;
137 38         112 my $p = $self->parser;
138 38         225 my $r = {
139             test => $self->test,
140             html_id => $self->html_id,
141             results => $self->results,
142             };
143              
144             # add parser info:
145 38         553 for my $key (qw(
146             tests_planned
147             tests_run
148             start_time
149             end_time
150             skip_all
151             has_problems
152             passed
153             failed
154             todo_passed
155             actual_passed
156             actual_failed
157             wait
158             exit
159             )) {
160 494         3649 $r->{$key} = $p->$key;
161             }
162              
163 38         349 $r->{num_parse_errors} = scalar $p->parse_errors;
164 38         277 $r->{parse_errors} = [ $p->parse_errors ];
165 38         236 $r->{passed_tests} = [ $p->passed ];
166 38         987 $r->{failed_tests} = [ $p->failed ];
167              
168             # do some other handy calcs:
169 38 100       345 $r->{test_status} = $r->{has_problems} ? 'failed' : 'passed';
170 38         106 $r->{elapsed_time} = $r->{end_time} - $r->{start_time};
171 38         124 $r->{severity} = '';
172 38 100       146 if ($r->{tests_planned}) {
    50          
173             # Calculate percentage passed as # passes *excluding* unplanned passes
174             # so we can't get > 100%. Also calc # passes _including_ unplanned
175             # in case that's useful for someone.
176 34   50     122 my $num_passed = $self->meta->{passed} || 0;
177 34   50     248 my $num_actual_passed = $self->meta->{passed_including_unplanned} || 0;
178 34         411 my $p = $r->{percent_passed} = sprintf('%.1f', $num_passed / $r->{tests_planned} * 100);
179 34         183 $r->{percent_actual_passed} = sprintf('%.1f', $num_actual_passed / $r->{tests_planned} * 100);
180 34 100       206 if ($p != 100) {
181 11         30 my $s;
182 11 50       87 if ($p < 25) { $s = 'very-high' }
  0 50       0  
    100          
    50          
183 0         0 elsif ($p < 50) { $s = 'high' }
184 9         32 elsif ($p < 75) { $s = 'med' }
185 2         5 elsif ($p < 95) { $s = 'low' }
186 0         0 else { $s = 'very-low' }
187             # classify >100% as very-low
188 11         34 $r->{severity} = $s;
189             }
190             } elsif ($r->{skip_all}) {
191             ; # do nothing
192             } else {
193 0         0 $r->{percent_passed} = 0;
194 0         0 $r->{severity} = 'very-high';
195             }
196              
197 38 100       135 if (my $num = $r->{num_parse_errors}) {
198 2 50 33     22 if ($num == 1 && ! $p->is_good_plan) {
199 2   50     34 $r->{severity} ||= 'low'; # prefer value set calculating % passed
200             } else {
201 0         0 $r->{severity} = 'very-high';
202             }
203             }
204              
205             # check for scripts that died abnormally:
206 38 100 100     302 if ($r->{exit} && $r->{exit} == 255 && $p->is_good_plan) {
      100        
207 6   50     66 $r->{severity} ||= 'very-high';
208             }
209              
210             # catch-all:
211 38 100       125 if ($r->{has_problems}) {
212 19   50     52 $r->{severity} ||= 'high';
213             }
214              
215 38         126 return $r;
216             }
217              
218             sub html_id_iterator {
219 1775     1775 0 3868 shift->formatter->html_id_iterator;
220             }
221              
222             sub log {
223 1206     1206 0 22282 my ($self, @args) = @_;
224 1206         2076 $self->formatter->log_test(@args);
225             }
226              
227             sub info {
228 607     607 0 2907 my ($self, @args) = @_;
229 607         1239 $self->formatter->log_test_info(@args);
230             }
231              
232              
233             1;
234              
235             __END__