Skip to content
runtests.pl 79.3 KiB
Newer Older
    my $pidfile = $SSHPIDFILE;

    my $pid = checkserver($pidfile);
    if($pid > 0) {
        stopserver($pid);
    }

    my $flag=$debugprotocol?"-v ":"";
    my $cmd="$perl $srcdir/sshserver.pl $flag-u $USER -l $HOSTIP -d $srcdir $port";
        startnew($cmd, $pidfile,0); # start the server in a new process
    if($sshpid <= 0 || !kill(0, $sshpid)) {
        logmsg "RUN: failed to start the SSH server\n";
        # failed to talk to it properly. Kill the server and return failure
        stopserver("$sshpid $pid2");
        return -1;
    }

    if (!verifyserver('ssh',$ip,$port)) {
        logmsg "RUN: SSH server failed verification\n";
        return (0,0);
    }
    if($verbose) {
        logmsg "RUN: SSH server is now running PID $sshpid\n";
    }

    return ($pid2, $sshpid);
}

#######################################################################
# Start the socks server
#
sub runsocksserver {
    my ($id, $verbose, $ipv6) = @_;
    my $ip=$HOSTIP;
    my $port = $SOCKSPORT;
    my $pidfile = $SOCKSPIDFILE;

    my $flag=$debugprotocol?"-v ":"";
    my $cmd="ssh -D ${HOSTIP}:$SOCKSPORT -N -F curl_ssh_config ${USER}\@${HOSTIP} -p ${SSHPORT} -vv >log/ssh.log 2>&1";
    my ($sshpid, $pid2) =
        startnew($cmd, $pidfile,1); # start the server in a new process

    if($sshpid <= 0 || !kill(0, $sshpid)) {
        # it is NOT alive
        logmsg "RUN: failed to start the SOCKS server\n";
        # failed to talk to it properly. Kill the server and return failure
        stopserver("$sshpid $pid2");
        return (0,0);
    }

    # Ugly hack but ssh doesn't support pid files
    if (!verifyserver('socks',$ip,$port)) {
        logmsg "RUN: SOCKS server failed verification\n";
        return (0,0);
    }
    if($verbose) {
        logmsg "RUN: SOCKS server is now running PID $sshpid\n";
    }

    return ($pid2, $sshpid);
}

#######################################################################
# Remove all files in the specified directory
#
sub cleardir {
    my $dir = $_[0];
    my $count;
Daniel Stenberg's avatar
Daniel Stenberg committed
    my $file;

    # Get all files
    opendir(DIR, $dir) ||
        return 0; # can't open dir
    while($file = readdir(DIR)) {
        if($file !~ /^\./) {
            unlink("$dir/$file");
            $count++;
        }
    }
    closedir DIR;
    return $count;
}

#######################################################################
# filter out the specified pattern from the given input file and store the
# results in the given output file
#
sub filteroff {
    my $infile=$_[0];
    my $filter=$_[1];
    my $ofile=$_[2];

    # logmsg "FILTER: off $filter from $infile to $ofile\n";
        $_ =~ s/$filter//;

#######################################################################
# compare test results with the expected output, we might filter off
# some pattern that is allowed to differ, output test results
#

sub compare {
    # filter off patterns _before_ this comparison!
    my ($subject, $firstref, $secondref)=@_;
    my $result = compareparts($firstref, $secondref);
            logmsg "\n $subject FAILED:\n";
            logmsg showdiff($LOGDIR, $firstref, $secondref);
    return $result;
#######################################################################
# display information about curl and the host the test suite runs on
#

    unlink($memdump); # remove this if there was one left

    my $versretval;
    my $versnoexec;
    my @version=();

    my $curlverout="$LOGDIR/curlverout.log";
    my $curlvererr="$LOGDIR/curlvererr.log";
    my $versioncmd="$CURL --version 1>$curlverout 2>$curlvererr";

    unlink($curlverout);
    unlink($curlvererr);

    $versretval = runclient($versioncmd);
    open(VERSOUT, "<$curlverout");
    @version = <VERSOUT>;
    close(VERSOUT);
        if($_ =~ /^curl/) {
            $curl = $_;
            $curl =~ s/^(.*)(libcurl.*)/$1/g;
            if($curl =~ /mingw32/) {
                # This is a windows minw32 build, we need to translate the
                # given path to the "actual" windows path.

                my @m = `mount`;
                my $matchlen;
                my $bestmatch;
                my $mount;

# example mount output:
# C:\DOCUME~1\Temp on /tmp type user (binmode,noumount)
# c:\ActiveState\perl on /perl type user (binmode)
# C:\msys\1.0\bin on /usr/bin type user (binmode,cygexec,noumount)
# C:\msys\1.0\bin on /bin type user (binmode,cygexec,noumount)

                foreach $mount (@m) {
                    if( $mount =~ /(.*) on ([^ ]*) type /) {
                        my ($mingw, $real)=($2, $1);
                        if($pwd =~ /^$mingw/) {
                            # the path we got from pwd starts with the path
                            # we found on this line in the mount output

                            my $len = length($real);
                            if($len > $matchlen) {
                                # we remember the match that is the longest
                                $matchlen = $len;
                                $bestmatch = $real;
                            }
                        }
                    }
                }
                if(!$matchlen) {
                    logmsg "Serious error, can't find our \"real\" path\n";
                }
                else {
                    # now prepend the prefix from the mount command to build
                    # our "actual path"
                    $pwd = "$bestmatch$pwd";
                }
                $pwd =~ s#\\#/#g;
            }
            elsif ($curl =~ /win32/) {
               # Native Windows builds don't understand the
               # output of cygwin's pwd.  It will be
               # something like /cygdrive/c/<some path>.
               #
               # Use the cygpath utility to convert the
               # working directory to a Windows friendly
               # path.  The -m option converts to use drive
               # letter:, but it uses / instead \.  Forward
               # slashes (/) are easier for us.  We don't
               # have to escape them to get them to curl
               # through a shell.
               chomp($pwd = `cygpath -m $pwd`);
           }
           elsif ($libcurl =~ /openssl/i) {
           elsif ($libcurl =~ /gnutls/i) {
               $ssllib="NSS";
           }
           elsif ($libcurl =~ /yassl/i) {
               $has_yassl=1;
               $has_openssl=1;
               $ssllib="yassl";
            # these are the protocols compiled in to this libcurl
            @protocols = split(' ', $1);

            # Generate a "proto-ipv6" version of each protocol to match the
            # IPv6 <server> name. This works even if IPv6 support isn't
            # compiled in because the <features> test will fail.
            push @protocols, map($_ . "-ipv6", @protocols);

            # 'none' is used in test cases to mean no server
            push @protocols, ('none');
            if($feat =~ /debug/i) {
                # debug is a listed "feature", use that knowledge
                $curl_debug = 1;
                # set the NETRC debug env
                $ENV{'CURL_DEBUG_NETRC'} = 'log/netrc';
            }
            if($feat =~ /SSL/i) {
                # ssl enabled
                $ssl_version=1;
            }
            if($feat =~ /Largefile/i) {
                # large file support
                $large_file=1;
            }
Daniel Stenberg's avatar
Daniel Stenberg committed
            if($feat =~ /IPv6/i) {
                $has_ipv6 = 1;
            }
        logmsg "unable to get curl's version, further details are:\n";
        logmsg "issued command: \n";
        if ($versretval == -1) {
            logmsg "command failed with: \n";
            logmsg "$versnoexec \n";
        }
        elsif ($versretval & 127) {
            logmsg sprintf("command died with signal %d, and %s coredump.\n",
                           ($versretval & 127), ($versretval & 128)?"a":"no");
        }
        else {
            logmsg sprintf("command exited with value %d \n", $versretval >> 8);
        logmsg "contents of $curlverout: \n";
        displaylogcontent("$curlverout");
        logmsg "contents of $curlvererr: \n";
        displaylogcontent("$curlvererr");
        die "couldn't get curl's version";
        open(CONF, "<../lib/config.h");
            if($_ =~ /^\#define HAVE_GETRLIMIT/) {
                $has_getrlimit = 1;
            }
        }
        # client has ipv6 support

        # check if the HTTP server has it!
        my @sws = `server/sws --version`;
        if($sws[0] =~ /IPv6/) {
            # HTTP server has ipv6 support!
            $http_ipv6 = 1;
        }
        @sws = `server/sockfilt --version`;
        if($sws[0] =~ /IPv6/) {
            # FTP server has ipv6 support!
            $ftp_ipv6 = 1;
        }
    if(!$curl_debug && $torture) {
        die "can't run torture tests since curl was not build with debug";
    }

    # curl doesn't list cryptographic support separately, so assume it's
    # always available
    $has_crypto=1;

    my $hostname=join(' ', runclientoutput("hostname"));
    my $hosttype=join(' ', runclientoutput("uname -a"));
    logmsg ("********* System characteristics ******** \n",
    "* Features: $feat\n",
Daniel Stenberg's avatar
Daniel Stenberg committed
    "* Host: $hostname",
    "* System: $hosttype");

    logmsg sprintf("* Server SSL:     %s\n", $stunnel?"ON":"OFF");
    logmsg sprintf("* libcurl SSL:    %s\n", $ssl_version?"ON":"OFF");
    logmsg sprintf("* libcurl debug:  %s\n", $curl_debug?"ON":"OFF");
    logmsg sprintf("* valgrind:       %s\n", $valgrind?"ON":"OFF");
    logmsg sprintf("* HTTP IPv6       %s\n", $http_ipv6?"ON":"OFF");
    logmsg sprintf("* FTP IPv6        %s\n", $ftp_ipv6?"ON":"OFF");

    logmsg sprintf("* HTTP port:      %d\n", $HTTPPORT);
    logmsg sprintf("* FTP port:       %d\n", $FTPPORT);
    logmsg sprintf("* FTP port 2:     %d\n", $FTP2PORT);
        logmsg sprintf("* FTPS port:      %d\n", $FTPSPORT);
        logmsg sprintf("* HTTPS port:     %d\n", $HTTPSPORT);
        logmsg sprintf("* HTTP IPv6 port: %d\n", $HTTP6PORT);
        logmsg sprintf("* FTP IPv6 port:  %d\n", $FTP6PORT);
    logmsg sprintf("* TFTP port:      %d\n", $TFTPPORT);
    if($tftp_ipv6) {
        logmsg sprintf("* TFTP IPv6 port: %d\n", $TFTP6PORT);
    }
    logmsg sprintf("* SCP/SFTP port:  %d\n", $SSHPORT);
    logmsg sprintf("* SOCKS port:     %d\n", $SOCKSPORT);
        logmsg sprintf("* SSL library:    %s\n", $ssllib);
    $has_textaware = ($^O eq 'MSWin32') || ($^O eq 'msys');

    logmsg sprintf("* Libtool lib:    %s\n", $libtool?"ON":"OFF");
    logmsg "***************************************** \n";
#######################################################################
# substitute the variable stuff into either a joined up file or
# a command, in either case passed by reference
#
sub subVariables {
  my ($thing) = @_;
  $$thing =~ s/%HOSTIP/$HOSTIP/g;
  $$thing =~ s/%HOST6IP/$HOST6IP/g;
  $$thing =~ s/%HTTP6PORT/$HTTP6PORT/g;
  $$thing =~ s/%HTTPSPORT/$HTTPSPORT/g;
  $$thing =~ s/%FTPPORT/$FTPPORT/g;
  $$thing =~ s/%FTPSPORT/$FTPSPORT/g;
  $$thing =~ s/%SRCDIR/$srcdir/g;
  $$thing =~ s/%PWD/$pwd/g;
  $$thing =~ s/%TFTPPORT/$TFTPPORT/g;
  $$thing =~ s/%TFTP6PORT/$TFTP6PORT/g;
  $$thing =~ s/%SSHPORT/$SSHPORT/g;
  $$thing =~ s/%SOCKSPORT/$SOCKSPORT/g;
  $$thing =~ s/%CLIENTIP/$CLIENTIP/g;
  $$thing =~ s/%CLIENT6IP/$CLIENT6IP/g;

  # The purpose of FTPTIME2 and FTPTIME3 is to provide times that can be
  # used for time-out tests and that whould work on most hosts as these
  # adjust for the startup/check time for this particular host. We needed
  # to do this to make the test suite run better on very slow hosts.

  my $ftp2 = $ftpchecktime * 2;
  my $ftp3 = $ftpchecktime * 3;

  $$thing =~ s/%FTPTIME2/$ftp2/g;
  $$thing =~ s/%FTPTIME3/$ftp3/g;
#######################################################################
# Run a single specified test case
#

sub singletest {
    # load the test case file definition
    if(loadtest("${TESTDIR}/test${testnum}")) {
        if($verbose) {
            # this is not a test
            logmsg "RUN: $testnum doesn't look like a test case\n";
    }
    else {
        @what = getpart("client", "features");
        elsif($f eq "OpenSSL") {
            if($has_openssl) {
                next;
            }
        }
        elsif($f eq "GnuTLS") {
            if($has_gnutls) {
                next;
            }
        }
Daniel Stenberg's avatar
Daniel Stenberg committed
        elsif($f eq "ipv6") {
            if($has_ipv6) {
                next;
            }
        }
        elsif($f eq "getrlimit") {
            if($has_getrlimit) {
                next;
        # See if this "feature" is in the list of supported protocols
        elsif (grep /^$f$/, @protocols) {
            next;
        }
    if(!$why) {
        my @keywords = getpart("info", "keywords");
        my $k;
        for $k (@keywords) {
            chomp $k;
            if ($disabled_keywords{$k}) {
            	$why = "disabled by keyword";
            }
        }
    }

        my @precheck = getpart("client", "precheck");
            logmsg "prechecked $cmd\n" if($verbose);
    if($why && !$listonly) {
        # there's a problem, count it as "skipped"
        $skipped++;
        $skipped{$why}++;
        $teststat[$testnum]=$why; # store reason for this test case
            printf "test %03d SKIPPED: $why\n", $testnum;
    logmsg sprintf("test %03d...", $testnum);
    # extract the reply data
    my @reply = getpart("reply", "data");
    my @replycheck = getpart("reply", "datacheck");

    if (@replycheck) {
        # we use this file instead to check the final output against
Daniel Stenberg's avatar
Daniel Stenberg committed

        my %hash = getpartattr("reply", "datacheck");
        if($hash{'nonewline'}) {
            # Yes, we must cut off the final newline from the final line
            # of the datacheck
            chomp($replycheck[$#replycheck]);
        }
        @reply=@replycheck;
    }

    # curl command to run
    my @curlcmd= fixarray ( getpart("client", "command") );
    # this is the valid protocol blurb curl should generate
    my @protocol= fixarray ( getpart("verify", "protocol") );
    # redirected stdout/stderr to these files
    $STDOUT="$LOGDIR/stdout$testnum";
    $STDERR="$LOGDIR/stderr$testnum";
    # if this section exists, we verify that the stdout contained this:
    my @validstdout = fixarray ( getpart("verify", "stdout") );
    # if this section exists, we verify upload
    my @upload = getpart("verify", "upload");
    # if this section exists, it might be FTP server instructions:
    my @ftpservercmd = getpart("reply", "servercmd");
    my $CURLOUT="$LOGDIR/curl$testnum.out"; # curl output if not stdout
    # name of the test
    my @testname= getpart("client", "name");
        my $name = $testname[0];
        $name =~ s/\n//g;
Daniel Stenberg's avatar
Daniel Stenberg committed
    if($listonly) {
        return 0; # look successful
    }

    my @codepieces = getpart("client", "tool");

    my $tool="";
    if(@codepieces) {
        $tool = $codepieces[0];
        chomp $tool;
    }

Daniel Stenberg's avatar
Daniel Stenberg committed
    unlink($SERVERIN);
Daniel Stenberg's avatar
Daniel Stenberg committed

    if(@ftpservercmd) {
        # write the instructions to file
        writearray($FTPDCMD, \@ftpservercmd);
    }

    my (@setenv)= getpart("client", "setenv");
    my @envs;

    my $s;
    for $s (@setenv) {
        chomp $s; # cut off the newline

        subVariables \$s;

        if($s =~ /([^=]*)=(.*)/) {
            my ($var, $content)=($1, $2);
            $ENV{$var}=$content;
            # remember which, so that we can clear them afterwards!
            push @envs, $var;
        }
    }

    # get the command line options to use
    my @blaha;
    ($cmd, @blaha)= getpart("client", "command");

    # make some nice replace operations
    $cmd =~ s/\n//g; # no newlines please
Daniel Stenberg's avatar
Daniel Stenberg committed
    # substitute variables in the command line
    my @inputfile=getpart("client", "file");
    if(@inputfile) {
        # we need to generate a file before this test is invoked
        my %fileattr = getpartattr("client", "file");
            logmsg "ERROR: section client=>file has no name attribute\n";
        my $fileContent = join('', @inputfile);
        subVariables \$fileContent;
#        logmsg "DEBUG: writing file " . $filename . "\n";
        open(OUTFILE, ">$filename");
        binmode OUTFILE; # for crapage systems, use binary
        print OUTFILE $fileContent;
        close(OUTFILE);
    my %cmdhash = getpartattr("client", "command");

Daniel Stenberg's avatar
Daniel Stenberg committed
    if($cmdhash{'option'} !~ /no-output/) {
        #We may slap on --output!
        if (!@validstdout) {
            $out=" --output $CURLOUT ";
    my $cmdargs;
    if(!$tool) {
        # run curl, add -v for debug information output
        $cmdargs ="$out --include -v --trace-time $cmd";
    }
    else {
        $cmdargs = " $cmd"; # $cmd is the command line for the test file
        $CURLOUT = $STDOUT; # sends received data to stdout
    }
    my @stdintest = getpart("client", "stdin");

    if(@stdintest) {
        my $stdinfile="$LOGDIR/stdin-for-$testnum";
        writearray($stdinfile, \@stdintest);

        $cmdargs .= " <$stdinfile";
    if(!$tool) {
        $CMDLINE="$CURL";
    else {
        $CMDLINE="$LIBDIR/$tool";
        if(! -f $CMDLINE) {
            print "The tool set in the test case for this: '$tool' does not exist\n";
            return -1;
        }
    my $usevalgrind = $valgrind && ((getpart("verify", "valgrind"))[0] !~ /disable/);
    if($usevalgrind) {
        $CMDLINE = "$valgrind ".$valgrind_tool."--leak-check=yes --num-callers=16 ${valgrind_logfile}=log/valgrind$testnum $CMDLINE";
Daniel Stenberg's avatar
Daniel Stenberg committed
    $CMDLINE .= "$cmdargs >>$STDOUT 2>>$STDERR";
    print CMDLOG "$CMDLINE\n";
    # Apr 2007: precommand isn't being used and could be removed
    my @precommand= getpart("client", "precommand");
    if($precommand[0]) {
        # this is pure perl to eval!
        my $code = join("", @precommand);
        eval $code;
        if($@) {
            logmsg "perl: $code\n";
            logmsg "precommand: $@";
            stopservers($verbose);
        open(GDBCMD, ">log/gdbcmd");
        print GDBCMD "set args $cmdargs\n";
        print GDBCMD "show args\n";
        close(GDBCMD);
    }
    # run the command line we built
    if ($torture) {
        $cmdres = torture($CMDLINE,
                       "$gdb --directory libtest $DBGCURL -x log/gdbcmd");
        runclient("$gdb --directory libtest $DBGCURL -x log/gdbcmd");
        $cmdres=0; # makes it always continue after a debugged run
        $cmdres = runclient("$CMDLINE");

        if(!$anyway && ($signal_num || $dumped_core)) {
    if(!$dumped_core) {
        if(-r "core") {
            # there's core file present now!
            $dumped_core = 1;
        }
    }

    if($dumped_core) {
Daniel Stenberg's avatar
Daniel Stenberg committed
        if(0 && $gdb) {
            logmsg "running gdb for post-mortem analysis:\n";
            open(GDBCMD, ">log/gdbcmd2");
            print GDBCMD "bt\n";
            close(GDBCMD);
            runclient("$gdb --directory libtest -x log/gdbcmd2 -batch $DBGCURL core ");
    # run the postcheck command
    my @postcheck= getpart("client", "postcheck");
    $cmd = $postcheck[0];
    chomp $cmd;
    subVariables \$cmd;
    if($cmd) {
	my $rc = runclient("$cmd");
	    logmsg " postcheck FAILED\n";
	    return 1;
	}
	logmsg "postchecked $cmd\n" if($verbose);
    }

    # remove the special FTP command file after each test!
    unlink($FTPDCMD);

    # Skip all the verification on torture tests
	if(!$cmdres && !$keepoutfiles) {
	    cleardir($LOGDIR);
	}
    my @err = getpart("verify", "errorcode");
    if (@validstdout) {
        # verify redirected stdout
        my @actual = loadarray($STDOUT);
        # get all attributes
        my %hash = getpartattr("verify", "stdout");

        # get the mode attribute
        my $filemode=$hash{'mode'};
        if(($filemode eq "text") && $has_textaware) {
            # text mode when running on windows: fix line endings
            map s/\r\n/\n/g, @actual;
        }

        $res = compare("stdout", \@actual, \@validstdout);
        $ok .= "s";
    }
    else {
        $ok .= "-"; # stdout not checked
    my %replyattr = getpartattr("reply", "data");
    if(!$replyattr{'nocheck'} && (@reply || $replyattr{'sendzero'})) {
        # verify the received data
        my @out = loadarray($CURLOUT);
        my %hash = getpartattr("reply", "data");
        # get the mode attribute
        my $filemode=$hash{'mode'};
        if(($filemode eq "text") && $has_textaware) {
            # text mode when running on windows: fix line endings
            map s/\r\n/\n/g, @out;
        }

        $res = compare("data", \@out, \@reply);
        $ok .= "d";
    }
    else {
        $ok .= "-"; # data not checked
    if(@upload) {
        # verify uploaded data
        my @out = loadarray("$LOGDIR/upload.$testnum");
        $res = compare("upload", \@out, \@upload);
        if ($res) {
            return 1;
        $ok .= "u";
    }
    else {
        $ok .= "-"; # upload not checked
    if(@protocol) {
        my @out;
        my $retry = 5;

        # Verify the sent request. Sometimes, like in test 513 on some hosts,
        # curl will return back faster than the server writes down the request
        # to its file, so we might need to wait here for a while to see if the
        # file gets written a bit later.

        while($retry--) {
            @out = loadarray($SERVERIN);

            if(!$out[0]) {
                # nothing there yet, wait a while and try again
                sleep(1);
            }
        }
        # what to cut off from the live protocol sent by curl
        my @strip = getpart("verify", "strip");
Daniel Stenberg's avatar
Daniel Stenberg committed
        my @protstrip=@protocol;

        # check if there's any attributes on the verify/protocol section
        my %hash = getpartattr("verify", "protocol");

        if($hash{'nonewline'}) {
            # Yes, we must cut off the final newline from the final line
            # of the protocol data
            chomp($protstrip[$#protstrip]);
        }

Daniel Stenberg's avatar
Daniel Stenberg committed
        for(@strip) {
            # strip off all lines that match the patterns from both arrays
Daniel Stenberg's avatar
Daniel Stenberg committed
            @out = striparray( $_, \@out);
            @protstrip= striparray( $_, \@protstrip);
        }
Daniel Stenberg's avatar
Daniel Stenberg committed
        # what parts to cut off from the protocol
        my @strippart = getpart("verify", "strippart");
        my $strip;
        for $strip (@strippart) {
            chomp $strip;
            for(@out) {
                eval $strip;
            }
        }

        $res = compare("protocol", \@out, \@protstrip);
    my @outfile=getpart("verify", "file");
    if(@outfile) {
        # we're supposed to verify a dynamically generated file!
        my %hash = getpartattr("verify", "file");

        my $filename=$hash{'name'};
        if(!$filename) {
            logmsg "ERROR: section verify=>file has no name attribute\n";
            stopservers($verbose);
        }
        my @generated=loadarray($filename);

        # what parts to cut off from the file
        my @stripfile = getpart("verify", "stripfile");
        my $filemode=$hash{'mode'};
        if(($filemode eq "text") && $has_textaware) {
            # text mode when running on windows means adding an extra
            # strip expression