run-api-tests doesn't print test name when the test crashes
[WebKit-https.git] / Tools / Scripts / run-api-tests
1 #!/usr/bin/perl -w
2
3 # Copyright (C) 2010-2012, 2014-2015 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 sigtrap qw(die normal-signals);
35 use webkitdirs;
36 use VCSUtils;
37
38 sub buildTestTool();
39 sub dumpTestsBySuite(\@);
40 sub listAllTests();
41 sub runTest($$);
42 sub runTestsBySuite(\@);
43 sub prepareEnvironmentForRunningTestTool();
44 sub archCommandLineArgumentsForRestrictedEnvironmentVariables();
45 sub testToolPaths();
46
47 # Defined in VCSUtils.
48 sub possiblyColored($$);
49
50 # Timeout for individual test, in sec
51 my $timeout = 30;
52
53 my $showHelp = 0;
54 my $verbose = 0;
55 my $showLeaks = 0;
56 my $dumpTests = 0;
57 my $disableTimeout = 0;
58 my $build = 1;
59 my $root;
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] [suite or test prefixes]
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   --root=               Path to the pre-built root containing TestWebKitAPI
72   --show-leaks          Show leaks in the output
73   --no-timeout          Disable test timeouts
74
75 Platform options:
76   --ios-simulator       Run tests in the iOS Simulator
77
78 @{[ sharedCommandLineOptionsUsage(indent => 2, switchWidth => 21) ]}
79 Examples
80
81 The following command will run a single test:
82     $programName WebKit2.AboutBlank
83
84 The following command will run all tests in suites that begin with 'WebKit2':
85     $programName WebKit2
86
87 EOF
88
89 my $getOptionsResult = GetOptions(
90     sharedCommandLineOptions(),
91     'help' => \$showHelp,
92     'verbose|v' => \$verbose,
93     'show-leaks' => \$showLeaks,
94     'no-timeout' => \$disableTimeout,
95     'dump|d' => \$dumpTests,
96     'build!' => \$build,
97     'root=s' => \$root
98 );
99
100 if (!$getOptionsResult || $showHelp) {
101    print STDERR $usage;
102    exit 1;
103 }
104
105 setConfiguration();
106
107 setConfigurationProductDir(Cwd::abs_path($root)) if (defined($root));
108
109 buildTestTool() if $build && !defined($root);
110 setPathForRunningWebKitApp(\%ENV);
111
112 my $simulatorDevice;
113 if (willUseIOSSimulatorSDK()) {
114     $simulatorDevice = findOrCreateSimulatorForIOSDevice(SIMULATOR_DEVICE_SUFFIX_FOR_WEBKIT_DEVELOPMENT);
115     restartIOSSimulatorDevice($simulatorDevice);
116 }
117 my @testsToRun = listAllTests();
118
119 @testsToRun = grep { my $test = $_; grep { $test =~ m/^\Q$_\E/ } @ARGV; } @testsToRun if @ARGV;
120
121 if ($dumpTests) {
122     dumpTestsBySuite(@testsToRun);
123     exit 0;
124 }
125
126 END { shutDownIOSSimulatorDevice($simulatorDevice) if $simulatorDevice; }
127
128 exit runTestsBySuite(@testsToRun);
129
130 sub isSupportedPlatform()
131 {
132     return isAppleMacWebKit() || isAppleWinWebKit();
133 }
134
135 sub dumpTestsBySuite(\@)
136 {
137     my ($tests) = @_;
138     print "Dumping test cases\n";
139     print "------------------\n";
140     my $lastSuite = "";
141     for my $suiteAndTest (sort @$tests) {
142         my ($suite, $test) = split(/\./, $suiteAndTest);
143         if ($lastSuite ne $suite) {
144             $lastSuite = $suite;
145             print "$suite:\n";
146         }
147         print "   $test\n";
148     }
149     print "------------------\n";
150 }
151
152 sub runTestsBySuite(\@)
153 {
154     my ($tests) = @_;
155     for my $suiteAndTest (sort @$tests) {
156         my ($suite, $test) = split(/\./, $suiteAndTest);
157         runTest($suite, $test);
158     }
159
160     if (@testsFailed) {
161         print "\nTests that failed:\n";
162         for my $test (@testsFailed) {
163             print "  $test\n";
164         }
165     }
166     if (@testsTimedOut) {
167         print "\nTests that timed out:\n";
168         for my $test (@testsTimedOut) {
169             print "  $test\n";
170         }
171     }
172
173     return @testsFailed > 0 || @testsTimedOut > 0;
174 }
175
176 sub runTest($$)
177 {
178     my ($suite, $testName) = @_;
179     my $test = $suite . "." . $testName;
180
181     my $gtestArg = "--gtest_filter=" . $test;
182
183     my $result = 0;
184     my $timedOut = 0;
185
186     die "run-api-tests is not supported on this platform.\n" unless isSupportedPlatform();
187
188     local %ENV = %ENV;
189     prepareEnvironmentForRunningTestTool();
190
191     local *DEVNULL;
192     my ($childIn, $childOut, $childErr);
193     if ($verbose || $showLeaks) {
194         $childErr = 0;
195     } else {
196         open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null";
197         $childErr = ">&DEVNULL";
198     }
199
200     foreach(testToolPaths()) {
201         my $pid;
202         my @commonArguments = ($_, $gtestArg, @ARGV);
203         if (willUseIOSSimulatorSDK()) {
204             $pid = open3($childIn, $childOut, $childErr, qw(xcrun --sdk iphonesimulator simctl spawn), $simulatorDevice->{UDID}, @commonArguments) or die "Failed to run test: $test.";
205         } elsif (isAppleMacWebKit() && architecture()) {
206             $pid = open3($childIn, $childOut, $childErr, "arch", "-" . architecture(), archCommandLineArgumentsForRestrictedEnvironmentVariables(), @commonArguments) or die "Failed to run test: $test.";
207         } else {
208             $pid = open3($childIn, $childOut, $childErr, @commonArguments) or die "Failed to run test: $test.";
209         }
210
211         eval {
212             if ($disableTimeout) {
213                 waitpid($pid, 0);    
214             } else {
215                 local $SIG{ALRM} = sub { die "alarm\n" };
216                 alarm $timeout;
217                 waitpid($pid, 0);
218                 alarm 0;
219             }
220             $result = $?;
221         };
222         if ($@) {
223             die unless $@ eq "alarm\n";
224             kill SIGTERM, $pid or kill SIGKILL, $pid;
225             $timedOut = 1;
226         }
227
228         my @testOutput = <$childOut>;
229         @testOutput = grep { !/^LEAK:/ } @testOutput unless $showLeaks;
230         map { s/\*\*PASS\*\*/possiblyColored("bold green", "PASS")/eg } @testOutput;
231         map { s/\*\*FAIL\*\*/possiblyColored("bold red", "FAIL")/eg } @testOutput;
232
233         if ($result) {
234             push @testsFailed, $test;
235             if (!$timedOut && index("@testOutput", $test) == -1) {
236                 print STDOUT possiblyColored("bold red", "UNEXPECTEDLY EXITED"), " $test\n";
237             }
238         } elsif ($timedOut) {
239             push @testsTimedOut, $test;
240             print STDOUT possiblyColored("bold yellow", "TIMEOUT"), " $test\n";
241         }
242
243         print STDOUT @testOutput;
244
245         close($childIn);
246         close($childOut);
247         close($childErr) unless ($verbose || $showLeaks);
248         close(DEVNULL) unless ($verbose || $showLeaks);
249
250         if ($timedOut || $result) {
251             return $timedOut || $result;
252         }
253     }
254     return 0;
255 }
256
257 sub listAllTests()
258 {
259     my @toolOutput;
260     my $timedOut;
261
262     die "run-api-tests is not supported on this platform.\n" unless isSupportedPlatform();
263
264     prepareEnvironmentForRunningTestTool();
265
266     local *DEVNULL;
267     my ($childIn, $childOut, $childErr);
268     if ($verbose) {
269         $childErr = ">&STDERR";
270     } else {
271         open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null";
272         $childErr = ">&DEVNULL";
273     }
274
275     my @tests = ();
276     foreach(testToolPaths()) {
277         my $pid;
278         my @commonArguments = ($_, "--gtest_list_tests");
279         if (isIOSWebKit()) {
280             $pid = open3($childIn, $childOut, $childErr, qw(xcrun --sdk iphonesimulator simctl spawn), $simulatorDevice->{UDID}, @commonArguments) or die "Failed to build list of tests!";
281         } elsif (isAppleMacWebKit() && architecture()) {
282             $pid = open3($childIn, $childOut, $childErr, "arch", "-" . architecture(), archCommandLineArgumentsForRestrictedEnvironmentVariables(), @commonArguments) or die "Failed to build list of tests!";
283         } else {
284             $pid = open3($childIn, $childOut, $childErr, @commonArguments) or die "Failed to build list of tests!";
285         }
286
287         close($childIn);
288         @toolOutput = <$childOut>;
289         close($childOut);
290         close($childErr);
291         close(DEVNULL) unless ($verbose);
292
293         waitpid($pid, 0);
294         my $result = $?;
295
296         if ($result) {
297             print STDERR "Failed to build list of tests!--\n";
298             exit exitStatus($result);
299         }
300
301         my $suite;
302         for my $line (@toolOutput) {
303            $line =~ s/[\r\n]*$//;
304            if ($line =~ m/\.$/) {
305               $suite = $line; # "SuiteName."
306            } else {
307               # Disabling WebKit2 API test on Windows since we will be disabling WebKit2 on Windows.
308               next if (isAppleWinWebKit() && $suite =~ m/WebKit2*/);       
309               $line =~ s/^\s*//; # "TestName"
310               push @tests, $suite . $line; # "SuiteName.TestName"
311             }
312         }
313     }
314     return @tests;
315 }
316
317 sub buildTestTool()
318 {
319     my $originalCwd = getcwd();
320
321     chdirWebKit();
322
323     my $buildTestTool = "build-api-tests";
324     print STDERR "Running $buildTestTool\n";
325
326     local *DEVNULL;
327     my ($childIn, $childOut, $childErr);
328     if ($verbose) {
329         # When not quiet, let the child use our stdout/stderr.
330         $childOut = ">&STDOUT";
331         $childErr = ">&STDERR";
332     } else {
333         open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null";
334         $childOut = ">&DEVNULL";
335         $childErr = ">&DEVNULL";
336     }
337
338     my @args = argumentsForConfiguration();
339     my $pathToBuildTestTool = File::Spec->catfile("Tools", "Scripts", $buildTestTool);
340     my $buildProcess = open3($childIn, $childOut, $childErr, "perl", $pathToBuildTestTool, @args) or die "Failed to run " . $buildTestTool;
341
342     close($childIn);
343     close($childOut);
344     close($childErr);
345     close(DEVNULL) unless ($verbose);
346
347     waitpid($buildProcess, 0);
348     my $buildResult = $?;
349
350     if ($buildResult) {
351         print STDERR "Compiling TestWebKitAPI failed!\n";
352         exit exitStatus($buildResult);
353     }
354
355     chdir $originalCwd;
356 }
357
358 sub prepareEnvironmentForRunningTestTool()
359 {
360     return unless isAppleMacWebKit();
361
362     if (willUseIOSSimulatorSDK()) {
363         my %simulatorENV;
364         {
365             local %ENV;
366             setupIOSWebKitEnvironment(productDir());
367             %simulatorENV = %ENV;
368         }
369         # Prefix the environment variables with SIMCTL_CHILD_ per `xcrun simctl help launch`.
370         foreach my $key (keys %simulatorENV) {
371             $ENV{"SIMCTL_CHILD_$key"} = $simulatorENV{$key};
372         }
373         return;
374     }
375     setupMacWebKitEnvironment(productDir());
376 }
377
378 sub archCommandLineArgumentsForRestrictedEnvironmentVariables()
379 {
380     my @arguments = ();
381     foreach my $key (keys(%ENV)) {
382         if ($key =~ /^DYLD_/) {
383             push @arguments, "-e", "$key=$ENV{$key}";
384         }
385     }
386     return @arguments;
387 }
388
389 sub testToolPaths()
390 {
391     my $path = File::Spec->catfile(productDir(), "TestWebKitAPI");
392     return ($path) unless isAppleWinWebKit();
393
394     my $binDir = isWin64() ? "bin64" : "bin32";
395     my $pathWTF = File::Spec->catfile(productDir(), $binDir, "TestWTF");
396     my $pathWebCore = File::Spec->catfile(productDir(), $binDir, "TestWebCore");
397     my $pathWebKit = File::Spec->catfile(productDir(), $binDir, "TestWebKit");
398
399     my $suffix;
400     if (configuration() eq "Debug_All") {
401         $suffix = "_debug";
402     } else {
403         $suffix = "";
404     }
405     return ("$pathWTF$suffix.exe", "$pathWebCore$suffix.exe", "$pathWebKit$suffix.exe");
406 }