1 # Copyright (C) 2018 Apple Inc. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
6 # 1. Redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer.
8 # 2. Redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution.
12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 from webkitpy.common.memoized import memoized
27 from webkitpy.layout_tests.models.test_configuration import TestConfiguration
28 from webkitpy.port.darwin import DarwinPort
29 from webkitpy.port.simulator_process import SimulatorProcess
32 _log = logging.getLogger(__name__)
35 class DevicePort(DarwinPort):
39 def __init__(self, *args, **kwargs):
40 super(DevicePort, self).__init__(*args, **kwargs)
41 self._test_runner_process_constructor = SimulatorProcess
42 self._printing_cmd_line = False
44 def driver_cmd_line_for_logging(self):
45 # Avoid creating/connecting to devices just for command line logging.
46 self._printing_cmd_line = True
47 result = super(DevicePort, self).driver_cmd_line_for_logging()
48 self._printing_cmd_line = False
51 def _generate_all_test_configurations(self):
53 for build_type in self.ALL_BUILD_TYPES:
54 for architecture in self.ARCHITECTURES:
55 configurations.append(TestConfiguration(version=self.version_name(), architecture=architecture, build_type=build_type))
59 def child_processes(self):
60 return int(self.get_option('child_processes'))
62 def driver_name(self):
63 if self.get_option('driver_name'):
64 return self.get_option('driver_name')
65 if self.get_option('webkit_test_runner'):
66 return 'WebKitTestRunnerApp.app'
67 return 'DumpRenderTree.app'
69 # A device is the target host for a specific worker number.
70 def target_host(self, worker_number=None):
71 if self._printing_cmd_line or worker_number is None:
73 if self.DEVICE_MANAGER is None:
74 raise RuntimeError('No device manager for specified port')
75 if self.DEVICE_MANAGER.INITIALIZED_DEVICES is None:
76 raise RuntimeError('No initialized devices for testing')
77 return self.DEVICE_MANAGER.INITIALIZED_DEVICES[worker_number]
80 if self.DEVICE_MANAGER is None:
82 if self.DEVICE_MANAGER.INITIALIZED_DEVICES is None:
84 return self.DEVICE_MANAGER.INITIALIZED_DEVICES
86 def _create_devices(self, device_class):
87 raise NotImplementedError
89 # Despite their names, these flags do not actually get passed all the way down to webkit-build.
90 def _build_driver_flags(self):
91 return ['--sdk', self.SDK] + (['ARCHS=%s' % self.architecture()] if self.architecture() else [])
94 if not self.get_option('install'):
95 _log.debug('Skipping installation')
98 for i in xrange(self.child_processes()):
99 device = self.target_host(i)
100 _log.debug('Installing to {}'.format(device))
101 # Without passing DYLD_LIBRARY_PATH, libWebCoreTestSupport cannot be loaded and DRT/WKTR will crash pre-launch,
102 # leaving a crash log which will be picked up in results. DYLD_FRAMEWORK_PATH is needed to prevent an early crash.
103 if not device.install_app(self._path_to_driver(), {'DYLD_LIBRARY_PATH': self._build_path(), 'DYLD_FRAMEWORK_PATH': self._build_path()}):
104 raise RuntimeError('Failed to install app {} on device {}'.format(self._path_to_driver(), device.udid))
105 if not device.install_dylibs(self._build_path()):
106 raise RuntimeError('Failed to install dylibs at {} on device {}'.format(self._build_path(), device.udid))
108 def setup_test_run(self, device_class=None):
109 self._create_devices(device_class)
112 for i in xrange(self.child_processes()):
113 host = self.target_host(i)
114 host.prepare_for_testing(
115 self.ports_to_forward(),
116 self.app_identifier_from_bundle(self._path_to_driver()),
117 self.layout_tests_dir(),
119 self._crash_logs_to_skip_for_host[host] = host.filesystem.files_under(self.path_to_crash_logs())
121 def clean_up_test_run(self):
122 super(DevicePort, self).clean_up_test_run()
124 # Best effort to let every device teardown before throwing any exceptions here.
125 # Failure to teardown devices can leave things in a bad state.
127 for i in xrange(self.child_processes()):
128 device = self.target_host(i)
132 device.finished_testing()
133 except BaseException as e:
134 trace = traceback.format_exc()
135 if isinstance(e, Exception):
136 exception_list.append([e, trace])
138 exception_list.append([Exception('Exception while tearing down {}'.format(device)), trace])
140 if len(exception_list) == 1:
142 if len(exception_list) > 1:
144 for exception in exception_list:
145 _log.error('{} raised: {}'.format(exception[0].__class__.__name__, exception[0]))
146 _log.error(exception[1])
147 _log.error('--------------------------------------------------')
149 raise RuntimeError('Multiple failures when teardown devices')
151 def did_spawn_worker(self, worker_number):
152 super(DevicePort, self).did_spawn_worker(worker_number)
154 self.target_host(worker_number).release_worker_resources()
156 def setup_environ_for_server(self, server_name=None):
157 env = super(DevicePort, self).setup_environ_for_server(server_name)
158 if server_name == self.driver_name() and self.get_option('guard_malloc'):
159 self._append_value_colon_separated(env, 'DYLD_INSERT_LIBRARIES', '/usr/lib/libgmalloc.dylib')
160 self._append_value_colon_separated(env, '__XPC_DYLD_INSERT_LIBRARIES', '/usr/lib/libgmalloc.dylib')
161 env['XML_CATALOG_FILES'] = '' # work around missing /etc/catalog <rdar://problem/4292995>