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