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 Google name 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 base64
     30 import copy
     31 import sys
     32 import time
     33 
     34 from webkitpy.layout_tests.port import DeviceFailure, Driver, DriverOutput, Port
     35 from webkitpy.layout_tests.port.base import VirtualTestSuite
     36 from webkitpy.layout_tests.models.test_configuration import TestConfiguration
     37 from webkitpy.layout_tests.models import test_run_results
     38 from webkitpy.common.system.filesystem_mock import MockFileSystem
     39 from webkitpy.common.system.crashlogs import CrashLogs
     40 
     41 
     42 # This sets basic expectations for a test. Each individual expectation
     43 # can be overridden by a keyword argument in TestList.add().
     44 class TestInstance(object):
     45     def __init__(self, name):
     46         self.name = name
     47         self.base = name[(name.rfind("/") + 1):name.rfind(".")]
     48         self.crash = False
     49         self.web_process_crash = False
     50         self.exception = False
     51         self.keyboard = False
     52         self.error = ''
     53         self.timeout = False
     54         self.is_reftest = False
     55         self.device_failure = False
     56         self.leak = False
     57 
     58         # The values of each field are treated as raw byte strings. They
     59         # will be converted to unicode strings where appropriate using
     60         # FileSystem.read_text_file().
     61         self.actual_text = self.base + '-txt'
     62         self.actual_checksum = self.base + '-checksum'
     63 
     64         # We add the '\x8a' for the image file to prevent the value from
     65         # being treated as UTF-8 (the character is invalid)
     66         self.actual_image = self.base + '\x8a' + '-png' + 'tEXtchecksum\x00' + self.actual_checksum
     67 
     68         self.expected_text = self.actual_text
     69         self.expected_image = self.actual_image
     70 
     71         self.actual_audio = None
     72         self.expected_audio = None
     73 
     74 
     75 # This is an in-memory list of tests, what we want them to produce, and
     76 # what we want to claim are the expected results.
     77 class TestList(object):
     78     def __init__(self):
     79         self.tests = {}
     80 
     81     def add(self, name, **kwargs):
     82         test = TestInstance(name)
     83         for key, value in kwargs.items():
     84             test.__dict__[key] = value
     85         self.tests[name] = test
     86 
     87     def add_reftest(self, name, reference_name, same_image, crash=False):
     88         self.add(name, actual_checksum='xxx', actual_image='XXX', is_reftest=True, crash=crash)
     89         if same_image:
     90             self.add(reference_name, actual_checksum='xxx', actual_image='XXX', is_reftest=True)
     91         else:
     92             self.add(reference_name, actual_checksum='yyy', actual_image='YYY', is_reftest=True)
     93 
     94     def keys(self):
     95         return self.tests.keys()
     96 
     97     def __contains__(self, item):
     98         return item in self.tests
     99 
    100     def __getitem__(self, item):
    101         return self.tests[item]
    102 
    103 #
    104 # These numbers may need to be updated whenever we add or delete tests. This includes virtual tests.
    105 #
    106 TOTAL_TESTS = 113
    107 TOTAL_SKIPS = 29
    108 
    109 UNEXPECTED_PASSES = 1
    110 UNEXPECTED_FAILURES = 26
    111 
    112 def unit_test_list():
    113     tests = TestList()
    114     tests.add('failures/expected/crash.html', crash=True)
    115     tests.add('failures/expected/exception.html', exception=True)
    116     tests.add('failures/expected/device_failure.html', device_failure=True)
    117     tests.add('failures/expected/timeout.html', timeout=True)
    118     tests.add('failures/expected/leak.html', leak=True)
    119     tests.add('failures/expected/missing_text.html', expected_text=None)
    120     tests.add('failures/expected/needsrebaseline.html', actual_text='needsrebaseline text')
    121     tests.add('failures/expected/needsmanualrebaseline.html', actual_text='needsmanualrebaseline text')
    122     tests.add('failures/expected/image.html',
    123               actual_image='image_fail-pngtEXtchecksum\x00checksum_fail',
    124               expected_image='image-pngtEXtchecksum\x00checksum-png')
    125     tests.add('failures/expected/image_checksum.html',
    126               actual_checksum='image_checksum_fail-checksum',
    127               actual_image='image_checksum_fail-png')
    128     tests.add('failures/expected/audio.html',
    129               actual_audio=base64.b64encode('audio_fail-wav'), expected_audio='audio-wav',
    130               actual_text=None, expected_text=None,
    131               actual_image=None, expected_image=None,
    132               actual_checksum=None)
    133     tests.add('failures/expected/keyboard.html', keyboard=True)
    134     tests.add('failures/expected/missing_check.html',
    135               expected_image='missing_check-png')
    136     tests.add('failures/expected/missing_image.html', expected_image=None)
    137     tests.add('failures/expected/missing_audio.html', expected_audio=None,
    138               actual_text=None, expected_text=None,
    139               actual_image=None, expected_image=None,
    140               actual_checksum=None)
    141     tests.add('failures/expected/missing_text.html', expected_text=None)
    142     tests.add('failures/expected/newlines_leading.html',
    143               expected_text="\nfoo\n", actual_text="foo\n")
    144     tests.add('failures/expected/newlines_trailing.html',
    145               expected_text="foo\n\n", actual_text="foo\n")
    146     tests.add('failures/expected/newlines_with_excess_CR.html',
    147               expected_text="foo\r\r\r\n", actual_text="foo\n")
    148     tests.add('failures/expected/text.html', actual_text='text_fail-png')
    149     tests.add('failures/expected/crash_then_text.html')
    150     tests.add('failures/expected/skip_text.html', actual_text='text diff')
    151     tests.add('failures/flaky/text.html')
    152     tests.add('failures/unexpected/missing_text.html', expected_text=None)
    153     tests.add('failures/unexpected/missing_check.html', expected_image='missing-check-png')
    154     tests.add('failures/unexpected/missing_image.html', expected_image=None)
    155     tests.add('failures/unexpected/missing_render_tree_dump.html', actual_text="""layer at (0,0) size 800x600
    156   RenderView at (0,0) size 800x600
    157 layer at (0,0) size 800x34
    158   RenderBlock {HTML} at (0,0) size 800x34
    159     RenderBody {BODY} at (8,8) size 784x18
    160       RenderText {#text} at (0,0) size 133x18
    161         text run at (0,0) width 133: "This is an image test!"
    162 """, expected_text=None)
    163     tests.add('failures/unexpected/crash.html', crash=True)
    164     tests.add('failures/unexpected/crash-with-stderr.html', crash=True,
    165               error="mock-std-error-output")
    166     tests.add('failures/unexpected/web-process-crash-with-stderr.html', web_process_crash=True,
    167               error="mock-std-error-output")
    168     tests.add('failures/unexpected/pass.html')
    169     tests.add('failures/unexpected/text-checksum.html',
    170               actual_text='text-checksum_fail-txt',
    171               actual_checksum='text-checksum_fail-checksum')
    172     tests.add('failures/unexpected/text-image-checksum.html',
    173               actual_text='text-image-checksum_fail-txt',
    174               actual_image='text-image-checksum_fail-pngtEXtchecksum\x00checksum_fail',
    175               actual_checksum='text-image-checksum_fail-checksum')
    176     tests.add('failures/unexpected/checksum-with-matching-image.html',
    177               actual_checksum='text-image-checksum_fail-checksum')
    178     tests.add('failures/unexpected/skip_pass.html')
    179     tests.add('failures/unexpected/text.html', actual_text='text_fail-txt')
    180     tests.add('failures/unexpected/text_then_crash.html')
    181     tests.add('failures/unexpected/timeout.html', timeout=True)
    182     tests.add('failures/unexpected/leak.html', leak=True)
    183     tests.add('http/tests/passes/text.html')
    184     tests.add('http/tests/passes/image.html')
    185     tests.add('http/tests/ssl/text.html')
    186     tests.add('passes/args.html')
    187     tests.add('passes/error.html', error='stuff going to stderr')
    188     tests.add('passes/image.html')
    189     tests.add('passes/audio.html',
    190               actual_audio=base64.b64encode('audio-wav'), expected_audio='audio-wav',
    191               actual_text=None, expected_text=None,
    192               actual_image=None, expected_image=None,
    193               actual_checksum=None)
    194     tests.add('passes/platform_image.html')
    195     tests.add('passes/checksum_in_image.html',
    196               expected_image='tEXtchecksum\x00checksum_in_image-checksum')
    197     tests.add('passes/skipped/skip.html')
    198 
    199     # Note that here the checksums don't match but the images do, so this test passes "unexpectedly".
    200     # See https://bugs.webkit.org/show_bug.cgi?id=69444 .
    201     tests.add('failures/unexpected/checksum.html', actual_checksum='checksum_fail-checksum')
    202 
    203     # Text output files contain "\r\n" on Windows.  This may be
    204     # helpfully filtered to "\r\r\n" by our Python/Cygwin tooling.
    205     tests.add('passes/text.html',
    206               expected_text='\nfoo\n\n', actual_text='\nfoo\r\n\r\r\n')
    207 
    208     # For reftests.
    209     tests.add_reftest('passes/reftest.html', 'passes/reftest-expected.html', same_image=True)
    210 
    211     # This adds a different virtual reference to ensure that that also works.
    212     tests.add('virtual/virtual_passes/passes/reftest-expected.html', actual_checksum='xxx', actual_image='XXX', is_reftest=True)
    213 
    214     tests.add_reftest('passes/mismatch.html', 'passes/mismatch-expected-mismatch.html', same_image=False)
    215     tests.add_reftest('passes/svgreftest.svg', 'passes/svgreftest-expected.svg', same_image=True)
    216     tests.add_reftest('passes/xhtreftest.xht', 'passes/xhtreftest-expected.html', same_image=True)
    217     tests.add_reftest('passes/phpreftest.php', 'passes/phpreftest-expected-mismatch.svg', same_image=False)
    218     tests.add_reftest('failures/expected/reftest.html', 'failures/expected/reftest-expected.html', same_image=False)
    219     tests.add_reftest('failures/expected/mismatch.html', 'failures/expected/mismatch-expected-mismatch.html', same_image=True)
    220     tests.add_reftest('failures/unexpected/crash-reftest.html', 'failures/unexpected/crash-reftest-expected.html', same_image=True, crash=True)
    221     tests.add_reftest('failures/unexpected/reftest.html', 'failures/unexpected/reftest-expected.html', same_image=False)
    222     tests.add_reftest('failures/unexpected/mismatch.html', 'failures/unexpected/mismatch-expected-mismatch.html', same_image=True)
    223     tests.add('failures/unexpected/reftest-nopixel.html', actual_checksum=None, actual_image=None, is_reftest=True)
    224     tests.add('failures/unexpected/reftest-nopixel-expected.html', actual_checksum=None, actual_image=None, is_reftest=True)
    225     tests.add('reftests/foo/test.html')
    226     tests.add('reftests/foo/test-ref.html')
    227 
    228     tests.add('reftests/foo/multiple-match-success.html', actual_checksum='abc', actual_image='abc')
    229     tests.add('reftests/foo/multiple-match-failure.html', actual_checksum='abc', actual_image='abc')
    230     tests.add('reftests/foo/multiple-mismatch-success.html', actual_checksum='abc', actual_image='abc')
    231     tests.add('reftests/foo/multiple-mismatch-failure.html', actual_checksum='abc', actual_image='abc')
    232     tests.add('reftests/foo/multiple-both-success.html', actual_checksum='abc', actual_image='abc')
    233     tests.add('reftests/foo/multiple-both-failure.html', actual_checksum='abc', actual_image='abc')
    234 
    235     tests.add('reftests/foo/matching-ref.html', actual_checksum='abc', actual_image='abc')
    236     tests.add('reftests/foo/mismatching-ref.html', actual_checksum='def', actual_image='def')
    237     tests.add('reftests/foo/second-mismatching-ref.html', actual_checksum='ghi', actual_image='ghi')
    238 
    239     # The following files shouldn't be treated as reftests
    240     tests.add_reftest('reftests/foo/unlistedtest.html', 'reftests/foo/unlistedtest-expected.html', same_image=True)
    241     tests.add('reftests/foo/reference/bar/common.html')
    242     tests.add('reftests/foo/reftest/bar/shared.html')
    243 
    244     tests.add('websocket/tests/passes/text.html')
    245 
    246     # For testing that we don't run tests under platform/. Note that these don't contribute to TOTAL_TESTS.
    247     tests.add('platform/test-mac-leopard/http/test.html')
    248     tests.add('platform/test-win-win7/http/test.html')
    249 
    250     # For testing if perf tests are running in a locked shard.
    251     tests.add('perf/foo/test.html')
    252     tests.add('perf/foo/test-ref.html')
    253 
    254     # For testing --pixel-test-directories.
    255     tests.add('failures/unexpected/pixeldir/image_in_pixeldir.html',
    256         actual_image='image_in_pixeldir-pngtEXtchecksum\x00checksum_fail',
    257         expected_image='image_in_pixeldir-pngtEXtchecksum\x00checksum-png')
    258     tests.add('failures/unexpected/image_not_in_pixeldir.html',
    259         actual_image='image_not_in_pixeldir-pngtEXtchecksum\x00checksum_fail',
    260         expected_image='image_not_in_pixeldir-pngtEXtchecksum\x00checksum-png')
    261 
    262     # For testing that virtual test suites don't expand names containing themselves
    263     # See webkit.org/b/97925 and base_unittest.PortTest.test_tests().
    264     tests.add('passes/test-virtual-passes.html')
    265     tests.add('passes/virtual_passes/test-virtual-passes.html')
    266 
    267     return tests
    268 
    269 
    270 # Here we use a non-standard location for the layout tests, to ensure that
    271 # this works. The path contains a '.' in the name because we've seen bugs
    272 # related to this before.
    273 
    274 LAYOUT_TEST_DIR = '/test.checkout/LayoutTests'
    275 PERF_TEST_DIR = '/test.checkout/PerformanceTests'
    276 
    277 
    278 # Here we synthesize an in-memory filesystem from the test list
    279 # in order to fully control the test output and to demonstrate that
    280 # we don't need a real filesystem to run the tests.
    281 def add_unit_tests_to_mock_filesystem(filesystem):
    282     # Add the test_expectations file.
    283     filesystem.maybe_make_directory('/mock-checkout/LayoutTests')
    284     if not filesystem.exists('/mock-checkout/LayoutTests/TestExpectations'):
    285         filesystem.write_text_file('/mock-checkout/LayoutTests/TestExpectations', """
    286 Bug(test) failures/expected/crash.html [ Crash ]
    287 Bug(test) failures/expected/crash_then_text.html [ Failure ]
    288 Bug(test) failures/expected/image.html [ ImageOnlyFailure ]
    289 Bug(test) failures/expected/needsrebaseline.html [ NeedsRebaseline ]
    290 Bug(test) failures/expected/needsmanualrebaseline.html [ NeedsManualRebaseline ]
    291 Bug(test) failures/expected/audio.html [ Failure ]
    292 Bug(test) failures/expected/image_checksum.html [ ImageOnlyFailure ]
    293 Bug(test) failures/expected/mismatch.html [ ImageOnlyFailure ]
    294 Bug(test) failures/expected/missing_check.html [ Missing Pass ]
    295 Bug(test) failures/expected/missing_image.html [ Missing Pass ]
    296 Bug(test) failures/expected/missing_audio.html [ Missing Pass ]
    297 Bug(test) failures/expected/missing_text.html [ Missing Pass ]
    298 Bug(test) failures/expected/newlines_leading.html [ Failure ]
    299 Bug(test) failures/expected/newlines_trailing.html [ Failure ]
    300 Bug(test) failures/expected/newlines_with_excess_CR.html [ Failure ]
    301 Bug(test) failures/expected/reftest.html [ ImageOnlyFailure ]
    302 Bug(test) failures/expected/text.html [ Failure ]
    303 Bug(test) failures/expected/timeout.html [ Timeout ]
    304 Bug(test) failures/expected/keyboard.html [ WontFix ]
    305 Bug(test) failures/expected/exception.html [ WontFix ]
    306 Bug(test) failures/expected/device_failure.html [ WontFix ]
    307 Bug(test) failures/expected/leak.html [ Leak ]
    308 Bug(test) failures/unexpected/pass.html [ Failure ]
    309 Bug(test) passes/skipped/skip.html [ Skip ]
    310 Bug(test) passes/text.html [ Pass ]
    311 """)
    312 
    313     filesystem.maybe_make_directory(LAYOUT_TEST_DIR + '/reftests/foo')
    314     filesystem.write_text_file(LAYOUT_TEST_DIR + '/reftests/foo/reftest.list', """
    315 == test.html test-ref.html
    316 
    317 == multiple-match-success.html mismatching-ref.html
    318 == multiple-match-success.html matching-ref.html
    319 == multiple-match-failure.html mismatching-ref.html
    320 == multiple-match-failure.html second-mismatching-ref.html
    321 != multiple-mismatch-success.html mismatching-ref.html
    322 != multiple-mismatch-success.html second-mismatching-ref.html
    323 != multiple-mismatch-failure.html mismatching-ref.html
    324 != multiple-mismatch-failure.html matching-ref.html
    325 == multiple-both-success.html matching-ref.html
    326 == multiple-both-success.html mismatching-ref.html
    327 != multiple-both-success.html second-mismatching-ref.html
    328 == multiple-both-failure.html matching-ref.html
    329 != multiple-both-failure.html second-mismatching-ref.html
    330 != multiple-both-failure.html matching-ref.html
    331 """)
    332 
    333     # FIXME: This test was only being ignored because of missing a leading '/'.
    334     # Fixing the typo causes several tests to assert, so disabling the test entirely.
    335     # Add in a file should be ignored by port.find_test_files().
    336     #files[LAYOUT_TEST_DIR + '/userscripts/resources/iframe.html'] = 'iframe'
    337 
    338     def add_file(test, suffix, contents):
    339         dirname = filesystem.join(LAYOUT_TEST_DIR, test.name[0:test.name.rfind('/')])
    340         base = test.base
    341         filesystem.maybe_make_directory(dirname)
    342         filesystem.write_binary_file(filesystem.join(dirname, base + suffix), contents)
    343 
    344     # Add each test and the expected output, if any.
    345     test_list = unit_test_list()
    346     for test in test_list.tests.values():
    347         add_file(test, test.name[test.name.rfind('.'):], '')
    348         if test.is_reftest:
    349             continue
    350         if test.actual_audio:
    351             add_file(test, '-expected.wav', test.expected_audio)
    352             continue
    353         add_file(test, '-expected.txt', test.expected_text)
    354         add_file(test, '-expected.png', test.expected_image)
    355 
    356     filesystem.write_text_file(filesystem.join(LAYOUT_TEST_DIR, 'virtual', 'virtual_passes', 'passes', 'args-expected.txt'), 'args-txt --virtual-arg')
    357     # Clear the list of written files so that we can watch what happens during testing.
    358     filesystem.clear_written_files()
    359 
    360 
    361 class TestPort(Port):
    362     port_name = 'test'
    363     default_port_name = 'test-mac-leopard'
    364 
    365     """Test implementation of the Port interface."""
    366     ALL_BASELINE_VARIANTS = (
    367         'test-linux-x86_64',
    368         'test-mac-snowleopard', 'test-mac-leopard',
    369         'test-win-win7', 'test-win-xp',
    370     )
    371 
    372     FALLBACK_PATHS = {
    373         'xp':          ['test-win-win7', 'test-win-xp'],
    374         'win7':        ['test-win-win7'],
    375         'leopard':     ['test-mac-leopard', 'test-mac-snowleopard'],
    376         'snowleopard': ['test-mac-snowleopard'],
    377         'lucid':       ['test-linux-x86_64', 'test-win-win7'],
    378     }
    379 
    380     @classmethod
    381     def determine_full_port_name(cls, host, options, port_name):
    382         if port_name == 'test':
    383             return TestPort.default_port_name
    384         return port_name
    385 
    386     def __init__(self, host, port_name=None, **kwargs):
    387         Port.__init__(self, host, port_name or TestPort.default_port_name, **kwargs)
    388         self._tests = unit_test_list()
    389         self._flakes = set()
    390 
    391         # FIXME: crbug.com/279494. This needs to be in the "real layout tests
    392         # dir" in a mock filesystem, rather than outside of the checkout, so
    393         # that tests that want to write to a TestExpectations file can share
    394         # this between "test" ports and "real" ports.  This is the result of
    395         # rebaseline_unittest.py having tests that refer to "real" port names
    396         # and real builders instead of fake builders that point back to the
    397         # test ports. rebaseline_unittest.py needs to not mix both "real" ports
    398         # and "test" ports
    399 
    400         self._generic_expectations_path = '/mock-checkout/LayoutTests/TestExpectations'
    401         self._results_directory = None
    402 
    403         self._operating_system = 'mac'
    404         if self._name.startswith('test-win'):
    405             self._operating_system = 'win'
    406         elif self._name.startswith('test-linux'):
    407             self._operating_system = 'linux'
    408 
    409         version_map = {
    410             'test-win-xp': 'xp',
    411             'test-win-win7': 'win7',
    412             'test-mac-leopard': 'leopard',
    413             'test-mac-snowleopard': 'snowleopard',
    414             'test-linux-x86_64': 'lucid',
    415         }
    416         self._version = version_map[self._name]
    417 
    418     def repository_paths(self):
    419         """Returns a list of (repository_name, repository_path) tuples of its depending code base."""
    420         # FIXME: We override this just to keep the perf tests happy.
    421         return [('blink', self.layout_tests_dir())]
    422 
    423     def buildbot_archives_baselines(self):
    424         return self._name != 'test-win-xp'
    425 
    426     def default_pixel_tests(self):
    427         return True
    428 
    429     def _path_to_driver(self):
    430         # This routine shouldn't normally be called, but it is called by
    431         # the mock_drt Driver. We return something, but make sure it's useless.
    432         return 'MOCK _path_to_driver'
    433 
    434     def default_child_processes(self):
    435         return 1
    436 
    437     def check_build(self, needs_http, printer):
    438         return test_run_results.OK_EXIT_STATUS
    439 
    440     def check_sys_deps(self, needs_http):
    441         return test_run_results.OK_EXIT_STATUS
    442 
    443     def default_configuration(self):
    444         return 'Release'
    445 
    446     def diff_image(self, expected_contents, actual_contents):
    447         diffed = actual_contents != expected_contents
    448         if not actual_contents and not expected_contents:
    449             return (None, None)
    450         if not actual_contents or not expected_contents:
    451             return (True, None)
    452         if diffed:
    453             return ("< %s\n---\n> %s\n" % (expected_contents, actual_contents), None)
    454         return (None, None)
    455 
    456     def layout_tests_dir(self):
    457         return LAYOUT_TEST_DIR
    458 
    459     def perf_tests_dir(self):
    460         return PERF_TEST_DIR
    461 
    462     def webkit_base(self):
    463         return '/test.checkout'
    464 
    465     def _skipped_tests_for_unsupported_features(self, test_list):
    466         return set(['failures/expected/skip_text.html',
    467                     'failures/unexpected/skip_pass.html',
    468                     'virtual/skipped/failures/expected'])
    469 
    470     def name(self):
    471         return self._name
    472 
    473     def operating_system(self):
    474         return self._operating_system
    475 
    476     def _path_to_wdiff(self):
    477         return None
    478 
    479     def default_results_directory(self):
    480         return '/tmp/layout-test-results'
    481 
    482     def setup_test_run(self):
    483         pass
    484 
    485     def _driver_class(self):
    486         return TestDriver
    487 
    488     def start_http_server(self, additional_dirs, number_of_drivers):
    489         pass
    490 
    491     def start_websocket_server(self):
    492         pass
    493 
    494     def acquire_http_lock(self):
    495         pass
    496 
    497     def stop_http_server(self):
    498         pass
    499 
    500     def stop_websocket_server(self):
    501         pass
    502 
    503     def release_http_lock(self):
    504         pass
    505 
    506     def path_to_apache(self):
    507         return "/usr/sbin/httpd"
    508 
    509     def path_to_apache_config_file(self):
    510         return self._filesystem.join(self.layout_tests_dir(), 'http', 'conf', 'httpd.conf')
    511 
    512     def path_to_generic_test_expectations_file(self):
    513         return self._generic_expectations_path
    514 
    515     def _port_specific_expectations_files(self):
    516         return [self._filesystem.join(self._webkit_baseline_path(d), 'TestExpectations') for d in ['test', 'test-win-xp']]
    517 
    518     def all_test_configurations(self):
    519         """Returns a sequence of the TestConfigurations the port supports."""
    520         # By default, we assume we want to test every graphics type in
    521         # every configuration on every system.
    522         test_configurations = []
    523         for version, architecture in self._all_systems():
    524             for build_type in self._all_build_types():
    525                 test_configurations.append(TestConfiguration(
    526                     version=version,
    527                     architecture=architecture,
    528                     build_type=build_type))
    529         return test_configurations
    530 
    531     def _all_systems(self):
    532         return (('leopard', 'x86'),
    533                 ('snowleopard', 'x86'),
    534                 ('xp', 'x86'),
    535                 ('win7', 'x86'),
    536                 ('lucid', 'x86'),
    537                 ('lucid', 'x86_64'))
    538 
    539     def _all_build_types(self):
    540         return ('debug', 'release')
    541 
    542     def configuration_specifier_macros(self):
    543         """To avoid surprises when introducing new macros, these are intentionally fixed in time."""
    544         return {'mac': ['leopard', 'snowleopard'], 'win': ['xp', 'win7'], 'linux': ['lucid']}
    545 
    546     def all_baseline_variants(self):
    547         return self.ALL_BASELINE_VARIANTS
    548 
    549     def virtual_test_suites(self):
    550         return [
    551             VirtualTestSuite(prefix='virtual_passes', base='passes', args=['--virtual-arg']),
    552             VirtualTestSuite(prefix='skipped', base='failures/expected', args=['--virtual-arg2']),
    553         ]
    554 
    555 
    556 class TestDriver(Driver):
    557     """Test/Dummy implementation of the driver interface."""
    558     next_pid = 1
    559 
    560     def __init__(self, *args, **kwargs):
    561         super(TestDriver, self).__init__(*args, **kwargs)
    562         self.started = False
    563         self.pid = 0
    564 
    565     def cmd_line(self, pixel_tests, per_test_args):
    566         pixel_tests_flag = '-p' if pixel_tests else ''
    567         return [self._port._path_to_driver()] + [pixel_tests_flag] + self._port.get_option('additional_drt_flag', []) + per_test_args
    568 
    569     def run_test(self, driver_input, stop_when_done):
    570         if not self.started:
    571             self.started = True
    572             self.pid = TestDriver.next_pid
    573             TestDriver.next_pid += 1
    574 
    575         start_time = time.time()
    576         test_name = driver_input.test_name
    577         test_args = driver_input.args or []
    578         test = self._port._tests[test_name]
    579         if test.keyboard:
    580             raise KeyboardInterrupt
    581         if test.exception:
    582             raise ValueError('exception from ' + test_name)
    583         if test.device_failure:
    584             raise DeviceFailure('device failure in ' + test_name)
    585 
    586         audio = None
    587         actual_text = test.actual_text
    588         crash = test.crash
    589         web_process_crash = test.web_process_crash
    590 
    591         if 'flaky/text.html' in test_name and not test_name in self._port._flakes:
    592             self._port._flakes.add(test_name)
    593             actual_text = 'flaky text failure'
    594 
    595         if 'crash_then_text.html' in test_name:
    596             if test_name in self._port._flakes:
    597                 actual_text = 'text failure'
    598             else:
    599                 self._port._flakes.add(test_name)
    600                 crashed_process_name = self._port.driver_name()
    601                 crashed_pid = 1
    602                 crash = True
    603 
    604         if 'text_then_crash.html' in test_name:
    605             if test_name in self._port._flakes:
    606                 crashed_process_name = self._port.driver_name()
    607                 crashed_pid = 1
    608                 crash = True
    609             else:
    610                 self._port._flakes.add(test_name)
    611                 actual_text = 'text failure'
    612 
    613         if actual_text and test_args and test_name == 'passes/args.html':
    614             actual_text = actual_text + ' ' + ' '.join(test_args)
    615 
    616         if test.actual_audio:
    617             audio = base64.b64decode(test.actual_audio)
    618         crashed_process_name = None
    619         crashed_pid = None
    620         if crash:
    621             crashed_process_name = self._port.driver_name()
    622             crashed_pid = 1
    623         elif web_process_crash:
    624             crashed_process_name = 'WebProcess'
    625             crashed_pid = 2
    626 
    627         crash_log = ''
    628         if crashed_process_name:
    629             crash_logs = CrashLogs(self._port.host)
    630             crash_log = crash_logs.find_newest_log(crashed_process_name, None) or ''
    631 
    632         if stop_when_done:
    633             self.stop()
    634 
    635         if test.actual_checksum == driver_input.image_hash:
    636             image = None
    637         else:
    638             image = test.actual_image
    639         return DriverOutput(actual_text, image, test.actual_checksum, audio,
    640             crash=(crash or web_process_crash), crashed_process_name=crashed_process_name,
    641             crashed_pid=crashed_pid, crash_log=crash_log,
    642             test_time=time.time() - start_time, timeout=test.timeout, error=test.error, pid=self.pid,
    643             leak=test.leak)
    644 
    645     def stop(self):
    646         self.started = False
    647