Home | History | Annotate | Download | only in port
      1 # Copyright (C) 2010 Google Inc. All rights reserved.
      2 #
      3 # Redistribution and use in source and binary forms, with or without
      4 # modification, are permitted provided that the following conditions are
      5 # met:
      6 #
      7 #    * Redistributions of source code must retain the above copyright
      8 # notice, this list of conditions and the following disclaimer.
      9 #    * Redistributions in binary form must reproduce the above
     10 # copyright notice, this list of conditions and the following disclaimer
     11 # in the documentation and/or other materials provided with the
     12 # distribution.
     13 #    * Neither the name of Google Inc. nor the names of its
     14 # contributors may be used to endorse or promote products derived from
     15 # this software without specific prior written permission.
     16 #
     17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 import logging
     30 import optparse
     31 import sys
     32 import tempfile
     33 import webkitpy.thirdparty.unittest2 as unittest
     34 
     35 from webkitpy.common.system.executive import Executive, ScriptError
     36 from webkitpy.common.system import executive_mock
     37 from webkitpy.common.system.filesystem_mock import MockFileSystem
     38 from webkitpy.common.system.outputcapture import OutputCapture
     39 from webkitpy.common.system.path import abspath_to_uri
     40 from webkitpy.thirdparty.mock import Mock
     41 from webkitpy.tool.mocktool import MockOptions
     42 from webkitpy.common.system.executive_mock import MockExecutive, MockExecutive2
     43 from webkitpy.common.system.systemhost_mock import MockSystemHost
     44 
     45 from webkitpy.layout_tests.port import Port, Driver, DriverOutput
     46 from webkitpy.layout_tests.port.test import add_unit_tests_to_mock_filesystem, TestPort
     47 
     48 class PortTest(unittest.TestCase):
     49     def make_port(self, executive=None, with_tests=False, port_name=None, **kwargs):
     50         host = MockSystemHost()
     51         if executive:
     52             host.executive = executive
     53         if with_tests:
     54             add_unit_tests_to_mock_filesystem(host.filesystem)
     55             return TestPort(host, **kwargs)
     56         return Port(host, port_name or 'baseport', **kwargs)
     57 
     58     def test_default_child_processes(self):
     59         port = self.make_port()
     60         self.assertIsNotNone(port.default_child_processes())
     61 
     62     def test_format_wdiff_output_as_html(self):
     63         output = "OUTPUT %s %s %s" % (Port._WDIFF_DEL, Port._WDIFF_ADD, Port._WDIFF_END)
     64         html = self.make_port()._format_wdiff_output_as_html(output)
     65         expected_html = "<head><style>.del { background: #faa; } .add { background: #afa; }</style></head><pre>OUTPUT <span class=del> <span class=add> </span></pre>"
     66         self.assertEqual(html, expected_html)
     67 
     68     def test_wdiff_command(self):
     69         port = self.make_port()
     70         port._path_to_wdiff = lambda: "/path/to/wdiff"
     71         command = port._wdiff_command("/actual/path", "/expected/path")
     72         expected_command = [
     73             "/path/to/wdiff",
     74             "--start-delete=##WDIFF_DEL##",
     75             "--end-delete=##WDIFF_END##",
     76             "--start-insert=##WDIFF_ADD##",
     77             "--end-insert=##WDIFF_END##",
     78             "/actual/path",
     79             "/expected/path",
     80         ]
     81         self.assertEqual(command, expected_command)
     82 
     83     def _file_with_contents(self, contents, encoding="utf-8"):
     84         new_file = tempfile.NamedTemporaryFile()
     85         new_file.write(contents.encode(encoding))
     86         new_file.flush()
     87         return new_file
     88 
     89     def test_pretty_patch_os_error(self):
     90         port = self.make_port(executive=executive_mock.MockExecutive2(exception=OSError))
     91         oc = OutputCapture()
     92         oc.capture_output()
     93         self.assertEqual(port.pretty_patch_text("patch.txt"),
     94                          port._pretty_patch_error_html)
     95 
     96         # This tests repeated calls to make sure we cache the result.
     97         self.assertEqual(port.pretty_patch_text("patch.txt"),
     98                          port._pretty_patch_error_html)
     99         oc.restore_output()
    100 
    101     def test_pretty_patch_script_error(self):
    102         # FIXME: This is some ugly white-box test hacking ...
    103         port = self.make_port(executive=executive_mock.MockExecutive2(exception=ScriptError))
    104         port._pretty_patch_available = True
    105         self.assertEqual(port.pretty_patch_text("patch.txt"),
    106                          port._pretty_patch_error_html)
    107 
    108         # This tests repeated calls to make sure we cache the result.
    109         self.assertEqual(port.pretty_patch_text("patch.txt"),
    110                          port._pretty_patch_error_html)
    111 
    112     def test_wdiff_text(self):
    113         port = self.make_port()
    114         port.wdiff_available = lambda: True
    115         port._run_wdiff = lambda a, b: 'PASS'
    116         self.assertEqual('PASS', port.wdiff_text(None, None))
    117 
    118     def test_diff_text(self):
    119         port = self.make_port()
    120         # Make sure that we don't run into decoding exceptions when the
    121         # filenames are unicode, with regular or malformed input (expected or
    122         # actual input is always raw bytes, not unicode).
    123         port.diff_text('exp', 'act', 'exp.txt', 'act.txt')
    124         port.diff_text('exp', 'act', u'exp.txt', 'act.txt')
    125         port.diff_text('exp', 'act', u'a\xac\u1234\u20ac\U00008000', 'act.txt')
    126 
    127         port.diff_text('exp' + chr(255), 'act', 'exp.txt', 'act.txt')
    128         port.diff_text('exp' + chr(255), 'act', u'exp.txt', 'act.txt')
    129 
    130         # Though expected and actual files should always be read in with no
    131         # encoding (and be stored as str objects), test unicode inputs just to
    132         # be safe.
    133         port.diff_text(u'exp', 'act', 'exp.txt', 'act.txt')
    134         port.diff_text(
    135             u'a\xac\u1234\u20ac\U00008000', 'act', 'exp.txt', 'act.txt')
    136 
    137         # And make sure we actually get diff output.
    138         diff = port.diff_text('foo', 'bar', 'exp.txt', 'act.txt')
    139         self.assertIn('foo', diff)
    140         self.assertIn('bar', diff)
    141         self.assertIn('exp.txt', diff)
    142         self.assertIn('act.txt', diff)
    143         self.assertNotIn('nosuchthing', diff)
    144 
    145     def test_setup_test_run(self):
    146         port = self.make_port()
    147         # This routine is a no-op. We just test it for coverage.
    148         port.setup_test_run()
    149 
    150     def test_test_dirs(self):
    151         port = self.make_port()
    152         port.host.filesystem.write_text_file(port.layout_tests_dir() + '/canvas/test', '')
    153         port.host.filesystem.write_text_file(port.layout_tests_dir() + '/css2.1/test', '')
    154         dirs = port.test_dirs()
    155         self.assertIn('canvas', dirs)
    156         self.assertIn('css2.1', dirs)
    157 
    158     def test_skipped_perf_tests(self):
    159         port = self.make_port()
    160 
    161         def add_text_file(dirname, filename, content='some content'):
    162             dirname = port.host.filesystem.join(port.perf_tests_dir(), dirname)
    163             port.host.filesystem.maybe_make_directory(dirname)
    164             port.host.filesystem.write_text_file(port.host.filesystem.join(dirname, filename), content)
    165 
    166         add_text_file('inspector', 'test1.html')
    167         add_text_file('inspector', 'unsupported_test1.html')
    168         add_text_file('inspector', 'test2.html')
    169         add_text_file('inspector/resources', 'resource_file.html')
    170         add_text_file('unsupported', 'unsupported_test2.html')
    171         add_text_file('', 'Skipped', '\n'.join(['Layout', '', 'SunSpider', 'Supported/some-test.html']))
    172         self.assertEqual(port.skipped_perf_tests(), ['Layout', 'SunSpider', 'Supported/some-test.html'])
    173 
    174     def test_get_option__set(self):
    175         options, args = optparse.OptionParser().parse_args([])
    176         options.foo = 'bar'
    177         port = self.make_port(options=options)
    178         self.assertEqual(port.get_option('foo'), 'bar')
    179 
    180     def test_get_option__unset(self):
    181         port = self.make_port()
    182         self.assertIsNone(port.get_option('foo'))
    183 
    184     def test_get_option__default(self):
    185         port = self.make_port()
    186         self.assertEqual(port.get_option('foo', 'bar'), 'bar')
    187 
    188     def test_additional_platform_directory(self):
    189         port = self.make_port(port_name='foo')
    190         port.default_baseline_search_path = lambda: ['LayoutTests/platform/foo']
    191         layout_test_dir = port.layout_tests_dir()
    192         test_file = 'fast/test.html'
    193 
    194         # No additional platform directory
    195         self.assertEqual(
    196             port.expected_baselines(test_file, '.txt'),
    197             [(None, 'fast/test-expected.txt')])
    198         self.assertEqual(port.baseline_path(), 'LayoutTests/platform/foo')
    199 
    200         # Simple additional platform directory
    201         port._options.additional_platform_directory = ['/tmp/local-baselines']
    202         port._filesystem.write_text_file('/tmp/local-baselines/fast/test-expected.txt', 'foo')
    203         self.assertEqual(
    204             port.expected_baselines(test_file, '.txt'),
    205             [('/tmp/local-baselines', 'fast/test-expected.txt')])
    206         self.assertEqual(port.baseline_path(), '/tmp/local-baselines')
    207 
    208         # Multiple additional platform directories
    209         port._options.additional_platform_directory = ['/foo', '/tmp/local-baselines']
    210         self.assertEqual(
    211             port.expected_baselines(test_file, '.txt'),
    212             [('/tmp/local-baselines', 'fast/test-expected.txt')])
    213         self.assertEqual(port.baseline_path(), '/foo')
    214 
    215     def test_nonexistant_expectations(self):
    216         port = self.make_port(port_name='foo')
    217         port.expectations_files = lambda: ['/mock-checkout/LayoutTests/platform/exists/TestExpectations', '/mock-checkout/LayoutTests/platform/nonexistant/TestExpectations']
    218         port._filesystem.write_text_file('/mock-checkout/LayoutTests/platform/exists/TestExpectations', '')
    219         self.assertEqual('\n'.join(port.expectations_dict().keys()), '/mock-checkout/LayoutTests/platform/exists/TestExpectations')
    220 
    221     def test_additional_expectations(self):
    222         port = self.make_port(port_name='foo')
    223         port.port_name = 'foo'
    224         port._filesystem.write_text_file('/mock-checkout/LayoutTests/platform/foo/TestExpectations', '')
    225         port._filesystem.write_text_file(
    226             '/tmp/additional-expectations-1.txt', 'content1\n')
    227         port._filesystem.write_text_file(
    228             '/tmp/additional-expectations-2.txt', 'content2\n')
    229 
    230         self.assertEqual('\n'.join(port.expectations_dict().values()), '')
    231 
    232         port._options.additional_expectations = [
    233             '/tmp/additional-expectations-1.txt']
    234         self.assertEqual('\n'.join(port.expectations_dict().values()), '\ncontent1\n')
    235 
    236         port._options.additional_expectations = [
    237             '/tmp/nonexistent-file', '/tmp/additional-expectations-1.txt']
    238         self.assertEqual('\n'.join(port.expectations_dict().values()), '\ncontent1\n')
    239 
    240         port._options.additional_expectations = [
    241             '/tmp/additional-expectations-1.txt', '/tmp/additional-expectations-2.txt']
    242         self.assertEqual('\n'.join(port.expectations_dict().values()), '\ncontent1\n\ncontent2\n')
    243 
    244     def test_additional_env_var(self):
    245         port = self.make_port(options=optparse.Values({'additional_env_var': ['FOO=BAR', 'BAR=FOO']}))
    246         self.assertEqual(port.get_option('additional_env_var'), ['FOO=BAR', 'BAR=FOO'])
    247         environment = port.setup_environ_for_server()
    248         self.assertTrue(('FOO' in environment) & ('BAR' in environment))
    249         self.assertEqual(environment['FOO'], 'BAR')
    250         self.assertEqual(environment['BAR'], 'FOO')
    251 
    252     def test_find_no_paths_specified(self):
    253         port = self.make_port(with_tests=True)
    254         layout_tests_dir = port.layout_tests_dir()
    255         tests = port.tests([])
    256         self.assertNotEqual(len(tests), 0)
    257 
    258     def test_find_one_test(self):
    259         port = self.make_port(with_tests=True)
    260         tests = port.tests(['failures/expected/image.html'])
    261         self.assertEqual(len(tests), 1)
    262 
    263     def test_find_glob(self):
    264         port = self.make_port(with_tests=True)
    265         tests = port.tests(['failures/expected/im*'])
    266         self.assertEqual(len(tests), 2)
    267 
    268     def test_find_with_skipped_directories(self):
    269         port = self.make_port(with_tests=True)
    270         tests = port.tests(['userscripts'])
    271         self.assertNotIn('userscripts/resources/iframe.html', tests)
    272 
    273     def test_find_with_skipped_directories_2(self):
    274         port = self.make_port(with_tests=True)
    275         tests = port.tests(['userscripts/resources'])
    276         self.assertEqual(tests, [])
    277 
    278     def test_is_test_file(self):
    279         filesystem = MockFileSystem()
    280         self.assertTrue(Port.is_test_file(filesystem, '', 'foo.html'))
    281         self.assertTrue(Port.is_test_file(filesystem, '', 'foo.svg'))
    282         self.assertTrue(Port.is_test_file(filesystem, '', 'test-ref-test.html'))
    283         self.assertFalse(Port.is_test_file(filesystem, '', 'foo.png'))
    284         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected.html'))
    285         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected.svg'))
    286         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected.xht'))
    287         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected-mismatch.html'))
    288         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected-mismatch.svg'))
    289         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected-mismatch.xhtml'))
    290         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-ref.html'))
    291         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-notref.html'))
    292         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-notref.xht'))
    293         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-ref.xhtml'))
    294         self.assertFalse(Port.is_test_file(filesystem, '', 'ref-foo.html'))
    295         self.assertFalse(Port.is_test_file(filesystem, '', 'notref-foo.xhr'))
    296 
    297     def test_parse_reftest_list(self):
    298         port = self.make_port(with_tests=True)
    299         port.host.filesystem.files['bar/reftest.list'] = "\n".join(["== test.html test-ref.html",
    300         "",
    301         "# some comment",
    302         "!= test-2.html test-notref.html # more comments",
    303         "== test-3.html test-ref.html",
    304         "== test-3.html test-ref2.html",
    305         "!= test-3.html test-notref.html"])
    306 
    307         reftest_list = Port._parse_reftest_list(port.host.filesystem, 'bar')
    308         self.assertEqual(reftest_list, {'bar/test.html': [('==', 'bar/test-ref.html')],
    309             'bar/test-2.html': [('!=', 'bar/test-notref.html')],
    310             'bar/test-3.html': [('==', 'bar/test-ref.html'), ('==', 'bar/test-ref2.html'), ('!=', 'bar/test-notref.html')]})
    311 
    312     def test_reference_files(self):
    313         port = self.make_port(with_tests=True)
    314         self.assertEqual(port.reference_files('passes/svgreftest.svg'), [('==', port.layout_tests_dir() + '/passes/svgreftest-expected.svg')])
    315         self.assertEqual(port.reference_files('passes/xhtreftest.svg'), [('==', port.layout_tests_dir() + '/passes/xhtreftest-expected.html')])
    316         self.assertEqual(port.reference_files('passes/phpreftest.php'), [('!=', port.layout_tests_dir() + '/passes/phpreftest-expected-mismatch.svg')])
    317 
    318     def test_operating_system(self):
    319         self.assertEqual('mac', self.make_port().operating_system())
    320 
    321     def test_http_server_supports_ipv6(self):
    322         port = self.make_port()
    323         self.assertTrue(port.http_server_supports_ipv6())
    324         port.host.platform.os_name = 'cygwin'
    325         self.assertFalse(port.http_server_supports_ipv6())
    326         port.host.platform.os_name = 'win'
    327         self.assertTrue(port.http_server_supports_ipv6())
    328 
    329     def test_check_httpd_success(self):
    330         port = self.make_port(executive=MockExecutive2())
    331         port._path_to_apache = lambda: '/usr/sbin/httpd'
    332         capture = OutputCapture()
    333         capture.capture_output()
    334         self.assertTrue(port.check_httpd())
    335         _, _, logs = capture.restore_output()
    336         self.assertEqual('', logs)
    337 
    338     def test_httpd_returns_error_code(self):
    339         port = self.make_port(executive=MockExecutive2(exit_code=1))
    340         port._path_to_apache = lambda: '/usr/sbin/httpd'
    341         capture = OutputCapture()
    342         capture.capture_output()
    343         self.assertFalse(port.check_httpd())
    344         _, _, logs = capture.restore_output()
    345         self.assertEqual('httpd seems broken. Cannot run http tests.\n', logs)
    346 
    347     def test_test_exists(self):
    348         port = self.make_port(with_tests=True)
    349         self.assertTrue(port.test_exists('passes'))
    350         self.assertTrue(port.test_exists('passes/text.html'))
    351         self.assertFalse(port.test_exists('passes/does_not_exist.html'))
    352 
    353         self.assertTrue(port.test_exists('virtual'))
    354         self.assertFalse(port.test_exists('virtual/does_not_exist.html'))
    355         self.assertTrue(port.test_exists('virtual/passes/text.html'))
    356 
    357     def test_test_isfile(self):
    358         port = self.make_port(with_tests=True)
    359         self.assertFalse(port.test_isfile('passes'))
    360         self.assertTrue(port.test_isfile('passes/text.html'))
    361         self.assertFalse(port.test_isfile('passes/does_not_exist.html'))
    362 
    363         self.assertFalse(port.test_isfile('virtual'))
    364         self.assertTrue(port.test_isfile('virtual/passes/text.html'))
    365         self.assertFalse(port.test_isfile('virtual/does_not_exist.html'))
    366 
    367     def test_test_isdir(self):
    368         port = self.make_port(with_tests=True)
    369         self.assertTrue(port.test_isdir('passes'))
    370         self.assertFalse(port.test_isdir('passes/text.html'))
    371         self.assertFalse(port.test_isdir('passes/does_not_exist.html'))
    372         self.assertFalse(port.test_isdir('passes/does_not_exist/'))
    373 
    374         self.assertTrue(port.test_isdir('virtual'))
    375         self.assertFalse(port.test_isdir('virtual/does_not_exist.html'))
    376         self.assertFalse(port.test_isdir('virtual/does_not_exist/'))
    377         self.assertFalse(port.test_isdir('virtual/passes/text.html'))
    378 
    379     def test_tests(self):
    380         port = self.make_port(with_tests=True)
    381         tests = port.tests([])
    382         self.assertIn('passes/text.html', tests)
    383         self.assertIn('virtual/passes/text.html', tests)
    384 
    385         tests = port.tests(['passes'])
    386         self.assertIn('passes/text.html', tests)
    387         self.assertIn('passes/passes/test-virtual-passes.html', tests)
    388         self.assertNotIn('virtual/passes/text.html', tests)
    389 
    390         tests = port.tests(['virtual/passes'])
    391         self.assertNotIn('passes/text.html', tests)
    392         self.assertIn('virtual/passes/test-virtual-passes.html', tests)
    393         self.assertIn('virtual/passes/passes/test-virtual-passes.html', tests)
    394         self.assertNotIn('virtual/passes/test-virtual-virtual/passes.html', tests)
    395         self.assertNotIn('virtual/passes/virtual/passes/test-virtual-passes.html', tests)
    396 
    397     def test_build_path(self):
    398         port = self.make_port(options=optparse.Values({'build_directory': '/my-build-directory/'}))
    399         self.assertEqual(port._build_path(), '/my-build-directory/Release')
    400 
    401     def test_dont_require_http_server(self):
    402         port = self.make_port()
    403         self.assertEqual(port.requires_http_server(), False)
    404 
    405 
    406 class NaturalCompareTest(unittest.TestCase):
    407     def setUp(self):
    408         self._port = TestPort(MockSystemHost())
    409 
    410     def assert_cmp(self, x, y, result):
    411         self.assertEqual(cmp(self._port._natural_sort_key(x), self._port._natural_sort_key(y)), result)
    412 
    413     def test_natural_compare(self):
    414         self.assert_cmp('a', 'a', 0)
    415         self.assert_cmp('ab', 'a', 1)
    416         self.assert_cmp('a', 'ab', -1)
    417         self.assert_cmp('', '', 0)
    418         self.assert_cmp('', 'ab', -1)
    419         self.assert_cmp('1', '2', -1)
    420         self.assert_cmp('2', '1', 1)
    421         self.assert_cmp('1', '10', -1)
    422         self.assert_cmp('2', '10', -1)
    423         self.assert_cmp('foo_1.html', 'foo_2.html', -1)
    424         self.assert_cmp('foo_1.1.html', 'foo_2.html', -1)
    425         self.assert_cmp('foo_1.html', 'foo_10.html', -1)
    426         self.assert_cmp('foo_2.html', 'foo_10.html', -1)
    427         self.assert_cmp('foo_23.html', 'foo_10.html', 1)
    428         self.assert_cmp('foo_23.html', 'foo_100.html', -1)
    429 
    430 
    431 class KeyCompareTest(unittest.TestCase):
    432     def setUp(self):
    433         self._port = TestPort(MockSystemHost())
    434 
    435     def assert_cmp(self, x, y, result):
    436         self.assertEqual(cmp(self._port.test_key(x), self._port.test_key(y)), result)
    437 
    438     def test_test_key(self):
    439         self.assert_cmp('/a', '/a', 0)
    440         self.assert_cmp('/a', '/b', -1)
    441         self.assert_cmp('/a2', '/a10', -1)
    442         self.assert_cmp('/a2/foo', '/a10/foo', -1)
    443         self.assert_cmp('/a/foo11', '/a/foo2', 1)
    444         self.assert_cmp('/ab', '/a/a/b', -1)
    445         self.assert_cmp('/a/a/b', '/ab', 1)
    446         self.assert_cmp('/foo-bar/baz', '/foo/baz', -1)
    447