Changes by Ben Lamonica and Eric Seidel, reviewed mostly by Eric and
[WebKit-https.git] / WebKitTools / Scripts / run-webkit-tests
1 #!/usr/bin/perl -w
2
3 # Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # 1.  Redistributions of source code must retain the above copyright
10 #     notice, this list of conditions and the following disclaimer. 
11 # 2.  Redistributions in binary form must reproduce the above copyright
12 #     notice, this list of conditions and the following disclaimer in the
13 #     documentation and/or other materials provided with the distribution. 
14 # 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 #     its contributors may be used to endorse or promote products derived
16 #     from this software without specific prior written permission. 
17 #
18 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 # Script to run the Web Kit Open Source Project layout tests.
30
31 use strict;
32 use IPC::Open2;
33 use Getopt::Long;
34 use FindBin;
35 use Cwd;
36 use lib $FindBin::Bin;
37 use webkitdirs;
38
39 # Run all the tests passed in on the command line.
40 # If no tests are passed, find all the .html, .xml, .xhtml (and svg) files in the test directory.
41
42 # Run each text.
43 # Compare against the existing file xxx-expected.txt.
44 # If there is a mismatch, generate xxx-actual.txt and xxx-diffs.txt.
45
46 # At the end, report:
47 #   the number of tests that got the expected results
48 #   the number of tests that ran, but did not get the expected results
49 #   the number of tests that failed to run
50 #   the number of tests that were run but had no expected results to compare against
51
52 setConfiguration();
53 my $productDir = productDir();
54
55 chdirWebKit();
56
57 # Argument handling
58 my $testSVGs = '';
59 my $pixelTests = '';
60 my $maxWidth = '';
61 my $maxHeight = '';
62 my $verbose = '';
63 my $quiet = '';
64
65 GetOptions('svg' => \$testSVGs, 
66     'pixel-tests' => \$pixelTests,
67     'max-width' => \$maxWidth,
68     'max-height' => \$maxHeight, 
69     'verbose' => \$verbose,
70     'quiet' => \$quiet);
71
72 my $toolname = "DumpRenderTree";
73
74 my $result = system "WebKitTools/Scripts/build-dumprendertree", @ARGV;
75 exit $result if $result;
76 if ($testSVGs) {
77     my $result = system "WebKitTools/Scripts/build-dumpkcanvastree", @ARGV;
78     exit $result if $result;
79     $toolname = "DumpKCanvasTree";
80 }
81
82 my $tool = "$productDir/$toolname";
83 my $imageDiffTool = "$productDir/ImageDiff";
84 die "can't find executable $toolname tool (looked in $productDir)\n" if !-x $tool;
85 die "can't find executable $imageDiffTool tool (looked in $productDir)\n" if $pixelTests && !-x $imageDiffTool;
86
87 if ($testSVGs) {
88     checkSVGFrameworks();
89 } else {
90     checkFrameworks();
91 }
92
93 my $layoutTestsName = "layout-tests";
94 if ($testSVGs) {
95     $layoutTestsName = "svg-tests";
96 }
97
98 my $workingDir = getcwd();
99 my $WebCoreDirectory = "$workingDir/WebCore";
100 my $testDirectory = "$WebCoreDirectory/$layoutTestsName";
101 my $testResultsDirectory = "/tmp/layout-test-results";
102 my $testResults = "$testResultsDirectory/results.html";
103
104 $ENV{DYLD_FRAMEWORK_PATH} = $productDir;
105
106 my @tests;
107
108 my $findArguments = "\\( -name resources \\! -prune \\) -or -name '*.html' -or -name '*.xml' -or -name '*.xhtml'";
109 if ($testSVGs) {
110     $findArguments = "\\( -name resources \\! -prune \\) -or -name '*.svg'";
111 }
112 my $foundTestName = 0;
113 for my $test (@ARGV) {
114     next if $test =~ /^-/;
115     $foundTestName = 1;
116     $test =~ s/^$testDirectory\///;
117     if ($test =~ /^\//) {
118         print "can't run test outside $testDirectory\n";
119     } elsif (-f "$testDirectory/$test") {
120         if ($test !~ /\.(html|xml|xhtml|svg)$/) {
121             print "test $test does not have a supported extension\n";
122         } else {
123             push @tests, $test;
124         }
125     } elsif (-d "$testDirectory/$test") {
126         push @tests, map { chomp; s-^$testDirectory/--; $_; } `find -Ls $testDirectory/$test $findArguments`;
127     } else {
128         print "test $test not found\n";
129     }
130 }
131 if (!$foundTestName) {
132     @tests = map { chomp; s-^$testDirectory/--; $_; } `find -Ls $testDirectory $findArguments`;
133 }
134
135 die "no tests to run\n" if !@tests;
136
137 my %counts;
138 my %tests;
139 my %imagesPresent;
140 my $count;
141 my @toolArgs;
142
143 if ($pixelTests) {
144     push @toolArgs, "--pixel-tests";
145     if ($maxWidth) {
146         push @toolArgs, ("--width", $maxWidth);
147     }
148  
149     if ($maxWidth) {
150         push @toolArgs, ("--height", $maxWidth);
151     }
152 }
153
154 push  @toolArgs, "-";
155
156 $| = 1;
157
158 open2(\*IN, \*OUT, $tool, @toolArgs) or die;
159
160 if ($pixelTests) {
161     open2(\*DIFFIN, \*DIFFOUT, $imageDiffTool, "") or die "unable to open $imageDiffTool\n";
162 }
163
164 my $column = 0;
165 my $lastDirectory = "";
166
167 for my $test (@tests) {
168     next if $test eq 'results.html';
169     
170     my $base = $test;
171     $base =~ s/\.(html|xml|xhtml|svg)$//;
172     
173     if ($verbose) {
174         print "running $base test";
175     } elsif (!$quiet) {
176         my $dir = $base;
177         $dir =~ s|/[^/]+$||;
178         if ($dir ne $lastDirectory) {
179             print "\n" unless $column == 0;
180             print $dir . " ";
181             $lastDirectory = $dir;
182             $column = 0;
183         }
184
185         print ".";
186         $column++;
187     }
188
189     my $result;
190
191     print OUT "$testDirectory/$test\n";
192
193     my $actual = "";
194     while (<IN>) {
195         last if /#EOF/;
196         $actual .= $_;
197     }
198
199     my $expected;
200     if (open EXPECTED, "<", "$testDirectory/$base-expected.txt") {
201         $expected = "";
202         while (<EXPECTED>) {
203             $expected .= $_;
204         }
205         close EXPECTED;
206     }
207
208     my $textDumpMatches = $expected && ($actual eq $expected);
209     my $actualHash = "";
210     my $expectedHash = "";
211     my $hashMatches = "";
212     my $actualPNG = "";
213     my $actualPNGSize = 0;
214     my $expectedPNG = "";
215     my $expectedPNGSize = 0;
216     my $diffPNG = "";
217     my $diffPercentage = "";
218     my $diffResult = "passed";
219     
220     if ($pixelTests) {
221         while (<IN>) {
222             last if /#EOF/;
223             if (/ActualHash: ([a-f0-9]{32})/) {
224                 $actualHash = $1;
225             } elsif (/BaselineHash: ([a-f0-9]{32})/) {
226                 $expectedHash = $1;
227             } elsif (/Content-length: (\d+)\s*/) {
228                 $actualPNGSize = $1;
229                 read(IN, $actualPNG, $actualPNGSize);
230             }
231         }
232
233         if ($hashMatches = ($expectedHash eq $actualHash)) {
234             $diffResult = "passed";
235         }
236
237         if (!$hashMatches && -f "$testDirectory/$base-expected.png" && $textDumpMatches) {
238             $expectedPNGSize = getFilesize("$testDirectory/$base-expected.png");
239             open EXPECTEDPNG, "$testDirectory/$base-expected.png";
240             read(EXPECTEDPNG, $expectedPNG, $expectedPNGSize);
241
242             print DIFFOUT "Content-length: $actualPNGSize\n";
243             print DIFFOUT $actualPNG;
244
245             print DIFFOUT "Content-length: $expectedPNGSize\n";
246             print DIFFOUT $expectedPNG;
247
248             while (<DIFFIN>) {
249                 last if /^error/ || /^diff:/;
250                 if (/Content-length: (\d+)\s*/) {
251                     read(DIFFIN, $diffPNG, $1);
252                 }
253             }
254
255             if (/^diff: (.+)% (passed|failed)/) {
256                 $diffPercentage = $1;
257                 $diffResult = $2;
258             }
259         }
260     }
261
262     if ($pixelTests) {
263         if ($actualPNGSize != 0 && ! -f "$testDirectory/$base-expected.png") {
264             open EXPECTED, ">", "$testDirectory/$base-expected.png" or die "could not create $testDirectory/$base-expected.png\n";
265             print EXPECTED $actualPNG;
266             close EXPECTED;
267         }
268
269         # update the expected hash if the image diff said that there was no difference
270         if ($actualHash ne "" && ! -f "$testDirectory/$base-expected.checksum") {
271             open EXPECTED, ">", "$testDirectory/$base-expected.checksum" or die "could not create $testDirectory/$base-expected.checksum\n";
272             print EXPECTED $actualHash;
273             close EXPECTED;
274         }
275     }
276
277     if (!defined $expected) {
278         if ($verbose) {
279             print " -> new test\n";
280         }
281         $result = "new";
282         open EXPECTED, ">", "$testDirectory/$base-expected.txt" or die "could not create $testDirectory/$base-expected.txt\n";
283         print EXPECTED $actual;
284         close EXPECTED;
285
286         unlink "$testResultsDirectory/$base-actual.txt";
287         unlink "$testResultsDirectory/$base-diffs.txt";
288     } elsif ($textDumpMatches && (!$pixelTests || ($pixelTests && $diffResult eq "passed"))) {
289         if ($verbose) {
290             print " -> succeeded\n";
291         }
292         $result = "match";
293         unlink "$testResultsDirectory/$base-actual.txt";
294         unlink "$testResultsDirectory/$base-diffs.txt";
295     } elsif (!$textDumpMatches || ($pixelTests && $diffResult ne "passed")) {
296         print "\n" unless $column == 0;
297         print "$test -> failed\n";
298         $column = 0;        
299
300         $result = "mismatch";
301
302         my $dir = "$testResultsDirectory/$base";
303         $dir =~ s|/[^/]+$||;
304         system "mkdir", "-p", $dir;
305
306         open ACTUAL, ">", "$testResultsDirectory/$base-actual.txt" or die;
307         print ACTUAL $actual;
308         close ACTUAL;
309
310         system "diff -u \"$testDirectory/$base-expected.txt\" \"$testResultsDirectory/$base-actual.txt\" > \"$testResultsDirectory/$base-diffs.txt\"";
311
312         if ($pixelTests && $diffPNG && $diffPNG ne "") {
313             $imagesPresent{$base} = 1;
314
315             open ACTUAL, ">", "$testResultsDirectory/$base-actual.png" or die;
316             print ACTUAL $actualPNG;
317             close ACTUAL;
318
319             open DIFF, ">", "$testResultsDirectory/$base-diffs.png" or die;
320             print DIFF $diffPNG;
321             close DIFF;
322
323             open DIFFHTML, ">$testResultsDirectory/$base-diffs.html" or die;
324             print DIFFHTML "<html>\n";
325             print DIFFHTML "<head>\n";
326             print DIFFHTML "<title>$base Image Compare</title>\n";
327             print DIFFHTML "<script language=\"Javascript\" type=\"text/javascript\">\n";
328             print DIFFHTML "var actualImageVisible = true;\n";
329             print DIFFHTML "function animateImage() {\n";
330             print DIFFHTML "    var image = document.getElementById(\"animatedImage\");\n";
331             print DIFFHTML "    var imageText = document.getElementById(\"imageText\");\n";
332             print DIFFHTML "    if (actualImageVisible) {\n";
333             print DIFFHTML "        image.src=\"$testDirectory/$base-expected.png\";\n";
334             print DIFFHTML "        imageText.innerHTML = \"Expected Image\";\n";
335             print DIFFHTML "        actualImageVisible = false;\n";
336             print DIFFHTML "    } else {\n";
337             print DIFFHTML "        image.src=\"$testResultsDirectory/$base-actual.png\";\n";
338             print DIFFHTML "        imageText.innerHTML = \"Actual Image\";\n";
339             print DIFFHTML "        actualImageVisible = true;\n";
340             print DIFFHTML "    }\n";
341             print DIFFHTML "    setTimeout('animateImage()',2000);\n";
342             print DIFFHTML "}\n";
343             print DIFFHTML "</script>\n";
344             print DIFFHTML "</head>\n";
345             print DIFFHTML "<body onLoad=\"animateImage();\">\n";
346             print DIFFHTML "<table>\n";
347             if ($diffPercentage) {
348                 print DIFFHTML "<tr>\n";
349                 print DIFFHTML "<td>Difference between images: <a href=\"$testResultsDirectory/$base-diffs.png\">$diffPercentage%</a></td>\n";
350                 print DIFFHTML "</tr>\n";
351             }
352             print DIFFHTML "<tr>\n";
353             print DIFFHTML "<td id=\"imageText\" style=\"text-weight: bold;\">Actual Image</td>\n";
354             print DIFFHTML "</tr>\n";
355             print DIFFHTML "<tr>\n";
356             print DIFFHTML "<td><img src=\"$testResultsDirectory/$base-actual.png\" id=\"animatedImage\"></td>\n";
357             print DIFFHTML "</tr>\n";
358             print DIFFHTML "</table>\n";
359             print DIFFHTML "</body>\n";
360             print DIFFHTML "</html>\n";
361         }
362     } else {
363         $result = "fail";
364         print "\n" unless $column == 0;
365         print "$test -> crashed\n";
366         $column = 0;
367
368         close IN;
369         close OUT;
370         open2(\*IN, \*OUT, $tool, @toolArgs) or die;
371     }
372     
373     $count += 1;
374     $counts{$result} += 1;
375     push @{$tests{$result}}, $test;
376 }
377
378 close IN;
379 close OUT;
380
381 my %text = (
382     match => "succeeded",
383     mismatch => "had incorrect layout",
384     new => "were new",
385     fail => "failed (tool did not execute successfully)",
386 );
387
388 print "\n";
389
390 if ($counts{match} && $counts{match} == $count) {
391     print "all $count test cases succeeded\n";
392     unlink $testResults;
393 } else {
394     for my $type ("match", "mismatch", "new", "fail") {
395         my $c = $counts{$type};
396         if ($c) {
397             my $t = $text{$type};
398             my $message;
399             if ($c == 1) {
400                 $t =~ s/were/was/;
401                 $message = sprintf "1 test case (%d%%) %s\n", 1 * 100 / $count, $t;
402             } else {
403                 $message = sprintf "%d test cases (%d%%) %s\n", $c, $c * 100 / $count, $t;
404             }
405             $message =~ s-\(0%\)-(<1%)-;
406             print $message;
407         }
408     }
409     
410     system "mkdir", "-p", $testResultsDirectory;
411
412     open HTML, ">", $testResults or die;
413     print HTML "<html>\n";
414     print HTML "<head>\n";
415     print HTML "<title>Layout Test Results</title>\n";
416     print HTML "</head>\n";
417     print HTML "<body>\n";
418
419     if ($counts{mismatch}) {
420         print HTML "<p>Tests where results did not match expected results:</p>\n";
421         print HTML "<table>\n";
422         for my $test (@{$tests{mismatch}}) {
423             my $base = $test;
424             $base =~ s/\.(html|xml|xhtml|svg)$//;
425             print HTML "<tr>\n";
426             print HTML "<td><a href=\"$testDirectory/$test\">$base</a></td>\n";
427             print HTML "<td><a href=\"$testDirectory/$base-expected.txt\">expected</a></td>\n";
428             print HTML "<td><a href=\"$base-actual.txt\">actual</a></td>\n";
429             print HTML "<td><a href=\"$base-diffs.txt\">diffs</a></td>\n";
430             if ($pixelTests) {
431                 print HTML "<td><a href=\"$testDirectory/$base-expected.png\">expected image</a></td>\n";
432                 if ($imagesPresent{$base}) {
433                     print HTML "<td><a href=\"$base-diffs.html\">image diffs</a></td>\n";
434                 }
435             }
436             print HTML "</tr>\n";
437         }
438         print HTML "</table>\n";
439     }
440
441     if ($counts{fail}) {
442         print HTML "<p>Tests that caused the DumpRenderTree tool to fail:</p>\n";
443         print HTML "<table>\n";
444         for my $test (@{$tests{fail}}) {
445             my $base = $test;
446             $base =~ s/\.(html|xml|xhtml|svg)$//;
447             print HTML "<tr>\n";
448             print HTML "<td><a href=\"$testDirectory/$test\">$base</a></td>\n";
449             print HTML "</tr>\n";
450         }
451         print HTML "</table>\n";
452     }
453
454     if ($counts{new}) {
455         print HTML "<p>Tests that had no expected results (probably new):</p>\n";
456         print HTML "<table>\n";
457         for my $test (@{$tests{new}}) {
458             my $base = $test;
459             $base =~ s/\.(html|xml|xhtml|svg)$//;
460             print HTML "<tr>\n";
461             print HTML "<td><a href=\"$testDirectory/$test\">$base</a></td>\n";
462             print HTML "<td><a href=\"$testDirectory/$base-expected.txt\">results</a></td>\n";
463             if ($pixelTests) {
464                 print HTML "<td><a href=\"$testDirectory/$base-expected.png\">image</a></td>\n";
465             }
466             print HTML "</tr>\n";
467         }
468         print HTML "</table>\n";
469     }
470
471     print HTML "</body>\n";
472     print HTML "</html>\n";
473     close HTML;
474     
475     system "open", $testResults;
476 }
477
478 sub getFilesize
479 {
480     my $filename = shift;
481     my @fileStats;
482
483     @fileStats = stat($filename);
484
485     $fileStats[7];
486 }