Herein lies Test-Reporter-1.38.
[test-reporter.git] / lib / Test / Reporter.pm
blob658b615c1f55eb158139719242b0a833e719016a
1 # Test::Reporter - sends test results to cpan-testers@perl.org
2 # Copyright (c) 2007 Adam J. Foxson. All rights reserved.
4 # This program is free software; you can redistribute it and/or modify
5 # it under the same terms as Perl itself.
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 package Test::Reporter;
13 use strict;
14 use Cwd;
15 use Config;
16 use Carp;
17 use Net::SMTP;
18 use FileHandle;
19 use File::Temp;
20 use Sys::Hostname;
21 use Time::Local ();
22 use vars qw($VERSION $AUTOLOAD $Tempfile $Report $DNS $Domain $Send);
23 use constant FAKE_NO_NET_DNS => 0; # for debugging only
24 use constant FAKE_NO_NET_DOMAIN => 0; # for debugging only
25 use constant FAKE_NO_MAIL_SEND => 0; # for debugging only
27 $VERSION = '1.38';
29 local $^W = 1;
31 sub new {
32 my $type = shift;
33 my $class = ref($type) || $type;
34 my $self = {
35 '_mx' => ['mx.develooper.com'],
36 '_address' => 'cpan-testers@perl.org',
37 '_grade' => undef,
38 '_distribution' => undef,
39 '_report' => undef,
40 '_subject' => undef,
41 '_from' => undef,
42 '_comments' => '',
43 '_errstr' => '',
44 '_via' => '',
45 '_mail_send_args' => '',
46 '_timeout' => 120,
47 '_debug' => 0,
48 '_dir' => '',
49 '_subject_lock' => 0,
50 '_report_lock' => 0,
51 '_perl_version' => {
52 '_archname' => $Config{archname},
53 '_osvers' => $Config{osvers},
54 '_myconfig' => Config::myconfig(),
56 '_transport' => '',
59 bless $self, $class;
61 $self->{_attr} = {
62 map {$_ => 1} qw(
63 _address _distribution _comments _errstr _via _timeout _debug _dir
67 warn __PACKAGE__, ": new\n" if $self->debug();
68 croak __PACKAGE__, ": new: even number of named arguments required"
69 unless scalar @_ % 2 == 0;
71 $self->_process_params(@_) if @_;
72 $self->_get_mx(@_) if $self->_have_net_dns();
74 return $self;
77 sub _get_mx {
78 my $self = shift;
79 warn __PACKAGE__, ": _get_mx\n" if $self->debug();
81 my %params = @_;
83 return if exists $params{'mx'};
85 my $dom = $params{'address'} || $self->address();
86 my @mx;
88 $dom =~ s/^.+\@//;
90 for my $mx (sort {$a->preference() <=> $b->preference()} Net::DNS::mx($dom)) {
91 push @mx, $mx->exchange();
94 if (not @mx) {
95 warn __PACKAGE__,
96 ": _get_mx: unable to find MX's for $dom, using defaults\n" if
97 $self->debug();
98 return;
101 $self->mx(\@mx);
104 sub _process_params {
105 my $self = shift;
106 warn __PACKAGE__, ": _process_params\n" if $self->debug();
108 my %params = @_;
109 my @defaults = qw(
110 mx address grade distribution from comments via timeout debug dir perl_version transport);
111 my %defaults = map {$_ => 1} @defaults;
113 for my $param (keys %params) {
114 croak __PACKAGE__, ": new: parameter '$param' is invalid." unless
115 exists $defaults{$param};
118 for my $param (keys %params) {
119 $self->$param($params{$param});
123 sub subject {
124 my $self = shift;
125 warn __PACKAGE__, ": subject\n" if $self->debug();
126 croak __PACKAGE__, ": subject: grade and distribution must first be set"
127 if not defined $self->{_grade} or not defined $self->{_distribution};
129 return $self->{_subject} if $self->{_subject_lock};
131 my $subject = uc($self->{_grade}) . ' ' . $self->{_distribution} .
132 " $self->{_perl_version}->{_archname} $self->{_perl_version}->{_osvers}";
134 return $self->{_subject} = $subject;
137 sub report {
138 my $self = shift;
139 warn __PACKAGE__, ": report\n" if $self->debug();
141 return $self->{_report} if $self->{_report_lock};
143 my $report;
144 $report .= "This distribution has been tested as part of the cpan-testers\n";
145 $report .= "effort to test as many new uploads to CPAN as possible. See\n";
146 $report .= "http://testers.cpan.org/\n\n";
147 $report .= "Please cc any replies to cpan-testers\@perl.org to keep other\n";
148 $report .= "test volunteers informed and to prevent any duplicate effort.\n";
150 if (not $self->{_comments}) {
151 $report .= "\n\n--\n\n";
153 else {
154 $report .= "\n--\n" . $self->{_comments} . "\n--\n\n";
157 $report .= $self->{_perl_version}->{_myconfig};
159 chomp $report;
160 chomp $report;
162 return $self->{_report} = $report;
165 sub grade {
166 my ($self, $grade) = @_;
167 warn __PACKAGE__, ": grade\n" if $self->debug();
169 my %grades = (
170 'pass' => "all tests passed",
171 'fail' => "one or more tests failed",
172 'na' => "distribution will not work on this platform",
173 'unknown' => "distribution did not include tests",
176 return $self->{_grade} if scalar @_ == 1;
178 croak __PACKAGE__, ":grade: '$grade' is invalid, choose from: " .
179 join ' ', keys %grades unless $grades{$grade};
181 return $self->{_grade} = $grade;
184 sub transport {
185 my $self = shift;
186 warn __PACKAGE__, ": transport\n" if $self->debug();
188 my %transports = (
189 # support for plugin transports will eventually be added, but not today
190 'Net::SMTP' => 'Builtin transport using Net::SMTP',
191 'Mail::Send' => 'Builtin transport using Mail::Send',
194 return $self->{_transport} unless scalar @_;
196 my $transport = shift;
198 croak __PACKAGE__, ":transport: '$transport' is invalid, choose from: " .
199 join ' ', keys %transports unless $transports{$transport};
201 my $args = shift;
203 if ($transport eq 'Mail::Send' && defined $args && ref $args eq 'ARRAY') {
204 $self->mail_send_args($args);
207 return $self->{_transport} = $transport;
210 sub edit_comments {
211 my($self, %args) = @_;
212 warn __PACKAGE__, ": edit_comments\n" if $self->debug();
214 my %tempfile_args = (
215 UNLINK => 1,
216 SUFFIX => '.txt',
219 if (exists $args{'suffix'} && defined $args{'suffix'} && length $args{'suffix'}) {
220 $tempfile_args{SUFFIX} = $args{'suffix'};
221 # prefix the extension with a period, if the user didn't.
222 $tempfile_args{SUFFIX} =~ s/^(?!\.)(?=.)/./;
225 ($Tempfile, $Report) = File::Temp::tempfile(%tempfile_args);
227 print $Tempfile $self->{_comments};
229 $self->_start_editor();
231 my $comments;
233 local $/;
234 open FH, $Report or die __PACKAGE__, ": Can't open comment file '$Report': $!";
235 $comments = <FH>;
236 close FH or die __PACKAGE__, ": Can't close comment file '$Report': $!";
239 chomp $comments;
241 $self->{_comments} = $comments;
243 return;
246 sub send {
247 my ($self, @recipients) = @_;
248 warn __PACKAGE__, ": send\n" if $self->debug();
250 $self->from();
251 $self->report();
252 $self->subject();
254 return unless $self->_verify();
256 if ($self->_is_a_perl_release($self->distribution())) {
257 $self->errstr(__PACKAGE__ . ": use perlbug for reporting test " .
258 "results against perl itself");
259 return;
262 my $transport = $self->transport();
264 if ($transport eq 'Mail::Send' && $self->_have_mail_send()) {
265 return $self->_mail_send(@recipients);
267 elsif ($transport eq 'Net::SMTP') {
268 return $self->_send_smtp(@recipients);
270 else {
271 # Addresses #9831: Usage of Mail::Mailer is broken on Win32
272 if ($^O !~ /^(?:cygwin|MSWin32|VMS)$/ && $self->_have_mail_send()) {
273 return $self->_mail_send(@recipients);
275 else {
276 return $self->_send_smtp(@recipients);
281 sub write {
282 my $self = shift;
283 warn __PACKAGE__, ": write\n" if $self->debug();
285 my $from = $self->from();
286 my $report = $self->report();
287 my $subject = $self->subject();
288 my $distribution = $self->distribution();
289 my $grade = $self->grade();
290 my $dir = $self->dir() || cwd;
292 return unless $self->_verify();
294 $distribution =~ s/[^A-Za-z0-9\.\-]+//g;
296 my($fh, $file); unless ($fh = $_[0]) {
297 $file = "$grade.$distribution.$self->{_perl_version}->{_archname}.$self->{_perl_version}->{_osvers}.${\(time)}.$$.rpt";
299 if ($^O eq 'VMS') {
300 $file = "$grade.$distribution.$self->{_perl_version}->{_archname}";
301 my $ext = "$self->{_perl_version}->{_osvers}.${\(time)}.$$.rpt";
302 # only 1 period in filename
303 # we also only have 39.39 for filename
304 $file =~ s/\./_/g;
305 $ext =~ s/\./_/g;
306 $file = $file . '.' . $ext;
309 $file = File::Spec->catfile($dir, $file);
311 warn $file if $self->debug();
312 $fh = FileHandle->new();
313 open $fh, ">$file" or die __PACKAGE__, ": Can't open report file '$file': $!";
315 print $fh "From: $from\n";
316 print $fh "Subject: $subject\n";
317 print $fh "Report: $report";
318 unless ($_[0]) {
319 close $fh or die __PACKAGE__, ": Can't close report file '$file': $!";
320 warn $file if $self->debug();
321 return $file;
322 } else {
323 return $fh;
327 sub read {
328 my ($self, $file) = @_;
329 warn __PACKAGE__, ": read\n" if $self->debug();
331 my $buffer;
334 local $/;
335 open REPORT, $file or die __PACKAGE__, ": Can't open report file '$file': $!";
336 $buffer = <REPORT>;
337 close REPORT or die __PACKAGE__, ": Can't close report file '$file': $!";
340 if (my ($from, $subject, $report) = $buffer =~ /^From:\s(.+)Subject:\s(.+)Report:\s(.+)$/s) {
341 my ($grade, $distribution) = (split /\s/, $subject)[0,1];
342 $self->from($from) unless $self->from();
343 $self->{_subject} = $subject;
344 $self->{_report} = $report;
345 $self->{_grade} = lc $grade;
346 $self->{_distribution} = $distribution;
347 $self->{_subject_lock} = 1;
348 $self->{_report_lock} = 1;
349 } else {
350 die __PACKAGE__, ": Failed to parse report file '$file'\n";
353 return $self;
356 sub _verify {
357 my $self = shift;
358 warn __PACKAGE__, ": _verify\n" if $self->debug();
360 my @undefined;
362 for my $key (keys %{$self}) {
363 push @undefined, $key unless defined $self->{$key};
366 $self->errstr(__PACKAGE__ . ": Missing values for: " .
367 join ', ', map {$_ =~ /^_(.+)$/} @undefined) if
368 scalar @undefined > 0;
369 return $self->errstr() ? return 0 : return 1;
372 sub _mail_send {
373 my $self = shift;
374 warn __PACKAGE__, ": _mail_send\n" if $self->debug();
376 my $fh;
377 my $recipients;
378 my @recipients = @_;
379 my $via = $self->via();
380 my $msg = Mail::Send->new();
382 if (@recipients) {
383 $recipients = join ', ', @recipients;
384 chomp $recipients;
385 chomp $recipients;
388 $via = ', via ' . $via if $via;
390 $msg->to($self->address());
391 $msg->set('From', $self->from());
392 $msg->subject($self->subject());
393 $msg->add('X-Reported-Via', "Test::Reporter ${VERSION}$via");
394 $msg->add('Cc', $recipients) if @_;
396 if ($self->mail_send_args() and ref $self->mail_send_args() eq 'ARRAY') {
397 $fh = $msg->open(@{$self->mail_send_args()});
399 else {
400 $fh = $msg->open();
403 print $fh $self->report();
405 $fh->close();
408 sub _send_smtp {
409 my $self = shift;
410 warn __PACKAGE__, ": _send_smtp\n" if $self->debug();
412 my $helo = $self->_maildomain();
413 my $from = $self->from();
414 my $via = $self->via();
415 my $debug = $self->debug();
416 my @recipients = @_;
417 my @tmprecipients = ();
418 my @bad = ();
419 my $success = 0;
420 my $fail = 0;
421 my $recipients;
422 my $smtp;
424 my $mx;
425 for my $server (@{$self->{_mx}}) {
426 $smtp = Net::SMTP->new($server, Hello => $helo,
427 Timeout => $self->{_timeout}, Debug => $debug);
429 if (defined $smtp) {
430 $mx = $server;
431 last;
433 else {
434 warn __PACKAGE__, ": Unable to connect to MX '$server'\n" if $self->debug();
435 $fail++;
439 unless ($mx && $smtp) {
440 $self->errstr(__PACKAGE__ . ': Unable to connect to any MX\'s');
441 return 0;
444 if (@recipients) {
445 if ($mx =~ /(?:^|\.)(?:perl|cpan)\.org$/) {
446 for my $recipient (sort @recipients) {
447 if ($recipient =~ /(?:@|\.)(?:perl|cpan)\.org$/) {
448 push @tmprecipients, $recipient;
449 } else {
450 push @bad, $recipient;
454 if (@bad) {
455 warn __PACKAGE__, ": Will not attempt to cc the following recipients since perl.org MX's will not relay for them. Either install Mail::Send, use other MX's, or only cc address ending in cpan.org or perl.org: ${\(join ', ', @bad)}.\n";
458 @recipients = @tmprecipients;
461 $recipients = join ', ', @recipients;
462 chomp $recipients;
463 chomp $recipients;
466 $via = ', via ' . $via if $via;
468 my $envelope_sender = $from;
469 $envelope_sender =~ s/\s\([^)]+\)$//; # email only; no name
471 $success += $smtp->mail($envelope_sender);
472 $success += $smtp->to($self->{_address});
473 $success += $smtp->cc(@recipients) if @recipients;
474 $success += $smtp->data();
475 $success += $smtp->datasend("Date: ", $self->_format_date, "\n");
476 $success += $smtp->datasend("Subject: ", $self->subject(), "\n");
477 $success += $smtp->datasend("From: $from\n");
478 $success += $smtp->datasend("To: ", $self->{_address}, "\n");
479 $success += $smtp->datasend("Cc: $recipients\n") if @recipients && $success == 8;
480 $success += $smtp->datasend("Message-ID: ", $self->message_id(), "\n");
481 $success +=
482 $smtp->datasend("X-Reported-Via: Test::Reporter ${VERSION}$via\n");
483 $success += $smtp->datasend("\n");
484 $success += $smtp->datasend($self->report());
485 $success += $smtp->dataend();
486 $success += $smtp->quit;
488 if (@recipients) {
489 $self->errstr(__PACKAGE__ .
490 ": Unable to send test report to one or more recipients\n") if $success != 15;
492 else {
493 $self->errstr(__PACKAGE__ . ": Unable to send test report\n") if $success != 13;
496 return $self->errstr() ? 0 : 1;
499 # Courtesy of Email::MessageID
500 sub message_id {
501 my $self = shift;
502 warn __PACKAGE__, ": message_id\n" if $self->debug();
504 my $unique_value = 0;
505 my @CHARS = ('A'..'F','a'..'f',0..9);
506 my $length = 3;
508 $length = rand(8) until $length > 3;
510 my $pseudo_random = join '', (map $CHARS[rand $#CHARS], 0 .. $length), $unique_value++;
511 my $user = join '.', time, $pseudo_random, $$;
513 return '<' . $user . '@' . Sys::Hostname::hostname() . '>';
516 sub from {
517 my $self = shift;
518 warn __PACKAGE__, ": from\n" if $self->debug();
520 if (@_) {
521 $self->{_from} = shift;
522 return $self->{_from};
524 else {
525 return $self->{_from} if defined $self->{_from} and $self->{_from};
526 $self->{_from} = $self->_mailaddress();
527 return $self->{_from};
532 sub mx {
533 my $self = shift;
534 warn __PACKAGE__, ": mx\n" if $self->debug();
536 if (@_) {
537 my $mx = shift;
538 croak __PACKAGE__,
539 ": mx: array reference required" if ref $mx ne 'ARRAY';
540 $self->{_mx} = $mx;
543 return $self->{_mx};
546 sub mail_send_args {
547 my $self = shift;
548 warn __PACKAGE__, ": mail_send_args\n" if $self->debug();
549 croak __PACKAGE__, ": mail_send_args cannot be called unless Mail::Send is installed\n" unless $self->_have_mail_send();
551 if (@_) {
552 my $mail_send_args = shift;
553 croak __PACKAGE__, ": mail_send_args: array reference required" if
554 ref $mail_send_args ne 'ARRAY';
555 $self->{_mail_send_args} = $mail_send_args;
558 return $self->{_mail_send_args};
561 sub perl_version {
562 my $self = shift;
563 warn __PACKAGE__, ": perl_version\n" if $self->debug();
565 if( @_) {
566 my $perl = shift;
567 my $q = ( ($^O eq "MSWin32") || ($^O eq 'VMS') ) ? '"' : "'"; # quote for command-line perl
568 my $magick = int(rand(1000)); # just to check that we get a valid result back
569 my $cmd = "$perl -MConfig -e$q print qq{$magick\n\$Config{archname}\n\$Config{osvers}\n},Config::myconfig();$q";
570 if($^O eq 'VMS'){
571 my $sh = $Config{'sh'};
572 $cmd = "$sh $perl $q-MConfig$q -e$q print qq{$magick\\n\$Config{archname}\\n\$Config{osvers}\\n},Config::myconfig();$q";
574 my $conf = `$cmd`;
575 my %conf;
576 ( @conf{ qw( magick _archname _osvers _myconfig) } ) = split( /\n/, $conf, 4);
577 croak __PACKAGE__, ": cannot get perl version info from $perl: $conf" if( $conf{magick} ne $magick);
578 delete $conf{magick};
579 $self->{_perl_version} = \%conf;
581 return $self->{_perl_version};
584 sub AUTOLOAD {
585 my $self = $_[0];
586 my ($package, $method) = ($AUTOLOAD =~ /(.*)::(.*)/);
588 return if $method =~ /^DESTROY$/;
590 unless ($self->{_attr}->{"_$method"}) {
591 croak __PACKAGE__, ": No such method: $method; aborting";
594 my $code = q{
595 sub {
596 my $self = shift;
597 warn __PACKAGE__, ": METHOD\n" if $self->{_debug};
598 $self->{_METHOD} = shift if @_;
599 return $self->{_METHOD};
603 $code =~ s/METHOD/$method/g;
606 no strict 'refs';
607 *$AUTOLOAD = eval $code;
610 goto &$AUTOLOAD;
613 sub _have_net_dns {
614 my $self = shift;
615 warn __PACKAGE__, ": _have_net_dns\n" if $self->debug();
617 return $DNS if defined $DNS;
618 return 0 if FAKE_NO_NET_DNS;
620 $DNS = eval {require Net::DNS};
623 sub _have_net_domain {
624 my $self = shift;
625 warn __PACKAGE__, ": _have_net_domain\n" if $self->debug();
627 return $Domain if defined $Domain;
628 return 0 if FAKE_NO_NET_DOMAIN;
630 $Domain = eval {require Net::Domain};
633 sub _have_mail_send {
634 my $self = shift;
635 warn __PACKAGE__, ": _have_mail_send\n" if $self->debug();
637 return $Send if defined $Send;
638 return 0 if FAKE_NO_MAIL_SEND;
640 $Send = eval {require Mail::Send};
643 sub _start_editor {
644 my $self = shift;
645 warn __PACKAGE__, ": _start_editor\n" if $self->debug();
647 my $editor = $ENV{VISUAL} || $ENV{EDITOR} || $ENV{EDIT}
648 || ($^O eq 'VMS' and "edit/tpu")
649 || ($^O eq 'MSWin32' and "notepad")
650 || 'vi';
652 $editor = $self->_prompt('Editor', $editor);
654 die __PACKAGE__, ": The editor `$editor' could not be run" if system "$editor $Report";
655 die __PACKAGE__, ": Report has disappeared; terminated" unless -e $Report;
656 die __PACKAGE__, ": Empty report; terminated" unless -s $Report > 2;
659 sub _prompt {
660 my $self = shift;
661 warn __PACKAGE__, ": _prompt\n" if $self->debug();
663 my ($label, $default) = @_;
665 printf "$label%s", (" [$default]: ");
666 my $input = scalar <STDIN>;
667 chomp $input;
669 return (length $input) ? $input : $default;
672 # From Mail::Util 1.74 (c) 1995-2001 Graham Barr (c) 2002-2005 Mark Overmeer
673 sub _maildomain {
674 my $self = shift;
675 warn __PACKAGE__, ": _maildomain\n" if $self->debug();
677 my $domain = $ENV{MAILDOMAIN};
679 return $domain if defined $domain;
681 local *CF;
682 local $_;
684 my @sendmailcf = qw(
685 /etc /etc/sendmail /etc/ucblib /etc/mail /usr/lib /var/adm/sendmail
688 my $config = (grep(-r, map("$_/sendmail.cf", @sendmailcf)))[0];
690 if (defined $config && open(CF, $config)) {
691 my %var;
692 while (<CF>) {
693 if (my ($v, $arg) = /^D([a-zA-Z])([\w.\$\-]+)/) {
694 $arg =~ s/\$([a-zA-Z])/exists $var{$1} ? $var{$1} : '$'.$1/eg;
695 $var{$v} = $arg;
698 close(CF) || die $!;
699 $domain = $var{j} if defined $var{j};
700 $domain = $var{M} if defined $var{M};
702 $domain = $1
703 if ($domain && $domain =~ m/([A-Za-z0-9](?:[\.\-A-Za-z0-9]+))/);
705 undef $domain if $^O eq 'darwin' && $domain =~ /\.local$/;
707 return $domain if (defined $domain && $domain !~ /\$/);
710 if (open(CF, "/usr/lib/smail/config")) {
711 while (<CF>) {
712 if (/\A\s*hostnames?\s*=\s*(\S+)/) {
713 $domain = (split(/:/,$1))[0];
714 undef $domain if $^O eq 'darwin' && $domain =~ /\.local$/;
715 last if defined $domain and $domain;
718 close(CF) || die $!;
720 return $domain if defined $domain;
723 if (eval {require Net::SMTP}) {
724 my $host;
726 for $host (qw(mailhost localhost)) {
727 my $smtp = eval {Net::SMTP->new($host)};
729 if (defined $smtp) {
730 $domain = $smtp->domain;
731 $smtp->quit;
732 undef $domain if $^O eq 'darwin' && $domain =~ /\.local$/;
733 last if defined $domain and $domain;
738 unless (defined $domain) {
739 if ($self->_have_net_domain()) {
740 ###################################################################
741 # The below statement might possibly exhibit intermittent blocking
742 # behavior. Be advised!
743 ###################################################################
744 $domain = Net::Domain::domainname();
745 undef $domain if $^O eq 'darwin' && $domain =~ /\.local$/;
749 $domain = "localhost" unless defined $domain;
751 return $domain;
754 # From Mail::Util 1.74 (c) 1995-2001 Graham Barr (c) 2002-2005 Mark Overmeer
755 sub _mailaddress {
756 my $self = shift;
757 warn __PACKAGE__, ": _mailaddress\n" if $self->debug();
759 my $mailaddress = $ENV{MAILADDRESS};
760 $mailaddress ||= $ENV{USER} ||
761 $ENV{LOGNAME} ||
762 eval {getpwuid($>)} ||
763 "postmaster";
764 $mailaddress .= '@' . $self->_maildomain() unless $mailaddress =~ /\@/;
765 $mailaddress =~ s/(^.*<|>.*$)//g;
767 my $realname = $self->_realname();
768 if ($realname) {
769 $mailaddress = "$mailaddress ($realname)";
772 return $mailaddress;
775 sub _realname {
776 my $self = shift;
777 warn __PACKAGE__, ": _realname\n" if $self->debug();
779 my $realname = '';
781 $realname =
782 eval {(split /,/, (getpwuid($>))[6])[0]} ||
783 $ENV{QMAILNAME} ||
784 $ENV{REALNAME} ||
785 $ENV{USER};
787 return $realname;
790 sub _is_a_perl_release {
791 my $self = shift;
792 warn __PACKAGE__, ": _is_a_perl_release\n" if $self->debug();
794 my $perl = shift;
796 return $perl =~ /^perl-?\d\.\d/;
800 # Next two subs courtesy of Casey West, Ricardo SIGNES, and Email::Date
801 # Visit the Perl Email Project at: http://emailproject.perl.org/
802 sub _tz_diff {
803 my $self = shift;
804 warn __PACKAGE__, ": _tz_diff\n" if $self->debug();
806 my ($time) = @_;
808 my $diff = Time::Local::timegm(localtime $time)
809 - Time::Local::timegm(gmtime $time);
811 my $direc = $diff < 0 ? '-' : '+';
812 $diff = abs $diff;
813 my $tz_hr = int( $diff / 3600 );
814 my $tz_mi = int( $diff / 60 - $tz_hr * 60 );
816 return ($direc, $tz_hr, $tz_mi);
819 sub _format_date {
820 my $self = shift;
821 warn __PACKAGE__, ": _format_date\n" if $self->debug();
823 my ($time) = @_;
824 $time = time unless defined $time;
826 my ($sec, $min, $hour, $mday, $mon, $year, $wday) = (localtime $time);
827 my $day = (qw[Sun Mon Tue Wed Thu Fri Sat])[$wday];
828 my $month = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$mon];
829 $year += 1900;
831 my ($direc, $tz_hr, $tz_mi) = $self->_tz_diff($time);
833 sprintf "%s, %d %s %d %02d:%02d:%02d %s%02d%02d",
834 $day, $mday, $month, $year, $hour, $min, $sec, $direc, $tz_hr, $tz_mi;
837 =head1 NAME
839 Test::Reporter - sends test results to cpan-testers@perl.org
841 =head1 SYNOPSIS
843 use Test::Reporter;
845 my $reporter = Test::Reporter->new();
847 $reporter->grade('pass');
848 $reporter->distribution('Mail-Freshmeat-1.20');
849 $reporter->send() || die $reporter->errstr();
851 # or
853 my $reporter = Test::Reporter->new();
855 $reporter->grade('fail');
856 $reporter->distribution('Mail-Freshmeat-1.20');
857 $reporter->comments('output of a failed make test goes here...');
858 $reporter->edit_comments(); # if you want to edit comments in an editor
859 $reporter->send('afoxson@cpan.org') || die $reporter->errstr();
861 # or
863 my $reporter = Test::Reporter->new(
864 grade => 'fail',
865 distribution => 'Mail-Freshmeat-1.20',
866 from => 'whoever@wherever.net (Whoever Wherever)',
867 comments => 'output of a failed make test goes here...',
868 via => 'CPANPLUS X.Y.Z',
870 $reporter->send() || die $reporter->errstr();
872 =head1 DESCRIPTION
874 Test::Reporter reports the test results of any given distribution to the CPAN
875 Testers. Test::Reporter has wide support for various perl5's and platforms. For
876 further information visit the below links:
878 =over 4
880 =item * L<http://cpantesters.perl.org/>
882 CPAN Testers reports (new site)
884 =item * L<http://testers.cpan.org/>
886 CPAN Testers reports (old site)
888 =item * L<http://cpantest.grango.org/>
890 The new CPAN Testers Wiki (thanks Barbie!)
892 =item * L<http://lists.cpan.org/showlist.cgi?name=cpan-testers>
894 The cpan-testers mailing list
896 =back
898 Test::Reporter itself--as a project--also has several links for your visiting
899 enjoyment:
901 =over 4
903 =item * L<http://code.google.com/p/test-reporter/>
905 Test::Reporter's master project page
907 =item * L<http://groups.google.com/group/test-reporter>
909 Discussion group for Test::Reporter
911 =item * L<http://code.google.com/p/test-reporter/w/list>
913 The Wiki for Test::Reporter
915 =item * L<http://repo.or.cz/w/test-reporter.git>
917 Test::Reporter's public git source code repository.
919 =item * L<http://search.cpan.org/dist/Test-Reporter/>
921 Test::Reporter on CPAN
923 =item * L<http://code.google.com/p/test-reporter/issues/list>
925 UNFORTUNATELY, WE ARE UNABLE TO ACCEPT TICKETS FILED WITH RT.
927 Please file all bug reports and enhancement requests at our Google Code issue
928 tracker. Thank you for your support and understanding.
930 =item * L<http://backpan.cpan.org/authors/id/F/FO/FOX/>
932 =item * L<http://backpan.cpan.org/authors/id/A/AF/AFOXSON/>
934 If you happen to--for some strange reason--be looking for primordial versions
935 of Test::Reporter, you can almost certainly find them at the above 2 links.
937 =back
939 =head1 METHODS
941 =over 4
943 =item * B<address>
945 Optional. Gets or sets the e-mail address that the reports will be
946 sent to. By default, this is set to cpan-testers@perl.org. You shouldn't
947 need this unless the CPAN Tester's change the e-mail address to send
948 report's to.
950 =item * B<comments>
952 Optional. Gets or sets the comments on the test report. This is most
953 commonly used for distributions that did not pass a 'make test'.
955 =item * B<debug>
957 Optional. Gets or sets the value that will turn debugging on or off.
958 Debug messages are sent to STDERR. 1 for on, 0 for off. Debugging
959 generates very verbose output and is useful mainly for finding bugs
960 in Test::Reporter itself.
962 =item * B<dir>
964 Optional. Defaults to the current working directory. This method specifies
965 the directory that write() writes test report files to.
967 =item * B<distribution>
969 Gets or sets the name of the distribution you're working on, for example
970 Foo-Bar-0.01. There are no restrictions on what can be put here.
972 =item * B<edit_comments>
974 Optional. Allows one to interactively edit the comments within a text
975 editor. comments() doesn't have to be first specified, but it will work
976 properly if it was. Accepts an optional hash of arguments:
978 =over 4
980 =item * B<suffix>
982 Optional. Allows one to specify the suffix ("extension") of the temp
983 file used by B<edit_comments>. Defaults to '.txt'.
985 =back
987 =item * B<errstr>
989 Returns an error message describing why something failed. You must check
990 errstr() on a send() in order to be guaranteed delivery. This is optional
991 if you don't intend to use Test::Reporter to send reports via e-mail,
992 see 'send' below for more information.
994 =item * B<from>
996 Optional. Gets or sets the e-mail address of the individual submitting
997 the test report, i.e. "afoxson@pobox.com (Adam Foxson)". This is
998 mostly of use to testers running under Windows, since Test::Reporter
999 will usually figure this out automatically. Alternatively, you can use
1000 the MAILADDRESS environmental variable to accomplish the same.
1002 =item * B<grade>
1004 Gets or sets the success or failure of the distributions's 'make test'
1005 result. This must be one of:
1007 grade meaning
1008 ----- -------
1009 pass all tests passed
1010 fail one or more tests failed
1011 na distribution will not work on this platform
1012 unknown distribution did not include tests
1014 =item * B<mail_send_args>
1016 Optional. If you have MailTools installed and you want to have it
1017 behave in a non-default manner, parameters that you give this
1018 method will be passed directly to the constructor of
1019 Mail::Mailer. See L<Mail::Mailer> and L<Mail::Send> for details.
1021 =item * B<message_id>
1023 Returns an automatically generated Message ID. This Message ID will later
1024 be included as an outgoing mail header in the test report e-mail. This was
1025 included to conform to local mail policies at perl.org. This method courtesy
1026 of Email::MessageID.
1028 =item * B<mx>
1030 Optional. Gets or sets the mail exchangers that will be used to send
1031 the test reports. If you override the default values make sure you
1032 pass in a reference to an array. By default, this contains the MX's
1033 known at the time of release for perl.org. If you do not have
1034 Mail::Send installed (thus using the Net::SMTP interface) and do have
1035 Net::DNS installed it will dynamically retrieve the latest MX's. You
1036 really shouldn't need to use this unless the hardcoded MX's have
1037 become wrong and you don't have Net::DNS installed.
1039 =item * B<new>
1041 This constructor returns a Test::Reporter object. It will optionally accept
1042 named parameters for: mx, address, grade, distribution, from, comments,
1043 via, timeout, debug, dir, perl_version, and transport.
1045 =item * B<perl_version>
1047 Returns a hashref containing _archname, _osvers, and _myconfig based upon the
1048 perl that you are using. Alternatively, you may supply a different perl (path
1049 to the binary) as an argument, in which case the supplied perl will be used as
1050 the basis of the above data.
1052 =item * B<report>
1054 Returns the actual content of a report, i.e.
1055 "This distribution has been tested as part of the cpan-testers...".
1056 'comments' must first be specified before calling this method, if you have
1057 comments to make and expect them to be included in the report.
1059 =item * B<send>
1061 Sends the test report to cpan-testers@perl.org and cc's the e-mail to the
1062 specified recipients, if any. If you do specify recipients to be cc'd and
1063 you do not have Mail::Send installed be sure that you use the author's
1064 @cpan.org address otherwise they will not be delivered. You must check
1065 errstr() on a send() in order to be guaranteed delivery. Technically, this
1066 is optional, as you may use Test::Reporter to only obtain the 'subject' and
1067 'report' without sending an e-mail at all, although that would be unusual.
1069 =item * B<subject>
1071 Returns the subject line of a report, i.e.
1072 "PASS Mail-Freshmeat-1.20 Darwin 6.0". 'grade' and 'distribution' must
1073 first be specified before calling this method.
1075 =item * B<timeout>
1077 Optional. Gets or sets the timeout value for the submission of test
1078 reports. Default is 120 seconds.
1080 =item * B<transport>
1082 Optional. Gets or sets the transport method. If you do not specify a transport,
1083 one will be selected automatically on your behalf: If you're on Windows,
1084 Net::SMTP will be selected, if you're not on Windows, Net::SMTP will be
1085 selected unless Mail::Send is installed, in which case Mail::Send is used.
1087 At the moment, this must be one of either 'Net::SMTP', or 'Mail::Send'.
1088 Support for authenticated SMTP may soon be possibly added as well.
1090 If you specify 'Mail::Send' as a transport, you can add an additional
1091 argument in the form of an array reference which will be passed to the
1092 constructor of the lower-level Mail::Mailer. This can be used to great
1093 effect for all manner of fun and enjoyment. ;-)
1095 This is not designed to be an extensible platform upon which to build
1096 transport plugins. That functionality is planned for the next-generation
1097 release of Test::Reporter, which will reside in the CPAN::Testers namespace.
1099 =item * B<via>
1101 Optional. Gets or sets the value that will be appended to
1102 X-Reported-Via, generally this is useful for distributions that use
1103 Test::Reporter to report test results. This would be something
1104 like "CPANPLUS 0.036".
1106 =item * B<write and read>
1108 These methods are used in situations where you test on a machine that has
1109 port 25 blocked and there is no local MTA. You use write() on the machine
1110 that you are testing from, transfer the written test reports from the
1111 testing machine to the sending machine, and use read() on the machine that
1112 you actually want to submit the reports from. write() will write a file in
1113 an internal format that contains 'From', 'Subject', and the content of the
1114 report. The filename will be represented as:
1115 grade.distribution.archname.osvers.seconds_since_epoch.pid.rpt. write()
1116 uses the value of dir() if it was specified, else the cwd.
1118 On the machine you are testing from:
1120 my $reporter = Test::Reporter->new
1122 grade => 'pass',
1123 distribution => 'Test-Reporter-1.16',
1124 )->write();
1126 On the machine you are submitting from:
1128 my $reporter;
1129 $reporter = Test::Reporter->new()->read('pass.Test-Reporter-1.16.i686-linux.2.2.16.1046685296.14961.rpt')->send() || die $reporter->errstr(); # wrap in an opendir if you've a lot to submit
1131 write() also accepts an optional filehandle argument:
1133 my $fh; open $fh, '>-'; # create a STDOUT filehandle object
1134 $reporter->write($fh); # prints the report to STDOUT
1136 =back
1138 =head1 CAVEATS
1140 If you specify recipients to be cc'd while using send() (and you do not have
1141 Mail::Send installed) be sure that you use the author's @cpan.org address
1142 otherwise they may not be delivered, since the perl.org MX's are unlikely
1143 to relay for anything other than perl.org and cpan.org.
1145 =head1 COPYRIGHT
1147 Copyright (c) 2007 Adam J. Foxson. All rights reserved.
1149 =head1 LICENSE
1151 This program is free software; you may redistribute it
1152 and/or modify it under the same terms as Perl itself.
1154 =head1 SEE ALSO
1156 =over 4
1158 =item * L<perl>
1160 =item * L<Config>
1162 =item * L<Net::SMTP>
1164 =item * L<File::Spec>
1166 =item * L<File::Temp>
1168 =item * L<Net::Domain>
1170 This is optional. If it's installed Test::Reporter will try even
1171 harder at guessing your mail domain.
1173 =item * L<Net::DNS>
1175 This is optional. If it's installed Test::Reporter will dynamically
1176 retrieve the mail exchangers for perl.org, instead of relying on the
1177 MX's known at the time of this release.
1179 =item * L<Mail::Send>
1181 This is optional. If it's installed Test::Reporter will use Mail::Send
1182 instead of Net::SMTP.
1184 =back
1186 =head1 AUTHOR
1188 Adam J. Foxson E<lt>F<afoxson@pobox.com>E<gt> and
1189 Richard Soderberg E<lt>F<rsod@cpan.org>E<gt>, with much deserved credit to
1190 Kirrily "Skud" Robert E<lt>F<skud@cpan.org>E<gt>, and
1191 Kurt Starsinic E<lt>F<Kurt.Starsinic@isinet.com>E<gt> for predecessor versions
1192 (CPAN::Test::Reporter, and cpantest respectively).
1194 =cut