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

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: removed TODO, now a feature :) 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') | tools/android/timemodified/timemodified.cc » ('J')
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 hashes_on_device = _ComputeFileListHash( 743 hashes_on_device = _ComputeFileListHash(
729 self.RunShellCommand(MD5SUM_LD_LIBRARY_PATH + ' ' + self._util_wrapper + 744 self.RunShellCommand(MD5SUM_LD_LIBRARY_PATH + ' ' + self._util_wrapper +
730 ' ' + MD5SUM_DEVICE_PATH + ' ' + device_path)) 745 ' ' + MD5SUM_DEVICE_PATH + ' ' + device_path))
731 assert os.path.exists(local_path), 'Local path not found %s' % local_path 746 assert os.path.exists(local_path), 'Local path not found %s' % local_path
732 md5sum_output = cmd_helper.GetCmdOutput( 747 md5sum_output = cmd_helper.GetCmdOutput(
733 ['%s/md5sum_bin_host' % self._md5sum_build_dir, local_path]) 748 ['%s/md5sum_bin_host' % self._md5sum_build_dir, local_path])
734 hashes_on_host = _ComputeFileListHash(md5sum_output.splitlines()) 749 hashes_on_host = _ComputeFileListHash(md5sum_output.splitlines())
735 750
736 if ignore_paths: 751 if ignore_paths:
737 hashes_on_device = [h.split()[0] for h in hashes_on_device] 752 hashes_on_device = [h.split()[0] for h in hashes_on_device]
738 hashes_on_host = [h.split()[0] for h in hashes_on_host] 753 hashes_on_host = [h.split()[0] for h in hashes_on_host]
739 754
740 return hashes_on_device == hashes_on_host 755 return hashes_on_device == hashes_on_host
741 756
757 def AreFilesUnsynchronized(self, local_path, device_path):
bulach 2013/07/15 18:35:20 nit: make this internal, "_" prefix
craigdh 2013/07/15 19:06:50 Done.
758 """Compares the time modified of a local path against a device path.
bulach 2013/07/15 18:35:20 could probably add a note saying that files transf
craigdh 2013/07/15 19:06:50 Done.
759
760 Args:
761 local_path: Path (file or directory) on the host.
762 device_path: Path (file or directory) on the device.
763
764 Returns:
765 True if the time the files were modified match.
766 """
767 TIMEMODIFIED_DEVICE_DIRECTORY = (constants.TEST_EXECUTABLE_DIR +
768 '/timemodified/')
769 TIMEMODIFIED_DEVICE_PATH = (TIMEMODIFIED_DEVICE_DIRECTORY +
770 'timemodified_bin')
771 TIMEMODIFIED_LD_LIBRARY_PATH = ('LD_LIBRARY_PATH=%s' %
772 TIMEMODIFIED_DEVICE_DIRECTORY)
773 assert os.path.exists(local_path), 'Local path not found %s' % local_path
774
775 if not self._timemodified_build_dir:
776 default_build_type = os.environ.get('BUILD_TYPE', 'Debug')
777 build_dir = '%s/%s/' % (
778 cmd_helper.OutDirectory().get(), default_build_type)
779 host_timemodified_dist_path = '%s/timemodified_dist' % build_dir
780 if not os.path.exists(host_timemodified_dist_path):
781 build_dir = '%s/Release/' % cmd_helper.OutDirectory().get()
782 host_timemodified_dist_path = '%s/timemodified_dist' % build_dir
783 assert os.path.exists(host_timemodified_dist_path), 'Build timemodified'
784 command = 'push %s %s' % (host_timemodified_dist_path,
785 TIMEMODIFIED_DEVICE_DIRECTORY)
786 assert _HasAdbPushSucceeded(self._adb.SendCommand(command))
787 self._timemodified_build_dir = build_dir
788
789 self._pushed_files.append(device_path)
790 device_timemodified_output = self.RunShellCommand(
791 TIMEMODIFIED_LD_LIBRARY_PATH + ' ' + self._util_wrapper + ' ' +
792 TIMEMODIFIED_DEVICE_PATH + ' ' + device_path)
793 device_time_tuples = _ComputeFileListTimesModified(
794 device_timemodified_output)
795 assert os.path.exists(local_path), 'Local path not found %s' % local_path
796 host_timemodified_output = cmd_helper.GetCmdOutput(
797 ['%s/timemodified_bin_host' % self._timemodified_build_dir, local_path])
798 host_time_tuples = _ComputeFileListTimesModified(
799 host_timemodified_output.splitlines())
800
801 if len(device_time_tuples) < len(host_time_tuples):
802 return True
803
804 # Ignore extra files on the device.
805 if len(device_time_tuples) > len(host_time_tuples):
806 host_files = [os.path.relpath(os.path.normpath(p.path),
807 os.path.normpath(local_path)) for p in host_time_tuples]
808
809 def _host_has(fname):
810 return any(path in fname for path in host_files)
811
812 times_on_device = [t.time for t in device_time_tuples if
813 _host_has(t.path)]
814 else:
815 times_on_device = [t.time for t in device_time_tuples]
816
817 # Compare timestamps between host and device files.
818 times_on_host = [t.time for t in host_time_tuples]
819 times_on_device.sort()
820 times_on_host.sort()
821 return any(int(dtime) < int(htime) for dtime, htime in
bulach 2013/07/15 18:35:20 should we check for != instead? i.e., a file modif
craigdh 2013/07/15 19:06:50 Done.
822 zip(times_on_device, times_on_host))
823
742 def PushIfNeeded(self, local_path, device_path): 824 def PushIfNeeded(self, local_path, device_path):
743 """Pushes |local_path| to |device_path|. 825 """Pushes |local_path| to |device_path|.
744 826
745 Works for files and directories. This method skips copying any paths in 827 Works for files and directories. This method skips copying any paths in
746 |test_data_paths| that already exist on the device with the same hash. 828 |test_data_paths| that already exist on the device with the same modified
829 time.
747 830
748 All pushed files can be removed by calling RemovePushedFiles(). 831 All pushed files can be removed by calling RemovePushedFiles().
749 """ 832 """
750 assert os.path.exists(local_path), 'Local path not found %s' % local_path 833 assert os.path.exists(local_path), 'Local path not found %s' % local_path
751 size = int(cmd_helper.GetCmdOutput(['du', '-sb', local_path]).split()[0]) 834 size = int(cmd_helper.GetCmdOutput(['du', '-sb', local_path]).split()[0])
752 self._pushed_files.append(device_path) 835 self._pushed_files.append(device_path)
753 self._potential_push_size += size 836 self._potential_push_size += size
754 837
755 if self.CheckMd5Sum(local_path, device_path): 838 if not self.AreFilesUnsynchronized(local_path, device_path):
756 return 839 return
757 840
758 self._actual_push_size += size 841 self._actual_push_size += size
759 # They don't match, so remove everything first and then create it. 842 # They don't match, so remove everything first and then create it.
760 if os.path.isdir(local_path): 843 if os.path.isdir(local_path):
761 self.RunShellCommand('rm -r %s' % device_path, timeout_time=2 * 60) 844 self.RunShellCommand('rm -r %s' % device_path, timeout_time=2 * 60)
762 self.RunShellCommand('mkdir -p %s' % device_path) 845 self.RunShellCommand('mkdir -p %s' % device_path)
763 846
764 # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout of 847 # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout of
765 # 60 seconds which isn't sufficient for a lot of users of this method. 848 # 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
1297 host_dir = os.path.dirname(host_file) 1380 host_dir = os.path.dirname(host_file)
1298 if not os.path.exists(host_dir): 1381 if not os.path.exists(host_dir):
1299 os.makedirs(host_dir) 1382 os.makedirs(host_dir)
1300 device_file = '%s/screenshot.png' % self.GetExternalStorage() 1383 device_file = '%s/screenshot.png' % self.GetExternalStorage()
1301 self.RunShellCommand('/system/bin/screencap -p %s' % device_file) 1384 self.RunShellCommand('/system/bin/screencap -p %s' % device_file)
1302 assert self._adb.Pull(device_file, host_file) 1385 assert self._adb.Pull(device_file, host_file)
1303 assert os.path.exists(host_file) 1386 assert os.path.exists(host_file)
1304 1387
1305 def SetUtilWrapper(self, util_wrapper): 1388 def SetUtilWrapper(self, util_wrapper):
1306 """Sets a wrapper prefix to be used when running a locally-built 1389 """Sets a wrapper prefix to be used when running a locally-built
1307 binary on the device (ex.: md5sum_bin). 1390 binary on the device (ex.: timemodified_bin).
1308 """ 1391 """
1309 self._util_wrapper = util_wrapper 1392 self._util_wrapper = util_wrapper
1310 1393
1311 def RunInstrumentationTest(self, test, test_package, instr_args, timeout): 1394 def RunInstrumentationTest(self, test, test_package, instr_args, timeout):
1312 """Runs a single instrumentation test. 1395 """Runs a single instrumentation test.
1313 1396
1314 Args: 1397 Args:
1315 test: Test class/method. 1398 test: Test class/method.
1316 test_package: Package name of test apk. 1399 test_package: Package name of test apk.
1317 instr_args: Extra key/value to pass to am instrument. 1400 instr_args: Extra key/value to pass to am instrument.
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
1367 """ 1450 """
1368 def __init__(self, output): 1451 def __init__(self, output):
1369 self._output = output 1452 self._output = output
1370 1453
1371 def write(self, data): 1454 def write(self, data):
1372 data = data.replace('\r\r\n', '\n') 1455 data = data.replace('\r\r\n', '\n')
1373 self._output.write(data) 1456 self._output.write(data)
1374 1457
1375 def flush(self): 1458 def flush(self):
1376 self._output.flush() 1459 self._output.flush()
OLDNEW
« no previous file with comments | « no previous file | build/java_apk.gypi » ('j') | tools/android/timemodified/timemodified.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698