[WinCairo] httpd service install needs to precede server start
[WebKit-https.git] / Tools / Scripts / webkitpy / port / port_testcase.py
1 # Copyright (C) 2010 Google Inc. All rights reserved.
2 # Copyright (C) 2014-2016 Apple Inc. All rights reserved.
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
30 """Unit testing base class for Port implementations."""
31
32 import errno
33 import logging
34 import os
35 import socket
36 import sys
37 import time
38 import unittest
39
40 from contextlib import contextmanager
41
42 from webkitpy.common.system.executive_mock import MockExecutive
43 from webkitpy.common.system.filesystem_mock import MockFileSystem
44 from webkitpy.common.system.outputcapture import OutputCapture
45 from webkitpy.common.system.systemhost_mock import MockSystemHost
46 from webkitpy.common.version_name_map import INTERNAL_TABLE
47 from webkitpy.port.base import Port
48 from webkitpy.port.config import apple_additions
49 from webkitpy.port.image_diff import ImageDiffer
50 from webkitpy.port.server_process_mock import MockServerProcess
51 from webkitpy.layout_tests.servers import http_server_base
52 from webkitpy.tool.mocktool import MockOptions
53
54
55 # FIXME: get rid of this fixture
56 class TestWebKitPort(Port):
57     port_name = "testwebkitport"
58
59     def __init__(self, port_name=None, symbols_string=None,
60                  expectations_file=None, skips_file=None, host=None, config=None,
61                  **kwargs):
62         port_name = port_name or TestWebKitPort.port_name
63         self.symbols_string = symbols_string  # Passing "" disables all staticly-detectable features.
64         host = host or MockSystemHost()
65         super(TestWebKitPort, self).__init__(host, port_name=port_name, **kwargs)
66
67     def all_test_configurations(self):
68         return [self.test_configuration()]
69
70     def _symbols_string(self):
71         return self.symbols_string
72
73     def _tests_for_other_platforms(self):
74         return ["media", ]
75
76     def _tests_for_disabled_features(self):
77         return ["accessibility", ]
78
79
80 @contextmanager
81 def bind_mock_apple_additions():
82     from webkitpy.common.version_name_map import PUBLIC_TABLE, VersionNameMap
83
84     class MockAppleAdditions(object):
85
86         @staticmethod
87         def layout_tests_path():
88             return '/additional_testing_path/'
89
90         @staticmethod
91         def version_name_mapping(platform=None):
92             result = VersionNameMap(platform)
93             result.mapping[INTERNAL_TABLE] = {}
94             for platform in result.mapping[PUBLIC_TABLE]:
95                 result.mapping[INTERNAL_TABLE][platform] = {}
96                 for name, version in result.mapping[PUBLIC_TABLE][platform].iteritems():
97                     result.mapping[INTERNAL_TABLE][platform]['add-' + name] = version
98             return result
99
100     # apple_additions is a memoized function. Take advantage of this fact and manipulate the cache
101     # to temporarily return a mocked result
102     apple_additions._results_cache[()] = MockAppleAdditions
103     VersionNameMap.map._results_cache = {}
104     yield
105     apple_additions._results_cache[()] = None
106     VersionNameMap.map._results_cache = {}
107
108
109 class PortTestCase(unittest.TestCase):
110     """Tests that all Port implementations must pass."""
111     HTTP_PORTS = (8000, 8080, 8443)
112     WEBSOCKET_PORTS = (8880,)
113
114     # Subclasses override this to point to their Port subclass.
115     os_name = None
116     os_version = None
117     port_maker = TestWebKitPort
118     port_name = None
119     disable_setup = False
120
121     def make_port(self, host=None, port_name=None, options=None, os_name=None, os_version=None, **kwargs):
122         host = host or MockSystemHost(os_name=(os_name or self.os_name), os_version=(os_version or self.os_version))
123         options = options or MockOptions(configuration='Release')
124         port_name = port_name or self.port_name
125         port_name = self.port_maker.determine_full_port_name(host, options, port_name)
126         port = self.port_maker(host, port_name, options=options, **kwargs)
127         port._config.build_directory = lambda configuration: '/mock-build'
128         return port
129
130     def test_default_timeout_ms(self):
131         self.assertEqual(self.make_port(options=MockOptions(configuration='Release')).default_timeout_ms(), 30000)
132         self.assertEqual(self.make_port(options=MockOptions(configuration='Debug')).default_timeout_ms(), 30000)
133
134     def test_default_pixel_tests(self):
135         self.assertEqual(self.make_port().default_pixel_tests(), False)
136
137     def test_driver_cmd_line(self):
138         port = self.make_port()
139         self.assertTrue(len(port.driver_cmd_line_for_logging()))
140
141         options = MockOptions(additional_drt_flag=['--foo=bar', '--foo=baz'])
142         port = self.make_port(options=options)
143         cmd_line = port.driver_cmd_line_for_logging()
144         self.assertTrue('--foo=bar' in cmd_line)
145         self.assertTrue('--foo=baz' in cmd_line)
146
147     def assert_servers_are_down(self, host, ports):
148         for port in ports:
149             try:
150                 test_socket = socket.socket()
151                 test_socket.connect((host, port))
152                 self.fail()
153             except IOError as e:
154                 self.assertTrue(e.errno in (errno.ECONNREFUSED, errno.ECONNRESET))
155             finally:
156                 test_socket.close()
157
158     def assert_servers_are_up(self, host, ports):
159         for port in ports:
160             try:
161                 test_socket = socket.socket()
162                 test_socket.connect((host, port))
163             except IOError as e:
164                 self.fail('failed to connect to %s:%d' % (host, port))
165             finally:
166                 test_socket.close()
167
168     def integration_test_check_sys_deps(self):
169         port = self.make_port()
170         # Only checking that no exception is raised.
171         port.check_sys_deps()
172
173     def integration_test_helper(self):
174         port = self.make_port()
175         # Only checking that no exception is raised.
176         port.start_helper()
177         port.stop_helper()
178
179     def integration_test_http_server__normal(self):
180         port = self.make_port()
181         self.assert_servers_are_down('localhost', self.HTTP_PORTS)
182         port.start_http_server()
183         self.assert_servers_are_up('localhost', self.HTTP_PORTS)
184         port.stop_http_server()
185         self.assert_servers_are_down('localhost', self.HTTP_PORTS)
186
187     def integration_test_http_server__fails(self):
188         port = self.make_port()
189         # Test that if a port isn't available, the call fails.
190         for port_number in self.HTTP_PORTS:
191             test_socket = socket.socket()
192             try:
193                 try:
194                     test_socket.bind(('localhost', port_number))
195                 except socket.error as e:
196                     if e.errno in (errno.EADDRINUSE, errno.EALREADY):
197                         self.fail('could not bind to port %d' % port_number)
198                     raise
199                 try:
200                     port.start_http_server()
201                     self.fail('should not have been able to start the server while bound to %d' % port_number)
202                 except http_server_base.ServerError as e:
203                     pass
204             finally:
205                 port.stop_http_server()
206                 test_socket.close()
207
208         # Test that calling start() twice fails.
209         try:
210             port.start_http_server()
211             self.assertRaises(AssertionError, port.start_http_server)
212         finally:
213             port.stop_http_server()
214
215     def integration_test_http_server__two_servers(self):
216         # Test that calling start() on two different ports causes the
217         # first port to be treated as stale and killed.
218         port = self.make_port()
219         # Test that if a port isn't available, the call fails.
220         port.start_http_server()
221         new_port = self.make_port()
222         try:
223             new_port.start_http_server()
224
225             # Check that the first server was killed.
226             self.assertFalse(port._executive.check_running_pid(port._http_server._pid))
227
228             # Check that there is something running.
229             self.assert_servers_are_up('localhost', self.HTTP_PORTS)
230
231             # Test that calling stop() on a killed server is harmless.
232             port.stop_http_server()
233         finally:
234             port.stop_http_server()
235             new_port.stop_http_server()
236
237             # Test that calling stop() twice is harmless.
238             new_port.stop_http_server()
239
240     def integration_test_image_diff(self):
241         port = self.make_port()
242         # FIXME: This test will never run since we are using a MockFilesystem for these tests!?!?
243         if not port.check_image_diff():
244             # The port hasn't been built - don't run the tests.
245             return
246
247         dir = port.layout_tests_dir()
248         file1 = port._filesystem.join(dir, 'fast', 'css', 'button_center.png')
249         contents1 = port._filesystem.read_binary_file(file1)
250         file2 = port._filesystem.join(dir, 'fast', 'css',
251                                       'remove-shorthand-expected.png')
252         contents2 = port._filesystem.read_binary_file(file2)
253         tmpfd, tmpfile = port._filesystem.open_binary_tempfile('')
254         tmpfd.close()
255
256         self.assertFalse(port.diff_image(contents1, contents1)[0])
257         self.assertTrue(port.diff_image(contents1, contents2)[0])
258
259         self.assertTrue(port.diff_image(contents1, contents2, tmpfile)[0])
260
261         port._filesystem.remove(tmpfile)
262
263     def test_diff_image__missing_both(self):
264         port = self.make_port()
265         self.assertFalse(port.diff_image(None, None)[0])
266         self.assertFalse(port.diff_image(None, '')[0])
267         self.assertFalse(port.diff_image('', None)[0])
268
269         self.assertFalse(port.diff_image('', '')[0])
270
271     def test_diff_image__missing_actual(self):
272         port = self.make_port()
273         self.assertTrue(port.diff_image(None, 'foo')[0])
274         self.assertTrue(port.diff_image('', 'foo')[0])
275
276     def test_diff_image__missing_expected(self):
277         port = self.make_port()
278         self.assertTrue(port.diff_image('foo', None)[0])
279         self.assertTrue(port.diff_image('foo', '')[0])
280
281     def test_diff_image(self):
282         port = self.make_port()
283         self.proc = None
284
285         def make_proc(port, nm, cmd, env):
286             self.proc = MockServerProcess(port, nm, cmd, env, lines=['diff: 100% failed\n', 'diff: 100% failed\n'])
287             return self.proc
288
289         # FIXME: Can't pretend to run setup for some ports, so just skip this test.
290         if self.disable_setup:
291             return
292
293         port._server_process_constructor = make_proc
294         port.setup_test_run()
295
296         # First test the case of not using the JHBuild wrapper.
297         self.assertFalse(port._should_use_jhbuild())
298
299         self.assertEqual(port.diff_image('foo', 'bar'), ('', 100.0, None))
300         self.assertEqual(self.proc.cmd, [port._path_to_image_diff(), "--tolerance", "0.1"])
301         self.assertEqual(port.diff_image('foo', 'bar', None), ('', 100.0, None))
302         self.assertEqual(self.proc.cmd, [port._path_to_image_diff(), "--tolerance", "0.1"])
303         self.assertEqual(port.diff_image('foo', 'bar', 0), ('', 100.0, None))
304         self.assertEqual(self.proc.cmd, [port._path_to_image_diff(), "--tolerance", "0"])
305
306         # Now test the case of using JHBuild wrapper.
307         port._filesystem.maybe_make_directory(port.path_from_webkit_base('WebKitBuild', 'Dependencies%s' % port.port_name.upper()))
308         self.assertTrue(port._should_use_jhbuild())
309
310         self.assertEqual(port.diff_image('foo', 'bar'), ('', 100.0, None))
311         self.assertEqual(self.proc.cmd, port._jhbuild_wrapper + [port._path_to_image_diff(), "--tolerance", "0.1"])
312         self.assertEqual(port.diff_image('foo', 'bar', None), ('', 100.0, None))
313         self.assertEqual(self.proc.cmd, port._jhbuild_wrapper + [port._path_to_image_diff(), "--tolerance", "0.1"])
314         self.assertEqual(port.diff_image('foo', 'bar', 0), ('', 100.0, None))
315         self.assertEqual(self.proc.cmd, port._jhbuild_wrapper + [port._path_to_image_diff(), "--tolerance", "0"])
316
317         port.clean_up_test_run()
318         self.assertTrue(self.proc.stopped)
319         self.assertEqual(port._image_differ, None)
320
321     def test_diff_image_passed(self):
322         port = self.make_port()
323         port._server_process_constructor = lambda port, nm, cmd, env: MockServerProcess(lines=['diff: 0% passed\n'])
324         image_differ = ImageDiffer(port)
325         self.assertEqual(image_differ.diff_image('foo', 'bar', 0.1), (None, 0, None))
326
327     def test_diff_image_failed(self):
328         port = self.make_port()
329         port._server_process_constructor = lambda port, nm, cmd, env: MockServerProcess(lines=['diff: 100% failed\n'])
330         image_differ = ImageDiffer(port)
331         self.assertEqual(image_differ.diff_image('foo', 'bar', 0.1), ('', 100.0, None))
332
333     def test_diff_image_crashed(self):
334         port = self.make_port()
335         self.proc = None
336
337         def make_proc(port, nm, cmd, env):
338             self.proc = MockServerProcess(port, nm, cmd, env, crashed=True)
339             return self.proc
340
341         # FIXME: Can't pretend to run setup for some ports, so just skip this test.
342         if self.disable_setup:
343             return
344
345         port._server_process_constructor = make_proc
346         port.setup_test_run()
347         self.assertEqual(port.diff_image('foo', 'bar'), ('', 0, 'ImageDiff crashed\n'))
348         port.clean_up_test_run()
349
350     def integration_test_websocket_server__normal(self):
351         port = self.make_port()
352         self.assert_servers_are_down('localhost', self.WEBSOCKET_PORTS)
353         port.start_websocket_server()
354         self.assert_servers_are_up('localhost', self.WEBSOCKET_PORTS)
355         port.stop_websocket_server()
356         self.assert_servers_are_down('localhost', self.WEBSOCKET_PORTS)
357
358     def integration_test_websocket_server__fails(self):
359         port = self.make_port()
360
361         # Test that start() fails if a port isn't available.
362         for port_number in self.WEBSOCKET_PORTS:
363             test_socket = socket.socket()
364             try:
365                 test_socket.bind(('localhost', port_number))
366                 try:
367                     port.start_websocket_server()
368                     self.fail('should not have been able to start the server while bound to %d' % port_number)
369                 except http_server_base.ServerError as e:
370                     pass
371             finally:
372                 port.stop_websocket_server()
373                 test_socket.close()
374
375         # Test that calling start() twice fails.
376         try:
377             port.start_websocket_server()
378             self.assertRaises(AssertionError, port.start_websocket_server)
379         finally:
380             port.stop_websocket_server()
381
382     def integration_test_websocket_server__two_servers(self):
383         port = self.make_port()
384
385         # Test that calling start() on two different ports causes the
386         # first port to be treated as stale and killed.
387         port.start_websocket_server()
388         new_port = self.make_port()
389         try:
390             new_port.start_websocket_server()
391
392             # Check that the first server was killed.
393             self.assertFalse(port._executive.check_running_pid(port._websocket_server._pid))
394
395             # Check that there is something running.
396             self.assert_servers_are_up('localhost', self.WEBSOCKET_PORTS)
397
398             # Test that calling stop() on a killed server is harmless.
399             port.stop_websocket_server()
400         finally:
401             port.stop_websocket_server()
402             new_port.stop_websocket_server()
403
404             # Test that calling stop() twice is harmless.
405             new_port.stop_websocket_server()
406
407     def test_test_configuration(self):
408         port = self.make_port()
409         self.assertTrue(port.test_configuration())
410
411     def test_all_test_configurations(self):
412         port = self.make_port()
413         self.assertTrue(len(port.all_test_configurations()) > 0)
414         self.assertTrue(port.test_configuration() in port.all_test_configurations(), "%s not in %s" % (port.test_configuration(), port.all_test_configurations()))
415
416     def integration_test_http_server__loop(self):
417         port = self.make_port()
418
419         i = 0
420         while i < 10:
421             self.assert_servers_are_down('localhost', self.HTTP_PORTS)
422             port.start_http_server()
423
424             # We sleep in between alternating runs to ensure that this
425             # test handles both back-to-back starts and stops and
426             # starts and stops separated by a delay.
427             if i % 2:
428                 time.sleep(0.1)
429
430             self.assert_servers_are_up('localhost', self.HTTP_PORTS)
431             port.stop_http_server()
432             if i % 2:
433                 time.sleep(0.1)
434
435             i += 1
436
437     def test_get_crash_log(self):
438         port = self.make_port()
439         self.assertEqual(port._get_crash_log(None, None, None, None, newer_than=None),
440            (None,
441             'crash log for <unknown process name> (pid <unknown>):\n'
442             'STDOUT: <empty>\n'
443             'STDERR: <empty>\n'))
444
445         self.assertEqual(port._get_crash_log('foo', 1234, 'out bar\nout baz', 'err bar\nerr baz\n', newer_than=None),
446             ('err bar\nerr baz\n',
447              'crash log for foo (pid 1234):\n'
448              'STDOUT: out bar\n'
449              'STDOUT: out baz\n'
450              'STDERR: err bar\n'
451              'STDERR: err baz\n'))
452
453         self.assertEqual(port._get_crash_log('foo', 1234, 'foo\xa6bar', 'foo\xa6bar', newer_than=None),
454             ('foo\xa6bar',
455              u'crash log for foo (pid 1234):\n'
456              u'STDOUT: foo\ufffdbar\n'
457              u'STDERR: foo\ufffdbar\n'))
458
459         self.assertEqual(port._get_crash_log('foo', 1234, 'foo\xa6bar', 'foo\xa6bar', newer_than=1.0),
460             ('foo\xa6bar',
461              u'crash log for foo (pid 1234):\n'
462              u'STDOUT: foo\ufffdbar\n'
463              u'STDERR: foo\ufffdbar\n'))
464
465     def assert_build_path(self, options, dirs, expected_path):
466         port = self.make_port(options=options)
467         for directory in dirs:
468             port.host.filesystem.maybe_make_directory(directory)
469         self.assertEqual(port._build_path(), expected_path)
470
471     def test_expectations_ordering(self):
472         port = self.make_port()
473         for path in port.expectations_files():
474             port._filesystem.write_text_file(path, '')
475         ordered_dict = port.expectations_dict()
476         self.assertEqual(port.path_to_generic_test_expectations_file(), ordered_dict.keys()[0])
477         self.assertEqual(port.path_to_test_expectations_file(), ordered_dict.keys()[port.test_expectations_file_position()])
478
479         options = MockOptions(additional_expectations=['/tmp/foo', '/tmp/bar'])
480         port = self.make_port(options=options)
481         for path in port.expectations_files():
482             port._filesystem.write_text_file(path, '')
483         port._filesystem.write_text_file('/tmp/foo', 'foo')
484         port._filesystem.write_text_file('/tmp/bar', 'bar')
485         ordered_dict = port.expectations_dict()
486         self.assertEqual(ordered_dict.keys()[-2:], options.additional_expectations)  # pylint: disable=E1101
487         self.assertEqual(ordered_dict.values()[-2:], ['foo', 'bar'])
488
489     def test_path_to_test_expectations_file(self):
490         port = TestWebKitPort()
491         port._options = MockOptions(webkit_test_runner=False)
492         self.assertEqual(port.path_to_test_expectations_file(), '/mock-checkout/LayoutTests/platform/testwebkitport/TestExpectations')
493
494         port = TestWebKitPort()
495         port._options = MockOptions(webkit_test_runner=True)
496         self.assertEqual(port.path_to_test_expectations_file(), '/mock-checkout/LayoutTests/platform/testwebkitport/TestExpectations')
497
498         port = TestWebKitPort()
499         port.host.filesystem.files['/mock-checkout/LayoutTests/platform/testwebkitport/TestExpectations'] = 'some content'
500         port._options = MockOptions(webkit_test_runner=False)
501         self.assertEqual(port.path_to_test_expectations_file(), '/mock-checkout/LayoutTests/platform/testwebkitport/TestExpectations')
502
503     def test_skipped_directories_for_features(self):
504         supported_features = ["Accelerated Compositing", "Foo Feature"]
505         expected_directories = set(["animations/3d", "transforms/3d"])
506         port = TestWebKitPort(supported_features=supported_features)
507         port._runtime_feature_list = lambda: supported_features
508         result_directories = set(port._skipped_tests_for_unsupported_features(test_list=["animations/3d/foo.html"]))
509         self.assertEqual(result_directories, expected_directories)
510
511     def test_skipped_directories_for_features_no_matching_tests_in_test_list(self):
512         supported_features = ["Accelerated Compositing", "Foo Feature"]
513         expected_directories = set([])
514         result_directories = set(TestWebKitPort(supported_features=supported_features)._skipped_tests_for_unsupported_features(test_list=['foo.html']))
515         self.assertEqual(result_directories, expected_directories)
516
517     def test_skipped_tests_for_unsupported_features_empty_test_list(self):
518         supported_features = ["Accelerated Compositing", "Foo Feature"]
519         expected_directories = set([])
520         result_directories = set(TestWebKitPort(supported_features=supported_features)._skipped_tests_for_unsupported_features(test_list=None))
521         self.assertEqual(result_directories, expected_directories)
522
523     def test_skipped_layout_tests(self):
524         self.assertEqual(TestWebKitPort().skipped_layout_tests(test_list=[]), set(['media']))
525
526     def test_expectations_files(self):
527         port = TestWebKitPort()
528
529         def platform_dirs(port):
530             return [port.host.filesystem.basename(port.host.filesystem.dirname(f)) for f in port.expectations_files()]
531
532         self.assertEqual(platform_dirs(port), ['LayoutTests', 'testwebkitport'])
533
534         port = TestWebKitPort(port_name="testwebkitport-version")
535         self.assertEqual(platform_dirs(port), ['LayoutTests', 'testwebkitport', 'testwebkitport-version'])
536
537         port = TestWebKitPort(port_name="testwebkitport-version-wk2")
538         self.assertEqual(platform_dirs(port), ['LayoutTests', 'testwebkitport', 'testwebkitport-version', 'wk2', 'testwebkitport-wk2'])
539
540         port = TestWebKitPort(port_name="testwebkitport-version",
541                               options=MockOptions(additional_platform_directory=["internal-testwebkitport"]))
542         self.assertEqual(platform_dirs(port), ['LayoutTests', 'testwebkitport', 'testwebkitport-version', 'internal-testwebkitport'])
543
544     def test_root_option(self):
545         port = TestWebKitPort()
546         port._options = MockOptions(root='/foo')
547         if sys.platform.startswith('win'):
548             self.assertEqual(port._path_to_driver(), "/foo/DumpRenderTree.exe")
549         else:
550             self.assertEqual(port._path_to_driver(), "/foo/DumpRenderTree")
551
552     def test_test_expectations(self):
553         # Check that we read the expectations file
554         host = MockSystemHost()
555         host.filesystem.write_text_file('/mock-checkout/LayoutTests/platform/testwebkitport/TestExpectations',
556             'BUG_TESTEXPECTATIONS SKIP : fast/html/article-element.html = FAIL\n')
557         port = TestWebKitPort(host=host)
558         self.assertEqual(''.join(port.expectations_dict().values()), 'BUG_TESTEXPECTATIONS SKIP : fast/html/article-element.html = FAIL\n')
559
560     def test_build_driver(self):
561         output = OutputCapture()
562         port = TestWebKitPort()
563         # Delay setting _executive to avoid logging during construction
564         port._executive = MockExecutive(should_log=True)
565         port._options = MockOptions(configuration="Release")  # This should not be necessary, but I think TestWebKitPort is actually reading from disk (and thus detects the current configuration).
566         expected_logs = "MOCK run_command: ['Tools/Scripts/build-dumprendertree', '--release'], cwd=/mock-checkout, env={'MOCK_ENVIRON_COPY': '1'}\n"
567         self.assertTrue(output.assert_outputs(self, port._build_driver, expected_logs=expected_logs))
568
569         # Make sure WebKitTestRunner is used.
570         port._options = MockOptions(webkit_test_runner=True, configuration="Release")
571         expected_logs = "MOCK run_command: ['Tools/Scripts/build-dumprendertree', '--release'], cwd=/mock-checkout, env={'MOCK_ENVIRON_COPY': '1'}\nMOCK run_command: ['Tools/Scripts/build-webkittestrunner', '--release'], cwd=/mock-checkout, env={'MOCK_ENVIRON_COPY': '1'}\n"
572         self.assertTrue(output.assert_outputs(self, port._build_driver, expected_logs=expected_logs))
573
574         # Make sure we show the build log when --verbose is passed, which we simulate by setting the logging level to DEBUG.
575         output.set_log_level(logging.DEBUG)
576         port._options = MockOptions(configuration="Release")
577         expected_logs = """MOCK run_command: ['Tools/Scripts/build-dumprendertree', '--release'], cwd=/mock-checkout, env={'MOCK_ENVIRON_COPY': '1'}
578 Output of ['Tools/Scripts/build-dumprendertree', '--release']:
579 MOCK output of child process
580 """
581         self.assertTrue(output.assert_outputs(self, port._build_driver, expected_logs=expected_logs))
582         output.set_log_level(logging.INFO)
583
584         # Make sure that failure to build returns False.
585         port._executive = MockExecutive(should_log=True, should_throw=True)
586         # Because WK2 currently has to build both webkittestrunner and DRT, if DRT fails, that's the only one it tries.
587         expected_logs = """MOCK run_command: ['Tools/Scripts/build-dumprendertree', '--release'], cwd=/mock-checkout, env={'MOCK_ENVIRON_COPY': '1'}
588 MOCK ScriptError
589
590 MOCK output of child process
591 """
592         self.assertFalse(output.assert_outputs(self, port._build_driver, expected_logs=expected_logs))
593
594     def _assert_config_file_for_platform(self, port, platform, config_file):
595         self.assertEqual(port._apache_config_file_name_for_platform(platform), config_file)
596
597     def test_linux_distro_detection(self):
598         port = TestWebKitPort()
599         self.assertFalse(port._is_redhat_based())
600         self.assertFalse(port._is_debian_based())
601
602         port._filesystem = MockFileSystem({'/etc/redhat-release': ''})
603         self.assertTrue(port._is_redhat_based())
604         self.assertFalse(port._is_debian_based())
605
606         port._filesystem = MockFileSystem({'/etc/debian_version': ''})
607         self.assertFalse(port._is_redhat_based())
608         self.assertTrue(port._is_debian_based())
609
610         port._filesystem = MockFileSystem({'/etc/arch-release': ''})
611         self.assertFalse(port._is_redhat_based())
612         self.assertTrue(port._is_arch_based())
613
614     def test_apache_config_file_name_for_platform(self):
615         port = TestWebKitPort()
616         port._apache_version = lambda: '2.2'
617         self._assert_config_file_for_platform(port, 'cygwin', 'apache2.2-httpd-win.conf')
618
619         self._assert_config_file_for_platform(port, 'linux2', 'apache2.2-httpd.conf')
620         self._assert_config_file_for_platform(port, 'linux3', 'apache2.2-httpd.conf')
621
622         port._is_redhat_based = lambda: True
623         port._apache_version = lambda: '2.2'
624         self._assert_config_file_for_platform(port, 'linux2', 'fedora-httpd-2.2.conf')
625
626         port = TestWebKitPort()
627         port._is_debian_based = lambda: True
628         port._apache_version = lambda: '2.2'
629         self._assert_config_file_for_platform(port, 'linux2', 'debian-httpd-2.2.conf')
630
631         self._assert_config_file_for_platform(port, 'mac', 'apache2.2-httpd.conf')
632         self._assert_config_file_for_platform(port, 'win32', 'win-httpd-2.2-php7.conf')  # WinCairo uses win32. Only AppleWin port uses cygwin.
633         self._assert_config_file_for_platform(port, 'barf', 'apache2.2-httpd.conf')
634
635     def test_path_to_apache_config_file(self):
636         port = TestWebKitPort()
637
638         saved_environ = os.environ.copy()
639         try:
640             os.environ['WEBKIT_HTTP_SERVER_CONF_PATH'] = '/path/to/httpd.conf'
641             self.assertRaises(IOError, port._path_to_apache_config_file)
642             port._filesystem.write_text_file('/existing/httpd.conf', 'Hello, world!')
643             os.environ['WEBKIT_HTTP_SERVER_CONF_PATH'] = '/existing/httpd.conf'
644             self.assertEqual(port._path_to_apache_config_file(), '/existing/httpd.conf')
645         finally:
646             os.environ = saved_environ.copy()
647
648         # Mock out _apache_config_file_name_for_platform to ignore the passed sys.platform value.
649         port._apache_config_file_name_for_platform = lambda platform: 'httpd.conf'
650         self.assertEqual(port._path_to_apache_config_file(), '/mock-checkout/LayoutTests/http/conf/httpd.conf')
651
652         # Check that even if we mock out _apache_config_file_name, the environment variable takes precedence.
653         saved_environ = os.environ.copy()
654         try:
655             os.environ['WEBKIT_HTTP_SERVER_CONF_PATH'] = '/existing/httpd.conf'
656             self.assertEqual(port._path_to_apache_config_file(), '/existing/httpd.conf')
657         finally:
658             os.environ = saved_environ.copy()
659
660     def test_check_build(self):
661         port = self.make_port(options=MockOptions(build=True))
662         self.build_called = False
663
664         def build_driver_called():
665             self.build_called = True
666             return True
667
668         port._build_driver = build_driver_called
669         port.check_build()
670         self.assertTrue(self.build_called)
671
672         port = self.make_port(options=MockOptions(root='/tmp', build=True))
673         self.build_called = False
674         port._build_driver = build_driver_called
675         port.check_build()
676         self.assertFalse(self.build_called, None)
677
678         port = self.make_port(options=MockOptions(build=False))
679         self.build_called = False
680         port._build_driver = build_driver_called
681         port.check_build()
682         self.assertFalse(self.build_called, None)
683
684     def test_additional_platform_directory(self):
685         port = self.make_port(options=MockOptions(additional_platform_directory=['/tmp/foo']))
686         self.assertEqual(port.baseline_search_path()[0], '/tmp/foo')