layout_test_runner_unittest.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. # Copyright (C) 2012 Google Inc. All rights reserved.
  2. # Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions are
  6. # met:
  7. #
  8. # * Redistributions of source code must retain the above copyright
  9. # notice, this list of conditions and the following disclaimer.
  10. # * Redistributions in binary form must reproduce the above
  11. # copyright notice, this list of conditions and the following disclaimer
  12. # in the documentation and/or other materials provided with the
  13. # distribution.
  14. # * Neither the name of Google Inc. nor the names of its
  15. # contributors may be used to endorse or promote products derived from
  16. # this software without specific prior written permission.
  17. #
  18. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. import unittest2 as unittest
  30. from webkitpy.common.host_mock import MockHost
  31. from webkitpy.common.system.systemhost_mock import MockSystemHost
  32. from webkitpy.layout_tests import run_webkit_tests
  33. from webkitpy.layout_tests.controllers.layout_test_runner import LayoutTestRunner, Sharder, TestRunInterruptedException
  34. from webkitpy.layout_tests.models import test_expectations
  35. from webkitpy.layout_tests.models import test_failures
  36. from webkitpy.layout_tests.models.test_run_results import TestRunResults
  37. from webkitpy.layout_tests.models.test_input import TestInput
  38. from webkitpy.layout_tests.models.test_results import TestResult
  39. from webkitpy.port.test import TestPort
  40. TestExpectations = test_expectations.TestExpectations
  41. class FakePrinter(object):
  42. num_started = 0
  43. num_tests = 0
  44. def print_expected(self, run_results, get_tests_with_result_type):
  45. pass
  46. def print_workers_and_shards(self, num_workers, num_shards, num_locked_shards):
  47. pass
  48. def print_started_test(self, test_name):
  49. pass
  50. def print_finished_test(self, result, expected, exp_str, got_str):
  51. pass
  52. def write(self, msg):
  53. pass
  54. def write_update(self, msg):
  55. pass
  56. def flush(self):
  57. pass
  58. class LockCheckingRunner(LayoutTestRunner):
  59. def __init__(self, port, options, printer, tester, http_lock):
  60. super(LockCheckingRunner, self).__init__(options, port, printer, port.results_directory(), lambda test_name: False)
  61. self._finished_list_called = False
  62. self._tester = tester
  63. self._should_have_http_lock = http_lock
  64. def handle_finished_list(self, source, list_name, num_tests, elapsed_time):
  65. if not self._finished_list_called:
  66. self._tester.assertEqual(list_name, 'locked_tests')
  67. self._tester.assertTrue(self._remaining_locked_shards)
  68. self._tester.assertTrue(self._has_http_lock is self._should_have_http_lock)
  69. super(LockCheckingRunner, self).handle_finished_list(source, list_name, num_tests, elapsed_time)
  70. if not self._finished_list_called:
  71. self._tester.assertEqual(self._remaining_locked_shards, [])
  72. self._tester.assertFalse(self._has_http_lock)
  73. self._finished_list_called = True
  74. class LayoutTestRunnerTests(unittest.TestCase):
  75. def _runner(self, port=None):
  76. # FIXME: we shouldn't have to use run_webkit_tests.py to get the options we need.
  77. options = run_webkit_tests.parse_args(['--platform', 'test-mac-snowleopard'])[0]
  78. options.child_processes = '1'
  79. host = MockHost()
  80. port = port or host.port_factory.get(options.platform, options=options)
  81. return LockCheckingRunner(port, options, FakePrinter(), self, True)
  82. def _run_tests(self, runner, tests):
  83. test_inputs = [TestInput(test, 6000) for test in tests]
  84. expectations = TestExpectations(runner._port, tests)
  85. runner.run_tests(expectations, test_inputs, set(),
  86. num_workers=1, needs_http=any('http' in test for test in tests), needs_websockets=any(['websocket' in test for test in tests]), retrying=False)
  87. def test_http_locking(self):
  88. runner = self._runner()
  89. self._run_tests(runner, ['http/tests/passes/text.html', 'passes/text.html'])
  90. def test_perf_locking(self):
  91. runner = self._runner()
  92. self._run_tests(runner, ['http/tests/passes/text.html', 'perf/foo/test.html'])
  93. def test_interrupt_if_at_failure_limits(self):
  94. runner = self._runner()
  95. runner._options.exit_after_n_failures = None
  96. runner._options.exit_after_n_crashes_or_times = None
  97. test_names = ['passes/text.html', 'passes/image.html']
  98. runner._test_inputs = [TestInput(test_name, 6000) for test_name in test_names]
  99. run_results = TestRunResults(TestExpectations(runner._port, test_names), len(test_names))
  100. run_results.unexpected_failures = 100
  101. run_results.unexpected_crashes = 50
  102. run_results.unexpected_timeouts = 50
  103. # No exception when the exit_after* options are None.
  104. runner._interrupt_if_at_failure_limits(run_results)
  105. # No exception when we haven't hit the limit yet.
  106. runner._options.exit_after_n_failures = 101
  107. runner._options.exit_after_n_crashes_or_timeouts = 101
  108. runner._interrupt_if_at_failure_limits(run_results)
  109. # Interrupt if we've exceeded either limit:
  110. runner._options.exit_after_n_crashes_or_timeouts = 10
  111. self.assertRaises(TestRunInterruptedException, runner._interrupt_if_at_failure_limits, run_results)
  112. self.assertEqual(run_results.results_by_name['passes/text.html'].type, test_expectations.SKIP)
  113. self.assertEqual(run_results.results_by_name['passes/image.html'].type, test_expectations.SKIP)
  114. runner._options.exit_after_n_crashes_or_timeouts = None
  115. runner._options.exit_after_n_failures = 10
  116. exception = self.assertRaises(TestRunInterruptedException, runner._interrupt_if_at_failure_limits, run_results)
  117. def test_update_summary_with_result(self):
  118. # Reftests expected to be image mismatch should be respected when pixel_tests=False.
  119. runner = self._runner()
  120. runner._options.pixel_tests = False
  121. test = 'failures/expected/reftest.html'
  122. expectations = TestExpectations(runner._port, tests=[test])
  123. runner._expectations = expectations
  124. run_results = TestRunResults(expectations, 1)
  125. result = TestResult(test_name=test, failures=[test_failures.FailureReftestMismatchDidNotOccur()], reftest_type=['!='])
  126. runner._update_summary_with_result(run_results, result)
  127. self.assertEqual(1, run_results.expected)
  128. self.assertEqual(0, run_results.unexpected)
  129. run_results = TestRunResults(expectations, 1)
  130. result = TestResult(test_name=test, failures=[], reftest_type=['=='])
  131. runner._update_summary_with_result(run_results, result)
  132. self.assertEqual(0, run_results.expected)
  133. self.assertEqual(1, run_results.unexpected)
  134. def test_servers_started(self):
  135. def start_http_server(number_of_servers=None):
  136. self.http_started = True
  137. def start_websocket_server():
  138. self.websocket_started = True
  139. def stop_http_server():
  140. self.http_stopped = True
  141. def stop_websocket_server():
  142. self.websocket_stopped = True
  143. host = MockHost()
  144. port = host.port_factory.get('test-mac-leopard')
  145. port.start_http_server = start_http_server
  146. port.start_websocket_server = start_websocket_server
  147. port.stop_http_server = stop_http_server
  148. port.stop_websocket_server = stop_websocket_server
  149. self.http_started = self.http_stopped = self.websocket_started = self.websocket_stopped = False
  150. runner = self._runner(port=port)
  151. runner._needs_http = True
  152. runner._needs_websockets = False
  153. runner.start_servers_with_lock(number_of_servers=4)
  154. self.assertEqual(self.http_started, True)
  155. self.assertEqual(self.websocket_started, False)
  156. runner.stop_servers_with_lock()
  157. self.assertEqual(self.http_stopped, True)
  158. self.assertEqual(self.websocket_stopped, False)
  159. self.http_started = self.http_stopped = self.websocket_started = self.websocket_stopped = False
  160. runner._needs_http = True
  161. runner._needs_websockets = True
  162. runner.start_servers_with_lock(number_of_servers=4)
  163. self.assertEqual(self.http_started, True)
  164. self.assertEqual(self.websocket_started, True)
  165. runner.stop_servers_with_lock()
  166. self.assertEqual(self.http_stopped, True)
  167. self.assertEqual(self.websocket_stopped, True)
  168. self.http_started = self.http_stopped = self.websocket_started = self.websocket_stopped = False
  169. runner._needs_http = False
  170. runner._needs_websockets = False
  171. runner.start_servers_with_lock(number_of_servers=4)
  172. self.assertEqual(self.http_started, False)
  173. self.assertEqual(self.websocket_started, False)
  174. runner.stop_servers_with_lock()
  175. self.assertEqual(self.http_stopped, False)
  176. self.assertEqual(self.websocket_stopped, False)
  177. class SharderTests(unittest.TestCase):
  178. test_list = [
  179. "http/tests/websocket/tests/unicode.htm",
  180. "animations/keyframes.html",
  181. "http/tests/security/view-source-no-refresh.html",
  182. "http/tests/websocket/tests/websocket-protocol-ignored.html",
  183. "fast/css/display-none-inline-style-change-crash.html",
  184. "http/tests/xmlhttprequest/supported-xml-content-types.html",
  185. "dom/html/level2/html/HTMLAnchorElement03.html",
  186. "ietestcenter/Javascript/11.1.5_4-4-c-1.html",
  187. "dom/html/level2/html/HTMLAnchorElement06.html",
  188. "perf/object-keys.html",
  189. ]
  190. def get_test_input(self, test_file):
  191. return TestInput(test_file, requires_lock=(test_file.startswith('http') or test_file.startswith('perf')))
  192. def get_shards(self, num_workers, fully_parallel, test_list=None, max_locked_shards=1):
  193. port = TestPort(MockSystemHost())
  194. self.sharder = Sharder(port.split_test, max_locked_shards)
  195. test_list = test_list or self.test_list
  196. return self.sharder.shard_tests([self.get_test_input(test) for test in test_list], num_workers, fully_parallel)
  197. def assert_shards(self, actual_shards, expected_shard_names):
  198. self.assertEqual(len(actual_shards), len(expected_shard_names))
  199. for i, shard in enumerate(actual_shards):
  200. expected_shard_name, expected_test_names = expected_shard_names[i]
  201. self.assertEqual(shard.name, expected_shard_name)
  202. self.assertEqual([test_input.test_name for test_input in shard.test_inputs],
  203. expected_test_names)
  204. def test_shard_by_dir(self):
  205. locked, unlocked = self.get_shards(num_workers=2, fully_parallel=False)
  206. # Note that although there are tests in multiple dirs that need locks,
  207. # they are crammed into a single shard in order to reduce the # of
  208. # workers hitting the server at once.
  209. self.assert_shards(locked,
  210. [('locked_shard_1',
  211. ['http/tests/security/view-source-no-refresh.html',
  212. 'http/tests/websocket/tests/unicode.htm',
  213. 'http/tests/websocket/tests/websocket-protocol-ignored.html',
  214. 'http/tests/xmlhttprequest/supported-xml-content-types.html',
  215. 'perf/object-keys.html'])])
  216. self.assert_shards(unlocked,
  217. [('animations', ['animations/keyframes.html']),
  218. ('dom/html/level2/html', ['dom/html/level2/html/HTMLAnchorElement03.html',
  219. 'dom/html/level2/html/HTMLAnchorElement06.html']),
  220. ('fast/css', ['fast/css/display-none-inline-style-change-crash.html']),
  221. ('ietestcenter/Javascript', ['ietestcenter/Javascript/11.1.5_4-4-c-1.html'])])
  222. def test_shard_every_file(self):
  223. locked, unlocked = self.get_shards(num_workers=2, fully_parallel=True)
  224. self.assert_shards(locked,
  225. [('.', ['http/tests/websocket/tests/unicode.htm']),
  226. ('.', ['http/tests/security/view-source-no-refresh.html']),
  227. ('.', ['http/tests/websocket/tests/websocket-protocol-ignored.html']),
  228. ('.', ['http/tests/xmlhttprequest/supported-xml-content-types.html']),
  229. ('.', ['perf/object-keys.html'])]),
  230. self.assert_shards(unlocked,
  231. [('.', ['animations/keyframes.html']),
  232. ('.', ['fast/css/display-none-inline-style-change-crash.html']),
  233. ('.', ['dom/html/level2/html/HTMLAnchorElement03.html']),
  234. ('.', ['ietestcenter/Javascript/11.1.5_4-4-c-1.html']),
  235. ('.', ['dom/html/level2/html/HTMLAnchorElement06.html'])])
  236. def test_shard_in_two(self):
  237. locked, unlocked = self.get_shards(num_workers=1, fully_parallel=False)
  238. self.assert_shards(locked,
  239. [('locked_tests',
  240. ['http/tests/websocket/tests/unicode.htm',
  241. 'http/tests/security/view-source-no-refresh.html',
  242. 'http/tests/websocket/tests/websocket-protocol-ignored.html',
  243. 'http/tests/xmlhttprequest/supported-xml-content-types.html',
  244. 'perf/object-keys.html'])])
  245. self.assert_shards(unlocked,
  246. [('unlocked_tests',
  247. ['animations/keyframes.html',
  248. 'fast/css/display-none-inline-style-change-crash.html',
  249. 'dom/html/level2/html/HTMLAnchorElement03.html',
  250. 'ietestcenter/Javascript/11.1.5_4-4-c-1.html',
  251. 'dom/html/level2/html/HTMLAnchorElement06.html'])])
  252. def test_shard_in_two_has_no_locked_shards(self):
  253. locked, unlocked = self.get_shards(num_workers=1, fully_parallel=False,
  254. test_list=['animations/keyframe.html'])
  255. self.assertEqual(len(locked), 0)
  256. self.assertEqual(len(unlocked), 1)
  257. def test_shard_in_two_has_no_unlocked_shards(self):
  258. locked, unlocked = self.get_shards(num_workers=1, fully_parallel=False,
  259. test_list=['http/tests/websocket/tests/unicode.htm'])
  260. self.assertEqual(len(locked), 1)
  261. self.assertEqual(len(unlocked), 0)
  262. def test_multiple_locked_shards(self):
  263. locked, unlocked = self.get_shards(num_workers=4, fully_parallel=False, max_locked_shards=2)
  264. self.assert_shards(locked,
  265. [('locked_shard_1',
  266. ['http/tests/security/view-source-no-refresh.html',
  267. 'http/tests/websocket/tests/unicode.htm',
  268. 'http/tests/websocket/tests/websocket-protocol-ignored.html']),
  269. ('locked_shard_2',
  270. ['http/tests/xmlhttprequest/supported-xml-content-types.html',
  271. 'perf/object-keys.html'])])
  272. locked, unlocked = self.get_shards(num_workers=4, fully_parallel=False)
  273. self.assert_shards(locked,
  274. [('locked_shard_1',
  275. ['http/tests/security/view-source-no-refresh.html',
  276. 'http/tests/websocket/tests/unicode.htm',
  277. 'http/tests/websocket/tests/websocket-protocol-ignored.html',
  278. 'http/tests/xmlhttprequest/supported-xml-content-types.html',
  279. 'perf/object-keys.html'])])