summaryrefslogtreecommitdiffstats
path: root/t/Testhelper.pm
diff options
context:
space:
mode:
Diffstat (limited to 't/Testhelper.pm')
-rw-r--r--t/Testhelper.pm601
1 files changed, 601 insertions, 0 deletions
diff --git a/t/Testhelper.pm b/t/Testhelper.pm
new file mode 100644
index 0000000..272d79a
--- /dev/null
+++ b/t/Testhelper.pm
@@ -0,0 +1,601 @@
+# Provides common test routines to avoid repeating the same code over and over again.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+########################################################################
+
+# This accepts several type of tests:
+#
+#
+# * po4a.conf test. List of accepted keys:
+# doc (opt): Free string
+# po4a.conf (req): path to a config file for po4a
+# options (opt): extra parameters to pass to po4a, in addition to "--destdir tmp --verbose" that is unavoidable.
+
+# diff_outfile (opt): Command to use to check that the po4a command produced the right output
+# expected_outfile (opt): Name of the file containing the expected output of the po4a command
+# expected_retcode (opt): Return value expected for po4a (0 by default). Useful to test invalid inputs.
+
+# expected_files (opt): array of file names that are expected after the test execution. If provided:
+# - Any missing or unexpected file is a test failure.
+# - The expected content of $file must be provided in _$file or $file
+# (use _$file for pure output files that are not supposed to be read by po4a).
+# For that, a test is added automatically unless your tests already contain a line matching '$path/$file tmp/$path/$file'.
+# This test is 'PODIFF -I#: $path/$file tmp/$path/$file' if your file is a po/pot file, or 'diff -u $path/$file tmp/$path/$file'
+# tests (opt): array of shell commands to run after the test; any failure is reported as a test failure
+# Automatic substitutions in each command:
+# PATH -> Path to po4a binaries
+# PODIFF -> "diff -u -I'^\"POT-Creation-Date:' -I'^\"PO-Revision-Date:'"
+
+# setup (opt): array of shell commands to run before running the test
+# teardown (opt): array of shell commands to run after running the test
+# closed_path (opt): a shell pattern to pass to chmod to close the other directories.
+# If provided, setup/teardown commands are generated to (unless on windows):
+# - 'chmod 0' the directories marked 'closed_path'
+# - 'chmod +r-w+x' the test directory
+# - 'chmod +r-w' all files in the test directory
+# modes (opt): space separated modes to apply. Valid values:
+# - srcdir: cd tmp/$path ; po4a --srcdir $cwd/$path ; cd $cwd; <all tests>
+# - dstdir: cd $path ; po4a --destdir $cwd/tmp/$path ; cd $cwd: <all tests>
+# - srcdstdir: po4a --srcdir $cwd/$path --destdir $cwd/tmp/$path ; <all tests>
+# - curdir: cp $path/* tmp/$path ; cd tmp/$path ; po4a ; cd $cwd; <all tests but the one checking that only expected files were created>
+# If several values are provided, the tests are run several times. By default: run all modes.
+#
+
+# * 'format' test: modern tests to the formats. This kind of tests are mostly configured through files. Accepted keys
+# - format (req): the module to use
+# - input (req) : input file name (decomposed as $basename.$ext in the following)
+# - norm (default: $basename.norm): expected normalized document (translated with inexistant language)
+# - potfile (default: $basname.pot): expected POT file
+# - pofile (default: $basname.po): PO file used as input
+# All existing 'format' tests use uppercase as a translation language, so that everybody can work on them.
+# f=fmt/asciidoc/TablesCells; msgen $f.pot | msgfilter -o $f.po sed -e 's/./\U&/g' ; msgmerge -U $f.po $f.pot
+# - norm_stderr (optional -- default: $basename.norm.stderr): expected output of the normalization.
+# If the file does not exist, the normalization is expected to not output anything
+# - error (optional boolean -- default: false): whether the normalization is expected to fail
+# If so, the translation is not attempted (and the translation files don't have to exist)
+# - skip (optional): a set of stages that should be skipped, may contain 'normalize', 'translation' or 'updatepo'
+# in any combination
+# - trans (default: $basename.trans): expected translated document (translated with provided PO file)
+# - trans_stderr (optional -- default: $basename.trans.stderr): expected output of the translation
+# If the file does not exist, the normalization is expected to not output anything
+# - options (optional): options to pass to the translation, updatepo and normalization
+#
+# if a file $basename.desc exists, any comment is used as test description while the rest is added to the options
+
+# * 'run' test: ancient, unconverted tests
+
+package Testhelper;
+
+use 5.16.0;
+use strict;
+use warnings;
+use Test::More 'no_plan';
+use File::Path qw(make_path remove_tree);
+use File::Copy;
+use Cwd qw(cwd);
+
+use Exporter qw(import);
+our @EXPORT = qw(run_all_tests);
+
+# Set the right environment variables to normalize the outputs
+$ENV{'LC_ALL'} = "C";
+$ENV{'COLUMNS'} = "80";
+
+# Path to the tested executables. AUTOPKGTEST_TMP is set on salsa CI for Debian packages.
+my $execpath = defined $ENV{AUTOPKGTEST_TMP} ? "/usr/bin" : "perl ..";
+
+# msginit annoyances, see https://github.com/mquinson/po4a/issues/338
+my $PODIFF =
+ "-I'^\"Project-Id-Version:' -I'^\"POT-Creation-Date:' -I'^\"PO-Revision-Date:' "
+ . "-I'^# [^[:blank:]]* translations for ' -I'^# Language [^[:blank:]]* translations for ' -I'Copyright (C) 20.. Free Software Foundation, Inc.' -I'^# This file is distributed under the same license as the' -I'^# Automatically generated, 20...' ";
+
+sub show_files {
+ my $basename = shift;
+ foreach my $file ( glob("${basename}*") ) {
+ if ( -s $file ) {
+ diag("Produced file $file:");
+ open FH, "$file" || die "Cannot open exisiting $file, I'm puzzled";
+ while (<FH>) {
+ diag(" $_");
+ }
+ diag("(end of $file)\n");
+ diag("-------------------------------------------------------------");
+ close(FH);
+ }
+ }
+}
+
+my $root_dir = cwd();
+
+# Returns whether the details should be shown
+sub system_failed {
+ my ( $cmd, $doc, $expected_exit_status ) = @_;
+ $expected_exit_status //= 0;
+ my $exit_status = system($cmd);
+ $cmd =~ s/diff -u $PODIFF /PODIFF/g;
+ $cmd =~ s{$root_dir/}{BUILDPATH/}g;
+ $cmd =~ s{t/../po4a}{po4a};
+
+ if ( $exit_status == $expected_exit_status ) {
+ if ( $doc ne '' ) {
+ pass($doc);
+ pass(" Command: $cmd");
+ } else {
+ pass("$cmd");
+ }
+ return 0;
+ } else {
+ $doc = 'Provided command' unless $doc ne '';
+ fail( $doc . " (retcode: $exit_status)" );
+ note("Expected retcode: $expected_exit_status");
+ note("FAILED command: $cmd");
+ return 1;
+ }
+}
+
+# Remove that directory if it exists, and rebuild it fresh
+sub remake_path {
+ my $path = shift;
+ if ( -e $path ) {
+ remove_tree("$path") || die "Cannot cleanup $path/ on startup: $!";
+ }
+
+ make_path($path) || die "Cannot create $path/: $!";
+}
+
+sub teardown {
+ my $ref = shift;
+ my @cmds = @{$ref};
+
+ foreach my $cmd (@cmds) {
+ if ( system("$cmd 1>&2") ) {
+ diag("Error during teardown: $!");
+ diag(" Command: $cmd");
+ } else {
+ pass("Teardown command: $cmd");
+ }
+ }
+}
+
+sub setup {
+ my $ref = shift;
+ my @cmds = @{$ref};
+
+ foreach my $cmd (@cmds) {
+ if ( system("$cmd 1>&2") ) {
+ diag("Error during setup: $!");
+ diag(" Command: $cmd");
+ return 0;
+ } else {
+ pass("Setup command: $cmd");
+ }
+ }
+ return 1;
+}
+
+sub run_one_po4aconf {
+ my ( $t, $path, $basename, $ext, $mode ) = @_;
+
+ my %valid_options;
+ map { $valid_options{$_} = 1 }
+ qw(po4a.conf todo doc closed_path options setup tests teardown expected_retcode expected_files diff_outfile expected_outfile );
+ map { die "Invalid test " . $t->{'doc'} . ": invalid key '$_'\n" unless exists $valid_options{$_} } ( keys %{$t} );
+
+ my $po4aconf = $t->{'po4a.conf'} || fail("Broken test: po4a.conf must be provided");
+ my $options = "--verbose " . ( $t->{'options'} // "" );
+ my $closed_path = $t->{'closed_path'};
+ my $doc = $t->{'doc'};
+ my $expected_files = $t->{'expected_files'} // "";
+ my $expected_retcode = $t->{'expected_retcode'} // 0;
+
+ fail("Broken test: 'tests' is not an array as expected") if exists $t->{tests} && ref $t->{tests} ne 'ARRAY';
+ my ( @tests, @setup, @teardown ) = ( (), (), () );
+ map { push @tests, $_; } @{ $t->{'tests'} } if exists $t->{'tests'};
+ map { push @setup, $_; } @{ $t->{'setup'} } if exists $t->{'setup'};
+ map { push @teardown, $_; } @{ $t->{'teardown'} } if exists $t->{'teardown'};
+
+ fail("Broken test: path $path does not exist") unless -e $path;
+
+ if ( $closed_path && $^O ne 'MSWin32' ) {
+ push @setup, "chmod -r-w-x " . $closed_path; # Don't even look at the closed path
+ push @teardown, "chmod +r+w+x " . $closed_path; # Restore permissions
+ push @setup, "chmod +r+x $path"; # Look into the path of this test
+ push @setup, "chmod -w -R $path"; # But don't change any file in there
+ push @teardown, "chmod +w -R $path"; # Restore permissions
+ }
+
+ my $cwd = cwd();
+ my $tmppath = "tmp/$path";
+ $execpath = defined $ENV{AUTOPKGTEST_TMP} ? "/usr/bin" : "perl $cwd/..";
+
+ my $run_from = '.';
+ if ( $mode eq 'srcdir' ) {
+ $tmppath .= '-src';
+ $run_from = $tmppath;
+ $options = "--srcdir $cwd/$path $options";
+ } elsif ( $mode eq 'dstdir' ) {
+ $run_from = $path;
+ $options = "--destdir $cwd/$tmppath $options";
+ } elsif ( $mode eq 'srcdstdir' ) {
+ $tmppath .= '-srcdst';
+ $options = "--srcdir $path --destdir $tmppath $options";
+ } elsif ( $mode eq 'curdir' ) {
+ $tmppath .= '-cur';
+ push @setup, "cp -r $path/* $tmppath";
+ push @setup, "chmod +w -R $tmppath" unless $^O eq 'MSWin32';
+ $run_from = $tmppath;
+ } else {
+ die "Malformed test: mode $mode unknown\n";
+ }
+
+ remake_path($tmppath);
+ unless ( setup( \@setup ) ) { # Failed
+ teardown( \@teardown );
+ return;
+ }
+
+ my $cmd = "$execpath/po4a -f $cwd/$po4aconf $options > $cwd/$tmppath/output 2>&1";
+
+ chdir $run_from || fail "Cannot change directory to $run_from: $!";
+ pass("Change directory to $run_from");
+ die "Malformed test: conf file $cwd/$po4aconf does not exist from " . cwd() . " (mode:$mode)\n"
+ unless -e "$cwd/$po4aconf";
+ if ( system_failed( $cmd, "Executing po4a", $expected_retcode ) ) {
+ chdir $cwd || fail "Cannot change directory back to $cwd: $!";
+ note("Produced output:");
+ open FH, "$tmppath/output" || die "Cannot open output file that I just created, I'm puzzled";
+ while (<FH>) {
+ note(" $_");
+ }
+ note("(end of command output)\n");
+
+ teardown( \@teardown );
+
+ # show_files("$tmppath/");
+ return;
+ }
+ chdir $cwd || fail "Cannot change directory back to $cwd: $!";
+ pass("Change directory back to $cwd");
+
+ my $expected_outfile = $t->{'expected_outfile'} // "$path/_output";
+ unless ( -e $expected_outfile ) {
+ $expected_outfile = "$path/$expected_outfile"
+ if ( not -e $expected_outfile ) && ( -e "$path/$expected_outfile" );
+ unless ( -e $expected_outfile ) {
+ teardown( \@teardown );
+ die "Malformed test $path ($doc): no expected output. Please touch $expected_outfile\n";
+ }
+ }
+ my $diff_outfile = $t->{'diff_outfile'}
+ // " sed -e 's|$cwd/||' -e 's|$tmppath/||' -e 's|$path/||' $tmppath/output | " . "diff -u $expected_outfile -";
+ if ( system_failed( "$diff_outfile 2>&1 > $tmppath/diff_output", "Comparing output of po4a" ) ) {
+ note("Output difference:");
+ open FH, "$tmppath/diff_output" || die "Cannot open output file that I just created, I'm puzzled";
+ while (<FH>) {
+ chomp;
+ note(" $_");
+ }
+ note("(end of diff)\n");
+ teardown( \@teardown );
+ show_files("$tmppath/");
+ return;
+ }
+
+ my %expected;
+ map { s/\\ / /g; $expected{$_} = 1 } split /(?<!\\) +/, $expected_files;
+ if ( length $expected_files == 0 ) {
+ note("Expecting no output file.");
+ } else {
+ note( "Expecting " . ( scalar %expected ) . " output files: " . join( ":", keys %expected ) );
+ }
+ $expected{'output'} = 1;
+ $expected{'diff_output'} = 1;
+ FILE: foreach my $file ( glob("$tmppath/*") ) {
+ note("Seen file $file");
+ $file =~ s|$tmppath/||;
+ if ( not $expected{$file} ) {
+ if ( ( $mode eq 'srcdir' || $mode eq 'dstdir' || $mode eq 'srcdstdir' ) ) {
+ teardown( \@teardown );
+ fail "Unexpected file '$file'";
+ }
+ } else {
+ delete $expected{$file};
+ next FILE if $file eq 'output' || $file eq 'diff_output';
+ next FILE if -d "$path/$file";
+ if ( scalar grep { m{$tmppath/$file} } @tests ) {
+ note("Using the provided test to compare the content of $file");
+ } elsif ( -e "$path/_$file" ) {
+ push @tests,
+ ( $file =~ 'pot?$' ? "PODIFF -I#: " : "diff -u --strip-trailing-cr" )
+ . " \"$path/_$file\" \"$tmppath/$file\"";
+ } elsif ( -e "$path/$file" ) {
+ push @tests,
+ ( $file =~ 'pot?$' ? "PODIFF -I#: " : "diff -u --strip-trailing-cr" )
+ . " \"$path/$file\" \"$tmppath/$file\"";
+ } else {
+ teardown( \@teardown );
+ fail("Broken test $path: $path/_$file should be the expected content of produced file $file");
+ }
+ }
+ }
+ foreach my $file ( keys %expected ) {
+ fail "Missing file '$file'";
+ }
+
+ for my $tcmd (@tests) {
+
+ # print STDERR "cmd: $tcmd\n";
+ $tcmd =~ s/PATH/${execpath}/g;
+ $tcmd =~ s/PODIFF/diff -u --strip-trailing-cr $PODIFF /g;
+ $tcmd =~ s/\$tmppath/$tmppath/g;
+ $tcmd =~ s/\$path/$path/g;
+ if ( system_failed( "$tcmd 2>&1 > $tmppath/_cmd_output", "" ) ) {
+ note("Command output:");
+ open FH, "$tmppath/_cmd_output" || die "Cannot open output file that I just created, I'm puzzled";
+ while (<FH>) {
+ chomp;
+ note("| $_");
+ }
+ note("(end of output)\n");
+ teardown( \@teardown );
+ show_files("$tmppath/");
+ return;
+ }
+ unlink("$tmppath/_cmd_output");
+ }
+
+ teardown( \@teardown );
+}
+
+sub run_one_format {
+ my ( $test, $input ) = @_;
+
+ my $doc = $test->{'doc'} // "Format testing '$input'";
+ my $error = $test->{'error'} // 0; # Whether a normalization error is expected to interrupt the test
+
+ my %valid_options;
+ map { $valid_options{$_} = 1 } qw(format input potfile pofile doc options todo
+ norm norm_stderr error skip
+ trans trans_stderr );
+ map { die "Invalid test " . $test->{'doc'} . ": invalid key '$_'\n" unless exists $valid_options{$_} }
+ ( keys %{$test} );
+
+ fail "Broken test: input file $input does not exist or not readable." unless -e $input && -r $input;
+
+ my $format = $test->{'format'} // die "Broken test $input: no format provided\n";
+
+ $input =~ m|^(.*)/([^/]*)\.([^/]*)$| || die "Broken test: input file '$input' is not of form PATH/BASE.EXT";
+ my ( $path, $basename, $ext ) = ( $1, $2, $3 );
+
+ my $output = $test->{'norm'} // "$path/$basename.norm";
+ fail "Broken test $basename: expected normalized file '$output' does not exist" unless -e $output || $error;
+ my $potfile = $test->{'potfile'} // "$path/$basename.pot";
+ fail "Broken test $basename: expected POT file '$potfile' does not exist" unless -e $potfile || $error;
+ my $pofile = $test->{'pofile'} // "$path/$basename.po";
+ fail "Broken test $basename: expected PO file '$pofile' does not exist" unless -e $pofile || $error;
+ my $transfile = $test->{'trans'} // "$path/$basename.trans";
+ fail "Broken test $basename: expected translated file '$transfile' does not exist" unless -e $transfile || $error;
+
+ my $norm_stderr = $test->{'norm_stderr'} // "$path/$basename.norm.stderr"; # this file is optionnal
+ my $trans_stderr = $test->{'trans_stderr'} // "$path/$basename.trans.stderr"; # this file is optionnal
+
+ my $options = $test->{'options'} // "";
+ my (@tests);
+
+ if ( -e "$path/$basename.desc" ) {
+ open FH, "<$path/$basename.desc" || die "Cannot read $path/$basename.desc: $!";
+ while (<FH>) {
+ chomp;
+ s{#(.*)$}{} and $doc = $1;
+ s{^[ \t]+}{};
+ next if m{^$};
+ $options .= "$_ ";
+ }
+ }
+
+ remake_path("tmp/$path");
+ my $tmpbase = "tmp/$path/$basename";
+ my $cwd = cwd();
+ $execpath = defined $ENV{AUTOPKGTEST_TMP} ? "/usr/bin" : "perl $cwd/..";
+
+ unless (exists $test->{'skip'}{'normalize'}) {
+ # Normalize the document
+ my $real_stderr = "$cwd/tmp/$path/$basename.norm.stderr";
+ my $cmd =
+ "${execpath}/po4a-normalize --no-deprecation -f $format --quiet "
+ . "--pot $cwd/${tmpbase}.pot --localized $cwd/${tmpbase}.norm $options $basename.$ext"
+ . " > $real_stderr 2>&1";
+
+ chdir $path || fail "Cannot change directory to $path: $!";
+ note("Change directory to $path");
+ my $exit_status = system($cmd);
+ $cmd =~ s{$root_dir/}{}g;
+ $cmd =~ s{t/../po4a}{po4a};
+
+ if ( $error == 0 && $exit_status == 0 ) {
+ pass("Normalizing $doc");
+ note(" Pass: $cmd (retcode: $exit_status)");
+ } elsif ( $error != 0 && $exit_status != 0 ) {
+ pass("Expected error detected in $doc");
+ note(" Failing as expected: $cmd (retcode: $exit_status)");
+ note("Produced output:");
+ open FH, $real_stderr || die "Cannot open output file that I just created, I'm puzzled";
+ while (<FH>) {
+ note(" $_");
+ }
+ note("(end of command output)\n");
+ } else {
+ fail("Normalizing $doc: $exit_status");
+ note(" FAIL: $cmd");
+ note("Produced output:");
+ open FH, $real_stderr || die "Cannot open output file that I just created, I'm puzzled";
+ while (<FH>) {
+ note(" $_");
+ }
+ note("(end of command output)\n");
+ }
+
+ push @tests, "diff -uNwB $norm_stderr $real_stderr";
+ push @tests, "PODIFF $potfile $tmpbase.pot" unless $error;
+ push @tests, "diff -u $output $tmpbase.norm" unless $error;
+ }
+
+ unless ($error or exists $test->{'skip'}{'traslate'}) {
+ # Translate the document
+ my $cmd =
+ "${execpath}/po4a-translate --no-deprecation -f $format $options --master $basename.$ext"
+ . " --po $cwd/$pofile --localized $cwd/${tmpbase}.trans"
+ . " > $cwd/$tmpbase.trans.stderr 2>&1";
+
+ if ( system_failed( $cmd, "Translating $doc" ) ) {
+ note("Produced output:");
+ open FH, "$cwd/$tmpbase.trans.stderr" || die "Cannot open output file that I just created, I'm puzzled";
+ while (<FH>) {
+ note(" $_");
+ }
+ note("(end of command output)\n");
+ }
+ push @tests, "diff -uNwB $trans_stderr $tmpbase.trans.stderr";
+ push @tests, "diff -uN $transfile $tmpbase.trans";
+ }
+
+ unless ($error or exists $test->{'skip'}{'updatepo'}) {
+ # Update PO
+ copy( "$cwd/$pofile", "$cwd/${tmpbase}.po_updated" ) || fail "Cannot copy $pofile before updating it";
+ my $cmd =
+ "${execpath}/po4a-updatepo --no-deprecation -f $format $options "
+ . "--master $basename.$ext --po $cwd/${tmpbase}.po_updated"
+ . " > $cwd/tmp/$path/update.stderr 2>&1";
+
+ if ( system_failed( $cmd, "" ) ) {
+ note("Produced output:");
+ open FH, "$cwd/tmp/$path/update.stderr" || die "Cannot open output file that I just created, I'm puzzled";
+ while (<FH>) {
+ note(" $_");
+ }
+ note("(end of command output)\n");
+ }
+
+ push @tests, "PODIFF $pofile ${tmpbase}.po_updated";
+ }
+ chdir $cwd || fail "Cannot change directory back to $cwd: $!";
+ note("Change directory back to $cwd");
+
+ # Run all accumulated tests
+ for my $tcmd (@tests) {
+ $tcmd =~ s/PATH/${execpath}/g;
+ $tcmd =~ s/PODIFF/diff -u $PODIFF /g;
+ if ( system_failed( "$tcmd > $tmpbase.cmd_output 2>&1", "" ) ) {
+ note("Command output:");
+ open FH, "$tmpbase.cmd_output" || die "Cannot open output file that I just created, I'm puzzled";
+ while (<FH>) {
+ chomp;
+ note("| $_");
+ }
+ note("(end of output)\n");
+ show_files("tmp/$path");
+ return;
+ }
+ unlink("$tmpbase.cmd_output");
+ }
+}
+
+sub run_all_tests {
+ my @cases = @_;
+
+ # Change into test directory and create a temporary directory
+ chdir "t" or die "Can't chdir to test directory t\n";
+
+ remake_path("tmp");
+
+ TEST: foreach my $test (@cases) {
+ if ( exists $test->{'format'} ) {
+ my $input = $test->{'input'} // die "Broken test: 'format' tests must declare an 'input' key'";
+ $test->{'doc'} = "Format $input" unless exists $test->{'doc'};
+
+ if ( exists $test->{'todo'} ) {
+ TODO: {
+ print STDERR "TODO: " . $test->{doc} . "\n";
+ local our $TODO = $test->{'todo'};
+ subtest $test->{'doc'} => sub { run_one_format( $test, $input ); }
+ }
+ } else {
+ subtest $test->{'doc'} => sub { run_one_format( $test, $input ); }
+ }
+ } elsif ( exists $test->{'po4a.conf'} ) {
+
+ if ( $test->{'po4a.conf'} =~ m|^(.*)/([^/]*)\.([^./]*)$| ) {
+ my ( $path, $basename, $ext ) = ( $1, $2, $3 );
+
+ my @modes;
+ if ( exists $test->{'modes'} ) {
+ push @modes, split( / /, $test->{'modes'} );
+ delete $test->{'modes'};
+ } else {
+ push @modes, (qw(dstdir srcdir srcdstdir curdir));
+ }
+ map {
+ die "Malformed test: mode '$_' invalid."
+ unless $_ eq 'dstdir' || $_ eq 'srcdir' || $_ eq 'srcdstdir' || $_ eq 'curdir';
+ } @modes;
+
+ if ( exists $test->{'todo'} ) {
+ TODO: {
+ local our $TODO = $test->{'todo'};
+ foreach my $mode (@modes) {
+ subtest $test->{'doc'}
+ . " ($mode)" => sub { run_one_po4aconf( $test, $path, $basename, $ext, $mode ); };
+ }
+ }
+ } else {
+ foreach my $mode (@modes) {
+ subtest $test->{'doc'}
+ . " ($mode)" => sub { run_one_po4aconf( $test, $path, $basename, $ext, $mode ); };
+ }
+ }
+ } else {
+ fail "Test " . $test->{'doc'} . " malformed. Cannot parse the conf filename.";
+ }
+
+ } elsif ( exists $test->{'run'} ) {
+
+ my $cmd = $test->{'run'};
+ $cmd =~ s/PATH/${execpath}/;
+ my $exit_status = system($cmd);
+ is( $exit_status, 0, "Executing " . $test->{'doc'} . " -- Command: $cmd" );
+
+ fail "Malformed test " . $test->{'doc'} . ": missing tests." unless scalar $test->{tests};
+ for my $cmd ( @{ $test->{tests} } ) {
+
+ # print STDERR "cmd: $cmd\n";
+ $cmd =~ s/PODIFF/diff -u $PODIFF/g;
+ $exit_status = system( $cmd. ' 1>&2' );
+ $cmd =~
+ s/diff -u -I'Copyright .C. 20.. Free Software Foundation, Inc.' -I'.. Automatically generated, 20...' -I'."POT-Creation-Date:' -I'."PO-Revision-Date:'/PODIFF/g;
+ is( $exit_status, 0, "Test of " . $test->{'doc'} . " -- Command: $cmd" );
+ next TEST unless ( $exit_status == 0 );
+ }
+
+ } else {
+ fail "Test " . $test->{'doc'} . " does not have any 'po4a.conf' nor 'format' nor 'run' field";
+ }
+ }
+
+ chdir ".." or die "Can't chdir back to root directory\n";
+}
+
+1;