OpenSSL::Test: supported filtered command output
[openssl.git] / test / testlib / OpenSSL / Test.pm
index 24219e187ced1d150e208086bc401b4fca22d6c1..c76ca1caa67b4cf5d06105887ee12714828516ef 100644 (file)
@@ -20,6 +20,7 @@ $VERSION = "0.8";
                                    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
@@ -50,6 +51,11 @@ This module I<depends> on the environment variables C<$TOP> or C<$SRCTOP>
 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;
@@ -57,6 +63,7 @@ use File::Spec::Functions qw/file_name_is_absolute curdir canonpath splitdir
                              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
@@ -84,9 +91,9 @@ my %hooks = (
 
     # 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 },
 
     );
@@ -94,21 +101,6 @@ my %hooks = (
 # 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>.
@@ -223,7 +215,9 @@ sub indir {
 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>.
 
@@ -409,6 +403,18 @@ return the resulting output as an array of lines.  If false or not given,
 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
@@ -433,6 +439,9 @@ sub run {
     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
@@ -449,13 +458,26 @@ sub run {
     # 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}) {
@@ -578,6 +600,23 @@ sub srctop_file {
 
 =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>
@@ -777,6 +816,8 @@ failures will result in a C<BAIL_OUT> at the end of its run.
 =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");
@@ -785,6 +826,8 @@ sub __env {
     $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};
@@ -797,6 +840,14 @@ sub __env {
     $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);
 
@@ -823,6 +874,9 @@ sub __bldtop_dir {
     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
@@ -833,6 +887,15 @@ sub __exeext {
     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);
 
@@ -863,6 +926,13 @@ sub __fuzz_file {
     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);
 
@@ -870,6 +940,16 @@ sub __results_file {
     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 = @_;
@@ -944,6 +1024,7 @@ sub __cwd {
        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";
@@ -999,21 +1080,16 @@ sub __fixup_prg {
        $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;
     }
@@ -1022,6 +1098,16 @@ sub __fixup_prg {
     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);