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

Side by Side Diff: build/android/pylib/android_commands.py

Issue 17467003: [android] Switch test scripts to use last modified time instead of md5sum when checking dependencie… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 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 | build/java_apk.gypi » ('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 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 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 an interface to communicate with the device via the adb command. 5 """Provides an interface to communicate with the device via the adb command.
6 6
7 Assumes adb binary is currently on system path. 7 Assumes adb binary is currently on system path.
8 """ 8 """
9 9
10 import collections 10 import collections
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 54
55 # Keycode "enum" suitable for passing to AndroidCommands.SendKey(). 55 # Keycode "enum" suitable for passing to AndroidCommands.SendKey().
56 KEYCODE_HOME = 3 56 KEYCODE_HOME = 3
57 KEYCODE_BACK = 4 57 KEYCODE_BACK = 4
58 KEYCODE_DPAD_UP = 19 58 KEYCODE_DPAD_UP = 19
59 KEYCODE_DPAD_DOWN = 20 59 KEYCODE_DPAD_DOWN = 20
60 KEYCODE_DPAD_RIGHT = 22 60 KEYCODE_DPAD_RIGHT = 22
61 KEYCODE_ENTER = 66 61 KEYCODE_ENTER = 66
62 KEYCODE_MENU = 82 62 KEYCODE_MENU = 82
63 63
64 MD5SUM_DEVICE_FOLDER = constants.TEST_EXECUTABLE_DIR + '/md5sum/'
65 MD5SUM_DEVICE_PATH = MD5SUM_DEVICE_FOLDER + 'md5sum_bin'
66 MD5SUM_LD_LIBRARY_PATH = 'LD_LIBRARY_PATH=%s' % MD5SUM_DEVICE_FOLDER
67
68 def GetEmulators(): 64 def GetEmulators():
69 """Returns a list of emulators. Does not filter by status (e.g. offline). 65 """Returns a list of emulators. Does not filter by status (e.g. offline).
70 66
71 Both devices starting with 'emulator' will be returned in below output: 67 Both devices starting with 'emulator' will be returned in below output:
72 68
73 * daemon not running. starting it now on port 5037 * 69 * daemon not running. starting it now on port 5037 *
74 * daemon started successfully * 70 * daemon started successfully *
75 List of devices attached 71 List of devices attached
76 027c10494100b4d7 device 72 027c10494100b4d7 device
77 emulator-5554 offline 73 emulator-5554 offline
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
169 lastmod -= utc_delta 165 lastmod -= utc_delta
170 files[filename] = (int(file_match.group('size')), lastmod) 166 files[filename] = (int(file_match.group('size')), lastmod)
171 return files 167 return files
172 168
173 169
174 def _ComputeFileListHash(md5sum_output): 170 def _ComputeFileListHash(md5sum_output):
175 """Returns a list of MD5 strings from the provided md5sum output.""" 171 """Returns a list of MD5 strings from the provided md5sum output."""
176 return [line.split(' ')[0] for line in md5sum_output] 172 return [line.split(' ')[0] for line in md5sum_output]
177 173
178 174
175 def _ComputeFileListTimesModified(timemodified_output):
176 """Returns a list of tuples from the provided timemodified output.
177
178 Args:
179 timemodified_output: output directly from timemodified binary.
180
181 Returns:
182 List of namedtuples (time, path).
183 """
184 TimeAndPath = collections.namedtuple('TimeAndPath', ['time', 'path'])
185 split_lines = [line.split(' ') for line in timemodified_output]
186 assert all(len(s) == 2 for s in split_lines), 'Invalid timemodified output'
187 return [TimeAndPath._make(s) for s in split_lines]
188
189
179 def _HasAdbPushSucceeded(command_output): 190 def _HasAdbPushSucceeded(command_output):
180 """Returns whether adb push has succeeded from the provided output.""" 191 """Returns whether adb push has succeeded from the provided output."""
181 # TODO(frankf): We should look at the return code instead of the command 192 # TODO(frankf): We should look at the return code instead of the command
182 # output for many of the commands in this file. 193 # output for many of the commands in this file.
183 if not command_output: 194 if not command_output:
184 return True 195 return True
185 # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)" 196 # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)"
186 # Errors look like this: "failed to copy ... " 197 # Errors look like this: "failed to copy ... "
187 if not re.search('^[0-9]', command_output.splitlines()[-1]): 198 if not re.search('^[0-9]', command_output.splitlines()[-1]):
188 logging.critical('PUSH FAILED: ' + command_output) 199 logging.critical('PUSH FAILED: ' + command_output)
(...skipping 29 matching lines...) Expand all
218 self._adb.SetTargetSerial(device) 229 self._adb.SetTargetSerial(device)
219 self._device = device 230 self._device = device
220 self._logcat = None 231 self._logcat = None
221 self.logcat_process = None 232 self.logcat_process = None
222 self._logcat_tmpoutfile = None 233 self._logcat_tmpoutfile = None
223 self._pushed_files = [] 234 self._pushed_files = []
224 self._device_utc_offset = None 235 self._device_utc_offset = None
225 self._potential_push_size = 0 236 self._potential_push_size = 0
226 self._actual_push_size = 0 237 self._actual_push_size = 0
227 self._md5sum_build_dir = '' 238 self._md5sum_build_dir = ''
239 self._timemodified_build_dir = ''
228 self._external_storage = '' 240 self._external_storage = ''
229 self._util_wrapper = '' 241 self._util_wrapper = ''
230 242
231 def _LogShell(self, cmd): 243 def _LogShell(self, cmd):
232 """Logs the adb shell command.""" 244 """Logs the adb shell command."""
233 if self._device: 245 if self._device:
234 device_repr = self._device[-4:] 246 device_repr = self._device[-4:]
235 else: 247 else:
236 device_repr = '????' 248 device_repr = '????'
237 logging.info('[%s]> %s', device_repr, cmd) 249 logging.info('[%s]> %s', device_repr, cmd)
(...skipping 467 matching lines...) Expand 10 before | Expand all | Expand 10 after
705 717
706 Args: 718 Args:
707 local_path: Path (file or directory) on the host. 719 local_path: Path (file or directory) on the host.
708 device_path: Path on the device. 720 device_path: Path on the device.
709 ignore_paths: If False, both the md5sum and the relative paths/names of 721 ignore_paths: If False, both the md5sum and the relative paths/names of
710 files must match. If True, only the md5sum must match. 722 files must match. If True, only the md5sum must match.
711 723
712 Returns: 724 Returns:
713 True if the md5sums match. 725 True if the md5sums match.
714 """ 726 """
727 MD5SUM_DEVICE_FOLDER = constants.TEST_EXECUTABLE_DIR + '/md5sum/'
728 MD5SUM_DEVICE_PATH = MD5SUM_DEVICE_FOLDER + 'md5sum_bin'
729 MD5SUM_LD_LIBRARY_PATH = 'LD_LIBRARY_PATH=%s' % MD5SUM_DEVICE_FOLDER
715 if not self._md5sum_build_dir: 730 if not self._md5sum_build_dir:
716 default_build_type = os.environ.get('BUILD_TYPE', 'Debug') 731 default_build_type = os.environ.get('BUILD_TYPE', 'Debug')
717 build_dir = '%s/%s/' % ( 732 build_dir = '%s/%s/' % (
718 cmd_helper.OutDirectory().get(), default_build_type) 733 cmd_helper.OutDirectory().get(), default_build_type)
719 md5sum_dist_path = '%s/md5sum_dist' % build_dir 734 md5sum_dist_path = '%s/md5sum_dist' % build_dir
720 if not os.path.exists(md5sum_dist_path): 735 if not os.path.exists(md5sum_dist_path):
721 build_dir = '%s/Release/' % cmd_helper.OutDirectory().get() 736 build_dir = '%s/Release/' % cmd_helper.OutDirectory().get()
722 md5sum_dist_path = '%s/md5sum_dist' % build_dir 737 md5sum_dist_path = '%s/md5sum_dist' % build_dir
723 assert os.path.exists(md5sum_dist_path), 'Please build md5sum.' 738 assert os.path.exists(md5sum_dist_path), 'Please build md5sum.'
724 command = 'push %s %s' % (md5sum_dist_path, MD5SUM_DEVICE_FOLDER) 739 command = 'push %s %s' % (md5sum_dist_path, MD5SUM_DEVICE_FOLDER)
725 assert _HasAdbPushSucceeded(self._adb.SendCommand(command)) 740 assert _HasAdbPushSucceeded(self._adb.SendCommand(command))
726 self._md5sum_build_dir = build_dir 741 self._md5sum_build_dir = build_dir
727 742
728 cmd = (MD5SUM_LD_LIBRARY_PATH + ' ' + self._util_wrapper + ' ' + 743 cmd = (MD5SUM_LD_LIBRARY_PATH + ' ' + self._util_wrapper + ' ' +
729 MD5SUM_DEVICE_PATH + ' ' + device_path) 744 MD5SUM_DEVICE_PATH + ' ' + device_path)
730 hashes_on_device = _ComputeFileListHash( 745 hashes_on_device = _ComputeFileListHash(
731 self.RunShellCommand(cmd, timeout_time=2 * 60)) 746 self.RunShellCommand(cmd, timeout_time=2 * 60))
732 assert os.path.exists(local_path), 'Local path not found %s' % local_path 747 assert os.path.exists(local_path), 'Local path not found %s' % local_path
733 md5sum_output = cmd_helper.GetCmdOutput( 748 md5sum_output = cmd_helper.GetCmdOutput(
734 ['%s/md5sum_bin_host' % self._md5sum_build_dir, local_path]) 749 ['%s/md5sum_bin_host' % self._md5sum_build_dir, local_path])
735 hashes_on_host = _ComputeFileListHash(md5sum_output.splitlines()) 750 hashes_on_host = _ComputeFileListHash(md5sum_output.splitlines())
736 751
737 if ignore_paths: 752 if ignore_paths:
738 hashes_on_device = [h.split()[0] for h in hashes_on_device] 753 hashes_on_device = [h.split()[0] for h in hashes_on_device]
739 hashes_on_host = [h.split()[0] for h in hashes_on_host] 754 hashes_on_host = [h.split()[0] for h in hashes_on_host]
740 755
741 return hashes_on_device == hashes_on_host 756 return hashes_on_device == hashes_on_host
742 757
758 def _AreFilesUnsynchronized(self, local_path, device_path):
759 """Compares the time modified of a local path against a device path.
760
761 NOTE: Files transferred via "adb push" keep the host's modified time.
762
763 Args:
764 local_path: Path (file or directory) on the host.
765 device_path: Path (file or directory) on the device.
766
767 Returns:
768 True if the time the files were modified match.
769 """
770 TIMEMODIFIED_DEVICE_DIRECTORY = (constants.TEST_EXECUTABLE_DIR +
771 '/timemodified/')
772 TIMEMODIFIED_DEVICE_PATH = (TIMEMODIFIED_DEVICE_DIRECTORY +
773 'timemodified_bin')
774 TIMEMODIFIED_LD_LIBRARY_PATH = ('LD_LIBRARY_PATH=%s' %
775 TIMEMODIFIED_DEVICE_DIRECTORY)
776 assert os.path.exists(local_path), 'Local path not found %s' % local_path
777
778 if not self._timemodified_build_dir:
779 default_build_type = os.environ.get('BUILD_TYPE', 'Debug')
780 build_dir = '%s/%s/' % (
781 cmd_helper.OutDirectory().get(), default_build_type)
782 host_timemodified_dist_path = '%s/timemodified_dist' % build_dir
783 if not os.path.exists(host_timemodified_dist_path):
784 build_dir = '%s/Release/' % cmd_helper.OutDirectory().get()
785 host_timemodified_dist_path = '%s/timemodified_dist' % build_dir
786 assert os.path.exists(host_timemodified_dist_path), 'Build timemodified'
787 command = 'push %s %s' % (host_timemodified_dist_path,
788 TIMEMODIFIED_DEVICE_DIRECTORY)
789 assert _HasAdbPushSucceeded(self._adb.SendCommand(command))
790 self._timemodified_build_dir = build_dir
791
792 self._pushed_files.append(device_path)
793 device_timemodified_output = self.RunShellCommand(
794 TIMEMODIFIED_LD_LIBRARY_PATH + ' ' + self._util_wrapper + ' ' +
795 TIMEMODIFIED_DEVICE_PATH + ' ' + device_path)
796 device_time_tuples = _ComputeFileListTimesModified(
797 device_timemodified_output)
798 assert os.path.exists(local_path), 'Local path not found %s' % local_path
799 host_timemodified_output = cmd_helper.GetCmdOutput(
800 ['%s/timemodified_bin_host' % self._timemodified_build_dir, local_path])
801 host_time_tuples = _ComputeFileListTimesModified(
802 host_timemodified_output.splitlines())
803
804 if len(device_time_tuples) < len(host_time_tuples):
805 return True
806
807 # Ignore extra files on the device.
808 if len(device_time_tuples) > len(host_time_tuples):
809 host_files = [os.path.relpath(os.path.normpath(p.path),
810 os.path.normpath(local_path)) for p in host_time_tuples]
811
812 def _host_has(fname):
813 return any(path in fname for path in host_files)
814
815 times_on_device = [t.time for t in device_time_tuples if
816 _host_has(t.path)]
817 else:
818 times_on_device = [t.time for t in device_time_tuples]
819
820 # Compare timestamps between host and device files.
821 times_on_host = [t.time for t in host_time_tuples]
822 times_on_device.sort()
823 times_on_host.sort()
824 return any(int(dtime) != int(htime) for dtime, htime in
825 zip(times_on_device, times_on_host))
826
743 def PushIfNeeded(self, local_path, device_path): 827 def PushIfNeeded(self, local_path, device_path):
744 """Pushes |local_path| to |device_path|. 828 """Pushes |local_path| to |device_path|.
745 829
746 Works for files and directories. This method skips copying any paths in 830 Works for files and directories. This method skips copying any paths in
747 |test_data_paths| that already exist on the device with the same hash. 831 |test_data_paths| that already exist on the device with the same modified
832 time.
748 833
749 All pushed files can be removed by calling RemovePushedFiles(). 834 All pushed files can be removed by calling RemovePushedFiles().
750 """ 835 """
751 assert os.path.exists(local_path), 'Local path not found %s' % local_path 836 assert os.path.exists(local_path), 'Local path not found %s' % local_path
752 size = int(cmd_helper.GetCmdOutput(['du', '-sb', local_path]).split()[0]) 837 size = int(cmd_helper.GetCmdOutput(['du', '-sb', local_path]).split()[0])
753 self._pushed_files.append(device_path) 838 self._pushed_files.append(device_path)
754 self._potential_push_size += size 839 self._potential_push_size += size
755 840
756 if self.CheckMd5Sum(local_path, device_path): 841 if not self._AreFilesUnsynchronized(local_path, device_path):
757 return 842 return
758 843
759 self._actual_push_size += size 844 self._actual_push_size += size
760 # They don't match, so remove everything first and then create it. 845 # They don't match, so remove everything first and then create it.
761 if os.path.isdir(local_path): 846 if os.path.isdir(local_path):
762 self.RunShellCommand('rm -r %s' % device_path, timeout_time=2 * 60) 847 self.RunShellCommand('rm -r %s' % device_path, timeout_time=2 * 60)
763 self.RunShellCommand('mkdir -p %s' % device_path) 848 self.RunShellCommand('mkdir -p %s' % device_path)
764 849
765 # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout of 850 # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout of
766 # 60 seconds which isn't sufficient for a lot of users of this method. 851 # 60 seconds which isn't sufficient for a lot of users of this method.
(...skipping 531 matching lines...) Expand 10 before | Expand all | Expand 10 after
1298 host_dir = os.path.dirname(host_file) 1383 host_dir = os.path.dirname(host_file)
1299 if not os.path.exists(host_dir): 1384 if not os.path.exists(host_dir):
1300 os.makedirs(host_dir) 1385 os.makedirs(host_dir)
1301 device_file = '%s/screenshot.png' % self.GetExternalStorage() 1386 device_file = '%s/screenshot.png' % self.GetExternalStorage()
1302 self.RunShellCommand('/system/bin/screencap -p %s' % device_file) 1387 self.RunShellCommand('/system/bin/screencap -p %s' % device_file)
1303 assert self._adb.Pull(device_file, host_file) 1388 assert self._adb.Pull(device_file, host_file)
1304 assert os.path.exists(host_file) 1389 assert os.path.exists(host_file)
1305 1390
1306 def SetUtilWrapper(self, util_wrapper): 1391 def SetUtilWrapper(self, util_wrapper):
1307 """Sets a wrapper prefix to be used when running a locally-built 1392 """Sets a wrapper prefix to be used when running a locally-built
1308 binary on the device (ex.: md5sum_bin). 1393 binary on the device (ex.: timemodified_bin).
1309 """ 1394 """
1310 self._util_wrapper = util_wrapper 1395 self._util_wrapper = util_wrapper
1311 1396
1312 def RunInstrumentationTest(self, test, test_package, instr_args, timeout): 1397 def RunInstrumentationTest(self, test, test_package, instr_args, timeout):
1313 """Runs a single instrumentation test. 1398 """Runs a single instrumentation test.
1314 1399
1315 Args: 1400 Args:
1316 test: Test class/method. 1401 test: Test class/method.
1317 test_package: Package name of test apk. 1402 test_package: Package name of test apk.
1318 instr_args: Extra key/value to pass to am instrument. 1403 instr_args: Extra key/value to pass to am instrument.
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
1368 """ 1453 """
1369 def __init__(self, output): 1454 def __init__(self, output):
1370 self._output = output 1455 self._output = output
1371 1456
1372 def write(self, data): 1457 def write(self, data):
1373 data = data.replace('\r\r\n', '\n') 1458 data = data.replace('\r\r\n', '\n')
1374 self._output.write(data) 1459 self._output.write(data)
1375 1460
1376 def flush(self): 1461 def flush(self):
1377 self._output.flush() 1462 self._output.flush()
OLDNEW
« no previous file with comments | « no previous file | build/java_apk.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698