[iOS] Support TestWebKitAPI in the iOS Simulator
[WebKit-https.git] / Tools / Scripts / run-api-tests
1 #!/usr/bin/perl -w
2
3 # Copyright (C) 2010, 2011, 2012 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 use strict;
27 use warnings;
28
29 use File::Basename;
30 use FindBin;
31 use Getopt::Long qw(:config pass_through);
32 use IPC::Open3;
33 use lib $FindBin::Bin;
34 use webkitdirs;
35 use VCSUtils;
36
37 sub buildTestTool();
38 sub dumpTestsBySuite(\@);
39 sub listAllTests();
40 sub runTest($$$);
41 sub runTestsBySuite(\@$);
42 sub prepareEnvironmentForRunningTestTool();
43 sub testToolPath();
44
45 # Defined in VCSUtils.
46 sub possiblyColored($$);
47
48 # Timeout for individual test, in sec
49 my $timeout = 10;
50
51 my $showHelp = 0;
52 my $verbose = 0;
53 my $dumpTests = 0;
54 my $build = 1;
55 my $root;
56 my $buildDefault = $build ? "build" : "do not build";
57 my @testsFailed;
58 my @testsTimedOut;
59
60 my $programName = basename($0);
61 my $usage = <<EOF;
62 Usage: $programName [options] [suite or test prefixes]
63   --help                Show this help message
64   -v|--verbose          Verbose output
65   -d|--dump-tests       Dump the names of testcases without running them
66   --[no-]build          Build (or do not build) unit tests prior to running (default: $buildDefault)
67   --root=               Path to the pre-built root containing TestWebKitAPI
68
69 Examples
70
71 The following command will run a single test:
72     $programName WebKit2.AboutBlank
73
74 The following command will run all tests in suites that begin with 'WebKit2':
75     $programName WebKit2
76
77 EOF
78
79 GetOptions(
80     'help' => \$showHelp,
81     'verbose|v' => \$verbose,
82     'dump|d' => \$dumpTests,
83     'build!' => \$build,
84     'root=s' => \$root
85 );
86
87 if ($showHelp) {
88    print STDERR $usage;
89    exit 1;
90 }
91
92 setConfiguration();
93
94 setConfigurationProductDir(Cwd::abs_path($root)) if (defined($root));
95
96 buildTestTool() if $build && !defined($root);
97 setPathForRunningWebKitApp(\%ENV);
98 my @testsToRun = listAllTests();
99
100 @testsToRun = grep { my $test = $_; grep { $test =~ m/^\Q$_\E/ } @ARGV; } @testsToRun if @ARGV;
101
102 if ($dumpTests) {
103     dumpTestsBySuite(@testsToRun);
104     exit 0;
105 }
106
107 exit runTestsBySuite(@testsToRun, $verbose);
108
109 sub isSupportedPlatform()
110 {
111     return isAppleMacWebKit() || isAppleWinWebKit();
112 }
113
114 sub dumpTestsBySuite(\@)
115 {
116     my ($tests) = @_;
117     print "Dumping test cases\n";
118     print "------------------\n";
119     my $lastSuite = "";
120     for my $suiteAndTest (sort @$tests) {
121         my ($suite, $test) = split(/\./, $suiteAndTest);
122         if ($lastSuite ne $suite) {
123             $lastSuite = $suite;
124             print "$suite:\n";
125         }
126         print "   $test\n";
127     }
128     print "------------------\n";
129 }
130
131 sub runTestsBySuite(\@$)
132 {
133     my ($tests, $verbose) = @_;
134     my $anyFailures = 0;
135     my $lastSuite = "";
136     for my $suiteAndTest (sort @$tests) {
137         my ($suite, $test) = split(/\./, $suiteAndTest);
138         if ($lastSuite ne $suite) {
139             $lastSuite = $suite;
140             print "Suite: $suite\n" unless $verbose;
141         }
142         my $failed = runTest($suite, $test, $verbose);
143         $anyFailures ||= $failed;
144     }
145     
146     if ($verbose) {
147         if (@testsFailed) {
148             print "Tests that failed:\n";
149             for my $test (@testsFailed) {
150                 print "  $test\n";
151             }
152         }
153         if (@testsTimedOut) {
154             print "Tests that timed out:\n";
155             for my $test (@testsTimedOut) {
156                 print "  $test\n";
157             }
158         }
159     }
160     return $anyFailures;
161 }
162
163 sub runTest($$$)
164 {
165     my ($suite, $testName, $verbose) = @_;
166     my $test = $suite . "." . $testName;
167
168     my $gtestArg = "--gtest_filter=" . $test;
169
170     print "    Test: $testName -> " unless $verbose;
171
172     my $result = 0;
173     my $timedOut = 0;
174
175     die "run-api-tests is not supported on this platform.\n" unless isSupportedPlatform();
176
177     prepareEnvironmentForRunningTestTool();
178
179     local *DEVNULL;
180     my ($childIn, $childOut, $childErr);
181     if ($verbose) {
182         $childOut = ">&STDERR";
183         $childErr = ">&STDERR";
184     } else {
185         open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null";
186         $childOut = ">&DEVNULL";
187         $childErr = ">&DEVNULL";
188     }
189
190     my $pid;
191     my @commonArguments = (testToolPath(), $gtestArg, @ARGV);
192     if (isIOSWebKit()) {
193         $pid = open3($childIn, $childOut, $childErr, qw(xcrun -sdk iphonesimulator sim --environment=merge), @commonArguments) or die "Failed to run test: $test.";
194     } elsif (isAppleMacWebKit() && architecture()) {
195         $pid = open3($childIn, $childOut, $childErr, "arch", "-" . architecture(), @commonArguments) or die "Failed to run test: $test.";
196     } else {
197         $pid = open3($childIn, $childOut, $childErr, @commonArguments) or die "Failed to run test: $test.";
198     }
199
200     close($childIn);
201     close($childOut);
202     close($childErr);
203     close(DEVNULL) unless ($verbose);
204     eval {
205         local $SIG{ALRM} = sub { die "alarm\n" };
206         alarm $timeout;
207         waitpid($pid, 0);
208         alarm 0;
209         $result = $?;
210     };
211     if ($@) {
212         die unless $@ eq "alarm\n";
213         kill SIGTERM, $pid or kill SIGKILL, $pid;
214         $timedOut = 1;
215     }
216
217     if ($result) {
218         push @testsFailed, $test;
219     }
220     if ($timedOut) {
221         push @testsTimedOut, $test;
222         print possiblyColored("bold yellow", "Timeout"), "\n";
223     } elsif (!$verbose) {
224         if ($result) {
225             print possiblyColored("bold red", "Failed"), "\n";
226         } else {
227             print possiblyColored("bold green", "Passed"), "\n";
228         }
229     }
230
231     return $timedOut || $result;
232 }
233
234 sub listAllTests()
235 {
236     my @toolOutput;
237     my $timedOut;
238
239     die "run-api-tests is not supported on this platform.\n" unless isSupportedPlatform();
240
241     prepareEnvironmentForRunningTestTool();
242
243     local *DEVNULL;
244     my ($childIn, $childOut, $childErr);
245     if ($verbose) {
246         $childErr = ">&STDERR";
247     } else {
248         open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null";
249         $childErr = ">&DEVNULL";
250     }
251
252     my $pid;
253     my @commonArguments = (testToolPath(), "--gtest_list_tests");
254     if (isIOSWebKit()) {
255         $pid = open3($childIn, $childOut, $childErr, qw(xcrun -sdk iphonesimulator sim --environment=merge), @commonArguments) or die "Failed to build list of tests!";
256     } elsif (isAppleMacWebKit() && architecture()) {
257         $pid = open3($childIn, $childOut, $childErr, "arch", "-" . architecture(), @commonArguments) or die "Failed to build list of tests!";
258     } else {
259         $pid = open3($childIn, $childOut, $childErr, @commonArguments) or die "Failed to build list of tests!";
260     }
261
262     close($childIn);
263     @toolOutput = <$childOut>;
264     close($childOut);
265     close($childErr);
266     close(DEVNULL) unless ($verbose);
267
268     waitpid($pid, 0);
269     my $result = $?;
270
271     if ($result) {
272         print STDERR "Failed to build list of tests!\n";
273         exit exitStatus($result);
274     }
275
276     my @tests = ();
277     my $suite;
278     for my $line (@toolOutput) {
279        $line =~ s/[\r\n]*$//;
280        if ($line =~ m/\.$/) {
281           $suite = $line; # "SuiteName."
282        } else {
283           # Disabling WebKit2 API test on Windows since we will be disabling WebKit2 on Windows.
284           next if (isAppleWinWebKit() && $suite =~ m/WebKit2*/);       
285           $line =~ s/^\s*//; # "TestName"
286           push @tests, $suite . $line; # "SuiteName.TestName"
287         }
288     }
289
290     return @tests;
291 }
292
293 sub buildTestTool()
294 {
295     my $originalCwd = getcwd();
296
297     chdirWebKit();
298
299     my $buildTestTool = "build-api-tests";
300     print STDERR "Running $buildTestTool\n";
301
302     local *DEVNULL;
303     my ($childIn, $childOut, $childErr);
304     if ($verbose) {
305         # When not quiet, let the child use our stdout/stderr.
306         $childOut = ">&STDOUT";
307         $childErr = ">&STDERR";
308     } else {
309         open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null";
310         $childOut = ">&DEVNULL";
311         $childErr = ">&DEVNULL";
312     }
313
314     my @args = argumentsForConfiguration();
315     my $pathToBuildTestTool = File::Spec->catfile("Tools", "Scripts", $buildTestTool);
316     my $buildProcess = open3($childIn, $childOut, $childErr, "perl", $pathToBuildTestTool, @args) or die "Failed to run " . $buildTestTool;
317
318     close($childIn);
319     close($childOut);
320     close($childErr);
321     close(DEVNULL) unless ($verbose);
322
323     waitpid($buildProcess, 0);
324     my $buildResult = $?;
325
326     if ($buildResult) {
327         print STDERR "Compiling TestWebKitAPI failed!\n";
328         exit exitStatus($buildResult);
329     }
330
331     chdir $originalCwd;
332 }
333
334 sub prepareEnvironmentForRunningTestTool()
335 {
336     return unless isAppleMacWebKit();
337
338     $ENV{DYLD_FRAMEWORK_PATH} = productDir();
339     $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES";
340 }
341
342 sub testToolPath()
343 {
344     my $path = File::Spec->catfile(productDir(), "TestWebKitAPI");
345     return $path unless isAppleWinWebKit();
346
347     my $suffix;
348     if (configuration() eq "Debug_All") {
349         $suffix = "_debug";
350     } else {
351         $suffix = "";
352     }
353     return "$path$suffix.exe";
354 }