<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[211370] trunk/Tools</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/211370">211370</a></dd>
<dt>Author</dt> <dd>jbedard@apple.com</dd>
<dt>Date</dt> <dd>2017-01-30 09:31:47 -0800 (Mon, 30 Jan 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>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.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsScriptswebkitpyportbasepy">trunk/Tools/Scripts/webkitpy/port/base.py</a></li>
<li><a href="#trunkToolsScriptswebkitpyportdarwinpy">trunk/Tools/Scripts/webkitpy/port/darwin.py</a></li>
<li><a href="#trunkToolsScriptswebkitpyportdriverpy">trunk/Tools/Scripts/webkitpy/port/driver.py</a></li>
<li><a href="#trunkToolsScriptswebkitpyportdriver_unittestpy">trunk/Tools/Scripts/webkitpy/port/driver_unittest.py</a></li>
<li><a href="#trunkToolsScriptswebkitpyportiospy">trunk/Tools/Scripts/webkitpy/port/ios.py</a></li>
<li><a href="#trunkToolsScriptswebkitpyportios_unittestpy">trunk/Tools/Scripts/webkitpy/port/ios_unittest.py</a></li>
<li><a href="#trunkToolsScriptswebkitpyportserver_processpy">trunk/Tools/Scripts/webkitpy/port/server_process.py</a></li>
<li><a href="#trunkToolsScriptswebkitpyportserver_process_mockpy">trunk/Tools/Scripts/webkitpy/port/server_process_mock.py</a></li>
<li><a href="#trunkToolsScriptswebkitpyxcodesimulatorpy">trunk/Tools/Scripts/webkitpy/xcode/simulator.py</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkToolsScriptswebkitpyportsimulator_processpy">trunk/Tools/Scripts/webkitpy/port/simulator_process.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (211369 => 211370)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2017-01-30 17:09:06 UTC (rev 211369)
+++ trunk/Tools/ChangeLog        2017-01-30 17:31:47 UTC (rev 211370)
</span><span class="lines">@@ -1,3 +1,61 @@
</span><ins>+2017-01-30  Jonathan Bedard  &lt;jbedard@apple.com&gt;
+
+        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.
+
</ins><span class="cx"> 2017-01-30  Carlos Alberto Lopez Perez  &lt;clopez@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [GTK] pixman fails to compile on Raspberry Pi (GCC crash)
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpyportbasepy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/port/base.py (211369 => 211370)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/port/base.py        2017-01-30 17:09:06 UTC (rev 211369)
+++ trunk/Tools/Scripts/webkitpy/port/base.py        2017-01-30 17:31:47 UTC (rev 211370)
</span><span class="lines">@@ -131,6 +131,7 @@
</span><span class="cx">         self._web_platform_test_server = None
</span><span class="cx">         self._image_differ = None
</span><span class="cx">         self._server_process_constructor = server_process.ServerProcess  # overridable for testing
</span><ins>+        self._test_runner_process_constructor = server_process.ServerProcess
</ins><span class="cx"> 
</span><span class="cx">         if not hasattr(options, 'configuration') or not options.configuration:
</span><span class="cx">             self.set_option_default('configuration', self.default_configuration())
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpyportdarwinpy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/port/darwin.py (211369 => 211370)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/port/darwin.py        2017-01-30 17:09:06 UTC (rev 211369)
+++ trunk/Tools/Scripts/webkitpy/port/darwin.py        2017-01-30 17:31:47 UTC (rev 211370)
</span><span class="lines">@@ -174,3 +174,11 @@
</span><span class="cx">         except ScriptError:
</span><span class="cx">             _log.warn(&quot;xcrun failed; falling back to '%s'.&quot; % fallback)
</span><span class="cx">             return fallback
</span><ins>+
+    def app_identifier_from_bundle(self, app_bundle):
+        plist_path = self._filesystem.join(app_bundle, 'Info.plist')
+        if not self._filesystem.exists(plist_path):
+            plist_path = self._filesystem.join(app_bundle, 'Contents', 'Info.plist')
+        if not self._filesystem.exists(plist_path):
+            return None
+        return self._executive.run_command(['/usr/libexec/PlistBuddy', '-c', 'Print CFBundleIdentifier', plist_path]).rstrip()
</ins></span></pre></div>
<a id="trunkToolsScriptswebkitpyportdriverpy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/port/driver.py (211369 => 211370)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/port/driver.py        2017-01-30 17:09:06 UTC (rev 211369)
+++ trunk/Tools/Scripts/webkitpy/port/driver.py        2017-01-30 17:31:47 UTC (rev 211370)
</span><span class="lines">@@ -138,7 +138,7 @@
</span><span class="cx">         self._driver_user_directory_suffix = None
</span><span class="cx">         self._driver_user_cache_directory = None
</span><span class="cx"> 
</span><del>-        # WebKitTestRunner/LayoutTestRelay can report back subprocess crashes by printing
</del><ins>+        # WebKitTestRunner can report back subprocess crashes by printing
</ins><span class="cx">         # &quot;#CRASHED - PROCESSNAME&quot;.  Since those can happen at any time and ServerProcess
</span><span class="cx">         # won't be aware of them (since the actual tool didn't crash, just a subprocess)
</span><span class="cx">         # we record the crashed subprocess name here.
</span><span class="lines">@@ -327,7 +327,6 @@
</span><span class="cx"> 
</span><span class="cx">     def _setup_environ_for_driver(self, environment):
</span><span class="cx">         build_root_path = str(self._port._build_path())
</span><del>-        # FIXME: DYLD_* variables should be Mac-only. Even iOS Simulator doesn't need them, as LayoutTestRelay is a host binary.
</del><span class="cx">         self._append_environment_variable_path(environment, 'DYLD_LIBRARY_PATH', build_root_path)
</span><span class="cx">         self._append_environment_variable_path(environment, '__XPC_DYLD_LIBRARY_PATH', build_root_path)
</span><span class="cx">         self._append_environment_variable_path(environment, 'DYLD_FRAMEWORK_PATH', build_root_path)
</span><span class="lines">@@ -369,7 +368,7 @@
</span><span class="cx">         environment = self._setup_environ_for_test()
</span><span class="cx">         self._crashed_process_name = None
</span><span class="cx">         self._crashed_pid = None
</span><del>-        self._server_process = self._port._server_process_constructor(self._port, self._server_name, self.cmd_line(pixel_tests, per_test_args), environment)
</del><ins>+        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)
</ins><span class="cx">         self._server_process.start()
</span><span class="cx"> 
</span><span class="cx">     def _run_post_start_tasks(self):
</span><span class="lines">@@ -611,27 +610,6 @@
</span><span class="cx">         return True
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-# FIXME: this should be abstracted out via the Port subclass somehow.
-class IOSSimulatorDriver(Driver):
-    def cmd_line(self, pixel_tests, per_test_args):
-        cmd = super(IOSSimulatorDriver, self).cmd_line(pixel_tests, per_test_args)
-        relay_tool = self._port.relay_path
-        dump_tool = cmd[0]
-        dump_tool_args = cmd[1:]
-        product_dir = self._port._build_path()
-        relay_args = [
-            '-developerDir', self._port.developer_dir,
-            '-udid', self._port.device_id_for_worker_number(self._worker_number),
-            '-productDir', product_dir,
-            '-app', dump_tool,
-        ]
-        return [relay_tool] + relay_args + ['--'] + dump_tool_args
-
-    def _setup_environ_for_driver(self, environment):
-        environment['DEVELOPER_DIR'] = self._port.developer_dir
-        return super(IOSSimulatorDriver, self)._setup_environ_for_driver(environment)
-
-
</del><span class="cx"> class ContentBlock(object):
</span><span class="cx">     def __init__(self):
</span><span class="cx">         self.content_type = None
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpyportdriver_unittestpy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/port/driver_unittest.py (211369 => 211370)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/port/driver_unittest.py        2017-01-30 17:09:06 UTC (rev 211369)
+++ trunk/Tools/Scripts/webkitpy/port/driver_unittest.py        2017-01-30 17:31:47 UTC (rev 211370)
</span><span class="lines">@@ -264,7 +264,7 @@
</span><span class="cx"> 
</span><span class="cx">     def test_stop_cleans_up_properly(self):
</span><span class="cx">         port = TestWebKitPort()
</span><del>-        port._server_process_constructor = MockServerProcess
</del><ins>+        port._test_runner_process_constructor = MockServerProcess
</ins><span class="cx">         driver = Driver(port, 0, pixel_tests=True)
</span><span class="cx">         driver.start(True, [])
</span><span class="cx">         last_tmpdir = port._filesystem.last_tmpdir
</span><span class="lines">@@ -274,7 +274,7 @@
</span><span class="cx"> 
</span><span class="cx">     def test_two_starts_cleans_up_properly(self):
</span><span class="cx">         port = TestWebKitPort()
</span><del>-        port._server_process_constructor = MockServerProcess
</del><ins>+        port._test_runner_process_constructor = MockServerProcess
</ins><span class="cx">         driver = Driver(port, 0, pixel_tests=True)
</span><span class="cx">         driver.start(True, [])
</span><span class="cx">         last_tmpdir = port._filesystem.last_tmpdir
</span><span class="lines">@@ -283,7 +283,7 @@
</span><span class="cx"> 
</span><span class="cx">     def test_start_actually_starts(self):
</span><span class="cx">         port = TestWebKitPort()
</span><del>-        port._server_process_constructor = MockServerProcess
</del><ins>+        port._test_runner_process_constructor = MockServerProcess
</ins><span class="cx">         driver = Driver(port, 0, pixel_tests=True)
</span><span class="cx">         driver.start(True, [])
</span><span class="cx">         self.assertTrue(driver._server_process.started)
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpyportiospy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/port/ios.py (211369 => 211370)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/port/ios.py        2017-01-30 17:09:06 UTC (rev 211369)
+++ trunk/Tools/Scripts/webkitpy/port/ios.py        2017-01-30 17:31:47 UTC (rev 211370)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx"> from webkitpy.port import config as port_config
</span><span class="cx"> from webkitpy.port import driver, image_diff
</span><span class="cx"> from webkitpy.port.darwin import DarwinPort
</span><ins>+from webkitpy.port.simulator_process import SimulatorProcess
</ins><span class="cx"> from webkitpy.xcode.simulator import Simulator, Runtime, DeviceType
</span><span class="cx"> from webkitpy.common.system.crashlogs import CrashLogs
</span><span class="cx"> 
</span><span class="lines">@@ -78,7 +79,6 @@
</span><span class="cx">     SDK = 'iphonesimulator'
</span><span class="cx"> 
</span><span class="cx">     SIMULATOR_BUNDLE_ID = 'com.apple.iphonesimulator'
</span><del>-    relay_name = 'LayoutTestRelay'
</del><span class="cx">     SIMULATOR_DIRECTORY = &quot;/tmp/WebKitTestingSimulators/&quot;
</span><span class="cx">     LSREGISTER_PATH = &quot;/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Versions/Current/Support/lsregister&quot;
</span><span class="cx">     PROCESS_COUNT_ESTIMATE_PER_SIMULATOR_INSTANCE = 100
</span><span class="lines">@@ -97,6 +97,7 @@
</span><span class="cx"> 
</span><span class="cx">     def __init__(self, host, port_name, **kwargs):
</span><span class="cx">         DarwinPort.__init__(self, host, port_name, **kwargs)
</span><ins>+        self._test_runner_process_constructor = SimulatorProcess
</ins><span class="cx"> 
</span><span class="cx">         optional_device_class = self.get_option('device_class')
</span><span class="cx">         self._printing_cmd_line = False
</span><span class="lines">@@ -148,17 +149,7 @@
</span><span class="cx">             device_type = DeviceType.from_name(device_name)
</span><span class="cx">         return device_type
</span><span class="cx"> 
</span><del>-    @property
</del><span class="cx">     @memoized
</span><del>-    def relay_path(self):
-        if self._root_was_set:
-            path = self._filesystem.abspath(self.get_option('root'))
-        else:
-            mac_config = port_config.Config(self._executive, self._filesystem, 'mac')
-            path = mac_config.build_directory(self.get_option('configuration'))
-        return self._filesystem.join(path, self.relay_name)
-
-    @memoized
</del><span class="cx">     def child_processes(self):
</span><span class="cx">         return int(self.get_option('child_processes'))
</span><span class="cx"> 
</span><span class="lines">@@ -182,19 +173,6 @@
</span><span class="cx"> 
</span><span class="cx">         return min(maximum_simulator_count_on_this_system, best_child_process_count_for_cpu)
</span><span class="cx"> 
</span><del>-    def _check_relay(self):
-        if not self._filesystem.exists(self.relay_path):
-            _log.error(&quot;%s was not found at %s&quot; % (self.relay_name, self.relay_path))
-            return False
-        return True
-
-    def _check_port_build(self):
-        if not self._root_was_set and self.get_option('build') and not self._build_relay():
-            return False
-        if not self._check_relay():
-            return False
-        return True
-
</del><span class="cx">     def _get_crash_log(self, name, pid, stdout, stderr, newer_than, time_fn=time.time, sleep_fn=time.sleep, wait_for_log=True):
</span><span class="cx">         time_fn = time_fn or time.time
</span><span class="cx">         sleep_fn = sleep_fn or time.sleep
</span><span class="lines">@@ -215,7 +193,7 @@
</span><span class="cx">             return self._get_crash_log(crashed_subprocess_name_and_pid[0], crashed_subprocess_name_and_pid[1], stdout,
</span><span class="cx">                 '\n'.join(stderr_lines), newer_than, time_fn, sleep_fn, wait_for_log)
</span><span class="cx"> 
</span><del>-        # LayoutTestRelay crashed
</del><ins>+        # App crashed
</ins><span class="cx">         _log.debug('looking for crash log for %s:%s' % (name, str(pid)))
</span><span class="cx">         crash_log = ''
</span><span class="cx">         crash_logs = CrashLogs(self.host)
</span><span class="lines">@@ -233,25 +211,6 @@
</span><span class="cx">             return stderr, None
</span><span class="cx">         return stderr, crash_log
</span><span class="cx"> 
</span><del>-    def _build_relay(self):
-        environment = self.host.copy_current_environment()
-        environment.disable_gcc_smartquotes()
-        env = environment.to_dictionary()
-
-        try:
-            # FIXME: We should be passing _arguments_for_configuration(), which respects build configuration and port,
-            # instead of hardcoding --ios-simulator.
-            self._run_script(&quot;build-layouttestrelay&quot;, args=[&quot;--ios-simulator&quot;], env=env)
-        except ScriptError, e:
-            _log.error(e.message_with_output(output_limit=None))
-            return False
-        return True
-
-    def _build_driver(self):
-        built_tool = super(IOSSimulatorPort, self)._build_driver()
-        built_relay = self._build_relay()
-        return built_tool and built_relay
-
</del><span class="cx">     def _build_driver_flags(self):
</span><span class="cx">         archs = ['ARCHS=i386'] if self.architecture() == 'x86' else []
</span><span class="cx">         sdk = ['--sdk', 'iphonesimulator']
</span><span class="lines">@@ -264,9 +223,6 @@
</span><span class="cx">                 configurations.append(TestConfiguration(version=self._version, architecture=architecture, build_type=build_type))
</span><span class="cx">         return configurations
</span><span class="cx"> 
</span><del>-    def _driver_class(self):
-        return driver.IOSSimulatorDriver
-
</del><span class="cx">     def default_baseline_search_path(self):
</span><span class="cx">         if self.get_option('webkit_test_runner'):
</span><span class="cx">             fallback_names = [self._wk2_port_name()] + [self.port_name] + ['wk2']
</span><span class="lines">@@ -280,9 +236,9 @@
</span><span class="cx"> 
</span><span class="cx">     def _create_simulators(self):
</span><span class="cx">         if (self.default_child_processes() &lt; self.child_processes()):
</span><del>-                _log.warn(&quot;You have specified very high value({0}) for --child-processes&quot;.format(self.child_processes()))
-                _log.warn(&quot;maximum child-processes which can be supported on this system are: {0}&quot;.format(self.default_child_processes()))
-                _log.warn(&quot;This is very likely to fail.&quot;)
</del><ins>+            _log.warn('You have specified very high value({0}) for --child-processes'.format(self.child_processes()))
+            _log.warn('maximum child-processes which can be supported on this system are: {0}'.format(self.default_child_processes()))
+            _log.warn('This is very likely to fail.')
</ins><span class="cx"> 
</span><span class="cx">         if self._using_dedicated_simulators():
</span><span class="cx">             self._createSimulatorApps()
</span><span class="lines">@@ -413,7 +369,7 @@
</span><span class="cx">     def _testing_device(self, number):
</span><span class="cx">         return Simulator.device_number(number)
</span><span class="cx"> 
</span><del>-    # This is only exposed so that IOSSimulatorDriver can use it.
</del><ins>+    # FIXME: This is only exposed so that SimulatorProcess can use it.
</ins><span class="cx">     def device_id_for_worker_number(self, number):
</span><span class="cx">         if self._printing_cmd_line:
</span><span class="cx">             return '&lt;dummy id&gt;'
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpyportios_unittestpy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/port/ios_unittest.py (211369 => 211370)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/port/ios_unittest.py        2017-01-30 17:09:06 UTC (rev 211369)
+++ trunk/Tools/Scripts/webkitpy/port/ios_unittest.py        2017-01-30 17:31:47 UTC (rev 211370)
</span><span class="lines">@@ -70,7 +70,7 @@
</span><span class="cx">         port._run_script = run_script
</span><span class="cx">         self.assertEqual(port.architecture(), 'x86')
</span><span class="cx">         port._build_driver()
</span><del>-        self.assertEqual(self.args, ['--ios-simulator'])
</del><ins>+        self.assertEqual(self.args, ['ARCHS=i386', '--sdk', 'iphonesimulator'])
</ins><span class="cx"> 
</span><span class="cx">     def test_64bit(self):
</span><span class="cx">         # Apple Mac port is 64-bit by default
</span><span class="lines">@@ -82,7 +82,7 @@
</span><span class="cx"> 
</span><span class="cx">         port._run_script = run_script
</span><span class="cx">         port._build_driver()
</span><del>-        self.assertEqual(self.args, ['--ios-simulator'])
</del><ins>+        self.assertEqual(self.args, ['--sdk', 'iphonesimulator'])
</ins><span class="cx"> 
</span><span class="cx">     def test_sdk_name(self):
</span><span class="cx">         port = self.make_port()
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpyportserver_processpy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/port/server_process.py (211369 => 211370)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/port/server_process.py        2017-01-30 17:09:06 UTC (rev 211369)
+++ trunk/Tools/Scripts/webkitpy/port/server_process.py        2017-01-30 17:31:47 UTC (rev 211370)
</span><span class="lines">@@ -1,3 +1,4 @@
</span><ins>+# Copyright (C) 2017 Apple Inc. All rights reserved.
</ins><span class="cx"> # Copyright (C) 2010 Google Inc. All rights reserved.
</span><span class="cx"> #
</span><span class="cx"> # Redistribution and use in source and binary forms, with or without
</span><span class="lines">@@ -59,7 +60,7 @@
</span><span class="cx">     indefinitely. The class also handles transparently restarting processes
</span><span class="cx">     as necessary to keep issuing commands.&quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False, treat_no_data_as_crash=False):
</del><ins>+    def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False, treat_no_data_as_crash=False, worker_number=None):
</ins><span class="cx">         self._port = port_obj
</span><span class="cx">         self._name = name  # Should be the command name (e.g. DumpRenderTree, ImageDiff)
</span><span class="cx">         self._cmd = cmd
</span><span class="lines">@@ -103,6 +104,11 @@
</span><span class="cx">     def process_name(self):
</span><span class="cx">         return self._name
</span><span class="cx"> 
</span><ins>+    @staticmethod
+    def _set_file_nonblocking(file):
+        flags = fcntl.fcntl(file.fileno(), fcntl.F_GETFL)
+        fcntl.fcntl(file.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
+
</ins><span class="cx">     def _start(self):
</span><span class="cx">         if self._proc:
</span><span class="cx">             raise ValueError(&quot;%s already running&quot; % self._name)
</span><span class="lines">@@ -117,13 +123,9 @@
</span><span class="cx">             universal_newlines=self._universal_newlines)
</span><span class="cx">         self._pid = self._proc.pid
</span><span class="cx">         self._port.find_system_pid(self.name(), self._pid)
</span><del>-        fd = self._proc.stdout.fileno()
</del><span class="cx">         if not self._use_win32_apis:
</span><del>-            fl = fcntl.fcntl(fd, fcntl.F_GETFL)
-            fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
-            fd = self._proc.stderr.fileno()
-            fl = fcntl.fcntl(fd, fcntl.F_GETFL)
-            fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
</del><ins>+            self._set_file_nonblocking(self._proc.stdout)
+            self._set_file_nonblocking(self._proc.stderr)
</ins><span class="cx"> 
</span><span class="cx">     def _handle_possible_interrupt(self):
</span><span class="cx">         &quot;&quot;&quot;This routine checks to see if the process crashed or exited
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpyportserver_process_mockpy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/port/server_process_mock.py (211369 => 211370)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/port/server_process_mock.py        2017-01-30 17:09:06 UTC (rev 211369)
+++ trunk/Tools/Scripts/webkitpy/port/server_process_mock.py        2017-01-30 17:31:47 UTC (rev 211370)
</span><span class="lines">@@ -28,7 +28,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> class MockServerProcess(object):
</span><del>-    def __init__(self, port_obj=None, name=None, cmd=None, env=None, universal_newlines=False, lines=None, crashed=False):
</del><ins>+    def __init__(self, port_obj=None, name=None, cmd=None, env=None, universal_newlines=False, lines=None, crashed=False, worker_number=None):
</ins><span class="cx">         self.timed_out = False
</span><span class="cx">         self.lines = lines or []
</span><span class="cx">         self.crashed = crashed
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpyportsimulator_processpy"></a>
<div class="addfile"><h4>Added: trunk/Tools/Scripts/webkitpy/port/simulator_process.py (0 => 211370)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/port/simulator_process.py                                (rev 0)
+++ trunk/Tools/Scripts/webkitpy/port/simulator_process.py        2017-01-30 17:31:47 UTC (rev 211370)
</span><span class="lines">@@ -0,0 +1,134 @@
</span><ins>+# Copyright (C) 2017 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+import errno
+import os
+import signal
+import time
+
+from webkitpy.port.server_process import ServerProcess
+from webkitpy.xcode.simulator import Simulator
+
+
+class SimulatorProcess(ServerProcess):
+
+    class Popen(object):
+
+        def __init__(self, pid, stdin, stdout, stderr):
+            self.stdin = stdin
+            self.stdout = stdout
+            self.stderr = stderr
+            self.pid = pid
+            self.returncode = None
+
+        def poll(self):
+            if self.returncode:
+                return self.returncode
+            try:
+                os.kill(self.pid, 0)
+            except OSError, err:
+                assert err.errno == errno.ESRCH
+                self.returncode = 1
+            return self.returncode
+
+        def wait(self):
+            while not self.poll():
+                time.sleep(0.01)  # In seconds
+            return self.returncode
+
+    def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False, treat_no_data_as_crash=False, worker_number=None):
+        self._bundle_id = port_obj.app_identifier_from_bundle(cmd[0])
+        self._device = Simulator(port_obj.host).find_device_by_udid(port_obj.device_id_for_worker_number(worker_number))
+        if not self._device.install_app(cmd[0]):
+            raise RuntimeError('Failed to install app {} on simulator device {}'.format(cmd[0], self._device.udid))
+        env['IPC_IDENTIFIER'] = self._bundle_id + '-' + self._device.udid
+
+        # This location matches the location used by WebKitTestRunner and DumpRenderTree
+        # for the other side of these fifos.
+        file_location = '/tmp/' + env['IPC_IDENTIFIER']
+        self._in_path = file_location + '_IN'
+        self._out_path = file_location + '_OUT'
+        self._error_path = file_location + '_ERROR'
+
+        super(SimulatorProcess, self).__init__(port_obj, name, cmd, env, universal_newlines, treat_no_data_as_crash)
+
+    def _reset(self):
+        super(SimulatorProcess, self)._reset()
+
+        # Unlinks are needed on reset in the event that the Python code unexpectedly
+        # fails between _start() and kill().  This can be caused by a SIGKILL or a crash.
+        # This ensures that os.mkfifo() will not be obstructed by previous fifos.
+        # Other files will still cause os.mkfifo() to fail.
+        try:
+            os.unlink(self._in_path)
+        except:
+            pass
+        try:
+            os.unlink(self._out_path)
+        except:
+            pass
+        try:
+            os.unlink(self._error_path)
+        except:
+            pass
+
+    def _start(self):
+        if self._proc:
+            raise ValueError('{} already running'.format(self._name))
+        self._reset()
+
+        FIFO_PERMISSION_FLAGS = 0600  # Only owner can read and write
+        os.mkfifo(self._in_path, FIFO_PERMISSION_FLAGS)
+        os.mkfifo(self._out_path, FIFO_PERMISSION_FLAGS)
+        os.mkfifo(self._error_path, FIFO_PERMISSION_FLAGS)
+
+        stdout = os.fdopen(os.open(self._out_path, os.O_RDONLY | os.O_NONBLOCK), 'rb')
+        stderr = os.fdopen(os.open(self._error_path, os.O_RDONLY | os.O_NONBLOCK), 'rb')
+
+        self._pid = self._device.launch_app(self._bundle_id, self._cmd[1:], env=self._env)
+
+        def handler(signum, frame):
+            assert signum == signal.SIGALRM
+            raise Exception('Timed out waiting for process to open {}'.format(self._in_path))
+        signal.signal(signal.SIGALRM, handler)
+        signal.alarm(3)  # In seconds
+
+        stdin = None
+        try:
+            stdin = open(self._in_path, 'w', 0)  # Opening with no buffering, like popen
+        except:
+            # We set self._proc as _reset() and _kill() depend on it.
+            self._proc = SimulatorProcess.Popen(self._pid, stdin, stdout, stderr)
+            if self._proc.poll() is not None:
+                self._reset()
+                raise Exception('App {} crashed before stdin could be attached'.format(os.path.basename(self._cmd[0])))
+            self._kill()
+            self._reset()
+            raise
+        signal.alarm(0)  # Cancel alarm
+
+        self._proc = SimulatorProcess.Popen(self._pid, stdin, stdout, stderr)
+
+    def _kill(self):
+        self._device.terminate_app(self._bundle_id)
+        super(SimulatorProcess, self)._kill()
</ins></span></pre></div>
<a id="trunkToolsScriptswebkitpyxcodesimulatorpy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/xcode/simulator.py (211369 => 211370)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/xcode/simulator.py        2017-01-30 17:09:06 UTC (rev 211369)
+++ trunk/Tools/Scripts/webkitpy/xcode/simulator.py        2017-01-30 17:31:47 UTC (rev 211370)
</span><span class="lines">@@ -167,7 +167,7 @@
</span><span class="cx">     Represents a CoreSimulator device underneath a runtime
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def __init__(self, name, udid, available, runtime):
</del><ins>+    def __init__(self, name, udid, available, runtime, host):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         :param name: The device name
</span><span class="cx">         :type name: str
</span><span class="lines">@@ -177,7 +177,10 @@
</span><span class="cx">         :type available: bool
</span><span class="cx">         :param runtime: The iOS Simulator runtime that hosts this device
</span><span class="cx">         :type runtime: Runtime
</span><ins>+        :param host: The host which can run command line commands
+        :type host: Host
</ins><span class="cx">         &quot;&quot;&quot;
</span><ins>+        self._host = host
</ins><span class="cx">         self.name = name
</span><span class="cx">         self.udid = udid
</span><span class="cx">         self.available = available
</span><span class="lines">@@ -260,6 +263,31 @@
</span><span class="cx">         except subprocess.CalledProcessError:
</span><span class="cx">             raise RuntimeError('&quot;xcrun simctl erase&quot; failed: device state is {}'.format(Simulator.device_state(udid)))
</span><span class="cx"> 
</span><ins>+    def install_app(self, app_path):
+        return not self._host.executive.run_command(['xcrun', 'simctl', 'install', self.udid, app_path], return_exit_code=True)
+
+    def launch_app(self, bundle_id, args, env=None):
+        environment_to_use = {}
+        SIMCTL_ENV_PREFIX = 'SIMCTL_CHILD_'
+        for value in (env or {}):
+            if not value.startswith(SIMCTL_ENV_PREFIX):
+                environment_to_use[SIMCTL_ENV_PREFIX + value] = env[value]
+            else:
+                environment_to_use[value] = env[value]
+
+        output = self._host.executive.run_command(
+            ['xcrun', 'simctl', 'launch', self.udid, bundle_id] + args,
+            env=environment_to_use,
+        )
+
+        match = re.match(r'(?P&lt;bundle&gt;[^:]+): (?P&lt;pid&gt;\d+)\n', output)
+        if not match or match.group('bundle') != bundle_id:
+            raise RuntimeError('Failed to find process id for {}: {}'.format(bundle_id, output))
+        return int(match.group('pid'))
+
+    def terminate_app(self, bundle_id):
+        return not self._host.executive.run_command(['xcrun', 'simctl', 'terminate', self.udid, bundle_id], return_exit_code=True)
+
</ins><span class="cx">     def __eq__(self, other):
</span><span class="cx">         return self.udid == other.udid
</span><span class="cx"> 
</span><span class="lines">@@ -477,7 +505,8 @@
</span><span class="cx">                 device = Device(name=device_match.group('name').rstrip(),
</span><span class="cx">                                 udid=device_match.group('udid'),
</span><span class="cx">                                 available=device_match.group('availability') is None,
</span><del>-                                runtime=current_runtime)
</del><ins>+                                runtime=current_runtime,
+                                host=self._host)
</ins><span class="cx">                 current_runtime.devices.append(device)
</span><span class="cx"> 
</span><span class="cx">     def device_type(self, name=None, identifier=None):
</span></span></pre>
</div>
</div>

</body>
</html>