File Coverage

blib/lib/Test/Smoke/Reporter.pm
Criterion Covered Total %
statement 530 612 86.6
branch 196 276 71.0
condition 88 153 57.5
subroutine 44 46 95.6
pod 27 27 100.0
total 885 1114 79.4


line stmt bran cond sub pod time code
1             package Test::Smoke::Reporter;
2 6     6   210100 use warnings;
  6         29  
  6         208  
3 6     6   42 use strict;
  6         10  
  6         152  
4              
5 6     6   31 use vars qw( $VERSION );
  6         10  
  6         362  
6             $VERSION = '0.054';
7              
8             require File::Path;
9             require Test::Smoke;
10 6     6   34 use Cwd;
  6         33  
  6         361  
11 6     6   1662 use Encode qw( decode encode );
  6         45448  
  6         336  
12 6     6   978 use File::Spec::Functions;
  6         1751  
  6         489  
13 6     6   2884 use Test::Smoke::Util::LoadAJSON;
  6         16  
  6         41  
14 6     6   42 use POSIX qw( strftime );
  6         12  
  6         66  
15 6     6   12480 use System::Info;
  6         89859  
  6         383  
16 6         582 use Test::Smoke::Util qw(
17             grepccmsg grepnonfatal get_smoked_Config read_logfile
18             time_in_hhmm get_local_patches
19 6     6   1850 );
  6         15  
20 6     6   49 use Text::ParseWords;
  6         12  
  6         338  
21 6     6   39 use Test::Smoke::LogMixin;
  6         13  
  6         311  
22              
23 6     6   35 use constant USERNOTE_ON_TOP => 'top';
  6         18  
  6         54519  
24              
25             my %CONFIG = (
26             df_ddir => curdir(),
27             df_outfile => 'mktest.out',
28             df_rptfile => 'mktest.rpt',
29             df_jsnfile => 'mktest.jsn',
30             df_cfg => undef,
31             df_lfile => undef,
32             df_showcfg => 0,
33              
34             df_locale => undef,
35             df_defaultenv => undef,
36             df_is56x => undef,
37             df_skip_tests => undef,
38              
39             df_harnessonly => undef,
40             df_harness3opts => undef,
41              
42             df_v => 0,
43             df_hostname => undef,
44             df_from => '',
45             df_send_log => 'on_fail',
46             df_send_out => 'never',
47             df_user_note => '',
48             df_un_file => undef,
49             df_un_position => 'bottom', # != USERNOTE_ON_TOP for bottom
50             );
51              
52             =head1 NAME
53              
54             Test::Smoke::Reporter - OO interface for handling the testresults (mktest.out)
55              
56             =head1 SYNOPSIS
57              
58             use Test::Smoke;
59             use Test::Smoke::Reporter;
60              
61             my $reporter = Test::Smoke::Reporter->new( %args );
62             $reporter->write_to_file;
63             $reporter->transport( $url );
64              
65             =head1 DESCRIPTION
66              
67             Handle the parsing of the F file.
68              
69             =head1 METHODS
70              
71             =head2 Test::Smoke::Reporter->new( %args )
72              
73             [ Constructor | Public ]
74              
75             Initialise a new object.
76              
77             =cut
78              
79             sub new {
80 69     69 1 105845 my $proto = shift;
81 69 50       675 my $class = ref $proto ? ref $proto : $proto;
82              
83 69 50       1102 my %args_raw = @_ ? UNIVERSAL::isa( $_[0], 'HASH' ) ? %{ $_[0] } : @_ : ();
  0 50       0  
84              
85             my %args = map {
86 69         323 ( my $key = $_ ) =~ s/^-?(.+)$/lc $1/e;
  396         2021  
  396         1143  
87 396         1517 ( $key => $args_raw{ $_ } );
88             } keys %args_raw;
89              
90             my %fields = map {
91 1449 100       3136 my $value = exists $args{$_} ? $args{ $_ } : $CONFIG{ "df_$_" };
92 1449         2773 ( $_ => $value )
93 69         236 } keys %{ $class->config( 'all_defaults' ) };
  69         272  
94              
95 69         635 $fields{_conf_args} = { %args_raw };
96 69         259 my $self = bless \%fields, $class;
97 69         309 $self->read_parse( );
98             }
99              
100             =head2 $reporter->verbose()
101              
102             Accessor to the C attribute.
103              
104             =cut
105              
106             sub verbose {
107 1157     1157 1 1903 my $self = shift;
108              
109 1157 50       2170 $self->{v} = shift if @_;
110              
111 1157         4327 $self->{v};
112             }
113              
114             =head2 Test::Smoke::Reporter->config( $key[, $value] )
115              
116             [ Accessor | Public ]
117              
118             C is an interface to the package lexical C<%CONFIG>,
119             which holds all the default values for the C arguments.
120              
121             With the special key B this returns a reference
122             to a hash holding all the default values.
123              
124             =cut
125              
126             sub config {
127 69     69 1 141 my $dummy = shift;
128              
129 69         188 my $key = lc shift;
130              
131 69 50       218 if ( $key eq 'all_defaults' ) {
132             my %default = map {
133 69         3065 my( $pass_key ) = $_ =~ /^df_(.+)/;
  1449         3939  
134 1449         4259 ( $pass_key => $CONFIG{ $_ } );
135             } grep /^df_/ => keys %CONFIG;
136 69         951 return \%default;
137             }
138              
139 0 0       0 return undef unless exists $CONFIG{ "df_$key" };
140              
141 0 0       0 $CONFIG{ "df_$key" } = shift if @_;
142              
143 0         0 return $CONFIG{ "df_$key" };
144             }
145              
146             =head2 $self->read_parse( [$result_file] )
147              
148             C reads the smokeresults file and parses it.
149              
150             =cut
151              
152             sub read_parse {
153 116     116 1 24929 my $self = shift;
154              
155             my $result_file = @_ ? $_[0] : $self->{outfile}
156             ? catfile( $self->{ddir}, $self->{outfile} )
157 116 100       484 : "";
    100          
158 116         580 $self->log_debug("[%s::read_parse] found '%s'", ref($self), $result_file);
159              
160 116 100       291 if ( $result_file ) {
161 66         229 $self->_read( $result_file );
162 66         631 $self->_parse;
163             }
164 116         705 return $self;
165             }
166              
167             =head2 $self->_read( $nameorref )
168              
169             C<_read()> is a private method that handles the reading.
170              
171             =over 8
172              
173             =item B smokeresults are in C<$$nameorref>
174              
175             =item B smokeresults are in C<@$nameorref>
176              
177             =item B smokeresults are read from the filehandle
178              
179             =item B are taken as the filename for the smokeresults
180              
181             =back
182              
183             =cut
184              
185             sub _read {
186 66     66   138 my $self = shift;
187 66         127 my( $nameorref ) = @_;
188 66 50       161 $nameorref = '' unless defined $nameorref;
189              
190 66         125 my $vmsg = "";
191 66         521 local *SMOKERSLT;
192 66 100       302 if ( ref $nameorref eq 'SCALAR' ) {
    50          
    50          
193 50         134 $self->{_outfile} = $$nameorref;
194 50         125 $vmsg = "from internal content";
195             } elsif ( ref $nameorref eq 'ARRAY' ) {
196 0         0 $self->{_outfile} = join "", @$nameorref;
197 0         0 $vmsg = "from internal content";
198             } elsif ( ref $nameorref eq 'GLOB' ) {
199 0         0 *SMOKERSLT = *$nameorref;
200 0         0 $self->{_outfile} = do { local $/; };
  0         0  
  0         0  
201 0         0 $vmsg = "from anonymous filehandle";
202             } else {
203 16 50       66 if ( $nameorref ) {
204 16         105 $vmsg = "from $nameorref";
205 16         107 $self->{_outfile} = read_logfile($nameorref, $self->{v});
206 16 50       87 defined($self->{_outfile}) or do {
207 0         0 require Carp;
208 0         0 Carp::carp( "Cannot read smokeresults ($nameorref): $!" );
209 0         0 $vmsg = "did fail";
210             };
211             } else { # Allow intentional default_buildcfg()
212 0         0 $self->{_outfile} = undef;
213 0         0 $vmsg = "did fail";
214             }
215             }
216 66         247 $self->log_info("Reading smokeresult %s", $vmsg);
217             }
218              
219             =head2 $self->_parse( )
220              
221             Interpret the contents of the outfile and prepare them for processing,
222             so report can be made.
223              
224             =cut
225              
226             sub _parse {
227 66     66   184 my $self = shift;
228              
229 66         194 $self->{_rpt} = \my %rpt;
230 66         151 $self->{_cache} = {};
231 66         153 $self->{_mani} = [];
232 66         139 $self->{configs} = \my @new;
233 66 50       175 return $self unless defined $self->{_outfile};
234              
235 66         127 my ($cfgarg, $debug, $tstenv, $start, $statarg, $fcnt);
236 66         155 $rpt{count} = 0;
237             # reverse and use pop() instead of using unshift()
238 66         3265 my @lines = reverse split m/\n+/, $self->{_outfile};
239 66         235 my $previous = "";
240 66         316 my $previous_failed = "";
241              
242 66         269 while (defined (local $_ = pop @lines)) {
243 3439 100       9148 m/^\s*$/ and next;
244 3181 100       6308 m/^-+$/ and next;
245 3048         25083 s/\s*$//;
246              
247 3048 100       10938 if (my ($status, $time) = /(Started|Stopped) smoke at (\d+)/) {
248 572 100       1453 if ($status eq "Started") {
    100          
249 286         469 $start = $time;
250 286   66     1173 $rpt{started} ||= $time;
251             }
252             elsif (defined $start) {
253 283         688 my $elapsed = $time - $start;
254 283         537 $rpt{secs} += $elapsed;
255 283 100       706 @new and $new[-1]{duration} = $elapsed;
256             }
257 572         1789 next;
258             }
259              
260 2476 100       5635 if (my ($patch) = m/^ \s*
261             Smoking\ patch\s*
262             ((?:[0-9a-f]+\s+\S+)|(?:\d+\S*))
263             /x )
264             {
265 66         237 my ($pl, $descr) = split ' ', $patch;
266 66         174 $rpt{patchlevel} = $patch;
267 66   33     173 $rpt{patch} = $pl || $patch;
268 66   66     248 $rpt{patchdescr} = $descr || $pl;
269 66         195 next;
270             }
271 2410 100       4617 if (/^Smoking branch (\S+)/) {
272 3         17 $rpt{smokebranch} = $1;
273             }
274              
275 2410 100       4248 if (/^MANIFEST /) {
276 19         40 push @{$self->{_mani}}, $_;
  19         54  
277 19         55 next;
278             }
279              
280 2391 100       5995 if (s/^\s*Configuration:\s*//) {
281              
282             # You might need to do something here with
283             # the previous Configuration: $cfgarg
284 229 100       796 $rpt{statcfg}{$statarg} = $fcnt if defined $statarg;
285 229         340 $fcnt = 0;
286              
287 229         377 $rpt{count}++;
288 229         1052 s/-Dusedevel(\s+|$)//;
289 229         597 s/\s*-des//;
290 229         350 $statarg = $_;
291 229 100       1296 $debug = s/-D(DEBUGGING|usevmsdebug)\s*// ? "D" : "N";
292 229 100       771 $debug eq 'D' and $rpt{dbughow} = "-D$1";
293 229         904 s/\s+$//;
294              
295 229   100     633 $cfgarg = $_ || "";
296              
297 229         806 push(
298             @new,
299             {
300             arguments => $_,
301             debugging => $debug,
302             started => __posixdate($start),
303             results => [],
304             }
305             );
306 229 100       1677 push @{$rpt{cfglist}}, $_ unless $rpt{config}->{$cfgarg}++;
  139         496  
307 229         600 $tstenv = "";
308 229         605 $previous_failed = "";
309 229         833 next;
310             }
311              
312 2162 100       4477 if (my ($cinfo) = /^Compiler info: (.+)$/) {
313 149         518 $rpt{$cfgarg}->{cinfo} = $cinfo;
314 149   66     514 $rpt{cinfo} ||= $cinfo;
315 149         524 @{$new[-1]}{qw( cc ccversion )} = split m/ version / => $cinfo, 2;
  149         463  
316 149         464 next;
317             }
318              
319 2013 100 100     6784 if (m/(?:PERLIO|TSTENV)\s*=\s*([-\w:.]+)/
      100        
320             # skip this if it's from a build failure, since the
321             # Unable to build... pushed an M
322             && (!@{$new[-1]{results}}
323             || $new[-1]{results}[0]{summary} ne "M")) {
324 344         898 $tstenv = $1;
325 344         504 $previous_failed = "";
326 344   50     2177 $rpt{$cfgarg}->{summary}{$debug}{$tstenv} ||= "?";
327 344         1017 my ($io_env, $locale) = split m/:/ => $tstenv,
328             2;
329             push(
330 344         541 @{$new[-1]{results}},
  344         2203  
331             {
332             io_env => $io_env,
333             locale => $locale,
334             summary => "?",
335             statistics => undef,
336             stat_tests => undef,
337             stat_cpu_time => undef,
338             failures => [],
339             }
340             );
341              
342             # Deal with harness output
343 344         2204 s/^(?:PERLIO|TSTENV)\s*=\s+[-\w:.]+(?: :crlf)?\s*//;
344             }
345              
346 2013 100       8492 if (m/\b(Files=[0-9]+,\s*Tests=([0-9]+),.*?=\s*([0-9.]+)\s*CPU)/) {
    100          
347 2         10 $new[-1]{results}[-1]{statistics} = $1;
348 2         14 $new[-1]{results}[-1]{stat_tests} = $2;
349 2         7 $new[-1]{results}[-1]{stat_cpu_time} = $3;
350             }
351             elsif (
352             m/\b(u=([0-9.]+)\s+
353             s=([0-9.]+)\s+
354             cu=([0-9.]+)\s+
355             cs=([0-9.]+)\s+
356             scripts=[0-9]+\s+
357             tests=([0-9]+))/xi
358             )
359             {
360 198         674 $new[-1]{results}[-1]{statistics} = $1;
361 198         522 $new[-1]{results}[-1]{stat_tests} = $6;
362 198         1358 $new[-1]{results}[-1]{stat_cpu_time} = $2 + $3 + $4 + $5;
363             }
364              
365 2013 100       4336 if (m/^\s*All tests successful/) {
366 149         496 $rpt{$cfgarg}->{summary}{$debug}{$tstenv} = "O";
367 149         259 $new[-1]{results}[-1]{summary} = "O";
368 149         446 next;
369             }
370              
371 1864 100       3565 if (m/Inconsistent test ?results/) {
372             ref $rpt{$cfgarg}->{$debug}{$tstenv}{failed}
373 39 100       287 or $rpt{$cfgarg}->{$debug}{$tstenv}{failed} = [];
374              
375 39 100 66     457 if (not $rpt{$cfgarg}->{summary}{$debug}{$tstenv}
376             or $rpt{$cfgarg}->{summary}{$debug}{$tstenv} ne "F")
377             {
378 24         101 $rpt{$cfgarg}->{summary}{$debug}{$tstenv} = "X";
379 24         68 $new[-1]{results}[-1]{summary} = "X";
380             }
381 39         82 push @{$rpt{$cfgarg}->{$debug}{$tstenv}{failed}}, $_;
  39         182  
382 39         200 while (m/^ \s* (\S+?) \s* \.+(?:\s+\.+)* \s* (\w.*?) \s*$/xgm) {
383 0         0 my ($_test, $_info) = ($1, $2);
384              
385             push(
386 0 0 0     0 @{$new[-1]{results}[-1]{failures}},
  0 0       0  
387             $_info =~ m/^ \w+ $/x
388             ? {
389             test => $_test,
390             status => $_info,
391             extra => []
392             }
393             : # TEST output from minitest
394             $_info =~ m/^ (\w+) \s+at\ test\s+ (\d+) \s* $/x
395             || $_info =~ m/^ (\w+)--(\S.*\S) \s* $/x
396             ? {
397             test => $_test,
398             status => $1,
399             extra => [ $2 ]
400             }
401             : {
402             test => "?",
403             status => "?",
404             extra => []
405             }
406             );
407             }
408             }
409              
410 1864 100       3273 if (/^Finished smoking [\dA-Fa-f]+/) {
411 63         230 $rpt{statcfg}{$statarg} = $fcnt;
412 63         143 $rpt{finished} = "Finished";
413 63         177 next;
414             }
415              
416 1801 100       4518 if (my ($status, $mini) =
417             m/^ \s* Unable\ to
418             \ (?=([cbmt]))(?:build|configure|make|test)
419             \ (anything\ but\ mini)?perl/x
420             )
421             {
422 18 100       56 $mini and $status = uc $status; # M for no perl but miniperl
423             # $tstenv is only set *after* this
424 18 100 33     115 $tstenv ||= $mini ? "minitest" : "stdio";
425 18         68 $rpt{$cfgarg}->{summary}{$debug}{$tstenv} = $status;
426             push(
427 18         29 @{$new[-1]{results}},
  18         98  
428             {
429             io_env => $tstenv,
430             locale => undef,
431             summary => $status,
432             statistics => undef,
433             stat_tests => undef,
434             stat_cpu_time => undef,
435             failures => [],
436             }
437             );
438 18         31 $fcnt++;
439 18         61 next;
440             }
441              
442 1783 100 66     8850 if (m/FAILED/ || m/DIED/ || m/dubious$/ || m/\?\?\?\?\?\?$/) {
      100        
      100        
443             ref $rpt{$cfgarg}->{$debug}{$tstenv}{failed}
444 624 100       2410 or $rpt{$cfgarg}->{$debug}{$tstenv}{failed} = [];
445              
446 624 100       1354 if ($previous_failed ne $_) {
447 612 100 66     3502 if (not $rpt{$cfgarg}->{summary}{$debug}{$tstenv}
448             or $rpt{$cfgarg}->{summary}{$debug}{$tstenv} !~ m/[XM]/)
449             {
450 582         1372 $rpt{$cfgarg}->{summary}{$debug}{$tstenv} = "F";
451 582         949 $new[-1]{results}[-1]{summary} = "F";
452             }
453 612         862 push @{$rpt{$cfgarg}->{$debug}{$tstenv}{failed}}, $_;
  612         1632  
454             push(
455 612 50       905 @{$new[-1]{results}[-1]{failures}},
  612         6745  
456             m{^ \s* # leading space
457             ((?:\S+[/\\])? # Optional leading path to
458             \S(?:[^.]+|\.t)+) # test file name
459             [. ]+ # ....... ......
460             (\w.*?) # result
461             \s* $}x
462             ? {
463             test => $1,
464             status => $2,
465             extra => []
466             }
467             : {
468             test => "?",
469             status => "?",
470             extra => []
471             }
472             );
473              
474 612         1221 $fcnt++;
475             }
476 624         843 $previous_failed = $_;
477              
478 624         826 $previous = "failed";
479 624         1762 next;
480             }
481              
482 1159 100       2559 if (m/PASSED/) {
483             ref $rpt{$cfgarg}->{$debug}{$tstenv}{passed}
484 132 100       676 or $rpt{$cfgarg}->{$debug}{$tstenv}{passed} = [];
485              
486 132         230 push @{$rpt{$cfgarg}->{$debug}{$tstenv}{passed}}, $_;
  132         372  
487             push(
488 132 50       287 @{$new[-1]{results}[-1]{failures}},
  132         1443  
489             m/^ \s* (\S+?) \.+(?:\s+\.+)* (\w+) \s* $/x
490             ? {
491             test => $1,
492             status => $2,
493             extra => []
494             }
495             : {
496             test => "?",
497             status => "?",
498             extra => []
499             }
500             );
501 132         549 $previous = "passed";
502 132         377 next;
503             }
504              
505 1027         1567 my @captures = ();
506 1027 100       9885 if (@captures = $_ =~ m/
507             (?:^|,)\s+
508             (\d+(?:-\d+)?)
509             /gx) {
510 310 100       1132 if (ref $rpt{$cfgarg}->{$debug}{$tstenv}{$previous}) {
511 308         552 push @{$rpt{$cfgarg}->{$debug}{$tstenv}{$previous}}, $_;
  308         814  
512 308         437 push @{$new[-1]{results}[-1]{failures}[-1]{extra}}, @captures;
  308         1196  
513             }
514 310         1052 next;
515             }
516              
517 717 100       8687 if (/^\s+(?:Bad plan)|(?:No plan found)|^\s+(?:Non-zero exit status)/) {
518 42 50       196 if (ref $rpt{$cfgarg}->{$debug}{$tstenv}{failed}) {
519 42         143 push @{$rpt{$cfgarg}->{$debug}{$tstenv}{failed}}, $_;
  42         178  
520 42         203 s/^\s+//;
521 42         98 push @{$new[-1]{results}[-1]{failures}[-1]{extra}}, $_;
  42         141  
522             }
523 42         131 next;
524             }
525 675         1972 next;
526             }
527              
528 66         142 $rpt{last_cfg} = $statarg;
529 66 100       265 exists $rpt{statcfg}{$statarg} or $rpt{running} = $fcnt;
530 66 50       421 $rpt{avg} = $rpt{count} ? $rpt{secs} / $rpt{count} : 0;
531 66         200 $self->{_rpt} = \%rpt;
532 66         511 $self->_post_process;
533             }
534              
535             =head2 $self->_post_process( )
536              
537             C<_post_process()> sets up the report for easy printing. It needs to
538             sort the buildenvironments, statusletters and test failures.
539              
540             =cut
541              
542             sub _post_process {
543 66     66   173 my $self = shift;
544              
545 66 100       174 unless (defined $self->{is56x}) {
546 51         113 $self->{is56x} = 0;
547             # Overly defensive, as .out files might be analyzed outside of the
548             # original smoke environment
549 51 50 33     1174 if ($self->{ddir} && -d $self->{ddir}) {
550 51         313 my %cfg = get_smoked_Config($self->{ddir}, "version");
551 51 100       345 if ($cfg{version} =~ m/^\s* ([0-9]+) \. ([0-9]+) \. ([0-9]+) \s*$/x) {
552 49         359 my $p_version = sprintf "%d.%03d%03d", $1, $2, $3;
553 49         237 $self->{is56x} = $p_version < 5.007;
554             }
555             }
556             }
557 66   100     423 $self->{defaultenv} ||= $self->{is56x};
558              
559 66         107 my (%bldenv, %cfgargs);
560 66         110 my $rpt = $self->{_rpt};
561 66         110 foreach my $config (@{$rpt->{cfglist}}) {
  66         499  
562              
563 139         327 foreach my $buildenv (keys %{$rpt->{$config}{summary}{N}}) {
  139         645  
564 244         494 $bldenv{$buildenv}++;
565             }
566 139         229 foreach my $buildenv (keys %{$rpt->{$config}{summary}{D}}) {
  139         442  
567 118         196 $bldenv{$buildenv}++;
568             }
569 139         442 foreach my $ca (grep defined $_ => quotewords('\s+', 1, $config)) {
570 255         12659 $cfgargs{$ca}++;
571             }
572             }
573             my %common_args =
574 54         245 map { ($_ => 1) }
575 66   100     282 grep $cfgargs{$_} == @{$rpt->{cfglist}}
576             && !/^-[DU]use/ => keys %cfgargs;
577              
578 66         198 $rpt->{_common_args} = \%common_args;
579 66         380 $rpt->{common_args} = join " ", sort keys %common_args;
580 66   100     294 $rpt->{common_args} ||= 'none';
581              
582 66         349 $self->{_tstenv} = [reverse sort keys %bldenv];
583 66         480 my %count = (
584             O => 0,
585             F => 0,
586             X => 0,
587             M => 0,
588             m => 0,
589             c => 0,
590             o => 0,
591             t => 0
592             );
593 66         114 my (%failures, %order);
594 66         106 my $ord = 1;
595 66         97 my (%todo_passed, %order2);
596 66         94 my $ord2 = 1;
597 66   100     548 my $debugging = $rpt->{dbughow} || '-DDEBUGGING';
598              
599 66         131 foreach my $config (@{$rpt->{cfglist}}) {
  66         184  
600 139         254 foreach my $dbinfo (qw( N D )) {
601 278         465 my $cfg = $config;
602 278 100       982 ($cfg = $cfg ? "$debugging $cfg" : $debugging)
    100          
603             if $dbinfo eq "D";
604 278         960 $self->log_info("Processing [%s]", $cfg);
605 278         758 my $status = $self->{_rpt}{$config}{summary}{$dbinfo};
606 278         842 foreach my $tstenv (reverse sort keys %bldenv) {
607 524 100 100     1170 next if $tstenv eq 'minitest' && !exists $status->{$tstenv};
608              
609 512         1134 (my $showenv = $tstenv) =~ s/^locale://;
610 512 100       1042 if ($tstenv =~ /^locale:/) {
611             $self->{_locale_keys}{$showenv}++
612 54 100       202 or push @{$self->{_locale}}, $showenv;
  14         54  
613             }
614             $showenv = 'default'
615 512 100 66     1387 if $self->{defaultenv} && $showenv eq 'stdio';
616              
617 512   100     1659 $status->{$tstenv} ||= '-';
618              
619 512         1011 my $status2 = $self->{_rpt}{$config}{$dbinfo};
620 512 100       1352 if (exists $status2->{$tstenv}{failed}) {
621 195         262 my $failed = join "\n", @{$status2->{$tstenv}{failed}};
  195         746  
622 195 100 66     955 if ( exists $failures{$failed}
      100        
623 119         809 && @{$failures{$failed}}
624             && $failures{$failed}->[-1]{cfg} eq $cfg)
625             {
626 62         108 push @{$failures{$failed}->[-1]{env}}, $showenv;
  62         229  
627             }
628             else {
629 133         192 push @{$failures{$failed}},
  133         747  
630             {
631             cfg => $cfg,
632             env => [$showenv]
633             };
634 133   66     876 $order{$failed} ||= $ord++;
635             }
636             }
637 512 100       1127 if (exists $status2->{$tstenv}{passed}) {
638 66         98 my $passed = join "\n", @{$status2->{$tstenv}{passed}};
  66         213  
639 66 100 66     346 if ( exists $todo_passed{$passed}
      100        
640 33         321 && @{$todo_passed{$passed}}
641             && $todo_passed{$passed}->[-1]{cfg} eq $cfg)
642             {
643 30         110 push @{$todo_passed{$passed}->[-1]{env}}, $showenv;
  30         113  
644             }
645             else {
646             push(
647 36         121 @{$todo_passed{$passed}},
  36         315  
648             {
649             cfg => $cfg,
650             env => [$showenv]
651             }
652             );
653 36   66     431 $order2{$passed} ||= $ord2++;
654             }
655              
656             }
657              
658 512         1644 $self->log_debug("\t[%s]: %s", $showenv, $status->{$tstenv});
659 512 100       1469 if ($tstenv eq 'minitest') {
660 12         29 $status->{stdio} = "M";
661 12         32 delete $status->{minitest};
662             }
663             }
664 278 100       619 unless ($self->{defaultenv}) {
665 168 50       401 exists $status->{perlio} or $status->{perlio} = '-';
666 168   100     925 my @locales = split ' ', ($self->{locale} || '');
667 168         368 for my $locale (@locales) {
668             exists $status->{"locale:$locale"}
669 18 50       56 or $status->{"locale:$locale"} = '-';
670             }
671             }
672              
673             $count{$_}++
674 278 50       1073 for map { m/[cmMtFXO]/ ? $_ : m/-/ ? 'O' : 'o' }
  500 100       2237  
675             map $status->{$_} => keys %$status;
676             }
677             }
678 66 100       241 defined $self->{_locale} or $self->{_locale} = [];
679              
680             my @failures = map {
681             {
682             tests => $_,
683             cfgs => [
684             map {
685 133         340 my $cfg_clean = __rm_common_args($_->{cfg}, \%common_args);
686 133         305 my $env = join "/", @{$_->{env}};
  133         390  
687 133         809 "[$env] $cfg_clean";
688 76         156 } @{$failures{$_}}
  76         200  
689             ],
690             }
691 66         275 } sort { $order{$a} <=> $order{$b} } keys %failures;
  58         194  
692 66         185 $self->{_failures} = \@failures;
693              
694             my @todo_passed = map {
695             {
696             tests => $_,
697             cfgs => [
698             map {
699 36         121 my $cfg_clean = __rm_common_args($_->{cfg}, \%common_args);
700 36         90 my $env = join "/", @{$_->{env}};
  36         142  
701 36         206 "[$env] $cfg_clean";
702 33         68 } @{$todo_passed{$_}}
  33         68  
703             ],
704             }
705 66         250 } sort { $order2{$a} <=> $order2{$b} } keys %todo_passed;
  9         41  
706 66         208 $self->{_todo_passed} = \@todo_passed;
707              
708 66         283 $self->{_counters} = \%count;
709              
710             # Need to rebuild the test-environments as minitest changes into stdio
711 66         112 my %bldenv2;
712 66         93 foreach my $config (@{$rpt->{cfglist}}) {
  66         165  
713 139         169 foreach my $buildenv (keys %{$rpt->{$config}{summary}{N}}) {
  139         403  
714 250         435 $bldenv2{$buildenv}++;
715             }
716 139         196 foreach my $buildenv (keys %{$rpt->{$config}{summary}{D}}) {
  139         339  
717 250         452 $bldenv2{$buildenv}++;
718             }
719             }
720 66         138 $self->{_tstenvraw} = $self->{_tstenv};
721 66         1014 $self->{_tstenv} = [reverse sort keys %bldenv2];
722             }
723              
724             =head2 __posixdate($time)
725              
726             Returns C.
727              
728             =cut
729              
730             sub __posixdate {
731              
732             # Note that the format "%F %T %z" returns:
733             # Linux: 2012-04-02 10:57:58 +0200
734             # HP-UX: April 08:53:32 METDST
735             # ENOTPORTABLE! %F is C99 only!
736 229   33 229   533 my $stamp = shift || time;
737 229 50       11886 return $^O eq 'MSWin32'
738             ? POSIX::strftime("%Y-%m-%d %H:%M:%S Z", gmtime $stamp)
739             : POSIX::strftime("%Y-%m-%d %H:%M:%S %z", localtime $stamp);
740             }
741              
742             =head2 __rm_common_args( $cfg, \%common )
743              
744             Removes the the arguments stored as keys in C<%common> from C<$cfg>.
745              
746             =cut
747              
748             sub __rm_common_args {
749 169     169   373 my( $cfg, $common ) = @_;
750              
751 169         810 require Test::Smoke::BuildCFG;
752 169         1148 my $bcfg = Test::Smoke::BuildCFG::new_configuration( $cfg );
753              
754 169         908 return $bcfg->rm_arg( keys %$common );
755             }
756              
757             =head2 $reporter->get_logfile()
758              
759             Return the contents of C<< $self->{lfile} >> either by reading the file or
760             returning the cached version.
761              
762             =cut
763              
764             sub get_logfile {
765 0     0 1 0 my $self = shift;
766 0 0       0 return $self->{log_file} if $self->{log_file};
767              
768 0         0 return $self->{log_file} = read_logfile($self->{lfile}, $self->{v});
769             }
770              
771             =head2 $reporter->get_outfile()
772              
773             Return the contents of C<< $self->{outfile} >> either by reading the file or
774             returning the cached version.
775              
776             =cut
777              
778             sub get_outfile {
779 121     121 1 228 my $self = shift;
780 121 50       846 return $self->{_outfile} if $self->{_outfile};
781              
782 0         0 my $fq_outfile = catfile($self->{ddir}, $self->{outfile});
783 0         0 return $self->{_outfile} = read_logfile($fq_outfile, $self->{v});
784             }
785              
786             =head2 $reporter->write_to_file( [$name] )
787              
788             Write the C<< $self->report >> to file. If name is omitted it will
789             use C<< catfile( $self->{ddir}, $self->{rptfile} ) >>.
790              
791             =cut
792              
793             sub write_to_file {
794 1     1 1 1428 my $self = shift;
795 1 50       34 return unless defined $self->{_outfile};
796 1   33     15 my( $name ) = shift || ( catfile $self->{ddir}, $self->{rptfile} );
797              
798 1         19 $self->log_info("Writing report to '%s'", $name);
799 1         15 local *RPT;
800 1 50       87 open RPT, "> $name" or do {
801 0         0 require Carp;
802 0         0 Carp::carp( "Error creating '$name': $!" );
803 0         0 return;
804             };
805 1         22 print RPT $self->report;
806 1 50       71 close RPT or do {
807 0         0 require Carp;
808 0         0 Carp::carp( "Error writing to '$name': $!" );
809 0         0 return;
810             };
811 1         24 $self->log_info("'%s' written OK", $name);
812 1         12 return 1;
813             }
814              
815             =head2 $reporter->smokedb_data()
816              
817             Transport the report to the gateway. The transported data will also be stored
818             locally in the file mktest.jsn
819              
820             =cut
821              
822             sub smokedb_data {
823 0     0 1 0 my $self = shift;
824 0         0 $self->log_info("Gathering CoreSmokeDB information...");
825              
826 0         0 my %rpt = map { $_ => $self->{$_} } keys %$self;
  0         0  
827 0         0 $rpt{manifest_msgs} = delete $rpt{_mani};
828 0         0 $rpt{applied_patches} = [$self->registered_patches];
829 0         0 $rpt{sysinfo} = do {
830 0         0 my %Conf = get_smoked_Config($self->{ddir} => qw( version lfile ));
831 0         0 my $si = System::Info->new;
832 0         0 my ($osname, $osversion) = split m/ - / => $si->os, 2;
833 0   0     0 (my $ncpu = $si->ncpu || "?") =~ s/^\s*(\d+)\s*/$1/;
834 0   0     0 (my $user_note = $self->{user_note} || "") =~ s/(\S)[\s\r\n]*\z/$1\n/;
835             {
836             architecture => lc $si->cpu_type,
837             config_count => $self->{_rpt}{count},
838             cpu_count => $ncpu,
839             cpu_description => $si->cpu,
840             duration => $self->{_rpt}{secs},
841             git_describe => $self->{_rpt}{patchdescr},
842             git_id => $self->{_rpt}{patch},
843             smoke_branch => $self->{_rpt}{smokebranch},
844             hostname => $self->{hostname} || $si->host,
845             lang => $ENV{LANG},
846             lc_all => $ENV{LC_ALL},
847             osname => $osname,
848             osversion => $osversion,
849             perl_id => $Conf{version},
850             reporter => $self->{from},
851             reporter_version => $VERSION,
852             smoke_date => __posixdate($self->{_rpt}{started}),
853             smoke_revision => $Test::Smoke::VERSION,
854             smoker_version => $Test::Smoke::Smoker::VERSION,
855             smoke_version => $Test::Smoke::VERSION,
856             test_jobs => $ENV{TEST_JOBS},
857 0 0 0     0 username => $ENV{LOGNAME} || getlogin || getpwuid($<) || "?",
      0        
858             user_note => $user_note,
859             smoke_perl => ($^V ? sprintf("%vd", $^V) : $]),
860             };
861             };
862 0         0 $rpt{compiler_msgs} = [$self->ccmessages];
863 0         0 $rpt{nonfatal_msgs} = [$self->nonfatalmessages];
864 0         0 $rpt{skipped_tests} = [$self->user_skipped_tests];
865 0         0 $rpt{harness_only} = delete $rpt{harnessonly};
866 0         0 $rpt{summary} = $self->summary;
867              
868 0         0 $rpt{log_file} = undef;
869 0 0       0 my $rpt_fail = $rpt{summary} eq "PASS" ? 0 : 1;
870 0 0       0 if (my $send_log = $self->{send_log}) {
871 0 0 0     0 if ( ($send_log eq "always")
      0        
872             or ($send_log eq "on_fail" && $rpt_fail))
873             {
874 0         0 $rpt{log_file} = $self->get_logfile();
875             }
876             }
877 0         0 $rpt{out_file} = undef;
878 0 0       0 if (my $send_out = $self->{send_out}) {
879 0 0 0     0 if ( ($send_out eq "always")
      0        
880             or ($send_out eq "on_fail" && $rpt_fail))
881             {
882 0         0 $rpt{out_file} = $self->get_outfile();
883             }
884             }
885 0         0 delete $rpt{$_} for qw/from send_log send_out user_note/, grep m/^_/ => keys %rpt;
886              
887 0         0 my $json = Test::Smoke::Util::LoadAJSON->new->utf8(1)->pretty(1)->encode(\%rpt);
888              
889             # write the json to file:
890 0         0 my $jsn_file = catfile($self->{ddir}, $self->{jsnfile});
891 0 0       0 if (open my $jsn, ">", $jsn_file) {
892 0         0 binmode($jsn);
893 0         0 print {$jsn} $json;
  0         0  
894 0         0 close $jsn;
895 0         0 $self->log_info("Write to '%s': ok", $jsn_file);
896             }
897             else {
898 0         0 $self->log_warn("Error creating '%s': %s", $jsn_file, $!);
899             }
900              
901 0         0 return $self->{_json} = $json;
902             }
903              
904             =head2 $reporter->report( )
905              
906             Return a string with the full report
907              
908             =cut
909              
910             sub report {
911 61     61 1 37471 my $self = shift;
912 61 50       230 return unless defined $self->{_outfile};
913 61         170 $self->_get_usernote();
914              
915 61         160 my $report = $self->preamble;
916              
917 61         660 $report .= "Summary: ".$self->summary."\n\n";
918 61         614 $report .= $self->letter_legend . "\n";
919 61         600 $report .= $self->smoke_matrix . $self->bldenv_legend;
920              
921 61         623 $report .= $self->registered_patches;
922              
923 61         465 $report .= $self->harness3_options;
924              
925 61         203 $report .= $self->user_skipped_tests;
926              
927 61 100       214 $report .= "\nFailures: (common-args) $self->{_rpt}{common_args}\n"
928             . $self->failures if $self->has_test_failures;
929 61 100       262 $report .= "\n" . $self->mani_fail if $self->has_mani_failures;
930              
931 61 100       175 $report .= "\nPassed Todo tests: (common-args) $self->{_rpt}{common_args}\n"
932             . $self->todo_passed if $self->has_todo_passed;
933              
934 61         243 $report .= $self->ccmessages;
935              
936 61         198 $report .= $self->nonfatalmessages;
937              
938 61 0 33     188 if ( $self->{showcfg} && $self->{cfg} && $self->has_test_failures ) {
      0        
939 0         0 require Test::Smoke::BuildCFG;
940 0         0 my $bcfg = Test::Smoke::BuildCFG->new( $self->{cfg} );
941 0         0 $report .= "\nBuild configurations:\n" . $bcfg->as_string ."=\n";
942             }
943              
944 61         225 $report .= $self->signature;
945 61         1518 return $report;
946             }
947              
948             =head2 $reporter->_get_usernote()
949              
950             Return $self->{user_note} if exists.
951              
952             Check if C<< $self->{un_file} >> exists, and read contents into C<<
953             $self->{user_note} >>.
954              
955             =cut
956              
957             sub _get_usernote {
958 61     61   116 my $self = shift;
959              
960 61 50 66     841 if (!$self->{user_note} && $self->{un_file}) {
    50          
961 0 0       0 if (open my $unf, '<', $self->{un_file}) {
962 0         0 $self->{user_note} = join('', <$unf>);
963             }
964             else {
965 0         0 $self->log_warn("Cannot read '%s': %s", $self->{un_file}, $!);
966             }
967             }
968             elsif (!defined $self->{user_note}) {
969 0         0 $self->{user_note} = '';
970             }
971 61         333 $self->{user_note} =~ s/(?<=\S)\s*\z/\n/;
972             }
973              
974             =head2 $reporter->ccinfo( )
975              
976             Return the string containing the C-compiler info.
977              
978             =cut
979              
980             sub ccinfo {
981 73     73 1 2805 my $self = shift;
982 73         226 my $cinfo = $self->{_rpt}{cinfo};
983 73 100       218 unless ( $cinfo ) { # Old .out file?
984 53         701 my %Config = get_smoked_Config( $self->{ddir} => qw(
985             cc ccversion gccversion
986             ));
987 53         332 $cinfo = "? ";
988 53   50     892 my $ccvers = $Config{gccversion} || $Config{ccversion} || '';
989 53   50     473 $cinfo .= ( $Config{cc} || 'unknown cc' ) . " version $ccvers";
990 53   50     692 $self->{_ccinfo} = ($Config{cc} || 'cc') . " version $ccvers";
991             }
992 73         340 return $cinfo;
993             }
994              
995             =head2 $reporter->registered_patches()
996              
997             Return a section with the locally applied patches (from patchlevel.h).
998              
999             =cut
1000              
1001             sub registered_patches {
1002 62     62 1 145 my $self = shift;
1003              
1004 62         859 my @lpatches = get_local_patches($self->{ddir}, $self->{v});
1005 62 50 66     281 @lpatches && $lpatches[0] eq "uncommitted-changes" and shift @lpatches;
1006 62 50       180 wantarray and return @lpatches;
1007              
1008 62 100       568 @lpatches or return "";
1009              
1010 3         38 my $list = join "\n", map " $_" => @lpatches;
1011 3         17 return "\nLocally applied patches:\n$list\n";
1012             }
1013              
1014             =head2 $reporter->harness3_options
1015              
1016             Show indication of the options used for C.
1017              
1018             =cut
1019              
1020             sub harness3_options {
1021 61     61 1 168 my $self = shift;
1022              
1023 61 50       643 $self->{harnessonly} or return "";
1024              
1025 0         0 my $msg = "\nTestsuite was run only with 'harness'";
1026 0 0       0 $self->{harness3opts} or return $msg . "\n";
1027              
1028 0         0 return $msg . " and HARNESS_OPTIONS=$self->{harness3opts}\n";
1029             }
1030              
1031             =head2 $reporter->user_skipped_tests( )
1032              
1033             Show indication for the fact that the user requested to skip some tests.
1034              
1035             =cut
1036              
1037             sub user_skipped_tests {
1038 62     62 1 108 my $self = shift;
1039              
1040 62         112 my @skipped;
1041 62 50 66     416 if ($self->{skip_tests} && -f $self->{skip_tests} and open my $fh,
      66        
1042             "<", $self->{skip_tests})
1043             {
1044 2         56 while (my $raw = <$fh>) {
1045 2 50       13 next, if $raw =~ m/^# One test name on a line/;
1046 2         9 chomp($raw);
1047 2         36 push @skipped, " $raw";
1048             }
1049 2         31 close $fh;
1050             }
1051 62 50       151 wantarray and return @skipped;
1052              
1053 62 100       489 my $skipped = join "\n", @skipped or return "";
1054              
1055 2         29 return "\nTests skipped on user request:\n$skipped";
1056             }
1057              
1058             =head2 $reporter->ccmessages( )
1059              
1060             Use a port of Jarkko's F script to report the compiler messages.
1061              
1062             =cut
1063              
1064             sub ccmessages {
1065 62     62 1 615 my $self = shift;
1066              
1067 62   50     508 my $ccinfo = $self->{_rpt}{cinfo} || $self->{_ccinfo} || "cc";
1068 62         649 $ccinfo =~ s/^(.+)\s+version\s+.+/$1/;
1069              
1070 62 50       523 $^O =~ /^(?:linux|.*bsd.*|darwin)/ and $ccinfo = 'gcc';
1071 62 50       466 my $cc = $ccinfo =~ /(gcc|bcc32)/ ? $1 : $^O;
1072              
1073 62 100       190 if (!$self->{_ccmessages_}) {
1074              
1075 61         269 $self->log_info("Looking for cc messages: '%s'", $cc);
1076             $self->{_ccmessages_} = grepccmsg(
1077             $cc,
1078             $self->get_outfile(),
1079             $self->{v}
1080 61   50     435 ) || [];
1081             }
1082 62         352 $self->log_debug("Finished grepping for %s", $cc);
1083              
1084 62 50       151 return @{$self->{_ccmessages_}} if wantarray;
  0         0  
1085 62 50       175 return "" if !$self->{_ccmessages_};
1086              
1087 62         413 local $" = "\n";
1088 62         202 return <<" EOERRORS";
1089              
1090             Compiler messages($cc):
1091 62         269 @{$self->{_ccmessages_}}
1092             EOERRORS
1093             }
1094              
1095             =head2 $reporter->nonfatalmessages( )
1096              
1097             Find failures worth reporting that won't cause tests to fail
1098              
1099             =cut
1100              
1101             sub nonfatalmessages {
1102 61     61 1 112 my $self = shift;
1103              
1104 61   50     528 my $ccinfo = $self->{_rpt}{cinfo} || $self->{_ccinfo} || "cc";
1105 61         333 $ccinfo =~ s/^(.+)\s+version\s+.+/$1/;
1106              
1107 61 50       609 $^O =~ /^(?:linux|.*bsd.*|darwin)/ and $ccinfo = 'gcc';
1108 61 50       418 my $cc = $ccinfo =~ /(gcc|bcc32)/ ? $1 : $^O;
1109              
1110 61 100       184 if (!$self->{_nonfatal_}) {
1111              
1112 60         241 $self->log_info("Looking for non-fatal messages: '%s'", $cc);
1113             $self->{_nonfatal_} = grepnonfatal(
1114             $cc,
1115             $self->get_outfile(),
1116             $self->{v}
1117 60   50     166 ) || [];
1118             }
1119              
1120 61 50       199 return @{$self->{_nonfatal_}} if wantarray;
  0         0  
1121 61 50       150 return "" if !$self->{_nonfatal_};
1122              
1123 61         140 local $" = "\n";
1124 61         160 return <<" EOERRORS";
1125              
1126             Non-Fatal messages($cc):
1127 61         206 @{$self->{_nonfatal_}}
1128             EOERRORS
1129             }
1130              
1131             =head2 $reporter->preamble( )
1132              
1133             Returns the header of the report.
1134              
1135             =cut
1136              
1137             sub preamble {
1138 63     63 1 2468 my $self = shift;
1139              
1140 63         301 my %Config = get_smoked_Config( $self->{ddir} => qw(
1141             version libc gnulibc_version
1142             ));
1143 63         731 my $si = System::Info->new;
1144 63         587697 my $archname = lc $si->cpu_type;
1145              
1146 63   50     1561 (my $ncpu = $si->ncpu || "") =~ s/^(\d+)\s*/$1 cpu/;
1147 63         2054 $archname .= "/$ncpu";
1148              
1149 63         381 my $cpu = $si->cpu;
1150              
1151 63   66     1203 my $this_host = $self->{hostname} || $si->host;
1152 63         1895 my $time_msg = time_in_hhmm( $self->{_rpt}{secs} );
1153 63         380 my $savg_msg = time_in_hhmm( $self->{_rpt}{avg} );
1154              
1155 63         955 my $cinfo = $self->ccinfo;
1156              
1157 63         518 my $os = $si->os;
1158              
1159 63         1104 my $branch = '';
1160 63 50       385 if ($self->{_rpt}{smokebranch}) {
1161 0         0 $branch = " branch $self->{_rpt}{smokebranch}";
1162             }
1163              
1164 63         727 my $preamble = <<__EOH__;
1165             Automated smoke report for$branch $Config{version} patch $self->{_rpt}{patchlevel}
1166             $this_host: $cpu ($archname)
1167             on $os
1168             using $cinfo
1169             smoketime $time_msg (average $savg_msg)
1170              
1171             __EOH__
1172              
1173 63 100       228 if ($self->{un_position} eq USERNOTE_ON_TOP) {
1174 1         18 (my $user_note = $self->{user_note}) =~ s/(?<=\S)\s*\z/\n/;
1175 1         6 $preamble = "$user_note\n$preamble";
1176             }
1177              
1178 63         4650 return $preamble;
1179             }
1180              
1181             =head2 $reporter->smoke_matrix( )
1182              
1183             C returns a string with the result-letters and their
1184             configs.
1185              
1186             =cut
1187              
1188             sub smoke_matrix {
1189 118     118 1 195799 my $self = shift;
1190 118         530 my $rpt = $self->{_rpt};
1191              
1192             # Maximum of 6 letters => 11 positions
1193 118         281 my $rptl = length $rpt->{patchdescr};
1194 118 100       843 my $pad = $rptl >= 11 ? "" : " " x int( (11 - $rptl)/2 );
1195 118         685 my $patch = $pad . $rpt->{patchdescr};
1196             my $report = sprintf "%-11s Configuration (common) %s\n",
1197 118         846 $patch, $rpt->{common_args};
1198 118         346 $report .= ("-" x 11) . " " . ("-" x 57) . "\n";
1199              
1200 118         182 foreach my $config ( @{ $rpt->{cfglist} } ) {
  118         591  
1201 250         627 my $letters = "";
1202 250         467 foreach my $dbinfo (qw( N D )) {
1203 500         704 foreach my $tstenv ( @{ $self->{_tstenv} } ) {
  500         875  
1204 832         2415 $letters .= "$rpt->{$config}{summary}{$dbinfo}{$tstenv} ";
1205             }
1206             }
1207 250         1275 my $cfg = join " ", grep ! exists $rpt->{_common_args}{ $_ }
1208             => quotewords( '\s+', 1, $config );
1209 250         28565 $report .= sprintf "%-12s%s\n", $letters, $cfg;
1210             }
1211              
1212 118         921 return $report;
1213             }
1214              
1215             =head2 $reporter->summary( )
1216              
1217             Return the B or B string.
1218              
1219             =cut
1220              
1221             sub summary {
1222 109     109 1 28317 my $self = shift;
1223 109         327 my $count = $self->{_counters};
1224 109         864 my @rpt_sum_stat = grep $count->{$_} > 0 => qw( X F M m c t );
1225 109         470 my $rpt_summary = "";
1226 109 100       448 if (@rpt_sum_stat) {
1227 77         703 $rpt_summary = "FAIL(" . join("", @rpt_sum_stat) . ")";
1228             }
1229             else {
1230 32 50       160 $rpt_summary = $count->{o} == 0 ? "PASS" : "PASS-so-far";
1231             }
1232              
1233 109         655 return $rpt_summary;
1234             }
1235              
1236             =head2 $reporter->has_test_failures( )
1237              
1238             Returns true if C<< @{ $reporter->{_failures} >>.
1239              
1240             =cut
1241              
1242 61 50   61 1 442 sub has_test_failures { exists $_[0]->{_failures} && @{ $_[0]->{_failures} } }
  61         769  
1243              
1244             =head2 $reporter->failures( )
1245              
1246             report the failures (grouped by configurations).
1247              
1248             =cut
1249              
1250             sub failures {
1251 84     84 1 8252 my $self = shift;
1252              
1253             return join "\n", map {
1254 123         307 join "\n", @{ $_->{cfgs} }, $_->{tests}, ""
  123         1676  
1255 84         190 } @{ $self->{_failures} };
  84         312  
1256             }
1257              
1258             =head2 $reporter->has_todo_passed( )
1259              
1260             Returns true if C<< @{ $reporter->{_todo_pasesd} >>.
1261              
1262             =cut
1263              
1264 61 50   61 1 401 sub has_todo_passed { exists $_[0]->{_todo_passed} && @{ $_[0]->{_todo_passed} } }
  61         619  
1265              
1266             =head2 $reporter->todo_passed( )
1267              
1268             report the todo that passed (grouped by configurations).
1269              
1270             =cut
1271              
1272             sub todo_passed {
1273 54     54 1 16783 my $self = shift;
1274              
1275             return join "\n", map {
1276 66         297 join "\n", @{ $_->{cfgs} }, $_->{tests}, ""
  66         696  
1277 54         248 } @{ $self->{_todo_passed} };
  54         234  
1278             }
1279              
1280             =head2 $reporter->has_mani_failures( )
1281              
1282             Returns true if C<< @{ $reporter->{_mani} >>.
1283              
1284             =cut
1285              
1286 61 50   61 1 438 sub has_mani_failures { exists $_[0]->{_mani} && @{ $_[0]->{_mani} } }
  61         340  
1287              
1288             =head2 $reporter->mani_fail( )
1289              
1290             report the MANIFEST failures.
1291              
1292             =cut
1293              
1294             sub mani_fail {
1295 6     6 1 16 my $self = shift;
1296              
1297 6         9 return join "\n", @{ $self->{_mani} }, "";
  6         32  
1298             }
1299              
1300             =head2 $reporter->bldenv_legend( )
1301              
1302             Returns a string with the legend for build-environments
1303              
1304             =cut
1305              
1306             sub bldenv_legend {
1307 63     63 1 1097 my $self = shift;
1308 0         0 $self->{defaultenv} = ( @{ $self->{_tstenv} } == 1 )
1309 63 50       193 unless defined $self->{defaultenv};
1310 63   100     807 my $debugging = $self->{_rpt}{dbughow} || '-DDEBUGGING';
1311              
1312 63 100 66     541 if ( $self->{_locale} && @{ $self->{_locale} } ) {
  63         324  
1313 10         45 my @locale = ( @{ $self->{_locale} }, @{ $self->{_locale} } );
  10         29  
  10         79  
1314 10         49 my $lcnt = @locale;
1315 10         48 my $half = int(( 4 + $lcnt ) / 2 );
1316 10         24 my $cnt = 2 * $half;
1317              
1318 10         27 my $line = '';
1319 10         54 for my $i ( 0 .. $cnt-1 ) {
1320 62         161 $line .= '| ' x ( $cnt - 1 - $i );
1321 62         103 $line .= '+';
1322 62         137 $line .= '-' x (2 * $i);
1323 62         99 $line .= '- ';
1324              
1325 62 100       163 if ( ($i % $half) < ($lcnt / 2) ) {
1326 22         41 my $locale = shift @locale; # XXX: perhaps pop()
1327 22         58 $line .= "LC_ALL = $locale"
1328             } else {
1329 40 100       68 $line .= ( (($i - @{$self->{_locale}}) % $half) % 2 == 0 )
  40         103  
1330             ? "PERLIO = perlio"
1331             : "PERLIO = stdio ";
1332             }
1333 62 100       164 $i < $half and $line .= " $debugging";
1334 62         117 $line .= "\n";
1335             }
1336 10         126 return $line;
1337             }
1338              
1339 53         187 my $locale = ''; # XXX
1340 53 100       818 return $locale ? <{defaultenv} ? <
    50          
1341             | | | | | +- LC_ALL = $locale $debugging
1342             | | | | +--- PERLIO = perlio $debugging
1343             | | | +----- PERLIO = stdio $debugging
1344             | | +------- LC_ALL = $locale
1345             | +--------- PERLIO = perlio
1346             +----------- PERLIO = stdio
1347              
1348             EOL
1349             | +--------- $debugging
1350             +----------- no debugging
1351              
1352             EOS
1353             | | | +----- PERLIO = perlio $debugging
1354             | | +------- PERLIO = stdio $debugging
1355             | +--------- PERLIO = perlio
1356             +----------- PERLIO = stdio
1357              
1358             EOE
1359             }
1360              
1361             =head2 $reporter->letter_legend( )
1362              
1363             Returns a string with the legend for the letters in the matrix.
1364              
1365             =cut
1366              
1367             sub letter_legend {
1368 61     61 1 521 require Test::Smoke::Smoker;
1369             return <<__EOL__
1370             O = OK F = Failure(s), extended report at the bottom
1371             X = Failure(s) under TEST but not under harness
1372             ? = still running or test results not (yet) available
1373             Build failures during: - = unknown or N/A
1374             c = Configure, m = make, M = make (after miniperl), t = make test-prep
1375             __EOL__
1376 61         439 }
1377              
1378             =head2 $reporter->signature()
1379              
1380             Returns the signature for the e-mail message (starting with dash dash space
1381             newline) and some version numbers.
1382              
1383             =cut
1384              
1385             sub signature {
1386 62     62 1 772 my $self = shift;
1387 62 50       2000 my $this_pver = $^V ? sprintf "%vd", $^V : $];
1388 62         392 my $build_info = "$Test::Smoke::VERSION";
1389              
1390 62         259 my $signature = <<" __EOS__";
1391             --
1392             Report by Test::Smoke v$build_info running on perl $this_pver
1393             (Reporter v$VERSION / Smoker v$Test::Smoke::Smoker::VERSION)
1394             __EOS__
1395              
1396 62 50       208 if ($self->{un_position} ne USERNOTE_ON_TOP) {
1397 62         557 (my $user_note = $self->{user_note}) =~ s/(?<=\S)\s*\z/\n/;
1398 62         278 $signature = "\n$user_note\n$signature";
1399             }
1400              
1401 62         217 return $signature;
1402             }
1403              
1404             1;
1405              
1406             =head1 SEE ALSO
1407              
1408             L
1409              
1410             =head1 COPYRIGHT
1411              
1412             (c) 2002-2012, All rights reserved.
1413              
1414             * Abe Timmerman
1415             * H.Merijn Brand
1416              
1417             This library is free software; you can redistribute it and/or modify
1418             it under the same terms as Perl itself.
1419              
1420             See:
1421              
1422             =over 4
1423              
1424             =item * http://www.perl.com/perl/misc/Artistic.html
1425              
1426             =item * http://www.gnu.org/copyleft/gpl.html
1427              
1428             =back
1429              
1430             This program is distributed in the hope that it will be useful,
1431             but WITHOUT ANY WARRANTY; without even the implied warranty of
1432             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1433              
1434             =cut