Changeset 211370 in webkit


Ignore:
Timestamp:
Jan 30, 2017 9:31:47 AM (7 years ago)
Author:
Jonathan Bedard
Message:

Use simctl instead of LayoutTestRelay
https://bugs.webkit.org/show_bug.cgi?id=165927

Reviewed by Daniel Bates.

Part 1

LayoutTestRelay uses SPI, since recent versions of the iOS SDK allow for installing apps on
simulators through simctl (iOS 10 and later), use this functionality instead.

  • Scripts/webkitpy/port/base.py:

(Port.init): Added _test_runner_process_constructor.

  • Scripts/webkitpy/port/darwin.py:

(DarwinPort.app_identifier_from_bundle): Added function to extract bundle ID from plist.

  • Scripts/webkitpy/port/driver.py:

(Driver._start): Pass worker_number to server_process so we can look up the correct simulator device to use.
(IOSSimulatorDriver): Deleted.

  • Scripts/webkitpy/port/driver_unittest.py:

(DriverTest.test_stop_cleans_up_properly): Set _test_runner_process_constructor for testing.
(DriverTest.test_two_starts_cleans_up_properly): Ditto.
(DriverTest.test_start_actually_starts): Ditto.

  • Scripts/webkitpy/port/ios.py:

(IOSSimulatorPort): Remove relay_name.
(IOSSimulatorPort.init): Set _test_runner_process_constructor to SimulatorProcess for IOSSimulatorPort.
(IOSSimulatorPort._create_simulators): Formatting change.
(IOSSimulatorPort.relay_path): Deleted.
(IOSSimulatorPort._check_relay): Deleted.
(IOSSimulatorPort._check_port_build): Deleted. Use base class implementation
(IOSSimulatorPort._build_relay): Deleted.
(IOSSimulatorPort._build_driver): Deleted. Use base class implementation
(IOSSimulatorPort._driver_class): Deleted. Use base class implementation

  • Scripts/webkitpy/port/ios_unittest.py:

(iosTest.test_32bit): Update test.
(iosTest.test_64bit): Update test.

  • Scripts/webkitpy/port/server_process.py:

(ServerProcess.init): Added argument worker_number. This class does not make use of it. We will make use of this argument in SimulatorProcess to lookup the associated simulator device.
(ServerProcess._set_file_nonblocking): Added to share common code.

  • Scripts/webkitpy/port/server_process_mock.py:

(MockServerProcess.init): Added argument worker_number.

  • Scripts/webkitpy/port/simulator_process.py: Added.

(SimulatorProcess): Added.
(SimulatorProcess.Popen): Added.
(SimulatorProcess.Popen.init): Added. Initialize Popen structure with stdin, stdout, stderr and pid.
(SimulatorProcess.Popen.poll): Added. Check if the process is running.
(SimulatorProcess.Popen.wait): Added. Wait for process to close.
(SimulatorProcess.init): Added. Install app to device specified through port and worker_number.
(SimulatorProcess._reset): Added. Unlink fifos.
(SimulatorProcess._start): Added. Launch app on simulator, link fifos.
(SimulatorProcess._kill): Added. Shutdown app on simulator.

  • Scripts/webkitpy/xcode/simulator.py:

(Device.init): Accept host to run install/launch/terminate.
(Device.install_app): Install app to target Device.
(Device.launch_app): Launch app on target Device.
(Device.terminate_app): Shutdown app on target Device.
(Simulator._parse_devices): Pass host to Device.

Location:
trunk/Tools
Files:
1 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/Tools/ChangeLog

    r211369 r211370  
     12017-01-30  Jonathan Bedard  <jbedard@apple.com>
     2
     3        Use simctl instead of LayoutTestRelay
     4        https://bugs.webkit.org/show_bug.cgi?id=165927
     5
     6        Reviewed by Daniel Bates.
     7
     8        Part 1
     9
     10        LayoutTestRelay uses SPI, since recent versions of the iOS SDK allow for installing apps on
     11        simulators through simctl (iOS 10 and later), use this functionality instead.
     12
     13        * Scripts/webkitpy/port/base.py:
     14        (Port.__init__): Added _test_runner_process_constructor.
     15        * Scripts/webkitpy/port/darwin.py:
     16        (DarwinPort.app_identifier_from_bundle): Added function to extract bundle ID from plist.
     17        * Scripts/webkitpy/port/driver.py:
     18        (Driver._start): Pass worker_number to server_process so we can look up the correct simulator device to use.
     19        (IOSSimulatorDriver): Deleted.
     20        * Scripts/webkitpy/port/driver_unittest.py:
     21        (DriverTest.test_stop_cleans_up_properly): Set _test_runner_process_constructor for testing.
     22        (DriverTest.test_two_starts_cleans_up_properly): Ditto.
     23        (DriverTest.test_start_actually_starts): Ditto.
     24        * Scripts/webkitpy/port/ios.py:
     25        (IOSSimulatorPort): Remove relay_name.
     26        (IOSSimulatorPort.__init__): Set _test_runner_process_constructor to SimulatorProcess for IOSSimulatorPort.
     27        (IOSSimulatorPort._create_simulators): Formatting change.
     28        (IOSSimulatorPort.relay_path): Deleted.
     29        (IOSSimulatorPort._check_relay): Deleted.
     30        (IOSSimulatorPort._check_port_build): Deleted. Use base class implementation
     31        (IOSSimulatorPort._build_relay): Deleted.
     32        (IOSSimulatorPort._build_driver): Deleted. Use base class implementation
     33        (IOSSimulatorPort._driver_class): Deleted. Use base class implementation
     34        * Scripts/webkitpy/port/ios_unittest.py:
     35        (iosTest.test_32bit): Update test.
     36        (iosTest.test_64bit): Update test.
     37        * Scripts/webkitpy/port/server_process.py:
     38        (ServerProcess.__init__): Added argument worker_number. This class does not make use of it. We will make use of this argument in SimulatorProcess to lookup the associated simulator device.
     39        (ServerProcess._set_file_nonblocking): Added to share common code.
     40        * Scripts/webkitpy/port/server_process_mock.py:
     41        (MockServerProcess.__init__): Added argument worker_number.
     42        * Scripts/webkitpy/port/simulator_process.py: Added.
     43        (SimulatorProcess): Added.
     44        (SimulatorProcess.Popen): Added.
     45        (SimulatorProcess.Popen.__init__): Added. Initialize Popen structure with stdin, stdout, stderr and pid.
     46        (SimulatorProcess.Popen.poll): Added. Check if the process is running.
     47        (SimulatorProcess.Popen.wait): Added. Wait for process to close.
     48        (SimulatorProcess.__init__): Added. Install app to device specified through port and worker_number.
     49        (SimulatorProcess._reset): Added. Unlink fifos.
     50        (SimulatorProcess._start): Added. Launch app on simulator, link fifos.
     51        (SimulatorProcess._kill): Added. Shutdown app on simulator.
     52        * Scripts/webkitpy/xcode/simulator.py:
     53        (Device.__init__): Accept host to run install/launch/terminate.
     54        (Device.install_app): Install app to target Device.
     55        (Device.launch_app): Launch app on target Device.
     56        (Device.terminate_app): Shutdown app on target Device.
     57        (Simulator._parse_devices): Pass host to Device.
     58
    1592017-01-30  Carlos Alberto Lopez Perez  <clopez@igalia.com>
    260
  • trunk/Tools/Scripts/webkitpy/port/base.py

    r210510 r211370  
    132132        self._image_differ = None
    133133        self._server_process_constructor = server_process.ServerProcess  # overridable for testing
     134        self._test_runner_process_constructor = server_process.ServerProcess
    134135
    135136        if not hasattr(options, 'configuration') or not options.configuration:
  • trunk/Tools/Scripts/webkitpy/port/darwin.py

    r206934 r211370  
    175175            _log.warn("xcrun failed; falling back to '%s'." % fallback)
    176176            return fallback
     177
     178    def app_identifier_from_bundle(self, app_bundle):
     179        plist_path = self._filesystem.join(app_bundle, 'Info.plist')
     180        if not self._filesystem.exists(plist_path):
     181            plist_path = self._filesystem.join(app_bundle, 'Contents', 'Info.plist')
     182        if not self._filesystem.exists(plist_path):
     183            return None
     184        return self._executive.run_command(['/usr/libexec/PlistBuddy', '-c', 'Print CFBundleIdentifier', plist_path]).rstrip()
  • trunk/Tools/Scripts/webkitpy/port/driver.py

    r210446 r211370  
    139139        self._driver_user_cache_directory = None
    140140
    141         # WebKitTestRunner/LayoutTestRelay can report back subprocess crashes by printing
     141        # WebKitTestRunner can report back subprocess crashes by printing
    142142        # "#CRASHED - PROCESSNAME".  Since those can happen at any time and ServerProcess
    143143        # won't be aware of them (since the actual tool didn't crash, just a subprocess)
     
    328328    def _setup_environ_for_driver(self, environment):
    329329        build_root_path = str(self._port._build_path())
    330         # FIXME: DYLD_* variables should be Mac-only. Even iOS Simulator doesn't need them, as LayoutTestRelay is a host binary.
    331330        self._append_environment_variable_path(environment, 'DYLD_LIBRARY_PATH', build_root_path)
    332331        self._append_environment_variable_path(environment, '__XPC_DYLD_LIBRARY_PATH', build_root_path)
     
    370369        self._crashed_process_name = None
    371370        self._crashed_pid = None
    372         self._server_process = self._port._server_process_constructor(self._port, self._server_name, self.cmd_line(pixel_tests, per_test_args), environment)
     371        self._server_process = self._port._test_runner_process_constructor(self._port, self._server_name, self.cmd_line(pixel_tests, per_test_args), environment, worker_number=self._worker_number)
    373372        self._server_process.start()
    374373
     
    612611
    613612
    614 # FIXME: this should be abstracted out via the Port subclass somehow.
    615 class IOSSimulatorDriver(Driver):
    616     def cmd_line(self, pixel_tests, per_test_args):
    617         cmd = super(IOSSimulatorDriver, self).cmd_line(pixel_tests, per_test_args)
    618         relay_tool = self._port.relay_path
    619         dump_tool = cmd[0]
    620         dump_tool_args = cmd[1:]
    621         product_dir = self._port._build_path()
    622         relay_args = [
    623             '-developerDir', self._port.developer_dir,
    624             '-udid', self._port.device_id_for_worker_number(self._worker_number),
    625             '-productDir', product_dir,
    626             '-app', dump_tool,
    627         ]
    628         return [relay_tool] + relay_args + ['--'] + dump_tool_args
    629 
    630     def _setup_environ_for_driver(self, environment):
    631         environment['DEVELOPER_DIR'] = self._port.developer_dir
    632         return super(IOSSimulatorDriver, self)._setup_environ_for_driver(environment)
    633 
    634 
    635613class ContentBlock(object):
    636614    def __init__(self):
  • trunk/Tools/Scripts/webkitpy/port/driver_unittest.py

    r205014 r211370  
    265265    def test_stop_cleans_up_properly(self):
    266266        port = TestWebKitPort()
    267         port._server_process_constructor = MockServerProcess
     267        port._test_runner_process_constructor = MockServerProcess
    268268        driver = Driver(port, 0, pixel_tests=True)
    269269        driver.start(True, [])
     
    275275    def test_two_starts_cleans_up_properly(self):
    276276        port = TestWebKitPort()
    277         port._server_process_constructor = MockServerProcess
     277        port._test_runner_process_constructor = MockServerProcess
    278278        driver = Driver(port, 0, pixel_tests=True)
    279279        driver.start(True, [])
     
    284284    def test_start_actually_starts(self):
    285285        port = TestWebKitPort()
    286         port._server_process_constructor = MockServerProcess
     286        port._test_runner_process_constructor = MockServerProcess
    287287        driver = Driver(port, 0, pixel_tests=True)
    288288        driver.start(True, [])
  • trunk/Tools/Scripts/webkitpy/port/ios.py

    r209337 r211370  
    3535from webkitpy.port import driver, image_diff
    3636from webkitpy.port.darwin import DarwinPort
     37from webkitpy.port.simulator_process import SimulatorProcess
    3738from webkitpy.xcode.simulator import Simulator, Runtime, DeviceType
    3839from webkitpy.common.system.crashlogs import CrashLogs
     
    7980
    8081    SIMULATOR_BUNDLE_ID = 'com.apple.iphonesimulator'
    81     relay_name = 'LayoutTestRelay'
    8282    SIMULATOR_DIRECTORY = "/tmp/WebKitTestingSimulators/"
    8383    LSREGISTER_PATH = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Versions/Current/Support/lsregister"
     
    9898    def __init__(self, host, port_name, **kwargs):
    9999        DarwinPort.__init__(self, host, port_name, **kwargs)
     100        self._test_runner_process_constructor = SimulatorProcess
    100101
    101102        optional_device_class = self.get_option('device_class')
     
    149150        return device_type
    150151
    151     @property
    152     @memoized
    153     def relay_path(self):
    154         if self._root_was_set:
    155             path = self._filesystem.abspath(self.get_option('root'))
    156         else:
    157             mac_config = port_config.Config(self._executive, self._filesystem, 'mac')
    158             path = mac_config.build_directory(self.get_option('configuration'))
    159         return self._filesystem.join(path, self.relay_name)
    160 
    161152    @memoized
    162153    def child_processes(self):
     
    182173
    183174        return min(maximum_simulator_count_on_this_system, best_child_process_count_for_cpu)
    184 
    185     def _check_relay(self):
    186         if not self._filesystem.exists(self.relay_path):
    187             _log.error("%s was not found at %s" % (self.relay_name, self.relay_path))
    188             return False
    189         return True
    190 
    191     def _check_port_build(self):
    192         if not self._root_was_set and self.get_option('build') and not self._build_relay():
    193             return False
    194         if not self._check_relay():
    195             return False
    196         return True
    197175
    198176    def _get_crash_log(self, name, pid, stdout, stderr, newer_than, time_fn=time.time, sleep_fn=time.sleep, wait_for_log=True):
     
    216194                '\n'.join(stderr_lines), newer_than, time_fn, sleep_fn, wait_for_log)
    217195
    218         # LayoutTestRelay crashed
     196        # App crashed
    219197        _log.debug('looking for crash log for %s:%s' % (name, str(pid)))
    220198        crash_log = ''
     
    234212        return stderr, crash_log
    235213
    236     def _build_relay(self):
    237         environment = self.host.copy_current_environment()
    238         environment.disable_gcc_smartquotes()
    239         env = environment.to_dictionary()
    240 
    241         try:
    242             # FIXME: We should be passing _arguments_for_configuration(), which respects build configuration and port,
    243             # instead of hardcoding --ios-simulator.
    244             self._run_script("build-layouttestrelay", args=["--ios-simulator"], env=env)
    245         except ScriptError, e:
    246             _log.error(e.message_with_output(output_limit=None))
    247             return False
    248         return True
    249 
    250     def _build_driver(self):
    251         built_tool = super(IOSSimulatorPort, self)._build_driver()
    252         built_relay = self._build_relay()
    253         return built_tool and built_relay
    254 
    255214    def _build_driver_flags(self):
    256215        archs = ['ARCHS=i386'] if self.architecture() == 'x86' else []
     
    265224        return configurations
    266225
    267     def _driver_class(self):
    268         return driver.IOSSimulatorDriver
    269 
    270226    def default_baseline_search_path(self):
    271227        if self.get_option('webkit_test_runner'):
     
    281237    def _create_simulators(self):
    282238        if (self.default_child_processes() < self.child_processes()):
    283                 _log.warn("You have specified very high value({0}) for --child-processes".format(self.child_processes()))
    284                 _log.warn("maximum child-processes which can be supported on this system are: {0}".format(self.default_child_processes()))
    285                 _log.warn("This is very likely to fail.")
     239            _log.warn('You have specified very high value({0}) for --child-processes'.format(self.child_processes()))
     240            _log.warn('maximum child-processes which can be supported on this system are: {0}'.format(self.default_child_processes()))
     241            _log.warn('This is very likely to fail.')
    286242
    287243        if self._using_dedicated_simulators():
     
    414370        return Simulator.device_number(number)
    415371
    416     # This is only exposed so that IOSSimulatorDriver can use it.
     372    # FIXME: This is only exposed so that SimulatorProcess can use it.
    417373    def device_id_for_worker_number(self, number):
    418374        if self._printing_cmd_line:
  • trunk/Tools/Scripts/webkitpy/port/ios_unittest.py

    r206934 r211370  
    7171        self.assertEqual(port.architecture(), 'x86')
    7272        port._build_driver()
    73         self.assertEqual(self.args, ['--ios-simulator'])
     73        self.assertEqual(self.args, ['ARCHS=i386', '--sdk', 'iphonesimulator'])
    7474
    7575    def test_64bit(self):
     
    8383        port._run_script = run_script
    8484        port._build_driver()
    85         self.assertEqual(self.args, ['--ios-simulator'])
     85        self.assertEqual(self.args, ['--sdk', 'iphonesimulator'])
    8686
    8787    def test_sdk_name(self):
  • trunk/Tools/Scripts/webkitpy/port/server_process.py

    r204577 r211370  
     1# Copyright (C) 2017 Apple Inc. All rights reserved.
    12# Copyright (C) 2010 Google Inc. All rights reserved.
    23#
     
    6061    as necessary to keep issuing commands."""
    6162
    62     def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False, treat_no_data_as_crash=False):
     63    def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False, treat_no_data_as_crash=False, worker_number=None):
    6364        self._port = port_obj
    6465        self._name = name  # Should be the command name (e.g. DumpRenderTree, ImageDiff)
     
    103104    def process_name(self):
    104105        return self._name
     106
     107    @staticmethod
     108    def _set_file_nonblocking(file):
     109        flags = fcntl.fcntl(file.fileno(), fcntl.F_GETFL)
     110        fcntl.fcntl(file.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
    105111
    106112    def _start(self):
     
    118124        self._pid = self._proc.pid
    119125        self._port.find_system_pid(self.name(), self._pid)
    120         fd = self._proc.stdout.fileno()
    121126        if not self._use_win32_apis:
    122             fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    123             fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
    124             fd = self._proc.stderr.fileno()
    125             fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    126             fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
     127            self._set_file_nonblocking(self._proc.stdout)
     128            self._set_file_nonblocking(self._proc.stderr)
    127129
    128130    def _handle_possible_interrupt(self):
  • trunk/Tools/Scripts/webkitpy/port/server_process_mock.py

    r124958 r211370  
    2929
    3030class MockServerProcess(object):
    31     def __init__(self, port_obj=None, name=None, cmd=None, env=None, universal_newlines=False, lines=None, crashed=False):
     31    def __init__(self, port_obj=None, name=None, cmd=None, env=None, universal_newlines=False, lines=None, crashed=False, worker_number=None):
    3232        self.timed_out = False
    3333        self.lines = lines or []
  • trunk/Tools/Scripts/webkitpy/xcode/simulator.py

    r209337 r211370  
    168168    """
    169169
    170     def __init__(self, name, udid, available, runtime):
     170    def __init__(self, name, udid, available, runtime, host):
    171171        """
    172172        :param name: The device name
     
    178178        :param runtime: The iOS Simulator runtime that hosts this device
    179179        :type runtime: Runtime
    180         """
     180        :param host: The host which can run command line commands
     181        :type host: Host
     182        """
     183        self._host = host
    181184        self.name = name
    182185        self.udid = udid
     
    260263        except subprocess.CalledProcessError:
    261264            raise RuntimeError('"xcrun simctl erase" failed: device state is {}'.format(Simulator.device_state(udid)))
     265
     266    def install_app(self, app_path):
     267        return not self._host.executive.run_command(['xcrun', 'simctl', 'install', self.udid, app_path], return_exit_code=True)
     268
     269    def launch_app(self, bundle_id, args, env=None):
     270        environment_to_use = {}
     271        SIMCTL_ENV_PREFIX = 'SIMCTL_CHILD_'
     272        for value in (env or {}):
     273            if not value.startswith(SIMCTL_ENV_PREFIX):
     274                environment_to_use[SIMCTL_ENV_PREFIX + value] = env[value]
     275            else:
     276                environment_to_use[value] = env[value]
     277
     278        output = self._host.executive.run_command(
     279            ['xcrun', 'simctl', 'launch', self.udid, bundle_id] + args,
     280            env=environment_to_use,
     281        )
     282
     283        match = re.match(r'(?P<bundle>[^:]+): (?P<pid>\d+)\n', output)
     284        if not match or match.group('bundle') != bundle_id:
     285            raise RuntimeError('Failed to find process id for {}: {}'.format(bundle_id, output))
     286        return int(match.group('pid'))
     287
     288    def terminate_app(self, bundle_id):
     289        return not self._host.executive.run_command(['xcrun', 'simctl', 'terminate', self.udid, bundle_id], return_exit_code=True)
    262290
    263291    def __eq__(self, other):
     
    478506                                udid=device_match.group('udid'),
    479507                                available=device_match.group('availability') is None,
    480                                 runtime=current_runtime)
     508                                runtime=current_runtime,
     509                                host=self._host)
    481510                current_runtime.devices.append(device)
    482511
Note: See TracChangeset for help on using the changeset viewer.