File Coverage

blib/lib/Tapper/TAP/Harness.pm
Criterion Covered Total %
statement 286 288 99.3
branch 70 80 87.5
condition 38 49 77.5
subroutine 34 34 100.0
pod 5 5 100.0
total 433 456 94.9


line stmt bran cond sub pod time code
1             # git description: v5.0.10-2-g4e8ff93
2              
3             our $AUTHORITY = 'cpan:TAPPER';
4             # ABSTRACT: Tapper - Tapper specific TAP handling
5             $Tapper::TAP::Harness::VERSION = '5.0.11';
6             use 5.010;
7 35     35   1811002 use strict;
  35         384  
8 35     35   174 use warnings;
  35         101  
  35         799  
9 35     35   168  
  35         48  
  35         986  
10             use TAP::Parser;
11 35     35   15610 use TAP::Parser::Aggregator;
  35         1384771  
  35         991  
12 35     35   12689 use Directory::Scratch;
  35         169508  
  35         851  
13 35     35   14450 use File::Temp 'tempdir', 'tempfile';
  35         1343275  
  35         170  
14 35     35   1174 use YAML::Tiny;
  35         67  
  35         2102  
15 35     35   16342 use Archive::Tar;
  35         147741  
  35         1659  
16 35     35   19576 use IO::Scalar;
  35         2301692  
  35         2114  
17 35     35   13024 use IO::String;
  35         99090  
  35         1179  
18 35     35   218 use TAP::DOM;
  35         56  
  35         534  
19 35     35   15189  
  35         539046  
  35         4280  
20              
21             our @SUITE_HEADER_KEYS_GENERAL = qw(suite-version
22             hardwaredb-systems-id
23             machine-name
24             machine-description
25             reportername
26             starttime-test-program
27             endtime-test-program
28             );
29              
30             our @SUITE_HEADER_KEYS_DATE = qw(starttime-test-program
31             endtime-test-program
32             );
33              
34             our @SUITE_HEADER_KEYS_REPORTGROUP = qw(reportgroup-arbitrary
35             reportgroup-testrun
36             reportgroup-primary
37             owner
38             );
39              
40             our @SUITE_HEADER_KEYS_REPORTCOMMENT = qw(reportcomment );
41              
42             our @SECTION_HEADER_KEYS_GENERAL = qw(ram cpuinfo bios lspci lsusb uname osname uptime language-description
43             flags changeset kernel description
44             xen-version xen-changeset xen-dom0-kernel xen-base-os-description
45             xen-guest-description xen-guest-test xen-guest-start xen-guest-flags xen-hvbits
46             kvm-module-version kvm-userspace-version kvm-kernel
47             kvm-base-os-description kvm-guest-description
48             kvm-guest-test kvm-guest-start kvm-guest-flags
49             simnow-svn-version
50             simnow-version
51             simnow-svn-repository
52             simnow-device-interface-version
53             simnow-bsd-file
54             simnow-image-file
55             ticket-url wiki-url planning-id moreinfo-url codereview-url
56             tags
57             );
58              
59             use Moose;
60 35     35   15395  
  35         12258253  
  35         212  
61             has tap => ( is => 'rw', isa => 'Str' );
62             has tap_is_archive => ( is => 'rw' );
63             has parsed_report => ( is => 'rw', isa => 'HashRef', default => sub {{tap_sections => []}});
64             has section_names => ( is => 'rw', isa => 'HashRef', default => sub {{}} );
65              
66             our $re_prove_section = qr/^([-_\d\w\/.]*\w)\s?\.{2,}\s*$/;
67             our $re_tapper_meta = qr/^#\s*((?:Tapper|Artemis|Test)-)([-\w]+):(.+)$/i;
68             our $re_tapper_meta_section = qr/^#\s*((?:Tapper|Artemis|Test)-Section:)\s*(\S.*)$/i;
69             our $re_explicit_section_start = qr/^#\s*((?:Tapper|Artemis|Test)-explicit-section-start:)\s*(\S*)/i;
70              
71             my $prove1;
72             my $prove2;
73 17     17   42 ($prove1 = $^X) =~ s/perl([\d.]*)$/prove$1/;
74             ($prove2 = $^X) =~ s/[^\/]*$/prove/;
75 17         198 return $prove1 if -e $prove1;
76 17         130 return $prove2 if -e $prove2;
77 17 50       434 return; # undef/fail
78 0 0       0 }
79 0         0  
80             # report a uniqe section name
81             {
82             my ($self, $section_name) = @_;
83             return if not defined $section_name;
84             my $trail_number = 1;
85 460     460   1030 if (defined $self->section_names->{$section_name}
86 460 100       977 and not $section_name =~ m/\d$/)
87 450         578 {
88 450 100 66     10541 $section_name .= $trail_number;
89             }
90             while (defined $self->section_names->{$section_name}) {
91 42         97 $trail_number++;
92             $section_name =~ s/\d+$/$trail_number/;
93 450         8914 }
94 16         33 $self->section_names->{$section_name} = 1;
95 16         383 return $section_name;
96             }
97 450         8790  
98 450         1117 # hot fix known TAP errors
99             my ($tap) = @_;
100              
101             # TAP::Parser chokes on that
102             $tap =~ s/^(\s+---)\s+$/$1/msg;
103 49     49   130  
104             # known wrong YAML-in-TAP in database,
105             # usually Kernbench wrapper output
106 49         7930 $tap =~ s/^(\s+)(jiffies)\s*$/$1Clocksource: $2/msg;
107             $tap =~ s/^(\s+kvm-clock)\s*$/$1: ~/msg;
108             $tap =~ s/^(\s+acpi_pm)\s*$/$1: ~/msg;
109             $tap =~ s/^(\s+Cannot determine clocksource)\s*$/ Cannot_determine_clocksource: ~/msg;
110 49         1857 $tap =~ s/^(\s+linetail):\s*$/$1: ~/msg;
111 49         1611 $tap =~ s/^(\s+CPU\d+):\s*$/$1: ~/msg;
112 49         799 $tap =~ s/^(\s+)(\w{3} \w{3} +\d+ \d+:\d+:\d+ \w+ \d{4})$/$1date: $2/msg;
113 49         770575 $tap =~ s/^(\s+)(2\.6\.\d+[^\n]*)$/$1kernel: $2/msg; # kernel version
114 49         603515 $tap =~ s/^(\s+)(Average)\s*([^\n]*)$/$1average: $3/msg;
115 49         5889 $tap =~ s/^(\s+)(Elapsed Time)\s*([^\n]*)$/$1elapsed_time: $3/msg;
116 49         6534 $tap =~ s/^(\s+)(User Time)\s*([^\n]*)$/$1user_time: $3/msg;
117 49         24440 $tap =~ s/^(\s+)(System Time)\s*([^\n]*)$/$1system_time: $3/msg;
118 49         1596 $tap =~ s/^(\s+)(Percent CPU)\s*([^\n]*)$/$1percent_cpu: $3/msg;
119 49         1463 $tap =~ s/^(\s+)(Context Switches)\s*([^\n]*)$/$1context_switches: $3/msg;
120 49         1604 $tap =~ s/^(\s+)(Sleeps)\s*([^\n]*)$/$1sleeps: $3/msg;
121 49         1489  
122 49         845 return $tap;
123 49         1199 }
124 49         1224  
125             {
126 49         209 my ($self) = @_;
127             my @sections = $self->_get_tap_sections_from_single;
128             $self->_collect_meta_from_sections(@sections);
129             }
130              
131 13     13   29  
132 13         49 {
133 13         47 my ($self) = @_;
134             return if $self->tap_is_archive;
135             my @plans = ($self->tap) =~ m/^(1\.\.\d+)/mg;
136             return(int(@plans) == 1);
137             }
138              
139 62     62 1 138  
140 62 50       1358  
141 62         1287 {
142 62         361 my ($self) = @_;
143              
144             # Order matters
145             return $self->_parse_tap_into_sections_archive(@_) if $self->tap_is_archive;
146             return $self->_parse_tap_into_sections_one_section(@_) if $self->tap_single_plan;
147             return $self->_parse_tap_into_sections_raw(@_);
148             }
149 65     65   146  
150              
151              
152 65 100       1441 # return sections
153 62 100       218 {
154 49         183 my ($self) = @_;
155              
156              
157             my $report_tap = $self->tap;
158 88     88 1 97  
  88         386  
159             my $TAPVERSION = "TAP Version 13";
160             $report_tap = $TAPVERSION."\n".$report_tap unless $report_tap =~ /^TAP Version/msi;
161              
162             $report_tap = _fix_broken_tap($report_tap);
163 49     49   109 my $parser = new TAP::Parser ({ tap => $report_tap, version => 13 });
164              
165             my $i = 0;
166 49         972 my %section;
167             my $looks_like_prove_output = 0;
168 49         99 $self->parsed_report->{report_meta} = {
169 49 100       2396 'suite-name' => 'unknown',
170             'suite-version' => 'unknown',
171 49         164 'suite-type' => 'unknown',
172 49         639 'reportcomment' => undef,
173             };
174 49         34286 my $sections_marked_explicit = 0;
175 49         91 my $last_line_was_version = 0;
176 49         81 my $last_line_was_plan = 0;
177              
178 49         1616 while ( my $line = $parser->next )
179             {
180             my $raw = $line->raw;
181             my $is_plan = $line->is_plan;
182             my $is_version = $line->is_version;
183 49         110 my $is_unknown = $line->is_unknown;
184 49         74 my $is_yaml = $line->is_yaml;
185 49         97  
186             # prove section
187 49         183 if ( $is_unknown and $raw =~ $re_prove_section ) {
188             $looks_like_prove_output ||= 1;
189 11476         2040387 }
190 11476         30328  
191 11476         42491 # ----- store previous section, start new section -----
192 11476         38626  
193 11476         37762 $sections_marked_explicit = 1 if $raw =~ $re_explicit_section_start;
194              
195              
196 11476 100 100     41947 # start new section
197 88   100     198 if ( $raw =~ $re_explicit_section_start and ! $last_line_was_version
198             or
199             (! $sections_marked_explicit
200             and ( $i == 0 or
201             ( ! $looks_like_prove_output
202 11476 100       34230 and
203             (
204             ( $is_plan and not $last_line_was_version ) or
205             ( $is_version and not $last_line_was_plan )
206 11476 100 100     57550 )
      66        
      100        
      100        
207             ) or
208             ( $looks_like_prove_output and
209             ! $last_line_was_version and
210             ! $last_line_was_plan and
211             $raw =~ $re_prove_section
212             ) ) ) )
213             {
214             if (keys %section) {
215             # Store a copy (ie., not \%section) so it doesn't get overwritten in next loop
216             fix_last_ok(\ $section{raw}) if $looks_like_prove_output;
217             push @{$self->parsed_report->{tap_sections}}, { %section };
218             }
219             %section = ();
220             }
221              
222              
223 450 100       994 # ----- extract some meta information -----
224              
225 401 100       798 # a normal TAP line
226 401         517 if ( not $is_unknown ) {
  401         10442  
227             $section{raw} .= "$raw\n";
228 450         1023 }
229              
230             # looks like tapper meta line
231             if ( $line->is_comment and $raw =~ $re_tapper_meta )
232             {
233             my $key = lc $2;
234             my $val = $3;
235 11476 100       16455 $val =~ s/^\s+//;
236 10829         21316 $val =~ s/\s+$//;
237             if ($raw =~ $re_tapper_meta_section) {
238             $section{section_name} ||= $self->_unique_section_name( $val );
239             }
240 11476 100 100     18060 $section{section_meta}{$key} = $val; # section keys
241             $self->parsed_report->{report_meta}{$key} = $val; # also global keys, later entries win
242 953         8724 }
243 953         1469  
244 953         2193 # looks like filename line from "prove"
245 953         1756 if ( $is_unknown and $raw =~ $re_prove_section )
246 953 100       3411 {
247 326   66     1176 my $section_name = $self->_unique_section_name( $1 );
248             $section{section_name} //= $section_name;
249 953         1967 }
250 953         20147  
251             $i++;
252             $last_line_was_plan = $is_plan ? 1 : 0;
253             $last_line_was_version = $is_version ? 1 : 0;
254 11476 100 100     60504 }
255              
256 88         196 # store last section
257 88   33     252 fix_last_ok(\ $section{raw}) if $looks_like_prove_output;
258             push @{$self->parsed_report->{tap_sections}}, { %section } if keys %section;
259              
260 11476         12003 $self->fix_section_names;
261 11476 100       15166 }
262 11476 100       32481  
263             {
264             my ($self) = @_;
265              
266 49 100       12239 # some stacking to enable Archive::Tar read compressed in-memory string
267 49 50       372 my $TARSTR = IO::String->new($self->tap);
  49         1387  
268             my $TARZ = IO::Zlib->new($TARSTR, "rb");
269 49         331 my $tar = Archive::Tar->new($TARZ);
270              
271             my $meta = YAML::Tiny::Load($tar->get_content("meta.yml"));
272             my @tap_sections = map {
273             my $f1 = $_; # original name as-is
274 3     3   7 my $f2 = $_; $f2 =~ s,^\./,,; # force no-leading-dot
275             my $f3 = $_; $f3 = "./$_"; # force leading-dot
276             local $Archive::Tar::WARN = 0;
277 3         62 my $tap = $tar->get_content($f1) // $tar->get_content($f2) // $tar->get_content($f3);
278 3         230 $tap = "# Untar Bummer!" if ! defined $tar;
279 3         7565 { tap => $tap, filename => $f1 };
280             } @{$meta->{file_order}};
281 3         12815 return @tap_sections;
282             }
283 13         19  
284 13         13 {
  13         22  
285 13         14 my ($self) = @_;
  13         20  
286 13         15 my ($section_name) = ($self->tap) =~ $re_tapper_meta_section;
287 13   33     27 return({ tap => $self->tap, filename => $section_name}) if $section_name;
      33        
288 13 50       933 return({ tap => $self->tap});
289 13         43 }
290 3         3112  
  3         15  
291 3         88  
292             {
293             my ($self) = @_;
294              
295             my @tap_sections = $self->_get_tap_sections_from_archive($self->tap);
296 13     13   26 $self->_collect_meta_from_sections(@tap_sections);
297 13         261 }
298 13 50       36  
299 13         252 my ($self, @tap_sections) = @_;
300              
301             my $looks_like_prove_output = 0;
302             $self->parsed_report->{report_meta} = {
303             'suite-name' => 'unknown',
304             'suite-version' => 'unknown',
305 3     3   7 'suite-type' => 'unknown',
306             'reportcomment' => undef,
307 3         182 };
308 3         319  
309             my %section;
310              
311             foreach my $tap_file (@tap_sections)
312 16     16   41 {
313              
314 16         31 my $tap = $tap_file->{tap};
315             my $filename = $tap_file->{filename};
316 16         456  
317             # no empty tap
318             $tap = join "\n", grep { defined($_) } TAP::DOM->new(tap=>"", version=>13, noempty_tap=>1)->to_tap
319             if !$tap;
320              
321             my $parser = TAP::Parser->new ({ tap => $tap, version => 13 });
322 16         31  
323             # ----- store previous section, start new section -----
324 16         51  
325             # start new section
326             if (keys %section)
327 26         52 {
328 26         90 # Store a copy (ie., not \%section) so it doesn't get overwritten in next loop
329             push @{$self->parsed_report->{tap_sections}}, { %section };
330             }
331 26 100       70 %section = ();
  4         1868  
332              
333             while ( my $line = $parser->next )
334 26         324 {
335             my $raw = $line->raw;
336             my $is_plan = $line->is_plan;
337             my $is_version = $line->is_version;
338             my $is_unknown = $line->is_unknown;
339 26 100       14522 my $is_yaml = $line->is_yaml;
340              
341             # ----- extract some meta information -----
342 10         16  
  10         271  
343             # a normal TAP line and not a summary line from "prove"
344 26         60 if ( not $is_unknown )
345             {
346 26         78 $section{raw} .= "$raw\n";
347             }
348 186         43193  
349 186         618 my $re_tapper_meta = qr/^#\s*((?:Tapper|Artemis|Test)-)([-\w]+):(.+)$/i;
350 186         887 my $re_tapper_meta_section = qr/^#\s*((?:Tapper|Artemis|Test)-Section:)\s*(.+)$/i;
351 186         747 # looks like tapper meta line
352 186         818 if ( $line->is_comment and $raw =~ m/^#\s*((?:Tapper|Artemis|Test)-)([-\w]+):(.+)$/i ) # (
353             {
354             # TODO: refactor inner part with _parse_tap_into_sections_raw()
355             my $key = lc $2;
356             my $val = $3;
357 186 100       698 $val =~ s/^\s+//;
358             $val =~ s/\s+$//;
359 183         452 if ($raw =~ $re_tapper_meta_section)
360             {
361             $section{section_name} = $self->_unique_section_name( $val );
362 186         581 }
363 186         331 $section{section_meta}{$key} = $val; # section keys
364             $self->parsed_report->{report_meta}{$key} = $val; # also global keys, later entries win
365 186 100 100     486 }
366             }
367             $section{section_name} //= $self->_unique_section_name( $filename );
368 71         813 }
369 71         125  
370 71         1352 # store last section
371 71         146 push @{$self->parsed_report->{tap_sections}}, { %section } if keys %section;
372 71 100       286  
373             $self->fix_section_names;
374 3         13 }
375              
376 71         216  
377 71         1642 {
378             my ($self) = @_;
379              
380 26   100     5413 # augment section names
381             for (my $i = 0; $i < @{$self->parsed_report->{tap_sections}}; $i++)
382             {
383             $self->parsed_report->{tap_sections}->[$i]->{section_name} //= sprintf("section-%03d", $i);
384 16 50       58 }
  16         551  
385              
386 16         68 # delete whitespace from section names
387             for (my $i = 0; $i < @{$self->parsed_report->{tap_sections}}; $i++)
388             {
389             $self->parsed_report->{tap_sections}->[$i]->{section_name} =~ s/\s/-/g;
390             }
391             }
392 65     65 1 302  
393             {
394             my ($self) = @_;
395 65         450  
  541         10476  
396             my $aggregator = new TAP::Parser::Aggregator;
397 476   66     9225  
398             my $TAPVERSION = "TAP Version 13";
399              
400             $aggregator->start;
401 65         144 foreach my $section (@{$self->parsed_report->{tap_sections}})
  541         10329  
402             {
403 476         9240 my $rawtap = $section->{raw} || '';
404             $rawtap = $TAPVERSION."\n".$rawtap unless $rawtap =~ /^TAP Version/msi;
405             my $parser = new TAP::Parser ({ tap => $rawtap });
406             $parser->run;
407             $aggregator->add( $section->{section_name} => $parser );
408             }
409 65     65   151 $aggregator->stop;
410              
411 65         531 foreach (qw(total
412             passed
413 65         3847 parse_errors
414             skipped
415 65         288 todo
416 65         1793 todo_passed
  65         1668  
417             failed
418 476   50     38070 todo_passed
419 476 100       6823 ))
420 476         1984 {
421 476         184292 no strict 'refs'; ## no critic
422 476         2202917 $self->parsed_report->{stats}{$_} = $aggregator->$_;
423             }
424 65         6003 $self->parsed_report->{stats}{successgrade} = $aggregator->get_status;
425             $self->parsed_report->{stats}{success_ratio} = sprintf("%02.2f",
426 65         1461 $aggregator->total ? ($aggregator->passed / $aggregator->total * 100) : 100
427             );
428             }
429              
430             {
431             my ($self) = @_;
432              
433             # suite meta
434              
435             foreach my $key (@SUITE_HEADER_KEYS_GENERAL)
436 35     35   279582 {
  35         94  
  35         31371  
437 520         1501 my $value = $self->parsed_report->{report_meta}{$key};
438             my $accessor = $key;
439 65         263 $accessor =~ s/-/_/g;
440 65 50       204 $self->parsed_report->{db_report_meta}{$accessor} = $value if defined $value;
441             }
442              
443             foreach my $key (@SUITE_HEADER_KEYS_DATE)
444             {
445             my $value = $self->parsed_report->{report_meta}{$key};
446             my $accessor = $key;
447 65     65   206 $accessor =~ s/-/_/g;
448             $self->parsed_report->{db_report_date_meta}{$accessor} = $value if defined $value;
449             }
450              
451 65         246 foreach my $key (@SUITE_HEADER_KEYS_REPORTGROUP)
452             {
453 455         8883 my $value = $self->parsed_report->{report_meta}{$key};
454 455         629 my $accessor = $key;
455 455         1024 $accessor =~ s/-/_/g;
456 455 100       4556 $self->parsed_report->{db_report_reportgroup_meta}{$accessor} = $value if defined $value;
457             }
458              
459 65         260 foreach my $key (@SUITE_HEADER_KEYS_REPORTCOMMENT)
460             {
461 130         2587 my $value = $self->parsed_report->{report_meta}{$key};
462 130         190 my $accessor = $key;
463 130         518 $accessor =~ s/-/_/g;
464 130 100       1107 $self->parsed_report->{db_report_reportcomment_meta}{$accessor} = $value if defined $value;
465             }
466             }
467 65         246  
468             {
469 260         5061 my ($self) = @_;
470 260         382  
471 260         503 # section meta
472 260 100       1831  
473             foreach my $section ( @{$self->parsed_report->{tap_sections}} ) {
474             foreach my $key (@SECTION_HEADER_KEYS_GENERAL)
475 65         175 {
476             my $section_name = $section->{section_name};
477 65         1303 my $value = $section->{section_meta}{$key};
478 65         168 my $accessor = $key;
479 65         138 $accessor =~ s/-/_/g;
480 65 100       316 $section->{db_section_meta}{$accessor} = $value if defined $value;
481             }
482             }
483             }
484              
485             {
486 65     65   139 my ($self) = @_;
487              
488             $self->_process_suite_meta_information;
489             $self->_process_section_meta_information;
490 65         103  
  65         1382  
491 476         629 }
492              
493 19992         19847  
494 19992         20090 {
495 19992         18856 my ($self) = @_;
496 19992         26568 return if @{$self->parsed_report->{tap_sections}};
497 19992 100       28880 $self->_parse_tap_into_sections();
498             $self->_aggregate_sections();
499             $self->_process_meta_information();
500              
501             }
502              
503             {
504 65     65   278 my ($html) = @_;
505              
506 65         240 $html =~ s/^.*<body>//msg; # cut start
507 65         198 $html =~ s,<div id="footer">Generated by TAP::Formatter::HTML[^<]*</div>,,msg; # cut footer
508             # cut navigation that was meant for standalone html pages, not needed by us
509             $html =~ s,<div id="menu">[\t\n\s]*<ul>[\t\n\s]*<li>[\t\n\s]*<span id="show-all">[\t\n\s]*<a href="#" title="show all tests">show all</a>[\t\n\s]*</span>[\t\n\s]*<span id="show-failed">[\t\n\s]*<a href="#" title="show failed tests only">show failed</a>[\t\n\s]*</span>[\t\n\s]*</li>[\t\n\s]*</ul>[\t\n\s]*</div>,,msg;
510             $html =~ s,<th class="time">Time</th>,<th class="time">&nbsp;</th>,msg; # cut "Time" header
511              
512             return $html;
513             }
514 76     76 1 36565  
515 76 100       148  
  76         1922  
516 65         299 {
517 65         280 my ($self) = @_;
518 65         322  
519             $self->evaluate_report();
520              
521             my $temp = new Directory::Scratch (TEMPLATE => 'ATH_XXXXXXXXXXXX',
522             CLEANUP => 1);
523             my $dir = $temp->mkdir("section");
524 13     13   170 my $TAPVERSION = "TAP Version 13";
525             my @files = map {
526 13         6408 if ($_->{section_name} =~ m'(^\.{1,2})|/') {
527 13         1914 $_->{section_name} =~ s/^\.+$/_dot_/;
528             $_->{section_name} =~ s|^/||;
529 13         1716 }
530 13         1926 my $fname = "section/$_->{section_name}";
531             my $rawtap = $_->{raw};
532 13         121 $rawtap = $TAPVERSION."\n".$rawtap unless $rawtap =~ /^TAP Version/msi;
533             my $script_content = $rawtap;
534             my $file = $temp->touch($fname, $script_content);
535              
536             [ "$temp/$fname" => $_->{section_name} ];
537             } @{$self->parsed_report->{tap_sections}};
538 13     13 1 42551  
539             # Currently a TAP::Formatter::* is only usable via the
540 13         52 # TAP::Harness which in turn is easiest to use externally on
541             # unix shell level
542 13         143 my $prove = _get_prove() or die "Can not find 'prove', searched near $^X";
543              
544 13         11097 my $cmd = qq{cd $temp/section ; $^X $prove -vm --exec 'cat' --formatter=TAP::Formatter::HTML `find . -type f -print | sed -e 's,^\./,,' | sort`};
545 13         6824 my $html = qx( $cmd );
546              
547 76 100       1948 $html = _fix_generated_html( $html );
548 28         61  
549 28         40 $temp->cleanup; # above CLEANUP=>1 is not enough. Trust me.
550              
551 76         175 return $html;
552 76         155 }
553 76 100       2372  
554 76         118 1; # End of Tapper::TAP::Harness
555 76         224  
556              
557 76         244114 =pod
558 13         30  
  13         387  
559             =encoding UTF-8
560              
561             =head1 NAME
562              
563 13 50       393 Tapper::TAP::Harness - Tapper - Tapper specific TAP handling
564              
565 13         69 =head2 tap_single_plan
566 13         4294546  
567             Return true when TAP contains exactly one plan
568 13         702  
569             =head2 fix_last_ok
570 13         770  
571             The C<prove> tool adds an annoying last summary line, cut that away.
572 13         19245  
573             =head2 fix_section_names
574              
575             Create sensible section names that fit further processing,
576             eg. substitute whitespace by dashes, fill missing names, etc.
577              
578             =head2 evaluate_report
579              
580             Actually evaluate the content of the incoming report by parsing it,
581             aggregate the sections and extract contained meta information.
582              
583             =head2 generate_html
584              
585             Render TAP through TAP::Formatter::HTML and fix some formatting to fit
586             into Tapper.
587              
588             =head1 AUTHORS
589              
590             =over 4
591              
592             =item *
593              
594             AMD OSRC Tapper Team <tapper@amd64.org>
595              
596             =item *
597              
598             Tapper Team <tapper-ops@amazon.com>
599              
600             =back
601              
602             =head1 COPYRIGHT AND LICENSE
603              
604             This software is Copyright (c) 2022 by Advanced Micro Devices, Inc.
605              
606             This is free software, licensed under:
607              
608             The (two-clause) FreeBSD License
609              
610             =cut