diff options
author | jbudorick@chromium.org <jbudorick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-17 09:54:06 +0000 |
---|---|---|
committer | jbudorick@chromium.org <jbudorick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-17 09:54:06 +0000 |
commit | 4fbf991d7b36cb5700214d0fd4c6c10eadbc406f (patch) | |
tree | d4232330d437d37131d62cb56c63d0e74878ebe8 /build | |
parent | 2d6f7dde16dfca0c5253cc8a46e2db702d61be30 (diff) | |
download | chromium_src-4fbf991d7b36cb5700214d0fd4c6c10eadbc406f.zip chromium_src-4fbf991d7b36cb5700214d0fd4c6c10eadbc406f.tar.gz chromium_src-4fbf991d7b36cb5700214d0fd4c6c10eadbc406f.tar.bz2 |
[Android] Switch to DeviceUtils versions of GetPid, TakeScreenshot, and GetIoStats.
BUG=267773
Review URL: https://codereview.chromium.org/386053002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@283731 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'build')
-rw-r--r-- | build/android/pylib/device/device_utils.py | 239 | ||||
-rwxr-xr-x | build/android/pylib/device/device_utils_test.py | 263 | ||||
-rw-r--r-- | build/android/pylib/forwarder.py | 6 | ||||
-rw-r--r-- | build/android/pylib/instrumentation/test_runner.py | 2 | ||||
-rw-r--r-- | build/android/pylib/monkey/test_runner.py | 14 | ||||
-rw-r--r-- | build/android/pylib/screenshot.py | 15 | ||||
-rwxr-xr-x | build/android/screenshot.py | 4 |
7 files changed, 377 insertions, 166 deletions
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py index 4788472..33d6351 100644 --- a/build/android/pylib/device/device_utils.py +++ b/build/android/pylib/device/device_utils.py @@ -81,12 +81,12 @@ class DeviceUtils(object): """Checks whether the device is online. Args: - timeout: An integer containing the number of seconds to wait for the - operation to complete. - retries: An integer containing the number of times the operation should - be retried if it fails. + timeout: timeout in seconds + retries: number of retries Returns: True if the device is online, False otherwise. + Raises: + CommandTimeoutError on timeout. """ return self.old_interface.IsOnline() @@ -95,21 +95,26 @@ class DeviceUtils(object): """Checks whether or not adbd has root privileges. Args: - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Returns: True if adbd has root privileges, False otherwise. + Raises: + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ return self._HasRootImpl() def _HasRootImpl(self): - """ Implementation of HasRoot. + """Implementation of HasRoot. This is split from HasRoot to allow other DeviceUtils methods to call HasRoot without spawning a new timeout thread. Returns: Same as for |HasRoot|. + Raises: + Same as for |HasRoot|. """ return self.old_interface.IsRootEnabled() @@ -118,10 +123,11 @@ class DeviceUtils(object): """Restarts adbd with root privileges. Args: - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Raises: CommandFailedError if root could not be enabled. + CommandTimeoutError on timeout. """ if not self.old_interface.EnableAdbRoot(): raise device_errors.CommandFailedError( @@ -132,10 +138,14 @@ class DeviceUtils(object): """Get the device's path to its SD card. Args: - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Returns: The device's path to its SD card. + Raises: + CommandFailedError if the external storage path could not be determined. + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ try: return self.old_interface.GetExternalStorage() @@ -153,16 +163,17 @@ class DeviceUtils(object): Args: wifi: A boolean indicating if we should wait for wifi to come up or not. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Raises: + CommandFailedError on failure. CommandTimeoutError if one of the component waits times out. DeviceUnreachableError if the device becomes unresponsive. """ self._WaitUntilFullyBootedImpl(wifi=wifi, timeout=timeout) def _WaitUntilFullyBootedImpl(self, wifi=False, timeout=None): - """ Implementation of WaitUntilFullyBooted. + """Implementation of WaitUntilFullyBooted. This is split from WaitUntilFullyBooted to allow other DeviceUtils methods to call WaitUntilFullyBooted without spawning a new timeout thread. @@ -172,7 +183,7 @@ class DeviceUtils(object): Args: wifi: Same as for |WaitUntilFullyBooted|. - timeout: Same as for |IsOnline|. + timeout: timeout in seconds Raises: Same as for |WaitUntilFullyBooted|. """ @@ -197,8 +208,11 @@ class DeviceUtils(object): Args: block: A boolean indicating if we should wait for the reboot to complete. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries + Raises: + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ self.old_interface.Reboot() if block: @@ -218,11 +232,12 @@ class DeviceUtils(object): Args: apk_path: A string containing the path to the APK to install. reinstall: A boolean indicating if we should keep any existing app data. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Raises: CommandFailedError if the installation fails. CommandTimeoutError if the installation times out. + DeviceUnreachableError on missing device. """ package_name = apk_helper.GetPackageName(apk_path) device_path = self.old_interface.GetApplicationPath(package_name) @@ -266,12 +281,14 @@ class DeviceUtils(object): be checked. as_root: A boolean indicating whether the shell command should be run with root privileges. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. - Raises: - CommandFailedError if check_return is True and the return code is nozero. + timeout: timeout in seconds + retries: number of retries Returns: The output of the command. + Raises: + CommandFailedError if check_return is True and the return code is nozero. + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ return self._RunShellCommandImpl(cmd, check_return=check_return, as_root=as_root, timeout=timeout) @@ -290,7 +307,7 @@ class DeviceUtils(object): cmd: Same as for |RunShellCommand|. check_return: Same as for |RunShellCommand|. as_root: Same as for |RunShellCommand|. - timeout: Same as for |IsOnline|. + timeout: timeout in seconds Raises: Same as for |RunShellCommand|. Returns: @@ -323,10 +340,12 @@ class DeviceUtils(object): root privileges. blocking: A boolean indicating whether we should wait until all processes with the given |process_name| are dead. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Raises: CommandFailedError if no process was killed. + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ pids = self.old_interface.ExtractPid(process_name) if len(pids) == 0: @@ -357,10 +376,12 @@ class DeviceUtils(object): trace should be saved. force_stop: A boolean indicating whether we should stop the activity before starting it. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Raises: CommandFailedError if the activity could not be started. + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ single_category = (intent.category[0] if isinstance(intent.category, list) else intent.category) @@ -379,8 +400,11 @@ class DeviceUtils(object): Args: intent: An Intent to broadcast. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries + Raises: + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ package, old_intent = intent.action.rsplit('.', 1) if intent.extras is None: @@ -395,8 +419,11 @@ class DeviceUtils(object): """Return to the home screen. Args: - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries + Raises: + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ self.old_interface.GoHome() @@ -406,8 +433,11 @@ class DeviceUtils(object): Args: package: A string containing the name of the package to stop. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries + Raises: + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ self.old_interface.CloseApplication(package) @@ -417,8 +447,11 @@ class DeviceUtils(object): Args: package: A string containing the name of the package to stop. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries + Raises: + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ self.old_interface.ClearApplicationState(package) @@ -430,8 +463,11 @@ class DeviceUtils(object): Args: keycode: A integer keycode to send to the device. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries + Raises: + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ self.old_interface.SendKeyEvent(keycode) @@ -450,8 +486,12 @@ class DeviceUtils(object): on the host that should be minimally pushed to the device. device_path: A string containing the absolute path of the destination on the device. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries + Raises: + CommandFailedError on failure. + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ self.old_interface.PushIfNeeded(host_path, device_path) @@ -462,10 +502,13 @@ class DeviceUtils(object): Args: device_path: A string containing the absolute path to the file on the device. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Returns: True if the file exists on the device, False otherwise. + Raises: + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ return self._FileExistsImpl(device_path) @@ -479,6 +522,8 @@ class DeviceUtils(object): device_path: Same as for |FileExists|. Returns: True if the file exists on the device, False otherwise. + Raises: + Same as for |FileExists|. """ return self.old_interface.FileExistsOnDevice(device_path) @@ -491,10 +536,17 @@ class DeviceUtils(object): from the device. host_path: A string containing the absolute path of the destination on the host. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries + Raises: + CommandFailedError on failure. + CommandTimeoutError on timeout. """ - self.old_interface.PullFileFromDevice(device_path, host_path) + try: + self.old_interface.PullFileFromDevice(device_path, host_path) + except AssertionError as e: + raise device_errors.CommandFailedError( + str(e), device=str(self)), None, sys.exc_info()[2] @decorators.WithTimeoutAndRetriesFromInstance() def ReadFile(self, device_path, as_root=False, timeout=None, retries=None): @@ -505,12 +557,14 @@ class DeviceUtils(object): from the device. as_root: A boolean indicating whether the read should be executed with root privileges. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Returns: The contents of the file at |device_path| as a list of lines. Raises: CommandFailedError if the file can't be read. + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ # TODO(jbudorick) Evaluate whether we actually want to return a list of # lines after the implementation switch. @@ -533,10 +587,12 @@ class DeviceUtils(object): contents: A string containing the data to write to the device. as_root: A boolean indicating whether the write should be executed with root privileges. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Raises: CommandFailedError if the file could not be written on the device. + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ if as_root: if not self.old_interface.CanAccessProtectedFileContents(): @@ -553,10 +609,13 @@ class DeviceUtils(object): Args: device_path: A string containing the path of the directory on the device to list. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Returns: The contents of the directory specified by |device_path|. + Raises: + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. """ return self.old_interface.ListPathContents(device_path) @@ -567,8 +626,10 @@ class DeviceUtils(object): Args: enabled: A boolean indicating whether Java asserts should be enabled or disabled. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries + Raises: + CommandTimeoutError on timeout. """ self.old_interface.SetJavaAssertsEnabled(enabled) @@ -579,10 +640,12 @@ class DeviceUtils(object): Args: property_name: A string containing the name of the property to get from the device. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries Returns: The value of the device's |property_name| property. + Raises: + CommandTimeoutError on timeout. """ return self.old_interface.system_properties[property_name] @@ -595,11 +658,73 @@ class DeviceUtils(object): the device. value: A string containing the value to set to the property on the device. - timeout: Same as for |IsOnline|. - retries: Same as for |IsOnline|. + timeout: timeout in seconds + retries: number of retries + Raises: + CommandTimeoutError on timeout. """ self.old_interface.system_properties[property_name] = value + @decorators.WithTimeoutAndRetriesFromInstance() + def GetPids(self, process_name, timeout=None, retries=None): + """Returns the PIDs of processes with the given name. + + Note that the |process_name| is often the package name. + + Args: + process_name: A string containing the process name to get the PIDs for. + timeout: timeout in seconds + retries: number of retries + Returns: + A dict mapping process name to PID for each process that contained the + provided |process_name|. + Raises: + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. + """ + procs_pids = {} + for line in self._RunShellCommandImpl('ps'): + try: + ps_data = line.split() + if process_name in ps_data[-1]: + procs_pids[ps_data[-1]] = ps_data[1] + except IndexError: + pass + return procs_pids + + @decorators.WithTimeoutAndRetriesFromInstance() + def TakeScreenshot(self, host_path=None, timeout=None, retries=None): + """Takes a screenshot of the device. + + Args: + host_path: A string containing the path on the host to save the + screenshot to. If None, a file name will be generated. + timeout: timeout in seconds + retries: number of retries + Returns: + The name of the file on the host to which the screenshot was saved. + Raises: + CommandFailedError on failure. + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. + """ + return self.old_interface.TakeScreenshot(host_path) + + @decorators.WithTimeoutAndRetriesFromInstance() + def GetIOStats(self, timeout=None, retries=None): + """Gets cumulative disk IO stats since boot for all processes. + + Args: + timeout: timeout in seconds + retries: number of retries + Returns: + A dict containing |num_reads|, |num_writes|, |read_ms|, and |write_ms|. + Raises: + CommandTimeoutError on timeout. + DeviceUnreachableError on missing device. + """ + return self.old_interface.GetIoStats() + def __str__(self): """Returns the device serial.""" return self.old_interface.GetDevice() diff --git a/build/android/pylib/device/device_utils_test.py b/build/android/pylib/device/device_utils_test.py index 216fc38..bd671d1 100755 --- a/build/android/pylib/device/device_utils_test.py +++ b/build/android/pylib/device/device_utils_test.py @@ -72,6 +72,75 @@ class _PatchedFunction(object): self.mocked = mocked +class MockFileSystem(object): + + @staticmethod + def osStatResult( + st_mode=None, st_ino=None, st_dev=None, st_nlink=None, st_uid=None, + st_gid=None, st_size=None, st_atime=None, st_mtime=None, st_ctime=None): + MockOSStatResult = collections.namedtuple('MockOSStatResult', [ + 'st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid', 'st_gid', + 'st_size', 'st_atime', 'st_mtime', 'st_ctime']) + return MockOSStatResult(st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, + st_size, st_atime, st_mtime, st_ctime) + + MOCKED_FUNCTIONS = [ + ('os.path.abspath', ''), + ('os.path.dirname', ''), + ('os.path.exists', False), + ('os.path.getsize', 0), + ('os.path.isdir', False), + ('os.stat', osStatResult.__func__()), + ('os.walk', []), + ] + + def _get(self, mocked, path, default_val): + if self._verbose: + logging.debug('%s(%s)' % (mocked, path)) + return (self.mock_file_info[path][mocked] + if path in self.mock_file_info + else default_val) + + def _patched(self, target, default_val=None): + r = lambda f: self._get(target, f, default_val) + return _PatchedFunction(patched=mock.patch(target, side_effect=r)) + + def __init__(self, verbose=False): + self.mock_file_info = {} + self._patched_functions = [ + self._patched(m, d) for m, d in type(self).MOCKED_FUNCTIONS] + self._verbose = verbose + + def addMockFile(self, path, **kw): + self._addMockThing(path, False, **kw) + + def addMockDirectory(self, path, **kw): + self._addMockThing(path, True, **kw) + + def _addMockThing(self, path, is_dir, size=0, stat=None, walk=None): + if stat is None: + stat = self.osStatResult() + if walk is None: + walk = [] + self.mock_file_info[path] = { + 'os.path.abspath': path, + 'os.path.dirname': '/' + '/'.join(path.strip('/').split('/')[:-1]), + 'os.path.exists': True, + 'os.path.isdir': is_dir, + 'os.path.getsize': size, + 'os.stat': stat, + 'os.walk': walk, + } + + def __enter__(self): + for p in self._patched_functions: + p.mocked = p.patched.__enter__() + + def __exit__(self, exc_type, exc_val, exc_tb): + for p in self._patched_functions: + p.patched.__exit__() + + class DeviceUtilsOldImplTest(unittest.TestCase): class AndroidCommandsCalls(object): @@ -787,68 +856,6 @@ class DeviceUtilsSendKeyEventTest(DeviceUtilsOldImplTest): class DeviceUtilsPushChangedFilesTest(DeviceUtilsOldImplTest): - class MockFileSystem(object): - - @staticmethod - def osStatResult( - st_mode=None, st_ino=None, st_dev=None, st_nlink=None, st_uid=None, - st_gid=None, st_size=None, st_atime=None, st_mtime=None, st_ctime=None): - MockOSStatResult = collections.namedtuple('MockOSStatResult', [ - 'st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid', 'st_gid', - 'st_size', 'st_atime', 'st_mtime', 'st_ctime']) - return MockOSStatResult(st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, - st_size, st_atime, st_mtime, st_ctime) - - def _get(self, mocked, path, default_val): - return (self.mock_file_info[path][mocked] - if path in self.mock_file_info - else default_val) - - def _patched(self, target, default_val=None): - r = lambda f: self._get(target, f, default_val) - return _PatchedFunction(patched=mock.patch(target, side_effect=r)) - - def __init__(self): - self.mock_file_info = {} - self._os_path_exists = self._patched('os.path.exists', default_val=False) - self._os_path_getsize = self._patched('os.path.getsize', default_val=0) - self._os_path_isdir = self._patched('os.path.isdir', default_val=False) - self._os_stat = self._patched('os.stat', default_val=self.osStatResult()) - self._os_walk = self._patched('os.walk', default_val=[]) - - def addMockFile(self, path, size, **kw): - self._addMockThing(path, size, False, **kw) - - def addMockDirectory(self, path, size, **kw): - self._addMockThing(path, size, True, **kw) - - def _addMockThing(self, path, size, is_dir, stat=None, walk=None): - if stat is None: - stat = self.osStatResult() - if walk is None: - walk = [] - self.mock_file_info[path] = { - 'os.path.exists': True, - 'os.path.isdir': is_dir, - 'os.path.getsize': size, - 'os.stat': stat, - 'os.walk': walk, - } - - def __enter__(self): - self._os_path_exists.mocked = self._os_path_exists.patched.__enter__() - self._os_path_getsize.mocked = self._os_path_getsize.patched.__enter__() - self._os_path_isdir.mocked = self._os_path_isdir.patched.__enter__() - self._os_stat.mocked = self._os_stat.patched.__enter__() - self._os_walk.mocked = self._os_walk.patched.__enter__() - - def __exit__(self, exc_type, exc_val, exc_tb): - self._os_walk.patched.__exit__() - self._os_stat.patched.__exit__() - self._os_path_isdir.patched.__exit__(exc_type, exc_val, exc_tb) - self._os_path_getsize.patched.__exit__(exc_type, exc_val, exc_tb) - self._os_path_exists.patched.__exit__(exc_type, exc_val, exc_tb) - def testPushChangedFiles_noHostPath(self): with mock.patch('os.path.exists', return_value=False): @@ -861,8 +868,8 @@ class DeviceUtilsPushChangedFilesTest(DeviceUtilsOldImplTest): host_file_path = '/test/host/path' device_file_path = '/test/device/path' - mock_fs = self.MockFileSystem() - mock_fs.addMockFile(host_file_path, 100) + mock_fs = MockFileSystem() + mock_fs.addMockFile(host_file_path, size=100) self.device.old_interface.GetFilesChanged = mock.Mock(return_value=[]) @@ -877,10 +884,10 @@ class DeviceUtilsPushChangedFilesTest(DeviceUtilsOldImplTest): host_file_path = '/test/host/path' device_file_path = '/test/device/path' - mock_fs = self.MockFileSystem() + mock_fs = MockFileSystem() mock_fs.addMockFile( - host_file_path, 100, - stat=self.MockFileSystem.osStatResult(st_mtime=1000000000)) + host_file_path, size=100, + stat=MockFileSystem.osStatResult(st_mtime=1000000000)) self.device.old_interface.GetFilesChanged = mock.Mock( return_value=[('/test/host/path', '/test/device/path')]) @@ -896,16 +903,16 @@ class DeviceUtilsPushChangedFilesTest(DeviceUtilsOldImplTest): host_file_path = '/test/host/path' device_file_path = '/test/device/path' - mock_fs = self.MockFileSystem() + mock_fs = MockFileSystem() mock_fs.addMockDirectory( - host_file_path, 256, - stat=self.MockFileSystem.osStatResult(st_mtime=1000000000)) + host_file_path, size=256, + stat=MockFileSystem.osStatResult(st_mtime=1000000000)) mock_fs.addMockFile( - host_file_path + '/file1', 251, - stat=self.MockFileSystem.osStatResult(st_mtime=1000000001)) + host_file_path + '/file1', size=251, + stat=MockFileSystem.osStatResult(st_mtime=1000000001)) mock_fs.addMockFile( - host_file_path + '/file2', 252, - stat=self.MockFileSystem.osStatResult(st_mtime=1000000002)) + host_file_path + '/file2', size=252, + stat=MockFileSystem.osStatResult(st_mtime=1000000002)) self.device.old_interface.GetFilesChanged = mock.Mock(return_value=[]) @@ -921,17 +928,17 @@ class DeviceUtilsPushChangedFilesTest(DeviceUtilsOldImplTest): host_file_path = '/test/host/path' device_file_path = '/test/device/path' - mock_fs = self.MockFileSystem() + mock_fs = MockFileSystem() mock_fs.addMockDirectory( - host_file_path, 256, - stat=self.MockFileSystem.osStatResult(st_mtime=1000000000), + host_file_path, size=256, + stat=MockFileSystem.osStatResult(st_mtime=1000000000), walk=[('/test/host/path', [], ['file1', 'file2'])]) mock_fs.addMockFile( - host_file_path + '/file1', 256, - stat=self.MockFileSystem.osStatResult(st_mtime=1000000001)) + host_file_path + '/file1', size=256, + stat=MockFileSystem.osStatResult(st_mtime=1000000001)) mock_fs.addMockFile( - host_file_path + '/file2', 256, - stat=self.MockFileSystem.osStatResult(st_mtime=1000000002)) + host_file_path + '/file2', size=256, + stat=MockFileSystem.osStatResult(st_mtime=1000000002)) self.device.old_interface.GetFilesChanged = mock.Mock( return_value=[('/test/host/path/file1', '/test/device/path/file1')]) @@ -951,16 +958,16 @@ class DeviceUtilsPushChangedFilesTest(DeviceUtilsOldImplTest): host_file_path = '/test/host/path' device_file_path = '/test/device/path' - mock_fs = self.MockFileSystem() + mock_fs = MockFileSystem() mock_fs.addMockDirectory( - host_file_path, 256, - stat=self.MockFileSystem.osStatResult(st_mtime=1000000000)) + host_file_path, size=256, + stat=MockFileSystem.osStatResult(st_mtime=1000000000)) mock_fs.addMockFile( - host_file_path + '/file1', 256, - stat=self.MockFileSystem.osStatResult(st_mtime=1000000001)) + host_file_path + '/file1', size=256, + stat=MockFileSystem.osStatResult(st_mtime=1000000001)) mock_fs.addMockFile( - host_file_path + '/file2', 256, - stat=self.MockFileSystem.osStatResult(st_mtime=1000000002)) + host_file_path + '/file2', size=256, + stat=MockFileSystem.osStatResult(st_mtime=1000000002)) self.device.old_interface.GetFilesChanged = mock.Mock( return_value=[('/test/host/path/file1', '/test/device/path/file1'), @@ -1356,6 +1363,86 @@ class DeviceUtilsSetPropTest(DeviceUtilsOldImplTest): self.device.SetProp('this.is.a.test.property', 'test_property_value') +class DeviceUtilsGetPidsTest(DeviceUtilsOldImplTest): + + def testGetPids_noMatches(self): + with self.assertCalls( + "adb -s 0123456789abcdef shell 'ps'", + 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' + 'user 1000 100 1024 1024 ffffffff 00000000 no.match\r\n'): + self.assertEqual({}, self.device.GetPids('does.not.match')) + + def testGetPids_oneMatch(self): + with self.assertCalls( + "adb -s 0123456789abcdef shell 'ps'", + 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' + 'user 1000 100 1024 1024 ffffffff 00000000 not.a.match\r\n' + 'user 1001 100 1024 1024 ffffffff 00000000 one.match\r\n'): + self.assertEqual({'one.match': '1001'}, self.device.GetPids('one.match')) + + def testGetPids_mutlipleMatches(self): + with self.assertCalls( + "adb -s 0123456789abcdef shell 'ps'", + 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' + 'user 1000 100 1024 1024 ffffffff 00000000 not\r\n' + 'user 1001 100 1024 1024 ffffffff 00000000 one.match\r\n' + 'user 1002 100 1024 1024 ffffffff 00000000 two.match\r\n' + 'user 1003 100 1024 1024 ffffffff 00000000 three.match\r\n'): + self.assertEqual( + {'one.match': '1001', 'two.match': '1002', 'three.match': '1003'}, + self.device.GetPids('match')) + + def testGetPids_exactMatch(self): + with self.assertCalls( + "adb -s 0123456789abcdef shell 'ps'", + 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' + 'user 1000 100 1024 1024 ffffffff 00000000 not.exact.match\r\n' + 'user 1234 100 1024 1024 ffffffff 00000000 exact.match\r\n'): + self.assertEqual( + {'not.exact.match': '1000', 'exact.match': '1234'}, + self.device.GetPids('exact.match')) + + +class DeviceUtilsTakeScreenshotTest(DeviceUtilsOldImplTest): + + def testTakeScreenshot_fileNameProvided(self): + mock_fs = MockFileSystem() + mock_fs.addMockDirectory('/test/host') + mock_fs.addMockFile('/test/host/screenshot.png') + + with mock_fs: + with self.assertCallsSequence( + cmd_ret=[ + (r"adb -s 0123456789abcdef shell 'echo \$EXTERNAL_STORAGE'", + '/test/external/storage\r\n'), + (r"adb -s 0123456789abcdef shell '/system/bin/screencap -p \S+'", + ''), + (r"adb -s 0123456789abcdef shell ls \S+", + '/test/external/storage/screenshot.png\r\n'), + (r'adb -s 0123456789abcdef pull \S+ /test/host/screenshot.png', + '100 B/s (100 B in 1.000s)\r\n'), + (r"adb -s 0123456789abcdef shell 'rm -f \S+'", '') + ], + comp=re.match): + self.device.TakeScreenshot('/test/host/screenshot.png') + + +class DeviceUtilsGetIOStatsTest(DeviceUtilsOldImplTest): + + def testGetIOStats(self): + with self.assertCalls( + "adb -s 0123456789abcdef shell 'cat \"/proc/diskstats\" 2>/dev/null'", + '179 0 mmcblk0 1 2 3 4 5 6 7 8 9 10 11\r\n'): + self.assertEqual( + { + 'num_reads': 1, + 'num_writes': 5, + 'read_ms': 4, + 'write_ms': 8, + }, + self.device.GetIOStats()) + + if __name__ == '__main__': logging.getLogger().setLevel(logging.DEBUG) unittest.main(verbosity=2) diff --git a/build/android/pylib/forwarder.py b/build/android/pylib/forwarder.py index 7c68e21..be67d7d 100644 --- a/build/android/pylib/forwarder.py +++ b/build/android/pylib/forwarder.py @@ -341,9 +341,7 @@ class Forwarder(object): # 'kill-server') is not running on the bots anymore. timeout_sec = 5 try: - device.KillAll( - 'device_forwarder', blocking=True, timeout=timeout_sec) + device.KillAll('device_forwarder', blocking=True, timeout=timeout_sec) except device_errors.CommandFailedError: - pids = device.old_interface.ExtractPid('device_forwarder') - if pids: + if device.GetPids('device_forwarder'): raise diff --git a/build/android/pylib/instrumentation/test_runner.py b/build/android/pylib/instrumentation/test_runner.py index 4f05db6..923a3fd 100644 --- a/build/android/pylib/instrumentation/test_runner.py +++ b/build/android/pylib/instrumentation/test_runner.py @@ -142,7 +142,7 @@ class TestRunner(base_test_runner.BaseTestRunner): """Takes a screenshot from the device.""" screenshot_name = os.path.join(constants.SCREENSHOTS_DIR, '%s.png' % test) logging.info('Taking screenshot named %s', screenshot_name) - self.device.old_interface.TakeScreenshot(screenshot_name) + self.device.TakeScreenshot(screenshot_name) def SetUp(self): """Sets up the test harness and device before all tests are run.""" diff --git a/build/android/pylib/monkey/test_runner.py b/build/android/pylib/monkey/test_runner.py index 42b7d41..19dd339 100644 --- a/build/android/pylib/monkey/test_runner.py +++ b/build/android/pylib/monkey/test_runner.py @@ -58,22 +58,22 @@ class TestRunner(base_test_runner.BaseTestRunner): # Chrome crashes are not always caught by Monkey test runner. # Verify Chrome has the same PID before and after the test. - before_pids = self.device.old_interface.ExtractPid(self._package) + before_pids = self.device.GetPids(self._package) # Run the test. output = '' if before_pids: output = '\n'.join(self._LaunchMonkeyTest()) - after_pids = self.device.old_interface.ExtractPid(self._package) + after_pids = self.device.GetPids(self._package) crashed = True - if not before_pids: + if not self._package in before_pids: logging.error('Failed to start the process.') - elif not after_pids: - logging.error('Process %s has died.', before_pids[0]) - elif before_pids[0] != after_pids[0]: + elif not self._package in after_pids: + logging.error('Process %s has died.', before_pids[self._package]) + elif before_pids[self._package] != after_pids[self._package]: logging.error('Detected process restart %s -> %s', - before_pids[0], after_pids[0]) + before_pids[self._package], after_pids[self._package]) else: crashed = False diff --git a/build/android/pylib/screenshot.py b/build/android/pylib/screenshot.py index 25d9b1a..d69bb9a 100644 --- a/build/android/pylib/screenshot.py +++ b/build/android/pylib/screenshot.py @@ -3,6 +3,7 @@ # found in the LICENSE file. import os +import signal import tempfile from pylib import cmd_helper @@ -33,7 +34,6 @@ class VideoRecorder(object): self._device_file = ( '%s/screen-recording.mp4' % device.GetExternalStoragePath()) self._recorder = None - self._recorder_pids = None self._recorder_stdout = None self._is_started = False @@ -53,8 +53,7 @@ class VideoRecorder(object): self._recorder_stdout = tempfile.mkstemp()[1] self._recorder = cmd_helper.Popen( self._args, stdout=open(self._recorder_stdout, 'w')) - self._recorder_pids = self._device.old_interface.ExtractPid('screenrecord') - if not self._recorder_pids: + if not self._device.GetPids('screenrecord'): raise RuntimeError('Recording failed. Is your device running Android ' 'KitKat or later?') @@ -70,17 +69,18 @@ class VideoRecorder(object): """Stop recording video.""" os.remove(self._recorder_stdout) self._is_started = False - if not self._recorder or not self._recorder_pids: + if not self._recorder: return - self._device.RunShellCommand( - 'kill -SIGINT ' + ' '.join(self._recorder_pids)) + self._device.KillAll('screenrecord', signum=signal.SIGINT) self._recorder.wait() - def Pull(self, host_file): + def Pull(self, host_file=None): """Pull resulting video file from the device. Args: host_file: Path to the video file to store on the host. + Returns: + Output video file name on the host. """ host_file_name = host_file or ('screen-recording-%s.mp4' % self._device.old_interface.GetTimestamp()) @@ -88,3 +88,4 @@ class VideoRecorder(object): self._device.old_interface.EnsureHostDirectory(host_file_name) self._device.PullFile(self._device_file, host_file_name) self._device.RunShellCommand('rm -f "%s"' % self._device_file) + return host_file_name diff --git a/build/android/screenshot.py b/build/android/screenshot.py index a91bafd..fb1aee1 100755 --- a/build/android/screenshot.py +++ b/build/android/screenshot.py @@ -21,7 +21,7 @@ def _PrintMessage(heading, eol='\n'): def _CaptureScreenshot(device, host_file): - host_file = device.old_interface.TakeScreenshot(host_file) + host_file = device.TakeScreenshot(host_file) _PrintMessage('Screenshot written to %s' % os.path.abspath(host_file)) @@ -37,7 +37,7 @@ def _CaptureVideo(device, host_file, options): raw_input() finally: recorder.Stop() - recorder.Pull(host_file) + host_file = recorder.Pull(host_file) _PrintMessage('Video written to %s' % os.path.abspath(host_file)) |