perlapp perltest));
@EXPORT_OK = (@Test::More::EXPORT_OK, qw(bldtop_dir bldtop_file
srctop_dir srctop_file
+ data_file
pipe with cmdstr quotify));
=head1 NAME
and C<$BLDTOP>. Without one of the combinations it refuses to work.
See L</ENVIRONMENT> below.
+With each test recipe, a parallel data directory with (almost) the same name
+as the recipe is possible in the source directory tree. For example, for a
+recipe C<$SRCTOP/test/recipes/99-foo.t>, there could be a directory
+C<$SRCTOP/test/recipes/99-foo_data/>.
+
=cut
use File::Copy;
catdir catfile splitpath catpath devnull abs2rel
rel2abs/;
use File::Path 2.00 qw/rmtree mkpath/;
+use File::Basename;
# The name of the test. This is set by setup() and is used in the other
# exit_checker is used by run() directly after completion of a command.
# it receives the exit code from that command and is expected to return
- # 1 (for success) or 0 (for failure). This is the value that will be
- # returned by run().
- # NOTE: When run() gets the option 'capture => 1', this hook is ignored.
+ # 1 (for success) or 0 (for failure). This is the status value that run()
+ # will give back (through the |statusvar| referens and as returned value
+ # when capture => 1 doesn't apply).
exit_checker => sub { return shift == 0 ? 1 : 0 },
);
# Debug flag, to be set manually when needed
my $debug = 0;
-# Declare some utility functions that are defined at the end
-sub bldtop_file;
-sub bldtop_dir;
-sub srctop_file;
-sub srctop_dir;
-sub quotify;
-
-# Declare some private functions that are defined at the end
-sub __env;
-sub __cwd;
-sub __apps_file;
-sub __results_file;
-sub __fixup_cmd;
-sub __build_cmd;
-
=head2 Main functions
The following functions are exported by default when using C<OpenSSL::Test>.
This functions build up a platform dependent command based on the
input. It takes a reference to a list that is the executable or
script and its arguments, and some additional options (described
-further on).
+further on). Where necessary, the command will be wrapped in a
+suitable environment to make sure the correct shared libraries are
+used (currently only on Unix).
It returns a CODEREF to be used by C<run>, C<pipe> or C<cmdstr>.
the command will be executed with C<system()>, and C<run> will return 1 if
the command was successful or 0 if it wasn't.
+=item B<prefix =E<gt> EXPR>
+
+If specified, EXPR will be used as a string to prefix the output from the
+command. This is useful if the output contains lines starting with C<ok >
+or C<not ok > that can disturb Test::Harness.
+
+=item B<statusvar =E<gt> VARREF>
+
+If used, B<VARREF> must be a reference to a scalar variable. It will be
+assigned a boolean indicating if the command succeeded or not. This is
+particularly useful together with B<capture>.
+
=back
For further discussion on what is considered a successful command or not, see
my $r = 0;
my $e = 0;
+ die "OpenSSL::Test::run(): statusvar value not a scalar reference"
+ if $opts{statusvar} && ref($opts{statusvar}) ne "SCALAR";
+
# In non-verbose, we want to shut up the command interpreter, in case
# it has something to complain about. On VMS, it might complain both
# on stdout and stderr
# do. For example, a program that gets aborted (and therefore signals
# SIGABRT = 6) will appear to exit with the code 134. We mimic this
# to make it easier to compare with a manual run of the command.
- if ($opts{capture}) {
- @r = `$prefix$cmd`;
- $e = ($? & 0x7f) ? ($? & 0x7f)|0x80 : ($? >> 8);
+ if ($opts{capture} || defined($opts{prefix})) {
+ my $pipe;
+
+ open($pipe, '-|', "$prefix$cmd") or die "Can't start command: $!";
+ while(<$pipe>) {
+ my $l = ($opts{prefix} // "") . $_;
+ if ($opts{capture}) {
+ push @r, $l;
+ } else {
+ print STDOUT $l;
+ }
+ }
+ close $pipe;
} else {
system("$prefix$cmd");
- $e = ($? & 0x7f) ? ($? & 0x7f)|0x80 : ($? >> 8);
- $r = $hooks{exit_checker}->($e);
+ }
+ $e = ($? & 0x7f) ? ($? & 0x7f)|0x80 : ($? >> 8);
+ $r = $hooks{exit_checker}->($e);
+ if ($opts{statusvar}) {
+ ${$opts{statusvar}} = $r;
}
if ($ENV{HARNESS_ACTIVE} && !$ENV{HARNESS_VERBOSE}) {
=over 4
+=item B<data_file LIST, FILENAME>
+
+LIST is a list of directories that make up a path from the data directory
+associated with the test (see L</DESCRIPTION> above) and FILENAME is the name
+of a file located in that directory path. C<data_file> returns the resulting
+file path as a string, adapted to the local operating system.
+
+=back
+
+=cut
+
+sub data_file {
+ return __data_file(@_);
+}
+
+=over 4
+
=item B<pipe LIST>
LIST is a list of CODEREFs returned by C<app> or C<test>, from which C<pipe>
=cut
sub __env {
+ (my $recipe_datadir = basename($0)) =~ s/\.t$/_data/i;
+
$directories{SRCTOP} = $ENV{SRCTOP} || $ENV{TOP};
$directories{BLDTOP} = $ENV{BLDTOP} || $ENV{TOP};
$directories{BLDAPPS} = $ENV{BIN_D} || __bldtop_dir("apps");
$directories{SRCFUZZ} = __srctop_dir("fuzz");
$directories{BLDTEST} = $ENV{TEST_D} || __bldtop_dir("test");
$directories{SRCTEST} = __srctop_dir("test");
+ $directories{SRCDATA} = __srctop_dir("test", "recipes",
+ $recipe_datadir);
$directories{RESULTS} = $ENV{RESULT_D} || $directories{BLDTEST};
push @direnv, "TOP" if $ENV{TOP};
$end_with_bailout = $ENV{STOPTEST} ? 1 : 0;
};
+# __srctop_file and __srctop_dir are helpers to build file and directory
+# names on top of the source directory. They depend on $SRCTOP, and
+# therefore on the proper use of setup() and when needed, indir().
+# __bldtop_file and __bldtop_dir do the same thing but relative to $BLDTOP.
+# __srctop_file and __bldtop_file take the same kind of argument as
+# File::Spec::Functions::catfile.
+# Similarly, __srctop_dir and __bldtop_dir take the same kind of argument
+# as File::Spec::Functions::catdir
sub __srctop_file {
BAIL_OUT("Must run setup() first") if (! $test_name);
return catdir($directories{BLDTOP},@_);
}
+# __exeext is a function that returns the platform dependent file extension
+# for executable binaries, or the value of the environment variable $EXE_EXT
+# if that one is defined.
sub __exeext {
my $ext = "";
if ($^O eq "VMS" ) { # VMS
return $ENV{"EXE_EXT"} || $ext;
}
+# __test_file, __apps_file and __fuzz_file return the full path to a file
+# relative to the test/, apps/ or fuzz/ directory in the build tree or the
+# source tree, depending on where the file is found. Note that when looking
+# in the build tree, the file name with an added extension is looked for, if
+# an extension is given. The intent is to look for executable binaries (in
+# the build tree) or possibly scripts (in the source tree).
+# These functions all take the same arguments as File::Spec::Functions::catfile,
+# *plus* a mandatory extension argument. This extension argument can be undef,
+# and is ignored in such a case.
sub __test_file {
BAIL_OUT("Must run setup() first") if (! $test_name);
return $f;
}
+sub __data_file {
+ BAIL_OUT("Must run setup() first") if (! $test_name);
+
+ my $f = pop;
+ return catfile($directories{SRCDATA},@_,$f);
+}
+
sub __results_file {
BAIL_OUT("Must run setup() first") if (! $test_name);
return catfile($directories{RESULTS},@_,$f);
}
+# __cwd DIR
+# __cwd DIR, OPTS
+#
+# __cwd changes directory to DIR (string) and changes all the relative
+# entries in %directories accordingly. OPTS is an optional series of
+# hash style arguments to alter __cwd's behavior:
+#
+# create = 0|1 The directory we move to is created if 1, not if 0.
+# cleanup = 0|1 The directory we move from is removed if 1, not if 0.
+
sub __cwd {
my $dir = catdir(shift);
my %opts = @_;
print STDERR "DEBUG: __cwd(), directories and files:\n";
print STDERR " \$directories{BLDTEST} = \"$directories{BLDTEST}\"\n";
print STDERR " \$directories{SRCTEST} = \"$directories{SRCTEST}\"\n";
+ print STDERR " \$directories{SRCDATA} = \"$directories{SRCDATA}\"\n";
print STDERR " \$directories{RESULTS} = \"$directories{RESULTS}\"\n";
print STDERR " \$directories{BLDAPPS} = \"$directories{BLDAPPS}\"\n";
print STDERR " \$directories{SRCAPPS} = \"$directories{SRCAPPS}\"\n";
$prefix = ($prog =~ /^(?:[\$a-z0-9_]+:)?[<\[]/i ? "mcr " : "mcr []");
}
- # We test both with and without extension. The reason
- # is that we might be passed a complete file spec, with
- # extension.
+ # We test if the program to use exists.
if ( ! -x $prog ) {
- my $prog = "$prog";
- if ( ! -x $prog ) {
- $prog = undef;
- }
+ $prog = undef;
}
if (defined($prog)) {
# Make sure to quotify the program file on platforms that may
# have spaces or similar in their path name.
# To our knowledge, VMS is the exception where quotifying should
- # never happem.
+ # never happen.
($prog) = quotify($prog) unless $^O eq "VMS";
return $prefix.$prog;
}
return undef;
}
+# __decorate_cmd NUM, CMDARRAYREF
+#
+# __decorate_cmd takes a command number NUM and a command token array
+# CMDARRAYREF, builds up a command string from them and decorates it
+# with necessary redirections.
+# __decorate_cmd returns a list of two strings, one with the command
+# string to actually be used, the other to be displayed for the user.
+# The reason these strings might differ is that we redirect stderr to
+# the null device unless we're verbose and unless the user has
+# explicitly specified a stderr redirection.
sub __decorate_cmd {
BAIL_OUT("Must run setup() first") if (! $test_name);