Changeset 204477 in webkit
- Timestamp:
- Aug 15, 2016, 1:50:13 PM (9 years ago)
- Location:
- trunk/Tools
- Files:
-
- 13 edited
-
ChangeLog (modified) (1 diff)
-
Scripts/webkitpy/layout_tests/controllers/manager.py (modified) (8 diffs)
-
Scripts/webkitpy/layout_tests/controllers/manager_unittest.py (modified) (2 diffs)
-
Scripts/webkitpy/layout_tests/controllers/single_test_runner.py (modified) (1 diff)
-
Scripts/webkitpy/layout_tests/models/test_run_results.py (modified) (1 diff)
-
Scripts/webkitpy/layout_tests/views/printing.py (modified) (1 diff)
-
Scripts/webkitpy/port/base.py (modified) (2 diffs)
-
Scripts/webkitpy/port/driver.py (modified) (2 diffs)
-
Scripts/webkitpy/port/efl.py (modified) (1 diff)
-
Scripts/webkitpy/port/gtk.py (modified) (1 diff)
-
Scripts/webkitpy/port/ios.py (modified) (7 diffs)
-
Scripts/webkitpy/port/test.py (modified) (1 diff)
-
Scripts/webkitpy/port/win.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Tools/ChangeLog
r204471 r204477 1 2016-08-15 Simon Fraser <simon.fraser@apple.com> 2 3 Allow a port to run tests with a custom device setup 4 https://bugs.webkit.org/show_bug.cgi?id=160833 5 6 Reviewed by Daniel Bates. 7 8 These changes allow the IOSSimulator port to run tests in iPad mode. 9 10 This is made possible by allowing a platform to define CUSTOM_DEVICE_CLASSES, 11 in this case 'ipad'. When specified, any test in a directory with a suffix that matches 12 a custom device will be collected into a set, and run in that device's environment after 13 the other tests have run. 14 15 * Scripts/webkitpy/layout_tests/controllers/manager.py: 16 (Manager._custom_device_for_test): If the test contains a directory matching a 17 custom device suffix, return that custom device. 18 (Manager._set_up_run): Push the custom device class, if any, into options so 19 that the Worker can get to it. 20 (Manager.run): Go through the list of tests, and break it down into device-generic 21 tests, and tests for each device class. _run_test_subset is then called for 22 each collection of tests, and the results merged. 23 (Manager._run_test_subset): Some lines unwrapped. 24 (Manager._end_test_run): 25 (Manager._run_tests): 26 * Scripts/webkitpy/layout_tests/controllers/single_test_runner.py: 27 (SingleTestRunner.__init__): Unwrapped a line. 28 * Scripts/webkitpy/layout_tests/models/test_run_results.py: 29 (TestRunResults.merge): Add this function to merge TestRunResults 30 * Scripts/webkitpy/layout_tests/views/printing.py: 31 (Printer.print_workers_and_shards): Print the custom device, if any. 32 * Scripts/webkitpy/port/base.py: 33 (Port): Base port has empty array of custom devices. 34 (Port.setup_test_run): Add device_class argument. 35 * Scripts/webkitpy/port/driver.py: 36 (DriverInput.__repr__): 37 (Driver.check_driver.implementation): 38 * Scripts/webkitpy/port/efl.py: 39 (EflPort.setup_test_run): 40 * Scripts/webkitpy/port/gtk.py: 41 (GtkPort.setup_test_run): 42 * Scripts/webkitpy/port/ios.py: 43 (IOSSimulatorPort): Add CUSTOM_DEVICE_CLASSES for ipad. 44 (IOSSimulatorPort.__init__): 45 (IOSSimulatorPort.simulator_device_type): Use a device name from the DEVICE_CLASS_MAP 46 based on the custom device class. 47 (IOSSimulatorPort._set_device_class): 48 (IOSSimulatorPort._create_simulators): Factor some code into this function. 49 (IOSSimulatorPort.setup_test_run): 50 (IOSSimulatorPort.testing_device): 51 (IOSSimulatorPort.reset_preferences): This used to create the simulator apps, but that 52 seemed wrong for this function. That was moved to setup_test_run(). 53 (IOSSimulatorPort.check_sys_deps): This function used to create testing devices, 54 but this happened too early, before we knew which kind of devices to create. Devices 55 are now created in setup_test_run(). 56 * Scripts/webkitpy/port/win.py: 57 (WinPort.setup_test_run): 58 1 59 2016-08-15 Daniel Bates <dabates@apple.com> 2 60 -
trunk/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
r202362 r204477 40 40 import sys 41 41 import time 42 from collections import defaultdict 42 43 43 44 from webkitpy.common.checkout.scm.detection import SCMDetector … … 98 99 return self.web_platform_test_subdir in test 99 100 101 def _custom_device_for_test(self, test): 102 for device_class in self._port.CUSTOM_DEVICE_CLASSES: 103 directory_suffix = device_class + self._port.TEST_PATH_SEPARATOR 104 if directory_suffix in test: 105 return device_class 106 return None 107 100 108 def _http_tests(self, test_names): 101 109 return set(test for test in test_names if self._is_http_test(test)) … … 142 150 self._options.child_processes = worker_count 143 151 144 def _set_up_run(self, test_names ):152 def _set_up_run(self, test_names, device_class=None): 145 153 self._printer.write_update("Checking build ...") 146 154 if not self._port.check_build(self.needs_servers(test_names)): 147 155 _log.error("Build check failed") 148 156 return False 157 158 self._options.device_class = device_class 149 159 150 160 # This must be started before we check the system dependencies, … … 170 180 self._port.host.filesystem.maybe_make_directory(self._results_directory) 171 181 172 self._port.setup_test_run( )182 self._port.setup_test_run(self._options.device_class) 173 183 return True 174 184 … … 195 205 return test_run_results.RunDetails(exit_code=-1) 196 206 197 try: 207 default_device_tests = [] 208 209 # Look for tests with custom device requirements. 210 custom_device_tests = defaultdict(list) 211 for test_file in tests_to_run: 212 custom_device = self._custom_device_for_test(test_file) 213 if custom_device: 214 custom_device_tests[custom_device].append(test_file) 215 else: 216 default_device_tests.append(test_file) 217 218 if custom_device_tests: 219 for device_class in custom_device_tests: 220 _log.debug('{} tests use device {}'.format(len(custom_device_tests[device_class]), device_class)) 221 222 initial_results = None 223 retry_results = None 224 enabled_pixel_tests_in_retry = False 225 226 if default_device_tests: 227 _log.info('') 228 _log.info("Running %s", pluralize(len(tests_to_run), "test")) 229 _log.info('') 198 230 if not self._set_up_run(tests_to_run): 199 231 return test_run_results.RunDetails(exit_code=-1) 200 232 233 initial_results, retry_results, enabled_pixel_tests_in_retry = self._run_test_subset(default_device_tests, tests_to_skip) 234 235 for device_class in custom_device_tests: 236 device_tests = custom_device_tests[device_class] 237 if device_tests: 238 _log.info('') 239 _log.info('Running %s for %s', pluralize(len(device_tests), "test"), device_class) 240 _log.info('') 241 if not self._set_up_run(device_tests, device_class): 242 return test_run_results.RunDetails(exit_code=-1) 243 244 device_initial_results, device_retry_results, device_enabled_pixel_tests_in_retry = self._run_test_subset(device_tests, tests_to_skip) 245 246 initial_results = initial_results.merge(device_initial_results) if initial_results else device_initial_results 247 retry_results = retry_results.merge(device_retry_results) if retry_results else device_retry_results 248 enabled_pixel_tests_in_retry |= device_enabled_pixel_tests_in_retry 249 250 end_time = time.time() 251 return self._end_test_run(start_time, end_time, initial_results, retry_results, enabled_pixel_tests_in_retry) 252 253 def _run_test_subset(self, tests_to_run, tests_to_skip): 254 try: 201 255 enabled_pixel_tests_in_retry = False 202 initial_results = self._run_tests(tests_to_run, tests_to_skip, self._options.repeat_each, self._options.iterations, 203 int(self._options.child_processes), retrying=False) 256 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) 204 257 205 258 tests_to_retry = self._tests_to_retry(initial_results, include_crashes=self._port.should_retry_crashes()) … … 212 265 _log.info("Retrying %s ..." % pluralize(len(tests_to_retry), "unexpected failure")) 213 266 _log.info('') 214 retry_results = self._run_tests(tests_to_retry, tests_to_skip=set(), repeat_each=1, iterations=1, 215 num_workers=1, retrying=True) 267 retry_results = self._run_tests(tests_to_retry, tests_to_skip=set(), repeat_each=1, iterations=1, num_workers=1, retrying=True) 216 268 217 269 if enabled_pixel_tests_in_retry: … … 222 274 self._clean_up_run() 223 275 224 end_time = time.time() 225 276 return (initial_results, retry_results, enabled_pixel_tests_in_retry) 277 278 def _end_test_run(self, start_time, end_time, initial_results, retry_results, enabled_pixel_tests_in_retry): 226 279 # Some crash logs can take a long time to be written out so look 227 280 # for new logs after the test run finishes. 281 228 282 _log.debug("looking for new crash logs") 229 283 self._look_for_new_crash_logs(initial_results, start_time) … … 260 314 261 315 test_inputs = self._get_test_inputs(tests_to_run, repeat_each, iterations) 316 262 317 return self._runner.run_tests(self._expectations, test_inputs, tests_to_skip, num_workers, needs_http, needs_websockets, needs_web_platform_test_server, retrying) 263 318 -
trunk/Tools/Scripts/webkitpy/layout_tests/controllers/manager_unittest.py
r192944 r204477 38 38 from webkitpy.layout_tests.models import test_expectations 39 39 from webkitpy.layout_tests.models.test_run_results import TestRunResults 40 from webkitpy.port.test import TestPort 40 41 from webkitpy.thirdparty.mock import Mock 41 42 from webkitpy.tool.mocktool import MockOptions … … 100 101 manager = get_manager() 101 102 manager._look_for_new_crash_logs(run_results, time.time()) 103 104 def test_uses_custom_device(self): 105 class MockCustomDevicePort(TestPort): 106 CUSTOM_DEVICE_CLASSES = ['starship'] 107 def __init__(self, host): 108 super(MockCustomDevicePort, self).__init__(host) 109 110 def get_manager(): 111 host = MockHost() 112 port = MockCustomDevicePort(host) 113 manager = Manager(port, options=MockOptions(test_list=['fast/test-starship/lasers.html'], http=True), printer=Mock()) 114 return manager 115 116 manager = get_manager() 117 self.assertTrue(manager._custom_device_for_test('fast/test-starship/lasers.html') == 'starship') 118 -
trunk/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py
r177471 r204477 71 71 expected_filename = self._port.expected_filename(self._test_name, suffix) 72 72 if self._filesystem.exists(expected_filename): 73 _log.error('%s is a reftest, but has an unused expectation file. Please remove %s.', 74 self._test_name, expected_filename) 73 _log.error('%s is a reftest, but has an unused expectation file. Please remove %s.', self._test_name, expected_filename) 75 74 76 75 def _expected_driver_output(self): -
trunk/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py
r202362 r204477 94 94 self.slow_tests.add(test_result.test_name) 95 95 96 def merge(self, test_run_results): 97 # self.expectations should be the same for both 98 self.total += test_run_results.total 99 self.remaining += test_run_results.remaining 100 self.expected += test_run_results.expected 101 self.unexpected += test_run_results.unexpected 102 self.unexpected_failures += test_run_results.unexpected_failures 103 self.unexpected_crashes += test_run_results.unexpected_crashes 104 self.unexpected_timeouts += test_run_results.unexpected_timeouts 105 self.tests_by_expectation.update(test_run_results.tests_by_expectation) 106 self.tests_by_timeline.update(test_run_results.tests_by_timeline) 107 self.results_by_name.update(test_run_results.results_by_name) 108 self.all_results += test_run_results.all_results 109 self.unexpected_results_by_name.update(test_run_results.unexpected_results_by_name) 110 self.failures_by_name.update(test_run_results.failures_by_name) 111 self.total_failures += test_run_results.total_failures 112 self.expected_skips += test_run_results.expected_skips 113 self.tests_by_expectation.update(test_run_results.tests_by_expectation) 114 self.tests_by_timeline.update(test_run_results.tests_by_timeline) 115 self.slow_tests.update(test_run_results.slow_tests) 116 117 self.interrupted |= test_run_results.interrupted 118 self.keyboard_interrupted |= test_run_results.keyboard_interrupted 119 return self 96 120 97 121 class RunDetails(object): -
trunk/Tools/Scripts/webkitpy/layout_tests/views/printing.py
r202819 r204477 113 113 def print_workers_and_shards(self, num_workers, num_shards): 114 114 driver_name = self._port.driver_name() 115 116 device_suffix = ' for device "{}"'.format(self._options.device_class) if self._options.device_class else '' 115 117 if num_workers == 1: 116 self._print_default( "Running 1 %s." % driver_name)117 self._print_debug( "(%s)." % grammar.pluralize(num_shards, "shard"))118 else: 119 self._print_default( "Running %s in parallel." % (grammar.pluralize(num_workers, driver_name)))120 self._print_debug( "(%d shards)." % num_shards)118 self._print_default('Running 1 {}{}.'.format(driver_name, device_suffix)) 119 self._print_debug('({}).'.format(grammar.pluralize(num_shards, "shard"))) 120 else: 121 self._print_default('Running {} in parallel{}.'.format(grammar.pluralize(num_workers, driver_name), device_suffix)) 122 self._print_debug('({} shards).'.format(num_shards)) 121 123 self._print_default('') 122 124 -
trunk/Tools/Scripts/webkitpy/port/base.py
r204332 r204477 83 83 DEFAULT_ARCHITECTURE = 'x86' 84 84 85 CUSTOM_DEVICE_CLASSES = [] 86 85 87 @classmethod 86 88 def determine_full_port_name(cls, host, options, port_name): … … 804 806 return self._build_path('layout-test-results') 805 807 806 def setup_test_run(self ):808 def setup_test_run(self, device_class=None): 807 809 """Perform port-specific work at the beginning of a test run.""" 808 810 pass -
trunk/Tools/Scripts/webkitpy/port/driver.py
r204253 r204477 53 53 self.args = args or [] 54 54 55 def __repr__(self): 56 return "DriverInput(test_name='{}', timeout={}, image_hash={}, should_run_pixel_test={}'".format(self.test_name, self.timeout, self.image_hash, self.should_run_pixel_test) 57 55 58 56 59 class DriverOutput(object): … … 588 591 589 592 593 # FIXME: this should be abstracted out via the Port subclass somehow. 590 594 class IOSSimulatorDriver(Driver): 591 595 def cmd_line(self, pixel_tests, per_test_args): -
trunk/Tools/Scripts/webkitpy/port/efl.py
r203674 r204477 55 55 return "--efl" 56 56 57 def setup_test_run(self ):58 super(EflPort, self).setup_test_run( )57 def setup_test_run(self, device_class=None): 58 super(EflPort, self).setup_test_run(device_class) 59 59 self._pulseaudio_sanitizer.unload_pulseaudio_module() 60 60 -
trunk/Tools/Scripts/webkitpy/port/gtk.py
r203674 r204477 101 101 return super(GtkPort, self).driver_stop_timeout() 102 102 103 def setup_test_run(self ):104 super(GtkPort, self).setup_test_run( )103 def setup_test_run(self, device_class=None): 104 super(GtkPort, self).setup_test_run(device_class) 105 105 self._pulseaudio_sanitizer.unload_pulseaudio_module() 106 106 -
trunk/Tools/Scripts/webkitpy/port/ios.py
r204341 r204477 75 75 DEFAULT_ARCHITECTURE = 'x86_64' 76 76 77 DEFAULT_DEVICE_CLASS = 'iphone' 78 CUSTOM_DEVICE_CLASSES = ['ipad'] 79 77 80 SIMULATOR_BUNDLE_ID = 'com.apple.iphonesimulator' 78 81 relay_name = 'LayoutTestRelay' … … 81 84 PROCESS_COUNT_ESTIMATE_PER_SIMULATOR_INSTANCE = 100 82 85 86 DEVICE_CLASS_MAP = { 87 'x86_64': { 88 'iphone': 'iPhone 5s', 89 'ipad': 'iPad Air' 90 }, 91 'x86': { 92 'iphone': 'iPhone 5', 93 'ipad': 'iPad Retina' 94 }, 95 } 96 83 97 def __init__(self, host, port_name, **kwargs): 84 98 super(IOSSimulatorPort, self).__init__(host, port_name, **kwargs) 99 100 optional_device_class = self.get_option('device_class') 101 self._device_class = optional_device_class if optional_device_class else self.DEFAULT_DEVICE_CLASS 102 _log.debug('IOSSimulatorPort _device_class is %s', self._device_class) 85 103 86 104 def driver_name(self): … … 101 119 return runtime 102 120 103 @property104 @memoized105 121 def simulator_device_type(self): 106 122 device_type_identifier = self.get_option('device_type') 107 123 if device_type_identifier: 124 _log.debug('simulator_device_type for device identifier %s', device_type_identifier) 108 125 device_type = DeviceType.from_identifier(device_type_identifier) 109 126 else: 110 if self.architecture() == 'x86_64': 111 device_type = DeviceType.from_name('iPhone 5s') 112 else: 113 device_type = DeviceType.from_name('iPhone 5') 127 _log.debug('simulator_device_type for device %s', self._device_class) 128 device_name = self.DEVICE_CLASS_MAP[self.architecture()][self._device_class] 129 if not device_name: 130 raise Exception('Failed to find device for architecture {} and device class {}'.format(self.architecture()), self._device_class) 131 device_type = DeviceType.from_name(device_name) 114 132 return device_type 115 133 … … 206 224 return list(reversed([self._filesystem.join(self._webkit_baseline_path(p), 'TestExpectations') for p in self.baseline_search_path()])) 207 225 208 def setup_test_run(self): 226 def _set_device_class(self, device_class): 227 # Ideally we'd ensure that no simulators are running when this is called. 228 self._device_class = device_class if device_class else self.DEFAULT_DEVICE_CLASS 229 230 def _create_simulators(self): 231 if (self.default_child_processes() < self.child_processes()): 232 _log.warn("You have specified very high value({0}) for --child-processes".format(self.child_processes())) 233 _log.warn("maximum child-processes which can be supported on this system are: {0}".format(self.default_child_processes())) 234 _log.warn("This is very likely to fail.") 235 236 self._createSimulatorApps() 237 238 for i in xrange(self.child_processes()): 239 Simulator.wait_until_device_is_in_state(self.testing_device(i).udid, Simulator.DeviceState.SHUTDOWN) 240 Simulator.reset_device(self.testing_device(i).udid) 241 242 def setup_test_run(self, device_class=None): 209 243 mac_os_version = self.host.platform.os_version 244 245 self._set_device_class(device_class) 246 247 _log.debug('') 248 _log.debug('setup_test_run for %s', self._device_class) 249 250 self._create_simulators() 251 210 252 for i in xrange(self.child_processes()): 211 253 device_udid = self.testing_device(i).udid 254 _log.debug('testing device %s has udid %s', i, device_udid) 255 212 256 # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator 213 214 257 self._executive.run_command([ 215 258 'open', '-g', '-b', self.SIMULATOR_BUNDLE_ID + str(i), … … 285 328 _log.error('The iOS Simulator runtime with identifier "{0}" cannot be used because it is unavailable.'.format(self.simulator_runtime.identifier)) 286 329 return False 287 for i in xrange(self.child_processes()):288 # FIXME: This creates the devices sequentially, doing this in parallel can improve performance.289 testing_device = self.testing_device(i)290 330 return super(IOSSimulatorPort, self).check_sys_deps(needs_http) 291 331 … … 330 370 return stderr, crash_log 331 371 332 @memoized333 372 def testing_device(self, number): 334 return Simulator().lookup_or_create_device(self.simulator_device_type.name + ' WebKit Tester' + str(number), self.simulator_device_type, self.simulator_runtime) 373 # FIXME: rather than calling lookup_or_create_device every time, we should just store a mapping of 374 # number to device_udid. 375 device_type = self.simulator_device_type() 376 _log.debug(' testing_device %s using device_type %s', number, device_type) 377 return Simulator().lookup_or_create_device(device_type.name + ' WebKit Tester' + str(number), device_type, self.simulator_runtime) 335 378 336 379 def get_simulator_path(self, suffix=""): … … 351 394 def reset_preferences(self): 352 395 _log.debug("reset_preferences") 353 if (self.default_child_processes() < self.child_processes()):354 _log.warn("You have specified very high value({0}) for --child-processes".format(self.child_processes()))355 _log.warn("maximum child-processes which can be supported on this system are: {0}".format(self.default_child_processes()))356 _log.warn("This is very likely to fail.")357 358 396 self._quit_ios_simulator() 359 self._createSimulatorApps() 360 361 for i in xrange(self.child_processes()): 362 Simulator.wait_until_device_is_in_state(self.testing_device(i).udid, Simulator.DeviceState.SHUTDOWN) 363 Simulator.reset_device(self.testing_device(i).udid) 397 # Maybe this should delete all devices that we've created? 364 398 365 399 def nm_command(self): -
trunk/Tools/Scripts/webkitpy/port/test.py
r202362 r204477 458 458 return '/tmp/layout-test-results' 459 459 460 def setup_test_run(self ):460 def setup_test_run(self, device_class=None): 461 461 pass 462 462 -
trunk/Tools/Scripts/webkitpy/port/win.py
r204271 r204477 346 346 os.system("rm -rf /dev/shm/sem.*") 347 347 348 def setup_test_run(self ):348 def setup_test_run(self, device_class=None): 349 349 atexit.register(self.restore_crash_log_saving) 350 350 self.setup_crash_log_saving() 351 351 self.prevent_error_dialogs() 352 352 self.delete_sem_locks() 353 super(WinPort, self).setup_test_run( )353 super(WinPort, self).setup_test_run(device_class) 354 354 355 355 def clean_up_test_run(self):
Note:
See TracChangeset
for help on using the changeset viewer.