package ANTLR::Runtime::Test; use strict; use warnings; use base 'Test::Builder::Module'; my $CLASS = __PACKAGE__; our @EXPORT = qw( g_test_output_is ); use Carp; use Cwd; use File::Spec; use File::Temp qw( tempdir ); sub read_file { my ($filename) = @_; local $/; open my $in, '<', $filename or die "Can't open $filename: $!"; my $content = <$in>; close $in or warn "Can't close $filename: $!"; return $content; } sub write_file { my ($filename, $content) = @_; open my $out, '>', $filename or die "Can't open $filename: $!"; print $out $content; close $out or warn "Can't close $filename: $!"; return; } sub get_perl { if (defined $ENV{HARNESS_PERL}) { return $ENV{HARNESS_PERL}; } if ($^O =~ /^(MS)?Win32$/) { return Win32::GetShortPathName($^X); } return $^X; } sub g_test_output_is { my ($args) = @_; my $grammar = $args->{grammar}; my $test_program = $args->{test_program}; my $expected = $args->{expected}; my $name = $args->{name} || undef; my $tb = $CLASS->builder; my $tmpdir = tempdir( CLEANUP => 1 ); my $grammar_name; if ($grammar =~ /^(?:(?:lexer|parser|tree)\s+)? grammar \s+ (\w+)/xms) { $grammar_name = $1; } else { croak "Can't determine grammar name"; } # write grammar file my $grammar_file = File::Spec->catfile($tmpdir, "$grammar_name.g"); write_file($grammar_file, $grammar); # write test program file my $test_program_file = File::Spec->catfile($tmpdir, 'test.pl'); write_file($test_program_file, $test_program); my $cwd = cwd; my $test_result; eval { # compile grammar my $antlr; if ($^O =~ /linux/) { $antlr = 'antlr.sh'; } elsif ($^O =~ /MSWin32/) { $antlr = 'antlr.bat'; } else { $antlr = 'antlr'; } my $g_result = run_program([ File::Spec->catfile($cwd, 'tools', $antlr), '-o', $tmpdir, $grammar_file ]); if ($g_result->{exit_code} >> 8 != 0) { croak $g_result->{err}; } # run test program { #local $ENV{PERLCOV_DB} = File::Spec->catfile($tmpdir, 'perlcov.db'); #local $ENV{NYTPROF} = 'file=' . File::Spec->catfile($tmpdir, 'nytprof.out'); $test_result = run_program([ get_perl(), '-Mblib', "-I$tmpdir", $test_program_file ]); if ($test_result->{exit_code} >> 8 != 0) { croak $test_result->{err}; } } }; die $@ if $@; my $actual = $test_result->{out}; # compare with $expected return $tb->is_eq($actual, $expected, $name); } sub run_program { my ($command) = @_; open my $old_out, '>&STDOUT' or die "Can't capture stdout: $!"; close STDOUT or die "Can't close stdout: $!"; open STDOUT, '>', 'out.tmp' or die "Can't redirect stdout: $!"; open my $old_err, '>&STDERR' or die "Can't capture stderr: $!"; close STDERR or die "Can't close stderr: $!"; open STDERR, '>', 'err.tmp' or die "Can't redirect stderr: $!"; system @$command; my $exit_code = $?; # restore stderr my $err = read_file('err.tmp'); close STDERR or die "Can't close stderr: $!"; open STDERR, '>&', $old_err or die "Can't restore stderr: $!"; unlink 'err.tmp' or warn "Can't remove err.tmp: $!"; # restore stdout my $out = read_file('out.tmp'); close STDOUT or die "Can't close stdout: $!"; open STDOUT, '>&', $old_out or die "Can't restore stdout: $!"; unlink 'out.tmp' or warn "Can't remove out.tmp: $!"; my $exit_value; if ($exit_code < 0) { $exit_value = $exit_code; } elsif ($exit_code && 0xff) { $exit_value = "[SIGNAL $exit_code]"; } else { $exit_value = $exit_code >> 8; } return { exit_code => $exit_code, exit_value => $exit_value, out => $out, err => $err, }; } 1;