Skip to content
runtests.pl 61.5 KiB
Newer Older
        logmsg "contents of $curlvererr: \n";
        displaylogcontent("$curlvererr");
    if(-r "../lib/config.h") {
        open(CONF, "<../lib/config.h");
        while(<CONF>) {
            if($_ =~ /^\#define HAVE_GETRLIMIT/) {
                $has_getrlimit = 1;
            }
        }
        close(CONF);
    }

        # 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";
    }

    my $hostname=`hostname`;
    my $hosttype=`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("* SSL library:    %s\n",
               $has_gnutls?"GnuTLS":($has_openssl?"OpenSSL":"<unknown>"));
    }

    $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;

  # 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;
            }
        }
        my @precheck = getpart("client", "precheck");
        chomp $cmd;
        if($cmd) {
            my @o = `$cmd 2>/dev/null`;
            if($o[0]) {
                $why = $o[0];
                chomp $why;
            }
            logmsg "prechecked $cmd\n" if($verbose);
    if($why) {
        # 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
    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";
        $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";
    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: $@";
    if($gdbthis) {
        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) {
                       "$gdb --directory libtest $DBGCURL -x log/gdbcmd");
        system("$gdb --directory libtest $DBGCURL -x log/gdbcmd");
        $cmdres=0; # makes it always continue after a debugged run
        $cmdres = system("$CMDLINE");
        my $signal_num  = $cmdres & 127;

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

    if($dumped_core) {
        logmsg "core dumped!\n";
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);
            system("$gdb --directory libtest -x log/gdbcmd2 -batch $DBGCURL core ");
    # remove the special FTP command file after each test!
    unlink($FTPDCMD);

    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 dynamicly generated file!
        my %hash = getpartattr("verify", "file");

        my $filename=$hash{'name'};
        if(!$filename) {
            logmsg "ERROR: section verify=>file has no name attribute!\n";
        }
        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
            push @stripfile, "s/\r\n/\n/";
        }

        my $strip;
        for $strip (@stripfile) {
            chomp $strip;
            for(@generated) {
                eval $strip;
            }
        }

        $res = compare("output", \@generated, \@outfile);
    # accept multiple comma-separated error codes
    my @splerr = split(/ *, */, $errorcode);
    my $errok;
    foreach $e (@splerr) {
        if($e == $cmdres) {
            # a fine error code
            $errok = 1;
            last;
        }
    }

    if($errok) {
            printf "\ncurl returned $cmdres, %s was expected\n", $errorcode;
        logmsg " exit FAILED\n";
    @what = getpart("client", "killserver");
    for(@what) {
        my $serv = $_;
        chomp $serv;
        if($serv =~ /^ftp(\d*)(-ipv6|)/) {
            my ($id, $ext) = ($1, $2);
            #print STDERR "SERV $serv $id $ext\n";
            ftpkillslave($id, $ext, $verbose);
        if($run{$serv}) {
            stopserver($run{$serv}); # the pid file is in the hash table
            logmsg "RUN: The $serv server is not running\n";
            logmsg "\n** ALERT! memory debuggin without any output file?\n";
            my @memdata=`$memanalyze $memdump`;
                if($_ ne "") {
                    # well it could be other memory problems as well, but
                    # we call it leak for short here
                logmsg "\n** MEMORY FAILURE\n";
                logmsg @memdata;
        # this is the valid protocol blurb curl should generate
        my @disable= getpart("verify", "valgrind");

        if($disable[0] !~ /disable/) {

            opendir(DIR, "log") ||
                return 0; # can't open log dir
            my @files = readdir(DIR);
            closedir DIR;
            my $f;
            my $l;
            foreach $f (@files) {
                if($f =~ /^valgrind$testnum\.pid/) {
                    $l = $f;
                    last;
            my $src=$ENV{'srcdir'};
            if(!$src) {
                $src=".";
            }
            my @e = valgrindparse($src, $feature{'SSL'}, "log/$l");
            if($e[0]) {
                logmsg " valgrind ERROR ";
                logmsg @e;
                logmsg " valgrind SKIPPED";
    else {
        $ok .= "-"; # valgrind not checked
    logmsg "$ok " if(!$short);

    my $sofar= time()-$start;
    my $esttotal = $sofar/$count * $total;
    my $estleft = $esttotal - $sofar;
    my $left=sprintf("remaining: %02d:%02d",
    printf "OK (%-3d out of %-3d, %s)\n", $count, $total, $left;
    # the test succeeded, remove all log files
    if(!$keepoutfiles) {
        cleardir($LOGDIR);
    }

    unlink($FTPDCMD); # remove the instructions for this test

#######################################################################
# Stop all running test servers
sub stopservers {
            if($pid != $prev) {
                # no need to kill same pid twice!
                logmsg sprintf("* kill pid for %s => %d\n",
                               $server, $pid) if($verbose);
                stopserver($pid);
            }
            $prev = $pid;
#######################################################################
# startservers() starts all the named servers
#
# Returns: string with error reason or blank for success

    for(@what) {
        my $what = lc($_);
        $what =~ s/[^a-z0-9-]//g;
        if($what eq "ftp") {
            if(!$run{'ftp'}) {
                ($pid, $pid2) = runftpserver("", $verbose);
                printf ("* pid ftp => %d %d\n", $pid, $pid2) if($verbose);
        elsif($what eq "ftp2") {
            if(!$run{'ftp2'}) {
                ($pid, $pid2) = runftpserver("2", $verbose);
                if($pid <= 0) {
                    return "failed starting FTP2 server";
                }
                printf ("* pid ftp2 => %d %d\n", $pid, $pid2) if($verbose);
        elsif($what eq "ftp-ipv6") {
            if(!$run{'ftp-ipv6'}) {
                ($pid, $pid2) = runftpserver("", $verbose, "ipv6");
                if($pid <= 0) {
                    return "failed starting FTP-ipv6 server";
                }
                logmsg sprintf("* pid ftp-ipv6 => %d %d\n", $pid,
                       $pid2) if($verbose);
                $run{'ftp-ipv6'}="$pid $pid2";
        elsif($what eq "http") {
            if(!$run{'http'}) {
                ($pid, $pid2) = runhttpserver($verbose);
                printf ("* pid http => %d %d\n", $pid, $pid2) if($verbose);
        elsif($what eq "http-ipv6") {
            if(!$run{'http-ipv6'}) {
                ($pid, $pid2) = runhttpserver($verbose, "IPv6");
                    return "failed starting IPv6 HTTP server";
                logmsg sprintf("* pid http-ipv6 => %d %d\n", $pid, $pid2)
                    if($verbose);
                $run{'http-ipv6'}="$pid $pid2";
        elsif($what eq "ftps") {
            # we can't run ftps tests at all for the moment
            return "test suite lacks FTPS support";
        elsif($what eq "file") {
            # we support it but have no server!
        }
        elsif($what eq "https") {
Daniel Stenberg's avatar
Daniel Stenberg committed
            if(!$stunnel) {
                # we can't run ftps tests without stunnel
                return "no stunnel";
Daniel Stenberg's avatar
Daniel Stenberg committed
            if(!$ssl_version) {
Daniel Stenberg's avatar
Daniel Stenberg committed
                # we can't run ftps tests if libcurl is SSL-less
                return "curl lacks SSL support";
            }

                ($pid, $pid2) = runhttpserver($verbose);
                printf ("* pid http => %d %d\n", $pid, $pid2) if($verbose);
            if(!$run{'https'}) {
                ($pid, $pid2) = runhttpsserver($verbose);
                    return "failed starting HTTPS server (stunnel)";
                logmsg sprintf("* pid https => %d %d\n", $pid, $pid2)
        elsif($what eq "tftp") {
            if(!$run{'tftp'}) {
                ($pid, $pid2) = runtftpserver("", $verbose);
                if($pid <= 0) {
                    return "failed starting TFTP server";
                }
                printf ("* pid tftp => %d %d\n", $pid, $pid2) if($verbose);
                $run{'tftp'}="$pid $pid2";
            }
        }
        elsif($what eq "tftp-ipv6") {
            if(!$run{'tftp-ipv6'}) {
                ($pid, $pid2) = runtftpserver("", $verbose, "IPv6");
                if($pid <= 0) {
                    return "failed starting TFTP-IPv6 server";
                }
                printf("* pid tftp-ipv6 => %d %d\n", $pid, $pid2) if($verbose);
                $run{'tftp-ipv6'}="$pid $pid2";
            }
        }
Daniel Stenberg's avatar
Daniel Stenberg committed
        elsif($what eq "none") {
            logmsg "* starts no server\n" if ($verbose);
        else {
            warn "we don't support a server for $what";
    return 0;
}

##############################################################################
# This function makes sure the right set of server is running for the
# specified test case. This is a useful design when we run single tests as not
# all servers need to run then!
#
# Returns: a string, blank if everything is fine or a reason why it failed
#

sub serverfortest {
    my ($testnum)=@_;

    # load the test case file definition
    if(loadtest("${TESTDIR}/test${testnum}")) {
        if($verbose) {
            # this is not a test
            logmsg "$testnum doesn't look like a test case!\n";
    }

    my @what = getpart("client", "server");

    if(!$what[0]) {
        warn "Test case $testnum has no server(s) specified!";

#######################################################################
# Check options to this test program
#

do {
    if ($ARGV[0] eq "-v") {
        # verbose output
        $verbose=1;
    }
        # use this path to curl instead of default
        $DBGCURL=$CURL=$ARGV[1];
        # have the servers display protocol output
    elsif ($ARGV[0] eq "-f") {
        # run fork-servers, which makes the server fork for all new
        # connections This is NOT what you wanna do without knowing exactly
        # why and for what
        $forkserver=1;
    }
    elsif ($ARGV[0] eq "-g") {
        # run this test with gdb
        $gdbthis=1;
    }
    elsif($ARGV[0] eq "-s") {
        # short output
        $short=1;
    }
    elsif($ARGV[0] eq "-n") {
        # no valgrind
        undef $valgrind;
    }