File Coverage

blib/lib/Tapper/TAP/Harness.pm
Criterion Covered Total %
statement 281 283 99.2
branch 68 78 87.1
condition 38 49 77.5
subroutine 33 33 100.0
pod 5 5 100.0
total 425 448 94.8


line stmt bran cond sub pod time code
1             # git description: v5.0.8-1-gcd69c25
2              
3             our $AUTHORITY = 'cpan:TAPPER';
4             # ABSTRACT: Tapper - Tapper specific TAP handling
5             $Tapper::TAP::Harness::VERSION = '5.0.9';
6             use 5.010;
7 34     34   1794132 use strict;
  34         379  
8 34     34   178 use warnings;
  34         75  
  34         836  
9 34     34   149  
  34         57  
  34         954  
10             use TAP::Parser;
11 34     34   16354 use TAP::Parser::Aggregator;
  34         1400367  
  34         1133  
12 34     34   14008 use Directory::Scratch;
  34         171675  
  34         843  
13 34     34   14977 use File::Temp 'tempdir', 'tempfile';
  34         1361332  
  34         173  
14 34     34   1216 use YAML::Tiny;
  34         65  
  34         2038  
15 34     34   17023 use Archive::Tar;
  34         148642  
  34         1634  
16 34     34   20146 use IO::Scalar;
  34         2291985  
  34         2175  
17 34     34   13913 use IO::String;
  34         102067  
  34         1307  
18 34     34   210  
  34         65  
  34         3651  
19              
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 34     34   14607  
  34         12459657  
  34         215  
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   46 ($prove1 = $^X) =~ s/perl([\d.]*)$/prove$1/;
74             ($prove2 = $^X) =~ s/[^\/]*$/prove/;
75 17         264 return $prove1 if -e $prove1;
76 17         136 return $prove2 if -e $prove2;
77 17 50       469 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 455     455   990 if (defined $self->section_names->{$section_name}
86 455 100       989 and not $section_name =~ m/\d$/)
87 445         588 {
88 445 100 66     10444 $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 445         8879 }
94 16         32 $self->section_names->{$section_name} = 1;
95 16         352 return $section_name;
96             }
97 445         8666  
98 445         1221 # 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   110  
104             # known wrong YAML-in-TAP in database,
105             # usually Kernbench wrapper output
106 49         8024 $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         1858 $tap =~ s/^(\s+linetail):\s*$/$1: ~/msg;
111 49         1601 $tap =~ s/^(\s+CPU\d+):\s*$/$1: ~/msg;
112 49         762 $tap =~ s/^(\s+)(\w{3} \w{3} +\d+ \d+:\d+:\d+ \w+ \d{4})$/$1date: $2/msg;
113 49         761912 $tap =~ s/^(\s+)(2\.6\.\d+[^\n]*)$/$1kernel: $2/msg; # kernel version
114 49         600238 $tap =~ s/^(\s+)(Average)\s*([^\n]*)$/$1average: $3/msg;
115 49         5832 $tap =~ s/^(\s+)(Elapsed Time)\s*([^\n]*)$/$1elapsed_time: $3/msg;
116 49         6306 $tap =~ s/^(\s+)(User Time)\s*([^\n]*)$/$1user_time: $3/msg;
117 49         24307 $tap =~ s/^(\s+)(System Time)\s*([^\n]*)$/$1system_time: $3/msg;
118 49         1579 $tap =~ s/^(\s+)(Percent CPU)\s*([^\n]*)$/$1percent_cpu: $3/msg;
119 49         1508 $tap =~ s/^(\s+)(Context Switches)\s*([^\n]*)$/$1context_switches: $3/msg;
120 49         1614 $tap =~ s/^(\s+)(Sleeps)\s*([^\n]*)$/$1sleeps: $3/msg;
121 49         1479  
122 49         948 return $tap;
123 49         1210 }
124 49         1251  
125             {
126 49         460 my ($self) = @_;
127             my @sections = $self->_get_tap_sections_from_single;
128             $self->_collect_meta_from_sections(@sections);
129             }
130              
131 13     13   31  
132 13         49 {
133 13         74 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 139  
140 62 50       1262  
141 62         1326 {
142 62         357 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 64     64   143  
150              
151              
152 64 100       1466 # return sections
153 62 100       221 {
154 49         197 my ($self) = @_;
155              
156              
157             my $report_tap = $self->tap;
158 88     88 1 95  
  88         427  
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   118 my $parser = new TAP::Parser ({ tap => $report_tap, version => 13 });
164              
165             my $i = 0;
166 49         999 my %section;
167             my $looks_like_prove_output = 0;
168 49         100 $self->parsed_report->{report_meta} = {
169 49 100       2386 'suite-name' => 'unknown',
170             'suite-version' => 'unknown',
171 49         177 'suite-type' => 'unknown',
172 49         676 'reportcomment' => undef,
173             };
174 49         35333 my $sections_marked_explicit = 0;
175 49         98 my $last_line_was_version = 0;
176 49         79 my $last_line_was_plan = 0;
177              
178 49         1622 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         101 my $is_unknown = $line->is_unknown;
184 49         73 my $is_yaml = $line->is_yaml;
185 49         78  
186             # prove section
187 49         184 if ( $is_unknown and $raw =~ $re_prove_section ) {
188             $looks_like_prove_output ||= 1;
189 11476         2119639 }
190 11476         30996  
191 11476         43274 # ----- store previous section, start new section -----
192 11476         39425  
193 11476         38595 $sections_marked_explicit = 1 if $raw =~ $re_explicit_section_start;
194              
195              
196 11476 100 100     42647 # start new section
197 88   100     187 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       36715 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     59326 )
      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       1035 # ----- extract some meta information -----
224              
225 401 100       794 # a normal TAP line
226 401         487 if ( not $is_unknown ) {
  401         10934  
227             $section{raw} .= "$raw\n";
228 450         1025 }
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       16728 $val =~ s/^\s+//;
236 10829         22338 $val =~ s/\s+$//;
237             if ($raw =~ $re_tapper_meta_section) {
238             $section{section_name} ||= $self->_unique_section_name( $val );
239             }
240 11476 100 100     18779 $section{section_meta}{$key} = $val; # section keys
241             $self->parsed_report->{report_meta}{$key} = $val; # also global keys, later entries win
242 953         8813 }
243 953         1510  
244 953         2322 # looks like filename line from "prove"
245 953         1699 if ( $is_unknown and $raw =~ $re_prove_section )
246 953 100       3370 {
247 326   66     1175 my $section_name = $self->_unique_section_name( $1 );
248             $section{section_name} //= $section_name;
249 953         2013 }
250 953         20489  
251             $i++;
252             $last_line_was_plan = $is_plan ? 1 : 0;
253             $last_line_was_version = $is_version ? 1 : 0;
254 11476 100 100     61589 }
255              
256 88         199 # store last section
257 88   33     275 fix_last_ok(\ $section{raw}) if $looks_like_prove_output;
258             push @{$self->parsed_report->{tap_sections}}, { %section } if keys %section;
259              
260 11476         12039 $self->fix_section_names;
261 11476 100       15827 }
262 11476 100       34487  
263             {
264             my ($self) = @_;
265              
266 49 100       13144 # some stacking to enable Archive::Tar read compressed in-memory string
267 49 50       179 my $TARSTR = IO::String->new($self->tap);
  49         1404  
268             my $TARZ = IO::Zlib->new($TARSTR, "rb");
269 49         247 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 2     2   6 my $f2 = $_; $f2 =~ s,^\./,,; # force no-leading-dot
275             my $f3 = $_; $f3 = "./$_"; # force leading-dot
276             local $Archive::Tar::WARN = 0;
277 2         41 my $tap = $tar->get_content($f1) // $tar->get_content($f2) // $tar->get_content($f3);
278 2         174 $tap = "# Untar Bummer!" if ! defined $tar;
279 2         5991 { tap => $tap, filename => $f1 };
280             } @{$meta->{file_order}};
281 2         8748 return @tap_sections;
282             }
283 8         14  
284 8         10 {
  8         15  
285 8         11 my ($self) = @_;
  8         13  
286 8         14 my ($section_name) = ($self->tap) =~ $re_tapper_meta_section;
287 8   33     20 return({ tap => $self->tap, filename => $section_name}) if $section_name;
      33        
288 8 50       542 return({ tap => $self->tap});
289 8         29 }
290 2         1885  
  2         12  
291 2         50  
292             {
293             my ($self) = @_;
294              
295             my @tap_sections = $self->_get_tap_sections_from_archive($self->tap);
296 13     13   30 $self->_collect_meta_from_sections(@tap_sections);
297 13         278 }
298 13 50       37  
299 13         253 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 2     2   6 'suite-type' => 'unknown',
306             'reportcomment' => undef,
307 2         42 };
308 2         228  
309             my %section;
310              
311             foreach my $tap_file (@tap_sections)
312 15     15   41 {
313              
314 15         30 my $tap = $tap_file->{tap};
315             my $filename = $tap_file->{filename};
316 15         412  
317             my $parser = TAP::Parser->new ({ tap => $tap, version => 13 });
318              
319             # ----- store previous section, start new section -----
320              
321             # start new section
322 15         32 if (keys %section)
323             {
324 15         45 # Store a copy (ie., not \%section) so it doesn't get overwritten in next loop
325             push @{$self->parsed_report->{tap_sections}}, { %section };
326             }
327 21         46 %section = ();
328 21         36  
329             while ( my $line = $parser->next )
330 21         276 {
331             my $raw = $line->raw;
332             my $is_plan = $line->is_plan;
333             my $is_version = $line->is_version;
334             my $is_unknown = $line->is_unknown;
335 21 100       11107 my $is_yaml = $line->is_yaml;
336              
337             # ----- extract some meta information -----
338 6         8  
  6         151  
339             # a normal TAP line and not a summary line from "prove"
340 21         44 if ( not $is_unknown )
341             {
342 21         64 $section{raw} .= "$raw\n";
343             }
344 160         33342  
345 160         525 my $re_tapper_meta = qr/^#\s*((?:Tapper|Artemis|Test)-)([-\w]+):(.+)$/i;
346 160         762 my $re_tapper_meta_section = qr/^#\s*((?:Tapper|Artemis|Test)-Section:)\s*(.+)$/i;
347 160         674 # looks like tapper meta line
348 160         611 if ( $line->is_comment and $raw =~ m/^#\s*((?:Tapper|Artemis|Test)-)([-\w]+):(.+)$/i ) # (
349             {
350             # TODO: refactor inner part with _parse_tap_into_sections_raw()
351             my $key = lc $2;
352             my $val = $3;
353 160 100       620 $val =~ s/^\s+//;
354             $val =~ s/\s+$//;
355 157         349 if ($raw =~ $re_tapper_meta_section)
356             {
357             $section{section_name} = $self->_unique_section_name( $val );
358 160         379 }
359 160         290 $section{section_meta}{$key} = $val; # section keys
360             $self->parsed_report->{report_meta}{$key} = $val; # also global keys, later entries win
361 160 100 100     362 }
362             }
363             $section{section_name} //= $self->_unique_section_name( $filename );
364 61         616 }
365 61         99  
366 61         146 # store last section
367 61         123 push @{$self->parsed_report->{tap_sections}}, { %section } if keys %section;
368 61 100       241  
369             $self->fix_section_names;
370 3         11 }
371              
372 61         137  
373 61         1426 {
374             my ($self) = @_;
375              
376 21   100     4204 # augment section names
377             for (my $i = 0; $i < @{$self->parsed_report->{tap_sections}}; $i++)
378             {
379             $self->parsed_report->{tap_sections}->[$i]->{section_name} //= sprintf("section-%03d", $i);
380 15 50       61 }
  15         448  
381              
382 15         110 # delete whitespace from section names
383             for (my $i = 0; $i < @{$self->parsed_report->{tap_sections}}; $i++)
384             {
385             $self->parsed_report->{tap_sections}->[$i]->{section_name} =~ s/\s/-/g;
386             }
387             }
388 64     64 1 310  
389             {
390             my ($self) = @_;
391 64         159  
  535         10692  
392             my $aggregator = new TAP::Parser::Aggregator;
393 471   66     9133  
394             my $TAPVERSION = "TAP Version 13";
395              
396             $aggregator->start;
397 64         458 foreach my $section (@{$self->parsed_report->{tap_sections}})
  535         10484  
398             {
399 471         8986 my $rawtap = $section->{raw} || '';
400             $rawtap = $TAPVERSION."\n".$rawtap unless $rawtap =~ /^TAP Version/msi;
401             my $parser = new TAP::Parser ({ tap => $rawtap });
402             $parser->run;
403             $aggregator->add( $section->{section_name} => $parser );
404             }
405 64     64   262 $aggregator->stop;
406              
407 64         877 foreach (qw(total
408             passed
409 64         3723 parse_errors
410             skipped
411 64         308 todo
412 64         1700 todo_passed
  64         1672  
413             failed
414 471   50     37785 todo_passed
415 471 100       5878 ))
416 471         1738 {
417 471         182677 no strict 'refs'; ## no critic
418 471         2257541 $self->parsed_report->{stats}{$_} = $aggregator->$_;
419             }
420 64         6152 $self->parsed_report->{stats}{successgrade} = $aggregator->get_status;
421             $self->parsed_report->{stats}{success_ratio} = sprintf("%02.2f",
422 64         1564 $aggregator->total ? ($aggregator->passed / $aggregator->total * 100) : 100
423             );
424             }
425              
426             {
427             my ($self) = @_;
428              
429             # suite meta
430              
431             foreach my $key (@SUITE_HEADER_KEYS_GENERAL)
432 34     34   276091 {
  34         90  
  34         32580  
433 512         1496 my $value = $self->parsed_report->{report_meta}{$key};
434             my $accessor = $key;
435 64         262 $accessor =~ s/-/_/g;
436 64 50       209 $self->parsed_report->{db_report_meta}{$accessor} = $value if defined $value;
437             }
438              
439             foreach my $key (@SUITE_HEADER_KEYS_DATE)
440             {
441             my $value = $self->parsed_report->{report_meta}{$key};
442             my $accessor = $key;
443 64     64   193 $accessor =~ s/-/_/g;
444             $self->parsed_report->{db_report_date_meta}{$accessor} = $value if defined $value;
445             }
446              
447 64         261 foreach my $key (@SUITE_HEADER_KEYS_REPORTGROUP)
448             {
449 448         8832 my $value = $self->parsed_report->{report_meta}{$key};
450 448         633 my $accessor = $key;
451 448         993 $accessor =~ s/-/_/g;
452 448 100       4221 $self->parsed_report->{db_report_reportgroup_meta}{$accessor} = $value if defined $value;
453             }
454              
455 64         185 foreach my $key (@SUITE_HEADER_KEYS_REPORTCOMMENT)
456             {
457 128         2655 my $value = $self->parsed_report->{report_meta}{$key};
458 128         201 my $accessor = $key;
459 128         424 $accessor =~ s/-/_/g;
460 128 100       1178 $self->parsed_report->{db_report_reportcomment_meta}{$accessor} = $value if defined $value;
461             }
462             }
463 64         179  
464             {
465 256         5103 my ($self) = @_;
466 256         343  
467 256         516 # section meta
468 256 100       1898  
469             foreach my $section ( @{$self->parsed_report->{tap_sections}} ) {
470             foreach my $key (@SECTION_HEADER_KEYS_GENERAL)
471 64         201 {
472             my $section_name = $section->{section_name};
473 64         1343 my $value = $section->{section_meta}{$key};
474 64         125 my $accessor = $key;
475 64         128 $accessor =~ s/-/_/g;
476 64 100       290 $section->{db_section_meta}{$accessor} = $value if defined $value;
477             }
478             }
479             }
480              
481             {
482 64     64   177 my ($self) = @_;
483              
484             $self->_process_suite_meta_information;
485             $self->_process_section_meta_information;
486 64         111  
  64         1302  
487 471         608 }
488              
489 19782         19689  
490 19782         19494 {
491 19782         19102 my ($self) = @_;
492 19782         26596 return if @{$self->parsed_report->{tap_sections}};
493 19782 100       28663 $self->_parse_tap_into_sections();
494             $self->_aggregate_sections();
495             $self->_process_meta_information();
496              
497             }
498              
499             {
500 64     64   181 my ($html) = @_;
501              
502 64         247 $html =~ s/^.*<body>//msg; # cut start
503 64         193 $html =~ s,<div id="footer">Generated by TAP::Formatter::HTML[^<]*</div>,,msg; # cut footer
504             # cut navigation that was meant for standalone html pages, not needed by us
505             $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;
506             $html =~ s,<th class="time">Time</th>,<th class="time">&nbsp;</th>,msg; # cut "Time" header
507              
508             return $html;
509             }
510 75     75 1 37875  
511 75 100       149  
  75         1926  
512 64         264 {
513 64         308 my ($self) = @_;
514 64         322  
515             $self->evaluate_report();
516              
517             my $temp = new Directory::Scratch (TEMPLATE => 'ATH_XXXXXXXXXXXX',
518             CLEANUP => 1);
519             my $dir = $temp->mkdir("section");
520 13     13   171 my $TAPVERSION = "TAP Version 13";
521             my @files = map {
522 13         6405 if ($_->{section_name} =~ m'(^\.{1,2})|/') {
523 13         1414 $_->{section_name} =~ s/^\.+$/_dot_/;
524             $_->{section_name} =~ s|^/||;
525 13         1545 }
526 13         1664 my $fname = "section/$_->{section_name}";
527             my $rawtap = $_->{raw};
528 13         110 $rawtap = $TAPVERSION."\n".$rawtap unless $rawtap =~ /^TAP Version/msi;
529             my $script_content = $rawtap;
530             my $file = $temp->touch($fname, $script_content);
531              
532             [ "$temp/$fname" => $_->{section_name} ];
533             } @{$self->parsed_report->{tap_sections}};
534 13     13 1 43355  
535             # Currently a TAP::Formatter::* is only usable via the
536 13         59 # TAP::Harness which in turn is easiest to use externally on
537             # unix shell level
538 13         156 my $prove = _get_prove() or die "Can not find 'prove', searched near $^X";
539              
540 13         11651 my $cmd = qq{cd $temp/section ; $^X $prove -vm --exec 'cat' --formatter=TAP::Formatter::HTML `find . -type f -print | sed -e 's,^\./,,' | sort`};
541 13         6023 my $html = qx( $cmd );
542              
543 76 100       2290 $html = _fix_generated_html( $html );
544 28         57  
545 28         41 $temp->cleanup; # above CLEANUP=>1 is not enough. Trust me.
546              
547 76         176 return $html;
548 76         161 }
549 76 100       2426  
550 76         126 1; # End of Tapper::TAP::Harness
551 76         252  
552              
553 76         319266 =pod
554 13         32  
  13         403  
555             =encoding UTF-8
556              
557             =head1 NAME
558              
559 13 50       416 Tapper::TAP::Harness - Tapper - Tapper specific TAP handling
560              
561 13         68 =head2 tap_single_plan
562 13         4728437  
563             Return true when TAP contains exactly one plan
564 13         663  
565             =head2 fix_last_ok
566 13         541  
567             The C<prove> tool adds an annoying last summary line, cut that away.
568 13         18319  
569             =head2 fix_section_names
570              
571             Create sensible section names that fit further processing,
572             eg. substitute whitespace by dashes, fill missing names, etc.
573              
574             =head2 evaluate_report
575              
576             Actually evaluate the content of the incoming report by parsing it,
577             aggregate the sections and extract contained meta information.
578              
579             =head2 generate_html
580              
581             Render TAP through TAP::Formatter::HTML and fix some formatting to fit
582             into Tapper.
583              
584             =head1 AUTHORS
585              
586             =over 4
587              
588             =item *
589              
590             AMD OSRC Tapper Team <tapper@amd64.org>
591              
592             =item *
593              
594             Tapper Team <tapper-ops@amazon.com>
595              
596             =back
597              
598             =head1 COPYRIGHT AND LICENSE
599              
600             This software is Copyright (c) 2022 by Advanced Micro Devices, Inc.
601              
602             This is free software, licensed under:
603              
604             The (two-clause) FreeBSD License
605              
606             =cut