Title: [173937] trunk/Tools
Revision
173937
Author
[email protected]
Date
2014-09-24 16:28:13 -0700 (Wed, 24 Sep 2014)

Log Message

[iOS] run-webkit-tests should support minor versions under devices and create a testing device under the right runtime
https://bugs.webkit.org/show_bug.cgi?id=136895

Reviewed by David Kilzer.

Create Device, DeviceType, and Runtime data classes.
Create Simulator class represent simctl output.

Wherever possible, use structured data classes anywhere a raw
identifier or UDID string was used for a cleaner implementation
and to encapsulate the inherent fragility of scraping simctl output.

Create a suitably named testing device if one doesn't exist.

Finally, accurately handle having multiple simulator runtimes (SDKs)
installed in the active Xcode.app bundle.

* Scripts/webkitpy/layout_tests/run_webkit_tests.py:
(_set_up_derived_options):
* Scripts/webkitpy/port/driver.py:
(IOSSimulatorDriver.cmd_line):
Construct DeviceType and Runtime objects from identifiers passed at the
command line, still providing sensible defaults for 32- and 64-bit testing.
* Scripts/webkitpy/port/ios.py:
(IOSSimulatorPort.__init__):
(IOSSimulatorPort.setup_test_run):
(IOSSimulatorPort):
(IOSSimulatorPort.testing_device):
Cache the testing device once it is created or found.
(IOSSimulatorPort.reset_preferences):
Get the device path from the Device object instead of consructing it
in the port class.
(IOSSimulatorPort.simulator_udid): Deleted.
Get the UDID from the testing_device :: Device object itself.
* Scripts/webkitpy/xcode/simulator.py:
Created Device, DeviceType, Runtime, and Simulator classes.
(get_runtimes): Deleted.
(get_devices): Deleted.
(get_device_types): Deleted.
(get_latest_runtime): Deleted.

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (173936 => 173937)


--- trunk/Tools/ChangeLog	2014-09-24 23:11:46 UTC (rev 173936)
+++ trunk/Tools/ChangeLog	2014-09-24 23:28:13 UTC (rev 173937)
@@ -1,3 +1,46 @@
+2014-09-23  David Farler  <[email protected]>
+
+        [iOS] run-webkit-tests should support minor versions under devices and create a testing device under the right runtime
+        https://bugs.webkit.org/show_bug.cgi?id=136895
+
+        Reviewed by David Kilzer.
+
+        Create Device, DeviceType, and Runtime data classes.
+        Create Simulator class represent simctl output.
+
+        Wherever possible, use structured data classes anywhere a raw
+        identifier or UDID string was used for a cleaner implementation
+        and to encapsulate the inherent fragility of scraping simctl output.
+
+        Create a suitably named testing device if one doesn't exist.
+
+        Finally, accurately handle having multiple simulator runtimes (SDKs)
+        installed in the active Xcode.app bundle.
+
+        * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+        (_set_up_derived_options):
+        * Scripts/webkitpy/port/driver.py:
+        (IOSSimulatorDriver.cmd_line):
+        Construct DeviceType and Runtime objects from identifiers passed at the
+        command line, still providing sensible defaults for 32- and 64-bit testing.
+        * Scripts/webkitpy/port/ios.py:
+        (IOSSimulatorPort.__init__):
+        (IOSSimulatorPort.setup_test_run):
+        (IOSSimulatorPort):
+        (IOSSimulatorPort.testing_device):
+        Cache the testing device once it is created or found.
+        (IOSSimulatorPort.reset_preferences):
+        Get the device path from the Device object instead of consructing it
+        in the port class.
+        (IOSSimulatorPort.simulator_udid): Deleted.
+        Get the UDID from the testing_device :: Device object itself.
+        * Scripts/webkitpy/xcode/simulator.py:
+        Created Device, DeviceType, Runtime, and Simulator classes.
+        (get_runtimes): Deleted.
+        (get_devices): Deleted.
+        (get_device_types): Deleted.
+        (get_latest_runtime): Deleted.
+
 2014-09-24  Roger Fong  <[email protected]>
 
         [Windows] Tentative fix for Windows test bots.

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py (173936 => 173937)


--- trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py	2014-09-24 23:11:46 UTC (rev 173936)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py	2014-09-24 23:28:13 UTC (rev 173937)
@@ -394,10 +394,15 @@
     if options.platform == 'ios-simulator':
         from webkitpy import xcode
         if options.runtime is None:
-            options.runtime = xcode.simulator.get_latest_runtime()['identifier']
+            options.runtime = xcode.simulator.Simulator().latest_runtime
+        else:
+            options.runtime = xcode.simulator.Runtime.from_identifier(options.runtime)
         if options.device_type is None:
-            device_types = xcode.simulator.get_device_types()
-            options.device_type = device_types['iPhone 5'] if options.architecture == 'x86' else device_types['iPhone 5s']
+            iphone5 = xcode.simulator.DeviceType.from_name('iPhone 5')
+            iphone5s = xcode.simulator.DeviceType.from_name('iPhone 5s')
+            options.device_type = iphone5 if options.architecture == 'x86' else iphone5s
+        else:
+            options.device_type = xcode.simulator.DeviceType.from_identifier(options.device_type)
 
 
 def run(port, options, args, logging_stream):

Modified: trunk/Tools/Scripts/webkitpy/port/driver.py (173936 => 173937)


--- trunk/Tools/Scripts/webkitpy/port/driver.py	2014-09-24 23:11:46 UTC (rev 173936)
+++ trunk/Tools/Scripts/webkitpy/port/driver.py	2014-09-24 23:28:13 UTC (rev 173937)
@@ -511,8 +511,8 @@
         runtime = self._port.get_option('runtime')
         device_type = self._port.get_option('device_type')
         relay_args = [
-            '-runtime', runtime,
-            '-deviceType', device_type,
+            '-runtime', runtime.identifier,
+            '-deviceType', device_type.identifier,
             '-suffix', str(self._worker_number),
             '-productDir', product_dir,
             '-app', dump_tool,

Modified: trunk/Tools/Scripts/webkitpy/port/ios.py (173936 => 173937)


--- trunk/Tools/Scripts/webkitpy/port/ios.py	2014-09-24 23:11:46 UTC (rev 173936)
+++ trunk/Tools/Scripts/webkitpy/port/ios.py	2014-09-24 23:28:13 UTC (rev 173937)
@@ -35,6 +35,7 @@
 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
 
 
 _log = logging.getLogger(__name__)
@@ -67,6 +68,8 @@
         mac_config = port_config.Config(self._executive, self._filesystem, 'mac')
         self._mac_build_directory = mac_config.build_directory(self.get_option('configuration'))
 
+        self._testing_device = None
+
     def driver_name(self):
         if self.get_option('driver_name'):
             return self.get_option('driver_name')
@@ -157,7 +160,7 @@
         time.sleep(2)
         self._executive.run_command([
             'open', '-a', os.path.join(self.developer_dir, 'Applications', 'iOS Simulator.app'),
-            '--args', '-CurrentDeviceUDID', self.simulator_udid()])
+            '--args', '-CurrentDeviceUDID', self.testing_device.udid])
 
     def clean_up_test_run(self):
         super(IOSSimulatorPort, self).clean_up_test_run()
@@ -252,22 +255,16 @@
             return stderr, None
         return stderr, crash_log
 
-    def simulator_udid(self):
-        device_name = self.get_option('device_type').split('.')[-1].replace('-', ' ') + ' WebKit Tester'
-        stdout = subprocess.check_output(['xcrun', '--sdk', 'iphonesimulator', 'simctl', 'list'])
-        lines = stdout.splitlines()
-        try:
-            devices_index = lines.index('== Devices ==')
-            device_regex = re.compile('(?P<device_name>[^(]+) \((?P<udid>[^)]+)\) \((?P<state>[^)]+)\)')
-            for device_line in itertools.takewhile(lambda line: not line.startswith('=='), lines[devices_index + 1:]):
-                device = device_regex.match(device_line.lstrip().rstrip())
-                if not device:
-                    continue
-                if device.group('device_name') == device_name:
-                    return device.group('udid')
-        except ValueError:
-            pass
+    @property
+    def testing_device(self):
+        if self._testing_device is not None:
+            return self._testing_device
 
+        device_type = self.get_option('device_type')
+        runtime = self.get_option('runtime')
+        self._testing_device = simulator.Simulator().testing_device(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)))
@@ -323,9 +320,7 @@
         return self._image_differ.diff_image(expected_contents, actual_contents, tolerance)
 
     def reset_preferences(self):
-        simulator_path = self.simulator_path(self.simulator_udid())
-        if not simulator_path:
-            return
+        simulator_path = self.testing_device.path
         data_path = os.path.join(simulator_path, 'data')
         if os.path.isdir(data_path):
             shutil.rmtree(data_path)

Modified: trunk/Tools/Scripts/webkitpy/xcode/simulator.py (173936 => 173937)


--- trunk/Tools/Scripts/webkitpy/xcode/simulator.py	2014-09-24 23:11:46 UTC (rev 173936)
+++ trunk/Tools/Scripts/webkitpy/xcode/simulator.py	2014-09-24 23:28:13 UTC (rev 173937)
@@ -1,6 +1,12 @@
+import itertools
+import logging
+import os
+import re
 import subprocess
-import re
+import time
 
+_log = logging.getLogger(__name__)
+
 """
 Minimally wraps CoreSimulator functionality through simctl.
 
@@ -9,97 +15,389 @@
 """
 
 
-def get_runtimes(_only_available_=True):
+class DeviceType(object):
     """
-    Give a dictionary mapping
-    :return: A dictionary mapping iOS version string to runtime identifier.
-    :rtype: dict
+    Represents a CoreSimulator device type.
     """
-    runtimes = {}
-    runtime_re = re.compile(b'iOS (?P<version>[0-9]+\.[0-9]) \([0-9]+\.[0-9]+ - (?P<update>[^)]+)\) \((?P<identifier>[^)]+)\)( \((?P<unavailable>[^)]+)\))?')
-    stdout = subprocess.check_output(['xcrun', '-sdk', 'iphonesimulator', 'simctl', 'list', 'runtimes'])
-    lines = iter(stdout.splitlines())
-    header = next(lines)
-    if header != '== Runtimes ==':
-        return None
+    def __init__(self, name, identifier):
+        """
+        :param name: The device type's human-readable name
+        :type name: str
+        :param identifier: The CoreSimulator identifier.
+        :type identifier: str
+        """
+        self.name = name
+        self.identifier = identifier
 
-    for line in lines:
-        runtime_match = runtime_re.match(line)
-        if not runtime_match:
-            continue
-        runtime = runtime_match.groupdict()
-        version = tuple([int(component) for component in runtime_match.group('version').split('.')])
-        runtime = {
-            'identifier': runtime['identifier'],
-            'available': runtime['unavailable'] is None,
-            'version': version,
-        }
-        if only_available and not runtime['available']:
-            continue
+    @classmethod
+    def from_name(cls, name):
+        """
+        :param name: The name for the desired device type.
+        :type name: str
+        :returns: A `DeviceType` object with the specified identifier or throws a TypeError if it doesn't exist.
+        :rtype: DeviceType
+        """
+        identifier = None
+        for device_type in Simulator().device_types:
+            if device_type.name == name:
+                identifier = device_type.identifier
+                break
 
-        runtimes[version] = runtime
+        if identifier is None:
+            raise TypeError('A device type with name "{name}" does not exist.'.format(name=name))
 
-    return runtimes
+        return DeviceType(name, identifier)
 
+    @classmethod
+    def from_identifier(cls, identifier):
+        """
+        :param identifier: The CoreSimulator identifier for the desired runtime.
+        :type identifier: str
+        :returns: A `Runtime` object witht the specified identifier or throws a TypeError if it doesn't exist.
+        :rtype: DeviceType
+        """
+        name = None
+        for device_type in Simulator().device_types:
+            if device_type.identifier == identifier:
+                name = device_type.name
+                break
 
-def get_devices():
+        if name is None:
+            raise TypeError('A device type with identifier "{identifier}" does not exist.'.format(
+                identifier=identifier))
+
+        return DeviceType(name, identifier)
+
+    def __eq__(self, other):
+        return (self.name == other.name) and (self.identifier == other.identifier)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __repr__(self):
+        return '<DeviceType "{name}": {identifier}>'.format(name=self.name, identifier=self.identifier)
+
+
+class Runtime(object):
     """
-    :return: A dictionary mapping iOS version to device hardware model, simulator UDID, and state.
-    :rtype: dict
+    Represents a CoreSimulator runtime associated with an iOS SDK.
     """
-    devices = {}
-    version_re = re.compile('-- iOS (?P<version>[0-9]+\.[0-9]+) --')
-    devices_re = re.compile('\s*(?P<name>[^(]+ )\((?P<udid>[^)]+)\) \((?P<state>[^)]+)\)')
-    stdout = subprocess.check_output(['xcrun', '-sdk', 'iphonesimulator', 'simctl', 'list', 'devices'])
-    lines = iter(stdout.splitlines())
-    header = next(lines)
-    version = None
-    if header != '== Devices ==':
-        return None
 
-    for line in lines:
-        version_match = version_re.match(line)
-        if version_match:
-            version = tuple([int(component) for component in version_match.group('version').split('.')])
-            continue
-        device_match = devices_re.match(line)
-        if not device_match:
-            raise RuntimeError()
-        device = device_match.groupdict()
-        device['name'] = device['name'].rstrip()
+    def __init__(self, version, identifier, available, devices=None):
+        """
+        :param version: The iOS SDK version
+        :type version: tuple
+        :param identifier: The CoreSimualtor runtime identifier
+        :type identifier: str
+        :param availability: Whether the runtime is available for use.
+        :type availability: bool
+        :param devices: A list of devices under this runtime
+        :type devices: list or None
+        """
+        self.version = version
+        self.identifier = identifier
+        self.available = available
+        self.devices = devices or []
 
-        devices[version][device['udid']] = device
+    @classmethod
+    def from_identifier(cls, identifier):
+        """
+        :param identifier: The identifier for the desired CoreSimulator runtime.
+        :type identifier: str
+        :returns: A `Runtime` object with the specified identifier or throws a TypeError if it doesn't exist.
+        :rtype: Runtime
+        """
+        runtime = None
+        for runtime in Simulator().runtimes:
+            if runtime.identifier == identifier:
+                break
+        if runtime is None:
+            raise TypeError('A runtime with identifier "{identifier}" does not exist.'.format(identifier=identifier))
+        return runtime
 
-    return devices
+    def __eq__(self, other):
+        return (self.version == other.version) and (self.identifier == other.identifier)
 
+    def __ne__(self, other):
+        return not self.__eq__(other)
 
-def get_device_types():
+    def __repr__(self):
+        return '<Runtime {version}: {identifier}. Available: {available}, {num_devices} devices>'.format(
+            version='.'.join(map(str, self.version)),
+            identifier=self.identifier,
+            available=self.available,
+            num_devices=len(self.devices))
+
+
+class Device(object):
     """
-    :return: A dictionary mapping of device name -> identifier
-    :rtype: dict
+    Represents a CoreSimulator device underneath a runtime
     """
-    device_types = {}
+
+    def __init__(self, name, udid, state, 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
+        :type runtime: Runtime
+        """
+        self.name = name
+        self.udid = udid
+        self.state = state
+        self.available = available
+        self.runtime = runtime
+
+    @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)))
+
+    @classmethod
+    def create(cls, name, device_type, runtime):
+        """
+        Create a new CoreSimulator device.
+        :param name: The name of the device.
+        :type name: str
+        :param device_type: The CoreSimulatort device type.
+        :type device_type: DeviceType
+        :param runtime:  The CoreSimualtor runtime.
+        :type runtime: Runtime
+        :return: The new device or raises a CalledProcessError if ``simctl create`` failed.
+        :rtype: Device
+        """
+        sim = Simulator()
+        subprocess.check_call(['xcrun', 'simctl', 'create', name, device_type.identifier, runtime.identifier])
+
+        device = None
+        while device is None:
+            sim.refresh()
+            device = sim.device(name, runtime)
+            if device is None or device.state == 'Creating':
+                time.sleep(2)
+            else:
+                break
+        return device
+
+    def __eq__(self, other):
+        return self.udid == other.udid
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __repr__(self):
+        return '<Device "{name}": {udid}. State: {state}. Runtime: {runtime}, Available: {available}>'.format(
+            name=self.name,
+            udid=self.udid,
+            state=self.state,
+            available=self.available,
+            runtime=self.runtime.identifier)
+
+
+class Simulator(object):
+    """
+    Represents the iOS Simulator infrastructure under the currently select Xcode.app bundle.
+    """
     device_type_re = re.compile('(?P<name>[^(]+)\((?P<identifier>[^)]+)\)')
-    stdout = subprocess.check_output(['xcrun', '-sdk', 'iphonesimulator', 'simctl', 'list', 'devicetypes'])
-    lines = iter(stdout.splitlines())
-    header = next(lines)
-    if header != '== Device Types ==':
+    runtime_re = re.compile(
+        'iOS (?P<version>[0-9]+\.[0-9]) \([0-9]+\.[0-9]+ - (?P<build_version>[^)]+)\) \((?P<identifier>[^)]+)\)( \((?P<availability>[^)]+)\))?')
+    version_re = re.compile('-- iOS (?P<version>[0-9]+\.[0-9]+) --')
+    devices_re = re.compile(
+        '\s*(?P<name>[^(]+ )\((?P<udid>[^)]+)\) \((?P<state>[^)]+)\)( \((?P<availability>[^)]+)\))?')
+
+    def __init__(self):
+        self.runtimes = []
+        self.device_types = []
+        self.refresh()
+
+    def refresh(self):
+        """
+        Refresh runtime and device type information from ``simctl list``.
+        """
+        command = ['xcrun', 'simctl', 'list']
+        simctl_p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        stdout, stderr = simctl_p.communicate()
+        if simctl_p.returncode != 0:
+            raise RuntimeError(
+                '{command} failed:\n{stdout}\n{stderr}'.format(command=' '.join(command), stdout=stdout, stderr=stderr))
+
+        lines = (line for line in stdout.splitlines())
+        device_types_header = next(lines)
+        if device_types_header != '== Device Types ==':
+            raise RuntimeError('Expected == Device Types == header but got: "{}"'.format(device_types_header))
+        self._parse_device_types(lines)
+
+    def _parse_device_types(self, lines):
+        """
+        Parse device types from ``simctl list``.
+        :param lines: A generator for the output lines from ``simctl list``.
+        :type lines: genexpr
+        :return: None
+        """
+        for line in lines:
+            device_type_match = self.device_type_re.match(line)
+            if not device_type_match:
+                if line != '== Runtimes ==':
+                    raise RuntimeError('Expected == Runtimes == header but got: "{}"'.format(line))
+                break
+            device_type = DeviceType(name=device_type_match.group('name').rstrip(),
+                                     identifier=device_type_match.group('identifier'))
+            self.device_types.append(device_type)
+
+        self._parse_runtimes(lines)
+
+    def _parse_runtimes(self, lines):
+        """
+        Continue to parse runtimes from ``simctl list``.
+        :param lines: A generator for the output lines from ``simctl list``.
+        :type lines: genexpr
+        :return: None
+        """
+        for line in lines:
+            runtime_match = self.runtime_re.match(line)
+            if not runtime_match:
+                if line != '== Devices ==':
+                    raise RuntimeError('Expected == Devices == header but got: "{}"'.format(line))
+                break
+            version = tuple(map(int, runtime_match.group('version').split('.')))
+            runtime = Runtime(version=version,
+                              identifier=runtime_match.group('identifier'),
+                              available=runtime_match.group('availability') is None)
+            self.runtimes.append(runtime)
+        self._parse_devices(lines)
+
+    def _parse_devices(self, lines):
+        """
+        Continue to parse devices from ``simctl list``.
+        :param lines: A generator for the output lines from ``simctl list``.
+        :type lines: genexpr
+        :return: None
+        """
+        current_runtime = None
+        for line in lines:
+            version_match = self.version_re.match(line)
+            if version_match:
+                version = tuple(map(int, version_match.group('version').split('.')))
+                current_runtime = self.runtime(version=version)
+                assert current_runtime
+                continue
+            device_match = self.devices_re.match(line)
+            if not device_match:
+                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)
+
+    def device_type(self, name=None, identifier=None):
+        """
+        :param name: The short name of the device type.
+        :type name: str
+        :param identifier: The CoreSimulator identifier of the desired device type.
+        :type identifier: str
+        :return: A device type with the specified name and/or identifier, or None if one doesn't exist as such.
+        :rtype: DeviceType
+        """
+        for device_type in self.device_types:
+            if name and device_type.name != name:
+                continue
+            if identifier and device_type.identifier != identifier:
+                continue
+            return device_type
         return None
 
-    for line in lines:
-        device_type_match = device_type_re.match(line)
-        if not device_type_match:
-            continue
-        device_type = device_type_match.groupdict()
-        device_type['name'] = device_type['name'].rstrip()
-        device_types[device_type['name']] = device_type['identifier']
+    def runtime(self, version=None, identifier=None):
+        """
+        :param version: The iOS version of the desired runtime.
+        :type version: tuple
+        :param identifier: The CoreSimulator identifier of the desired runtime.
+        :type identifier: str
+        :return: A runtime with the specified version and/or identifier, or None if one doesn't exist as such.
+        :rtype: Runtime or None
+        """
+        if version is None and identifier is None:
+            raise TypeError('Must supply version and/or identifier.')
 
-    return device_types
+        for runtime in self.runtimes:
+            if version and runtime.version != version:
+                continue
+            if identifier and runtime.identifier != identifier:
+                continue
+            return runtime
+        return None
 
+    def device(self, name=None, runtime=None):
+        """
+        :param name: The name of the desired device.
+        :type name: str
+        :param runtime: The runtime of the desired device.
+        :type runtime: Runtime
+        :return: A device with the specified name and/or runtime, or None if one doesn't exist as such
+        :rtype: Device or None
+        """
+        if name is None and runtime is None:
+            raise TypeError('Must supply name and/or runtime.')
 
-def get_latest_runtime():
-    runtimes = get_runtimes()
-    if not runtimes:
+        for device in self.devices:
+            if name and device.name != name:
+                continue
+            if runtime and device.runtime != runtime:
+                continue
+            return device
         return None
-    latest_version = sorted(runtimes.keys())[0]
-    return runtimes[latest_version]
+
+    @property
+    def devices(self):
+        """
+        :return: An iterator of all devices from all runtimes.
+        :rtype: iter
+        """
+        return itertools.chain(*[runtime.devices for runtime in self.runtimes])
+
+    @property
+    def latest_runtime(self):
+        """
+        :return: Returns a Runtime object with the highest version.
+        :rtype: Runtime or None
+        """
+        if not self.runtimes:
+            return None
+        return sorted(self.runtimes, key=lambda runtime: runtime.version)[-1]
+
+    def testing_device(self, device_type, runtime):
+        """
+        Get an iOS Simulator device for testing.
+        :param device_type: The CoreSimulator device type.
+        :type device_type: DeviceType
+        :param runtime: The CoreSimulator runtime.
+        :type runtime: Runtime
+        :return: A dictionary describing the device.
+        :rtype: Device
+        """
+        # Check to see if the testing device already exists
+        name = device_type.name + ' WebKit Tester'
+        return self.device(name=name, runtime=runtime) or Device.create(name, device_type, runtime)
+
+    def __repr__(self):
+        return '<iOS Simulator: {num_runtimes} runtimes, {num_device_types} device types>'.format(
+            num_runtimes=len(self.runtimes),
+            num_device_types=len(self.device_types))
+
+    def __str__(self):
+        description = ['iOS Simulator:']
+        description += map(str, self.runtimes)
+        description += map(str, self.device_types)
+        description += map(str, self.devices)
+        return '\n'.join(description)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to