webkitpy: Provide option to skip install
[WebKit-https.git] / Tools / Scripts / webkitpy / port / ios.py
1 # Copyright (C) 2014-2017 Apple Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
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.
11 #
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.
22
23 import logging
24 import traceback
25
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
30
31 _log = logging.getLogger(__name__)
32
33
34 class IOSPort(DarwinPort):
35     port_name = "ios"
36
37     def __init__(self, host, port_name, **kwargs):
38         super(IOSPort, self).__init__(host, port_name, **kwargs)
39         self._test_runner_process_constructor = SimulatorProcess
40         self._printing_cmd_line = False
41         self._current_device = None
42
43     def _device_for_worker_number_map(self):
44         raise NotImplementedError
45
46     def driver_cmd_line_for_logging(self):
47         # Avoid creating/connecting to devices just for logging the commandline.
48         self._printing_cmd_line = True
49         result = super(IOSPort, self).driver_cmd_line_for_logging()
50         self._printing_cmd_line = False
51         return result
52
53     def driver_name(self):
54         if self.get_option('driver_name'):
55             return self.get_option('driver_name')
56         if self.get_option('webkit_test_runner'):
57             return 'WebKitTestRunnerApp.app'
58         return 'DumpRenderTree.app'
59
60     def _generate_all_test_configurations(self):
61         configurations = []
62         for build_type in self.ALL_BUILD_TYPES:
63             for architecture in self.ARCHITECTURES:
64                 configurations.append(TestConfiguration(version=self._version, architecture=architecture, build_type=build_type))
65         return configurations
66
67     @memoized
68     def child_processes(self):
69         return int(self.get_option('child_processes'))
70
71     def using_multiple_devices(self):
72         return False
73
74     def _testing_device(self, number):
75         device = self._device_for_worker_number_map()[number]
76         if not device:
77             raise RuntimeError('Device at {} could not be found'.format(number))
78         return device
79
80     # A device is the target host for a specific worker number.
81     def target_host(self, worker_number=None):
82         if self._printing_cmd_line or worker_number is None:
83             return self.host
84         # When using simulated devices, this means webkitpy is managing the devices.
85         if self.using_multiple_devices():
86             return self._testing_device(worker_number)
87         return self._current_device
88
89     def default_baseline_search_path(self):
90         wk_string = 'wk1'
91         if self.get_option('webkit_test_runner'):
92             wk_string = 'wk2'
93         fallback_names = [
94             '{}-{}'.format(self.port_name, wk_string),
95             self.port_name,
96             '{}-{}'.format(IOSPort.port_name, wk_string),
97             IOSPort.port_name,
98         ]
99         if self.get_option('webkit_test_runner'):
100             fallback_names.append('wk2')
101
102         return map(self._webkit_baseline_path, fallback_names)
103
104     def test_expectations_file_position(self):
105         return 3
106
107     def _create_devices(self, device_class):
108         raise NotImplementedError
109
110     def setup_test_run(self, device_class=None):
111         self._create_devices(device_class)
112
113         if self.get_option('install'):
114             for i in xrange(self.child_processes()):
115                 device = self.target_host(i)
116                 _log.debug('Installing to {}'.format(device))
117                 # Without passing DYLD_LIBRARY_PATH, libWebCoreTestSupport cannot be loaded and DRT/WKTR will crash pre-launch,
118                 # leaving a crash log which will be picked up in results.  No DYLD_FRAMEWORK_PATH will also cause the DRT/WKTR to
119                 # crash, but this crash will occur post-launch, after install_app has already killed the process.
120                 if not device.install_app(self._path_to_driver(), {'DYLD_LIBRARY_PATH': self._build_path()}):
121                     raise RuntimeError('Failed to install app {} on device {}'.format(self._path_to_driver(), device.udid))
122         else:
123             _log.debug('Skipping installation')
124
125         for i in xrange(self.child_processes()):
126             self.target_host(i).prepare_for_testing()
127
128     def clean_up_test_run(self):
129         super(IOSPort, self).clean_up_test_run()
130
131         # Best effort to let every device teardown before throwing any exceptions here.
132         # Failure to teardown devices can leave things in a bad state.
133         exception_list = []
134         for i in xrange(self.child_processes()):
135             device = self.target_host(i)
136             try:
137                 if device:
138                     device.finished_testing()
139             except BaseException as e:
140                 trace = traceback.format_exc()
141                 if isinstance(e, Exception):
142                     exception_list.append([e, trace])
143                 else:
144                     exception_list.append([Exception('Exception tearing down {}'.format(device)), trace])
145         if len(exception_list) == 1:
146             raise
147         elif len(exception_list) > 1:
148             print '\n'
149             for exception in exception_list:
150                 _log.error('{} raised: {}'.format(exception[0].__class__.__name__, exception[0]))
151                 _log.error(exception[1])
152                 _log.error('--------------------------------------------------')
153
154             raise RuntimeError('Multiple failures when teardown devices')