Home | History | Annotate | Download | only in port
      1 #!/usr/bin/env python
      2 # Copyright (C) 2011 Google 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 Google name 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 """
     31 This is an implementation of the Port interface that overrides other
     32 ports and changes the Driver binary to "MockDRT".
     33 """
     34 
     35 import base64
     36 import logging
     37 import optparse
     38 import os
     39 import sys
     40 
     41 from webkitpy.common.system import filesystem
     42 
     43 from webkitpy.layout_tests.port import base
     44 from webkitpy.layout_tests.port import factory
     45 
     46 _log = logging.getLogger(__name__)
     47 
     48 
     49 class MockDRTPort(object):
     50     """MockPort implementation of the Port interface."""
     51 
     52     def __init__(self, **kwargs):
     53         prefix = 'mock-'
     54         if 'port_name' in kwargs:
     55             kwargs['port_name'] = kwargs['port_name'][len(prefix):]
     56         self.__delegate = factory.get(**kwargs)
     57         self.__real_name = prefix + self.__delegate.name()
     58 
     59     def real_name(self):
     60         return self.__real_name
     61 
     62     def __getattr__(self, name):
     63         return getattr(self.__delegate, name)
     64 
     65     def acquire_http_lock(self):
     66         pass
     67 
     68     def release_http_lock(self):
     69         pass
     70 
     71     def check_build(self, needs_http):
     72         return True
     73 
     74     def check_sys_deps(self, needs_http):
     75         return True
     76 
     77     def driver_cmd_line(self):
     78         driver = self.create_driver(0)
     79         return driver.cmd_line()
     80 
     81     def _path_to_driver(self):
     82         return os.path.abspath(__file__)
     83 
     84     def create_driver(self, worker_number):
     85         # We need to create a driver object as the delegate would, but
     86         # overwrite the path to the driver binary in its command line. We do
     87         # this by actually overwriting its cmd_line() method with a proxy
     88         # method that splices in the mock_drt path and command line arguments
     89         # in place of the actual path to the driver binary.
     90 
     91         def overriding_cmd_line():
     92             cmd = self.__original_driver_cmd_line()
     93             index = cmd.index(self.__delegate._path_to_driver())
     94             cmd[index:index + 1] = [sys.executable, self._path_to_driver(),
     95                                     '--platform', self.name()]
     96             return cmd
     97 
     98         delegated_driver = self.__delegate.create_driver(worker_number)
     99         self.__original_driver_cmd_line = delegated_driver.cmd_line
    100         delegated_driver.cmd_line = overriding_cmd_line
    101         return delegated_driver
    102 
    103     def start_helper(self):
    104         pass
    105 
    106     def start_http_server(self):
    107         pass
    108 
    109     def start_websocket_server(self):
    110         pass
    111 
    112     def stop_helper(self):
    113         pass
    114 
    115     def stop_http_server(self):
    116         pass
    117 
    118     def stop_websocket_server(self):
    119         pass
    120 
    121 
    122 def main(argv, fs, stdin, stdout, stderr):
    123     """Run the tests."""
    124 
    125     options, args = parse_options(argv)
    126     if options.chromium:
    127         drt = MockChromiumDRT(options, args, fs, stdin, stdout, stderr)
    128     else:
    129         drt = MockDRT(options, args, fs, stdin, stdout, stderr)
    130     return drt.run()
    131 
    132 
    133 def parse_options(argv):
    134     # FIXME: We have to do custom arg parsing instead of using the optparse
    135     # module.  First, Chromium and non-Chromium DRTs have a different argument
    136     # syntax.  Chromium uses --pixel-tests=<path>, and non-Chromium uses
    137     # --pixel-tests as a boolean flag. Second, we don't want to have to list
    138     # every command line flag DRT accepts, but optparse complains about
    139     # unrecognized flags. At some point it might be good to share a common
    140     # DRT options class between this file and webkit.py and chromium.py
    141     # just to get better type checking.
    142     platform_index = argv.index('--platform')
    143     platform = argv[platform_index + 1]
    144 
    145     pixel_tests = False
    146     pixel_path = None
    147     chromium = False
    148     if platform.startswith('chromium'):
    149         chromium = True
    150         for arg in argv:
    151             if arg.startswith('--pixel-tests'):
    152                 pixel_tests = True
    153                 pixel_path = arg[len('--pixel-tests='):]
    154     else:
    155         pixel_tests = '--pixel-tests' in argv
    156     options = base.DummyOptions(chromium=chromium,
    157                                 platform=platform,
    158                                 pixel_tests=pixel_tests,
    159                                 pixel_path=pixel_path)
    160     return (options, [])
    161 
    162 
    163 # FIXME: Should probably change this to use DriverInput after
    164 # https://bugs.webkit.org/show_bug.cgi?id=53004 lands.
    165 class _DRTInput(object):
    166     def __init__(self, line):
    167         vals = line.strip().split("'")
    168         if len(vals) == 1:
    169             self.uri = vals[0]
    170             self.checksum = None
    171         else:
    172             self.uri = vals[0]
    173             self.checksum = vals[1]
    174 
    175 
    176 class MockDRT(object):
    177     def __init__(self, options, args, filesystem, stdin, stdout, stderr):
    178         self._options = options
    179         self._args = args
    180         self._filesystem = filesystem
    181         self._stdout = stdout
    182         self._stdin = stdin
    183         self._stderr = stderr
    184 
    185         port_name = None
    186         if options.platform:
    187             port_name = options.platform
    188         self._port = factory.get(port_name, options=options, filesystem=filesystem)
    189 
    190     def run(self):
    191         while True:
    192             line = self._stdin.readline()
    193             if not line:
    194                 break
    195             self.run_one_test(self.parse_input(line))
    196         return 0
    197 
    198     def parse_input(self, line):
    199         return _DRTInput(line)
    200 
    201     def run_one_test(self, test_input):
    202         port = self._port
    203         if test_input.uri.startswith('http'):
    204             test_name = port.uri_to_test_name(test_input.uri)
    205             test_path = self._filesystem.join(port.layout_tests_dir(), test_name)
    206         else:
    207             test_path = test_input.uri
    208 
    209         actual_text = port.expected_text(test_path)
    210         actual_audio = port.expected_audio(test_path)
    211         if self._options.pixel_tests and test_input.checksum:
    212             actual_checksum = port.expected_checksum(test_path)
    213             actual_image = port.expected_image(test_path)
    214 
    215         if actual_audio:
    216             self._stdout.write('Content-Type: audio/wav\n')
    217             self._stdout.write('Content-Transfer-Encoding: base64\n')
    218             output = base64.b64encode(actual_audio)
    219             self._stdout.write('Content-Length: %s\n' % len(output))
    220             self._stdout.write(output)
    221         else:
    222             self._stdout.write('Content-Type: text/plain\n')
    223             # FIXME: Note that we don't ensure there is a trailing newline!
    224             # This mirrors actual (Mac) DRT behavior but is a bug.
    225             self._stdout.write(actual_text)
    226 
    227         self._stdout.write('#EOF\n')
    228 
    229         if self._options.pixel_tests and test_input.checksum:
    230             self._stdout.write('\n')
    231             self._stdout.write('ActualHash: %s\n' % actual_checksum)
    232             self._stdout.write('ExpectedHash: %s\n' % test_input.checksum)
    233             if actual_checksum != test_input.checksum:
    234                 self._stdout.write('Content-Type: image/png\n')
    235                 self._stdout.write('Content-Length: %s\n' % len(actual_image))
    236                 self._stdout.write(actual_image)
    237         self._stdout.write('#EOF\n')
    238         self._stdout.flush()
    239         self._stderr.flush()
    240 
    241 
    242 # FIXME: Should probably change this to use DriverInput after
    243 # https://bugs.webkit.org/show_bug.cgi?id=53004 lands.
    244 class _ChromiumDRTInput(_DRTInput):
    245     def __init__(self, line):
    246         vals = line.strip().split()
    247         if len(vals) == 3:
    248             self.uri, self.timeout, self.checksum = vals
    249         else:
    250             self.uri = vals[0]
    251             self.timeout = vals[1]
    252             self.checksum = None
    253 
    254 
    255 class MockChromiumDRT(MockDRT):
    256     def parse_input(self, line):
    257         return _ChromiumDRTInput(line)
    258 
    259     def run_one_test(self, test_input):
    260         port = self._port
    261         test_name = self._port.uri_to_test_name(test_input.uri)
    262         test_path = self._filesystem.join(port.layout_tests_dir(), test_name)
    263 
    264         actual_text = port.expected_text(test_path)
    265         actual_image = ''
    266         actual_checksum = ''
    267         if self._options.pixel_tests and test_input.checksum:
    268             actual_checksum = port.expected_checksum(test_path)
    269             if actual_checksum != test_input.checksum:
    270                 actual_image = port.expected_image(test_path)
    271 
    272         self._stdout.write("#URL:%s\n" % test_input.uri)
    273         if self._options.pixel_tests and test_input.checksum:
    274             self._stdout.write("#MD5:%s\n" % actual_checksum)
    275             self._filesystem.write_binary_file(self._options.pixel_path,
    276                                                actual_image)
    277         self._stdout.write(actual_text)
    278 
    279         # FIXME: (See above FIXME as well). Chromium DRT appears to always
    280         # ensure the text output has a trailing newline. Mac DRT does not.
    281         if not actual_text.endswith('\n'):
    282             self._stdout.write('\n')
    283         self._stdout.write('#EOF\n')
    284         self._stdout.flush()
    285 
    286 
    287 if __name__ == '__main__':
    288     fs = filesystem.FileSystem()
    289     sys.exit(main(sys.argv[1:], fs, sys.stdin, sys.stdout, sys.stderr))
    290