LayoutTestRelay does not install DumpRenderTree.app/WebKitTestRunnerApp.app
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 5 Feb 2015 22:46:30 +0000 (22:46 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 5 Feb 2015 22:46:30 +0000 (22:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=139746
<rdar://problem/19283658>

Reviewed by Alexey Proskuryakov.

Fixes an issues where LayoutTestRelay may fail to install DumpRenderTree.app/WebKitTestRunnerApp.app
if the simulator device is not in state Booted.

Currently run-webkit-test --ios-sim executes LayoutTestRelay immediately after
launching/relaunching the iOS Simulator app and a simulator app can only be installed
on a device that is in the Booted state. LayoutTestRelay may run before the
device is booted and hence fail to install DumpRenderTree.app/WebKitTestRunnerApp.app.
We should defer executing LayoutTestRelay until the simulator device booted by
iOS Simulator is in the Booted state.

* Scripts/webkitpy/port/ios.py: Import webkitpy.xcode.simulator.Simulator to avoid prefixing
Simulator methods with the module name, simulator.
(IOSSimulatorPort.setup_test_run): Wait for the simulator device to be in the Booted state
after launching iOS Simulator. Also, wait until the simulator device is in the Shutdown state
before launching iOS Simulator to boot it.
(IOSSimulatorPort.testing_device): Fix up caller since we now import webkitpy.xcode.simulator.Simulator.
(IOSSimulatorPort.simulator_path): Deleted; moved this function to class Simulator and renamed to device_directory().
* Scripts/webkitpy/xcode/simulator.py:
(Device.__init__): Remove parameter state and an instance variable of the same name, which represented
the state of the device when we created this object as part of parsing the output of `simctl list`. Callers
interested in the state of the device are more likely interested in the current state of the device as
opposed to the state of the device when the Device object was created.
(Device.state): Added; turn around and call Simulator.device_state() for the current state of the device.
(Device.path): Extracted implementation into Simulator.device_directory() so that it can be called
from both this function and Simulator.device_state().
(Device.create): Use Simulator.wait_until_device_is_in_state() to simplify the implementation of this function.
(Simulator.DeviceState): Added; class of constants.
(Simulator.wait_until_device_is_in_state): Added; this function does not return until the specified
device is in the specified state.
(Simulator.device_state): Added; parses the state of the device from the appropriate CoreSimulator device.plist file.
(Simulator.device_directory): Added.
(Simulator._parse_devices): Do not pass argument state to Device constructor as it no longer accepts it.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@179712 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/ChangeLog
Tools/Scripts/webkitpy/port/ios.py
Tools/Scripts/webkitpy/xcode/simulator.py

index e6ce05b..85a63ef 100644 (file)
@@ -1,3 +1,44 @@
+2015-02-05  Daniel Bates  <dabates@apple.com>
+
+        LayoutTestRelay does not install DumpRenderTree.app/WebKitTestRunnerApp.app
+        https://bugs.webkit.org/show_bug.cgi?id=139746
+        <rdar://problem/19283658>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Fixes an issues where LayoutTestRelay may fail to install DumpRenderTree.app/WebKitTestRunnerApp.app
+        if the simulator device is not in state Booted.
+
+        Currently run-webkit-test --ios-sim executes LayoutTestRelay immediately after
+        launching/relaunching the iOS Simulator app and a simulator app can only be installed
+        on a device that is in the Booted state. LayoutTestRelay may run before the
+        device is booted and hence fail to install DumpRenderTree.app/WebKitTestRunnerApp.app.
+        We should defer executing LayoutTestRelay until the simulator device booted by
+        iOS Simulator is in the Booted state.
+
+        * Scripts/webkitpy/port/ios.py: Import webkitpy.xcode.simulator.Simulator to avoid prefixing
+        Simulator methods with the module name, simulator.
+        (IOSSimulatorPort.setup_test_run): Wait for the simulator device to be in the Booted state
+        after launching iOS Simulator. Also, wait until the simulator device is in the Shutdown state
+        before launching iOS Simulator to boot it.
+        (IOSSimulatorPort.testing_device): Fix up caller since we now import webkitpy.xcode.simulator.Simulator.
+        (IOSSimulatorPort.simulator_path): Deleted; moved this function to class Simulator and renamed to device_directory().
+        * Scripts/webkitpy/xcode/simulator.py:
+        (Device.__init__): Remove parameter state and an instance variable of the same name, which represented
+        the state of the device when we created this object as part of parsing the output of `simctl list`. Callers
+        interested in the state of the device are more likely interested in the current state of the device as
+        opposed to the state of the device when the Device object was created.
+        (Device.state): Added; turn around and call Simulator.device_state() for the current state of the device.
+        (Device.path): Extracted implementation into Simulator.device_directory() so that it can be called
+        from both this function and Simulator.device_state().
+        (Device.create): Use Simulator.wait_until_device_is_in_state() to simplify the implementation of this function.
+        (Simulator.DeviceState): Added; class of constants.
+        (Simulator.wait_until_device_is_in_state): Added; this function does not return until the specified
+        device is in the specified state.
+        (Simulator.device_state): Added; parses the state of the device from the appropriate CoreSimulator device.plist file.
+        (Simulator.device_directory): Added.
+        (Simulator._parse_devices): Do not pass argument state to Device constructor as it no longer accepts it.
+
 2015-02-05  Alexey Proskuryakov  <ap@apple.com>
 
         Disable retries on Mac debug testers
index 5b6c11e..25928eb 100644 (file)
@@ -36,7 +36,7 @@ from webkitpy.port import driver, image_diff
 from webkitpy.port.base import Port
 from webkitpy.port.leakdetector import LeakDetector
 from webkitpy.port import config as port_config
-from webkitpy.xcode import simulator
+from webkitpy.xcode.simulator import Simulator
 
 
 _log = logging.getLogger(__name__)
@@ -179,10 +179,12 @@ class IOSSimulatorPort(Port):
 
     def setup_test_run(self):
         self._executive.run_command(['osascript', '-e', 'tell application "iOS Simulator" to quit'])
-        time.sleep(2)
+        device_udid = self.testing_device.udid
+        Simulator.wait_until_device_is_in_state(device_udid, Simulator.DeviceState.SHUTDOWN)
         self._executive.run_command([
             'open', '-a', os.path.join(self.developer_dir, 'Applications', 'iOS Simulator.app'),
-            '--args', '-CurrentDeviceUDID', self.testing_device.udid])
+            '--args', '-CurrentDeviceUDID', device_udid])
+        Simulator.wait_until_device_is_in_state(device_udid, Simulator.DeviceState.BOOTED)
 
     def clean_up_test_run(self):
         super(IOSSimulatorPort, self).clean_up_test_run()
@@ -287,13 +289,9 @@ class IOSSimulatorPort(Port):
 
         device_type = self.get_option('device_type')
         runtime = self.get_option('runtime')
-        self._testing_device = simulator.Simulator().lookup_or_create_device(device_type.name + ' WebKit Tester', device_type, runtime)
+        self._testing_device = Simulator().lookup_or_create_device(device_type.name + ' WebKit Tester', device_type, runtime)
         return self.testing_device
 
-    def simulator_path(self, udid):
-        if udid:
-            return os.path.realpath(os.path.expanduser(os.path.join('~/Library/Developer/CoreSimulator/Devices', udid)))
-
     def look_for_new_crash_logs(self, crashed_processes, start_time):
         crash_logs = {}
         for (test_name, process_name, pid) in crashed_processes:
index 94f99d3..bbc9a82 100644 (file)
@@ -1,6 +1,7 @@
 import itertools
 import logging
 import os
+import plistlib
 import re
 import subprocess
 import time
@@ -14,7 +15,6 @@ If possible, use real CoreSimulator.framework functionality by linking to the fr
 Do not use PyObjC to dlopen the framework.
 """
 
-
 class DeviceType(object):
     """
     Represents a CoreSimulator device type.
@@ -140,14 +140,12 @@ class Device(object):
     Represents a CoreSimulator device underneath a runtime
     """
 
-    def __init__(self, name, udid, state, available, runtime):
+    def __init__(self, name, udid, available, runtime):
         """
         :param name: The device name
         :type name: str
         :param udid: The device UDID (a UUID string)
         :type udid: str
-        :param state: The last known device state
-        :type state: str
         :param available: Whether the device is available for use.
         :type available: bool
         :param runtime: The iOS Simulator runtime that hosts this device
@@ -155,19 +153,24 @@ class Device(object):
         """
         self.name = name
         self.udid = udid
-        self.state = state
         self.available = available
         self.runtime = runtime
 
     @property
+    def state(self):
+        """
+        :returns: The current state of the device.
+        :rtype: Simulator.DeviceState
+        """
+        return Simulator.device_state(self.udid)
+
+    @property
     def path(self):
         """
         :returns: The filesystem path that contains the simulator device's data.
         :rtype: str
         """
-        return os.path.realpath(
-            os.path.expanduser(
-                os.path.join('~/Library/Developer/CoreSimulator/Devices', self.udid)))
+        return Simulator.device_directory(self.udid)
 
     @classmethod
     def create(cls, name, device_type, runtime):
@@ -182,16 +185,9 @@ class Device(object):
         :return: The new device or raises a CalledProcessError if ``simctl create`` failed.
         :rtype: Device
         """
-        sim = Simulator()
         device_udid = subprocess.check_output(['xcrun', 'simctl', 'create', name, device_type.identifier, runtime.identifier]).rstrip()
-        assert(device_udid)
-        while True:
-            sim.refresh()
-            device = sim.find_device_by_udid(device_udid)
-            if not device or device.state == 'Creating':
-                time.sleep(2)
-                continue
-            return device
+        Simulator.wait_until_device_is_in_state(device_udid, Simulator.DeviceState.SHUTDOWN)
+        return Simulator().find_device_by_udid(device_udid)
 
     def __eq__(self, other):
         return self.udid == other.udid
@@ -228,6 +224,32 @@ class Simulator(object):
         self.device_types = []
         self.refresh()
 
+    # Keep these constants synchronized with the SimDeviceState constants in CoreSimulator/SimDevice.h.
+    class DeviceState:
+        DOES_NOT_EXIST = -1
+        CREATING = 0
+        SHUTDOWN = 1
+        BOOTING = 2
+        BOOTED = 3
+        SHUTTING_DOWN = 4
+
+    @staticmethod
+    def wait_until_device_is_in_state(udid, wait_until_state):
+        # FIXME: Implement support for a timed wait.
+        while (Simulator.device_state(udid) != wait_until_state):
+            time.sleep(0.5)
+
+    @staticmethod
+    def device_state(udid):
+        device_plist = os.path.join(Simulator.device_directory(udid), 'device.plist')
+        if not os.path.isfile(device_plist):
+            return Simulator.DeviceState.DOES_NOT_EXIST
+        return plistlib.readPlist(device_plist)['state']
+
+    @staticmethod
+    def device_directory(udid):
+        return os.path.realpath(os.path.expanduser(os.path.join('~/Library/Developer/CoreSimulator/Devices', udid)))
+
     def refresh(self):
         """
         Refresh runtime and device type information from ``simctl list``.
@@ -305,7 +327,6 @@ class Simulator(object):
                 raise RuntimeError('Expected an iOS Simulator device line, got "{}"'.format(line))
             device = Device(name=device_match.group('name').rstrip(),
                             udid=device_match.group('udid'),
-                            state=device_match.group('state'),
                             available=device_match.group('availability') is None,
                             runtime=current_runtime)
             current_runtime.devices.append(device)