Commit 4d4b0d910c for openssl.org

commit 4d4b0d910c3bcf7b95d06af098c5714919c3998b
Author: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Date:   Sat Aug 29 13:58:26 2026 +0200

    test framework: add app_fails and slurp_file to OpenSSL::Test

    Several test recipes duplicate a helper that runs an openssl
    application expecting a failure exit status and checks that the
    stderr output matches a regular expression.  Add a shared app_fails
    function so new recipes do not need to repeat it, together with a
    slurp_file function for reading back a captured output file.

    Assisted-by: Claude:claude-fable-5
    Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    Merge-date: Mon Sep  7 14:15:49 2026
    Merged-from: https://github.com/openssl/openssl/pull/32592

diff --git a/util/perl/OpenSSL/Test.pm b/util/perl/OpenSSL/Test.pm
index fab84f8d7a..67e59b65e6 100644
--- a/util/perl/OpenSSL/Test.pm
+++ b/util/perl/OpenSSL/Test.pm
@@ -23,7 +23,8 @@ $VERSION = "1.0";
                                          srctop_dir srctop_file
                                          data_file data_dir
                                          result_file result_dir
-                                         pipe with cmdstr
+                                         pipe with cmdstr app_fails
+                                         slurp_file
                                          openssl_versions
                                          ok_nofips is_nofips isnt_nofips));

@@ -837,6 +838,60 @@ sub with {

 =over 4

+=item B<slurp_file FILENAME>
+
+C<slurp_file> reads back the whole file FILENAME, usually an output
+captured with the C<stdout> or C<stderr> option of C<cmd> and its
+derivatives, and returns its content as a single string.  If the file
+cannot be opened, an empty string is returned.
+
+=back
+
+=cut
+
+sub slurp_file {
+    my ($file) = @_;
+    my $content = '';
+
+    if (open(my $fh, '<', $file)) {
+        $content = do { local $/; <$fh> };
+        close($fh);
+    }
+    return $content;
+}
+
+=over 4
+
+=item B<app_fails APPNAME, TEST_NAME, REGEXP, LIST>
+
+C<app_fails> runs the C<openssl> sub-command B<APPNAME> with the command
+line arguments in LIST, expecting a non-zero (failure) exit status, as a
+test named B<TEST_NAME>.  If REGEXP is defined, it additionally checks, as
+a second test, that the stderr output of the command matches it.
+
+=back
+
+=cut
+
+sub app_fails {
+    my ($appname, $testtext, $re, @args) = @_;
+
+    my $stderr_file = "app_fails_stderr.txt";
+
+    with({ exit_checker => sub { return shift != 0; } },
+        sub {
+            ok(run(app(['openssl', $appname, @args], stderr => $stderr_file)),
+               $testtext);
+        });
+
+    if (defined $re) {
+        ok(slurp_file($stderr_file) =~ $re, "$testtext: stderr matches");
+    }
+    unlink($stderr_file) if -f $stderr_file;
+}
+
+=over 4
+
 =item B<cmdstr CODEREF, OPTS>

 C<cmdstr> takes a CODEREF from C<app> or C<test> and simply returns the