3 # Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
4 # Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
10 # 1. Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
15 # 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 # its contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
19 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 # Script to run the Web Kit Open Source Project layout tests.
32 # Run all the tests passed in on the command line.
33 # If no tests are passed, find all the .html, .shtml, .xml, .xhtml, .pl, .php (and svg) files in the test directory.
36 # Compare against the existing file xxx-expected.txt.
37 # If there is a mismatch, generate xxx-actual.txt and xxx-diffs.txt.
40 # the number of tests that got the expected results
41 # the number of tests that ran, but did not get the expected results
42 # the number of tests that failed to run
43 # the number of tests that were run but had no expected results to compare against
54 use File::Spec::Functions;
58 use Time::HiRes qw(time);
60 use lib $FindBin::Bin;
63 sub closeDumpRenderTree();
65 sub countAndPrintLeaks($$$);
66 sub fileNameWithNumber($$);
68 sub openDumpRenderTreeIfNeeded();
69 sub openHTTPDIfNeeded();
71 sub processIgnoreTests($);
83 my $repaintSweepHorizontally = '';
84 my $repaintTests = '';
85 my $report10Slowest = 0;
90 my $testOnlySVGs = '';
91 my $testResultsDirectory = "/tmp/layout-test-results";
94 my $expectedTag = "expected";
96 $expectedTag = "expected-win";
98 my $actualTag = "actual";
100 $actualTag = "actual-win";
102 my $diffsTag = "diffs";
104 $actualTag = "diffs-win";
108 "Usage: " . basename($0) . " [options] [testdir|testpath ...]\n" .
109 " -g|--guard-malloc Enable malloc guard\n" .
110 " --help Show this help message\n" .
111 " -h|--horizontal-sweep Change repaint to sweep horizontally instead of vertically (implies --repaint-tests)\n" .
112 " --[no-]http Run (or do not run) http tests (default: " . ($testHTTP ? "run" : "do not run") . ")\n" .
113 " -i|--ignore-tests Comma-separated list of directories or tests to ignore\n" .
114 " --[no-]launch-safari Launch (or do not launch) Safari to display test results (default: "
115 . ($launchSafari ? "launch" : "do not launch") . ")\n" .
116 " -l|--leaks Enable leaks checking\n" .
117 " -p|--pixel-tests Enable pixel tests\n" .
118 " --port Web server port to use with http tests\n" .
119 " -q|--quiet Less verbose output\n" .
120 " -r|--repaint-tests Run repaint tests (implies --pixel-tests)\n" .
121 " --reset-results Reset ALL results (including pixel tests if --pixel-tests is set)\n" .
122 " -o|--results-directory Output results directory (default: " . $testResultsDirectory . ")\n" .
123 " -1|--singly Isolate each test case run (implies --verbose)\n" .
124 " --slowest Report the 10 slowest tests\n" .
125 " --svg Run only SVG tests (implies --pixel-tests)\n" .
126 " -v|--verbose More verbose output (overrides --quiet)\n" .
127 " --debug|--release Set DumpRenderTree build configuration\n";
129 # Parse out build options (--debug or --release) first
130 my $buildConfiguration = setConfiguration();
132 my $getOptionsResult = GetOptions(
133 'guard-malloc|g' => \$guardMalloc,
134 'help' => \$showHelp,
135 'horizontal-sweep|h' => \$repaintSweepHorizontally,
136 'http!' => \$testHTTP,
137 'ignore-tests|i=s' => \$ignoreTests,
138 'launch-safari!' => \$launchSafari,
139 'leaks|l' => \$checkLeaks,
140 'pixel-tests|p' => \$pixelTests,
141 'port=i' => \$httpdPort,
142 'quiet|q' => \$quiet,
143 'repaint-tests|r' => \$repaintTests,
144 'reset-results' => \$resetResults,
145 'results-directory|o=s' => \$testResultsDirectory,
146 'singly|1' => \$singly,
147 'slowest' => \$report10Slowest,
148 'svg' => \$testOnlySVGs,
149 'verbose|v' => \$verbose,
152 if (!$getOptionsResult || $showHelp) {
157 $repaintTests = 1 if $repaintSweepHorizontally;
159 $pixelTests = 1 if $testOnlySVGs;
160 $pixelTests = 1 if $repaintTests;
162 $verbose = 1 if $singly;
164 my $productDir = productDir();
168 my @buildOptions = ();
169 if ($buildConfiguration) {
170 push @buildOptions, '--' . lc($buildConfiguration);
172 my $buildResult = system "WebKitTools/Scripts/build-dumprendertree", @buildOptions;
173 exit WEXITSTATUS($buildResult) if $buildResult;
175 my $dumpToolName = "DumpRenderTree";
176 my $tool = "$productDir/$dumpToolName";
177 die "can't find executable $dumpToolName (looked in $productDir)\n" if !-x $tool;
179 my $imageDiffTool = "$productDir/ImageDiff";
180 die "can't find executable $imageDiffTool (looked in $productDir)\n" if $pixelTests && !-x $imageDiffTool;
184 my $layoutTestsName = $testOnlySVGs ? "LayoutTests/svg" : "LayoutTests";
185 my $testDirectory = File::Spec->rel2abs($layoutTestsName);
186 my $testResults = catfile($testResultsDirectory, "results.html");
188 print "Running tests from $testDirectory\n";
192 my %ignoredFiles = ();
193 my %ignoredDirectories = ();
194 my %ignoredLocalDirectories = map { $_ => 1 } qw(.svn _svn resources);
195 my %supportedFileExtensions = map { $_ => 1 } qw(html shtml xml xhtml pl php);
197 %supportedFileExtensions = map { $_ => 1 } qw(svg xml);
198 } elsif (checkWebCoreSVGSupport($testOnlySVGs)) {
199 $supportedFileExtensions{'svg'} = 1;
201 $ignoredLocalDirectories{'svg'} = 1;
204 $ignoredDirectories{'http'} = 1;
208 processIgnoreTests($ignoreTests);
211 my $directoryFilter = sub {
212 return () if exists $ignoredLocalDirectories{basename($File::Find::dir)};
213 return () if exists $ignoredDirectories{File::Spec->abs2rel($File::Find::dir, $testDirectory)};
217 my $fileFilter = sub {
219 if ($filename =~ /\.([^.]+)$/) {
220 if (exists $supportedFileExtensions{$1}) {
221 my $path = File::Spec->abs2rel(catfile($File::Find::dir, $filename), $testDirectory);
222 push @tests, $path if !exists $ignoredFiles{$path};
227 for my $test (@ARGV) {
228 $test =~ s/^($layoutTestsName|$testDirectory)\///;
229 my $fullPath = catfile($testDirectory, $test);
230 if (file_name_is_absolute($test)) {
231 print "can't run test $test outside $testDirectory\n";
232 } elsif (-f $fullPath) {
233 my ($filename, $pathname, $fileExtension) = fileparse($test, qr{\.[^.]+$});
234 if (!exists $supportedFileExtensions{substr($fileExtension, 1)}) {
235 print "test $test does not have a supported extension\n";
236 } elsif ($testHTTP || $pathname !~ /^http\//) {
239 } elsif (-d $fullPath) {
240 find({ preprocess => $directoryFilter, wanted => $fileFilter }, $fullPath);
242 print "test $test not found\n";
246 find({ preprocess => $directoryFilter, wanted => $fileFilter }, $testDirectory);
249 die "no tests to run\n" if !@tests;
251 @tests = sort pathcmp @tests;
258 my $maxTestsPerLeaksRun = 1000; # more than 3000 and malloc logging will normally run out of memory
259 my $leaksOutputFileNumber = 1;
263 push @toolArgs, "--dump-all-pixels" if $pixelTests && $resetResults;
264 push @toolArgs, "--pixel-tests" if $pixelTests;
265 push @toolArgs, "--repaint" if $repaintTests;
266 push @toolArgs, "--horizontal-sweep" if $repaintSweepHorizontally;
271 my $imageDiffToolPID;
274 $ENV{MallocStackLogging} = 1 if $checkLeaks;
275 $imageDiffToolPID = open2(\*DIFFIN, \*DIFFOUT, $imageDiffTool, "") or die "unable to open $imageDiffTool\n";
282 my $lastDirectory = "";
286 print "Testing ", scalar @tests, " test cases.\n";
287 my $overallStartTime = time;
289 for my $test (@tests) {
290 next if $test eq 'results.html';
292 openDumpRenderTreeIfNeeded();
295 $base =~ s/\.[a-zA-Z]+$//;
298 print "running $test -> ";
303 if ($dir ne $lastDirectory) {
304 print "\n" unless $atLineStart;
306 $lastDirectory = $dir;
314 my $startTime = time if $report10Slowest;
316 if ($test !~ /^http\//) {
317 my $testPath = "$testDirectory/$test";
319 $testPath = `cygpath -m -s "$testPath"`;
322 $testPath = canonpath($testPath);
324 print OUT "$testPath\n";
328 my $path = canonpath($test);
329 $path =~ s/^http\/tests\///;
330 print OUT "http://127.0.0.1:$httpdPort/$path\n";
339 $durations{$test} = time - $startTime if $report10Slowest;
342 if (!$resetResults && open EXPECTED, "<", "$testDirectory/$base-$expectedTag.txt") {
350 if ($checkLeaks && $singly) {
356 my $diffPercentage = "";
357 my $diffResult = "passed";
361 my $expectedHash = "";
362 my $actualPNGSize = 0;
365 if (/ActualHash: ([a-f0-9]{32})/) {
367 } elsif (/BaselineHash: ([a-f0-9]{32})/) {
369 } elsif (/Content-length: (\d+)\s*/) {
371 read(IN, $actualPNG, $actualPNGSize);
375 if ($expectedHash ne $actualHash && -f "$testDirectory/$base-$expectedTag.png") {
376 my $expectedPNGSize = -s "$testDirectory/$base-$expectedTag.png";
377 my $expectedPNG = "";
378 open EXPECTEDPNG, "$testDirectory/$base-$expectedTag.png";
379 read(EXPECTEDPNG, $expectedPNG, $expectedPNGSize);
381 print DIFFOUT "Content-length: $actualPNGSize\n";
382 print DIFFOUT $actualPNG;
384 print DIFFOUT "Content-length: $expectedPNGSize\n";
385 print DIFFOUT $expectedPNG;
388 last if /^error/ || /^diff:/;
389 if (/Content-length: (\d+)\s*/) {
390 read(DIFFIN, $diffPNG, $1);
394 if (/^diff: (.+)% (passed|failed)/) {
395 $diffPercentage = $1;
400 if ($actualPNGSize && ($resetResults || !-f "$testDirectory/$base-$expectedTag.png")) {
401 open EXPECTED, ">", "$testDirectory/$base-expected.png" or die "could not create $testDirectory/$base-expected.png\n";
402 print EXPECTED $actualPNG;
406 # update the expected hash if the image diff said that there was no difference
407 if ($actualHash ne "" && ($resetResults || !-f "$testDirectory/$base-$expectedTag.checksum")) {
408 open EXPECTED, ">", "$testDirectory/$base-$expectedTag.checksum" or die "could not create $testDirectory/$base-$expectedTag.checksum\n";
409 print EXPECTED $actualHash;
414 if (!defined $expected) {
416 print "new " . ($resetResults ? "result" : "test") ."\n";
420 open EXPECTED, ">", "$testDirectory/$base-$expectedTag.txt" or die "could not create $testDirectory/$base-$expectedTag.txt\n";
421 print EXPECTED $actual;
423 unlink "$testResultsDirectory/$base-$actualTag.txt";
424 unlink "$testResultsDirectory/$base-$diffsTag.txt";
425 } elsif ($actual eq $expected && $diffResult eq "passed") {
431 unlink "$testResultsDirectory/$base-$actualTag.txt";
432 unlink "$testResultsDirectory/$base-$diffsTag.txt";
435 print "\n" unless $atLineStart;
441 $result = "mismatch";
443 my $dir = "$testResultsDirectory/$base";
444 $dir =~ s|/([^/]+)$|| or die "Failed to find test name from base\n";
448 open ACTUAL, ">", "$testResultsDirectory/$base-$actualTag.txt" or die;
449 print ACTUAL $actual;
452 system "diff -u \"$testDirectory/$base-$expectedTag.txt\" \"$testResultsDirectory/$base-$actualTag.txt\" > \"$testResultsDirectory/$base-$diffsTag.txt\"";
454 if ($pixelTests && $diffPNG && $diffPNG ne "") {
455 $imagesPresent{$base} = 1;
457 open ACTUAL, ">", "$testResultsDirectory/$base-$actualTag.png" or die;
458 print ACTUAL $actualPNG;
461 open DIFF, ">", "$testResultsDirectory/$base-$diffsTag.png" or die;
465 copy("$testDirectory/$base-$expectedTag.png", "$testResultsDirectory/$base-$expectedTag.png");
467 open DIFFHTML, ">$testResultsDirectory/$base-$diffsTag.html" or die;
468 print DIFFHTML "<html>\n";
469 print DIFFHTML "<head>\n";
470 print DIFFHTML "<title>$base Image Compare</title>\n";
471 print DIFFHTML "<script language=\"Javascript\" type=\"text/javascript\">\n";
472 print DIFFHTML "var currentImage = 0;\n";
473 print DIFFHTML "var imageNames = new Array(\"Actual\", \"Expected\");\n";
474 print DIFFHTML "var imagePaths = new Array(\"$testName-$actualTag.png\", \"$testName-$expectedTag.png\");\n";
475 if (-f "$testDirectory/$base-w3c.png") {
476 copy("$testDirectory/$base-w3c.png", "$testResultsDirectory/$base-w3c.png");
477 print DIFFHTML "imageNames.push(\"W3C\");\n";
478 print DIFFHTML "imagePaths.push(\"$testName-w3c.png\");\n";
480 print DIFFHTML "function animateImage() {\n";
481 print DIFFHTML " var image = document.getElementById(\"animatedImage\");\n";
482 print DIFFHTML " var imageText = document.getElementById(\"imageText\");\n";
483 print DIFFHTML " image.src = imagePaths[currentImage];\n";
484 print DIFFHTML " imageText.innerHTML = imageNames[currentImage] + \" Image\";\n";
485 print DIFFHTML " currentImage = (currentImage + 1) % imageNames.length;\n";
486 print DIFFHTML " setTimeout('animateImage()',2000);\n";
487 print DIFFHTML "}\n";
488 print DIFFHTML "</script>\n";
489 print DIFFHTML "</head>\n";
490 print DIFFHTML "<body onLoad=\"animateImage();\">\n";
491 print DIFFHTML "<table>\n";
492 if ($diffPercentage) {
493 print DIFFHTML "<tr>\n";
494 print DIFFHTML "<td>Difference between images: <a href=\"$testName-$diffsTag.png\">$diffPercentage%</a></td>\n";
495 print DIFFHTML "</tr>\n";
497 print DIFFHTML "<tr>\n";
498 print DIFFHTML "<td id=\"imageText\" style=\"text-weight: bold;\">Actual Image</td>\n";
499 print DIFFHTML "</tr>\n";
500 print DIFFHTML "<tr>\n";
501 print DIFFHTML "<td><img src=\"$testName-$actualTag.png\" id=\"animatedImage\"></td>\n";
502 print DIFFHTML "</tr>\n";
503 print DIFFHTML "</table>\n";
504 print DIFFHTML "</body>\n";
505 print DIFFHTML "</html>\n";
509 if ($checkLeaks && $toolOpen) {
511 $totalLeaks += countAndPrintLeaks($dumpToolName, $dumpToolPID, "$testResultsDirectory/$base-leaks.txt");
512 } elsif ($count && (($count % $maxTestsPerLeaksRun) == 0)) {
513 my $leaksFileName = fileNameWithNumber($dumpToolName, $leaksOutputFileNumber);
514 my $leaksCount = countAndPrintLeaks($dumpToolName, $dumpToolPID, "$testResultsDirectory/$leaksFileName-leaks.txt");
515 $leaksOutputFileNumber++ if ($leaksCount);
516 $totalLeaks += $leaksCount;
517 closeDumpRenderTree();
521 if ($singly && $toolOpen) {
522 closeDumpRenderTree();
526 $counts{$result} += 1;
527 push @{$tests{$result}}, $test;
529 printf "\n%0.2fs total testing time\n", (time - $overallStartTime) . "";
533 if ($checkLeaks && !$singly && $toolOpen) {
534 my $leaksFileName = fileNameWithNumber($dumpToolName, $leaksOutputFileNumber);
535 $totalLeaks += countAndPrintLeaks($dumpToolName, $dumpToolPID, "$testResultsDirectory/$leaksFileName-leaks.txt");
536 $leaksOutputFileNumber++;
539 # FIXME: Do we really want to check the image-comparison tool for leaks every time?
540 if ($checkLeaks && $pixelTests) {
541 $totalLeaks += countAndPrintLeaks("ImageDiff", $imageDiffToolPID, "$testResultsDirectory/ImageDiff-leaks.txt");
545 print "\nWARNING: $totalLeaks total leaks found!\n";
546 print "See above for individual leaks results.\n" if ($leaksOutputFileNumber > 2);
552 if ($report10Slowest) {
553 print "\n\nThe 10 slowest tests:\n\n";
555 for my $test (sort slowestcmp keys %durations) {
556 printf "%0.2f secs: %s\n", $durations{$test}, $test;
557 last if ++$count == 10;
563 if ($resetResults || ($counts{match} && $counts{match} == $count)) {
564 print "all $count test cases succeeded\n";
570 match => "succeeded",
571 mismatch => "had incorrect layout",
573 fail => "failed (tool did not execute successfully)",
576 for my $type ("match", "mismatch", "new", "fail") {
577 my $c = $counts{$type};
579 my $t = $text{$type};
583 $message = sprintf "1 test case (%d%%) %s\n", 1 * 100 / $count, $t;
585 $message = sprintf "%d test cases (%d%%) %s\n", $c, $c * 100 / $count, $t;
587 $message =~ s-\(0%\)-(<1%)-;
592 mkpath $testResultsDirectory;
594 open HTML, ">", $testResults or die;
595 print HTML "<html>\n";
596 print HTML "<head>\n";
597 print HTML "<title>Layout Test Results</title>\n";
598 print HTML "</head>\n";
599 print HTML "<body>\n";
601 if ($counts{mismatch}) {
602 print HTML "<p>Tests where results did not match expected results:</p>\n";
603 print HTML "<table>\n";
604 for my $test (@{$tests{mismatch}}) {
606 $base =~ s/\.[a-zA-Z]+$//;
607 copy("$testDirectory/$base-$expectedTag.txt", "$testResultsDirectory/$base-$expectedTag.txt");
609 print HTML "<td><a href=\"$testDirectory/$test\">$base</a></td>\n";
610 if (-s "$testResultsDirectory/$base-$diffsTag.txt") {
611 print HTML "<td><a href=\"$base-$expectedTag.txt\">expected</a></td>\n";
612 print HTML "<td><a href=\"$base-$actualTag.txt\">actual</a></td>\n";
613 print HTML "<td><a href=\"$base-$diffsTag.txt\">diffs</a></td>\n";
615 print HTML "<td></td><td></td><td></td>\n";
618 if ($imagesPresent{$base}) {
619 print HTML "<td><a href=\"$base-$expectedTag.png\">expected image</a></td>\n";
620 print HTML "<td><a href=\"$base-$diffsTag.html\">image diffs</a></td>\n";
622 print HTML "<td></td><td></td>\n";
625 print HTML "</tr>\n";
627 print HTML "</table>\n";
631 print HTML "<p>Tests that caused the DumpRenderTree tool to fail:</p>\n";
632 print HTML "<table>\n";
633 for my $test (@{$tests{fail}}) {
635 $base =~ s/\.[a-zA-Z]+$//;
637 print HTML "<td><a href=\"$testDirectory/$test\">$base</a></td>\n";
638 print HTML "</tr>\n";
640 print HTML "</table>\n";
644 print HTML "<p>Tests that had no expected results (probably new):</p>\n";
645 print HTML "<table>\n";
646 for my $test (@{$tests{new}}) {
648 $base =~ s/\.[a-zA-Z]+$//;
650 print HTML "<td><a href=\"$testDirectory/$test\">$base</a></td>\n";
651 print HTML "<td><a href=\"$testDirectory/$base-$expectedTag.txt\">results</a></td>\n";
652 if ($pixelTests && -f "$testDirectory/$base-$expectedTag.png") {
653 print HTML "<td><a href=\"$testDirectory/$base-$expectedTag.png\">image</a></td>\n";
655 print HTML "</tr>\n";
657 print HTML "</table>\n";
660 print HTML "</body>\n";
661 print HTML "</html>\n";
664 system "WebKitTools/Scripts/run-safari", "-NSOpen", $testResults if $launchSafari;
668 sub countAndPrintLeaks($$$)
670 my ($toolName, $toolPID, $leaksFilePath) = @_;
672 print "\n" unless $atLineStart;
675 # We are excluding the following reported leaks so they don't get in our way when looking for WebKit leaks:
678 "pthread_create", # false positive leak of 'THRD', Radar 3387783
679 "_CFPreferencesDomainDeepCopyDictionary", # leak apparently in CFPreferences, Radar 4220786
680 "FOGetCoveredUnicodeChars", # leak apparently in ATS, Radar 3943604
681 "PCFragPrepareClosureFromFile", # leak in Code Fragment Manager, Radar 3426998
682 "Flash_EnforceLocalSecurity", # leaks in flash plugin code, Radar 4449747
683 "ICCFPrefWrapper::GetPrefDictionary()" # leaks in quicktime plugin code, Radar 4449794
686 # Note that the exclusion for bug 3387783 doesn't quite work right; sometimes a leak of 'THRD' will
687 # still appear in the leaks output.
689 my $excludes = "-exclude '" . (join "' -exclude '", @exclude) . "'";
691 print " ? checking for leaks in $toolName\n";
692 my $leaksOutput = `leaks $excludes $toolPID`;
693 my ($count, $bytes) = $leaksOutput =~ /Process $toolPID: (\d+) leaks? for (\d+) total/;
694 my ($excluded) = $leaksOutput =~ /(\d+) leaks? excluded/;
696 my $adjustedCount = $count;
697 $adjustedCount -= $excluded if $excluded;
699 if (!$adjustedCount) {
700 print " - no leaks found\n";
701 unlink $leaksFilePath;
704 my $dir = $leaksFilePath;
705 $dir =~ s|/[^/]+$|| or die;
709 print " + $adjustedCount leaks ($bytes bytes including $excluded excluded leaks) were found, details in $leaksFilePath\n";
711 print " + $count leaks ($bytes bytes) were found, details in $leaksFilePath\n";
714 open LEAKS, ">", $leaksFilePath or die;
715 print LEAKS $leaksOutput;
719 return $adjustedCount;
722 # Break up a path into the directory (with slash) and base name.
727 my $pathSeparator = "/";
728 my $dirname = dirname($path) . $pathSeparator;
729 $dirname = "" if $dirname eq "." . $pathSeparator;
731 return ($dirname, basename($path));
734 # Sort first by directory, then by file, so all paths in one directory are grouped
735 # rather than being interspersed with items from subdirectories.
736 # Use numericcmp to sort directory and filenames to make order logical.
739 my ($patha, $pathb) = @_;
741 my ($dira, $namea) = splitpath($patha);
742 my ($dirb, $nameb) = splitpath($pathb);
744 return numericcmp($dira, $dirb) if $dira ne $dirb;
745 return numericcmp($namea, $nameb);
748 # Sort numeric parts of strings as numbers, other parts as strings.
749 # Makes 1.33 come after 1.3, which is cool.
754 my @a = split /(\d+)/, $aa;
755 my @b = split /(\d+)/, $bb;
757 # Compare one chunk at a time.
758 # Each chunk is either all numeric digits, or all not numeric digits.
763 # Use numeric comparison if chunks are non-equal numbers.
764 return $a <=> $b if $a =~ /^\d/ && $b =~ /^\d/ && $a != $b;
766 # Use string comparison if chunks are any other kind of non-equal string.
767 return $a cmp $b if $a ne $b;
770 # One of the two is now empty; compare lengths for result in this case.
774 # Sort slowest tests first.
777 my ($testa, $testb) = @_;
779 my $dura = $durations{$testa};
780 my $durb = $durations{$testb};
781 return $durb <=> $dura if $dura != $durb;
782 return pathcmp($testa, $testb);
785 sub openDumpRenderTreeIfNeeded()
790 $ENV{DYLD_FRAMEWORK_PATH} = $productDir;
791 $ENV{XML_CATALOG_FILES} = ""; # work around missing /etc/catalog <rdar://problem/4292995>
792 $ENV{MallocStackLogging} = 1 if $checkLeaks;
793 $ENV{DYLD_INSERT_LIBRARIES} = "/usr/lib/libgmalloc.dylib" if $guardMalloc;
794 $dumpToolPID = open2(\*IN, \*OUT, $tool, @toolArgs) or die "Failed to start tool: $tool\n";
798 sub closeDumpRenderTree()
800 return if !$toolOpen;
804 waitpid $dumpToolPID, 0;
808 sub openHTTPDIfNeeded()
810 return if $httpdOpen;
814 if (-f "/tmp/WebKit/httpd.pid") {
815 my $oldPid = `cat /tmp/WebKit/httpd.pid`;
817 if (0 != kill 0, $oldPid) {
818 print "\nhttpd is already running: pid $oldPid, killing...\n";
822 while ((0 != kill 0, $oldPid) && $retryCount) {
827 die "Timed out waiting for httpd to quit" unless $retryCount;
831 my $httpdPath = "/usr/sbin/httpd";
832 my $httpdConfig = "$testDirectory/http/conf/httpd.conf";
833 $httpdConfig = "$testDirectory/http/conf/apache2-httpd.conf" if `$httpdPath -v` =~ m|Apache/2|;
834 my $documentRoot = "$testDirectory/http/tests";
835 my $typesConfig = "$testDirectory/http/conf/mime.types";
836 my $listen = "127.0.0.1:$httpdPort";
837 my $absTestResultsDirectory = File::Spec->rel2abs(glob $testResultsDirectory);
839 mkpath $absTestResultsDirectory;
841 open2(\*HTTPDIN, \*HTTPDOUT, $httpdPath,
842 "-f", "$httpdConfig",
843 "-C", "DocumentRoot \"$documentRoot\"",
844 "-C", "Listen $listen",
845 "-c", "TypesConfig \"$typesConfig\"",
846 "-c", "CustomLog \"$absTestResultsDirectory/access_log.txt\" common",
847 "-c", "ErrorLog \"$absTestResultsDirectory/error_log.txt\"",
848 # Apache wouldn't run CGIs with permissions==700 otherwise
849 "-c", "User \"#$<\"");
852 while (system("/usr/bin/curl -q --silent --stderr - --output /dev/null $listen") && $retryCount) {
857 die "Timed out waiting for httpd to start" unless $retryCount;
864 return if !$httpdOpen;
869 kill 15, `cat /tmp/WebKit/httpd.pid` if -f "/tmp/WebKit/httpd.pid";
874 sub fileNameWithNumber($$)
876 my ($base, $number) = @_;
877 return "$base$number" if ($number > 1);
881 sub processIgnoreTests($) {
882 my @ignoreList = split(/\s*,\s*/, shift);
883 my $addIgnoredDirectories = sub {
884 return () if exists $ignoredLocalDirectories{basename($File::Find::dir)};
885 $ignoredDirectories{File::Spec->abs2rel($File::Find::dir, $testDirectory)} = 1;
888 foreach my $item (@ignoreList) {
889 my $path = catfile($testDirectory, $item);
891 $ignoredDirectories{$item} = 1;
892 find({ preprocess => $addIgnoredDirectories, wanted => sub {} }, $path);
895 $ignoredFiles{$item} = 1;
898 print "ignoring '$item' on ignore-tests list\n";