3 # Copyright (C) 2011, 2012 Igalia S.L.
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Library General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Library General Public License for more details.
15 # You should have received a copy of the GNU Library General Public License
16 # along with this library; see the file COPYING.LIB. If not, write to
17 # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 # Boston, MA 02110-1301, USA.
26 from signal import alarm, signal, SIGALRM, SIGKILL
27 from gi.repository import Gio, GLib
32 def __init__(self, test, test_case, reason, bug=None):
34 self.test_case = test_case
39 skipped_test_str = "%s" % self.test
41 if not(self.skip_entire_suite()):
42 skipped_test_str += " [%s]" % self.test_case
44 skipped_test_str += ": %s " % self.reason
45 if self.bug is not None:
46 skipped_test_str += "(https://bugs.webkit.org/show_bug.cgi?id=%d)" % self.bug
47 return skipped_test_str
49 def skip_entire_suite(self):
50 return self.test_case == SkippedTest.ENTIRE_SUITE
52 class TestTimeout(Exception):
56 TEST_DIRS = [ "unittests", "WebKit2APITests", "TestWebKitAPI" ]
59 SkippedTest("unittests/testdownload", "/webkit/download/not-found", "Test fails in GTK Linux 64-bit Release bot", 82329),
60 SkippedTest("unittests/testwebview", "/webkit/webview/icon-uri", "Test times out in GTK Linux 64-bit Release bot", 82328),
61 SkippedTest("unittests/testwebresource", "/webkit/webresource/sub_resource_loading", "Test fails in GTK Linux 64-bit Release bot", 82330),
62 SkippedTest("unittests/testwebinspector", "/webkit/webinspector/close-and-inspect", "Test is flaky in GTK Linux 32-bit Release bot", 82869),
63 SkippedTest("WebKit2APITests/TestWebKitWebView", "/webkit2/WebKitWebView/mouse-target", "Test is flaky in GTK Linux 32-bit Release bot", 82866),
64 SkippedTest("WebKit2APITests/TestResources", "/webkit2/WebKitWebView/resources", "Test is flaky in GTK Linux 32-bit Release bot", 82868),
65 SkippedTest("WebKit2APITests/TestWebKitFindController", "/webkit2/WebKitFindController/next", "Test fails ", 91083),
66 SkippedTest("WebKit2APITests/TestWebKitFindController", "/webkit2/WebKitFindController/previous", "Test fails", 91083),
67 SkippedTest("WebKit2APITests/TestWebKitFindController", "/webkit2/WebKitFindController/hide", "Test always fails in Xvfb", 89810),
68 SkippedTest("TestWebKitAPI/TestWebKit2", "WebKit2.WKConnection", "Tests fail and time out out", 84959),
69 SkippedTest("TestWebKitAPI/TestWebKit2", "WebKit2.RestoreSessionStateContainingFormData", "Session State is not implemented in GTK+ port", 84960),
70 SkippedTest("TestWebKitAPI/TestWebKit2", "WebKit2.SpacebarScrolling", "Test fails", 84961),
71 SkippedTest("TestWebKitAPI/TestWebKit2", "WebKit2.NewFirstVisuallyNonEmptyLayoutForImages", "Test is flaky", 85066),
72 SkippedTest("TestWebKitAPI/TestWebKit2", "WebKit2.NewFirstVisuallyNonEmptyLayoutFrames", "Test fails", 85037),
73 SkippedTest("TestWebKitAPI/TestWebKit2", "WebKit2.MouseMoveAfterCrash", "Test is flaky", 85066),
74 SkippedTest("TestWebKitAPI/TestWebKit2", "WebKit2.CanHandleRequest", "Test fails", 88453),
75 SkippedTest("TestWebKitAPI/TestWebKit2", "WebKit2.WKPageGetScaleFactorNotZero", "Test fails and times out", 88455),
78 def __init__(self, options, tests=[]):
79 self._options = options
80 self._programs_path = common.build_path("Programs")
81 self._tests = self._get_tests(tests)
82 self._skipped_tests = TestRunner.SKIPPED
84 # These SPI daemons need to be active for the accessibility tests to work.
85 self._spi_registryd = None
86 self._spi_bus_launcher = None
88 def _get_tests(self, tests):
93 for test_dir in self.TEST_DIRS:
94 absolute_test_dir = os.path.join(self._programs_path, test_dir)
95 if not os.path.isdir(absolute_test_dir):
97 for test_file in os.listdir(absolute_test_dir):
98 if not test_file.lower().startswith("test"):
100 test_path = os.path.join(self._programs_path, test_dir, test_file)
101 if os.path.isfile(test_path) and os.access(test_path, os.X_OK):
102 tests.append(test_path)
105 def _lookup_atspi2_binary(self, filename):
106 exec_prefix = common.pkg_config_file_variable('atspi-2', 'exec_prefix')
109 for path in ['libexec', 'lib/at-spi2-core', 'lib32/at-spi2-core', 'lib64/at-spi2-core']:
110 filepath = os.path.join(exec_prefix, path, filename)
111 if os.path.isfile(filepath):
116 def _start_accessibility_daemons(self):
118 spi_bus_launcher_path = self._lookup_atspi2_binary('at-spi-bus-launcher')
119 spi_registryd_path = self._lookup_atspi2_binary('at-spi2-registryd')
120 if not spi_bus_launcher_path or not spi_registryd_path:
124 self._ally_bus_launcher = subprocess.Popen([spi_bus_launcher_path], env=self._test_env)
126 sys.stderr.write("Failed to launch the accessibility bus\n")
130 # We need to wait until the SPI bus is launched before trying to start the SPI
131 # registry, so we spin a main loop until the bus name appears on DBus.
132 loop = GLib.MainLoop()
133 Gio.bus_watch_name(Gio.BusType.SESSION, 'org.a11y.Bus', Gio.BusNameWatcherFlags.NONE,
134 lambda *args: loop.quit(), None)
138 self._spi_registryd = subprocess.Popen([spi_registryd_path], env=self._test_env)
140 sys.stderr.write("Failed to launch the accessibility registry\n")
146 def _setup_testing_environment(self):
147 self._test_env = os.environ
148 self._test_env["DISPLAY"] = self._options.display
149 self._test_env["WEBKIT_INSPECTOR_PATH"] = os.path.abspath(os.path.join(self._programs_path, 'resources', 'inspector'))
150 self._test_env['GSETTINGS_BACKEND'] = 'memory'
151 self._test_env["TEST_WEBKIT_API_WEBKIT2_RESOURCES_PATH"] = common.top_level_path("Tools", "TestWebKitAPI", "Tests", "WebKit2")
152 self._test_env["TEST_WEBKIT_API_WEBKIT2_INJECTED_BUNDLE_PATH"] = common.build_path("Libraries")
153 self._test_env["WEBKIT_EXEC_PATH"] = self._programs_path
156 self._xvfb = subprocess.Popen(["Xvfb", self._options.display, "-screen", "0", "800x600x24", "-nolisten", "tcp"],
157 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
158 except Exception as e:
159 sys.stderr.write("Failed to run Xvfb: %s\n" % e)
163 # If we cannot start the accessibility daemons, we can just skip the accessibility tests.
164 if not self._start_accessibility_daemons():
165 print "Could not start accessibility bus, so skipping TestWebKitAccessibility"
166 self._skipped_tests.append(SkippedTest("WebKit2APITests/TestWebKitAccessibility", SkippedTest.ENTIRE_SUITE, "Could not start accessibility bus"))
169 def _tear_down_testing_environment(self):
170 if self._spi_registryd:
171 self._spi_registryd.terminate()
172 if self._spi_bus_launcher:
173 self._spi_bus_launcher.terminate()
174 self._xvfb.terminate()
176 def _test_cases_to_skip(self, test_program):
177 if self._options.skipped_action != 'skip':
181 for skipped in self._skipped_tests:
182 if test_program.endswith(skipped.test) and not skipped.skip_entire_suite():
183 test_cases.append(skipped.test_case)
186 def _should_run_test_program(self, test_program):
187 # This is not affected by the command-line arguments, since programs are skipped for
188 # problems in the harness, such as failing to start the accessibility bus.
189 for skipped in self._skipped_tests:
190 if test_program.endswith(skipped.test) and skipped.skip_entire_suite():
194 def _get_child_pid_from_test_output(self, output):
197 match = re.search(r'\(pid=(?P<child_pid>[0-9]+)\)', output)
200 return int(match.group('child_pid'))
202 def _kill_process(self, pid):
204 os.kill(pid, SIGKILL)
206 # Process already died.
209 def _run_test_command(self, command, timeout=-1):
210 def alarm_handler(signum, frame):
213 p = subprocess.Popen(command, stdout=subprocess.PIPE, env=self._test_env)
215 signal(SIGALRM, alarm_handler)
220 stdout = p.communicate()[0]
223 sys.stdout.write(stdout)
226 self._kill_process(p.pid)
227 child_pid = self._get_child_pid_from_test_output(stdout)
229 self._kill_process(child_pid)
232 return not p.returncode
234 def _run_test_glib(self, test_program):
235 tester_command = ['gtester']
236 if self._options.verbose:
237 tester_command.append('--verbose')
238 for test_case in self._test_cases_to_skip(test_program):
239 tester_command.extend(['-s', test_case])
240 tester_command.append(test_program)
242 return self._run_test_command(tester_command, self._options.timeout)
244 def _run_test_google(self, test_program):
245 tester_command = [test_program]
246 skipped_tests_cases = self._test_cases_to_skip(test_program)
247 if skipped_tests_cases:
248 tester_command.append("--gtest_filter=-%s" % ":".join(skipped_tests_cases))
250 return self._run_test_command(tester_command, self._options.timeout)
252 def _run_test(self, test_program):
253 if "unittests" in test_program or "WebKit2APITests" in test_program:
254 return self._run_test_glib(test_program)
256 if "TestWebKitAPI" in test_program:
257 return self._run_test_google(test_program)
263 sys.stderr.write("ERROR: tests not found in %s.\n" % (self._programs_path))
267 if not self._setup_testing_environment():
270 # Remove skipped tests now instead of when we find them, because
271 # some tests might be skipped while setting up the test environment.
272 self._tests = [test for test in self._tests if self._should_run_test_program(test)]
277 for test in self._tests:
280 success = self._run_test(test)
282 sys.stdout.write("TEST: %s: TIMEOUT\n" % test)
284 timed_out_tests.append(test)
287 failed_tests.append(test)
289 self._tear_down_testing_environment()
292 names = [test.replace(self._programs_path, '', 1) for test in failed_tests]
293 sys.stdout.write("Tests failed: %s\n" % ", ".join(names))
297 names = [test.replace(self._programs_path, '', 1) for test in timed_out_tests]
298 sys.stdout.write("Tests that timed out: %s\n" % ", ".join(names))
301 if self._skipped_tests and self._options.skipped_action == 'skip':
302 sys.stdout.write("Tests skipped:\n%s\n" % "\n".join([str(skipped) for skipped in self._skipped_tests]))
305 return len(failed_tests)
307 if __name__ == "__main__":
308 option_parser = optparse.OptionParser(usage='usage: %prog [options] [test...]')
309 option_parser.add_option('-r', '--release',
310 action='store_true', dest='release',
311 help='Run in Release')
312 option_parser.add_option('-d', '--debug',
313 action='store_true', dest='debug',
315 option_parser.add_option('-v', '--verbose',
316 action='store_true', dest='verbose',
317 help='Run gtester in verbose mode')
318 option_parser.add_option('--display', action='store', dest='display', default=':55',
319 help='Display to run Xvfb')
320 option_parser.add_option('--skipped', action='store', dest='skipped_action',
321 choices=['skip', 'ignore', 'only'], default='skip',
322 metavar='skip|ignore|only',
323 help='Specifies how to treat the skipped tests')
324 option_parser.add_option('-t', '--timeout',
325 action='store', type='int', dest='timeout', default=10,
326 help='Time in seconds until a test times out')
327 options, args = option_parser.parse_args()
329 sys.exit(TestRunner(options, args).run_tests())