File rakudo-test-log.diff of Package rakudo
diff --git a/tools/templates/Makefile-backend-common.in b/tools/templates/Makefile-backend-common.in
index 57396c6df..0688cbe59 100644
--- a/tools/templates/Makefile-backend-common.in
+++ b/tools/templates/Makefile-backend-common.in
@@ -126,7 +126,7 @@
@backend_prefix@-stresstest: @backend_prefix@-stresstest@bpm(HARNESS_TYPE)@
@backend_prefix@-coretest5: @backend_prefix@-all
- @bpm(HARNESS5)@ $(COMMON_TEST_DIRS) @bsm(SPECIFIC_TEST_DIRS)@
+ PERL5LIB=lib5 @bpm(HARNESS5)@ --archive=make-test.tar $(COMMON_TEST_DIRS) @bsm(SPECIFIC_TEST_DIRS)@ || { mkdir results && cd results && tar xf ../make-test.tar && find . -type f -exec echo '{}' \; -exec cat '{}' \; ; false ; }
# Run the spectests that we know work.
@backend_prefix@-spectest5: @backend_prefix@-testable $(SPECTEST_DATA)
diff --git a/lib5/TAP/Harness/Archive.pm b/lib5/TAP/Harness/Archive.pm
new file mode 100644
index 000000000..23810ba2d
--- /dev/null
+++ b/lib5/TAP/Harness/Archive.pm
@@ -0,0 +1,450 @@
+package TAP::Harness::Archive;
+use warnings;
+use strict;
+use base 'TAP::Harness';
+use Cwd ();
+use File::Basename ();
+use File::Temp ();
+use File::Spec ();
+use File::Path ();
+use File::Find ();
+use Archive::Tar ();
+use TAP::Parser ();
+use YAML::Tiny ();
+use TAP::Parser::Aggregator ();
+
+=head1 NAME
+
+TAP::Harness::Archive - Create an archive of TAP test results
+
+=cut
+
+our $VERSION = '0.18';
+
+=head1 SYNOPSIS
+
+ use TAP::Harness::Archive;
+ my $harness = TAP::Harness::Archive->new(\%args);
+ $harness->runtests(@tests);
+
+=head1 DESCRIPTION
+
+This module is a direct subclass of L<TAP::Harness> and behaves
+in exactly the same way except for one detail. In addition to
+outputting a running progress of the tests and an ending summary
+it can also capture all of the raw TAP from the individual test
+files or streams into an archive file (C<.tar> or C<.tar.gz>).
+
+=head1 METHODS
+
+All methods are exactly the same as our base L<TAP::Harness> except
+for the following.
+
+=head2 new
+
+In addition to the options that L<TAP::Harness> allow to this method,
+we also allow the following:
+
+=over
+
+=item archive
+
+This is the name of the archive file to generate. We use L<Archive::Tar>
+in the background so we only support C<.tar> and C<.tar.gz> archive file
+formats. This can optionally be an existing directory that will have
+the TAP archive's contents deposited therein without any file archiving
+(no L<Archive::Tar> involved).
+
+=item extra_files
+
+This is an array reference to extra files that you want to include in the TAP
+archive but which are not TAP files themselves. This is useful if you want to
+include some log files that contain useful information about the test run.
+
+=item extra_properties
+
+This is a hash reference of extra properties that you've collected during your
+test run. Some things you might want to include are the Perl version, the system's
+architecture, the operating system, etc.
+
+=back
+
+=cut
+
+my (%ARCHIVE_TYPES, @ARCHIVE_EXTENSIONS);
+BEGIN {
+ %ARCHIVE_TYPES = (
+ 'tar' => 'tar',
+ 'tar.gz' => 'tar.gz',
+ 'tgz' => 'tar.gz',
+ );
+ @ARCHIVE_EXTENSIONS = map { ".$_" } keys %ARCHIVE_TYPES;
+}
+
+sub new {
+ my ($class, $args) = @_;
+ $args ||= {};
+
+ # these can't be passed on to Test::Harness
+ my $archive = delete $args->{archive};
+ my $extra_files = delete $args->{extra_files};
+ my $extra_props = delete $args->{extra_properties};
+
+ $class->_croak("You must provide the name of the archive to create!")
+ unless $archive;
+
+ my $self = $class->SUPER::new($args);
+
+ my $is_directory = -d $archive ? 1 : 0;
+ if ($is_directory) {
+ $self->{__archive_is_directory} = $is_directory;
+ $self->{__archive_tempdir} = $archive;
+ } else {
+ my $format = $class->_get_archive_format_from_filename($archive);
+
+ # if it's not a format we understand, or it's not a directory
+ $class->_croak("Archive is not a known format type!")
+ unless $format && $ARCHIVE_TYPES{$format};
+
+ $self->{__archive_file} = $archive;
+ $self->{__archive_format} = $format;
+ $self->{__archive_tempdir} = File::Temp::tempdir();
+ }
+
+ # handle any extra files
+ if($extra_files) {
+ ref $extra_files eq 'ARRAY'
+ or $class->_croak("extra_files must be an array reference!");
+ foreach my $file (@$extra_files) {
+ $class->_croak("extra_file $file does not exist!") unless -e $file;
+ $class->_croak("extra_file $file is not readable!") unless -r $file;
+ }
+ $self->{__archive_extra_files} = $extra_files;
+ }
+
+ if($extra_props) {
+ ref $extra_props eq 'HASH'
+ or $class->_croak("extra_properties must be a hash reference!");
+ $self->{__archive_extra_props} = $extra_props;
+ }
+
+ return $self;
+}
+
+sub _get_archive_format_from_filename {
+ my ($self, $filename) = @_;
+
+ # try to guess it if we don't have one
+ my (undef, undef, $suffix) = File::Basename::fileparse($filename, @ARCHIVE_EXTENSIONS);
+ $suffix =~ s/^\.//;
+ return $ARCHIVE_TYPES{$suffix};
+}
+
+=head2 runtests
+
+Takes the same arguments as L<TAP::Harness>'s version and returns the
+same thing (a L<TAP::Parser::Aggregator> object). The only difference
+is that in addition to the normal test running and progress output
+we also create the TAP Archive when it's all done.
+
+=cut
+
+sub runtests {
+ my ($self, @files) = @_;
+
+ # tell TAP::Harness to put the raw tap someplace we can find it later
+ my $dir = $self->{__archive_tempdir};
+ $ENV{PERL_TEST_HARNESS_DUMP_TAP} = $dir;
+
+ # get some meta information about this run
+ my %meta = (
+ file_order => \@files,
+ start_time => time(),
+ );
+
+ my $aggregator = $self->SUPER::runtests(@files);
+
+ $meta{stop_time} = time();
+
+ my @parsers = $aggregator->parsers;
+ for ( my $i = 0; $i < @parsers; $i++ ) {
+ $parsers[ $i ] = {
+ start_time => $parsers[ $i ]->start_time,
+ end_time => $parsers[ $i ]->end_time,
+ description => $files[ $i ],
+ };
+ }
+ $meta{file_attributes} = \@parsers;
+
+ my $cwd = Cwd::getcwd();
+ my $is_dir = $self->{__archive_is_directory};
+ my ($archive, $output_file);
+ if( $is_dir ) {
+ $output_file = $self->{__archive_tempdir};
+ } else {
+ $output_file = $self->{__archive_file};
+
+ # go into the dir so that we can reference files
+ # relatively and put them in the archive that way
+ chdir($dir) or $self->_croak("Could not change to directory $dir: $!");
+
+ unless (File::Spec->file_name_is_absolute($output_file)) {
+ $output_file = File::Spec->catfile($cwd, $output_file);
+ }
+
+ # create the archive
+ $archive = Archive::Tar->new();
+ $archive->add_files($self->_get_all_tap_files);
+ chdir($cwd) or $self->_croak("Could not return to directory $cwd: $!");
+ }
+
+ # add in any extra files
+ if(my $x_files = $self->{__archive_extra_files}) {
+ my @rel_x_files;
+ foreach my $x_file (@$x_files) {
+ # handle both relative and absolute file names
+ my $rel_file;
+ if( File::Spec->file_name_is_absolute($x_file) ) {
+ $rel_file = File::Spec->abs2rel($x_file, $cwd);
+ } else {
+ $rel_file = $x_file;
+ }
+ push(@rel_x_files, $rel_file);
+ }
+ $archive->add_files(@rel_x_files) unless $is_dir;
+ $meta{extra_files} = \@rel_x_files;
+ }
+
+ # add any extra_properties to the meta
+ if(my $extra_props = $self->{__archive_extra_props}) {
+ $meta{extra_properties} = $extra_props;
+ }
+
+ # create the YAML meta file
+ my $yaml = YAML::Tiny->new();
+ $yaml->[0] = \%meta;
+ if( $is_dir ) {
+ my $meta_file = File::Spec->catfile($output_file, 'meta.yml');
+ open(my $out, '>', $meta_file) or die "Could not create meta.yml: $!";
+ print $out $yaml->write_string;
+ close($out);
+ } else {
+ $archive->add_data('meta.yml', $yaml->write_string);
+ $archive->write($output_file, $self->{__archive_format} eq 'tar.gz') or die $archive->error;
+ # be nice and clean up
+ File::Path::rmtree($dir);
+ }
+
+ print "\nTAP Archive created at $output_file\n" unless $self->verbosity < -1;
+
+ return $aggregator;
+}
+
+sub _get_all_tap_files {
+ my ($self, $dir, $meta) = @_;
+ $dir ||= $self->{__archive_tempdir};
+ my @files;
+ my %x_files;
+ if($meta && $meta->{extra_files}) {
+ %x_files = map { $_ => 1 } @{$meta->{extra_files}};
+ }
+
+ File::Find::find(
+ {
+ no_chdir => 1,
+ wanted => sub {
+ return if /^\./;
+ return if -d;
+ my $rel_name = File::Spec->abs2rel($_, $dir);
+ return if $rel_name eq 'meta.yml';
+ push(@files, $rel_name) unless $x_files{$rel_name};
+ },
+ },
+ $dir
+ );
+ return @files;
+}
+
+=head2 aggregator_from_archive
+
+This class method will return a L<TAP::Parser::Aggregator> object
+when given a TAP Archive to open and parse. It's pretty much the reverse
+of creating a TAP Archive from using C<new> and C<runtests>.
+
+It takes a hash of arguments which are as follows:
+
+=over
+
+=item archive
+
+The path to the archive file. This can also be a directory if you created
+the archive as a directory. This is required.
+
+=item parser_callbacks
+
+This is a hash ref containing callbacks for the L<TAP::Parser> objects
+that are created while parsing the TAP files. See the L<TAP::Parser>
+documentation for details about these callbacks.
+
+=item made_parser_callback
+
+This callback is executed every time a new L<TAP::Parser> object
+is created. It will be passed the new parser object, the name
+of the file to be parsed, and also the full (temporary) path of that file.
+
+=item meta_yaml_callback
+
+This is a subroutine that will be called if we find and parse a YAML
+file containing meta information about the test run in the archive.
+The structure of the YAML file will be passed in as an argument.
+
+=back
+
+ my $aggregator = TAP::Harness::Archive->aggregator_from_archive(
+ {
+ archive => 'my_tests.tar.gz',
+ parser_callbacks => {
+ plan => sub { warn "Nice to see you plan ahead..." },
+ unknown => sub { warn "Your TAP is bad!" },
+ },
+ made_parser_callback => sub {
+ my ($parser, $file, $full_path) = @_;
+ warn "$file is temporarily located at $full_path\n";
+ }
+
+ }
+ );
+
+=cut
+
+sub aggregator_from_archive {
+ my ($class, $args) = @_;
+ my $meta;
+
+ my $file = Cwd::abs_path( $args->{archive} )
+ or $class->_croak("You must provide the path to the archive!");
+
+ my $is_directory = -d $file;
+
+ # extract the files out into a temporary directory
+ my $dir = $is_directory ? $file : File::Temp::tempdir();
+ my $cwd = Cwd::getcwd();
+ chdir($dir) or $class->_croak("Could not change to directory $dir: $!");
+ my @files;
+
+ Archive::Tar->new()->extract_archive($file) unless $is_directory;
+ my @tap_files;
+
+ # do we have a meta.yml file in the archive?
+ my $yaml_file = File::Spec->catfile($dir, 'meta.yml');
+ if( -e $yaml_file) {
+
+ # parse it into a structure
+ if ($YAML::Tiny::VERSION < 1.57) {
+ $meta = YAML::Tiny->new()->read($yaml_file);
+ die "Could not read YAML $yaml_file: " . YAML::Tiny->errstr if YAML::Tiny->errstr;
+ } else {
+ $meta = eval {
+ YAML::Tiny->new()->read($yaml_file);
+ };
+ if ($@) {
+ die "Could not read YAML $yaml_file: ".$@;
+ }
+ }
+
+ if($args->{meta_yaml_callback}) {
+ $args->{meta_yaml_callback}->($meta);
+ }
+ $meta = $meta->[0];
+
+ if($meta->{file_order} && ref $meta->{file_order} eq 'ARRAY') {
+ foreach my $file (@{$meta->{file_order}}) {
+ push(@tap_files, $file) if -e $file;
+ }
+ }
+ }
+
+ # if we didn't get the files from the YAML file, just find them all
+ unless(@tap_files) {
+ @tap_files = $class->_get_all_tap_files($dir, $meta);
+ }
+
+ # now create the aggregator
+ my $aggregator = TAP::Parser::Aggregator->new();
+ foreach my $tap_file (@tap_files) {
+ open(my $fh, $tap_file) or die "Could not open $tap_file for reading: $!";
+ my $parser = TAP::Parser->new({source => $fh, callbacks => $args->{parser_callbacks}});
+ if($args->{made_parser_callback}) {
+ $args->{made_parser_callback}->($parser, $tap_file, File::Spec->catfile($dir, $tap_file));
+ }
+ $parser->run;
+ $aggregator->add($tap_file, $parser);
+ }
+
+ # be nice and clean up
+ chdir($cwd) or $class->_croak("Could not return to directory $cwd: $!");
+ File::Path::rmtree($dir) unless $is_directory;
+
+ return $aggregator;
+}
+
+=head1 AUTHOR
+
+Michael Peters, C<< <mpeters at plusthree.com> >>
+
+=head1 BUGS
+
+Please report any bugs or feature requests to
+C<bug-tap-harness-archive at rt.cpan.org>, or through the web interface at
+L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=TAP-Harness-Archive>.
+I will be notified, and then you'll automatically be notified of progress on
+your bug as I make changes.
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+ perldoc TAP::Harness::Archive
+
+You can also look for information at:
+
+=over 4
+
+=item * AnnoCPAN: Annotated CPAN documentation
+
+L<http://annocpan.org/dist/TAP-Harness-Archive>
+
+=item * CPAN Ratings
+
+L<http://cpanratings.perl.org/d/TAP-Harness-Archive>
+
+=item * RT: CPAN's request tracker
+
+L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=TAP-Harness-Archive>
+
+=item * Search CPAN
+
+L<http://search.cpan.org/dist/TAP-Harness-Archive>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+=over
+
+=item * A big thanks to Plus Three, LP (L<http://www.plusthree.com>) for sponsoring my work on this module and other open source pursuits.
+
+=item * Andy Armstrong
+
+=back
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright 2007 Michael Peters, all rights reserved.
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=cut
+
+1; # End of TAP::Harness::Archive