1 1.1 christos #! /usr/bin/env perl 2 1.1 christos # Copyright 2015-2022 The OpenSSL Project Authors. All Rights Reserved. 3 1.1 christos # 4 1.1 christos # Licensed under the Apache License 2.0 (the "License"). You may not use 5 1.1 christos # this file except in compliance with the License. You can obtain a copy 6 1.1 christos # in the file LICENSE in the source distribution or at 7 1.1 christos # https://www.openssl.org/source/license.html 8 1.1 christos 9 1.1 christos use strict; 10 1.1 christos use warnings; 11 1.1 christos 12 1.1 christos # Recognise VERBOSE aka V which is common on other projects. 13 1.1 christos # Additionally, recognise VERBOSE_FAILURE aka VF aka REPORT_FAILURES 14 1.1 christos # and recognise VERBOSE_FAILURE_PROGRESS aka VFP aka REPORT_FAILURES_PROGRESS. 15 1.1 christos BEGIN { 16 1.1 christos $ENV{HARNESS_VERBOSE} = "yes" if $ENV{VERBOSE} || $ENV{V}; 17 1.1 christos $ENV{HARNESS_VERBOSE_FAILURE} = "yes" 18 1.1 christos if $ENV{VERBOSE_FAILURE} || $ENV{VF} || $ENV{REPORT_FAILURES}; 19 1.1 christos $ENV{HARNESS_VERBOSE_FAILURE_PROGRESS} = "yes" 20 1.1 christos if ($ENV{VERBOSE_FAILURE_PROGRESS} || $ENV{VFP} 21 1.1 christos || $ENV{REPORT_FAILURES_PROGRESS}); 22 1.1 christos } 23 1.1 christos 24 1.1 christos use File::Spec::Functions qw/catdir catfile curdir abs2rel rel2abs/; 25 1.1 christos use File::Basename; 26 1.1 christos use FindBin; 27 1.1 christos use lib "$FindBin::Bin/../util/perl"; 28 1.1 christos use OpenSSL::Glob; 29 1.1 christos 30 1.1 christos my $srctop = $ENV{SRCTOP} || $ENV{TOP}; 31 1.1 christos my $bldtop = $ENV{BLDTOP} || $ENV{TOP}; 32 1.1 christos my $recipesdir = catdir($srctop, "test", "recipes"); 33 1.1 christos my $libdir = rel2abs(catdir($srctop, "util", "perl")); 34 1.1 christos my $jobs = $ENV{HARNESS_JOBS} // 1; 35 1.1 christos 36 1.1 christos $ENV{OPENSSL_CONF} = rel2abs(catfile($srctop, "apps", "openssl.cnf")); 37 1.1 christos $ENV{OPENSSL_CONF_INCLUDE} = rel2abs(catdir($bldtop, "test")); 38 1.1 christos $ENV{OPENSSL_MODULES} = rel2abs(catdir($bldtop, "providers")); 39 1.1 christos $ENV{OPENSSL_ENGINES} = rel2abs(catdir($bldtop, "engines")); 40 1.1 christos $ENV{CTLOG_FILE} = rel2abs(catfile($srctop, "test", "ct", "log_list.cnf")); 41 1.1 christos 42 1.1 christos # On platforms that support this, this will ensure malloc returns data that is 43 1.1 christos # set to a non-zero value. Can be helpful for detecting uninitialized reads in 44 1.1 christos # some situations. 45 1.1 christos $ENV{'MALLOC_PERTURB_'} = '128' if !defined $ENV{'MALLOC_PERTURB_'}; 46 1.1 christos 47 1.1 christos my %tapargs = 48 1.1 christos ( verbosity => $ENV{HARNESS_VERBOSE} ? 1 : 0, 49 1.1 christos lib => [ $libdir ], 50 1.1 christos switches => '-w', 51 1.1 christos merge => 1, 52 1.1 christos timer => $ENV{HARNESS_TIMER} ? 1 : 0, 53 1.1 christos ); 54 1.1 christos 55 1.1 christos if ($jobs > 1) { 56 1.1 christos if ($ENV{HARNESS_VERBOSE}) { 57 1.1 christos print "Warning: HARNESS_JOBS > 1 ignored with HARNESS_VERBOSE\n"; 58 1.1 christos } else { 59 1.1 christos $tapargs{jobs} = $jobs; 60 1.1 christos print "Using HARNESS_JOBS=$jobs\n"; 61 1.1 christos } 62 1.1 christos } 63 1.1 christos 64 1.1 christos # Additional OpenSSL special TAP arguments. Because we can't pass them via 65 1.1 christos # TAP::Harness->new(), they will be accessed directly, see the 66 1.1 christos # TAP::Parser::OpenSSL implementation further down 67 1.1 christos my %openssl_args = (); 68 1.1 christos 69 1.1 christos $openssl_args{'failure_verbosity'} = $ENV{HARNESS_VERBOSE} ? 0 : 70 1.1 christos $ENV{HARNESS_VERBOSE_FAILURE_PROGRESS} ? 2 : 71 1.1 christos 1; # $ENV{HARNESS_VERBOSE_FAILURE} 72 1.1 christos print "Warning: HARNESS_VERBOSE overrides HARNESS_VERBOSE_FAILURE*\n" 73 1.1 christos if ($ENV{HARNESS_VERBOSE} && ($ENV{HARNESS_VERBOSE_FAILURE} 74 1.1 christos || $ENV{HARNESS_VERBOSE_FAILURE_PROGRESS})); 75 1.1 christos print "Warning: HARNESS_VERBOSE_FAILURE_PROGRESS overrides HARNESS_VERBOSE_FAILURE\n" 76 1.1 christos if ($ENV{HARNESS_VERBOSE_FAILURE_PROGRESS} && $ENV{HARNESS_VERBOSE_FAILURE}); 77 1.1 christos 78 1.1 christos my $outfilename = $ENV{HARNESS_TAP_COPY}; 79 1.1 christos open $openssl_args{'tap_copy'}, ">$outfilename" 80 1.1 christos or die "Trying to create $outfilename: $!\n" 81 1.1 christos if defined $outfilename; 82 1.1 christos 83 1.1 christos my @alltests = find_matching_tests("*"); 84 1.1 christos my %tests = (); 85 1.1 christos 86 1.1 christos sub reorder { 87 1.1 christos my $key = pop; 88 1.1 christos 89 1.1 christos # for parallel test runs, do slow tests first 90 1.1 christos if ($jobs > 1 && $key =~ m/test_ssl_new|test_fuzz/) { 91 1.1 christos $key =~ s/(\d+)-/01-/; 92 1.1 christos } 93 1.1 christos return $key; 94 1.1 christos } 95 1.1 christos 96 1.1 christos my $initial_arg = 1; 97 1.1 christos foreach my $arg (@ARGV ? @ARGV : ('alltests')) { 98 1.1 christos if ($arg eq 'list') { 99 1.1 christos foreach (@alltests) { 100 1.1 christos (my $x = basename($_)) =~ s|^[0-9][0-9]-(.*)\.t$|$1|; 101 1.1 christos print $x,"\n"; 102 1.1 christos } 103 1.1 christos exit 0; 104 1.1 christos } 105 1.1 christos if ($arg eq 'alltests') { 106 1.1 christos warn "'alltests' encountered, ignoring everything before that...\n" 107 1.1 christos unless $initial_arg; 108 1.1 christos %tests = map { $_ => 1 } @alltests; 109 1.1 christos } elsif ($arg =~ m/^(-?)(.*)/) { 110 1.1 christos my $sign = $1; 111 1.1 christos my $test = $2; 112 1.1 christos my @matches = find_matching_tests($test); 113 1.1 christos 114 1.1 christos # If '-foo' is the first arg, it's short for 'alltests -foo' 115 1.1 christos if ($sign eq '-' && $initial_arg) { 116 1.1 christos %tests = map { $_ => 1 } @alltests; 117 1.1 christos } 118 1.1 christos 119 1.1 christos if (scalar @matches == 0) { 120 1.1 christos warn "Test $test found no match, skipping ", 121 1.1 christos ($sign eq '-' ? "removal" : "addition"), 122 1.1 christos "...\n"; 123 1.1 christos } else { 124 1.1 christos foreach $test (@matches) { 125 1.1 christos if ($sign eq '-') { 126 1.1 christos delete $tests{$test}; 127 1.1 christos } else { 128 1.1 christos $tests{$test} = 1; 129 1.1 christos } 130 1.1 christos } 131 1.1 christos } 132 1.1 christos } else { 133 1.1 christos warn "I don't know what '$arg' is about, ignoring...\n"; 134 1.1 christos } 135 1.1 christos 136 1.1 christos $initial_arg = 0; 137 1.1 christos } 138 1.1 christos 139 1.1 christos # prep recipes are mandatory and need to be always run first 140 1.1 christos my @preps = glob(catfile($recipesdir,"00-prep_*.t")); 141 1.1 christos foreach my $test (@preps) { 142 1.1 christos delete $tests{$test}; 143 1.1 christos } 144 1.1 christos 145 1.1 christos sub find_matching_tests { 146 1.1 christos my ($glob) = @_; 147 1.1 christos 148 1.1 christos if ($glob =~ m|^[\d\[\]\?\-]+$|) { 149 1.1 christos return glob(catfile($recipesdir,"$glob-*.t")); 150 1.1 christos } 151 1.1 christos 152 1.1 christos return glob(catfile($recipesdir,"*-$glob.t")); 153 1.1 christos } 154 1.1 christos 155 1.1 christos # The following is quite a bit of hackery to adapt to both TAP::Harness 156 1.1 christos # and Test::Harness, depending on what's available. 157 1.1 christos # The TAP::Harness hack allows support for HARNESS_VERBOSE_FAILURE* and 158 1.1 christos # HARNESS_TAP_COPY, while the Test::Harness hack can't, because the pre 159 1.1 christos # TAP::Harness Test::Harness simply doesn't have support for this sort of 160 1.1 christos # thing. 161 1.1 christos # 162 1.1 christos # We use eval to avoid undue interruption if TAP::Harness isn't present. 163 1.1 christos 164 1.1 christos my $package; 165 1.1 christos my $eres; 166 1.1 christos 167 1.1 christos $eres = eval { 168 1.1 christos package TAP::Parser::OpenSSL; 169 1.1 christos use parent -norequire, 'TAP::Parser'; 170 1.1 christos require TAP::Parser; 171 1.1 christos 172 1.1 christos sub new { 173 1.1 christos my $class = shift; 174 1.1 christos my %opts = %{ shift() }; 175 1.1 christos my $failure_verbosity = $openssl_args{failure_verbosity}; 176 1.1 christos my @plans = (); # initial level, no plan yet 177 1.1 christos my $output_buffer = ""; 178 1.1 christos my $in_indirect = 0; 179 1.1 christos 180 1.1 christos # We rely heavily on perl closures to make failure verbosity work 181 1.1 christos # We need to do so, because there's no way to safely pass extra 182 1.1 christos # objects down all the way to the TAP::Parser::Result object 183 1.1 christos my @failure_output = (); 184 1.1 christos my %callbacks = (); 185 1.1 christos if ($failure_verbosity > 0 || defined $openssl_args{tap_copy}) { 186 1.1 christos $callbacks{ALL} = sub { # on each line of test output 187 1.1 christos my $self = shift; 188 1.1 christos my $fh = $openssl_args{tap_copy}; 189 1.1 christos print $fh $self->as_string, "\n" 190 1.1 christos if defined $fh; 191 1.1 christos 192 1.1 christos my $failure_verbosity = $openssl_args{failure_verbosity}; 193 1.1 christos if ($failure_verbosity > 0) { 194 1.1 christos my $is_plan = $self->is_plan; 195 1.1 christos my $tests_planned = $is_plan && $self->tests_planned; 196 1.1 christos my $is_test = $self->is_test; 197 1.1 christos my $is_ok = $is_test && $self->is_ok; 198 1.1 christos 199 1.1 christos # workaround for parser not coping with sub-test indentation 200 1.1 christos if ($self->is_unknown) { 201 1.1 christos my $level = $#plans; 202 1.1 christos my $indent = $level < 0 ? "" : " " x ($level * 4); 203 1.1 christos 204 1.1 christos ($is_plan, $tests_planned) = (1, $1) 205 1.1 christos if ($self->as_string =~ m/^$indent 1\.\.(\d+)/); 206 1.1 christos ($is_test, $is_ok) = (1, !$1) 207 1.1 christos if ($self->as_string =~ m/^$indent(not )?ok /); 208 1.1 christos } 209 1.1 christos 210 1.1 christos if ($is_plan) { 211 1.1 christos push @plans, $tests_planned; 212 1.1 christos $output_buffer = ""; # ignore comments etc. until plan 213 1.1 christos } elsif ($is_test) { # result of a test 214 1.1 christos pop @plans if @plans && --($plans[-1]) <= 0; 215 1.1 christos if ($output_buffer =~ /.*Indirect leak of.*/ == 1) { 216 1.1 christos my @asan_array = split("\n", $output_buffer); 217 1.1 christos foreach (@asan_array) { 218 1.1 christos if ($_ =~ /.*Indirect leak of.*/ == 1) { 219 1.1 christos if ($in_indirect != 1) { 220 1.1 christos print "::group::Indirect Leaks\n"; 221 1.1 christos } 222 1.1 christos $in_indirect = 1; 223 1.1 christos } 224 1.1 christos print "$_\n"; 225 1.1 christos if ($_ =~ /.*Indirect leak of.*/ != 1) { 226 1.1 christos if ($_ =~ /^ #.*/ == 0) { 227 1.1 christos if ($in_indirect != 0) { 228 1.1 christos print "\n::endgroup::\n"; 229 1.1 christos } 230 1.1 christos $in_indirect = 0; 231 1.1 christos } 232 1.1 christos } 233 1.1 christos } 234 1.1 christos } else { 235 1.1 christos print $output_buffer if !$is_ok; 236 1.1 christos } 237 1.1 christos print "\n".$self->as_string 238 1.1 christos if !$is_ok || $failure_verbosity == 2; 239 1.1 christos print "\n# ------------------------------------------------------------------------------" if !$is_ok; 240 1.1 christos $output_buffer = ""; 241 1.1 christos } elsif ($self->as_string ne "") { 242 1.1 christos # typically is_comment or is_unknown 243 1.1 christos $output_buffer .= "\n".$self->as_string; 244 1.1 christos } 245 1.1 christos } 246 1.1 christos } 247 1.1 christos } 248 1.1 christos 249 1.1 christos if ($failure_verbosity > 0) { 250 1.1 christos $callbacks{EOF} = sub { 251 1.1 christos my $self = shift; 252 1.1 christos 253 1.1 christos # We know we are a TAP::Parser::Aggregator object 254 1.1 christos if (scalar $self->failed > 0 && @failure_output) { 255 1.1 christos # We add an extra empty line, because in the case of a 256 1.1 christos # progress counter, we're still at the end of that progress 257 1.1 christos # line. 258 1.1 christos print $_, "\n" foreach (("", @failure_output)); 259 1.1 christos } 260 1.1 christos # Echo any trailing comments etc. 261 1.1 christos print "$output_buffer"; 262 1.1 christos }; 263 1.1 christos } 264 1.1 christos 265 1.1 christos if (keys %callbacks) { 266 1.1 christos # If %opts already has a callbacks element, the order here 267 1.1 christos # ensures we do not override it 268 1.1 christos %opts = ( callbacks => { %callbacks }, %opts ); 269 1.1 christos } 270 1.1 christos 271 1.1 christos return $class->SUPER::new({ %opts }); 272 1.1 christos } 273 1.1 christos 274 1.1 christos package TAP::Harness::OpenSSL; 275 1.1 christos use parent -norequire, 'TAP::Harness'; 276 1.1 christos require TAP::Harness; 277 1.1 christos 278 1.1 christos package main; 279 1.1 christos 280 1.1 christos $tapargs{parser_class} = "TAP::Parser::OpenSSL"; 281 1.1 christos $package = 'TAP::Harness::OpenSSL'; 282 1.1 christos }; 283 1.1 christos 284 1.1 christos unless (defined $eres) { 285 1.1 christos $eres = eval { 286 1.1 christos # Fake TAP::Harness in case it's not loaded 287 1.1 christos package TAP::Harness::fake; 288 1.1 christos use parent 'Test::Harness'; 289 1.1 christos 290 1.1 christos sub new { 291 1.1 christos my $class = shift; 292 1.1 christos my %args = %{ shift() }; 293 1.1 christos 294 1.1 christos return bless { %args }, $class; 295 1.1 christos } 296 1.1 christos 297 1.1 christos sub runtests { 298 1.1 christos my $self = shift; 299 1.1 christos 300 1.1 christos # Pre TAP::Harness Test::Harness doesn't support [ filename, name ] 301 1.1 christos # elements, so convert such elements to just be the filename 302 1.1 christos my @args = map { ref($_) eq 'ARRAY' ? $_->[0] : $_ } @_; 303 1.1 christos 304 1.1 christos my @switches = (); 305 1.1 christos if ($self->{switches}) { 306 1.1 christos push @switches, $self->{switches}; 307 1.1 christos } 308 1.1 christos if ($self->{lib}) { 309 1.1 christos foreach (@{$self->{lib}}) { 310 1.1 christos my $l = $_; 311 1.1 christos 312 1.1 christos # It seems that $switches is getting interpreted with 'eval' 313 1.1 christos # or something like that, and that we need to take care of 314 1.1 christos # backslashes or they will disappear along the way. 315 1.1 christos $l =~ s|\\|\\\\|g if $^O eq "MSWin32"; 316 1.1 christos push @switches, "-I$l"; 317 1.1 christos } 318 1.1 christos } 319 1.1 christos 320 1.1 christos $Test::Harness::switches = join(' ', @switches); 321 1.1 christos Test::Harness::runtests(@args); 322 1.1 christos } 323 1.1 christos 324 1.1 christos package main; 325 1.1 christos $package = 'TAP::Harness::fake'; 326 1.1 christos }; 327 1.1 christos } 328 1.1 christos 329 1.1 christos unless (defined $eres) { 330 1.1 christos print $@,"\n" if $@; 331 1.1 christos print $!,"\n" if $!; 332 1.1 christos exit 127; 333 1.1 christos } 334 1.1 christos 335 1.1 christos my $harness = $package->new(\%tapargs); 336 1.1 christos my $ret = 337 1.1 christos $harness->runtests(map { [ abs2rel($_, rel2abs(curdir())), basename($_) ] } 338 1.1 christos @preps); 339 1.1 christos 340 1.1 christos if (ref($ret) ne "TAP::Parser::Aggregator" || !$ret->has_errors) { 341 1.1 christos $ret = 342 1.1 christos $harness->runtests(map { [ abs2rel($_, rel2abs(curdir())), basename($_) ] } 343 1.1 christos sort { reorder($a) cmp reorder($b) } keys %tests); 344 1.1 christos } 345 1.1 christos 346 1.1 christos # If this is a TAP::Parser::Aggregator, $ret->has_errors is the count of 347 1.1 christos # tests that failed. We don't bother with that exact number, just exit 348 1.1 christos # with an appropriate exit code when it isn't zero. 349 1.1 christos if (ref($ret) eq "TAP::Parser::Aggregator") { 350 1.1 christos exit 0 unless $ret->has_errors; 351 1.1 christos exit 1 unless $^O eq 'VMS'; 352 1.1 christos # On VMS, perl converts an exit 1 to SS$_ABORT (%SYSTEM-F-ABORT), which 353 1.1 christos # is a bit harsh. As per perl recommendations, we explicitly use the 354 1.1 christos # same VMS status code as typical C programs would for exit(1), except 355 1.1 christos # we set the error severity rather than success. 356 1.1 christos # Ref: https://perldoc.perl.org/perlport#exit 357 1.1 christos # https://perldoc.perl.org/perlvms#$? 358 1.1 christos exit 0x35a000 # C facility code 359 1.1 christos + 8 # 1 << 3 (to make space for the 3 severity bits) 360 1.1 christos + 2 # severity: E(rror) 361 1.1 christos + 0x10000000; # bit 28 set => the shell stays silent 362 1.1 christos } 363 1.1 christos 364 1.1 christos # If this isn't a TAP::Parser::Aggregator, it's the pre-TAP test harness, 365 1.1 christos # which simply dies at the end if any test failed, so we don't need to bother 366 1.1 christos # with any exit code in that case. 367