# $Id: /mirror/trunk/lib/Parrot/Test/Perl6.pm 21463 2007-09-21T16:51:42.601126Z paultcochrane  $

# Copyright (C) 2006-2007, The Perl Foundation.

=head1 NAME

Parrot::Test::Perl6 -- testing routines for languages/perl6

=head1 SYNOPSIS

    use Parrot::Test::Perl6 tests => 3;

    perl6_output_is(<<'CODE', <<'OUTPUT', 'hello, world!');
    say 'hello, world!';
    CODE
    hello, world!
    OUTPUT

    perl6_stderr_like($code, qr/$expected/, $desc);

    perl6_stdout_isnt($code, qr/$expected/, $desc);

=head1 DESCRIPTION

This module provides Perl6 test functions. It has been heavily refactored
from Parrot::Test. Hopefully,similar refactoring will be carried out in
Parrot::Test someday soon.

=cut

package Parrot::Test::Perl6;

use strict;
use warnings;

use File::Basename;
use File::Spec;
use Parrot::Config;

require Exporter;
require Parrot::Test;
require Test::Builder;
require Test::More;

our @EXPORT = qw( plan skip );

my $lang    = 'perl6';
my $streams = {
    output => sub { return ( STDOUT => $_[0],               STDERR => $_[0] ) },
    stdout => sub { return ( STDOUT => $_[0],               STDERR => File::Spec->devnull ) },
    stderr => sub { return ( STDOUT => File::Spec->devnull, STDERR => $_[0] ) },
};
my $tests = {
    is   => 'is_eq',
    like => 'like',
    isnt => 'isnt_eq',
};

## create a map of test names and info
my $test_map = {};
for my $t ( keys %{$tests} ) {
    for my $s ( keys %$streams ) {
        $test_map->{ join( '_' => $lang, $s, $t ) } = {
            lang   => $lang,
            stream => { name => $s, data => $streams->{$s}, },
            test   => { name => $t, data => $tests->{$t}, },
        };
    }
}

push @EXPORT => keys %{$test_map};

use base qw( Parrot::Test Exporter );

my $b = Test::Builder->new();

my $path_to_parrot = $INC{"Parrot/Config.pm"};
$path_to_parrot =~ s:lib/Parrot/Config.pm$::;
$path_to_parrot = File::Spec->curdir()
    if $path_to_parrot eq '';

my $parrot = File::Spec->catfile( $path_to_parrot, 'parrot' . $PConfig{exe} );

my $perl6 = File::Spec->catfile( $path_to_parrot, qw/ languages perl6 p6shell.pir /, );

sub import {
    my ( $class, $plan, @args ) = @_;
    $b->plan( $plan, @args );
    __PACKAGE__->export_to_level( 2, __PACKAGE__ );
}

sub set_test_info {
    my $next_test_num = 1 + $b->current_test();
    my $f_out         = Parrot::Test::per_test( '.out', $next_test_num );
    my $f_code        = Parrot::Test::per_test( '.p6', $next_test_num );
    $f_code = File::Spec->rel2abs($f_code);

    return ( $next_test_num, $f_out, $f_code );
}

sub _generate_functions {
    my ($package) = @_;

    for my $func ( keys %$test_map ) {
        no strict 'refs';

        *{ $package . '::' . $func } = sub ($$;$%) {
            my ( $code, $expected, $desc, %extra ) = @_;

            Parrot::Test::convert_line_endings($code);

            ## set a default description
            $desc = sprintf '(%s line %s)' => ( caller() )[ 1, 2 ]
                unless $desc;

            my ( $test_num, $f_out, $f_code ) = set_test_info();

            ## get test arguments from environment
            my $args = $ENV{TEST_PROG_ARGS} || '';

            Parrot::Test::write_code_to_file( $code, $f_code );

            ## build the command and set the run options
            my $cmd         = qq{$parrot $args $perl6 "$f_code"};
            my $run_options = {
                CD => '.',
                $test_map->{$func}->{stream}->{data}->($f_out),
            };

            ## run the command and capture the exit code
            my $exit_code = Parrot::Test::run_command( $cmd, %{$run_options}, );

            ## read in the command output
            my $actual_output = Parrot::Test::slurp_file($f_out);

            ## tell Test::Builder if the test is marked as a todo-item
            my $call_pkg = $b->exported_to() || '';
            local *{ $call_pkg . '::TODO' } = ## no critic Variables::ProhibitConditionalDeclarations
                \$extra{todo}
                if defined $extra{todo};

            ## run Test::Builder's test on actual vs. expected output
            my $method = $test_map->{$func}->{test}->{data};
            my $pass = $b->$method( $actual_output, $expected, $desc );

            ## print diagnostic info if the test fails
            $b->diag("'$cmd' failed with exit code $exit_code")
                if $exit_code and not $pass;

            unless ( $ENV{POSTMORTEM} ) {
                unlink $f_out;
            }

            return $pass;
        };
    }
}

__PACKAGE__->_generate_functions();

$_ ^= ~{ AUTHOR => 'particle' };

# Local Variables:
#   mode: cperl
#   cperl-indent-level: 4
#   fill-column: 100
# End:
# vim: expandtab shiftwidth=4:
