1#!/usr/bin/env perl 2#*************************************************************************** 3# _ _ ____ _ 4# Project ___| | | | _ \| | 5# / __| | | | |_) | | 6# | (__| |_| | _ <| |___ 7# \___|\___/|_| \_\_____| 8# 9# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 10# 11# This software is licensed as described in the file COPYING, which 12# you should have received as part of this distribution. The terms 13# are also available at https://curl.se/docs/copyright.html. 14# 15# You may opt to use, copy, modify, merge, publish, distribute and/or sell 16# copies of the Software, and permit persons to whom the Software is 17# furnished to do so, under the terms of the COPYING file. 18# 19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 20# KIND, either express or implied. 21# 22# SPDX-License-Identifier: curl 23# 24########################################################################### 25 26# For documentation, run `man ./runtests.1` and see README.md. 27 28# Experimental hooks are available to run tests remotely on machines that 29# are able to run curl but are unable to run the test harness. 30# The following sections need to be modified: 31# 32# $HOSTIP, $HOST6IP - Set to the address of the host running the test suite 33# $CLIENTIP, $CLIENT6IP - Set to the address of the host running curl 34# runclient, runclientoutput - Modify to copy all the files in the log/ 35# directory to the system running curl, run the given command remotely 36# and save the return code or returned stdout (respectively), then 37# copy all the files from the remote system's log/ directory back to 38# the host running the test suite. This can be done a few ways, such 39# as using scp & ssh, rsync & telnet, or using a NFS shared directory 40# and ssh. 41# 42# 'make && make test' needs to be done on both machines before making the 43# above changes and running runtests.pl manually. In the shared NFS case, 44# the contents of the tests/server/ directory must be from the host 45# running the test suite, while the rest must be from the host running curl. 46# 47# Note that even with these changes a number of tests will still fail (mainly 48# to do with cookies, those that set environment variables, or those that 49# do more than touch the file system in a <precheck> or <postcheck> 50# section). These can be added to the $TESTCASES line below, 51# e.g. $TESTCASES="!8 !31 !63 !cookies..." 52# 53# Finally, to properly support -g and -n, checktestcmd needs to change 54# to check the remote system's PATH, and the places in the code where 55# the curl binary is read directly to determine its type also need to be 56# fixed. As long as the -g option is never given, and the -n is always 57# given, this won't be a problem. 58 59use strict; 60# Promote all warnings to fatal 61use warnings FATAL => 'all'; 62use 5.006; 63use POSIX qw(strftime); 64 65# These should be the only variables that might be needed to get edited: 66 67BEGIN { 68 # Define srcdir to the location of the tests source directory. This is 69 # usually set by the Makefile, but for out-of-tree builds with direct 70 # invocation of runtests.pl, it may not be set. 71 if(!defined $ENV{'srcdir'}) { 72 use File::Basename; 73 $ENV{'srcdir'} = dirname(__FILE__); 74 } 75 push(@INC, $ENV{'srcdir'}); 76 # run time statistics needs Time::HiRes 77 eval { 78 no warnings "all"; 79 require Time::HiRes; 80 import Time::HiRes qw( time ); 81 } 82} 83 84use Digest::MD5 qw(md5); 85use List::Util 'sum'; 86use I18N::Langinfo qw(langinfo CODESET); 87 88use pathhelp qw( 89 exe_ext 90 sys_native_current_path 91 ); 92use processhelp qw( 93 portable_sleep 94 ); 95 96use appveyor; 97use azure; 98use getpart; # array functions 99use servers; 100use valgrind; # valgrind report parser 101use globalconfig; 102use runner; 103use testutil; 104 105my %custom_skip_reasons; 106 107my $ACURL=$VCURL; # what curl binary to use to talk to APIs (relevant for CI) 108 # ACURL is handy to set to the system one for reliability 109my $CURLCONFIG="../curl-config"; # curl-config from current build 110 111# Normally, all test cases should be run, but at times it is handy to 112# simply run a particular one: 113my $TESTCASES="all"; 114 115# To run specific test cases, set them like: 116# $TESTCASES="1 2 3 7 8"; 117 118####################################################################### 119# No variables below this point should need to be modified 120# 121 122my $libtool; 123my $repeat = 0; 124 125my $start; # time at which testing started 126my $args; # command-line arguments 127 128my $uname_release = `uname -r`; 129my $is_wsl = $uname_release =~ /Microsoft$/; 130 131my $http_ipv6; # set if HTTP server has IPv6 support 132my $http_unix; # set if HTTP server has Unix sockets support 133my $ftp_ipv6; # set if FTP server has IPv6 support 134 135my $resolver; # name of the resolver backend (for human presentation) 136 137my %skipped; # skipped{reason}=counter, reasons for skip 138my @teststat; # teststat[testnum]=reason, reasons for skip 139my %disabled_keywords; # key words of tests to skip 140my %ignored_keywords; # key words of tests to ignore results 141my %enabled_keywords; # key words of tests to run 142my %disabled; # disabled test cases 143my %ignored; # ignored results of test cases 144my %ignoretestcodes; # if test results are to be ignored 145 146my $passedign; # tests passed with results ignored 147 148my $timestats; # time stamping and stats generation 149my $fullstats; # show time stats for every single test 150my %timeprepini; # timestamp for each test preparation start 151my %timesrvrini; # timestamp for each test required servers verification start 152my %timesrvrend; # timestamp for each test required servers verification end 153my %timetoolini; # timestamp for each test command run starting 154my %timetoolend; # timestamp for each test command run stopping 155my %timesrvrlog; # timestamp for each test server logs lock removal 156my %timevrfyend; # timestamp for each test result verification end 157my $globalabort; # flag signalling program abort 158 159# values for $singletest_state 160use constant { 161 ST_INIT => 0, 162 ST_CLEARLOCKS => 1, 163 ST_INITED => 2, 164 ST_PREPROCESS => 3, 165 ST_RUN => 4, 166}; 167my %singletest_state; # current state of singletest() by runner ID 168my %singletest_logs; # log messages while in singletest array ref by runner 169my $singletest_bufferedrunner; # runner ID which is buffering logs 170my %runnerids; # runner IDs by number 171my @runnersidle; # runner IDs idle and ready to execute a test 172my %countforrunner; # test count by runner ID 173my %runnersrunning; # tests currently running by runner ID 174 175####################################################################### 176# variables that command line options may set 177# 178my $short; 179my $no_debuginfod; 180my $keepoutfiles; # keep stdout and stderr files after tests 181my $clearlocks; # force removal of files by killing locking processes 182my $postmortem; # display detailed info about failed tests 183my $run_disabled; # run the specific tests even if listed in DISABLED 184my $scrambleorder; 185my $jobs = 0; 186 187# Azure Pipelines specific variables 188my $AZURE_RUN_ID = 0; 189my $AZURE_RESULT_ID = 0; 190 191####################################################################### 192# logmsg is our general message logging subroutine. 193# 194sub logmsg { 195 if($singletest_bufferedrunner) { 196 # Logs are currently being buffered 197 return singletest_logmsg(@_); 198 } 199 for(@_) { 200 my $line = $_; 201 if(!$line) { 202 next; 203 } 204 if ($is_wsl) { 205 # use \r\n for WSL shell 206 $line =~ s/\r?\n$/\r\n/g; 207 } 208 print "$line"; 209 } 210} 211 212####################################################################### 213# enable logmsg buffering for the given runner ID 214# 215sub logmsg_bufferfortest { 216 my ($runnerid)=@_; 217 if($jobs) { 218 # Only enable buffering in multiprocess mode 219 $singletest_bufferedrunner = $runnerid; 220 } 221} 222####################################################################### 223# Store a log message in a buffer for this test 224# The messages can then be displayed all at once at the end of the test 225# which prevents messages from different tests from being interleaved. 226sub singletest_logmsg { 227 if(!exists $singletest_logs{$singletest_bufferedrunner}) { 228 # initialize to a reference to an empty anonymous array 229 $singletest_logs{$singletest_bufferedrunner} = []; 230 } 231 my $logsref = $singletest_logs{$singletest_bufferedrunner}; 232 push @$logsref, @_; 233} 234 235####################################################################### 236# Stop buffering log messages, but don't touch them 237sub singletest_unbufferlogs { 238 undef $singletest_bufferedrunner; 239} 240 241####################################################################### 242# Clear the buffered log messages & stop buffering after returning them 243sub singletest_dumplogs { 244 if(!defined $singletest_bufferedrunner) { 245 # probably not multiprocess mode and logs weren't buffered 246 return undef; 247 } 248 my $logsref = $singletest_logs{$singletest_bufferedrunner}; 249 my $msg = join("", @$logsref); 250 delete $singletest_logs{$singletest_bufferedrunner}; 251 singletest_unbufferlogs(); 252 return $msg; 253} 254 255sub catch_zap { 256 my $signame = shift; 257 print "runtests.pl received SIG$signame, exiting\r\n"; 258 $globalabort = 1; 259} 260$SIG{INT} = \&catch_zap; 261$SIG{TERM} = \&catch_zap; 262 263sub catch_usr1 { 264 print "runtests.pl internal state:\r\n"; 265 print scalar(%runnersrunning) . " busy test runner(s) of " . scalar(keys %runnerids) . "\r\n"; 266 foreach my $rid (sort(keys(%runnersrunning))) { 267 my $runnernum = "unknown"; 268 foreach my $rnum (keys %runnerids) { 269 if($runnerids{$rnum} == $rid) { 270 $runnernum = $rnum; 271 last; 272 } 273 } 274 print "Runner $runnernum (id $rid) running test $runnersrunning{$rid} in state $singletest_state{$rid}\r\n"; 275 } 276} 277 278eval { 279 # some msys2 perl versions don't define SIGUSR1 280 $SIG{USR1} = \&catch_usr1; 281}; 282$SIG{PIPE} = 'IGNORE'; # these errors are captured in the read/write calls 283 284########################################################################## 285# Clear all possible '*_proxy' environment variables for various protocols 286# to prevent them to interfere with our testing! 287 288foreach my $protocol (('ftp', 'http', 'ftps', 'https', 'no', 'all')) { 289 my $proxy = "${protocol}_proxy"; 290 # clear lowercase version 291 delete $ENV{$proxy} if($ENV{$proxy}); 292 # clear uppercase version 293 delete $ENV{uc($proxy)} if($ENV{uc($proxy)}); 294} 295 296# make sure we don't get affected by other variables that control our 297# behavior 298 299delete $ENV{'SSL_CERT_DIR'} if($ENV{'SSL_CERT_DIR'}); 300delete $ENV{'SSL_CERT_PATH'} if($ENV{'SSL_CERT_PATH'}); 301delete $ENV{'CURL_CA_BUNDLE'} if($ENV{'CURL_CA_BUNDLE'}); 302 303# provide defaults from our config file for ENV vars not explicitly 304# set by the caller 305if (open(my $fd, "<", "config")) { 306 while(my $line = <$fd>) { 307 next if ($line =~ /^#/); 308 chomp $line; 309 my ($name, $val) = split(/\s*:\s*/, $line, 2); 310 $ENV{$name} = $val if(!$ENV{$name}); 311 } 312 close($fd); 313} 314 315# Check if we have nghttpx available and if it talks http/3 316my $nghttpx_h3 = 0; 317if (!$ENV{"NGHTTPX"}) { 318 $ENV{"NGHTTPX"} = checktestcmd("nghttpx"); 319} 320if ($ENV{"NGHTTPX"}) { 321 my $cmd = "\"$ENV{'NGHTTPX'}\" -v 2>$dev_null"; 322 my $nghttpx_version=join(' ', `$cmd`); 323 $nghttpx_h3 = $nghttpx_version =~ /nghttp3\//; 324 chomp $nghttpx_h3; 325} 326 327 328####################################################################### 329# Get the list of tests that the tests/data/Makefile.am knows about! 330# 331my $disttests = ""; 332sub get_disttests { 333 # If a non-default $TESTDIR is being used there may not be any 334 # Makefile.am in which case there's nothing to do. 335 open(my $dh, "<", "$TESTDIR/Makefile.am") or return; 336 while(<$dh>) { 337 chomp $_; 338 if(($_ =~ /^#/) ||($_ !~ /test/)) { 339 next; 340 } 341 $disttests .= $_; 342 } 343 close($dh); 344} 345 346 347####################################################################### 348# Remove all files in the specified directory 349# 350sub cleardir { 351 my $dir = $_[0]; 352 my $done = 1; # success 353 my $file; 354 355 # Get all files 356 opendir(my $dh, $dir) || 357 return 0; # can't open dir 358 while($file = readdir($dh)) { 359 # Don't clear the $PIDDIR or $LOCKDIR since those need to live beyond 360 # one test 361 if(($file !~ /^(\.|\.\.)\z/) && 362 "$file" ne $PIDDIR && "$file" ne $LOCKDIR) { 363 if(-d "$dir/$file") { 364 if(!cleardir("$dir/$file")) { 365 $done = 0; 366 } 367 if(!rmdir("$dir/$file")) { 368 $done = 0; 369 } 370 } 371 else { 372 # Ignore stunnel since we cannot do anything about its locks 373 if(!unlink("$dir/$file") && "$file" !~ /_stunnel\.log$/) { 374 $done = 0; 375 } 376 } 377 } 378 } 379 closedir $dh; 380 return $done; 381} 382 383 384####################################################################### 385# Given two array references, this function will store them in two temporary 386# files, run 'diff' on them, store the result and return the diff output! 387sub showdiff { 388 my ($logdir, $firstref, $secondref)=@_; 389 390 my $file1="$logdir/check-generated"; 391 my $file2="$logdir/check-expected"; 392 393 open(my $temp, ">", "$file1") || die "Failure writing diff file"; 394 for(@$firstref) { 395 my $l = $_; 396 $l =~ s/\r/[CR]/g; 397 $l =~ s/\n/[LF]/g; 398 $l =~ s/([^\x20-\x7f])/sprintf "%%%02x", ord $1/eg; 399 print $temp $l; 400 print $temp "\n"; 401 } 402 close($temp) || die "Failure writing diff file"; 403 404 open($temp, ">", "$file2") || die "Failure writing diff file"; 405 for(@$secondref) { 406 my $l = $_; 407 $l =~ s/\r/[CR]/g; 408 $l =~ s/\n/[LF]/g; 409 $l =~ s/([^\x20-\x7f])/sprintf "%%%02x", ord $1/eg; 410 print $temp $l; 411 print $temp "\n"; 412 } 413 close($temp) || die "Failure writing diff file"; 414 my @out = `diff -u $file2 $file1 2>$dev_null`; 415 416 if(!$out[0]) { 417 @out = `diff -c $file2 $file1 2>$dev_null`; 418 } 419 420 return @out; 421} 422 423 424####################################################################### 425# compare test results with the expected output, we might filter off 426# some pattern that is allowed to differ, output test results 427# 428sub compare { 429 my ($runnerid, $testnum, $testname, $subject, $firstref, $secondref)=@_; 430 431 my $result = compareparts($firstref, $secondref); 432 433 if($result) { 434 # timestamp test result verification end 435 $timevrfyend{$testnum} = Time::HiRes::time(); 436 437 if(!$short) { 438 logmsg "\n $testnum: $subject FAILED:\n"; 439 my $logdir = getrunnerlogdir($runnerid); 440 logmsg showdiff($logdir, $firstref, $secondref); 441 } 442 elsif(!$automakestyle) { 443 logmsg "FAILED\n"; 444 } 445 else { 446 # automakestyle 447 logmsg "FAIL: $testnum - $testname - $subject\n"; 448 } 449 } 450 return $result; 451} 452 453####################################################################### 454# Numeric-sort words in a string 455sub numsortwords { 456 my ($string)=@_; 457 return join(' ', sort { $a <=> $b } split(' ', $string)); 458} 459 460####################################################################### 461# Parse and store the protocols in curl's Protocols: line 462sub parseprotocols { 463 my ($line)=@_; 464 465 @protocols = split(' ', lc($line)); 466 467 # Generate a "proto-ipv6" version of each protocol to match the 468 # IPv6 <server> name and a "proto-unix" to match the variant which 469 # uses Unix domain sockets. This works even if support isn't 470 # compiled in because the <features> test will fail. 471 push @protocols, map(("$_-ipv6", "$_-unix"), @protocols); 472 473 # 'http-proxy' is used in test cases to do CONNECT through 474 push @protocols, 'http-proxy'; 475 476 # 'none' is used in test cases to mean no server 477 push @protocols, 'none'; 478} 479 480 481####################################################################### 482# Check & display information about curl and the host the test suite runs on. 483# Information to do with servers is displayed in displayserverfeatures, after 484# the server initialization is performed. 485sub checksystemfeatures { 486 my $proto; 487 my $feat; 488 my $curl; 489 my $libcurl; 490 my $versretval; 491 my $versnoexec; 492 my @version=(); 493 my @disabled; 494 my $dis = ""; 495 496 my $curlverout="$LOGDIR/curlverout.log"; 497 my $curlvererr="$LOGDIR/curlvererr.log"; 498 my $versioncmd=shell_quote($CURL) . " --version 1>$curlverout 2>$curlvererr"; 499 500 unlink($curlverout); 501 unlink($curlvererr); 502 503 $versretval = runclient($versioncmd); 504 $versnoexec = $!; 505 506 my $current_time = int(time()); 507 $ENV{'SOURCE_DATE_EPOCH'} = $current_time; 508 $DATE = strftime "%Y-%m-%d", gmtime($current_time); 509 510 open(my $versout, "<", "$curlverout"); 511 @version = <$versout>; 512 close($versout); 513 514 open(my $disabledh, "-|", "server/disabled".exe_ext('TOOL')); 515 @disabled = <$disabledh>; 516 close($disabledh); 517 518 if($disabled[0]) { 519 s/[\r\n]//g for @disabled; 520 $dis = join(", ", @disabled); 521 } 522 523 $resolver="stock"; 524 for(@version) { 525 chomp; 526 527 if($_ =~ /^curl ([^ ]*)/) { 528 $curl = $_; 529 $CURLVERSION = $1; 530 $CURLVERNUM = $CURLVERSION; 531 $CURLVERNUM =~ s/^([0-9.]+)(.*)/$1/; # leading dots and numbers 532 $curl =~ s/^(.*)(libcurl.*)/$1/g || die "Failure determining curl binary version"; 533 534 $libcurl = $2; 535 if($curl =~ /linux|bsd|solaris/) { 536 # system support LD_PRELOAD; may be disabled later 537 $feature{"ld_preload"} = 1; 538 } 539 if($curl =~ /win32|Windows|mingw(32|64)/) { 540 # This is a Windows MinGW build or native build, we need to use 541 # Windows-style path. 542 $pwd = sys_native_current_path(); 543 $feature{"win32"} = 1; 544 } 545 if ($libcurl =~ /\s(winssl|schannel)\b/i) { 546 $feature{"Schannel"} = 1; 547 $feature{"SSLpinning"} = 1; 548 } 549 elsif ($libcurl =~ /\sopenssl\b/i) { 550 $feature{"OpenSSL"} = 1; 551 $feature{"SSLpinning"} = 1; 552 } 553 elsif ($libcurl =~ /\sgnutls\b/i) { 554 $feature{"GnuTLS"} = 1; 555 $feature{"SSLpinning"} = 1; 556 } 557 elsif ($libcurl =~ /\srustls-ffi\b/i) { 558 $feature{"rustls"} = 1; 559 } 560 elsif ($libcurl =~ /\swolfssl\b/i) { 561 $feature{"wolfssl"} = 1; 562 $feature{"SSLpinning"} = 1; 563 } 564 elsif ($libcurl =~ /\sbearssl\b/i) { 565 $feature{"bearssl"} = 1; 566 } 567 elsif ($libcurl =~ /\ssecuretransport\b/i) { 568 $feature{"sectransp"} = 1; 569 $feature{"SSLpinning"} = 1; 570 } 571 elsif ($libcurl =~ /\sBoringSSL\b/i) { 572 # OpenSSL compatible API 573 $feature{"OpenSSL"} = 1; 574 $feature{"SSLpinning"} = 1; 575 } 576 elsif ($libcurl =~ /\slibressl\b/i) { 577 # OpenSSL compatible API 578 $feature{"OpenSSL"} = 1; 579 $feature{"SSLpinning"} = 1; 580 } 581 elsif ($libcurl =~ /\squictls\b/i) { 582 # OpenSSL compatible API 583 $feature{"OpenSSL"} = 1; 584 $feature{"SSLpinning"} = 1; 585 } 586 elsif ($libcurl =~ /\smbedTLS\b/i) { 587 $feature{"mbedtls"} = 1; 588 $feature{"SSLpinning"} = 1; 589 } 590 if ($libcurl =~ /ares/i) { 591 $feature{"c-ares"} = 1; 592 $resolver="c-ares"; 593 } 594 if ($libcurl =~ /Hyper/i) { 595 $feature{"hyper"} = 1; 596 } 597 if ($libcurl =~ /nghttp2/i) { 598 # nghttp2 supports h2c, hyper does not 599 $feature{"h2c"} = 1; 600 } 601 if ($libcurl =~ /AppleIDN/) { 602 $feature{"AppleIDN"} = 1; 603 } 604 if ($libcurl =~ /WinIDN/) { 605 $feature{"WinIDN"} = 1; 606 } 607 if ($libcurl =~ /libidn2/) { 608 $feature{"libidn2"} = 1; 609 } 610 if ($libcurl =~ /libssh2/i) { 611 $feature{"libssh2"} = 1; 612 } 613 if ($libcurl =~ /libssh\/([0-9.]*)\//i) { 614 $feature{"libssh"} = 1; 615 if($1 =~ /(\d+)\.(\d+).(\d+)/) { 616 my $v = $1 * 100 + $2 * 10 + $3; 617 if($v < 94) { 618 # before 0.9.4 619 $feature{"oldlibssh"} = 1; 620 } 621 } 622 } 623 if ($libcurl =~ /wolfssh/i) { 624 $feature{"wolfssh"} = 1; 625 } 626 } 627 elsif($_ =~ /^Protocols: (.*)/i) { 628 $proto = $1; 629 # these are the protocols compiled in to this libcurl 630 parseprotocols($proto); 631 } 632 elsif($_ =~ /^Features: (.*)/i) { 633 $feat = $1; 634 635 # built with memory tracking support (--enable-curldebug); may be disabled later 636 $feature{"TrackMemory"} = $feat =~ /TrackMemory/i; 637 # curl was built with --enable-debug 638 $feature{"Debug"} = $feat =~ /Debug/i; 639 # ssl enabled 640 $feature{"SSL"} = $feat =~ /SSL/i; 641 # multiple ssl backends available. 642 $feature{"MultiSSL"} = $feat =~ /MultiSSL/i; 643 # large file support 644 $feature{"Largefile"} = $feat =~ /Largefile/i; 645 # IDN support 646 $feature{"IDN"} = $feat =~ /IDN/i; 647 # IPv6 support 648 $feature{"IPv6"} = $feat =~ /IPv6/i; 649 # Unix sockets support 650 $feature{"UnixSockets"} = $feat =~ /UnixSockets/i; 651 # libz compression 652 $feature{"libz"} = $feat =~ /libz/i; 653 # Brotli compression 654 $feature{"brotli"} = $feat =~ /brotli/i; 655 # Zstd compression 656 $feature{"zstd"} = $feat =~ /zstd/i; 657 # NTLM enabled 658 $feature{"NTLM"} = $feat =~ /NTLM/i; 659 # NTLM delegation to winbind daemon ntlm_auth helper enabled 660 $feature{"NTLM_WB"} = $feat =~ /NTLM_WB/i; 661 # SSPI enabled 662 $feature{"SSPI"} = $feat =~ /SSPI/i; 663 # GSS-API enabled 664 $feature{"GSS-API"} = $feat =~ /GSS-API/i; 665 # Kerberos enabled 666 $feature{"Kerberos"} = $feat =~ /Kerberos/i; 667 # SPNEGO enabled 668 $feature{"SPNEGO"} = $feat =~ /SPNEGO/i; 669 # TLS-SRP enabled 670 $feature{"TLS-SRP"} = $feat =~ /TLS-SRP/i; 671 # PSL enabled 672 $feature{"PSL"} = $feat =~ /PSL/i; 673 # alt-svc enabled 674 $feature{"alt-svc"} = $feat =~ /alt-svc/i; 675 # HSTS support 676 $feature{"HSTS"} = $feat =~ /HSTS/i; 677 if($feat =~ /AsynchDNS/i) { 678 if(!$feature{"c-ares"}) { 679 # this means threaded resolver 680 $feature{"threaded-resolver"} = 1; 681 $resolver="threaded"; 682 } 683 } 684 # http2 enabled 685 $feature{"http/2"} = $feat =~ /HTTP2/; 686 if($feature{"http/2"}) { 687 push @protocols, 'http/2'; 688 } 689 # http3 enabled 690 $feature{"http/3"} = $feat =~ /HTTP3/; 691 if($feature{"http/3"}) { 692 push @protocols, 'http/3'; 693 } 694 # https proxy support 695 $feature{"HTTPS-proxy"} = $feat =~ /HTTPS-proxy/; 696 if($feature{"HTTPS-proxy"}) { 697 # 'https-proxy' is used as "server" so consider it a protocol 698 push @protocols, 'https-proxy'; 699 } 700 # Unicode support 701 $feature{"Unicode"} = $feat =~ /Unicode/i; 702 # Thread-safe init 703 $feature{"threadsafe"} = $feat =~ /threadsafe/i; 704 } 705 # 706 # Test harness currently uses a non-stunnel server in order to 707 # run HTTP TLS-SRP tests required when curl is built with https 708 # protocol support and TLS-SRP feature enabled. For convenience 709 # 'httptls' may be included in the test harness protocols array 710 # to differentiate this from classic stunnel based 'https' test 711 # harness server. 712 # 713 if($feature{"TLS-SRP"}) { 714 my $add_httptls; 715 for(@protocols) { 716 if($_ =~ /^https(-ipv6|)$/) { 717 $add_httptls=1; 718 last; 719 } 720 } 721 if($add_httptls && (! grep /^httptls$/, @protocols)) { 722 push @protocols, 'httptls'; 723 push @protocols, 'httptls-ipv6'; 724 } 725 } 726 } 727 728 if(!$curl) { 729 logmsg "unable to get curl's version, further details are:\n"; 730 logmsg "issued command: \n"; 731 logmsg "$versioncmd \n"; 732 if ($versretval == -1) { 733 logmsg "command failed with: \n"; 734 logmsg "$versnoexec \n"; 735 } 736 elsif ($versretval & 127) { 737 logmsg sprintf("command died with signal %d, and %s coredump.\n", 738 ($versretval & 127), ($versretval & 128)?"a":"no"); 739 } 740 else { 741 logmsg sprintf("command exited with value %d \n", $versretval >> 8); 742 } 743 logmsg "contents of $curlverout: \n"; 744 displaylogcontent("$curlverout"); 745 logmsg "contents of $curlvererr: \n"; 746 displaylogcontent("$curlvererr"); 747 die "couldn't get curl's version"; 748 } 749 750 if(-r "../lib/curl_config.h") { 751 open(my $conf, "<", "../lib/curl_config.h"); 752 while(<$conf>) { 753 if($_ =~ /^\#define HAVE_GETRLIMIT/) { 754 # set if system has getrlimit() 755 $feature{"getrlimit"} = 1; 756 } 757 } 758 close($conf); 759 } 760 761 # allow this feature only if debug mode is disabled 762 $feature{"ld_preload"} = $feature{"ld_preload"} && !$feature{"Debug"}; 763 764 if($feature{"IPv6"}) { 765 # client has IPv6 support 766 767 # check if the HTTP server has it! 768 my $cmd = "server/sws".exe_ext('SRV')." --version"; 769 my @sws = `$cmd`; 770 if($sws[0] =~ /IPv6/) { 771 # HTTP server has IPv6 support! 772 $http_ipv6 = 1; 773 } 774 775 # check if the FTP server has it! 776 $cmd = "server/sockfilt".exe_ext('SRV')." --version"; 777 @sws = `$cmd`; 778 if($sws[0] =~ /IPv6/) { 779 # FTP server has IPv6 support! 780 $ftp_ipv6 = 1; 781 } 782 } 783 784 if($feature{"UnixSockets"}) { 785 # client has Unix sockets support, check whether the HTTP server has it 786 my $cmd = "server/sws".exe_ext('SRV')." --version"; 787 my @sws = `$cmd`; 788 $http_unix = 1 if($sws[0] =~ /unix/); 789 } 790 791 open(my $manh, "-|", shell_quote($CURL) . " -M 2>&1"); 792 while(my $s = <$manh>) { 793 if($s =~ /built-in manual was disabled at build-time/) { 794 $feature{"manual"} = 0; 795 last; 796 } 797 $feature{"manual"} = 1; 798 last; 799 } 800 close($manh); 801 802 $feature{"unittest"} = $feature{"Debug"}; 803 $feature{"nghttpx"} = !!$ENV{'NGHTTPX'}; 804 $feature{"nghttpx-h3"} = !!$nghttpx_h3; 805 806 # 807 # strings that must exactly match the names used in server/disabled.c 808 # 809 $feature{"cookies"} = 1; 810 # Use this as a proxy for any cryptographic authentication 811 $feature{"crypto"} = $feature{"NTLM"} || $feature{"Kerberos"} || $feature{"SPNEGO"}; 812 $feature{"DoH"} = 1; 813 $feature{"HTTP-auth"} = 1; 814 $feature{"Mime"} = 1; 815 $feature{"form-api"} = 1; 816 $feature{"netrc"} = 1; 817 $feature{"parsedate"} = 1; 818 $feature{"proxy"} = 1; 819 $feature{"shuffle-dns"} = 1; 820 $feature{"typecheck"} = 1; 821 $feature{"verbose-strings"} = 1; 822 $feature{"wakeup"} = 1; 823 $feature{"headers-api"} = 1; 824 $feature{"xattr"} = 1; 825 $feature{"large-time"} = 1; 826 $feature{"sha512-256"} = 1; 827 $feature{"local-http"} = servers::localhttp(); 828 $feature{"codeset-utf8"} = lc(langinfo(CODESET())) eq "utf-8"; 829 830 # make each protocol an enabled "feature" 831 for my $p (@protocols) { 832 $feature{$p} = 1; 833 } 834 # 'socks' was once here but is now removed 835 836 $has_shared = `sh $CURLCONFIG --built-shared`; 837 chomp $has_shared; 838 $has_shared = $has_shared eq "yes"; 839 840 if(!$feature{"TrackMemory"} && $torture) { 841 die "can't run torture tests since curl was built without ". 842 "TrackMemory feature (--enable-curldebug)"; 843 } 844 845 my $hostname=join(' ', runclientoutput("hostname")); 846 chomp $hostname; 847 my $hosttype=join(' ', runclientoutput("uname -a")); 848 chomp $hosttype; 849 my $hostos=$^O; 850 851 # display summary information about curl and the test host 852 logmsg ("********* System characteristics ******** \n", 853 "* $curl\n", 854 "* $libcurl\n", 855 "* Protocols: $proto\n", 856 "* Features: $feat\n", 857 "* Disabled: $dis\n", 858 "* Host: $hostname\n", 859 "* System: $hosttype\n", 860 "* OS: $hostos\n", 861 "* Perl: $^V ($^X)\n", 862 "* Args: $args\n"); 863 864 if($jobs) { 865 # Only show if not the default for now 866 logmsg "* Jobs: $jobs\n"; 867 } 868 if($feature{"TrackMemory"} && $feature{"threaded-resolver"}) { 869 logmsg("*\n", 870 "*** DISABLES memory tracking when using threaded resolver\n", 871 "*\n"); 872 } 873 874 logmsg sprintf("* Env: %s%s%s%s%s", $valgrind?"Valgrind ":"", 875 $run_duphandle?"test-duphandle ":"", 876 $run_event_based?"event-based ":"", 877 $bundle?"bundle ":"", 878 $nghttpx_h3); 879 logmsg sprintf("%s\n", $libtool?"Libtool ":""); 880 logmsg ("* Seed: $randseed\n"); 881 882 # Disable memory tracking when using threaded resolver 883 $feature{"TrackMemory"} = $feature{"TrackMemory"} && !$feature{"threaded-resolver"}; 884 885 # toggle off the features that were disabled in the build 886 for my $d(@disabled) { 887 $feature{$d} = 0; 888 } 889} 890 891####################################################################### 892# display information about server features 893# 894sub displayserverfeatures { 895 logmsg sprintf("* Servers: %s", $stunnel?"SSL ":""); 896 logmsg sprintf("%s", $http_ipv6?"HTTP-IPv6 ":""); 897 logmsg sprintf("%s", $http_unix?"HTTP-unix ":""); 898 logmsg sprintf("%s\n", $ftp_ipv6?"FTP-IPv6 ":""); 899 logmsg "***************************************** \n"; 900} 901 902####################################################################### 903# Provide time stamps for single test skipped events 904# 905sub timestampskippedevents { 906 my $testnum = $_[0]; 907 908 return if((not defined($testnum)) || ($testnum < 1)); 909 910 if($timestats) { 911 912 if($timevrfyend{$testnum}) { 913 return; 914 } 915 elsif($timesrvrlog{$testnum}) { 916 $timevrfyend{$testnum} = $timesrvrlog{$testnum}; 917 return; 918 } 919 elsif($timetoolend{$testnum}) { 920 $timevrfyend{$testnum} = $timetoolend{$testnum}; 921 $timesrvrlog{$testnum} = $timetoolend{$testnum}; 922 } 923 elsif($timetoolini{$testnum}) { 924 $timevrfyend{$testnum} = $timetoolini{$testnum}; 925 $timesrvrlog{$testnum} = $timetoolini{$testnum}; 926 $timetoolend{$testnum} = $timetoolini{$testnum}; 927 } 928 elsif($timesrvrend{$testnum}) { 929 $timevrfyend{$testnum} = $timesrvrend{$testnum}; 930 $timesrvrlog{$testnum} = $timesrvrend{$testnum}; 931 $timetoolend{$testnum} = $timesrvrend{$testnum}; 932 $timetoolini{$testnum} = $timesrvrend{$testnum}; 933 } 934 elsif($timesrvrini{$testnum}) { 935 $timevrfyend{$testnum} = $timesrvrini{$testnum}; 936 $timesrvrlog{$testnum} = $timesrvrini{$testnum}; 937 $timetoolend{$testnum} = $timesrvrini{$testnum}; 938 $timetoolini{$testnum} = $timesrvrini{$testnum}; 939 $timesrvrend{$testnum} = $timesrvrini{$testnum}; 940 } 941 elsif($timeprepini{$testnum}) { 942 $timevrfyend{$testnum} = $timeprepini{$testnum}; 943 $timesrvrlog{$testnum} = $timeprepini{$testnum}; 944 $timetoolend{$testnum} = $timeprepini{$testnum}; 945 $timetoolini{$testnum} = $timeprepini{$testnum}; 946 $timesrvrend{$testnum} = $timeprepini{$testnum}; 947 $timesrvrini{$testnum} = $timeprepini{$testnum}; 948 } 949 } 950} 951 952 953# Setup CI Test Run 954sub citest_starttestrun { 955 if(azure_check_environment()) { 956 $AZURE_RUN_ID = azure_create_test_run($ACURL); 957 logmsg "Azure Run ID: $AZURE_RUN_ID\n" if ($verbose); 958 } 959 # Appveyor doesn't require anything here 960} 961 962 963# Register the test case with the CI runner 964sub citest_starttest { 965 my $testnum = $_[0]; 966 967 # get the name of the test early 968 my $testname= (getpart("client", "name"))[0]; 969 chomp $testname; 970 971 # create test result in CI services 972 if(azure_check_environment() && $AZURE_RUN_ID) { 973 $AZURE_RESULT_ID = azure_create_test_result($ACURL, $AZURE_RUN_ID, $testnum, $testname); 974 } 975 elsif(appveyor_check_environment()) { 976 appveyor_create_test_result($ACURL, $testnum, $testname); 977 } 978} 979 980 981# Submit the test case result with the CI runner 982sub citest_finishtest { 983 my ($testnum, $error) = @_; 984 # update test result in CI services 985 if(azure_check_environment() && $AZURE_RUN_ID && $AZURE_RESULT_ID) { 986 $AZURE_RESULT_ID = azure_update_test_result($ACURL, $AZURE_RUN_ID, $AZURE_RESULT_ID, $testnum, $error, 987 $timeprepini{$testnum}, $timevrfyend{$testnum}); 988 } 989 elsif(appveyor_check_environment()) { 990 appveyor_update_test_result($ACURL, $testnum, $error, $timeprepini{$testnum}, $timevrfyend{$testnum}); 991 } 992} 993 994# Complete CI test run 995sub citest_finishtestrun { 996 if(azure_check_environment() && $AZURE_RUN_ID) { 997 $AZURE_RUN_ID = azure_update_test_run($ACURL, $AZURE_RUN_ID); 998 } 999 # Appveyor doesn't require anything here 1000} 1001 1002 1003# add one set of test timings from the runner to global set 1004sub updatetesttimings { 1005 my ($testnum, %testtimings)=@_; 1006 1007 if(defined $testtimings{"timeprepini"}) { 1008 $timeprepini{$testnum} = $testtimings{"timeprepini"}; 1009 } 1010 if(defined $testtimings{"timesrvrini"}) { 1011 $timesrvrini{$testnum} = $testtimings{"timesrvrini"}; 1012 } 1013 if(defined $testtimings{"timesrvrend"}) { 1014 $timesrvrend{$testnum} = $testtimings{"timesrvrend"}; 1015 } 1016 if(defined $testtimings{"timetoolini"}) { 1017 $timetoolini{$testnum} = $testtimings{"timetoolini"}; 1018 } 1019 if(defined $testtimings{"timetoolend"}) { 1020 $timetoolend{$testnum} = $testtimings{"timetoolend"}; 1021 } 1022 if(defined $testtimings{"timesrvrlog"}) { 1023 $timesrvrlog{$testnum} = $testtimings{"timesrvrlog"}; 1024 } 1025} 1026 1027 1028####################################################################### 1029# Return the log directory for the given test runner 1030sub getrunnernumlogdir { 1031 my $runnernum = $_[0]; 1032 return $jobs > 1 ? "$LOGDIR/$runnernum" : $LOGDIR; 1033} 1034 1035####################################################################### 1036# Return the log directory for the given test runner ID 1037sub getrunnerlogdir { 1038 my $runnerid = $_[0]; 1039 if($jobs <= 1) { 1040 return $LOGDIR; 1041 } 1042 # TODO: speed up this O(n) operation 1043 for my $runnernum (keys %runnerids) { 1044 if($runnerid eq $runnerids{$runnernum}) { 1045 return "$LOGDIR/$runnernum"; 1046 } 1047 } 1048 die "Internal error: runner ID $runnerid not found"; 1049} 1050 1051 1052####################################################################### 1053# Verify that this test case should be run 1054sub singletest_shouldrun { 1055 my $testnum = $_[0]; 1056 my $why; # why the test won't be run 1057 my $errorreturncode = 1; # 1 means normal error, 2 means ignored error 1058 my @what; # what features are needed 1059 1060 if($disttests !~ /test$testnum(\W|\z)/ ) { 1061 logmsg "Warning: test$testnum not present in tests/data/Makefile.am\n"; 1062 } 1063 if($disabled{$testnum}) { 1064 if(!$run_disabled) { 1065 $why = "listed in DISABLED"; 1066 } 1067 else { 1068 logmsg "Warning: test$testnum is explicitly disabled\n"; 1069 } 1070 } 1071 if($ignored{$testnum}) { 1072 logmsg "Warning: test$testnum result is ignored\n"; 1073 $errorreturncode = 2; 1074 } 1075 1076 if(loadtest("${TESTDIR}/test${testnum}")) { 1077 if($verbose) { 1078 # this is not a test 1079 logmsg "RUN: $testnum doesn't look like a test case\n"; 1080 } 1081 $why = "no test"; 1082 } 1083 else { 1084 @what = getpart("client", "features"); 1085 } 1086 1087 # We require a feature to be present 1088 for(@what) { 1089 my $f = $_; 1090 $f =~ s/\s//g; 1091 1092 if($f =~ /^([^!].*)$/) { 1093 if($feature{$1}) { 1094 next; 1095 } 1096 1097 $why = "curl lacks $1 support"; 1098 last; 1099 } 1100 } 1101 1102 # We require a feature to not be present 1103 if(!$why) { 1104 for(@what) { 1105 my $f = $_; 1106 $f =~ s/\s//g; 1107 1108 if($f =~ /^!(.*)$/) { 1109 if(!$feature{$1}) { 1110 next; 1111 } 1112 } 1113 else { 1114 next; 1115 } 1116 1117 $why = "curl has $1 support"; 1118 last; 1119 } 1120 } 1121 1122 my @info_keywords; 1123 if(!$why) { 1124 @info_keywords = getpart("info", "keywords"); 1125 1126 if(!$info_keywords[0]) { 1127 $why = "missing the <keywords> section!"; 1128 } 1129 1130 my $match; 1131 for my $k (@info_keywords) { 1132 chomp $k; 1133 if ($disabled_keywords{lc($k)}) { 1134 $why = "disabled by keyword"; 1135 } 1136 elsif ($enabled_keywords{lc($k)}) { 1137 $match = 1; 1138 } 1139 if ($ignored_keywords{lc($k)}) { 1140 logmsg "Warning: test$testnum result is ignored due to $k\n"; 1141 $errorreturncode = 2; 1142 } 1143 } 1144 1145 if(!$why && !$match && %enabled_keywords) { 1146 $why = "disabled by missing keyword"; 1147 } 1148 } 1149 1150 if (!$why && defined $custom_skip_reasons{test}{$testnum}) { 1151 $why = $custom_skip_reasons{test}{$testnum}; 1152 } 1153 1154 if (!$why && defined $custom_skip_reasons{tool}) { 1155 foreach my $tool (getpart("client", "tool")) { 1156 foreach my $tool_skip_pattern (keys %{$custom_skip_reasons{tool}}) { 1157 if ($tool =~ /$tool_skip_pattern/i) { 1158 $why = $custom_skip_reasons{tool}{$tool_skip_pattern}; 1159 } 1160 } 1161 } 1162 } 1163 1164 if (!$why && defined $custom_skip_reasons{keyword}) { 1165 foreach my $keyword (@info_keywords) { 1166 foreach my $keyword_skip_pattern (keys %{$custom_skip_reasons{keyword}}) { 1167 if ($keyword =~ /$keyword_skip_pattern/i) { 1168 $why = $custom_skip_reasons{keyword}{$keyword_skip_pattern}; 1169 } 1170 } 1171 } 1172 } 1173 1174 return ($why, $errorreturncode); 1175} 1176 1177 1178####################################################################### 1179# Print the test name and count tests 1180sub singletest_count { 1181 my ($testnum, $why) = @_; 1182 1183 if($why && !$listonly) { 1184 # there's a problem, count it as "skipped" 1185 $skipped{$why}++; 1186 $teststat[$testnum]=$why; # store reason for this test case 1187 1188 if(!$short) { 1189 if($skipped{$why} <= 3) { 1190 # show only the first three skips for each reason 1191 logmsg sprintf("test %04d SKIPPED: $why\n", $testnum); 1192 } 1193 } 1194 1195 timestampskippedevents($testnum); 1196 return -1; 1197 } 1198 1199 # At this point we've committed to run this test 1200 logmsg sprintf("test %04d...", $testnum) if(!$automakestyle); 1201 1202 # name of the test 1203 my $testname= (getpart("client", "name"))[0]; 1204 chomp $testname; 1205 logmsg "[$testname]\n" if(!$short); 1206 1207 if($listonly) { 1208 timestampskippedevents($testnum); 1209 } 1210 return 0; 1211} 1212 1213# Make sure all line endings in the array are the same: CRLF 1214sub normalize_text { 1215 my ($ref) = @_; 1216 s/\r\n/\n/g for @$ref; 1217 s/\n/\r\n/g for @$ref; 1218} 1219 1220####################################################################### 1221# Verify test succeeded 1222sub singletest_check { 1223 my ($runnerid, $testnum, $cmdres, $CURLOUT, $tool, $usedvalgrind)=@_; 1224 1225 # Skip all the verification on torture tests 1226 if ($torture) { 1227 # timestamp test result verification end 1228 $timevrfyend{$testnum} = Time::HiRes::time(); 1229 return -2; 1230 } 1231 1232 my $logdir = getrunnerlogdir($runnerid); 1233 my @err = getpart("verify", "errorcode"); 1234 my $errorcode = $err[0] || "0"; 1235 my $ok=""; 1236 my $res; 1237 chomp $errorcode; 1238 my $testname= (getpart("client", "name"))[0]; 1239 chomp $testname; 1240 # what parts to cut off from stdout/stderr 1241 my @stripfile = getpart("verify", "stripfile"); 1242 1243 my @validstdout = getpart("verify", "stdout"); 1244 # get all attributes 1245 my %hash = getpartattr("verify", "stdout"); 1246 1247 my $loadfile = $hash{'loadfile'}; 1248 if ($loadfile) { 1249 open(my $tmp, "<", "$loadfile") || die "Cannot open file $loadfile: $!"; 1250 @validstdout = <$tmp>; 1251 close($tmp); 1252 1253 # Enforce LF newlines on load 1254 s/\r\n/\n/g for @validstdout; 1255 } 1256 1257 if (@validstdout) { 1258 # verify redirected stdout 1259 my @actual = loadarray(stdoutfilename($logdir, $testnum)); 1260 1261 foreach my $strip (@stripfile) { 1262 chomp $strip; 1263 my @newgen; 1264 for(@actual) { 1265 eval $strip; 1266 if($_) { 1267 push @newgen, $_; 1268 } 1269 } 1270 # this is to get rid of array entries that vanished (zero 1271 # length) because of replacements 1272 @actual = @newgen; 1273 } 1274 1275 # get the mode attribute 1276 my $filemode=$hash{'mode'}; 1277 if($filemode && ($filemode eq "text")) { 1278 normalize_text(\@validstdout); 1279 normalize_text(\@actual); 1280 } 1281 1282 if($hash{'nonewline'}) { 1283 # Yes, we must cut off the final newline from the final line 1284 # of the protocol data 1285 chomp($validstdout[-1]); 1286 } 1287 1288 if($hash{'crlf'} || 1289 ($feature{"hyper"} && ($keywords{"HTTP"} 1290 || $keywords{"HTTPS"}))) { 1291 subnewlines(0, \$_) for @validstdout; 1292 } 1293 1294 $res = compare($runnerid, $testnum, $testname, "stdout", \@actual, \@validstdout); 1295 if($res) { 1296 return -1; 1297 } 1298 $ok .= "s"; 1299 } 1300 else { 1301 $ok .= "-"; # stdout not checked 1302 } 1303 1304 my @validstderr = getpart("verify", "stderr"); 1305 if (@validstderr) { 1306 # verify redirected stderr 1307 my @actual = loadarray(stderrfilename($logdir, $testnum)); 1308 1309 foreach my $strip (@stripfile) { 1310 chomp $strip; 1311 my @newgen; 1312 for(@actual) { 1313 eval $strip; 1314 if($_) { 1315 push @newgen, $_; 1316 } 1317 } 1318 # this is to get rid of array entries that vanished (zero 1319 # length) because of replacements 1320 @actual = @newgen; 1321 } 1322 1323 # get all attributes 1324 my %hash = getpartattr("verify", "stderr"); 1325 1326 # get the mode attribute 1327 my $filemode=$hash{'mode'}; 1328 if($filemode && ($filemode eq "text") && $feature{"hyper"}) { 1329 # text mode check in hyper-mode. Sometimes necessary if the stderr 1330 # data *looks* like HTTP and thus has gotten CRLF newlines 1331 # mistakenly 1332 normalize_text(\@validstderr); 1333 } 1334 if($filemode && ($filemode eq "text")) { 1335 normalize_text(\@validstderr); 1336 normalize_text(\@actual); 1337 } 1338 1339 if($hash{'nonewline'}) { 1340 # Yes, we must cut off the final newline from the final line 1341 # of the protocol data 1342 chomp($validstderr[-1]); 1343 } 1344 1345 if($hash{'crlf'}) { 1346 subnewlines(0, \$_) for @validstderr; 1347 } 1348 1349 $res = compare($runnerid, $testnum, $testname, "stderr", \@actual, \@validstderr); 1350 if($res) { 1351 return -1; 1352 } 1353 $ok .= "r"; 1354 } 1355 else { 1356 $ok .= "-"; # stderr not checked 1357 } 1358 1359 # what to cut off from the live protocol sent by curl 1360 my @strip = getpart("verify", "strip"); 1361 1362 # what parts to cut off from the protocol & upload 1363 my @strippart = getpart("verify", "strippart"); 1364 1365 # this is the valid protocol blurb curl should generate 1366 my @protocol= getpart("verify", "protocol"); 1367 if(@protocol) { 1368 # Verify the sent request 1369 my @out = loadarray("$logdir/$SERVERIN"); 1370 1371 # check if there's any attributes on the verify/protocol section 1372 my %hash = getpartattr("verify", "protocol"); 1373 1374 if($hash{'nonewline'}) { 1375 # Yes, we must cut off the final newline from the final line 1376 # of the protocol data 1377 chomp($protocol[-1]); 1378 } 1379 1380 for(@strip) { 1381 # strip off all lines that match the patterns from both arrays 1382 chomp $_; 1383 @out = striparray( $_, \@out); 1384 @protocol= striparray( $_, \@protocol); 1385 } 1386 1387 for my $strip (@strippart) { 1388 chomp $strip; 1389 for(@out) { 1390 eval $strip; 1391 } 1392 } 1393 1394 if($hash{'crlf'}) { 1395 subnewlines(1, \$_) for @protocol; 1396 } 1397 1398 if((!$out[0] || ($out[0] eq "")) && $protocol[0]) { 1399 logmsg "\n $testnum: protocol FAILED!\n". 1400 " There was no content at all in the file $logdir/$SERVERIN.\n". 1401 " Server glitch? Total curl failure? Returned: $cmdres\n"; 1402 # timestamp test result verification end 1403 $timevrfyend{$testnum} = Time::HiRes::time(); 1404 return -1; 1405 } 1406 1407 $res = compare($runnerid, $testnum, $testname, "protocol", \@out, \@protocol); 1408 if($res) { 1409 return -1; 1410 } 1411 1412 $ok .= "p"; 1413 1414 } 1415 else { 1416 $ok .= "-"; # protocol not checked 1417 } 1418 1419 my %replyattr = getpartattr("reply", "data"); 1420 my @reply; 1421 if (partexists("reply", "datacheck")) { 1422 for my $partsuffix (('', '1', '2', '3', '4')) { 1423 my @replycheckpart = getpart("reply", "datacheck".$partsuffix); 1424 if(@replycheckpart) { 1425 my %replycheckpartattr = getpartattr("reply", "datacheck".$partsuffix); 1426 # get the mode attribute 1427 my $filemode=$replycheckpartattr{'mode'}; 1428 if($filemode && ($filemode eq "text")) { 1429 normalize_text(\@replycheckpart); 1430 } 1431 if($replycheckpartattr{'nonewline'}) { 1432 # Yes, we must cut off the final newline from the final line 1433 # of the datacheck 1434 chomp($replycheckpart[-1]); 1435 } 1436 if($replycheckpartattr{'crlf'} || 1437 ($feature{"hyper"} && ($keywords{"HTTP"} 1438 || $keywords{"HTTPS"}))) { 1439 subnewlines(0, \$_) for @replycheckpart; 1440 } 1441 push(@reply, @replycheckpart); 1442 } 1443 } 1444 } 1445 else { 1446 # check against the data section 1447 @reply = getpart("reply", "data"); 1448 if(@reply) { 1449 if($replyattr{'nonewline'}) { 1450 # cut off the final newline from the final line of the data 1451 chomp($reply[-1]); 1452 } 1453 } 1454 # get the mode attribute 1455 my $filemode=$replyattr{'mode'}; 1456 if($filemode && ($filemode eq "text")) { 1457 normalize_text(\@reply); 1458 } 1459 if($replyattr{'crlf'} || 1460 ($feature{"hyper"} && ($keywords{"HTTP"} 1461 || $keywords{"HTTPS"}))) { 1462 subnewlines(0, \$_) for @reply; 1463 } 1464 } 1465 1466 if(!$replyattr{'nocheck'} && (@reply || $replyattr{'sendzero'})) { 1467 # verify the received data 1468 my @out = loadarray($CURLOUT); 1469 1470 # get the mode attribute 1471 my $filemode=$replyattr{'mode'}; 1472 if($filemode && ($filemode eq "text")) { 1473 normalize_text(\@out); 1474 } 1475 $res = compare($runnerid, $testnum, $testname, "data", \@out, \@reply); 1476 if ($res) { 1477 return -1; 1478 } 1479 $ok .= "d"; 1480 } 1481 else { 1482 $ok .= "-"; # data not checked 1483 } 1484 1485 # if this section exists, we verify upload 1486 my @upload = getpart("verify", "upload"); 1487 if(@upload) { 1488 my %hash = getpartattr("verify", "upload"); 1489 if($hash{'nonewline'}) { 1490 # cut off the final newline from the final line of the upload data 1491 chomp($upload[-1]); 1492 } 1493 for my $line (@upload) { 1494 subbase64(\$line); 1495 subsha256base64file(\$line); 1496 substrippemfile(\$line); 1497 } 1498 1499 # verify uploaded data 1500 my @out = loadarray("$logdir/upload.$testnum"); 1501 for my $strip (@strippart) { 1502 chomp $strip; 1503 for(@out) { 1504 eval $strip; 1505 } 1506 } 1507 if($hash{'crlf'}) { 1508 subnewlines(1, \$_) for @upload; 1509 } 1510 if($hash{'nonewline'}) { 1511 # Yes, we must cut off the final newline from the final line 1512 # of the upload data 1513 chomp($upload[-1]); 1514 } 1515 1516 $res = compare($runnerid, $testnum, $testname, "upload", \@out, \@upload); 1517 if ($res) { 1518 return -1; 1519 } 1520 $ok .= "u"; 1521 } 1522 else { 1523 $ok .= "-"; # upload not checked 1524 } 1525 1526 # this is the valid protocol blurb curl should generate to a proxy 1527 my @proxyprot = getpart("verify", "proxy"); 1528 if(@proxyprot) { 1529 # Verify the sent proxy request 1530 # check if there's any attributes on the verify/protocol section 1531 my %hash = getpartattr("verify", "proxy"); 1532 1533 if($hash{'nonewline'}) { 1534 # Yes, we must cut off the final newline from the final line 1535 # of the protocol data 1536 chomp($proxyprot[-1]); 1537 } 1538 1539 my @out = loadarray("$logdir/$PROXYIN"); 1540 for(@strip) { 1541 # strip off all lines that match the patterns from both arrays 1542 chomp $_; 1543 @out = striparray( $_, \@out); 1544 @proxyprot= striparray( $_, \@proxyprot); 1545 } 1546 1547 for my $strip (@strippart) { 1548 chomp $strip; 1549 for(@out) { 1550 eval $strip; 1551 } 1552 } 1553 1554 if($hash{'crlf'} || 1555 ($feature{"hyper"} && ($keywords{"HTTP"} || $keywords{"HTTPS"}))) { 1556 subnewlines(0, \$_) for @proxyprot; 1557 } 1558 1559 $res = compare($runnerid, $testnum, $testname, "proxy", \@out, \@proxyprot); 1560 if($res) { 1561 return -1; 1562 } 1563 1564 $ok .= "P"; 1565 1566 } 1567 else { 1568 $ok .= "-"; # proxy not checked 1569 } 1570 1571 my $outputok; 1572 for my $partsuffix (('', '1', '2', '3', '4')) { 1573 my @outfile=getpart("verify", "file".$partsuffix); 1574 if(@outfile || partexists("verify", "file".$partsuffix) ) { 1575 # we're supposed to verify a dynamically generated file! 1576 my %hash = getpartattr("verify", "file".$partsuffix); 1577 1578 my $filename=$hash{'name'}; 1579 if(!$filename) { 1580 logmsg " $testnum: IGNORED: section verify=>file$partsuffix ". 1581 "has no name attribute\n"; 1582 if (runnerac_stopservers($runnerid)) { 1583 logmsg "ERROR: runner $runnerid seems to have died\n"; 1584 } else { 1585 1586 # TODO: this is a blocking call that will stall the controller, 1587 if($verbose) { 1588 logmsg "WARNING: blocking call in async function\n"; 1589 } 1590 # but this error condition should never happen except during 1591 # development. 1592 my ($rid, $unexpected, $logs) = runnerar($runnerid); 1593 if(!$rid) { 1594 logmsg "ERROR: runner $runnerid seems to have died\n"; 1595 } else { 1596 logmsg $logs; 1597 } 1598 } 1599 # timestamp test result verification end 1600 $timevrfyend{$testnum} = Time::HiRes::time(); 1601 return -1; 1602 } 1603 my @generated=loadarray($filename); 1604 1605 # what parts to cut off from the file 1606 my @stripfilepar = getpart("verify", "stripfile".$partsuffix); 1607 1608 my $filemode=$hash{'mode'}; 1609 if($filemode && ($filemode eq "text")) { 1610 normalize_text(\@outfile); 1611 normalize_text(\@generated); 1612 } 1613 if($hash{'crlf'} || 1614 ($feature{"hyper"} && ($keywords{"HTTP"} 1615 || $keywords{"HTTPS"}))) { 1616 subnewlines(0, \$_) for @outfile; 1617 } 1618 1619 for my $strip (@stripfilepar) { 1620 chomp $strip; 1621 my @newgen; 1622 for(@generated) { 1623 eval $strip; 1624 if($_) { 1625 push @newgen, $_; 1626 } 1627 } 1628 # this is to get rid of array entries that vanished (zero 1629 # length) because of replacements 1630 @generated = @newgen; 1631 } 1632 1633 if($hash{'nonewline'}) { 1634 # cut off the final newline from the final line of the 1635 # output data 1636 chomp($outfile[-1]); 1637 } 1638 1639 $res = compare($runnerid, $testnum, $testname, "output ($filename)", 1640 \@generated, \@outfile); 1641 if($res) { 1642 return -1; 1643 } 1644 1645 $outputok = 1; # output checked 1646 } 1647 } 1648 $ok .= ($outputok) ? "o" : "-"; # output checked or not 1649 1650 # verify SOCKS proxy details 1651 my @socksprot = getpart("verify", "socks"); 1652 if(@socksprot) { 1653 # Verify the sent SOCKS proxy details 1654 my @out = loadarray("$logdir/$SOCKSIN"); 1655 $res = compare($runnerid, $testnum, $testname, "socks", \@out, \@socksprot); 1656 if($res) { 1657 return -1; 1658 } 1659 } 1660 1661 # accept multiple comma-separated error codes 1662 my @splerr = split(/ *, */, $errorcode); 1663 my $errok; 1664 foreach my $e (@splerr) { 1665 if($e == $cmdres) { 1666 # a fine error code 1667 $errok = 1; 1668 last; 1669 } 1670 } 1671 1672 if($errok) { 1673 $ok .= "e"; 1674 } 1675 else { 1676 if(!$short) { 1677 logmsg sprintf("\n%s returned $cmdres, when expecting %s\n", 1678 (!$tool)?"curl":$tool, $errorcode); 1679 } 1680 logmsg " $testnum: exit FAILED\n"; 1681 # timestamp test result verification end 1682 $timevrfyend{$testnum} = Time::HiRes::time(); 1683 return -1; 1684 } 1685 1686 if($feature{"TrackMemory"}) { 1687 if(! -f "$logdir/$MEMDUMP") { 1688 my %cmdhash = getpartattr("client", "command"); 1689 my $cmdtype = $cmdhash{'type'} || "default"; 1690 logmsg "\n** ALERT! memory tracking with no output file?\n" 1691 if(!$cmdtype eq "perl"); 1692 $ok .= "-"; # problem with memory checking 1693 } 1694 else { 1695 my @memdata=`$memanalyze "$logdir/$MEMDUMP"`; 1696 my $leak=0; 1697 for(@memdata) { 1698 if($_ ne "") { 1699 # well it could be other memory problems as well, but 1700 # we call it leak for short here 1701 $leak=1; 1702 } 1703 } 1704 if($leak) { 1705 logmsg "\n** MEMORY FAILURE\n"; 1706 logmsg @memdata; 1707 # timestamp test result verification end 1708 $timevrfyend{$testnum} = Time::HiRes::time(); 1709 return -1; 1710 } 1711 else { 1712 $ok .= "m"; 1713 } 1714 } 1715 } 1716 else { 1717 $ok .= "-"; # memory not checked 1718 } 1719 1720 my @notexists = getpart("verify", "notexists"); 1721 if(@notexists) { 1722 # a list of directory entries that must not exist 1723 my $err; 1724 while (@notexists) { 1725 my $fname = shift @notexists; 1726 chomp $fname; 1727 if (-e $fname) { 1728 logmsg "Found '$fname' when not supposed to exist.\n"; 1729 $err++; 1730 } 1731 elsif($verbose) { 1732 logmsg "Found '$fname' confirmed to not exist.\n"; 1733 } 1734 } 1735 if($err) { 1736 return -1; 1737 } 1738 } 1739 if($valgrind) { 1740 if($usedvalgrind) { 1741 if(!opendir(DIR, "$logdir")) { 1742 logmsg "ERROR: unable to read $logdir\n"; 1743 # timestamp test result verification end 1744 $timevrfyend{$testnum} = Time::HiRes::time(); 1745 return -1; 1746 } 1747 my @files = readdir(DIR); 1748 closedir(DIR); 1749 my $vgfile; 1750 foreach my $file (@files) { 1751 if($file =~ /^valgrind$testnum(\..*|)$/) { 1752 $vgfile = $file; 1753 last; 1754 } 1755 } 1756 if(!$vgfile) { 1757 logmsg "ERROR: valgrind log file missing for test $testnum\n"; 1758 # timestamp test result verification end 1759 $timevrfyend{$testnum} = Time::HiRes::time(); 1760 return -1; 1761 } 1762 my @e = valgrindparse("$logdir/$vgfile"); 1763 if(@e && $e[0]) { 1764 if($automakestyle) { 1765 logmsg "FAIL: $testnum - $testname - valgrind\n"; 1766 } 1767 else { 1768 logmsg " valgrind ERROR "; 1769 logmsg @e; 1770 } 1771 # timestamp test result verification end 1772 $timevrfyend{$testnum} = Time::HiRes::time(); 1773 return -1; 1774 } 1775 $ok .= "v"; 1776 } 1777 else { 1778 if($verbose) { 1779 logmsg " valgrind SKIPPED\n"; 1780 } 1781 $ok .= "-"; # skipped 1782 } 1783 } 1784 else { 1785 $ok .= "-"; # valgrind not checked 1786 } 1787 # add 'E' for event-based 1788 $ok .= $run_event_based ? "E" : "-"; 1789 1790 logmsg "$ok " if(!$short); 1791 1792 # timestamp test result verification end 1793 $timevrfyend{$testnum} = Time::HiRes::time(); 1794 1795 return 0; 1796} 1797 1798 1799####################################################################### 1800# Report a successful test 1801sub singletest_success { 1802 my ($testnum, $count, $total, $errorreturncode)=@_; 1803 1804 my $sofar= time()-$start; 1805 my $esttotal = $sofar/$count * $total; 1806 my $estleft = $esttotal - $sofar; 1807 my $timeleft=sprintf("remaining: %02d:%02d", 1808 $estleft/60, 1809 $estleft%60); 1810 my $took = $timevrfyend{$testnum} - $timeprepini{$testnum}; 1811 my $duration = sprintf("duration: %02d:%02d", 1812 $sofar/60, $sofar%60); 1813 if(!$automakestyle) { 1814 logmsg sprintf("OK (%-3d out of %-3d, %s, took %.3fs, %s)\n", 1815 $count, $total, $timeleft, $took, $duration); 1816 } 1817 else { 1818 my $testname= (getpart("client", "name"))[0]; 1819 chomp $testname; 1820 logmsg "PASS: $testnum - $testname\n"; 1821 } 1822 1823 if($errorreturncode==2) { 1824 # ignored test success 1825 $passedign .= "$testnum "; 1826 logmsg "Warning: test$testnum result is ignored, but passed!\n"; 1827 } 1828} 1829 1830####################################################################### 1831# Run a single specified test case 1832# This is structured as a state machine which changes state after an 1833# asynchronous call is made that awaits a response. The function returns with 1834# an error code and a flag that indicates if the state machine has completed, 1835# which means (if not) the function must be called again once the response has 1836# arrived. 1837# 1838sub singletest { 1839 my ($runnerid, $testnum, $count, $total)=@_; 1840 1841 # start buffering logmsg; stop it on return 1842 logmsg_bufferfortest($runnerid); 1843 if(!exists $singletest_state{$runnerid}) { 1844 # First time in singletest() for this test 1845 $singletest_state{$runnerid} = ST_INIT; 1846 } 1847 1848 if($singletest_state{$runnerid} == ST_INIT) { 1849 my $logdir = getrunnerlogdir($runnerid); 1850 # first, remove all lingering log & lock files 1851 if((!cleardir($logdir) || !cleardir("$logdir/$LOCKDIR")) 1852 && $clearlocks) { 1853 # On Windows, lock files can't be deleted when the process still 1854 # has them open, so kill those processes first 1855 if(runnerac_clearlocks($runnerid, "$logdir/$LOCKDIR")) { 1856 logmsg "ERROR: runner $runnerid seems to have died\n"; 1857 $singletest_state{$runnerid} = ST_INIT; 1858 return (-1, 0); 1859 } 1860 $singletest_state{$runnerid} = ST_CLEARLOCKS; 1861 } else { 1862 $singletest_state{$runnerid} = ST_INITED; 1863 # Recursively call the state machine again because there is no 1864 # event expected that would otherwise trigger a new call. 1865 return singletest(@_); 1866 } 1867 1868 } elsif($singletest_state{$runnerid} == ST_CLEARLOCKS) { 1869 my ($rid, $logs) = runnerar($runnerid); 1870 if(!$rid) { 1871 logmsg "ERROR: runner $runnerid seems to have died\n"; 1872 $singletest_state{$runnerid} = ST_INIT; 1873 return (-1, 0); 1874 } 1875 logmsg $logs; 1876 my $logdir = getrunnerlogdir($runnerid); 1877 cleardir($logdir); 1878 $singletest_state{$runnerid} = ST_INITED; 1879 # Recursively call the state machine again because there is no 1880 # event expected that would otherwise trigger a new call. 1881 return singletest(@_); 1882 1883 } elsif($singletest_state{$runnerid} == ST_INITED) { 1884 ################################################################### 1885 # Restore environment variables that were modified in a previous run. 1886 # Test definition may instruct to (un)set environment vars. 1887 # This is done this early so that leftover variables don't affect 1888 # starting servers or CI registration. 1889 # restore_test_env(1); 1890 1891 ################################################################### 1892 # Load test file so CI registration can get the right data before the 1893 # runner is called 1894 loadtest("${TESTDIR}/test${testnum}"); 1895 1896 ################################################################### 1897 # Register the test case with the CI environment 1898 citest_starttest($testnum); 1899 1900 if(runnerac_test_preprocess($runnerid, $testnum)) { 1901 logmsg "ERROR: runner $runnerid seems to have died\n"; 1902 $singletest_state{$runnerid} = ST_INIT; 1903 return (-1, 0); 1904 } 1905 $singletest_state{$runnerid} = ST_PREPROCESS; 1906 1907 } elsif($singletest_state{$runnerid} == ST_PREPROCESS) { 1908 my ($rid, $why, $error, $logs, $testtimings) = runnerar($runnerid); 1909 if(!$rid) { 1910 logmsg "ERROR: runner $runnerid seems to have died\n"; 1911 $singletest_state{$runnerid} = ST_INIT; 1912 return (-1, 0); 1913 } 1914 logmsg $logs; 1915 updatetesttimings($testnum, %$testtimings); 1916 if($error == -2) { 1917 if($postmortem) { 1918 # Error indicates an actual problem starting the server, so 1919 # display the server logs 1920 displaylogs($rid, $testnum); 1921 } 1922 } 1923 1924 ####################################################################### 1925 # Load test file for this test number 1926 my $logdir = getrunnerlogdir($runnerid); 1927 loadtest("${logdir}/test${testnum}"); 1928 1929 ####################################################################### 1930 # Print the test name and count tests 1931 $error = singletest_count($testnum, $why); 1932 if($error) { 1933 # Submit the test case result with the CI environment 1934 citest_finishtest($testnum, $error); 1935 $singletest_state{$runnerid} = ST_INIT; 1936 logmsg singletest_dumplogs(); 1937 return ($error, 0); 1938 } 1939 1940 ####################################################################### 1941 # Execute this test number 1942 my $cmdres; 1943 my $CURLOUT; 1944 my $tool; 1945 my $usedvalgrind; 1946 if(runnerac_test_run($runnerid, $testnum)) { 1947 logmsg "ERROR: runner $runnerid seems to have died\n"; 1948 $singletest_state{$runnerid} = ST_INIT; 1949 return (-1, 0); 1950 } 1951 $singletest_state{$runnerid} = ST_RUN; 1952 1953 } elsif($singletest_state{$runnerid} == ST_RUN) { 1954 my ($rid, $error, $logs, $testtimings, $cmdres, $CURLOUT, $tool, $usedvalgrind) = runnerar($runnerid); 1955 if(!$rid) { 1956 logmsg "ERROR: runner $runnerid seems to have died\n"; 1957 $singletest_state{$runnerid} = ST_INIT; 1958 return (-1, 0); 1959 } 1960 logmsg $logs; 1961 updatetesttimings($testnum, %$testtimings); 1962 if($error == -1) { 1963 # no further verification will occur 1964 $timevrfyend{$testnum} = Time::HiRes::time(); 1965 my $err = ignoreresultcode($testnum); 1966 # Submit the test case result with the CI environment 1967 citest_finishtest($testnum, $err); 1968 $singletest_state{$runnerid} = ST_INIT; 1969 logmsg singletest_dumplogs(); 1970 # return a test failure, either to be reported or to be ignored 1971 return ($err, 0); 1972 } 1973 elsif($error == -2) { 1974 # fill in the missing timings on error 1975 timestampskippedevents($testnum); 1976 # Submit the test case result with the CI environment 1977 citest_finishtest($testnum, $error); 1978 $singletest_state{$runnerid} = ST_INIT; 1979 logmsg singletest_dumplogs(); 1980 return ($error, 0); 1981 } 1982 elsif($error > 0) { 1983 # no further verification will occur 1984 $timevrfyend{$testnum} = Time::HiRes::time(); 1985 # Submit the test case result with the CI environment 1986 citest_finishtest($testnum, $error); 1987 $singletest_state{$runnerid} = ST_INIT; 1988 logmsg singletest_dumplogs(); 1989 return ($error, 0); 1990 } 1991 1992 ####################################################################### 1993 # Verify that the test succeeded 1994 # 1995 # Load test file for this test number 1996 my $logdir = getrunnerlogdir($runnerid); 1997 loadtest("${logdir}/test${testnum}"); 1998 readtestkeywords(); 1999 2000 $error = singletest_check($runnerid, $testnum, $cmdres, $CURLOUT, $tool, $usedvalgrind); 2001 if($error == -1) { 2002 my $err = ignoreresultcode($testnum); 2003 # Submit the test case result with the CI environment 2004 citest_finishtest($testnum, $err); 2005 $singletest_state{$runnerid} = ST_INIT; 2006 logmsg singletest_dumplogs(); 2007 # return a test failure, either to be reported or to be ignored 2008 return ($err, 0); 2009 } 2010 elsif($error == -2) { 2011 # torture test; there is no verification, so the run result holds the 2012 # test success code 2013 # Submit the test case result with the CI environment 2014 citest_finishtest($testnum, $cmdres); 2015 $singletest_state{$runnerid} = ST_INIT; 2016 logmsg singletest_dumplogs(); 2017 return ($cmdres, 0); 2018 } 2019 2020 2021 ####################################################################### 2022 # Report a successful test 2023 singletest_success($testnum, $count, $total, ignoreresultcode($testnum)); 2024 2025 # Submit the test case result with the CI environment 2026 citest_finishtest($testnum, 0); 2027 $singletest_state{$runnerid} = ST_INIT; 2028 2029 logmsg singletest_dumplogs(); 2030 return (0, 0); # state machine is finished 2031 } 2032 singletest_unbufferlogs(); 2033 return (0, 1); # state machine must be called again on event 2034} 2035 2036####################################################################### 2037# runtimestats displays test-suite run time statistics 2038# 2039sub runtimestats { 2040 my $lasttest = $_[0]; 2041 2042 return if(not $timestats); 2043 2044 logmsg "::group::Run Time Stats\n"; 2045 2046 logmsg "\nTest suite total running time breakdown per task...\n\n"; 2047 2048 my @timesrvr; 2049 my @timeprep; 2050 my @timetool; 2051 my @timelock; 2052 my @timevrfy; 2053 my @timetest; 2054 my $timesrvrtot = 0.0; 2055 my $timepreptot = 0.0; 2056 my $timetooltot = 0.0; 2057 my $timelocktot = 0.0; 2058 my $timevrfytot = 0.0; 2059 my $timetesttot = 0.0; 2060 my $counter; 2061 2062 for my $testnum (1 .. $lasttest) { 2063 if($timesrvrini{$testnum}) { 2064 $timesrvrtot += $timesrvrend{$testnum} - $timesrvrini{$testnum}; 2065 $timepreptot += 2066 (($timetoolini{$testnum} - $timeprepini{$testnum}) - 2067 ($timesrvrend{$testnum} - $timesrvrini{$testnum})); 2068 $timetooltot += $timetoolend{$testnum} - $timetoolini{$testnum}; 2069 $timelocktot += $timesrvrlog{$testnum} - $timetoolend{$testnum}; 2070 $timevrfytot += $timevrfyend{$testnum} - $timesrvrlog{$testnum}; 2071 $timetesttot += $timevrfyend{$testnum} - $timeprepini{$testnum}; 2072 push @timesrvr, sprintf("%06.3f %04d", 2073 $timesrvrend{$testnum} - $timesrvrini{$testnum}, $testnum); 2074 push @timeprep, sprintf("%06.3f %04d", 2075 ($timetoolini{$testnum} - $timeprepini{$testnum}) - 2076 ($timesrvrend{$testnum} - $timesrvrini{$testnum}), $testnum); 2077 push @timetool, sprintf("%06.3f %04d", 2078 $timetoolend{$testnum} - $timetoolini{$testnum}, $testnum); 2079 push @timelock, sprintf("%06.3f %04d", 2080 $timesrvrlog{$testnum} - $timetoolend{$testnum}, $testnum); 2081 push @timevrfy, sprintf("%06.3f %04d", 2082 $timevrfyend{$testnum} - $timesrvrlog{$testnum}, $testnum); 2083 push @timetest, sprintf("%06.3f %04d", 2084 $timevrfyend{$testnum} - $timeprepini{$testnum}, $testnum); 2085 } 2086 } 2087 2088 { 2089 no warnings 'numeric'; 2090 @timesrvr = sort { $b <=> $a } @timesrvr; 2091 @timeprep = sort { $b <=> $a } @timeprep; 2092 @timetool = sort { $b <=> $a } @timetool; 2093 @timelock = sort { $b <=> $a } @timelock; 2094 @timevrfy = sort { $b <=> $a } @timevrfy; 2095 @timetest = sort { $b <=> $a } @timetest; 2096 } 2097 2098 logmsg "Spent ". sprintf("%08.3f ", $timesrvrtot) . 2099 "seconds starting and verifying test harness servers.\n"; 2100 logmsg "Spent ". sprintf("%08.3f ", $timepreptot) . 2101 "seconds reading definitions and doing test preparations.\n"; 2102 logmsg "Spent ". sprintf("%08.3f ", $timetooltot) . 2103 "seconds actually running test tools.\n"; 2104 logmsg "Spent ". sprintf("%08.3f ", $timelocktot) . 2105 "seconds awaiting server logs lock removal.\n"; 2106 logmsg "Spent ". sprintf("%08.3f ", $timevrfytot) . 2107 "seconds verifying test results.\n"; 2108 logmsg "Spent ". sprintf("%08.3f ", $timetesttot) . 2109 "seconds doing all of the above.\n"; 2110 2111 $counter = 25; 2112 logmsg "\nTest server starting and verification time per test ". 2113 sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); 2114 logmsg "-time- test\n"; 2115 logmsg "------ ----\n"; 2116 foreach my $txt (@timesrvr) { 2117 last if((not $fullstats) && (not $counter--)); 2118 logmsg "$txt\n"; 2119 } 2120 2121 $counter = 10; 2122 logmsg "\nTest definition reading and preparation time per test ". 2123 sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); 2124 logmsg "-time- test\n"; 2125 logmsg "------ ----\n"; 2126 foreach my $txt (@timeprep) { 2127 last if((not $fullstats) && (not $counter--)); 2128 logmsg "$txt\n"; 2129 } 2130 2131 $counter = 25; 2132 logmsg "\nTest tool execution time per test ". 2133 sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); 2134 logmsg "-time- test\n"; 2135 logmsg "------ ----\n"; 2136 foreach my $txt (@timetool) { 2137 last if((not $fullstats) && (not $counter--)); 2138 logmsg "$txt\n"; 2139 } 2140 2141 $counter = 15; 2142 logmsg "\nTest server logs lock removal time per test ". 2143 sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); 2144 logmsg "-time- test\n"; 2145 logmsg "------ ----\n"; 2146 foreach my $txt (@timelock) { 2147 last if((not $fullstats) && (not $counter--)); 2148 logmsg "$txt\n"; 2149 } 2150 2151 $counter = 10; 2152 logmsg "\nTest results verification time per test ". 2153 sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); 2154 logmsg "-time- test\n"; 2155 logmsg "------ ----\n"; 2156 foreach my $txt (@timevrfy) { 2157 last if((not $fullstats) && (not $counter--)); 2158 logmsg "$txt\n"; 2159 } 2160 2161 $counter = 50; 2162 logmsg "\nTotal time per test ". 2163 sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); 2164 logmsg "-time- test\n"; 2165 logmsg "------ ----\n"; 2166 foreach my $txt (@timetest) { 2167 last if((not $fullstats) && (not $counter--)); 2168 logmsg "$txt\n"; 2169 } 2170 2171 logmsg "\n"; 2172 2173 logmsg "::endgroup::\n"; 2174} 2175 2176####################################################################### 2177# returns code indicating why a test was skipped 2178# 0=unknown test, 1=use test result, 2=ignore test result 2179# 2180sub ignoreresultcode { 2181 my ($testnum)=@_; 2182 if(defined $ignoretestcodes{$testnum}) { 2183 return $ignoretestcodes{$testnum}; 2184 } 2185 return 0; 2186} 2187 2188####################################################################### 2189# Put the given runner ID onto the queue of runners ready for a new task 2190# 2191sub runnerready { 2192 my ($runnerid)=@_; 2193 push @runnersidle, $runnerid; 2194} 2195 2196####################################################################### 2197# Create test runners 2198# 2199sub createrunners { 2200 my ($numrunners)=@_; 2201 if(! $numrunners) { 2202 $numrunners++; 2203 } 2204 # create $numrunners runners with minimum 1 2205 for my $runnernum (1..$numrunners) { 2206 my $dir = getrunnernumlogdir($runnernum); 2207 cleardir($dir); 2208 mkdir($dir, 0777); 2209 $runnerids{$runnernum} = runner_init($dir, $jobs); 2210 runnerready($runnerids{$runnernum}); 2211 } 2212} 2213 2214####################################################################### 2215# Pick a test runner for the given test 2216# 2217sub pickrunner { 2218 my ($testnum)=@_; 2219 scalar(@runnersidle) || die "No runners available"; 2220 2221 return pop @runnersidle; 2222} 2223 2224####################################################################### 2225# Check options to this test program 2226# 2227 2228# Special case for CMake: replace '$TFLAGS' by the contents of the 2229# environment variable (if any). 2230if(@ARGV && $ARGV[-1] eq '$TFLAGS') { 2231 pop @ARGV; 2232 push(@ARGV, split(' ', $ENV{'TFLAGS'})) if defined($ENV{'TFLAGS'}); 2233} 2234 2235$args = join(' ', @ARGV); 2236 2237$valgrind = checktestcmd("valgrind"); 2238my $number=0; 2239my $fromnum=-1; 2240my @testthis; 2241while(@ARGV) { 2242 if ($ARGV[0] eq "-v") { 2243 # verbose output 2244 $verbose=1; 2245 } 2246 elsif ($ARGV[0] eq "-c") { 2247 # use this path to curl instead of default 2248 $DBGCURL=$CURL=$ARGV[1]; 2249 shift @ARGV; 2250 } 2251 elsif ($ARGV[0] eq "-vc") { 2252 # use this path to a curl used to verify servers 2253 2254 # Particularly useful when you introduce a crashing bug somewhere in 2255 # the development version as then it won't be able to run any tests 2256 # since it can't verify the servers! 2257 2258 $VCURL=shell_quote($ARGV[1]); 2259 shift @ARGV; 2260 } 2261 elsif ($ARGV[0] eq "-ac") { 2262 # use this curl only to talk to APIs (currently only CI test APIs) 2263 $ACURL=shell_quote($ARGV[1]); 2264 shift @ARGV; 2265 } 2266 elsif ($ARGV[0] eq "-bundle") { 2267 # use test bundles 2268 $bundle=1; 2269 } 2270 elsif ($ARGV[0] eq "-d") { 2271 # have the servers display protocol output 2272 $debugprotocol=1; 2273 } 2274 elsif(($ARGV[0] eq "-e") || ($ARGV[0] eq "--test-event")) { 2275 # run the tests cases event based if possible 2276 $run_event_based=1; 2277 } 2278 elsif($ARGV[0] eq "--test-duphandle") { 2279 # run the tests with --test-duphandle 2280 $run_duphandle=1; 2281 } 2282 elsif($ARGV[0] eq "-f") { 2283 # force - run the test case even if listed in DISABLED 2284 $run_disabled=1; 2285 } 2286 elsif($ARGV[0] eq "-E") { 2287 # load additional reasons to skip tests 2288 shift @ARGV; 2289 my $exclude_file = $ARGV[0]; 2290 open(my $fd, "<", $exclude_file) or die "Couldn't open '$exclude_file': $!"; 2291 while(my $line = <$fd>) { 2292 next if ($line =~ /^#/); 2293 chomp $line; 2294 my ($type, $patterns, $skip_reason) = split(/\s*:\s*/, $line, 3); 2295 2296 die "Unsupported type: $type\n" if($type !~ /^keyword|test|tool$/); 2297 2298 foreach my $pattern (split(/,/, $patterns)) { 2299 if($type eq "test") { 2300 # Strip leading zeros in the test number 2301 $pattern = int($pattern); 2302 } 2303 $custom_skip_reasons{$type}{$pattern} = $skip_reason; 2304 } 2305 } 2306 close($fd); 2307 } 2308 elsif ($ARGV[0] eq "-g") { 2309 # run this test with gdb 2310 $gdbthis=1; 2311 } 2312 elsif ($ARGV[0] eq "-gl") { 2313 # run this test with lldb 2314 $gdbthis=2; 2315 } 2316 elsif ($ARGV[0] eq "-gw") { 2317 # run this test with windowed gdb 2318 $gdbthis=1; 2319 $gdbxwin=1; 2320 } 2321 elsif($ARGV[0] eq "-s") { 2322 # short output 2323 $short=1; 2324 } 2325 elsif($ARGV[0] eq "-am") { 2326 # automake-style output 2327 $short=1; 2328 $automakestyle=1; 2329 } 2330 elsif($ARGV[0] eq "-n") { 2331 # no valgrind 2332 undef $valgrind; 2333 } 2334 elsif($ARGV[0] eq "--no-debuginfod") { 2335 # disable the valgrind debuginfod functionality 2336 $no_debuginfod = 1; 2337 } 2338 elsif ($ARGV[0] eq "-R") { 2339 # execute in scrambled order 2340 $scrambleorder=1; 2341 } 2342 elsif($ARGV[0] =~ /^-t(.*)/) { 2343 # torture 2344 $torture=1; 2345 my $xtra = $1; 2346 2347 if($xtra =~ s/(\d+)$//) { 2348 $tortalloc = $1; 2349 } 2350 } 2351 elsif($ARGV[0] =~ /--shallow=(\d+)/) { 2352 # Fail no more than this amount per tests when running 2353 # torture. 2354 my ($num)=($1); 2355 $shallow=$num; 2356 } 2357 elsif($ARGV[0] =~ /--repeat=(\d+)/) { 2358 # Repeat-run the given tests this many times 2359 $repeat = $1; 2360 } 2361 elsif($ARGV[0] =~ /--seed=(\d+)/) { 2362 # Set a fixed random seed (used for -R and --shallow) 2363 $randseed = $1; 2364 } 2365 elsif($ARGV[0] eq "-a") { 2366 # continue anyway, even if a test fail 2367 $anyway=1; 2368 } 2369 elsif($ARGV[0] eq "-o") { 2370 shift @ARGV; 2371 if ($ARGV[0] =~ /^(\w+)=([\w.:\/\[\]-]+)$/) { 2372 my ($variable, $value) = ($1, $2); 2373 eval "\$$variable='$value'" or die "Failed to set \$$variable to $value: $@"; 2374 } else { 2375 die "Failed to parse '-o $ARGV[0]'. May contain unexpected characters.\n"; 2376 } 2377 } 2378 elsif($ARGV[0] eq "-p") { 2379 $postmortem=1; 2380 } 2381 elsif($ARGV[0] eq "-P") { 2382 shift @ARGV; 2383 $proxy_address=$ARGV[0]; 2384 } 2385 elsif($ARGV[0] eq "-L") { 2386 # require additional library file 2387 shift @ARGV; 2388 require $ARGV[0]; 2389 } 2390 elsif($ARGV[0] eq "-l") { 2391 # lists the test case names only 2392 $listonly=1; 2393 } 2394 elsif($ARGV[0] =~ /^-j(.*)/) { 2395 # parallel jobs 2396 $jobs=1; 2397 my $xtra = $1; 2398 if($xtra =~ s/(\d+)$//) { 2399 $jobs = $1; 2400 } 2401 } 2402 elsif($ARGV[0] eq "-k") { 2403 # keep stdout and stderr files after tests 2404 $keepoutfiles=1; 2405 } 2406 elsif($ARGV[0] eq "-r") { 2407 # run time statistics needs Time::HiRes 2408 if($Time::HiRes::VERSION) { 2409 # presize hashes appropriately to hold an entire test run 2410 keys(%timeprepini) = 2000; 2411 keys(%timesrvrini) = 2000; 2412 keys(%timesrvrend) = 2000; 2413 keys(%timetoolini) = 2000; 2414 keys(%timetoolend) = 2000; 2415 keys(%timesrvrlog) = 2000; 2416 keys(%timevrfyend) = 2000; 2417 $timestats=1; 2418 $fullstats=0; 2419 } 2420 } 2421 elsif($ARGV[0] eq "-rf") { 2422 # run time statistics needs Time::HiRes 2423 if($Time::HiRes::VERSION) { 2424 # presize hashes appropriately to hold an entire test run 2425 keys(%timeprepini) = 2000; 2426 keys(%timesrvrini) = 2000; 2427 keys(%timesrvrend) = 2000; 2428 keys(%timetoolini) = 2000; 2429 keys(%timetoolend) = 2000; 2430 keys(%timesrvrlog) = 2000; 2431 keys(%timevrfyend) = 2000; 2432 $timestats=1; 2433 $fullstats=1; 2434 } 2435 } 2436 elsif($ARGV[0] eq "-rm") { 2437 # force removal of files by killing locking processes 2438 $clearlocks=1; 2439 } 2440 elsif($ARGV[0] eq "-u") { 2441 # error instead of warning on server unexpectedly alive 2442 $err_unexpected=1; 2443 } 2444 elsif(($ARGV[0] eq "-h") || ($ARGV[0] eq "--help")) { 2445 # show help text 2446 print <<"EOHELP" 2447Usage: runtests.pl [options] [test selection(s)] 2448 -a continue even if a test fails 2449 -ac path use this curl only to talk to APIs (currently only CI test APIs) 2450 -am automake style output PASS/FAIL: [number] [name] 2451 -bundle use test bundles 2452 -c path use this curl executable 2453 -d display server debug info 2454 -e, --test-event event-based execution 2455 --test-duphandle duplicate handles before use 2456 -E file load the specified file to exclude certain tests 2457 -f forcibly run even if disabled 2458 -g run the test case with gdb 2459 -gw run the test case with gdb as a windowed application 2460 -h this help text 2461 -j[N] spawn this number of processes to run tests (default 0) 2462 -k keep stdout and stderr files present after tests 2463 -L path require an additional perl library file to replace certain functions 2464 -l list all test case names/descriptions 2465 -n no valgrind 2466 --no-debuginfod disable the valgrind debuginfod functionality 2467 -o variable=value set internal variable to the specified value 2468 -P proxy use the specified proxy 2469 -p print log file contents when a test fails 2470 -R scrambled order (uses the random seed, see --seed) 2471 -r run time statistics 2472 -rf full run time statistics 2473 -rm force removal of files by killing locking processes (Windows only) 2474 --repeat=[num] run the given tests this many times 2475 -s short output 2476 --seed=[num] set the random seed to a fixed number 2477 --shallow=[num] randomly makes the torture tests "thinner" 2478 -t[N] torture (simulate function failures); N means fail Nth function 2479 -u error instead of warning on server unexpectedly alive 2480 -v verbose output 2481 -vc path use this curl only to verify the existing servers 2482 [num] like "5 6 9" or " 5 to 22 " to run those tests only 2483 [!num] like "!5 !6 !9" to disable those tests 2484 [~num] like "~5 ~6 ~9" to ignore the result of those tests 2485 [keyword] like "IPv6" to select only tests containing the key word 2486 [!keyword] like "!cookies" to disable any tests containing the key word 2487 [~keyword] like "~cookies" to ignore results of tests containing key word 2488EOHELP 2489 ; 2490 exit; 2491 } 2492 elsif($ARGV[0] =~ /^(\d+)/) { 2493 $number = $1; 2494 if($fromnum >= 0) { 2495 for my $n ($fromnum .. $number) { 2496 push @testthis, $n; 2497 } 2498 $fromnum = -1; 2499 } 2500 else { 2501 push @testthis, $1; 2502 } 2503 } 2504 elsif($ARGV[0] =~ /^to$/i) { 2505 $fromnum = $number+1; 2506 } 2507 elsif($ARGV[0] =~ /^!(\d+)/) { 2508 $fromnum = -1; 2509 $disabled{$1}=$1; 2510 } 2511 elsif($ARGV[0] =~ /^~(\d+)/) { 2512 $fromnum = -1; 2513 $ignored{$1}=$1; 2514 } 2515 elsif($ARGV[0] =~ /^!(.+)/) { 2516 $disabled_keywords{lc($1)}=$1; 2517 } 2518 elsif($ARGV[0] =~ /^~(.+)/) { 2519 $ignored_keywords{lc($1)}=$1; 2520 } 2521 elsif($ARGV[0] =~ /^([-[{a-zA-Z].*)/) { 2522 $enabled_keywords{lc($1)}=$1; 2523 } 2524 else { 2525 print "Unknown option: $ARGV[0]\n"; 2526 exit; 2527 } 2528 shift @ARGV; 2529} 2530 2531delete $ENV{'DEBUGINFOD_URLS'} if($ENV{'DEBUGINFOD_URLS'} && $no_debuginfod); 2532 2533if(!$randseed) { 2534 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = 2535 localtime(time); 2536 # seed of the month. December 2019 becomes 201912 2537 $randseed = ($year+1900)*100 + $mon+1; 2538 print "Using curl: $CURL\n"; 2539 open(my $curlvh, "-|", shell_quote($CURL) . " --version 2>$dev_null") || 2540 die "could not get curl version!"; 2541 my @c = <$curlvh>; 2542 close($curlvh) || die "could not get curl version!"; 2543 # use the first line of output and get the md5 out of it 2544 my $str = md5($c[0]); 2545 $randseed += unpack('S', $str); # unsigned 16 bit value 2546} 2547srand $randseed; 2548 2549if(@testthis && ($testthis[0] ne "")) { 2550 $TESTCASES=join(" ", @testthis); 2551} 2552 2553if($valgrind) { 2554 # we have found valgrind on the host, use it 2555 2556 # verify that we can invoke it fine 2557 my $code = runclient("valgrind >$dev_null 2>&1"); 2558 2559 if(($code>>8) != 1) { 2560 #logmsg "Valgrind failure, disable it\n"; 2561 undef $valgrind; 2562 } else { 2563 2564 # since valgrind 2.1.x, '--tool' option is mandatory 2565 # use it, if it is supported by the version installed on the system 2566 # (this happened in 2003, so we could probably don't need to care about 2567 # that old version any longer and just delete this check) 2568 runclient("valgrind --help 2>&1 | grep -- --tool >$dev_null 2>&1"); 2569 if (($? >> 8)) { 2570 $valgrind_tool=""; 2571 } 2572 open(my $curlh, "<", "$CURL"); 2573 my $l = <$curlh>; 2574 if($l =~ /^\#\!/) { 2575 # A shell script. This is typically when built with libtool, 2576 $valgrind="../libtool --mode=execute $valgrind"; 2577 } 2578 close($curlh); 2579 2580 # valgrind 3 renamed the --logfile option to --log-file!!! 2581 # (this happened in 2005, so we could probably don't need to care about 2582 # that old version any longer and just delete this check) 2583 my $ver=join(' ', runclientoutput("valgrind --version")); 2584 # cut off all but digits and dots 2585 $ver =~ s/[^0-9.]//g; 2586 2587 if($ver =~ /^(\d+)/) { 2588 $ver = $1; 2589 if($ver < 3) { 2590 $valgrind_logfile="--logfile"; 2591 } 2592 } 2593 } 2594} 2595 2596if ($gdbthis) { 2597 # open the executable curl and read the first 4 bytes of it 2598 open(my $check, "<", "$CURL"); 2599 my $c; 2600 sysread $check, $c, 4; 2601 close($check); 2602 if($c eq "#! /") { 2603 # A shell script. This is typically when built with libtool, 2604 $libtool = 1; 2605 $gdb = "../libtool --mode=execute gdb"; 2606 } 2607} 2608 2609####################################################################### 2610# clear and create logging directory: 2611# 2612 2613# TODO: figure how to get around this. This dir is needed for checksystemfeatures() 2614# Maybe create & use & delete a temporary directory in that function 2615cleardir($LOGDIR); 2616mkdir($LOGDIR, 0777); 2617mkdir("$LOGDIR/$LOCKDIR", 0777); 2618 2619####################################################################### 2620# initialize some variables 2621# 2622 2623get_disttests(); 2624if(!$jobs) { 2625 # Disable buffered logging with only one test job 2626 setlogfunc(\&logmsg); 2627} 2628 2629####################################################################### 2630# Output curl version and host info being tested 2631# 2632 2633if(!$listonly) { 2634 checksystemfeatures(); 2635} 2636 2637####################################################################### 2638# Output information about the curl build 2639# 2640if(!$listonly) { 2641 if(open(my $fd, "<", "../buildinfo.txt")) { 2642 while(my $line = <$fd>) { 2643 chomp $line; 2644 if($line && $line !~ /^#/) { 2645 logmsg("* $line\n"); 2646 } 2647 } 2648 close($fd); 2649 } 2650} 2651 2652####################################################################### 2653# initialize configuration needed to set up servers 2654# TODO: rearrange things so this can be called only in runner_init() 2655# 2656initserverconfig(); 2657 2658if(!$listonly) { 2659 # these can only be displayed after initserverconfig() has been called 2660 displayserverfeatures(); 2661 2662 # globally disabled tests 2663 disabledtests("$TESTDIR/DISABLED"); 2664} 2665 2666####################################################################### 2667# Fetch all disabled tests, if there are any 2668# 2669 2670sub disabledtests { 2671 my ($file) = @_; 2672 my @input; 2673 2674 if(open(my $disabledh, "<", "$file")) { 2675 while(<$disabledh>) { 2676 if(/^ *\#/) { 2677 # allow comments 2678 next; 2679 } 2680 push @input, $_; 2681 } 2682 close($disabledh); 2683 2684 # preprocess the input to make conditionally disabled tests depending 2685 # on variables 2686 my @pp = prepro(0, @input); 2687 for my $t (@pp) { 2688 if($t =~ /(\d+)/) { 2689 my ($n) = $1; 2690 $disabled{$n}=$n; # disable this test number 2691 if(! -f "$srcdir/data/test$n") { 2692 print STDERR "WARNING! Non-existing test $n in $file!\n"; 2693 # fail hard to make user notice 2694 exit 1; 2695 } 2696 logmsg "DISABLED: test $n\n" if ($verbose); 2697 } 2698 else { 2699 print STDERR "$file: rubbish content: $t\n"; 2700 exit 2; 2701 } 2702 } 2703 } 2704 else { 2705 print STDERR "Cannot open $file, exiting\n"; 2706 exit 3; 2707 } 2708} 2709 2710####################################################################### 2711# If 'all' tests are requested, find out all test numbers 2712# 2713 2714if ( $TESTCASES eq "all") { 2715 # Get all commands and find out their test numbers 2716 opendir(DIR, $TESTDIR) || die "can't opendir $TESTDIR: $!"; 2717 my @cmds = grep { /^test([0-9]+)$/ && -f "$TESTDIR/$_" } readdir(DIR); 2718 closedir(DIR); 2719 2720 $TESTCASES=""; # start with no test cases 2721 2722 # cut off everything but the digits 2723 for(@cmds) { 2724 $_ =~ s/[a-z\/\.]*//g; 2725 } 2726 # sort the numbers from low to high 2727 foreach my $n (sort { $a <=> $b } @cmds) { 2728 if($disabled{$n}) { 2729 # skip disabled test cases 2730 my $why = "configured as DISABLED"; 2731 $skipped{$why}++; 2732 $teststat[$n]=$why; # store reason for this test case 2733 next; 2734 } 2735 $TESTCASES .= " $n"; 2736 } 2737} 2738else { 2739 my $verified=""; 2740 for(split(" ", $TESTCASES)) { 2741 if (-e "$TESTDIR/test$_") { 2742 $verified.="$_ "; 2743 } 2744 } 2745 if($verified eq "") { 2746 print "No existing test cases were specified\n"; 2747 exit; 2748 } 2749 $TESTCASES = $verified; 2750} 2751if($repeat) { 2752 my $s; 2753 for(1 .. $repeat) { 2754 $s .= $TESTCASES; 2755 } 2756 $TESTCASES = $s; 2757} 2758 2759if($scrambleorder) { 2760 # scramble the order of the test cases 2761 my @rand; 2762 while($TESTCASES) { 2763 my @all = split(/ +/, $TESTCASES); 2764 if(!$all[0]) { 2765 # if the first is blank, shift away it 2766 shift @all; 2767 } 2768 my $r = rand @all; 2769 push @rand, $all[$r]; 2770 $all[$r]=""; 2771 $TESTCASES = join(" ", @all); 2772 } 2773 $TESTCASES = join(" ", @rand); 2774} 2775 2776# Display the contents of the given file. Line endings are canonicalized 2777# and excessively long files are elided 2778sub displaylogcontent { 2779 my ($file)=@_; 2780 if(open(my $single, "<", "$file")) { 2781 my $linecount = 0; 2782 my $truncate; 2783 my @tail; 2784 while(my $string = <$single>) { 2785 $string =~ s/\r\n/\n/g; 2786 $string =~ s/[\r\f\032]/\n/g; 2787 $string .= "\n" unless ($string =~ /\n$/); 2788 $string =~ tr/\n//; 2789 for my $line (split(m/\n/, $string)) { 2790 $line =~ s/\s*\!$//; 2791 if ($truncate) { 2792 push @tail, " $line\n"; 2793 } else { 2794 logmsg " $line\n"; 2795 } 2796 $linecount++; 2797 $truncate = $linecount > 1200; 2798 } 2799 } 2800 close($single); 2801 if(@tail) { 2802 my $tailshow = 200; 2803 my $tailskip = 0; 2804 my $tailtotal = scalar @tail; 2805 if($tailtotal > $tailshow) { 2806 $tailskip = $tailtotal - $tailshow; 2807 logmsg "=== File too long: $tailskip lines omitted here\n"; 2808 } 2809 for($tailskip .. $tailtotal-1) { 2810 logmsg "$tail[$_]"; 2811 } 2812 } 2813 } 2814} 2815 2816sub displaylogs { 2817 my ($runnerid, $testnum)=@_; 2818 my $logdir = getrunnerlogdir($runnerid); 2819 opendir(DIR, "$logdir") || 2820 die "can't open dir: $!"; 2821 my @logs = readdir(DIR); 2822 closedir(DIR); 2823 2824 logmsg "== Contents of files in the $logdir/ dir after test $testnum\n"; 2825 foreach my $log (sort @logs) { 2826 if($log =~ /\.(\.|)$/) { 2827 next; # skip "." and ".." 2828 } 2829 if($log =~ /^\.nfs/) { 2830 next; # skip ".nfs" 2831 } 2832 if(($log eq "memdump") || ($log eq "core")) { 2833 next; # skip "memdump" and "core" 2834 } 2835 if((-d "$logdir/$log") || (! -s "$logdir/$log")) { 2836 next; # skip directory and empty files 2837 } 2838 if(($log =~ /^stdout\d+/) && ($log !~ /^stdout$testnum/)) { 2839 next; # skip stdoutNnn of other tests 2840 } 2841 if(($log =~ /^stderr\d+/) && ($log !~ /^stderr$testnum/)) { 2842 next; # skip stderrNnn of other tests 2843 } 2844 if(($log =~ /^upload\d+/) && ($log !~ /^upload$testnum/)) { 2845 next; # skip uploadNnn of other tests 2846 } 2847 if(($log =~ /^curl\d+\.out/) && ($log !~ /^curl$testnum\.out/)) { 2848 next; # skip curlNnn.out of other tests 2849 } 2850 if(($log =~ /^test\d+\.txt/) && ($log !~ /^test$testnum\.txt/)) { 2851 next; # skip testNnn.txt of other tests 2852 } 2853 if(($log =~ /^file\d+\.txt/) && ($log !~ /^file$testnum\.txt/)) { 2854 next; # skip fileNnn.txt of other tests 2855 } 2856 if(($log =~ /^netrc\d+/) && ($log !~ /^netrc$testnum/)) { 2857 next; # skip netrcNnn of other tests 2858 } 2859 if(($log =~ /^trace\d+/) && ($log !~ /^trace$testnum/)) { 2860 next; # skip traceNnn of other tests 2861 } 2862 if(($log =~ /^valgrind\d+/) && ($log !~ /^valgrind$testnum(?:\..*)?$/)) { 2863 next; # skip valgrindNnn of other tests 2864 } 2865 if(($log =~ /^test$testnum$/)) { 2866 next; # skip test$testnum since it can be very big 2867 } 2868 logmsg "=== Start of file $log\n"; 2869 displaylogcontent("$logdir/$log"); 2870 logmsg "=== End of file $log\n"; 2871 } 2872} 2873 2874####################################################################### 2875# Scan tests to find suitable candidates 2876# 2877 2878my $failed; 2879my $failedign; 2880my $ok=0; 2881my $ign=0; 2882my $total=0; 2883my $lasttest=0; 2884my @at = split(" ", $TESTCASES); 2885my $count=0; 2886my $endwaitcnt=0; 2887 2888$start = time(); 2889 2890# scan all tests to find ones we should try to run 2891my @runtests; 2892foreach my $testnum (@at) { 2893 $lasttest = $testnum if($testnum > $lasttest); 2894 my ($why, $errorreturncode) = singletest_shouldrun($testnum); 2895 if($why || $listonly) { 2896 # Display test name now--test will be completely skipped later 2897 my $error = singletest_count($testnum, $why); 2898 next; 2899 } 2900 $ignoretestcodes{$testnum} = $errorreturncode; 2901 push(@runtests, $testnum); 2902} 2903my $totaltests = scalar(@runtests); 2904 2905if($listonly) { 2906 exit(0); 2907} 2908 2909####################################################################### 2910# Setup CI Test Run 2911citest_starttestrun(); 2912 2913####################################################################### 2914# Start test runners 2915# 2916my $numrunners = $jobs < scalar(@runtests) ? $jobs : scalar(@runtests); 2917createrunners($numrunners); 2918 2919####################################################################### 2920# The main test-loop 2921# 2922# Every iteration through the loop consists of these steps: 2923# - if the global abort flag is set, exit the loop; we are done 2924# - if a runner is idle, start a new test on it 2925# - if all runners are idle, exit the loop; we are done 2926# - if a runner has a response for us, process the response 2927 2928# run through each candidate test and execute it 2929my $runner_wait_cnt = 0; 2930while () { 2931 # check the abort flag 2932 if($globalabort) { 2933 logmsg singletest_dumplogs(); 2934 logmsg "Aborting tests\n"; 2935 logmsg "Waiting for " . scalar((keys %runnersrunning)) . " outstanding test(s) to finish...\n"; 2936 # Wait for the last requests to complete and throw them away so 2937 # that IPC calls & responses stay in sync 2938 # TODO: send a signal to the runners to interrupt a long test 2939 foreach my $rid (keys %runnersrunning) { 2940 runnerar($rid); 2941 delete $runnersrunning{$rid}; 2942 logmsg "."; 2943 $| = 1; 2944 } 2945 logmsg "\n"; 2946 last; 2947 } 2948 2949 # Start a new test if possible 2950 if(scalar(@runnersidle) && scalar(@runtests)) { 2951 # A runner is ready to run a test, and tests are still available to run 2952 # so start a new test. 2953 $count++; 2954 my $testnum = shift(@runtests); 2955 2956 # pick a runner for this new test 2957 my $runnerid = pickrunner($testnum); 2958 $countforrunner{$runnerid} = $count; 2959 2960 # Start the test 2961 my ($error, $again) = singletest($runnerid, $testnum, $countforrunner{$runnerid}, $totaltests); 2962 if($again) { 2963 # this runner is busy running a test 2964 $runnersrunning{$runnerid} = $testnum; 2965 } else { 2966 runnerready($runnerid); 2967 if($error >= 0) { 2968 # We make this simplifying assumption to avoid having to handle 2969 # $error properly here, but we must handle the case of runner 2970 # death without abending here. 2971 die "Internal error: test must not complete on first call"; 2972 } 2973 } 2974 } 2975 2976 # See if we've completed all the tests 2977 if(!scalar(%runnersrunning)) { 2978 # No runners are running; we must be done 2979 scalar(@runtests) && die 'Internal error: still have tests to run'; 2980 last; 2981 } 2982 2983 # See if a test runner needs attention 2984 # If we could be running more tests, don't wait so we can schedule a new 2985 # one immediately. If all runners are busy, wait a fraction of a second 2986 # for one to finish so we can still loop around to check the abort flag. 2987 my $runnerwait = scalar(@runnersidle) && scalar(@runtests) ? 0 : 1.0; 2988 my (@ridsready, $riderror) = runnerar_ready($runnerwait); 2989 if(@ridsready) { 2990 for my $ridready (@ridsready) { 2991 if($ridready && ! defined $runnersrunning{$ridready}) { 2992 # On Linux, a closed pipe still shows up as ready instead of error. 2993 # Detect this here by seeing if we are expecting it to be ready and 2994 # treat it as an error if not. 2995 logmsg "ERROR: Runner $ridready is unexpectedly ready; is probably actually dead\n"; 2996 $riderror = $ridready; 2997 undef $ridready; 2998 } 2999 if($ridready) { 3000 # This runner is ready to be serviced 3001 my $testnum = $runnersrunning{$ridready}; 3002 defined $testnum || die "Internal error: test for runner $ridready unknown"; 3003 delete $runnersrunning{$ridready}; 3004 my ($error, $again) = singletest($ridready, $testnum, $countforrunner{$ridready}, $totaltests); 3005 if($again) { 3006 # this runner is busy running a test 3007 $runnersrunning{$ridready} = $testnum; 3008 } else { 3009 # Test is complete 3010 $runner_wait_cnt = 0; 3011 runnerready($ridready); 3012 3013 if($error < 0) { 3014 # not a test we can run 3015 next; 3016 } 3017 3018 $total++; # number of tests we've run 3019 3020 if($error>0) { 3021 if($error==2) { 3022 # ignored test failures 3023 $failedign .= "$testnum "; 3024 } 3025 else { 3026 $failed.= "$testnum "; 3027 } 3028 if($postmortem) { 3029 # display all files in $LOGDIR/ in a nice way 3030 displaylogs($ridready, $testnum); 3031 } 3032 if($error==2) { 3033 $ign++; # ignored test result counter 3034 } 3035 elsif(!$anyway) { 3036 # a test failed, abort 3037 logmsg "\n - abort tests\n"; 3038 undef @runtests; # empty out the remaining tests 3039 } 3040 } 3041 elsif(!$error) { 3042 $ok++; # successful test counter 3043 } 3044 } 3045 } 3046 } 3047 } 3048 if(!@ridsready && $runnerwait && !$torture && scalar(%runnersrunning)) { 3049 $runner_wait_cnt++; 3050 if($runner_wait_cnt >= 5) { 3051 my $msg = "waiting for " . scalar(%runnersrunning) . " results:"; 3052 my $sep = " "; 3053 foreach my $rid (keys %runnersrunning) { 3054 $msg .= $sep . $runnersrunning{$rid} . "[$rid]"; 3055 $sep = ", " 3056 } 3057 logmsg "$msg\n"; 3058 } 3059 if($runner_wait_cnt >= 10) { 3060 $runner_wait_cnt = 0; 3061 foreach my $rid (keys %runnersrunning) { 3062 my $testnum = $runnersrunning{$rid}; 3063 logmsg "current state of test $testnum in [$rid]:\n"; 3064 displaylogs($rid, $testnum); 3065 } 3066 } 3067 } 3068 if($riderror) { 3069 logmsg "ERROR: runner $riderror is dead! aborting test run\n"; 3070 delete $runnersrunning{$riderror} if(defined $runnersrunning{$riderror}); 3071 $globalabort = 1; 3072 } 3073 if(!scalar(@runtests) && ++$endwaitcnt == (240 + $jobs)) { 3074 # Once all tests have been scheduled on a runner at the end of a test 3075 # run, we just wait for their results to come in. If we're still 3076 # waiting after a couple of minutes ($endwaitcnt multiplied by 3077 # $runnerwait, plus $jobs because that number won't time out), display 3078 # the same test runner status as we give with a SIGUSR1. This will 3079 # likely point to a single test that has hung. 3080 logmsg "Hmmm, the tests are taking a while to finish. Here is the status:\n"; 3081 catch_usr1(); 3082 } 3083} 3084 3085my $sofar = time() - $start; 3086 3087####################################################################### 3088# Finish CI Test Run 3089citest_finishtestrun(); 3090 3091# Tests done, stop the servers 3092foreach my $runnerid (values %runnerids) { 3093 runnerac_stopservers($runnerid); 3094} 3095 3096# Wait for servers to stop 3097my $unexpected; 3098foreach my $runnerid (values %runnerids) { 3099 my ($rid, $unexpect, $logs) = runnerar($runnerid); 3100 $unexpected ||= $unexpect; 3101 logmsg $logs; 3102} 3103 3104# Kill the runners 3105# There is a race condition here since we don't know exactly when the runners 3106# have each finished shutting themselves down, but we're about to exit so it 3107# doesn't make much difference. 3108foreach my $runnerid (values %runnerids) { 3109 runnerac_shutdown($runnerid); 3110 sleep 0; # give runner a context switch so it can shut itself down 3111} 3112 3113my $numskipped = %skipped ? sum values %skipped : 0; 3114my $all = $total + $numskipped; 3115 3116runtimestats($lasttest); 3117 3118if($all) { 3119 logmsg "TESTDONE: $all tests were considered during ". 3120 sprintf("%.0f", $sofar) ." seconds.\n"; 3121} 3122 3123if(%skipped && !$short) { 3124 my $s=0; 3125 # Temporary hash to print the restraints sorted by the number 3126 # of their occurrences 3127 my %restraints; 3128 logmsg "TESTINFO: $numskipped tests were skipped due to these restraints:\n"; 3129 3130 for(keys %skipped) { 3131 my $r = $_; 3132 my $skip_count = $skipped{$r}; 3133 my $log_line = sprintf("TESTINFO: \"%s\" %d time%s (", $r, $skip_count, 3134 ($skip_count == 1) ? "" : "s"); 3135 3136 # now gather all test case numbers that had this reason for being 3137 # skipped 3138 my $c=0; 3139 my $max = 9; 3140 for(0 .. scalar @teststat) { 3141 my $t = $_; 3142 if($teststat[$t] && ($teststat[$t] eq $r)) { 3143 if($c < $max) { 3144 $log_line .= ", " if($c); 3145 $log_line .= $t; 3146 } 3147 $c++; 3148 } 3149 } 3150 if($c > $max) { 3151 $log_line .= " and ".($c-$max)." more"; 3152 } 3153 $log_line .= ")\n"; 3154 $restraints{$log_line} = $skip_count; 3155 } 3156 foreach my $log_line (sort {$restraints{$b} <=> $restraints{$a} || uc($a) cmp uc($b)} keys %restraints) { 3157 logmsg $log_line; 3158 } 3159} 3160 3161sub testnumdetails { 3162 my ($desc, $numlist) = @_; 3163 foreach my $testnum (split(' ', $numlist)) { 3164 if(!loadtest("${TESTDIR}/test${testnum}")) { 3165 my @info_keywords = getpart("info", "keywords"); 3166 my $testname = (getpart("client", "name"))[0]; 3167 chomp $testname; 3168 logmsg "$desc $testnum: '$testname'"; 3169 my $first = 1; 3170 for my $k (@info_keywords) { 3171 chomp $k; 3172 my $sep = ($first == 1) ? " " : ", "; 3173 logmsg "$sep$k"; 3174 $first = 0; 3175 } 3176 logmsg "\n"; 3177 } 3178 } 3179} 3180 3181if($total) { 3182 if($passedign) { 3183 my $sorted = numsortwords($passedign); 3184 logmsg "::group::Passed Ignored Test details\n"; 3185 testnumdetails("PASSED-IGNORED", $sorted); 3186 logmsg "IGNORED: passed tests: $sorted\n"; 3187 logmsg "::endgroup::\n"; 3188 } 3189 3190 if($failedign) { 3191 my $sorted = numsortwords($failedign); 3192 testnumdetails("FAIL-IGNORED", $sorted); 3193 logmsg "IGNORED: failed tests: $sorted\n"; 3194 } 3195 logmsg sprintf("TESTDONE: $ok tests out of $total reported OK: %d%%\n", 3196 $ok/$total*100); 3197 3198 if($failed && ($ok != $total)) { 3199 my $failedsorted = numsortwords($failed); 3200 logmsg "\n"; 3201 testnumdetails("FAIL", $failedsorted); 3202 logmsg "\nTESTFAIL: These test cases failed: $failedsorted\n\n"; 3203 } 3204} 3205else { 3206 logmsg "\nTESTFAIL: No tests were performed\n\n"; 3207 if(scalar(keys %enabled_keywords)) { 3208 logmsg "TESTFAIL: Nothing matched these keywords: "; 3209 for(keys %enabled_keywords) { 3210 logmsg "$_ "; 3211 } 3212 logmsg "\n"; 3213 } 3214} 3215 3216if(($total && (($ok+$ign) != $total)) || !$total || $unexpected) { 3217 exit 1; 3218} 3219