#!/usr/bin/perl # # Convert standard PHP tests to run in a web environment. This was created # against PHP 4.3.7. # # This writes the target PHP file into the web server document root and then # fetches it using the LWP package. It then compare the results with the # expected results in the test case. Some amount of munging is done on the # results and the expected results to make it play nicely. # # This should be run from the PHP source directory, ala: # % cd php-4.3.7 # % perl webtest -h localhost:8080 -d /usr/local/webserver/docs/ # # 07/09/04 Initial version. Rob Crittenden (rcrittenden0569@aol.com) # 07/19/04 Modified to work with PHP 5.0.0 tests. Rob Crittenden require LWP; use File::Copy; use File::Basename; use Getopt::Std; use Time::localtime; use Time::Local; $verbose = 0; # to write out failures getopts("lh:d:"); usage() if ($opt_h eq "" || $opt_d eq ""); if ($verbose) { $OUTDIR="output/"; mkdir $OUTDIR; } $DOCROOT=$opt_d; installfiles(0, $DOCROOT); $passed = 0; $failed = 0; $skipped = 0; @TESTDIRS=("tests/run-test", "tests/basic", "tests/classes", "tests/func", "tests/lang", "tests/strings", "ext/ctype/tests", "ext/pcre/tests", "ext/session/tests", "ext/standard/tests/aggregation", "ext/standard/tests/array", "ext/standard/tests/assert", "ext/standard/tests/file", "ext/standard/tests/general_functions", "ext/standard/tests/image", "ext/standard/tests/math", "ext/standard/tests/reg", "ext/standard/tests/serialize", "ext/standard/tests/strings", "ext/standard/tests/time", "ext/standard/tests/versioning", ); $st = localtime; print "=====================================================================\n"; printf ("TIME START %d-%.2d-%.2d %.2d:%.2d:%.2d\n", $st->year+1900, $st->mon+1, $st->mday, $st->hour, $st->min, $st->sec); print "=====================================================================\n"; loop: foreach $testdir (@TESTDIRS) { if (-e "$testdir") { opendir(DIR, "$testdir") or die "Can't opendir $TESTSRC/$testdir: $!"; while (defined($testfile = readdir(DIR))) { if (-T "$testdir/$testfile" && $testfile =~ /\.phpt/) { $res = runtest("$testdir/$testfile"); if ($res == 1) { $passed++; } elsif ($res == 0) { $failed++; } elsif ($res == -1) { $skipped++; } print " [$testdir/$testfile]\n"; } } } closedir(DIR); } if ($opt_l) { goto loop; } $en = localtime; $s = timelocal($st->sec, $st->min, $st->hour, $st->mday, $st->mon, $st->year); $e = timelocal($en->sec, $en->min, $en->hour, $en->mday, $en->mon, $en->year); $diff = $e - $s; print "\n"; print "=====================================================================\n"; printf ("TIME END %d-%.2d-%.2d %.2d:%.2d:%.2d\n", $en->year+1900, $en->mon+1, $en->mday, $en->hour, $en->min, $en->sec); print "=====================================================================\n"; $total = $passed + $failed + $skipped; print "\n"; print "=====================================================================\n"; print "Test Summary\n"; print "---------------------------------------------------------------------\n"; printf ("Number of tests : %5d\n", $total); printf ("Tests skipped : %5d (%2.2f)\n", $skipped, ($skipped / $total) * 100); printf ("Tests failed : %5d (%2.2f)\n", $failed, ($failed / $total) * 100); printf ("Tests passed : %5d (%2.2f)\n", $passed, ($passed / $total) * 100); print "---------------------------------------------------------------------\n"; printf ("Time taken : %5d seconds\n", $diff); print "=====================================================================\n"; # remove any files we installed installfiles(1, $DOCROOT); rmdir("$DOCROOT/phptests"); exit 0; # # subroutine: runtest # # Parse and execute a single test case. # # Returns -1 for skipped test # 0 for failed test # 1 for passed test sub runtest { my($testfile) = @_; my $data, $i; my $file, $post, $get, $expect; my $out, $exp; my $url; my $ret; my $comp = 0; # slurp in the file $i = 0; open(IN, "$testfile") or die "Can't open test file $testfile: $!"; while () { $data[$i++] = $_; } close(IN); $desc = getsection ($data, "TEST", $i); $file = getsection($data, "FILE", $i); $post = getsection($data, "POST", $i); $get = getsection($data, "GET", $i); $expect = getsection($data, "EXPECT", $i); $skipif = getsection($data, "SKIPIF", $i); $expectregex = getsection($data, "EXPECTREGEX", $i); $expectf = getsection($data, "EXPECTF", $i); $ini = getsection($data, "INI", $i); chomp($desc); # Kludge. We skip any tests that may ever possibly get skipped. # We also skip any requiring regex on the backend or those that # require special configuration. if ($skipif ne "" || $expectregex ne "" || $ini ne "") { print "SKIPPED $desc"; return -1; } if (skiptest($testfile) == 1) { print "SKIPPED $desc"; return -1; } if ($expectf ne "") { $expect = flexexpect($expectf); } ($testname = $testfile) =~ s/\//./g; $file =~ s/^\s*//; # trim leading whitespace $file =~ s/(\.\.\/)+tests\/quicktester.inc/\.\.\/quicktester.inc/; writefile("$DOCROOT/phptests/webtest.php", $file); chomp($post); # Create the user agent object $ua = LWP::UserAgent->new; # Set up a request. $url = "http://$opt_h/phptests/webtest.php"; if ($get ne "") { $url .= "?$get"; } if ($post ne "") { $request = HTTP::Request->new("POST"); $request->header('Content-Type', "application/x-www-form-urlencoded"); $request->header('Content-Length', length $post); $request->content($post); } else { $request = HTTP::Request->new("GET"); } $request->url($url); $response = $ua->request($request); # Remove all trailing newlines to avoid headaches later. Some of the PHP # tests have them, some don't. Let's treat them all the same. ($out = $response->content) =~ s/\s*$//; ($exp = $expect) =~ s/\s*$//; $out =~ s/^\n*//; # trim leading whitespace too $out =~ s/\r*//g; # trim DOS newline characters $exp =~ s/^\n*//; # trim leading whitespace too $exp =~ s/\r*//g; # trim DOS newline characters $exp =~ s/\$//g; $out =~ s/\$//g; if ($expectf ne "") { $comp = ($out =~ /^$exp/); } else { $comp = ($exp eq $out); } if (!$comp) { print "FAILED $desc"; if ($verbose) { writefile("$OUTDIR/$testname.expected", $exp); writefile("$OUTDIR/$testname.out", $out); } $ret = 0; } else { print "PASSED $desc"; $ret = 1; # passed } # cleanup unlink("$DOCROOT/phptests/webtest.php"); return $ret; } # # subroutine: getsection # # return a "section" of the file as defined by --NAME-- sub getsection { my($data, $section, $len) = @_; my $i=0, $j=0; my $start=0; my $retval=""; while ($i < $len) { if ($data[$i] =~ "^--$section--.*") { $start = 1; } elsif ($data[$i] =~ "^--([A-Z]+)--" && $start == 1) { $start = 0; } elsif ($start == 1) { $retval .= $data[$i]; $j++; } $i++; } return $retval; } # # subroutine: writefile # # Given a string, write it into a file. sub writefile { my($filename, $data) = @_; open(OUT, ">$filename") or die "Unable to open $filename for writing: $!\n"; print OUT $data; close(OUT); } # # subroutine: skiptest # # Some tests just won't run in a web environment, lets skip them. These # do things like invoke the PHP interpreter directly. Some error messages # in the EXPECT are directed to the AOLserver error log rather than the # output file so cannot be compared. sub skiptest { my $t = shift; my @skiptests = ("ext/standard/tests/file/bug26938.phpt", "ext/standard/tests/strings/bug22207.phpt", "ext/standard/tests/file/proc_open01.phpt", "ext/standard/tests/file/userstreams.phpt", "ext/standard/tests/serialize/bug25378.phpt", "tests/lang/bug24773.phpt", "tests/classes/bug27504.phpt", "tests/classes/private_redeclare.phpt", "tests/classes/bug27468.phpt", "tests/classes/clone_005.phpt", "tests/lang/bug27439.phpt", "tests/lang/bug21094.phpt", "tests/strings/002.phpt", "tests/lang/bug24573.phpt", "tests/lang/bug22231.phpt", "tests/lang/bug22510.phpt", "tests/lang/bug24658.phpt", "ext/standard/tests/array/array_diff_1.phpt", "ext/standard/tests/file/bug26615.phpt", "tests/lang/039.phpt", "ext/standard/tests/time/bug20382.phpt" ); foreach $test (@skiptests) { if ($t eq $test) { return 1; } } return 0; } # # subroutine: installfiles # # Install any extra files needed by tests into the web server. sub installfiles { my $remove = shift; my $targetdir = shift; my $target; my @phpfiles = ("tests/lang/015.inc", "tests/lang/016.inc", "tests/lang/023-1.inc", "tests/lang/023-2.inc", "ext/standard/tests/file/test.csv", "ext/standard/tests/file/test2.csv", "ext/standard/tests/file/test3.csv", "ext/standard/tests/file/test4.csv", "ext/standard/tests/aggregation/aggregate.lib", "ext/standard/tests/general_functions/004.data", "ext/standard/tests/image/bug13213.jpg" ); my @rootfiles = ("tests/quicktester.inc"); mkdir ("$DOCROOT/phptests"); foreach $file (@phpfiles) { $target = basename($file); if ($remove) { unlink("$targetdir/phptests/$target"); } else { copy($file, "$targetdir/phptests/$target"); } } foreach $file (@rootfiles) { $target = basename($file); if ($remove) { unlink("$targetdir/$target"); } else { copy($file, "$targetdir/$target"); } } } # # subroutine: flexexpect # # Handle flexible expect lines sub flexexpect { my $e = shift; my $wanted = ""; $wanted = $e; $wanted =~ s/\*/\\\*/g; $wanted =~ s/\[/\\[/g; $wanted =~ s/\]/\\]/g; $wanted =~ s/\(/\\(/g; $wanted =~ s/\)/\\)/g; $wanted =~ s/%s/\.+?/g; $wanted =~ s/%i/[+\-]?[0-9]+/g; $wanted =~ s/%d/[0-9]+/g; $wanted =~ s/%x/[0-9a-fA-F]+/g; $wanted =~ s/%f/[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?/g; $wanted =~ s/%c/\./g; return $wanted; } # # subroutine: usage # # Basic usage instructions sub usage { print "webtest -lhd \n"; print "\tl - run the test in a big loop until ^C\n"; print "\th - hostname and port, e.g. localhost:80\n"; print "\td - document root of the webserver to test\n"; exit 0; }