File Coverage

blib/lib/Test/Smoke/Reporter.pm
Criterion Covered Total %
statement 531 614 86.4
branch 197 278 70.8
condition 88 153 57.5
subroutine 44 46 95.6
pod 27 27 100.0
total 887 1118 79.3


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