8add834906840987e6929cd68681264f2b1f6574
[WebKit-https.git] / Tools / Scripts / run-api-tests
1 #!/usr/bin/perl -w
2
3 # Copyright (C) 2010, 2011 Apple 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 # 1. Redistributions of source code must retain the above copyright
9 #    notice, this list of conditions and the following disclaimer.
10 # 2. Redistributions in binary form must reproduce the above copyright
11 #    notice, this list of conditions and the following disclaimer in the
12 #    documentation and/or other materials provided with the distribution.
13 #
14 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 # THE POSSIBILITY OF SUCH DAMAGE.
25
26 # Features to add:
27 #   - Command line option to run a single test.
28 #   - Command line option to run all tests in a suite.
29
30 use strict;
31 use warnings;
32
33 use File::Basename;
34 use FindBin;
35 use Getopt::Long qw(:config pass_through);
36 use IPC::Open3;
37 use lib $FindBin::Bin;
38 use webkitdirs;
39 use VCSUtils;
40
41 sub buildTestTool();
42 sub dumpAllTests();
43 sub populateTests();
44 sub runAllTests();
45 sub runAllTestsInSuite($);
46 sub runTest($$);
47 sub prepareEnvironmentForRunningTestTool();
48 sub testToolPath();
49
50 # Defined in VCSUtils.
51 sub possiblyColored($$);
52
53 # Timeout for individual test, in sec
54 my $timeout = 10;
55
56 my $showHelp = 0;
57 my $verbose = 0;
58 my $dump = 0;
59 my $build = 1;
60 my $buildDefault = $build ? "build" : "do not build";
61 my @testsFailed;
62 my @testsTimedOut;
63
64 my $programName = basename($0);
65 my $usage = <<EOF;
66 Usage: $programName [options]
67   --help                Show this help message
68   -v|--verbose          Verbose output
69   -d|--dump-tests       Dump the names of testcases without running them
70   --[no-]build          Build (or do not build) unit tests prior to running (default: $buildDefault)
71   --chromium            Run the Chromium port on Mac/Win/Linux
72 EOF
73
74 GetOptions(
75     'help' => \$showHelp,
76     'verbose|v' => \$verbose,
77     'dump|d' => \$dump,
78     'build!' => \$build
79 );
80
81 if ($showHelp) {
82    print STDERR $usage;
83    exit 1;
84 }
85
86 setConfiguration();
87 buildTestTool() if $build;
88 setPathForRunningWebKitApp(\%ENV);
89 my %testsToRun = populateTests();
90
91 if ($dump) {
92     dumpAllTests();
93     exit 0;
94 }
95
96 if (runAllTests()) {
97     exit 1;
98 }
99
100 sub isSupportedPlatform()
101 {
102     return isAppleMacWebKit() || isAppleWinWebKit() || isChromium();
103 }
104
105 sub dumpAllTests()
106 {
107     print "Dumping test cases\n";
108     print "------------------\n";
109     for my $suite (keys %testsToRun) {
110         print $suite . ":\n";
111         print map { "   " . $_ . "\n" } @{ $testsToRun{$suite} };
112     }
113     print "------------------\n";
114 }
115
116 sub runAllTests()
117 {
118     my $anyFailures = 0;
119     for my $suite (sort keys %testsToRun) {
120         my $failed = runAllTestsInSuite($suite);
121         if ($failed) {
122             $anyFailures = 1;
123         }
124     }
125     
126     if ($verbose) {
127         if (@testsFailed) {
128             print "Tests that failed:\n";
129             for my $test (@testsFailed) {
130                 print "  $test\n";
131             }
132         }
133         if (@testsTimedOut) {
134             print "Tests that timed out:\n";
135             for my $test (@testsTimedOut) {
136                 print "  $test\n";
137             }
138         }
139     }
140     return $anyFailures;
141 }
142
143 sub runAllTestsInSuite($)
144 {
145     my ($suite) = @_;
146     print "Suite: $suite\n" unless $verbose;
147
148     my $anyFailures = 0;
149     for my $test (sort @{$testsToRun{$suite}}) {
150         my $failed = runTest($suite, $test);
151         if ($failed) {
152             $anyFailures = 1;
153         }
154     }
155    
156     return $anyFailures;
157 }
158
159 sub runTest($$)
160 {
161     my ($suite, $testName) = @_;
162     my $test = $suite . "." . $testName;
163
164     my $gtestArg = "--gtest_filter=" . $test;
165
166     print "    Test: $testName -> " unless $verbose;
167
168     my $result = 0;
169     my $timedOut = 0;
170
171     die "run-api-tests is not supported on this platform.\n" unless isSupportedPlatform();
172
173     prepareEnvironmentForRunningTestTool();
174
175     local *DEVNULL;
176     my ($childIn, $childOut, $childErr);
177     if ($verbose) {
178         $childOut = ">&STDERR";
179         $childErr = ">&STDERR";
180     } else {
181         open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null";
182         $childOut = ">&DEVNULL";
183         $childErr = ">&DEVNULL";
184     }
185
186     my $pid;
187     if (isAppleMacWebKit() && architecture()) {
188         $pid = open3($childIn, $childOut, $childErr, "arch", "-" . architecture(), testToolPath(), $gtestArg, @ARGV) or die "Failed to run test: $test.";
189     } else {
190         $pid = open3($childIn, $childOut, $childErr, testToolPath(), $gtestArg, @ARGV) or die "Failed to run test: $test.";
191     }
192
193     close($childIn);
194     close($childOut);
195     close($childErr);
196     close(DEVNULL) unless ($verbose);
197     eval {
198         local $SIG{ALRM} = sub { die "alarm\n" };
199         alarm $timeout;
200         waitpid($pid, 0);
201         alarm 0;
202         $result = $?;
203     };
204     if ($@) {
205         die unless $@ eq "alarm\n";
206         kill SIGTERM, $pid or kill SIGKILL, $pid;
207         $timedOut = 1;
208     }
209
210     if ($result) {
211         push @testsFailed, $test;
212     }
213     if ($timedOut) {
214         push @testsTimedOut, $test;
215         print possiblyColored("bold yellow", "Timeout"), "\n";
216     } elsif (!$verbose) {
217         if ($result) {
218             print possiblyColored("bold red", "Failed"), "\n";
219         } else {
220             print possiblyColored("bold green", "Passed"), "\n";
221         }
222     }
223
224     return $timedOut || $result;
225 }
226
227 sub populateTests()
228 {
229     my @tests;
230     my $timedOut;
231
232     die "run-api-tests is not supported on this platform.\n" unless isSupportedPlatform();
233
234     prepareEnvironmentForRunningTestTool();
235
236     local *DEVNULL;
237     my ($childIn, $childOut, $childErr);
238     if ($verbose) {
239         $childErr = ">&STDERR";
240     } else {
241         open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null";
242         $childErr = ">&DEVNULL";
243     }
244
245     my $pid;
246     if (isAppleMacWebKit() && architecture()) {
247         $pid = open3($childIn, $childOut, $childErr, "arch", "-" . architecture(), testToolPath(), "--gtest_list_tests") or die "Failed to build list of tests!";
248     } else {
249         $pid = open3($childIn, $childOut, $childErr, testToolPath(), "--gtest_list_tests") or die "Failed to build list of tests!";
250     }
251
252     close($childIn);
253     @tests = <$childOut>;
254     close($childOut);
255     close($childErr);
256     close(DEVNULL) unless ($verbose);
257
258     waitpid($pid, 0);
259     my $result = $?;
260
261     if ($result) {
262         print STDERR "Failed to build list of tests!\n";
263         exit exitStatus($result);
264     }
265
266     my %keyedTests = ();
267     my $suite;
268     for my $test (@tests) {
269        $test =~ s/[\r\n]*$//;
270        if ($test =~ m/\.$/) {
271           $test =~ s/\.$//;
272           $suite = $test;
273        } else {
274           $test =~ s/^\s*//;
275           push @{$keyedTests{$suite}}, $test;
276         }
277     }
278  
279     return %keyedTests;
280 }
281
282
283 sub buildTestTool()
284 {
285     my $originalCwd = getcwd();
286
287     chdirWebKit();
288
289     my $buildTestTool = "build-api-tests";
290     print STDERR "Running $buildTestTool\n";
291
292     local *DEVNULL;
293     my ($childIn, $childOut, $childErr);
294     if ($verbose) {
295         # When not quiet, let the child use our stdout/stderr.
296         $childOut = ">&STDOUT";
297         $childErr = ">&STDERR";
298     } else {
299         open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null";
300         $childOut = ">&DEVNULL";
301         $childErr = ">&DEVNULL";
302     }
303
304     my @args = argumentsForConfiguration();
305     my $pathToBuildTestTool = File::Spec->catfile("Tools", "Scripts", $buildTestTool);
306     my $buildProcess = open3($childIn, $childOut, $childErr, "perl", $pathToBuildTestTool, @args) or die "Failed to run " . $buildTestTool;
307
308     close($childIn);
309     close($childOut);
310     close($childErr);
311     close(DEVNULL) unless ($verbose);
312
313     waitpid($buildProcess, 0);
314     my $buildResult = $?;
315
316     if ($buildResult) {
317         print STDERR "Compiling TestWebKitAPI failed!\n";
318         exit exitStatus($buildResult);
319     }
320
321     chdir $originalCwd;
322 }
323
324 sub prepareEnvironmentForRunningTestTool()
325 {
326     return unless isAppleMacWebKit();
327
328     $ENV{DYLD_FRAMEWORK_PATH} = productDir();
329     $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES";
330 }
331
332 sub testToolPath()
333 {
334     my $path = File::Spec->catfile(productDir(), "TestWebKitAPI");
335     return $path unless isAppleWinWebKit();
336
337     my $suffix;
338     if (configurationForVisualStudio() eq "Debug_All") {
339         $suffix = "_debug";
340     } else {
341         $suffix = "";
342     }
343     return "$path$suffix.exe";
344 }