Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py

Issue 16917006: Only consider Android devices with more than 30% battery remaining for layout tests (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (C) 2012 Google Inc. All rights reserved. 1 # Copyright (C) 2012 Google Inc. All rights reserved.
2 # 2 #
3 # Redistribution and use in source and binary forms, with or without 3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are 4 # modification, are permitted provided that the following conditions are
5 # met: 5 # met:
6 # 6 #
7 # * Redistributions of source code must retain the above copyright 7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer. 8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above 9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer 10 # copyright notice, this list of conditions and the following disclaimer
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 continue 268 continue
269 269
270 command_path = path_option 270 command_path = path_option
271 command_version = path_version 271 command_version = path_version
272 272
273 assert command_path, 'Unable to locate the "adb" command. Are you using an Android checkout of Chromium?' 273 assert command_path, 'Unable to locate the "adb" command. Are you using an Android checkout of Chromium?'
274 274
275 AndroidCommands._adb_command_path = command_path 275 AndroidCommands._adb_command_path = command_path
276 return command_path 276 return command_path
277 277
278 @staticmethod
279 def get_devices(executive):
280 re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE)
281 result = executive.run_command([AndroidCommands.adb_command_path(executi ve), 'devices'],
282 error_handler=executive.ignore_error)
283 devices = re_device.findall(result)
284 if not devices:
285 raise AssertionError('No devices attached. Result of "adb devices": %s' % result)
286
287 return devices
288
289 # Local private methods. 278 # Local private methods.
290 279
291 def _log_error(self, message): 280 def _log_error(self, message):
292 _log.error('[%s] %s' % (self._device_serial, message)) 281 _log.error('[%s] %s' % (self._device_serial, message))
293 282
294 def _log_debug(self, message): 283 def _log_debug(self, message):
295 _log.debug('[%s] %s' % (self._device_serial, message)) 284 _log.debug('[%s] %s' % (self._device_serial, message))
296 285
297 @staticmethod 286 @staticmethod
298 def _determine_adb_version(adb_command_path, executive): 287 def _determine_adb_version(adb_command_path, executive):
299 re_version = re.compile('^.*version ([\d\.]+)$') 288 re_version = re.compile('^.*version ([\d\.]+)$')
300 try: 289 try:
301 output = executive.run_command([adb_command_path, 'version'], error_ handler=executive.ignore_error) 290 output = executive.run_command([adb_command_path, 'version'], error_ handler=executive.ignore_error)
302 except OSError: 291 except OSError:
303 return None 292 return None
304 293
305 result = re_version.match(output) 294 result = re_version.match(output)
306 if not output or not result: 295 if not output or not result:
307 return None 296 return None
308 297
309 return [int(n) for n in result.group(1).split('.')] 298 return [int(n) for n in result.group(1).split('.')]
310 299
300
301 # A class to encapsulate device status and information, such as the AndroidComma nds
302 # instances and whether the device has been set up.
303 class AndroidDevices(object):
304 # Percentage of battery a device needs to have in order for it to be conside red
305 # to participate in running the layout tests.
306 MINIMUM_BATTERY_PERCENTAGE = 30
307
308 def __init__(self, executive, default_device=None):
309 self._usable_devices = []
310 self._default_device = default_device
311 self._prepared_devices = []
312
313 def usable_devices(self, executive):
314 if self._usable_devices:
315 return self._usable_devices
316
317 if self._default_device:
318 self._usable_devices = [AndroidCommands(executive, self._default_dev ice)]
319 return self._usable_devices
320
321 # Example "adb devices" command output:
322 # List of devices attached
323 # 0123456789ABCDEF device
324 re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE)
325
326 result = executive.run_command([AndroidCommands.adb_command_path(executi ve), 'devices'],
327 error_handler=executive.ignore_error)
328 devices = re_device.findall(result)
329 if not devices:
330 raise AssertionError('Unable to find attached Android devices. ADB o utput: %s' % result)
331
332 for device_serial in devices:
333 commands = AndroidCommands(executive, device_serial)
334 if self._battery_level_for_device(commands) < AndroidDevices.MINIMUM _BATTERY_PERCENTAGE:
335 continue
336
337 self._usable_devices.append(commands)
338
339 if not self._usable_devices:
340 raise AssertionError('No devices attached with more than %d percent battery.' %
341 AndroidDevices.MINIMUM_BATTERY_PERCENTAGE)
342
343 return self._usable_devices
344
345 def get_device(self, executive, device_index):
346 devices = self.usable_devices(executive)
347 if device_index >= len(devices):
348 raise AssertionError('Device index exceeds number of usable devices. ')
349
350 return devices[device_index]
351
352 def is_device_prepared(self, device_serial):
353 return device_serial in self._prepared_devices
354
355 def set_device_prepared(self, device_serial):
356 self._prepared_devices.append(device_serial)
357
358 # Private methods
359 def _battery_level_for_device(self, commands):
360 battery_status = commands.run(['shell', 'dumpsys', 'battery'])
361 if 'Error' in battery_status:
362 _log.warning('Unable to read the battery level from device with seri al "%s".' % commands.get_serial())
363 return 100
364
365 return int(re.findall('level: (\d+)', battery_status)[0])
366
367
311 class ChromiumAndroidPort(chromium.ChromiumPort): 368 class ChromiumAndroidPort(chromium.ChromiumPort):
312 port_name = 'chromium-android' 369 port_name = 'chromium-android'
313 370
314 # Avoid initializing the adb path [worker count]+1 times by storing it as a static member. 371 # Avoid initializing the adb path [worker count]+1 times by storing it as a static member.
315 _adb_path = None 372 _adb_path = None
316 373
317 SUPPORTED_VERSIONS = ('android') 374 SUPPORTED_VERSIONS = ('android')
318 375
319 FALLBACK_PATHS = { 'android': [ 'chromium-android' ] + chromium_linux.Chromi umLinuxPort.latest_platform_fallback_path() } 376 FALLBACK_PATHS = { 'android': [ 'chromium-android' ] + chromium_linux.Chromi umLinuxPort.latest_platform_fallback_path() }
320 377
321 def __init__(self, host, port_name, **kwargs): 378 def __init__(self, host, port_name, **kwargs):
322 super(ChromiumAndroidPort, self).__init__(host, port_name, **kwargs) 379 super(ChromiumAndroidPort, self).__init__(host, port_name, **kwargs)
323 380
324 self._operating_system = 'android' 381 self._operating_system = 'android'
325 self._version = 'icecreamsandwich' 382 self._version = 'icecreamsandwich'
326 383
327 self._host_port = factory.PortFactory(host).get('chromium', **kwargs) 384 self._host_port = factory.PortFactory(host).get('chromium', **kwargs)
328 self._server_process_constructor = self._android_server_process_construc tor 385 self._server_process_constructor = self._android_server_process_construc tor
329 386
330 if self.driver_name() == 'content_shell': 387 if self.driver_name() == 'content_shell':
331 self._driver_details = ContentShellDriverDetails() 388 self._driver_details = ContentShellDriverDetails()
332 else: 389 else:
333 self._driver_details = DumpRenderTreeDriverDetails() 390 self._driver_details = DumpRenderTreeDriverDetails()
334 391
392 # Initialize the AndroidDevices class which tracks available devices.
393 default_device = None
335 if hasattr(self._options, 'adb_device') and len(self._options.adb_device ): 394 if hasattr(self._options, 'adb_device') and len(self._options.adb_device ):
336 self._devices = [self._options.adb_device] 395 default_device = self._options.adb_device
337 else:
338 self._devices = []
339 396
397 self._devices = AndroidDevices(self._executive, default_device)
398
399 # Tell AndroidCommands where to search for the "adb" command.
340 AndroidCommands.set_adb_command_path_options(['adb', 400 AndroidCommands.set_adb_command_path_options(['adb',
341 self.path_from_chromium_base('third_party', 'android_tools', 'sdk', 'platform-tools', 'adb')]) 401 self.path_from_chromium_base('third_party', 'android_tools', 'sdk', 'platform-tools', 'adb')])
342 402
343 # Local public methods. 403 # Local public methods.
344 def get_device_serial(self, worker_number):
345 if not self._devices:
346 self._devices = AndroidCommands.get_devices(self._executive)
347 if worker_number >= len(self._devices):
348 raise AssertionError('Worker number exceeds available number of devi ces')
349 return self._devices[worker_number]
350
351 def path_to_forwarder(self): 404 def path_to_forwarder(self):
352 return self._build_path('forwarder') 405 return self._build_path('forwarder')
353 406
354 def path_to_md5sum(self): 407 def path_to_md5sum(self):
355 return self._build_path(MD5SUM_DEVICE_FILE_NAME) 408 return self._build_path(MD5SUM_DEVICE_FILE_NAME)
356 409
357 def path_to_md5sum_host(self): 410 def path_to_md5sum_host(self):
358 return self._build_path(MD5SUM_HOST_FILE_NAME) 411 return self._build_path(MD5SUM_HOST_FILE_NAME)
359 412
360 # Overridden public methods. 413 # Overridden public methods.
(...skipping 19 matching lines...) Expand all
380 return 0.0 433 return 0.0
381 434
382 def driver_name(self): 435 def driver_name(self):
383 if self.get_option('driver_name'): 436 if self.get_option('driver_name'):
384 return self.get_option('driver_name') 437 return self.get_option('driver_name')
385 if self.get_option('content_shell'): 438 if self.get_option('content_shell'):
386 return self.CONTENT_SHELL_NAME 439 return self.CONTENT_SHELL_NAME
387 return 'DumpRenderTree' 440 return 'DumpRenderTree'
388 441
389 def default_child_processes(self): 442 def default_child_processes(self):
390 if self._devices: 443 usable_device_count = len(self._devices.usable_devices(self._executive))
391 return len(self._devices) 444 if not usable_device_count:
445 raise AssertionError('There are no devices available to run the layo ut tests on.')
392 446
393 return len(AndroidCommands.get_devices(self._executive)) 447 return usable_device_count
394 448
395 def default_baseline_search_path(self): 449 def default_baseline_search_path(self):
396 return map(self._webkit_baseline_path, self.FALLBACK_PATHS['android']) 450 return map(self._webkit_baseline_path, self.FALLBACK_PATHS['android'])
397 451
398 def check_wdiff(self, logging=True): 452 def check_wdiff(self, logging=True):
399 return self._host_port.check_wdiff(logging) 453 return self._host_port.check_wdiff(logging)
400 454
401 def check_build(self, needs_http): 455 def check_build(self, needs_http):
402 result = super(ChromiumAndroidPort, self).check_build(needs_http) 456 result = super(ChromiumAndroidPort, self).check_build(needs_http)
403 result = self._check_file_exists(self.path_to_md5sum(), 'md5sum utility' ) and result 457 result = self._check_file_exists(self.path_to_md5sum(), 'md5sum utility' ) and result
(...skipping 25 matching lines...) Expand all
429 return True 483 return True
430 484
431 def start_http_server(self, additional_dirs=None, number_of_servers=0): 485 def start_http_server(self, additional_dirs=None, number_of_servers=0):
432 if not additional_dirs: 486 if not additional_dirs:
433 additional_dirs = {} 487 additional_dirs = {}
434 additional_dirs[PERF_TEST_PATH_PREFIX] = self.perf_tests_dir() 488 additional_dirs[PERF_TEST_PATH_PREFIX] = self.perf_tests_dir()
435 additional_dirs[LAYOUT_TEST_PATH_PREFIX] = self.layout_tests_dir() 489 additional_dirs[LAYOUT_TEST_PATH_PREFIX] = self.layout_tests_dir()
436 super(ChromiumAndroidPort, self).start_http_server(additional_dirs, numb er_of_servers) 490 super(ChromiumAndroidPort, self).start_http_server(additional_dirs, numb er_of_servers)
437 491
438 def create_driver(self, worker_number, no_timeout=False): 492 def create_driver(self, worker_number, no_timeout=False):
439 return ChromiumAndroidDriver(self, worker_number, pixel_tests=self.get_o ption('pixel_tests'), driver_details=self._driver_details, 493 return ChromiumAndroidDriver(self, worker_number, pixel_tests=self.get_o ption('pixel_tests'),
494 driver_details=self._driver_details,
495 android_devices=self._devices,
440 # Force no timeout to avoid test driver tim eouts before NRWT. 496 # Force no timeout to avoid test driver tim eouts before NRWT.
441 no_timeout=True) 497 no_timeout=True)
442 498
443 def driver_cmd_line(self): 499 def driver_cmd_line(self):
444 # Override to return the actual test driver's command line. 500 # Override to return the actual test driver's command line.
445 return self.create_driver(0)._android_driver_cmd_line(self.get_option('p ixel_tests'), []) 501 return self.create_driver(0)._android_driver_cmd_line(self.get_option('p ixel_tests'), [])
446 502
447 # Overridden protected methods. 503 # Overridden protected methods.
448 504
449 def _port_specific_expectations_files(self): 505 def _port_specific_expectations_files(self):
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
614 for instructions on installing pre-built copies of perfhost_linux 670 for instructions on installing pre-built copies of perfhost_linux
615 http://crbug.com/165250 discusses making these pre-built binaries externally ava ilable. 671 http://crbug.com/165250 discusses making these pre-built binaries externally ava ilable.
616 """ 672 """
617 673
618 perfhost_display_patch = perfhost_path if perfhost_path else 'perfhost_l inux' 674 perfhost_display_patch = perfhost_path if perfhost_path else 'perfhost_l inux'
619 print "To view the full profile, run:" 675 print "To view the full profile, run:"
620 print ' '.join([perfhost_display_patch] + perfhost_report_command) 676 print ' '.join([perfhost_display_patch] + perfhost_report_command)
621 677
622 678
623 class ChromiumAndroidDriver(driver.Driver): 679 class ChromiumAndroidDriver(driver.Driver):
624 def __init__(self, port, worker_number, pixel_tests, driver_details, no_time out=False): 680 def __init__(self, port, worker_number, pixel_tests, driver_details, android _devices, no_timeout=False):
625 super(ChromiumAndroidDriver, self).__init__(port, worker_number, pixel_t ests, no_timeout) 681 super(ChromiumAndroidDriver, self).__init__(port, worker_number, pixel_t ests, no_timeout)
626 self._in_fifo_path = driver_details.device_fifo_directory() + 'stdin.fif o' 682 self._in_fifo_path = driver_details.device_fifo_directory() + 'stdin.fif o'
627 self._out_fifo_path = driver_details.device_fifo_directory() + 'test.fif o' 683 self._out_fifo_path = driver_details.device_fifo_directory() + 'test.fif o'
628 self._err_fifo_path = driver_details.device_fifo_directory() + 'stderr.f ifo' 684 self._err_fifo_path = driver_details.device_fifo_directory() + 'stderr.f ifo'
629 self._read_stdout_process = None 685 self._read_stdout_process = None
630 self._read_stderr_process = None 686 self._read_stderr_process = None
631 self._forwarder_process = None 687 self._forwarder_process = None
632 self._has_setup = False
633 self._original_governors = {} 688 self._original_governors = {}
634 self._original_kptr_restrict = None 689 self._original_kptr_restrict = None
635 690
636 self._android_commands = AndroidCommands(port._executive, port.get_devic e_serial(worker_number)) 691 self._android_devices = android_devices
692 self._android_commands = android_devices.get_device(port._executive, wor ker_number)
637 self._driver_details = driver_details 693 self._driver_details = driver_details
638 694
639 # FIXME: If we taught ProfileFactory about "target" devices we could 695 # FIXME: If we taught ProfileFactory about "target" devices we could
640 # just use the logic in Driver instead of duplicating it here. 696 # just use the logic in Driver instead of duplicating it here.
641 if self._port.get_option("profile"): 697 if self._port.get_option("profile"):
642 # FIXME: This should be done once, instead of per-driver! 698 # FIXME: This should be done once, instead of per-driver!
643 symfs_path = self._find_or_create_symfs() 699 symfs_path = self._find_or_create_symfs()
644 kallsyms_path = self._update_kallsyms_cache(symfs_path) 700 kallsyms_path = self._update_kallsyms_cache(symfs_path)
645 # FIXME: We should pass this some sort of "Bridge" object abstractio n around ADB instead of a path/device pair. 701 # FIXME: We should pass this some sort of "Bridge" object abstractio n around ADB instead of a path/device pair.
646 self._profiler = AndroidPerf(self._port.host, self._port._path_to_dr iver(), self._port.results_directory(), 702 self._profiler = AndroidPerf(self._port.host, self._port._path_to_dr iver(), self._port.results_directory(),
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
703 if not self._android_commands.file_exists(MD5SUM_DEVICE_PATH): 759 if not self._android_commands.file_exists(MD5SUM_DEVICE_PATH):
704 if not self._android_commands.push(self._md5sum_path, MD5SUM_DEVICE_ PATH): 760 if not self._android_commands.push(self._md5sum_path, MD5SUM_DEVICE_ PATH):
705 raise AssertionError('Could not push md5sum to device') 761 raise AssertionError('Could not push md5sum to device')
706 762
707 self._push_executable() 763 self._push_executable()
708 self._push_fonts() 764 self._push_fonts()
709 self._push_test_resources() 765 self._push_test_resources()
710 self._push_platform_resources() 766 self._push_platform_resources()
711 767
712 def _setup_test(self): 768 def _setup_test(self):
713 if self._has_setup: 769 if self._android_devices.is_device_prepared(self._android_commands.get_s erial()):
714 return 770 return
715 771
716 self._android_commands.restart_as_root() 772 self._android_commands.restart_as_root()
717 self._setup_md5sum_and_push_data_if_needed() 773 self._setup_md5sum_and_push_data_if_needed()
718 self._has_setup = True
719 self._setup_performance() 774 self._setup_performance()
720 775
721 # Required by webkit_support::GetWebKitRootDirFilePath(). 776 # Required by webkit_support::GetWebKitRootDirFilePath().
722 # Other directories will be created automatically by adb push. 777 # Other directories will be created automatically by adb push.
723 self._android_commands.mkdir(DEVICE_SOURCE_ROOT_DIR + 'chrome') 778 self._android_commands.mkdir(DEVICE_SOURCE_ROOT_DIR + 'chrome')
724 779
725 # Allow the test driver to get full read and write access to the directo ry on the device, 780 # Allow the test driver to get full read and write access to the directo ry on the device,
726 # as well as for the FIFOs. We'll need a world writable directory. 781 # as well as for the FIFOs. We'll need a world writable directory.
727 self._android_commands.mkdir(self._driver_details.device_directory(), ch mod='777') 782 self._android_commands.mkdir(self._driver_details.device_directory(), ch mod='777')
728 self._android_commands.mkdir(self._driver_details.device_fifo_directory( ), chmod='777') 783 self._android_commands.mkdir(self._driver_details.device_fifo_directory( ), chmod='777')
729 784
730 # Make sure that the disk cache on the device resets to a clean state. 785 # Make sure that the disk cache on the device resets to a clean state.
731 self._android_commands.run(['shell', 'rm', '-r', self._driver_details.de vice_cache_directory()]) 786 self._android_commands.run(['shell', 'rm', '-r', self._driver_details.de vice_cache_directory()])
732 787
788 # Mark this device as having been set up.
789 self._android_devices.set_device_prepared(self._android_commands.get_ser ial())
790
733 def _log_error(self, message): 791 def _log_error(self, message):
734 _log.error('[%s] %s' % (self._android_commands.get_serial(), message)) 792 _log.error('[%s] %s' % (self._android_commands.get_serial(), message))
735 793
736 def _log_debug(self, message): 794 def _log_debug(self, message):
737 _log.debug('[%s] %s' % (self._android_commands.get_serial(), message)) 795 _log.debug('[%s] %s' % (self._android_commands.get_serial(), message))
738 796
739 def _abort(self, message): 797 def _abort(self, message):
740 raise AssertionError('[%s] %s' % (self._android_commands.get_serial(), m essage)) 798 raise AssertionError('[%s] %s' % (self._android_commands.get_serial(), m essage))
741 799
742 @staticmethod 800 @staticmethod
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after
1001 if self._read_stderr_process: 1059 if self._read_stderr_process:
1002 self._read_stderr_process.kill() 1060 self._read_stderr_process.kill()
1003 self._read_stderr_process = None 1061 self._read_stderr_process = None
1004 1062
1005 super(ChromiumAndroidDriver, self).stop() 1063 super(ChromiumAndroidDriver, self).stop()
1006 1064
1007 if self._forwarder_process: 1065 if self._forwarder_process:
1008 self._forwarder_process.kill() 1066 self._forwarder_process.kill()
1009 self._forwarder_process = None 1067 self._forwarder_process = None
1010 1068
1011 if self._has_setup: 1069 if self._android_devices.is_device_prepared(self._android_commands.get_s erial()):
1012 if not ChromiumAndroidDriver._loop_with_timeout(self._remove_all_pip es, DRIVER_START_STOP_TIMEOUT_SECS): 1070 if not ChromiumAndroidDriver._loop_with_timeout(self._remove_all_pip es, DRIVER_START_STOP_TIMEOUT_SECS):
1013 raise AssertionError('Failed to remove fifo files. May be locked .') 1071 raise AssertionError('Failed to remove fifo files. May be locked .')
1014 1072
1015 def _command_from_driver_input(self, driver_input): 1073 def _command_from_driver_input(self, driver_input):
1016 command = super(ChromiumAndroidDriver, self)._command_from_driver_input( driver_input) 1074 command = super(ChromiumAndroidDriver, self)._command_from_driver_input( driver_input)
1017 if command.startswith('/'): 1075 if command.startswith('/'):
1018 fs = self._port._filesystem 1076 fs = self._port._filesystem
1019 # FIXME: what happens if command lies outside of the layout_tests_di r on the host? 1077 # FIXME: what happens if command lies outside of the layout_tests_di r on the host?
1020 relative_test_filename = fs.relpath(command, fs.dirname(self._port.l ayout_tests_dir())) 1078 relative_test_filename = fs.relpath(command, fs.dirname(self._port.l ayout_tests_dir()))
1021 command = DEVICE_WEBKIT_BASE_DIR + relative_test_filename 1079 command = DEVICE_WEBKIT_BASE_DIR + relative_test_filename
1022 return command 1080 return command
1023 1081
1024 def _read_prompt(self, deadline): 1082 def _read_prompt(self, deadline):
1025 last_char = '' 1083 last_char = ''
1026 while True: 1084 while True:
1027 current_char = self._server_process.read_stdout(deadline, 1) 1085 current_char = self._server_process.read_stdout(deadline, 1)
1028 if current_char == ' ': 1086 if current_char == ' ':
1029 if last_char in ('#', '$'): 1087 if last_char in ('#', '$'):
1030 return 1088 return
1031 last_char = current_char 1089 last_char = current_char
OLDNEW
« no previous file with comments | « no previous file | Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698