Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Provides a variety of device interactions based on adb. | 5 """Provides a variety of device interactions based on adb. |
| 6 | 6 |
| 7 Eventually, this will be based on adb_wrapper. | 7 Eventually, this will be based on adb_wrapper. |
| 8 """ | 8 """ |
| 9 # pylint: disable=unused-argument | 9 # pylint: disable=unused-argument |
| 10 | 10 |
| (...skipping 467 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 478 retries: number of retries | 478 retries: number of retries |
| 479 | 479 |
| 480 Raises: | 480 Raises: |
| 481 CommandFailedError if the installation fails. | 481 CommandFailedError if the installation fails. |
| 482 CommandTimeoutError if the installation times out. | 482 CommandTimeoutError if the installation times out. |
| 483 DeviceUnreachableError on missing device. | 483 DeviceUnreachableError on missing device. |
| 484 """ | 484 """ |
| 485 package_name = apk_helper.GetPackageName(apk_path) | 485 package_name = apk_helper.GetPackageName(apk_path) |
| 486 device_path = self.GetApplicationPath(package_name) | 486 device_path = self.GetApplicationPath(package_name) |
| 487 if device_path is not None: | 487 if device_path is not None: |
| 488 should_install = bool(self._GetChangedFilesImpl(apk_path, device_path)) | 488 (files_to_push, _) = self._GetChangedAndStaleFiles( |
| 489 apk_path, device_path) | |
| 490 should_install = bool(files_to_push) | |
| 489 if should_install and not reinstall: | 491 if should_install and not reinstall: |
| 490 self.adb.Uninstall(package_name) | 492 self.adb.Uninstall(package_name) |
| 491 else: | 493 else: |
| 492 should_install = True | 494 should_install = True |
| 493 if should_install: | 495 if should_install: |
| 494 self.adb.Install(apk_path, reinstall=reinstall) | 496 self.adb.Install(apk_path, reinstall=reinstall) |
| 495 | 497 |
| 496 @decorators.WithTimeoutAndRetriesFromInstance() | 498 @decorators.WithTimeoutAndRetriesFromInstance() |
| 497 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, | 499 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, |
| 498 as_root=False, single_line=False, large_output=False, | 500 as_root=False, single_line=False, large_output=False, |
| (...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 825 self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')], | 827 self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')], |
| 826 check_return=True) | 828 check_return=True) |
| 827 | 829 |
| 828 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT | 830 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT |
| 829 PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES | 831 PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES |
| 830 | 832 |
| 831 @decorators.WithTimeoutAndRetriesDefaults( | 833 @decorators.WithTimeoutAndRetriesDefaults( |
| 832 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT, | 834 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT, |
| 833 PUSH_CHANGED_FILES_DEFAULT_RETRIES) | 835 PUSH_CHANGED_FILES_DEFAULT_RETRIES) |
| 834 def PushChangedFiles(self, host_device_tuples, timeout=None, | 836 def PushChangedFiles(self, host_device_tuples, timeout=None, |
| 835 retries=None): | 837 retries=None, delete_device_stale=False): |
| 836 """Push files to the device, skipping files that don't need updating. | 838 """Push files to the device, skipping files that don't need updating. |
| 837 | 839 |
| 840 When a directory is pushed, it is traversed recursively on the host and | |
| 841 all files in it are pushed to the device as needed. | |
| 842 Additionally, if delete_device_stale option is True, | |
| 843 files that exist on the device but don't exist on the host are deleted. | |
| 844 | |
| 838 Args: | 845 Args: |
| 839 host_device_tuples: A list of (host_path, device_path) tuples, where | 846 host_device_tuples: A list of (host_path, device_path) tuples, where |
| 840 |host_path| is an absolute path of a file or directory on the host | 847 |host_path| is an absolute path of a file or directory on the host |
| 841 that should be minimially pushed to the device, and |device_path| is | 848 that should be minimially pushed to the device, and |device_path| is |
| 842 an absolute path of the destination on the device. | 849 an absolute path of the destination on the device. |
| 843 timeout: timeout in seconds | 850 timeout: timeout in seconds |
| 844 retries: number of retries | 851 retries: number of retries |
| 852 delete_device_stale: option to delete stale files on device | |
| 845 | 853 |
| 846 Raises: | 854 Raises: |
| 847 CommandFailedError on failure. | 855 CommandFailedError on failure. |
| 848 CommandTimeoutError on timeout. | 856 CommandTimeoutError on timeout. |
| 849 DeviceUnreachableError on missing device. | 857 DeviceUnreachableError on missing device. |
| 850 """ | 858 """ |
| 851 | 859 |
| 852 files = [] | 860 all_changed_files = [] |
| 861 all_stale_files = [] | |
| 853 for h, d in host_device_tuples: | 862 for h, d in host_device_tuples: |
| 854 if os.path.isdir(h): | 863 if os.path.isdir(h): |
| 855 self.RunShellCommand(['mkdir', '-p', d], check_return=True) | 864 self.RunShellCommand(['mkdir', '-p', d], check_return=True) |
| 856 files += self._GetChangedFilesImpl(h, d) | 865 (changed_files, stale_files) = self._GetChangedAndStaleFiles(h, d) |
| 866 all_changed_files += changed_files | |
| 867 all_stale_files += stale_files | |
| 857 | 868 |
| 858 if not files: | 869 if delete_device_stale: |
| 870 for stale_file_path in all_stale_files: | |
| 871 self.RunShellCommand(['rm', '-f', stale_file_path], | |
|
jbudorick
2015/06/12 19:54:44
Does this work as
self.RunShellCommand(['rm', '
Menglin
2015/06/12 22:31:46
Yes it does, though in the description of the func
perezju
2015/06/15 09:03:52
I guess what John is saying is whether we could re
| |
| 872 check_return=True) | |
| 873 | |
| 874 if not all_changed_files: | |
| 859 return | 875 return |
| 860 | 876 |
| 877 self._PushFilesImpl(host_device_tuples, all_changed_files) | |
| 878 | |
| 879 def _GetChangedAndStaleFiles(self, host_path, device_path): | |
| 880 """Get files to push and delete | |
| 881 | |
| 882 Args: | |
| 883 host_path: an absolute path of a file or directory on the host | |
| 884 device_path: an absolute path of a file or directory on the device | |
| 885 | |
| 886 Returns: | |
| 887 a two-element tuple | |
| 888 1st element: a list of (host_files_path, device_files_path) tuples to push | |
| 889 2nd element: a list of stale files under device_path | |
| 890 """ | |
| 891 real_host_path = os.path.realpath(host_path) | |
| 892 try: | |
| 893 real_device_path = self.RunShellCommand( | |
| 894 ['realpath', device_path], single_line=True, check_return=True) | |
| 895 except device_errors.CommandFailedError: | |
| 896 real_device_path = None | |
| 897 if not real_device_path: | |
| 898 return ([(host_path, device_path)], []) | |
| 899 | |
| 900 try: | |
| 901 host_checksums = md5sum.CalculateHostMd5Sums([real_host_path]) | |
| 902 device_checksums = md5sum.CalculateDeviceMd5Sums( | |
| 903 [real_device_path], self) | |
| 904 except EnvironmentError as e: | |
| 905 logging.warning('Error calculating md5: %s', e) | |
| 906 return ([(host_path, device_path)], []) | |
| 907 | |
| 908 if os.path.isfile(host_path): | |
| 909 host_checksum = host_checksums.get(real_host_path) | |
| 910 device_checksum = device_checksums.get(real_device_path) | |
| 911 if host_checksum != device_checksum: | |
| 912 return ([(host_path, device_path)], []) | |
| 913 else: | |
| 914 return ([], []) | |
| 915 else: | |
| 916 to_push = [] | |
| 917 to_delete = [] | |
|
jbudorick
2015/06/12 19:54:45
nit: delete this line
Menglin
2015/06/12 22:31:46
Done.
| |
| 918 for host_abs_path, host_checksum in host_checksums.iteritems(): | |
| 919 device_abs_path = '%s/%s' % ( | |
| 920 real_device_path, os.path.relpath(host_abs_path, real_host_path)) | |
| 921 device_checksum = device_checksums.pop(device_abs_path, None) | |
| 922 if device_checksum != host_checksum: | |
| 923 to_push.append((host_abs_path, device_abs_path)) | |
| 924 to_delete = device_checksums.keys() | |
| 925 return (to_push, to_delete) | |
| 926 | |
| 927 def _PushFilesImpl(self, host_device_tuples, files): | |
| 861 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) | 928 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) |
| 862 file_count = len(files) | 929 file_count = len(files) |
| 863 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) | 930 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) |
| 864 for h, _ in host_device_tuples) | 931 for h, _ in host_device_tuples) |
| 865 dir_file_count = 0 | 932 dir_file_count = 0 |
| 866 for h, _ in host_device_tuples: | 933 for h, _ in host_device_tuples: |
| 867 if os.path.isdir(h): | 934 if os.path.isdir(h): |
| 868 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) | 935 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) |
| 869 else: | 936 else: |
| 870 dir_file_count += 1 | 937 dir_file_count += 1 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 881 dir_push_duration < zip_duration or not self._commands_installed): | 948 dir_push_duration < zip_duration or not self._commands_installed): |
| 882 self._PushChangedFilesIndividually(host_device_tuples) | 949 self._PushChangedFilesIndividually(host_device_tuples) |
| 883 elif push_duration < zip_duration or not self._commands_installed: | 950 elif push_duration < zip_duration or not self._commands_installed: |
| 884 self._PushChangedFilesIndividually(files) | 951 self._PushChangedFilesIndividually(files) |
| 885 else: | 952 else: |
| 886 self._PushChangedFilesZipped(files) | 953 self._PushChangedFilesZipped(files) |
| 887 self.RunShellCommand( | 954 self.RunShellCommand( |
| 888 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples], | 955 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples], |
| 889 as_root=True, check_return=True) | 956 as_root=True, check_return=True) |
| 890 | 957 |
| 891 def _GetChangedFilesImpl(self, host_path, device_path): | |
| 892 real_host_path = os.path.realpath(host_path) | |
| 893 try: | |
| 894 real_device_path = self.RunShellCommand( | |
| 895 ['realpath', device_path], single_line=True, check_return=True) | |
| 896 except device_errors.CommandFailedError: | |
| 897 real_device_path = None | |
| 898 if not real_device_path: | |
| 899 return [(host_path, device_path)] | |
| 900 | |
| 901 try: | |
| 902 host_checksums = md5sum.CalculateHostMd5Sums([real_host_path]) | |
| 903 device_paths_to_md5 = ( | |
| 904 real_device_path if os.path.isfile(real_host_path) | |
| 905 else ('%s/%s' % (real_device_path, os.path.relpath(p, real_host_path)) | |
| 906 for p in host_checksums.iterkeys())) | |
| 907 device_checksums = md5sum.CalculateDeviceMd5Sums( | |
| 908 device_paths_to_md5, self) | |
| 909 except EnvironmentError as e: | |
| 910 logging.warning('Error calculating md5: %s', e) | |
| 911 return [(host_path, device_path)] | |
| 912 | |
| 913 if os.path.isfile(host_path): | |
| 914 host_checksum = host_checksums.get(real_host_path) | |
| 915 device_checksum = device_checksums.get(real_device_path) | |
| 916 if host_checksum != device_checksum: | |
| 917 return [(host_path, device_path)] | |
| 918 else: | |
| 919 return [] | |
| 920 else: | |
| 921 to_push = [] | |
| 922 for host_abs_path, host_checksum in host_checksums.iteritems(): | |
| 923 device_abs_path = '%s/%s' % ( | |
| 924 real_device_path, os.path.relpath(host_abs_path, real_host_path)) | |
| 925 if (device_checksums.get(device_abs_path) != host_checksum): | |
| 926 to_push.append((host_abs_path, device_abs_path)) | |
| 927 return to_push | |
| 928 | |
| 929 def _InstallCommands(self): | 958 def _InstallCommands(self): |
| 930 if self._commands_installed is None: | 959 if self._commands_installed is None: |
| 931 try: | 960 try: |
| 932 if not install_commands.Installed(self): | 961 if not install_commands.Installed(self): |
| 933 install_commands.InstallCommands(self) | 962 install_commands.InstallCommands(self) |
| 934 self._commands_installed = True | 963 self._commands_installed = True |
| 935 except Exception as e: | 964 except Exception as e: |
| 936 logging.warning('unzip not available: %s' % str(e)) | 965 logging.warning('unzip not available: %s' % str(e)) |
| 937 self._commands_installed = False | 966 self._commands_installed = False |
| 938 | 967 |
| (...skipping 662 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1601 blacklist = device_blacklist.ReadBlacklist() | 1630 blacklist = device_blacklist.ReadBlacklist() |
| 1602 def blacklisted(adb): | 1631 def blacklisted(adb): |
| 1603 if adb.GetDeviceSerial() in blacklist: | 1632 if adb.GetDeviceSerial() in blacklist: |
| 1604 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) | 1633 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) |
| 1605 return True | 1634 return True |
| 1606 return False | 1635 return False |
| 1607 | 1636 |
| 1608 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() | 1637 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() |
| 1609 if not blacklisted(adb)] | 1638 if not blacklisted(adb)] |
| 1610 | 1639 |
| OLD | NEW |