Title: [193765] trunk/Tools
Revision
193765
Author
[email protected]
Date
2015-12-08 11:49:04 -0800 (Tue, 08 Dec 2015)

Log Message

https://bugs.webkit.org/show_bug.cgi?id=151243
<rdar://problem/22955197>

Patch by Aakash Jain <[email protected]> on 2015-12-08
Reviewed by Alexey Proskuryakov.

* LayoutTestRelay/LayoutTestRelay/main.m:
(getTestingSimDevice): Use separate testing device for each worker.
* Scripts/webkitpy/layout_tests/controllers/manager.py:
(Manager.run): Perform cleanup even if setup fails.
* Scripts/webkitpy/port/ios.py:
(IOSSimulatorPort.default_child_processes): Calculate number of simulators to use.
(IOSSimulatorPort.child_processes): Gets the number of simulators from options variable.
(IOSSimulatorPort.setup_test_run): Handle mulitple simulators.
(IOSSimulatorPort._quit_ios_simulator): Same
(IOSSimulatorPort.clean_up_test_run): Same
(IOSSimulatorPort.check_sys_deps): Same
(IOSSimulatorPort.testing_device): Same
(IOSSimulatorPort.reset_preferences): Same
(IOSSimulatorPort.get_simulator_path): Return simulator path.
(IOSSimulatorPort._createSimulatorApp): Create the copy of simulator app.
* Scripts/webkitpy/xcode/simulator.py:
(Device.delete): Delete the simulator device.
(Simulator.delete_device): Same
(Simulator.wait_until_device_is_booted): Wait for device booting.

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (193764 => 193765)


--- trunk/Tools/ChangeLog	2015-12-08 19:35:20 UTC (rev 193764)
+++ trunk/Tools/ChangeLog	2015-12-08 19:49:04 UTC (rev 193765)
@@ -1,3 +1,30 @@
+2015-12-08  Aakash Jain  <[email protected]>
+
+        https://bugs.webkit.org/show_bug.cgi?id=151243
+        <rdar://problem/22955197>
+
+        Reviewed by Alexey Proskuryakov.
+
+        * LayoutTestRelay/LayoutTestRelay/main.m:
+        (getTestingSimDevice): Use separate testing device for each worker.
+        * Scripts/webkitpy/layout_tests/controllers/manager.py:
+        (Manager.run): Perform cleanup even if setup fails.
+        * Scripts/webkitpy/port/ios.py:
+        (IOSSimulatorPort.default_child_processes): Calculate number of simulators to use.
+        (IOSSimulatorPort.child_processes): Gets the number of simulators from options variable.
+        (IOSSimulatorPort.setup_test_run): Handle mulitple simulators.
+        (IOSSimulatorPort._quit_ios_simulator): Same
+        (IOSSimulatorPort.clean_up_test_run): Same
+        (IOSSimulatorPort.check_sys_deps): Same
+        (IOSSimulatorPort.testing_device): Same
+        (IOSSimulatorPort.reset_preferences): Same
+        (IOSSimulatorPort.get_simulator_path): Return simulator path.
+        (IOSSimulatorPort._createSimulatorApp): Create the copy of simulator app.
+        * Scripts/webkitpy/xcode/simulator.py:
+        (Device.delete): Delete the simulator device.
+        (Simulator.delete_device): Same
+        (Simulator.wait_until_device_is_booted): Wait for device booting.
+
 2015-12-08  Ryuan Choi  <[email protected]>
 
         [EFL] Add API to provide preferences before creating ewk_view

Modified: trunk/Tools/LayoutTestRelay/LayoutTestRelay/main.m (193764 => 193765)


--- trunk/Tools/LayoutTestRelay/LayoutTestRelay/main.m	2015-12-08 19:35:20 UTC (rev 193764)
+++ trunk/Tools/LayoutTestRelay/LayoutTestRelay/main.m	2015-12-08 19:49:04 UTC (rev 193765)
@@ -43,10 +43,10 @@
     fprintf(stderr, "%s\n", [helpText UTF8String]);
 }
 
-SimDevice *getTestingSimDevice(SimDeviceType *deviceType, SimRuntime *runtime)
+SimDevice *getTestingSimDevice(SimDeviceType *deviceType, SimRuntime *runtime, NSString *suffix)
 {
     NSString *deviceName = [[[[deviceType identifier] componentsSeparatedByString:@"."] lastObject] stringByReplacingOccurrencesOfString:@"-" withString:@" "];
-    deviceName = [deviceName stringByAppendingString:@" WebKit Tester"];
+    deviceName = [NSString stringWithFormat:@"%@%@%@", deviceName, @" WebKit Tester", suffix];
 
     for (SimDevice *device in [[SimDeviceSet defaultSet] devices]) {
         if ([[device name] isEqualToString:deviceName] && [[device deviceType] isEqualTo:deviceType] && [[device runtime] isEqualTo:runtime])
@@ -136,7 +136,7 @@
         NSString *productDir = getRequiredStringArgument(@"productDir");
         NSArray *dumpToolArguments = getDumpToolArguments();
 
-        SimDevice *device = getTestingSimDevice(deviceType, runtime);
+        SimDevice *device = getTestingSimDevice(deviceType, runtime, suffix);
 
         relayController = [[LTRelayController alloc] initWithDevice:device productDir:productDir appPath:appPath identifierSuffix:suffix dumpToolArguments:dumpToolArguments];
         [relayController start];

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py (193764 => 193765)


--- trunk/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py	2015-12-08 19:35:20 UTC (rev 193764)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py	2015-12-08 19:49:04 UTC (rev 193765)
@@ -187,11 +187,11 @@
             _log.critical('No tests to run.')
             return test_run_results.RunDetails(exit_code=-1)
 
-        if not self._set_up_run(tests_to_run):
-            return test_run_results.RunDetails(exit_code=-1)
+        try:
+            if not self._set_up_run(tests_to_run):
+                return test_run_results.RunDetails(exit_code=-1)
 
-        enabled_pixel_tests_in_retry = False
-        try:
+            enabled_pixel_tests_in_retry = False
             initial_results = self._run_tests(tests_to_run, tests_to_skip, self._options.repeat_each, self._options.iterations,
                 int(self._options.child_processes), retrying=False)
 

Modified: trunk/Tools/Scripts/webkitpy/port/ios.py (193764 => 193765)


--- trunk/Tools/Scripts/webkitpy/port/ios.py	2015-12-08 19:35:20 UTC (rev 193764)
+++ trunk/Tools/Scripts/webkitpy/port/ios.py	2015-12-08 19:49:04 UTC (rev 193765)
@@ -70,16 +70,14 @@
 
 class IOSSimulatorPort(Port):
     port_name = "ios-simulator"
-
     FUTURE_VERSION = 'future'
-
     ARCHITECTURES = ['x86_64', 'x86']
-
     DEFAULT_ARCHITECTURE = 'x86_64'
-
     SIMULATOR_BUNDLE_ID = 'com.apple.iphonesimulator'
-
     relay_name = 'LayoutTestRelay'
+    SIMULATOR_DIRECTORY = "/tmp/WebKitTestingSimulators/"
+    LSREGISTER_PATH = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Versions/Current/Support/lsregister"
+    PROCESS_COUNT_ESTIMATE_PER_SIMULATOR_INSTANCE = 100
 
     def __init__(self, *args, **kwargs):
         super(IOSSimulatorPort, self).__init__(*args, **kwargs)
@@ -130,6 +128,28 @@
             path = mac_config.build_directory(self.get_option('configuration'))
         return self._filesystem.join(path, self.relay_name)
 
+    @memoized
+    def child_processes(self):
+        return int(self.get_option('child_processes'))
+
+    @memoized
+    def default_child_processes(self):
+        """Return the number of Simulators instances to use for this port."""
+        best_child_process_count_for_cpu = self._executive.cpu_count() / 2
+        system_process_count_limit = int(subprocess.check_output(["launchctl", "limit", "maxproc"]).strip().split()[1])
+        current_process_count = len(subprocess.check_output(["ps", "aux"]).strip().split('\n'))
+        _log.info('Process limit: %d, current #processes: %d' % (system_process_count_limit, current_process_count))
+        maximum_simulator_count_on_this_system = (system_process_count_limit - current_process_count) // self.PROCESS_COUNT_ESTIMATE_PER_SIMULATOR_INSTANCE
+        # FIXME: We should also take into account the available RAM.
+
+        if (maximum_simulator_count_on_this_system < best_child_process_count_for_cpu):
+            _log.warn("This machine could support %s child processes, but only has enough process limit for %s."
+                % (best_child_process_count_for_cpu, maximum_simulator_count_on_this_system))
+            _log.warn('Run "launchctl limit" to check these limits')
+            # FIXME: Add url for webpage explaining how to increase these limits.
+
+        return min(maximum_simulator_count_on_this_system, best_child_process_count_for_cpu)
+
     def default_timeout_ms(self):
         if self.get_option('guard_malloc'):
             return 350 * 1000
@@ -200,21 +220,24 @@
         return list(reversed([self._filesystem.join(self._webkit_baseline_path(p), 'TestExpectations') for p in self.baseline_search_path()]))
 
     def setup_test_run(self):
-        device_udid = self.testing_device.udid
-        # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator
-        self._executive.run_command([
-            'open', '-b', self.SIMULATOR_BUNDLE_ID,
-            '--args', '-CurrentDeviceUDID', device_udid])
-        Simulator.wait_until_device_is_in_state(device_udid, Simulator.DeviceState.BOOTED)
+        mac_os_version = self.host.platform.os_version
+        for i in xrange(self.child_processes()):
+            device_udid = self.testing_device(i).udid
+            # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator
+            self._executive.run_command([
+                'open', '-b', self.SIMULATOR_BUNDLE_ID + str(i),
+                '--args', '-CurrentDeviceUDID', device_udid])
 
-        # FIXME: Pause here until SpringBoard finishes launching to workaround <rdar://problem/20000383>.
-        boot_delay = 30
-        _log.debug('Waiting {seconds} seconds for iOS Simulator to finish booting ...'.format(seconds=boot_delay))
-        time.sleep(boot_delay)
+            if mac_os_version in ['elcapitan', 'yosemite', 'mavericks']:
+                time.sleep(1)
 
+        _log.info('Waiting for all iOS Simulators to finish booting.')
+        for i in xrange(self.child_processes()):
+            Simulator.wait_until_device_is_booted(self.testing_device(i).udid)
+
     def _quit_ios_simulator(self):
-        # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator
-        self._executive.run_command(['osascript', '-e', 'tell application id "{0}" to quit'.format(self.SIMULATOR_BUNDLE_ID)])
+        # FIXME: We should kill only the Simulators we started.
+        subprocess.call(["killall", "-9", "-v", "-m", "Simulator"])
 
     def clean_up_test_run(self):
         super(IOSSimulatorPort, self).clean_up_test_run()
@@ -227,6 +250,14 @@
                 _log.warning('Unable to remove ' + fifo)
                 pass
 
+        for i in xrange(self.child_processes()):
+            try:
+                subprocess.call([self.LSREGISTER_PATH, "-v", "-u", self.get_simulator_path(i)])
+                shutil.rmtree(self.get_simulator_path(i), ignore_errors=True)
+                Simulator().delete_device(self.testing_device(i).udid)
+            except:
+                _log.warning('Unable to remove Simulator' + str(i))
+
     def setup_environ_for_server(self, server_name=None):
         env = super(IOSSimulatorPort, self).setup_environ_for_server(server_name)
         if server_name == self.driver_name():
@@ -245,11 +276,16 @@
         if not self.simulator_runtime.available:
             _log.error('The iOS Simulator runtime with identifier "{0}" cannot be used because it is unavailable.'.format(self.simulator_runtime.identifier))
             return False
-        testing_device = self.testing_device  # May create a new simulator device
+        for i in xrange(self.child_processes()):
+            # FIXME: This creates the devices sequentially, doing this in parallel can improve performance.
+            testing_device = self.testing_device(i)
+            _log.debug('Verifying Simulator{0} with UDID {1}.'.format(i, testing_device.udid))
 
-        if not Simulator.check_simulator_device_and_erase_if_needed(self.host, testing_device.udid):
-            _log.error('Unable to boot the simulator device with UDID {0}.'.format(testing_device.udid))
-            return False
+            # FIXME: This is very slow, especially for mulitple simulators, we probably do not need
+            # this checking as we are re-creating new simulator apps and devices in every run.
+            if not Simulator.check_simulator_device_and_erase_if_needed(self.host, testing_device.udid):
+                _log.error('Unable to boot the simulator device with UDID {0}.'.format(testing_device.udid))
+                return False
         return super(IOSSimulatorPort, self).check_sys_deps(needs_http)
 
     def check_for_leaks(self, process_name, process_pid):
@@ -323,11 +359,13 @@
             return stderr, None
         return stderr, crash_log
 
-    @property
     @memoized
-    def testing_device(self):
-        return Simulator().lookup_or_create_device(self.simulator_device_type.name + ' WebKit Tester', self.simulator_device_type, self.simulator_runtime)
+    def testing_device(self, number):
+        return Simulator().lookup_or_create_device(self.simulator_device_type.name + ' WebKit Tester' + str(number), self.simulator_device_type, self.simulator_runtime)
 
+    def get_simulator_path(self, suffix=""):
+        return os.path.join(self.SIMULATOR_DIRECTORY, "Simulator" + str(suffix) + ".app")
+
     def _merge_crash_logs(self, logs, new_logs, crashed_processes):
         for test, crash_log in new_logs.iteritems():
             try:
@@ -396,15 +434,21 @@
         return self._image_differ.diff_image(expected_contents, actual_contents, tolerance)
 
     def reset_preferences(self):
-        # We assume that if testing_device is booted that it was booted by the iOS Simulator app
-        # (as opposed to simctl). So, quit the iOS Simulator app to shutdown testing_device.
+        if (self.default_child_processes() < self.child_processes()):
+                _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.")
+
         self._quit_ios_simulator()
-        Simulator.wait_until_device_is_in_state(self.testing_device.udid, Simulator.DeviceState.SHUTDOWN)
+        self._createSimulatorApps()
 
-        data_path = os.path.join(self.testing_device.path, 'data')
-        if os.path.isdir(data_path):
-            shutil.rmtree(data_path)
+        for i in xrange(self.child_processes()):
+            Simulator.wait_until_device_is_in_state(self.testing_device(i).udid, Simulator.DeviceState.SHUTDOWN)
 
+            data_path = os.path.join(self.testing_device(i).path, 'data')
+            if os.path.isdir(data_path):
+                shutil.rmtree(data_path)
+
     def make_command(self):
         return self.xcrun_find('make', '/usr/bin/make')
 
@@ -428,3 +472,23 @@
 
     def stderr_patterns_to_strip(self):
         return []
+
+    def _createSimulatorApps(self):
+        for i in xrange(self.child_processes()):
+            self._createSimulatorApp(i)
+
+    def _createSimulatorApp(self, suffix):
+        destination = self.get_simulator_path(suffix)
+        _log.info("Creating app:" + destination)
+        if os.path.exists(destination):
+            shutil.rmtree(destination, ignore_errors=True)
+        simulator_app_path = self.developer_dir + "/Applications/Simulator.app"
+        shutil.copytree(simulator_app_path, destination)
+
+        # Update app's package-name inside plist and re-code-sign it
+        plist_path = destination + "/Contents/Info.plist"
+        command = "Set CFBundleIdentifier com.apple.iphonesimulator" + str(suffix)
+        subprocess.check_output(["/usr/libexec/PlistBuddy", "-c", command, plist_path])
+        subprocess.check_output(["install_name_tool", "-add_rpath", self.developer_dir + "/Library/PrivateFrameworks/", destination + "/Contents/MacOS/Simulator"])
+        subprocess.check_output(["codesign", "-fs", "-", destination])
+        subprocess.check_output([self.LSREGISTER_PATH, "-v", "-f", destination])

Modified: trunk/Tools/Scripts/webkitpy/xcode/simulator.py (193764 => 193765)


--- trunk/Tools/Scripts/webkitpy/xcode/simulator.py	2015-12-08 19:35:20 UTC (rev 193764)
+++ trunk/Tools/Scripts/webkitpy/xcode/simulator.py	2015-12-08 19:49:04 UTC (rev 193765)
@@ -215,6 +215,15 @@
         Simulator.wait_until_device_is_in_state(device_udid, Simulator.DeviceState.SHUTDOWN)
         return Simulator().find_device_by_udid(device_udid)
 
+    @classmethod
+    def delete(cls, udid):
+        """
+        Delete the given CoreSimulator device.
+        :param udid: The udid of the device.
+        :type udid: str
+        """
+        subprocess.call(['xcrun', 'simctl', 'delete', udid])
+
     def __eq__(self, other):
         return self.udid == other.udid
 
@@ -265,6 +274,15 @@
         SHUTTING_DOWN = 4
 
     @staticmethod
+    def wait_until_device_is_booted(udid, timeout_seconds=60 * 5):
+        with timeout(seconds=timeout_seconds):
+            while True:
+                state = subprocess.check_output(['xcrun', 'simctl', 'spawn', udid, 'launchctl', 'print', 'system']).strip()
+                if re.search("A[\s]+com.apple.springboard.services", state):
+                    return
+                time.sleep(1)
+
+    @staticmethod
     def wait_until_device_is_in_state(udid, wait_until_state, timeout_seconds=60 * 5):
         with timeout(seconds=timeout_seconds):
             while (Simulator.device_state(udid) != wait_until_state):
@@ -299,6 +317,9 @@
             return Simulator._boot_and_shutdown_simulator_device(host, udid) == 0  # Can boot device
         return False  # Cannot boot or erase device
 
+    def delete_device(self, udid):
+        Device.delete(udid)
+
     def refresh(self):
         """
         Refresh runtime and device type information from ``simctl list``.
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to