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

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: addressed comments on timemodified.cc 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
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 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
164 if isinstance(utc_offset, str) and len(utc_offset) == 5: 160 if isinstance(utc_offset, str) and len(utc_offset) == 5:
165 utc_delta = datetime.timedelta(hours=int(utc_offset[1:3]), 161 utc_delta = datetime.timedelta(hours=int(utc_offset[1:3]),
166 minutes=int(utc_offset[3:5])) 162 minutes=int(utc_offset[3:5]))
167 if utc_offset[0:1] == '-': 163 if utc_offset[0:1] == '-':
168 utc_delta = -utc_delta 164 utc_delta = -utc_delta
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 _ComputeFileListTimesModified(timemodified_output):
175 """Returns a list of MD5 strings from the provided md5sum output.""" 171 """Returns a list of tuples from the provided timemodified output.
176 return [line.split(' ')[0] for line in md5sum_output] 172
173 Args:
174 timemodified_output: output directly from timemodified binary.
175
176 Returns:
177 List of tuples of the form (time_modified, file_path).
178 """
179 return [line.split(' ')[:2] for line in timemodified_output]
frankf 2013/07/02 18:45:29 Is this number of spaces important. Can you just d
craigdh 2013/07/02 20:34:15 Yep, important as discussed offline. Added an asse
177 180
178 181
179 def _HasAdbPushSucceeded(command_output): 182 def _HasAdbPushSucceeded(command_output):
180 """Returns whether adb push has succeeded from the provided output.""" 183 """Returns whether adb push has succeeded from the provided output."""
181 if not command_output: 184 if not command_output:
182 return False 185 return False
183 # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)" 186 # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)"
184 # Errors look like this: "failed to copy ... " 187 # Errors look like this: "failed to copy ... "
185 if not re.search('^[0-9]', command_output.splitlines()[-1]): 188 if not re.search('^[0-9]', command_output.splitlines()[-1]):
186 logging.critical('PUSH FAILED: ' + command_output) 189 logging.critical('PUSH FAILED: ' + command_output)
(...skipping 26 matching lines...) Expand all
213 os.environ['PATH'] += os.pathsep + adb_dir 216 os.environ['PATH'] += os.pathsep + adb_dir
214 self._adb = adb_interface.AdbInterface() 217 self._adb = adb_interface.AdbInterface()
215 if device: 218 if device:
216 self._adb.SetTargetSerial(device) 219 self._adb.SetTargetSerial(device)
217 self._device = device 220 self._device = device
218 self._logcat = None 221 self._logcat = None
219 self.logcat_process = None 222 self.logcat_process = None
220 self._logcat_tmpoutfile = None 223 self._logcat_tmpoutfile = None
221 self._pushed_files = [] 224 self._pushed_files = []
222 self._device_utc_offset = self.RunShellCommand('date +%z')[0] 225 self._device_utc_offset = self.RunShellCommand('date +%z')[0]
223 self._md5sum_build_dir = '' 226 self._timemodified_build_dir = ''
224 self._external_storage = '' 227 self._external_storage = ''
225 self._util_wrapper = '' 228 self._util_wrapper = ''
226 229
227 def _LogShell(self, cmd): 230 def _LogShell(self, cmd):
228 """Logs the adb shell command.""" 231 """Logs the adb shell command."""
229 if self._device: 232 if self._device:
230 device_repr = self._device[-4:] 233 device_repr = self._device[-4:]
231 else: 234 else:
232 device_repr = '????' 235 device_repr = '????'
233 logging.info('[%s]> %s', device_repr, cmd) 236 logging.info('[%s]> %s', device_repr, cmd)
(...skipping 455 matching lines...) Expand 10 before | Expand all | Expand 10 after
689 self.RunShellCommand('pm clear ' + package) 692 self.RunShellCommand('pm clear ' + package)
690 693
691 def SendKeyEvent(self, keycode): 694 def SendKeyEvent(self, keycode):
692 """Sends keycode to the device. 695 """Sends keycode to the device.
693 696
694 Args: 697 Args:
695 keycode: Numeric keycode to send (see "enum" at top of file). 698 keycode: Numeric keycode to send (see "enum" at top of file).
696 """ 699 """
697 self.RunShellCommand('input keyevent %d' % keycode) 700 self.RunShellCommand('input keyevent %d' % keycode)
698 701
699 def CheckMd5Sum(self, local_path, device_path, ignore_paths=False): 702 def AreFilesUnsynchronized(self, local_path, device_path):
700 """Compares the md5sum of a local path against a device path. 703 """Compares the time modified of a local path against a device path.
701 704
702 Args: 705 Args:
703 local_path: Path (file or directory) on the host. 706 local_path: Path (file or directory) on the host.
704 device_path: Path on the device. 707 device_path: Path on the device.
frankf 2013/07/02 18:45:29 file or directory?
craigdh 2013/07/02 20:34:15 either. clarified.
705 ignore_paths: If False, both the md5sum and the relative paths/names of
706 files must match. If True, only the md5sum must match.
707 708
708 Returns: 709 Returns:
709 True if the md5sums match. 710 True if the time the files were modified match.
710 """ 711 """
712 TIMEMODIFIED_DEVICE_DIRECTORY = (constants.TEST_EXECUTABLE_DIR +
713 '/timemodified/')
714 TIMEMODIFIED_DEVICE_PATH = (TIMEMODIFIED_DEVICE_DIRECTORY +
715 'timemodified_bin')
716 TIMEMODIFIED_LD_LIBRARY_PATH = ('LD_LIBRARY_PATH=%s' %
717 TIMEMODIFIED_DEVICE_DIRECTORY)
711 assert os.path.exists(local_path), 'Local path not found %s' % local_path 718 assert os.path.exists(local_path), 'Local path not found %s' % local_path
712 719
713 if not self._md5sum_build_dir: 720 if not self._timemodified_build_dir:
714 default_build_type = os.environ.get('BUILD_TYPE', 'Debug') 721 default_build_type = os.environ.get('BUILD_TYPE', 'Debug')
715 build_dir = '%s/%s/' % ( 722 build_dir = '%s/%s/' % (
716 cmd_helper.OutDirectory().get(), default_build_type) 723 cmd_helper.OutDirectory().get(), default_build_type)
717 md5sum_dist_path = '%s/md5sum_dist' % build_dir 724 host_timemodified_dist_path = '%s/timemodified_dist' % build_dir
frankf 2013/07/02 18:45:29 Why is this not a constant like the device counter
craigdh 2013/07/02 20:34:15 Because it is determined dynamically at runtime.
718 if not os.path.exists(md5sum_dist_path): 725 if not os.path.exists(host_timemodified_dist_path):
719 build_dir = '%s/Release/' % cmd_helper.OutDirectory().get() 726 build_dir = '%s/Release/' % cmd_helper.OutDirectory().get()
720 md5sum_dist_path = '%s/md5sum_dist' % build_dir 727 host_timemodified_dist_path = '%s/timemodified_dist' % build_dir
721 assert os.path.exists(md5sum_dist_path), 'Please build md5sum.' 728 assert os.path.exists(host_timemodified_dist_path), 'Build timemodified'
722 command = 'push %s %s' % (md5sum_dist_path, MD5SUM_DEVICE_FOLDER) 729 command = 'push %s %s' % (host_timemodified_dist_path,
730 TIMEMODIFIED_DEVICE_DIRECTORY)
723 assert _HasAdbPushSucceeded(self._adb.SendCommand(command)) 731 assert _HasAdbPushSucceeded(self._adb.SendCommand(command))
724 self._md5sum_build_dir = build_dir 732 self._timemodified_build_dir = build_dir
725 733
726 self._pushed_files.append(device_path) 734 self._pushed_files.append(device_path)
727 hashes_on_device = _ComputeFileListHash( 735 device_timemodified_output = self.RunShellCommand(
728 self.RunShellCommand(MD5SUM_LD_LIBRARY_PATH + ' ' + self._util_wrapper + 736 TIMEMODIFIED_LD_LIBRARY_PATH + ' ' + self._util_wrapper + ' ' +
729 ' ' + MD5SUM_DEVICE_PATH + ' ' + device_path)) 737 TIMEMODIFIED_DEVICE_PATH + ' ' + device_path)
738 device_time_tuples = _ComputeFileListTimesModified(
739 device_timemodified_output)
730 assert os.path.exists(local_path), 'Local path not found %s' % local_path 740 assert os.path.exists(local_path), 'Local path not found %s' % local_path
731 md5sum_output = cmd_helper.GetCmdOutput( 741 host_timemodified_output = cmd_helper.GetCmdOutput(
732 ['%s/md5sum_bin_host' % self._md5sum_build_dir, local_path]) 742 ['%s/timemodified_bin_host' % self._timemodified_build_dir, local_path])
733 hashes_on_host = _ComputeFileListHash(md5sum_output.splitlines()) 743 host_time_tuples = _ComputeFileListTimesModified(
frankf 2013/07/02 18:45:29 Can you use named tuples instead of p[0]/[1]
craigdh 2013/07/02 20:34:15 Done.
744 host_timemodified_output.splitlines())
734 745
735 if ignore_paths: 746 if len(device_time_tuples) < len(host_time_tuples):
736 hashes_on_device = [h.split()[0] for h in hashes_on_device] 747 return True
737 hashes_on_host = [h.split()[0] for h in hashes_on_host]
738 748
739 return hashes_on_device == hashes_on_host 749 # Ignore extra files on the device.
frankf 2013/07/02 18:45:29 Add a TODO to clean up old data on the device.
craigdh 2013/07/02 20:34:15 Done.
750 device_time_tuples = [t for t in device_time_tuples if t[1] != '.']
frankf 2013/07/02 18:45:29 Why are listing this?
craigdh 2013/07/02 20:34:15 Already removed in the patch I uploaded while you
751 if len(device_time_tuples) > len(host_time_tuples):
752 host_files = [os.path.relpath(os.path.normpath(p[1]),
753 os.path.normpath(local_path)) for p in host_time_tuples]
754 def _host_has(fname):
frankf 2013/07/02 18:45:29 Use blank lines to improve readability
craigdh 2013/07/02 20:34:15 Done.
755 return any(p in fname for p in host_files)
756 times_on_device = [p[0] for p in device_time_tuples if _host_has(p[1])]
757 else:
758 times_on_device = [t[0] for t in device_time_tuples]
759
760 # Compare timestamps between host and device files.
761 times_on_host = [t[0] for t in host_time_tuples]
762 times_on_device.sort()
763 times_on_host.sort()
764 return any(int(dtime) < int(htime) for dtime, htime in
765 zip(times_on_device, times_on_host))
740 766
741 def PushIfNeeded(self, local_path, device_path): 767 def PushIfNeeded(self, local_path, device_path):
742 """Pushes |local_path| to |device_path|. 768 """Pushes |local_path| to |device_path|.
743 769
744 Works for files and directories. This method skips copying any paths in 770 Works for files and directories. This method skips copying any paths in
745 |test_data_paths| that already exist on the device with the same hash. 771 |test_data_paths| that already exist on the device with the same modified
772 time.
746 773
747 All pushed files can be removed by calling RemovePushedFiles(). 774 All pushed files can be removed by calling RemovePushedFiles().
748 """ 775 """
749 if self.CheckMd5Sum(local_path, device_path): 776 if not self.AreFilesUnsynchronized(local_path, device_path):
750 return 777 return
751 778
752 # They don't match, so remove everything first and then create it. 779 # They don't match, so remove everything first and then create it.
753 if os.path.isdir(local_path): 780 if os.path.isdir(local_path):
754 self.RunShellCommand('rm -r %s' % device_path, timeout_time=2 * 60) 781 self.RunShellCommand('rm -r %s' % device_path, timeout_time=2 * 60)
755 self.RunShellCommand('mkdir -p %s' % device_path) 782 self.RunShellCommand('mkdir -p %s' % device_path)
756 783
757 # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout of 784 # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout of
758 # 60 seconds which isn't sufficient for a lot of users of this method. 785 # 60 seconds which isn't sufficient for a lot of users of this method.
759 push_command = 'push %s %s' % (local_path, device_path) 786 push_command = 'push %s %s' % (local_path, device_path)
760 self._LogShell(push_command) 787 self._LogShell(push_command)
761 output = self._adb.SendCommand(push_command, timeout_time=30 * 60) 788 output = self._adb.SendCommand(push_command, timeout_time=30 * 60)
762 assert _HasAdbPushSucceeded(output) 789 assert _HasAdbPushSucceeded(output)
763 790
764
765 def GetFileContents(self, filename, log_result=False): 791 def GetFileContents(self, filename, log_result=False):
766 """Gets contents from the file specified by |filename|.""" 792 """Gets contents from the file specified by |filename|."""
767 return self.RunShellCommand('cat "%s" 2>/dev/null' % filename, 793 return self.RunShellCommand('cat "%s" 2>/dev/null' % filename,
768 log_result=log_result) 794 log_result=log_result)
769 795
770 def SetFileContents(self, filename, contents): 796 def SetFileContents(self, filename, contents):
771 """Writes |contents| to the file specified by |filename|.""" 797 """Writes |contents| to the file specified by |filename|."""
772 with tempfile.NamedTemporaryFile() as f: 798 with tempfile.NamedTemporaryFile() as f:
773 f.write(contents) 799 f.write(contents)
774 f.flush() 800 f.flush()
(...skipping 492 matching lines...) Expand 10 before | Expand all | Expand 10 after
1267 host_dir = os.path.dirname(host_file) 1293 host_dir = os.path.dirname(host_file)
1268 if not os.path.exists(host_dir): 1294 if not os.path.exists(host_dir):
1269 os.makedirs(host_dir) 1295 os.makedirs(host_dir)
1270 device_file = '%s/screenshot.png' % self.GetExternalStorage() 1296 device_file = '%s/screenshot.png' % self.GetExternalStorage()
1271 self.RunShellCommand('/system/bin/screencap -p %s' % device_file) 1297 self.RunShellCommand('/system/bin/screencap -p %s' % device_file)
1272 assert self._adb.Pull(device_file, host_file) 1298 assert self._adb.Pull(device_file, host_file)
1273 assert os.path.exists(host_file) 1299 assert os.path.exists(host_file)
1274 1300
1275 def SetUtilWrapper(self, util_wrapper): 1301 def SetUtilWrapper(self, util_wrapper):
1276 """Sets a wrapper prefix to be used when running a locally-built 1302 """Sets a wrapper prefix to be used when running a locally-built
1277 binary on the device (ex.: md5sum_bin). 1303 binary on the device (ex.: timemodified_bin).
1278 """ 1304 """
1279 self._util_wrapper = util_wrapper 1305 self._util_wrapper = util_wrapper
1280 1306
1281 def RunInstrumentationTest(self, test, test_package, instr_args, timeout): 1307 def RunInstrumentationTest(self, test, test_package, instr_args, timeout):
1282 """Runs a single instrumentation test. 1308 """Runs a single instrumentation test.
1283 1309
1284 Args: 1310 Args:
1285 test: Test class/method. 1311 test: Test class/method.
1286 test_package: Package name of test apk. 1312 test_package: Package name of test apk.
1287 instr_args: Extra key/value to pass to am instrument. 1313 instr_args: Extra key/value to pass to am instrument.
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
1337 """ 1363 """
1338 def __init__(self, output): 1364 def __init__(self, output):
1339 self._output = output 1365 self._output = output
1340 1366
1341 def write(self, data): 1367 def write(self, data):
1342 data = data.replace('\r\r\n', '\n') 1368 data = data.replace('\r\r\n', '\n')
1343 self._output.write(data) 1369 self._output.write(data)
1344 1370
1345 def flush(self): 1371 def flush(self):
1346 self._output.flush() 1372 self._output.flush()
OLDNEW
« no previous file with comments | « no previous file | build/android/pylib/gtest/test_package_apk.py » ('j') | tools/android/timemodified/timemodified.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698