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   112 use strict;
  14         36  
  14         439  
14 14     14   67 use warnings;
  14         28  
  14         555  
15              
16             # DEBUG:
17             #use Data::Dumper 'Dumper';
18              
19 14     14   95 use base qw( TAP::Base );
  14         26  
  14         1389  
20 14     14   7293 use accessors qw( test formatter parser results html_id meta closed );
  14         13575  
  14         59  
21              
22             our $VERSION = '0.12';
23              
24             sub _initialize {
25 38     38   3268 my ($self, $args) = @_;
26              
27 38   50     294 $args ||= {};
28 38         740 $self->SUPER::_initialize($args);
29              
30 38         1468 $self->results([])->meta({})->closed(0);
31 38         1159 foreach my $arg (qw( test parser formatter )) {
32 114 50       1477 $self->$arg($args->{$arg}) if defined $args->{$arg};
33             }
34              
35             # make referring to it in HTML easy:
36 38         423 my $html_id = $self->test;
37 38         881 $html_id =~ s/[^a-zA-Z\d-]/-/g;
38 38         280 $self->html_id( $html_id );
39              
40 38         410 $self->info( $self->test, ':' );
41              
42 38         340 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 3304092 my ($self, $result) = @_;
51             #warn ref($self) . "->result called with args: " . Dumper( $result );
52              
53 1775         3841 my $iter = $self->html_id_iterator;
54 1775 100       11722 if ($result->is_test) {
55 1206         7254 $self->log( $result->as_string );
56             # make referring to it in HTML easy:
57 1206 50       5581 $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       2642 $result->{test_status} = $result->has_todo ? 'todo-' : '';
61 1206 100       5984 $result->{test_status} .= $result->has_skip ? 'skip-' : '';
62 1206 100       5499 $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         5788 my $short;
66 1206 100       2405 if ($result->has_todo) {
    100          
    100          
67 64 100       317 if ($result->is_actual_ok) {
68 4         40 $short = 'u'; # todo-ok = "unexpected" ok
69             } else {
70 60         380 $short = 't'; # todo-not-ok
71             }
72             } elsif ($result->has_skip) {
73 30         331 $short = 's'; # skip-ok
74             } elsif ($result->is_actual_ok) {
75 999         9778 $short = 'k'; # ok
76             } else {
77 113         1202 $short = 'n'; # not-ok
78             }
79 1206         2286 $result->{short_test_status} = $short;
80              
81             # keep track of passes for percent_passed calcs:
82 1206 100       2011 if ($result->is_ok) {
83 1091         15149 $self->meta->{passed}++;
84             }
85              
86             # keep track of passes (including unplanned!) for actual_percent_passed calcs:
87 1206 100 66     5796 if ($result->is_ok || $result->is_unplanned && $result->is_actual_ok) {
      100        
88 1093         14629 $self->meta->{passed_including_unplanned}++;
89             }
90              
91             # mark passed todo tests for easy reference:
92 1206 100 100     6462 if ($result->has_todo && $result->is_actual_ok) {
93 4         67 $result->{todo_passed} = 1;
94             }
95             } else {
96 569         3935 $self->info( $result->as_string );
97             }
98              
99 1775         9504 $self->set_result_css_type( $result );
100              
101 1775         2027 push @{ $self->results }, $result;
  1775         3278  
102 1775         7329 return;
103             }
104              
105             # TODO: inheritance was created for a reason... use it
106 14         9881 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   9952 };
  14         43  
117              
118             sub set_result_css_type {
119 1775     1775 0 2695 my ($self, $result) = @_;
120 1775   50     3156 my $type = $result->type || 'unknown';
121 1775   50     9231 my $css_type = $self->result_css_type_map->{$type} || 'unk';
122 1775         3342 $result->{css_type} = $css_type;
123 1775         2529 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 138307 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         216 $self->closed(1);
132 38         309 return;
133             }
134              
135             sub as_report {
136 38     38 0 93 my ($self) = @_;
137 38         115 my $p = $self->parser;
138 38         300 my $r = {
139             test => $self->test,
140             html_id => $self->html_id,
141             results => $self->results,
142             };
143              
144             # add parser info:
145 38         570 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         3755 $r->{$key} = $p->$key;
161             }
162              
163 38         338 $r->{num_parse_errors} = scalar $p->parse_errors;
164 38         265 $r->{parse_errors} = [ $p->parse_errors ];
165 38         237 $r->{passed_tests} = [ $p->passed ];
166 38         1018 $r->{failed_tests} = [ $p->failed ];
167              
168             # do some other handy calcs:
169 38 100       415 $r->{test_status} = $r->{has_problems} ? 'failed' : 'passed';
170 38         116 $r->{elapsed_time} = $r->{end_time} - $r->{start_time};
171 38         98 $r->{severity} = '';
172 38 100       129 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     125 my $num_passed = $self->meta->{passed} || 0;
177 34   50     315 my $num_actual_passed = $self->meta->{passed_including_unplanned} || 0;
178 34         420 my $p = $r->{percent_passed} = sprintf('%.1f', $num_passed / $r->{tests_planned} * 100);
179 34         182 $r->{percent_actual_passed} = sprintf('%.1f', $num_actual_passed / $r->{tests_planned} * 100);
180 34 100       303 if ($p != 100) {
181 11         24 my $s;
182 11 50       110 if ($p < 25) { $s = 'very-high' }
  0 50       0  
    100          
    50          
183 0         0 elsif ($p < 50) { $s = 'high' }
184 9         21 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         28 $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       159 if (my $num = $r->{num_parse_errors}) {
198 2 50 33     58 if ($num == 1 && ! $p->is_good_plan) {
199 2   50     41 $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     248 if ($r->{exit} && $r->{exit} == 255 && $p->is_good_plan) {
      100        
207 6   50     58 $r->{severity} ||= 'very-high';
208             }
209              
210             # catch-all:
211 38 100       118 if ($r->{has_problems}) {
212 19   50     58 $r->{severity} ||= 'high';
213             }
214              
215 38         156 return $r;
216             }
217              
218             sub html_id_iterator {
219 1775     1775 0 4146 shift->formatter->html_id_iterator;
220             }
221              
222             sub log {
223 1206     1206 0 22343 my ($self, @args) = @_;
224 1206         2109 $self->formatter->log_test(@args);
225             }
226              
227             sub info {
228 607     607 0 2934 my ($self, @args) = @_;
229 607         1211 $self->formatter->log_test_info(@args);
230             }
231              
232              
233             1;
234              
235             __END__