1 # Copyright (C) 2004-2007, The Perl Foundation.
6 Parrot::Test - testing routines for Parrot and language implementations
10 Set the number of tests to be run like this:
12 use Parrot::Test tests => 8;
14 Write individual tests like this:
16 pasm_output_is(<<'CODE', <<'OUTPUT', "description of test");
25 This module provides various Parrot-specific test functions.
29 The parameter C<$language> is the language of the code.
30 The parameter C<$code> is the code that should be executed or transformed.
31 The parameter C<$expected> is the expected result.
32 The parameter C<$unexpected> is the unexpected result.
33 The parameter C<$description> should describe the test.
35 Any optional parameters can follow. For example, to mark a test as a TODO test
36 (where you know the implementation does not yet work), pass:
38 todo => 'reason to consider this TODO'
40 at the end of the argument list. Valid reasons include C<bug>,
41 C<unimplemented>, and so on.
43 B<Note:> you I<must> use a C<$description> with TODO tests.
47 =item C<language_output_is( $language, $code, $expected, $description)>
49 =item C<language_error_output_is( $language, $code, $expected, $description)>
51 Runs a language test and passes the test if a string comparison
52 of the output with the expected result it true.
53 For C<language_error_output_is()> the exit code also has to be non-zero.
55 =item C<language_output_like( $language, $code, $expected, $description)>
57 =item C<language_error_output_like( $language, $code, $expected, $description)>
59 Runs a language test and passes the test if the output matches the expected
61 For C<language_error_output_like()> the exit code also has to be non-zero.
63 =item C<language_output_isnt( $language, $code, $expected, $description)>
65 =item C<language_error_output_isnt( $language, $code, $expected, $description)>
67 Runs a language test and passes the test if a string comparison
68 if a string comparison of the output with the unexpected result is false.
69 For C<language_error_output_isnt()> the exit code also has to be non-zero.
71 =item C<pasm_output_is($code, $expected, $description)>
73 Runs the Parrot Assembler code and passes the test if a string comparison of
74 the output with the expected result it true.
76 =item C<pasm_error_output_is($code, $expected, $description)>
78 Runs the Parrot Assembler code and passes the test if a string comparison of
79 the output with the expected result it true I<and> if Parrot exits with a
82 =item C<pasm_output_like($code, $expected, $description)>
84 Runs the Parrot Assembler code and passes the test if the output matches
87 =item C<pasm_error_output_like($code, $expected, $description)>
89 Runs the Parrot Assembler code and passes the test if the output matches
90 C<$expected> I<and> if Parrot exits with a non-zero exit code.
92 =item C<pasm_output_isnt($code, $unexpected, $description)>
94 Runs the Parrot Assembler code and passes the test if a string comparison of
95 the output with the unexpected result is false.
97 =item C<pasm_error_output_isnt($code, $unexpected, $description)>
99 Runs the Parrot Assembler code and passes the test if a string comparison of
100 the output with the unexpected result is false I<and> if Parrot exits with a
103 =item C<pir_output_is($code, $expected, $description)>
105 Runs the PIR code and passes the test if a string comparison of output with the
106 expected result is true.
108 =item C<pir_error_output_is($code, $expected, $description)>
110 Runs the PIR code and passes the test if a string comparison of output with the
111 expected result is true I<and> if Parrot exits with a non-zero exit code.
113 =item C<pir_output_like($code, $expected, $description)>
115 Runs the PIR code and passes the test if output matches the expected result.
117 =item C<pir_error_output_like($code, $expected, $description)>
119 Runs the PIR code and passes the test if output matches the expected result
120 I<and> if Parrot exits with a non-zero exit code.
122 =item C<pir_output_isnt($code, $unexpected, $description)>
124 Runs the PIR code and passes the test if a string comparison of the output with
125 the unexpected result is false.
127 =item C<pir_error_output_isnt($code, $unexpected, $description)>
129 Runs the PIR code and passes the test if a string comparison of the output with
130 the unexpected result is false I<and> if Parrot exits with a non-zero exit
133 =item C<pbc_output_is($code, $expected, $description)>
135 Runs the Parrot Bytecode and passes the test if a string comparison of output
136 with the expected result is true.
138 =item C<pbc_error_output_is($code, $expected, $description)>
140 Runs the Parrot Bytecode and passes the test if a string comparison of the output
141 with the expected result is true I<and> if Parrot exits with a non-zero exit code.
143 =item C<pbc_output_like($code, $expected, $description)>
145 Runs the Parrot Bytecode and passes the test if output matches the expected
148 =item C<pbc_error_output_like($code, $expected, $description)>
150 Runs the Parrot Bytecode and passes the test if output matches the expected
151 result I<and> if Parrot exits with a non-zero exit code.
153 =item C<pbc_output_isnt($code, $unexpected, $description)>
155 Runs the Parrot Bytecode and passes the test if a string comparison of output
156 with the unexpected result is false.
158 =item C<pbc_error_output_isnt($code, $unexpected, $description)>
160 Runs the Parrot Bytecode and passes the test if a string comparison of output
161 with the unexpected result is false I<and> if Parrot exits with a non-zero exit
164 =item C<pir_2_pasm_is($code, $expected, $description)>
166 Compile the Parrot Intermediate Representation and generate Parrot Assembler Code.
167 Pass if the generated PASM is $expected.
169 =item C<pir_2_pasm_like($code, $expected, $description)>
171 Compile the Parrot Intermediate Representation and generate Parrot Assembler Code.
172 Pass if the generated PASM matches $expected.
174 =item C<pir_2_pasm_isnt($code, $unexpected, $description)>
176 Compile the Parrot Intermediate Representation and generate Parrot Assembler
177 Code. Pass unless the generated PASM is $expected.
179 =item C<c_output_is($code, $expected, $description, %options)>
181 Compiles and runs the C code, passing the test if a string comparison of output
182 with the expected result it true. Valid options are 'todo' => 'reason' to mark
185 =item C<c_output_like($code, $expected, $description, %options)>
187 Compiles and runs the C code, passing the test if output matches the expected
188 result. Valid options are 'todo' => 'reason' to mark a TODO test.
190 =item C<c_output_isnt($code, $unexpected, $description, %options)>
192 Compiles and runs the C code, passing the test if a string comparison of output
193 with the unexpected result is false. Valid options are 'todo' => 'reason' to
196 =item C<example_output_is( $example_f, $expected, @todo )>
198 =item C<example_output_like( $example_f, $expected, @todo )>
200 =item C<example_output_isnt( $example_f, $expected, @todo )>
202 Determines the language, PIR or PASM, from the extension of C<$example_f> and runs
203 the appropriate C<^language_output_(is|kike|isnt)> sub.
204 C<$example_f> is used as a description, so don't pass one.
206 =item C<skip($why, $how_many)>
208 Use within a C<SKIP: { ... }> block to indicate why and how many tests to skip,
209 just like in Test::More.
211 =item C<run_command($command, %options)>
213 Run the given $command in a cross-platform manner.
217 STDOUT filehandle to redirect STDOUT to
218 STDERR filehandle to redirect STDERR to
219 CD directory to run the command in
223 # equivalent to "cd some_dir && make test"
224 run_command("make test", CD => "some_dir");
226 =item C<slurp_file($file_name)>
228 Read the whole file $file_name and return the content as a string.
230 =item C<convert_line_endings($text)>
232 Convert Win32 style line endins with Unix style line endings.
234 =item C<path_to_parrot()>
236 Construct a relative path from the current dir to the parrot root dir.
238 =item C<per_test( $ext, $test_no )>
240 Construct a path for a temporary files.
241 Takes C<$0> into account.
243 =item C<pasm_fp_equality_macros()>
245 Returns a PASM macro that can be used to test fp equality/inequality.
247 =item C<write_code_to_file($code, $code_f)>
249 Writes C<$code> into the file C<$code_f>.
251 =item C<generate_languages_functions>
253 Generate functions that are only used by a couple of
254 Parrot::Test::<lang> modules.
256 This implementation is experimental and currently only works
263 package Parrot
::Test
;
276 require Test
::Builder
;
279 our @EXPORT = qw( plan run_command skip slurp_file pasm_fp_equality_macro );
281 use base
qw( Exporter );
283 # Memoize functions with a fixed output
284 Memoize
::memoize
('path_to_parrot');
286 # Tell parrot it's being tested--disables searching of installed libraries.
287 # (see Parrot_get_runtime_prefix in src/library.c).
288 $ENV{PARROT_TEST
} = 1 unless defined $ENV{PARROT_TEST
};
290 my $builder = Test
::Builder
->new();
292 # Generate subs where the name serves as an
294 _generate_test_functions
();
297 my ( $class, $plan, @args ) = @_;
299 $builder->plan( $plan, @args );
301 __PACKAGE__
->export_to_level( 2, __PACKAGE__
);
304 # this kludge is an hopefully portable way of having
305 # redirections ( tested on Linux and Win2k )
306 # An alternative is using Test::Output
308 my ( $command, %options ) = @_;
310 # To run the command in a different directory.
311 my $chdir = delete $options{CD
};
313 while ( my ( $key, $value ) = each %options ) {
314 $key =~ m/^STD(OUT|ERR)$/
315 or die "I don't know how to redirect '$key' yet!";
316 $value = File
::Spec
->devnull()
317 if $value eq '/dev/null';
320 my $out = $options{'STDOUT'} || '';
321 my $err = $options{'STDERR'} || '';
323 if ( $out and $err and $out eq $err ) {
327 local *OLDOUT
if $out; ## no critic Variables::ProhibitConditionalDeclarations
328 local *OLDERR
if $err; ## no critic Variables::ProhibitConditionalDeclarations
330 # Save the old filehandles; we must not let them get closed.
331 open OLDOUT
, '>&STDOUT' ## no critic InputOutput::ProhibitBarewordFileHandles
332 or die "Can't save stdout"
334 open OLDERR
, '>&STDERR' ## no critic InputOutput::ProhibitBarewordFileHandles
335 or die "Can't save stderr"
338 open STDOUT
, '>', $out or die "Can't redirect stdout to $out" if $out;
340 # See 'Obscure Open Tricks' in perlopentut
341 open STDERR
, ">$err" ## no critic InputOutput::ProhibitTwoArgOpen
342 or die "Can't redirect stderr to $err"
345 # If $command isn't already an arrayref (because of a multi-command
346 # test), make it so now so the code below can treat everybody the
348 $command = [$command] unless ( ref $command );
350 if ( defined $ENV{VALGRIND
} ) {
351 $_ = "$ENV{VALGRIND} $_" for (@
$command);
360 # Execute all commands
361 # [#42161] [BUG] Parrot::Test throws "Can't spawn" warning on windows
362 # ...if a system call returns a negative value
363 # removed exec warnings to prevent this warning from messing up test results
366 system($_) for ( @
{$command} );
375 close STDOUT
or die "Can't close stdout" if $out;
376 close STDERR
or die "Can't close stderr" if $err;
378 open STDOUT
, ">&", \
*OLDOUT
or die "Can't restore stdout" if $out;
379 open STDERR
, ">&", \
*OLDERR
or die "Can't restore stderr" if $err;
382 ( $exit_code < 0 ) ?
$exit_code
383 : ( $exit_code & 0xFF ) ?
"[SIGNAL $exit_code]"
389 my ( $ext, $test_no ) = @_;
391 return unless defined $ext and defined $test_no;
393 my $t = $0; # $0 is name of the test script
394 $t =~ s/\.t$/_$test_no$ext/;
399 sub pasm_fp_equality_macro
{
400 my $fp_equality_macro = <<'ENDOFMACRO';
401 .macro fp_eq ( J, K, L )
406 gt N12, 0.000001, .$FPEQNOK
416 lt N12, 0.000001, .$FPNENOK
422 return $fp_equality_macro;
426 sub write_code_to_file
{
427 my ( $code, $code_f ) = @_;
429 open my $CODE, '>', $code_f or die "Unable to open '$code_f'";
437 # We can inherit from Test::More, so we do it.
438 *plan
= \
&Test
::More
::plan
;
439 *skip
= \
&Test
::More
::skip
;
441 # What about File::Slurp?
443 my ($file_name) = @_;
445 open( my $SLURP, '<', $file_name ) or die "open '$file_name': $!";
447 my $file = <$SLURP> . '';
448 $file =~ s/\cM\cJ/\n/g;
454 sub convert_line_endings
{
457 $text =~ s/\cM\cJ/\n/g;
464 my $path = $INC{'Parrot/Config.pm'};
465 $path =~ s{ /lib/Parrot/Config.pm \z}{}xms;
467 ? File
::Spec
->curdir()
471 sub generate_languages_functions
{
474 output_is
=> 'is_eq',
475 error_output_is
=> 'is_eq',
476 output_like
=> 'like',
477 error_output_like
=> 'like',
478 output_isnt
=> 'isnt_eq',
479 error_output_isnt
=> 'isnt_eq',
482 foreach my $func ( keys %test_map ) {
485 local *__ANON__
= $func;
487 my ( $code, $expected, $desc, %options ) = @_;
489 # set a todo-item for Test::Builder to find
490 my $call_pkg = $self->{builder
}->exported_to() || '';
494 local *{ $call_pkg . '::TODO' } = ## no critic Variables::ProhibitConditionalDeclarations
496 if defined $options{todo
};
498 my $count = $self->{builder
}->current_test() + 1;
500 # These are the thing that depend on the actual language implementation
501 my $out_f = $self->get_out_fn( $count, \
%options );
502 my $lang_f = $self->get_lang_fn( $count, \
%options );
503 my $cd = $self->get_cd( \
%options );
504 my @test_prog = $self->get_test_prog( $count, \
%options );
506 Parrot
::Test
::write_code_to_file
( $code, $lang_f );
508 # set a todo-item for Test::Builder to find
509 my $skip_why = $self->skip_why( \
%options );
511 $self->{builder
}->skip($skip_why);
515 # STDERR is written into same output file
516 my $exit_code = Parrot
::Test
::run_command
(
522 my $real_output = slurp_file
($out_f);
524 if ( $func =~ m/^ error_/xms ) {
525 return _handle_error_output
( $self->{builder
}, $real_output, $expected, $desc )
529 $self->{builder
}->ok( 0, $desc );
531 my $test_prog = join ' && ', @test_prog;
532 $self->{builder
}->diag("'$test_prog' failed with exit code $exit_code.");
537 my $meth = $test_map{$func};
538 $self->{builder
}->$meth( $real_output, $expected, $desc );
541 # The generated files are left in the t/* directories.
542 # Let 'make clean' and 'svn:ignore' take care of them.
547 my ($package) = caller();
551 *{ $package . '::' . $func } = $test_sub;
555 # The following methods are private.
556 # They should not be used by modules inheriting from Parrot::Test.
558 sub _handle_error_output
{
559 my ( $builder, $real_output, $expected, $desc ) = @_;
561 my $level = $builder->level();
562 $builder->level( $level + 1 );
563 $builder->ok( 0, $desc );
565 "Expected error but exited cleanly\n" . "Received:\n$real_output\nExpected:\n$expected\n" );
566 $builder->level($level);
572 local $SIG{__WARN__
} = \
&_report_odd_hash
;
573 my ( $func, $code, $expected, $desc, %extra ) = @_;
575 my $path_to_parrot = path_to_parrot
();
576 my $parrot = File
::Spec
->join( File
::Spec
->curdir(), 'parrot' . $PConfig{exe
} );
578 # Strange Win line endings
579 convert_line_endings
($expected);
581 # set up default description
583 ( undef, my $file, my $line ) = caller();
584 $desc = "($file line $line)";
587 # $test_no will be part of temporary file
588 my $test_no = $builder->current_test() + 1;
590 # Name of the file where output is written.
591 # Switch to a different extension when we are generating code.
592 my $out_f = per_test
( '.out', $test_no );
594 # Name of the file with test code.
595 # This depends on which kind of code we are testing.
597 if ( $func =~ m/^pir_.*?output/ ) {
598 $code_f = per_test
( '.pir', $test_no );
600 elsif ( $func =~ m/^pasm_.*?output_/ ) {
601 $code_f = per_test
( '.pasm', $test_no );
603 elsif ( $func =~ m/^pbc_.*?output_/ ) {
604 $code_f = per_test
( '.pbc', $test_no );
607 die "Unknown test function: $func";
609 $code_f = File
::Spec
->rel2abs($code_f);
610 my $code_basef = basename
($code_f);
612 # native tests are just run, others need to write code first
613 if ( $code_f !~ /\.pbc$/ ) {
614 write_code_to_file
( $code, $code_f );
617 # honor opt* filename to actually run code with -Ox
618 my $args = $ENV{TEST_PROG_ARGS
} || '';
619 my $opt = $code_basef =~ m!opt(.)! ?
"-O$1" : "";
623 if ( $args =~ s/--run-exec// ) {
625 my $pbc_f = per_test
( '.pbc', $test_no );
626 my $o_f = per_test
( '_pbcexe' . $PConfig{o
}, $test_no );
628 per_test
( '_pbcexe' . $PConfig{exe
}, $test_no )
629 ; # Make cleanup and svn:ignore more simple
630 my $exec_f = per_test
( '_pbcexe', $test_no ); # Make cleanup and svn:ignore more simple
631 $exe_f =~ s@
[\\/:]@
$PConfig{slash
}@g;
633 # RT#43751 put this into sub generate_pbc()
635 qq{$parrot $args -o
$pbc_f "$code_f"},
636 CD
=> $path_to_parrot,
642 qq{$parrot $args -o
$o_f "$pbc_f"},
643 CD
=> $path_to_parrot,
649 qq{$PConfig{make
} EXEC
=$exec_f exec},
650 CD
=> $path_to_parrot,
657 CD
=> $path_to_parrot,
666 my ( $exit_code, $cmd );
668 if ( $args =~ s/--run-pbc// || $args =~ s/-r // ) {
669 my $pbc_f = per_test
( '.pbc', $test_no );
670 $args = qq{$args -o
"$pbc_f"};
672 # In this case, we need to execute more than one command. Instead
673 # of a single scalar, build an array of commands.
674 $cmd = [ qq{$parrot $args "$code_f"}, qq{$parrot "$pbc_f"}, ];
677 $cmd = qq{$parrot $args "$code_f"};
679 $exit_code = run_command
(
681 CD
=> $path_to_parrot,
687 return ( $out_f, $cmd, $exit_code );
690 sub _report_odd_hash
{
692 if ( $warning =~ m/Odd number of elements in hash assignment/ ) {
694 my @args = DB
::uplevel_args
();
696 my $func = ( caller() )[2];
698 Carp
::carp
("Odd $func invocation; probably missing description for TODO test");
705 sub _generate_test_functions
{
707 my $package = 'Parrot::Test';
708 my $path_to_parrot = path_to_parrot
();
709 my $parrot = File
::Spec
->join( File
::Spec
->curdir(), 'parrot' . $PConfig{exe
} );
711 my %parrot_test_map = map {
712 $_ . '_output_is' => 'is_eq',
713 $_ . '_error_output_is' => 'is_eq',
714 $_ . '_output_isnt' => 'isnt_eq',
715 $_ . '_error_output_isnt' => 'isnt_eq',
716 $_ . '_output_like' => 'like',
717 $_ . '_error_output_like' => 'like',
718 $_ . '_output_unlike' => 'unlike',
719 $_ . '_error_output_unlike' => 'unlike',
720 } qw( pasm pbc pir );
721 for my $func ( keys %parrot_test_map ) {
725 local *__ANON__
= $func;
726 my ( $code, $expected, $desc, %extra ) = @_;
727 my $args = $ENV{TEST_PROG_ARGS
} || '';
729 if ( $func =~ /^pbc_output_/ && $args =~ /-r / ) {
730 # native tests with --run-pbc don't make sense
731 return $builder->skip("no native tests with -r");
734 my ( $out_f, $cmd, $exit_code ) = _run_test_file
( $func, @_ );
736 my $meth = $parrot_test_map{$func};
737 my $real_output = slurp_file
($out_f);
739 unlink $out_f unless $ENV{POSTMORTEM
};
741 # set a todo-item for Test::Builder to find
742 my $call_pkg = $builder->exported_to() || '';
745 local *{ $call_pkg . '::TODO' } = ## no critic Variables::ProhibitConditionalDeclarations
747 if defined $extra{todo
};
749 if ( $func =~ /_error_/ ) {
750 return _handle_error_output
( $builder, $real_output, $expected, $desc )
754 $builder->ok( 0, $desc );
755 $builder->diag( "Exited with error code: $exit_code\n"
756 . "Received:\n$real_output\nExpected:\n$expected\n" );
761 my $pass = $builder->$meth( $real_output, $expected, $desc );
762 $builder->diag("'$cmd' failed with exit code $exit_code")
763 if not $pass and $exit_code;
770 *{ $package . '::' . $func } = $test_sub;
773 my %pir_2_pasm_test_map = (
774 pir_2_pasm_is
=> 'is_eq',
775 pir_2_pasm_isnt
=> 'isnt_eq',
776 pir_2_pasm_like
=> 'like',
777 pir_2_pasm_unlike
=> 'unlike',
780 foreach my $func ( keys %pir_2_pasm_test_map ) {
785 local *__ANON__
= $func;
786 my ( $code, $expected, $desc, %extra ) = @_;
788 # Strange Win line endings
789 convert_line_endings
($expected);
791 # set up default description
793 ( undef, my $file, my $line ) = caller();
794 $desc = "($file line $line)";
797 # $test_no will be part of temporary file
798 my $test_no = $builder->current_test() + 1;
800 # Name of the file with test code.
801 my $code_f = File
::Spec
->rel2abs( per_test
( '.pir', $test_no ) );
802 my $code_basef = basename
($code_f);
805 my $out_f = per_test
( '.pasm', $test_no );
807 my $opt = $code_basef =~ m!opt(.)! ?
"-O$1" : "-O1";
808 my $args = $ENV{TEST_PROG_ARGS
} || '';
809 $args .= " $opt --output=$out_f";
810 $args =~ s/--run-exec//;
812 write_code_to_file
( $code, $code_f );
814 my $cmd = qq{$parrot $args "$code_f"};
815 my $exit_code = run_command
(
817 CD
=> $path_to_parrot,
822 my $meth = $pir_2_pasm_test_map{$func};
823 my $real_output = slurp_file
($out_f);
826 # The parrot open '--outfile=file.pasm' seems to create unnecessary whitespace
827 $real_output =~ s/^\s*$//gm;
828 $real_output =~ s/[\t ]+/ /gm;
829 $real_output =~ s/ +$//gm;
831 $expected =~ s/[\t ]+/ /gm;
834 # set a todo-item for Test::Builder to find
835 my $call_pkg = $builder->exported_to() || '';
837 local *{ $call_pkg . '::TODO' } = ## no critic Variables::ProhibitConditionalDeclarations
839 if defined $extra{todo
};
841 my $pass = $builder->$meth( $real_output, $expected, $desc );
842 $builder->diag("'$cmd' failed with exit code $exit_code")
843 if $exit_code and not $pass;
845 if ( !$ENV{POSTMORTEM
} ) {
854 *{ $package . '::' . $func } = $test_sub;
857 my %builtin_language_prefix = (
862 my %language_test_map = (
863 language_output_is
=> 'output_is',
864 language_error_output_is
=> 'error_output_is',
865 language_output_like
=> 'output_like',
866 language_error_output_like
=> 'error_output_like',
867 language_output_isnt
=> 'output_isnt',
868 language_error_output_isnt
=> 'error_output_isnt',
871 foreach my $func ( keys %language_test_map ) {
875 local *__ANON__
= $func;
876 my ( $language, @remaining ) = @_;
878 my $meth = $language_test_map{$func};
879 if ( my $prefix = $builtin_language_prefix{$language} ) {
881 # builtin languages are no tested with the example_output_xx() functions
882 my $level = $builder->level();
883 $builder->level( $level + 2 );
884 my $test_func = "${package}::${prefix}_${meth}";
888 $test_func->(@remaining);
889 $builder->level($level);
893 $language = ucfirst($language);
895 # make sure todo-items will work, by telling Test::Builder which
896 # package the .t file is in (one more than usual, due to the
897 # extra layer of package indirection
898 my $level = $builder->level();
901 # Load module that knows how to test the language implementation
902 require "Parrot/Test/$language.pm";
903 my $class = "Parrot::Test::${language}";
905 # set the builder object, and parrot config.
906 my $obj = $class->new();
907 $obj->{builder
} = $builder;
908 $obj->{relpath
} = $path_to_parrot;
909 $obj->{parrot
} = $parrot;
910 $obj->$meth(@remaining);
912 # restore prior level, just in case.
913 $builder->level($level);
919 *{ $package . '::' . $func } = $test_sub;
922 my %example_test_map = (
923 example_output_is
=> 'language_output_is',
924 example_output_like
=> 'language_output_like',
925 example_output_isnt
=> 'language_output_isnt',
928 foreach my $func ( keys %example_test_map ) {
932 local *__ANON__
= $func;
933 my ( $example_f, $expected, @options ) = @_;
935 my %lang_for_extension = (
940 my ($extension) = $example_f =~ m
{ [.] # introducing extension
941 ( pasm
| pir
) # match and capture the extension
942 \z
# at end of string
944 if ( defined $extension ) {
945 my $code = slurp_file
($example_f);
946 my $test_func = join( '::', $package, $example_test_map{$func} );
951 $lang_for_extension{$extension},
952 $code, $expected, $example_f, @options
956 fail
( defined $extension, "no extension recognized for $example_f" );
962 *{ $package . '::' . $func } = $test_sub;
966 c_output_is
=> 'is_eq',
967 c_output_isnt
=> 'isnt_eq',
968 c_output_like
=> 'like'
971 foreach my $func ( keys %c_test_map ) {
975 local *__ANON__
= $func;
976 my ( $source, $expected, $desc, %options ) = @_;
978 # $test_no will be part of temporary files
979 my $test_no = $builder->current_test() + 1;
981 convert_line_endings
($expected);
983 my $obj_f = per_test
( $PConfig{o
}, $test_no );
984 my $exe_f = per_test
( $PConfig{exe
}, $test_no );
985 $exe_f =~ s@
[\\/:]@
$PConfig{slash
}@g;
986 my $out_f = per_test
( '.out', $test_no );
987 my $build_f = per_test
( '.build', $test_no );
989 # set todo-option before trying to compile or link
991 *main
::TODO
= \
$options{todo
} if $options{todo
};
995 my $source_f = per_test
( '.c', $test_no );
996 write_code_to_file
( $source, $source_f );
999 "$PConfig{cc} $PConfig{ccflags} $PConfig{cc_debug} "
1000 . " -I./include -c "
1001 . "$PConfig{cc_o_out}$obj_f $source_f";
1002 my $exit_code = run_command
(
1004 'STDOUT' => $build_f,
1005 'STDERR' => $build_f
1007 $builder->diag("'$cmd' failed with exit code $exit_code")
1011 $builder->diag( "Failed to build '$obj_f': " . slurp_file
($build_f) );
1013 $builder->ok( 0, $desc );
1019 # link the compiled source, get an executable
1021 my $cfg = File
::Spec
->join( 'src', "parrot_config$PConfig{o}" );
1022 my $iculibs = $PConfig{has_icu
} ?
$PConfig{icu_shared
} : q{};
1024 $PConfig{parrot_is_shared
}
1025 ?
"$PConfig{rpath_blib} -L$PConfig{blib_dir} "
1028 ?
$PConfig{libparrot_ldflags
}
1031 : File
::Spec
->join( $PConfig{blib_dir
}, $PConfig{libparrot_static
} );
1033 "$PConfig{link} $PConfig{linkflags} $PConfig{ld_debug} "
1034 . "$obj_f $cfg $PConfig{ld_out}$exe_f "
1035 . "$libparrot $iculibs $PConfig{libs}";
1036 my $exit_code = run_command
(
1038 'STDOUT' => $build_f,
1039 'STDERR' => $build_f
1041 $builder->diag("'$cmd' failed with exit code $exit_code")
1045 $builder->diag( "Failed to build '$exe_f': " . slurp_file
($build_f) );
1047 $builder->ok( 0, $desc );
1053 # run the generated executable
1056 my $cmd = File
::Spec
->join( File
::Spec
->curdir(), $exe_f );
1057 my $exit_code = run_command
(
1062 my $output = slurp_file
($out_f);
1065 $pass = $builder->ok( 0, $desc );
1066 $builder->diag( "Exited with error code: $exit_code\n"
1067 . "Received:\n$output\nExpected:\n$expected\n" );
1070 my $meth = $c_test_map{$func};
1071 $pass = $builder->$meth( $output, $expected, $desc );
1072 $builder->diag("'$cmd' failed with exit code $exit_code")
1077 unless ( $ENV{POSTMORTEM
} ) {
1078 unlink $out_f, $build_f, $exe_f, $obj_f;
1079 unlink per_test
( '.ilk', $test_no );
1080 unlink per_test
( '.pdb', $test_no );
1088 *{ $package . '::' . $func } = $test_sub;
1100 =item F<docs/tests.pod>
1104 =item L<Test/Builder>
1113 my @foo = caller(2);
1122 # cperl-indent-level: 4
1125 # vim: expandtab shiftwidth=4: