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