diff --git a/android/gyp/create_device_library_links.py b/android/gyp/create_device_library_links.py index 599642aa7..d5828f4c3 100755 --- a/android/gyp/create_device_library_links.py +++ b/android/gyp/create_device_library_links.py @@ -64,7 +64,7 @@ def TriggerSymlinkScript(options): mkdir_cmd = ('if [ ! -e %(dir)s ]; then mkdir -p %(dir)s; fi ' % { 'dir': device_dir }) RunShellCommand(device, mkdir_cmd) - device.PushIfNeeded(options.script_host_path, options.script_device_path) + device.PushChangedFiles(options.script_host_path, options.script_device_path) trigger_cmd = ( 'APK_LIBRARIES_DIR=%(apk_libraries_dir)s; ' diff --git a/android/gyp/push_libraries.py b/android/gyp/push_libraries.py index 21258efe6..efb9dfcc6 100755 --- a/android/gyp/push_libraries.py +++ b/android/gyp/push_libraries.py @@ -40,7 +40,7 @@ def DoPush(options): if needs_directory: device.RunShellCommand('mkdir -p ' + options.device_dir) needs_directory[:] = [] # = False - device.PushIfNeeded(host_path, device_path) + device.PushChangedFiles(host_path, device_path) record_path = '%s.%s.push.md5.stamp' % (host_path, serial_number) md5_check.CallAndRecordIfStale( diff --git a/android/gyp/util/build_device.py b/android/gyp/util/build_device.py index a5b0dd539..b153a157b 100644 --- a/android/gyp/util/build_device.py +++ b/android/gyp/util/build_device.py @@ -33,8 +33,8 @@ class BuildDevice(object): def RunShellCommand(self, *args, **kwargs): return self.device.RunShellCommand(*args, **kwargs) - def PushIfNeeded(self, *args, **kwargs): - return self.device.old_interface.PushIfNeeded(*args, **kwargs) + def PushChangedFiles(self, *args, **kwargs): + return self.device.PushChangedFiles(*args, **kwargs) def GetSerialNumber(self): return self.id diff --git a/android/provision_devices.py b/android/provision_devices.py index beb4106a9..82833de71 100755 --- a/android/provision_devices.py +++ b/android/provision_devices.py @@ -72,7 +72,7 @@ def PushAndLaunchAdbReboot(devices, target): print ' Pushing adb_reboot ...' adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT, 'out/%s/adb_reboot' % target) - device.old_interface.PushIfNeeded(adb_reboot, '/data/local/tmp/') + device.PushChangedFiles(adb_reboot, '/data/local/tmp/') # Launch adb_reboot print ' Launching adb_reboot ...' device.old_interface.GetAndroidToolStatusAndOutput( @@ -91,9 +91,9 @@ def _ConfigureLocalProperties(device, is_perf): if not is_perf: local_props.append('%s=all' % android_commands.JAVA_ASSERT_PROPERTY) local_props.append('debug.checkjni=1') - device.old_interface.SetProtectedFileContents( + device.WriteFile( constants.DEVICE_LOCAL_PROPERTIES_PATH, - '\n'.join(local_props)) + '\n'.join(local_props), as_root=True) # Android will not respect the local props file if it is world writable. device.RunShellCommand( 'chmod 644 %s' % constants.DEVICE_LOCAL_PROPERTIES_PATH, @@ -114,8 +114,7 @@ def WipeDeviceData(device): Arguments: device: the device to wipe """ - device_authorized = device.old_interface.FileExistsOnDevice( - constants.ADB_KEYS_FILE) + device_authorized = device.FileExists(constants.ADB_KEYS_FILE) if device_authorized: adb_keys = device.RunShellCommand('cat %s' % constants.ADB_KEYS_FILE, as_root=True) diff --git a/android/pylib/android_commands.py b/android/pylib/android_commands.py index df7e56f70..3d13f196a 100644 --- a/android/pylib/android_commands.py +++ b/android/pylib/android_commands.py @@ -1054,7 +1054,9 @@ class AndroidCommands(object): All pushed files can be removed by calling RemovePushedFiles(). """ MAX_INDIVIDUAL_PUSHES = 50 - assert os.path.exists(host_path), 'Local path not found %s' % host_path + if not os.path.exists(host_path): + raise device_errors.CommandFailedError( + 'Local path not found %s' % host_path, device=str(self)) # See if the file on the host changed since the last push (if any) and # return early if it didn't. Note that this shortcut assumes that the tests @@ -1110,8 +1112,6 @@ class AndroidCommands(object): # approximates the push time for each method. if len(changed_files) > MAX_INDIVIDUAL_PUSHES or diff_size > 0.5 * size: self._actual_push_size += size - if os.path.isdir(host_path): - self.RunShellCommand('mkdir -p %s' % device_path) Push(host_path, device_path) else: for f in changed_files: @@ -1783,7 +1783,9 @@ class AndroidCommands(object): device_file: Absolute path to the file to retrieve from the device. host_file: Absolute path to the file to store on the host. """ - assert self._adb.Pull(device_file, host_file) + if not self._adb.Pull(device_file, host_file): + raise device_errors.AdbCommandFailedError( + ['pull', device_file, host_file], 'Failed to pull file from device.') assert os.path.exists(host_file) def SetUtilWrapper(self, util_wrapper): diff --git a/android/pylib/base/base_test_runner.py b/android/pylib/base/base_test_runner.py index 3431c46d0..cfd0c6c75 100644 --- a/android/pylib/base/base_test_runner.py +++ b/android/pylib/base/base_test_runner.py @@ -51,7 +51,7 @@ class BaseTestRunner(object): def _PushTestServerPortInfoToDevice(self): """Pushes the latest port information to device.""" - self.device.old_interface.SetFileContents( + self.device.WriteFile( self.device.GetExternalStoragePath() + '/' + NET_TEST_SERVER_PORT_INFO_FILE, '%d:%d' % (self.test_server_spawner_port, self.test_server_port)) diff --git a/android/pylib/device/device_utils.py b/android/pylib/device/device_utils.py index c87ca5143..28d258f4d 100644 --- a/android/pylib/device/device_utils.py +++ b/android/pylib/device/device_utils.py @@ -435,6 +435,117 @@ class DeviceUtils(object): """ self.old_interface.SendKeyEvent(keycode) + PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT + PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES + + @decorators.WithTimeoutAndRetriesDefaults( + PUSH_CHANGED_FILES_DEFAULT_TIMEOUT, + PUSH_CHANGED_FILES_DEFAULT_RETRIES) + def PushChangedFiles(self, host_path, device_path, timeout=None, + retries=None): + """Push files to the device, skipping files that don't need updating. + + Args: + host_path: A string containing the absolute path to the file or directory + 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|. + """ + self.old_interface.PushIfNeeded(host_path, device_path) + + @decorators.WithTimeoutAndRetriesFromInstance() + def FileExists(self, device_path, timeout=None, retries=None): + """Checks whether the given file exists on the device. + + 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|. + Returns: + True if the file exists on the device, False otherwise. + """ + return self._FileExistsImpl(device_path) + + def _FileExistsImpl(self, device_path): + """Implementation of FileExists. + + This is split from FileExists to allow other DeviceUtils methods to call + FileExists without spawning a new timeout thread. + + Args: + device_path: Same as for |FileExists|. + Returns: + True if the file exists on the device, False otherwise. + """ + return self.old_interface.FileExistsOnDevice(device_path) + + @decorators.WithTimeoutAndRetriesFromInstance() + def PullFile(self, device_path, host_path, timeout=None, retries=None): + """Pull a file from the device. + + Args: + device_path: A string containing the absolute path of the file to pull + 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|. + """ + self.old_interface.PullFileFromDevice(device_path, host_path) + + @decorators.WithTimeoutAndRetriesFromInstance() + def ReadFile(self, device_path, as_root=False, timeout=None, retries=None): + """Reads the contents of a file from the device. + + Args: + device_path: A string containing the absolute path of the file to read + from the device. + as_root: A boolean indicating whether the read should be executed with + root priveleges. + timeout: Same as for |IsOnline|. + retries: Same as for |IsOnline|. + Returns: + The contents of the file at |device_path| as a list of lines. + Raises: + CommandFailedError if the file can't be read. + """ + # TODO(jbudorick) Evaluate whether we actually want to return a list of + # lines after the implementation switch. + if as_root: + if not self.old_interface.CanAccessProtectedFileContents(): + raise device_errors.CommandFailedError( + 'Cannot read from %s with root priveleges.' % device_path) + return self.old_interface.GetProtectedFileContents(device_path) + else: + return self.old_interface.GetFileContents(device_path) + + @decorators.WithTimeoutAndRetriesFromInstance() + def WriteFile(self, device_path, contents, as_root=False, timeout=None, + retries=None): + """Writes |contents| to a file on the device. + + Args: + device_path: A string containing the absolute path to the file to write + on the device. + contents: A string containing the data to write to the device. + as_root: A boolean indicating whether the write should be executed with + root priveleges. + timeout: Same as for |IsOnline|. + retries: Same as for |IsOnline|. + Raises: + CommandFailedError if the file could not be written on the device. + """ + if as_root: + if not self.old_interface.CanAccessProtectedFileContents(): + raise device_errors.CommandFailedError( + 'Cannot write to %s with root priveleges.' % device_path) + self.old_interface.SetProtectedFileContents(device_path, contents) + else: + self.old_interface.SetFileContents(device_path, contents) + def __str__(self): """Returns the device serial.""" return self.old_interface.GetDevice() diff --git a/android/pylib/device/device_utils_test.py b/android/pylib/device/device_utils_test.py index 4c60514da..d7ac6d7c4 100644 --- a/android/pylib/device/device_utils_test.py +++ b/android/pylib/device/device_utils_test.py @@ -10,7 +10,9 @@ Unit tests for the contents of device_utils.py (mostly DeviceUtils). # pylint: disable=W0212 # pylint: disable=W0613 +import collections import os +import re import signal import sys import unittest @@ -64,8 +66,9 @@ class DeviceUtilsOldImplTest(unittest.TestCase): class AndroidCommandsCalls(object): - def __init__(self, test_case, cmd_ret): + def __init__(self, test_case, cmd_ret, comp): self._cmds = cmd_ret + self._comp = comp self._test_case = test_case self._total_received = 0 @@ -74,18 +77,20 @@ class DeviceUtilsOldImplTest(unittest.TestCase): atr_run_command.RunCommand.side_effect = lambda c, **kw: self._ret(c) def _ret(self, actual_cmd): - on_failure_fmt = ('\n' - ' received command: %s\n' - ' expected command: %s') - self._test_case.assertGreater( - len(self._cmds), self._total_received, - msg=on_failure_fmt % (actual_cmd, None)) - expected_cmd, ret = self._cmds[self._total_received] - self._total_received += 1 - self._test_case.assertEqual( - actual_cmd, expected_cmd, - msg=on_failure_fmt % (actual_cmd, expected_cmd)) - return ret + if sys.exc_info()[0] is None: + on_failure_fmt = ('\n' + ' received command: %s\n' + ' expected command: %s') + self._test_case.assertGreater( + len(self._cmds), self._total_received, + msg=on_failure_fmt % (actual_cmd, None)) + expected_cmd, ret = self._cmds[self._total_received] + self._total_received += 1 + self._test_case.assertTrue( + self._comp(expected_cmd, actual_cmd), + msg=on_failure_fmt % (actual_cmd, expected_cmd)) + return ret + return '' def __exit__(self, exc_type, _exc_val, exc_trace): if exc_type is None: @@ -99,18 +104,21 @@ class DeviceUtilsOldImplTest(unittest.TestCase): for (expected_cmd, _r), (_n, actual_args, actual_kwargs) in zip( self._cmds, atr_run_command.RunCommand.mock_calls): self._test_case.assertEqual(1, len(actual_args), msg=on_failure) - self._test_case.assertEqual(expected_cmd, actual_args[0], - msg=on_failure) + self._test_case.assertTrue(self._comp(expected_cmd, actual_args[0]), + msg=on_failure) self._test_case.assertTrue('timeout_time' in actual_kwargs, msg=on_failure) self._test_case.assertTrue('retry_count' in actual_kwargs, msg=on_failure) - def assertOldImplCalls(self, cmd, ret): - return type(self).AndroidCommandsCalls(self, [(cmd, ret)]) + def assertNoAdbCalls(self): + return type(self).AndroidCommandsCalls(self, [], str.__eq__) - def assertOldImplCallsSequence(self, cmd_ret): - return type(self).AndroidCommandsCalls(self, cmd_ret) + def assertOldImplCalls(self, cmd, ret, comp=str.__eq__): + return type(self).AndroidCommandsCalls(self, [(cmd, ret)], comp) + + def assertOldImplCallsSequence(self, cmd_ret, comp=str.__eq__): + return type(self).AndroidCommandsCalls(self, cmd_ret, comp) def setUp(self): self.device = device_utils.DeviceUtils( @@ -717,6 +725,468 @@ class DeviceUtilsOldImplTest(unittest.TestCase): ''): self.device.SendKeyEvent(66) + def testPushChangedFiles_noHostPath(self): + with mock.patch('os.path.exists', return_value=False): + with self.assertRaises(device_errors.CommandFailedError): + self.device.PushChangedFiles('/test/host/path', '/test/device/path') + + def testPushChangedFiles_file_noChange(self): + self.device.old_interface._push_if_needed_cache = {} + + host_file_path = '/test/host/path' + device_file_path = '/test/device/path' + + mock_file_info = { + '/test/host/path': { + 'os.path.exists': True, + 'os.path.isdir': False, + 'os.path.getsize': 100, + }, + } + + os_path_exists = mock.Mock() + os_path_exists.side_effect = lambda f: mock_file_info[f]['os.path.exists'] + + os_path_isdir = mock.Mock() + os_path_isdir.side_effect = lambda f: mock_file_info[f]['os.path.isdir'] + + os_path_getsize = mock.Mock() + os_path_getsize.side_effect = lambda f: mock_file_info[f]['os.path.getsize'] + + self.device.old_interface.GetFilesChanged = mock.Mock(return_value=[]) + + with mock.patch('os.path.exists', new=os_path_exists), ( + mock.patch('os.path.isdir', new=os_path_isdir)), ( + mock.patch('os.path.getsize', new=os_path_getsize)): + # GetFilesChanged is mocked, so its adb calls are omitted. + with self.assertNoAdbCalls(): + self.device.PushChangedFiles(host_file_path, device_file_path) + + @staticmethod + def createMockOSStatResult( + 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 testPushChangedFiles_file_changed(self): + self.device.old_interface._push_if_needed_cache = {} + + host_file_path = '/test/host/path' + device_file_path = '/test/device/path' + + mock_file_info = { + '/test/host/path': { + 'os.path.exists': True, + 'os.path.isdir': False, + 'os.path.getsize': 100, + 'os.stat': self.createMockOSStatResult(st_mtime=1000000000) + }, + } + + os_path_exists = mock.Mock() + os_path_exists.side_effect = lambda f: mock_file_info[f]['os.path.exists'] + + os_path_isdir = mock.Mock() + os_path_isdir.side_effect = lambda f: mock_file_info[f]['os.path.isdir'] + + os_path_getsize = mock.Mock() + os_path_getsize.side_effect = lambda f: mock_file_info[f]['os.path.getsize'] + + os_stat = mock.Mock() + os_stat.side_effect = lambda f: mock_file_info[f]['os.stat'] + + self.device.old_interface.GetFilesChanged = mock.Mock( + return_value=[('/test/host/path', '/test/device/path')]) + + with mock.patch('os.path.exists', new=os_path_exists), ( + mock.patch('os.path.isdir', new=os_path_isdir)), ( + mock.patch('os.path.getsize', new=os_path_getsize)), ( + mock.patch('os.stat', new=os_stat)): + with self.assertOldImplCalls('adb -s 0123456789abcdef push ' + '/test/host/path /test/device/path', '100 B/s (100 B in 1.000s)\r\n'): + self.device.PushChangedFiles(host_file_path, device_file_path) + + def testPushChangedFiles_directory_nothingChanged(self): + self.device.old_interface._push_if_needed_cache = {} + + host_file_path = '/test/host/path' + device_file_path = '/test/device/path' + + mock_file_info = { + '/test/host/path': { + 'os.path.exists': True, + 'os.path.isdir': True, + 'os.path.getsize': 256, + 'os.stat': self.createMockOSStatResult(st_mtime=1000000000) + }, + '/test/host/path/file1': { + 'os.path.exists': True, + 'os.path.isdir': False, + 'os.path.getsize': 251, + 'os.stat': self.createMockOSStatResult(st_mtime=1000000001) + }, + '/test/host/path/file2': { + 'os.path.exists': True, + 'os.path.isdir': False, + 'os.path.getsize': 252, + 'os.stat': self.createMockOSStatResult(st_mtime=1000000002) + }, + } + + os_path_exists = mock.Mock() + os_path_exists.side_effect = lambda f: mock_file_info[f]['os.path.exists'] + + os_path_isdir = mock.Mock() + os_path_isdir.side_effect = lambda f: mock_file_info[f]['os.path.isdir'] + + os_path_getsize = mock.Mock() + os_path_getsize.side_effect = lambda f: mock_file_info[f]['os.path.getsize'] + + os_stat = mock.Mock() + os_stat.side_effect = lambda f: mock_file_info[f]['os.stat'] + + self.device.old_interface.GetFilesChanged = mock.Mock(return_value=[]) + + with mock.patch('os.path.exists', new=os_path_exists), ( + mock.patch('os.path.isdir', new=os_path_isdir)), ( + mock.patch('os.path.getsize', new=os_path_getsize)), ( + mock.patch('os.stat', new=os_stat)): + with self.assertOldImplCallsSequence([ + ("adb -s 0123456789abcdef shell 'mkdir -p \"/test/device/path\"'", + '')]): + self.device.PushChangedFiles(host_file_path, device_file_path) + + def testPushChangedFiles_directory_somethingChanged(self): + self.device.old_interface._push_if_needed_cache = {} + + host_file_path = '/test/host/path' + device_file_path = '/test/device/path' + + mock_file_info = { + '/test/host/path': { + 'os.path.exists': True, + 'os.path.isdir': True, + 'os.path.getsize': 256, + 'os.stat': self.createMockOSStatResult(st_mtime=1000000000), + 'os.walk': [('/test/host/path', [], ['file1', 'file2'])] + }, + '/test/host/path/file1': { + 'os.path.exists': True, + 'os.path.isdir': False, + 'os.path.getsize': 256, + 'os.stat': self.createMockOSStatResult(st_mtime=1000000001) + }, + '/test/host/path/file2': { + 'os.path.exists': True, + 'os.path.isdir': False, + 'os.path.getsize': 256, + 'os.stat': self.createMockOSStatResult(st_mtime=1000000002) + }, + } + + os_path_exists = mock.Mock() + os_path_exists.side_effect = lambda f: mock_file_info[f]['os.path.exists'] + + os_path_isdir = mock.Mock() + os_path_isdir.side_effect = lambda f: mock_file_info[f]['os.path.isdir'] + + os_path_getsize = mock.Mock() + os_path_getsize.side_effect = lambda f: mock_file_info[f]['os.path.getsize'] + + os_stat = mock.Mock() + os_stat.side_effect = lambda f: mock_file_info[f]['os.stat'] + + os_walk = mock.Mock() + os_walk.side_effect = lambda f: mock_file_info[f]['os.walk'] + + self.device.old_interface.GetFilesChanged = mock.Mock( + return_value=[('/test/host/path/file1', '/test/device/path/file1')]) + + with mock.patch('os.path.exists', new=os_path_exists), ( + mock.patch('os.path.isdir', new=os_path_isdir)), ( + mock.patch('os.path.getsize', new=os_path_getsize)), ( + mock.patch('os.stat', new=os_stat)), ( + mock.patch('os.walk', new=os_walk)): + with self.assertOldImplCallsSequence([ + ("adb -s 0123456789abcdef shell 'mkdir -p \"/test/device/path\"'", + ''), + ('adb -s 0123456789abcdef push ' + '/test/host/path/file1 /test/device/path/file1', + '256 B/s (256 B in 1.000s)\r\n')]): + self.device.PushChangedFiles(host_file_path, device_file_path) + + def testPushChangedFiles_directory_everythingChanged(self): + self.device.old_interface._push_if_needed_cache = {} + + host_file_path = '/test/host/path' + device_file_path = '/test/device/path' + + mock_file_info = { + '/test/host/path': { + 'os.path.exists': True, + 'os.path.isdir': True, + 'os.path.getsize': 256, + 'os.stat': self.createMockOSStatResult(st_mtime=1000000000) + }, + '/test/host/path/file1': { + 'os.path.exists': True, + 'os.path.isdir': False, + 'os.path.getsize': 256, + 'os.stat': self.createMockOSStatResult(st_mtime=1000000001) + }, + '/test/host/path/file2': { + 'os.path.exists': True, + 'os.path.isdir': False, + 'os.path.getsize': 256, + 'os.stat': self.createMockOSStatResult(st_mtime=1000000002) + }, + } + + os_path_exists = mock.Mock() + os_path_exists.side_effect = lambda f: mock_file_info[f]['os.path.exists'] + + os_path_isdir = mock.Mock() + os_path_isdir.side_effect = lambda f: mock_file_info[f]['os.path.isdir'] + + os_path_getsize = mock.Mock() + os_path_getsize.side_effect = lambda f: mock_file_info[f]['os.path.getsize'] + + os_stat = mock.Mock() + os_stat.side_effect = lambda f: mock_file_info[f]['os.stat'] + + self.device.old_interface.GetFilesChanged = mock.Mock( + return_value=[('/test/host/path/file1', '/test/device/path/file1'), + ('/test/host/path/file2', '/test/device/path/file2')]) + + with mock.patch('os.path.exists', new=os_path_exists), ( + mock.patch('os.path.isdir', new=os_path_isdir)), ( + mock.patch('os.path.getsize', new=os_path_getsize)), ( + mock.patch('os.stat', new=os_stat)): + with self.assertOldImplCallsSequence([ + ("adb -s 0123456789abcdef shell 'mkdir -p \"/test/device/path\"'", + ''), + ('adb -s 0123456789abcdef push /test/host/path /test/device/path', + '768 B/s (768 B in 1.000s)\r\n')]): + self.device.PushChangedFiles(host_file_path, device_file_path) + + def testFileExists_usingTest_fileExists(self): + with self.assertOldImplCalls( + "adb -s 0123456789abcdef shell " + "'test -e \"/data/app/test.file.exists\"; echo $?'", + '0\r\n'): + self.assertTrue(self.device.FileExists('/data/app/test.file.exists')) + + def testFileExists_usingTest_fileDoesntExist(self): + with self.assertOldImplCalls( + "adb -s 0123456789abcdef shell " + "'test -e \"/data/app/test.file.does.not.exist\"; echo $?'", + '1\r\n'): + self.assertFalse(self.device.FileExists( + '/data/app/test.file.does.not.exist')) + + def testFileExists_usingLs_fileExists(self): + with self.assertOldImplCallsSequence([ + ("adb -s 0123456789abcdef shell " + "'test -e \"/data/app/test.file.exists\"; echo $?'", + 'test: not found\r\n'), + ("adb -s 0123456789abcdef shell " + "'ls \"/data/app/test.file.exists\" >/dev/null 2>&1; echo $?'", + '0\r\n')]): + self.assertTrue(self.device.FileExists('/data/app/test.file.exists')) + + def testFileExists_usingLs_fileDoesntExist(self): + with self.assertOldImplCallsSequence([ + ("adb -s 0123456789abcdef shell " + "'test -e \"/data/app/test.file.does.not.exist\"; echo $?'", + 'test: not found\r\n'), + ("adb -s 0123456789abcdef shell " + "'ls \"/data/app/test.file.does.not.exist\" " + ">/dev/null 2>&1; echo $?'", + '1\r\n')]): + self.assertFalse(self.device.FileExists( + '/data/app/test.file.does.not.exist')) + + def testPullFile_existsOnDevice(self): + with mock.patch('os.path.exists', return_value=True): + with self.assertOldImplCallsSequence([ + ('adb -s 0123456789abcdef shell ' + 'ls /data/app/test.file.exists', + '/data/app/test.file.exists'), + ('adb -s 0123456789abcdef pull ' + '/data/app/test.file.exists /test/file/host/path', + '100 B/s (100 bytes in 1.000s)\r\n')]): + self.device.PullFile('/data/app/test.file.exists', + '/test/file/host/path') + + def testPullFile_doesntExistOnDevice(self): + with mock.patch('os.path.exists', return_value=True): + with self.assertOldImplCalls( + 'adb -s 0123456789abcdef shell ' + 'ls /data/app/test.file.does.not.exist', + '/data/app/test.file.does.not.exist: No such file or directory\r\n'): + with self.assertRaises(device_errors.CommandFailedError): + self.device.PullFile('/data/app/test.file.does.not.exist', + '/test/file/host/path') + + def testReadFile_exists(self): + with self.assertOldImplCallsSequence([ + ("adb -s 0123456789abcdef shell " + "'cat \"/read/this/test/file\" 2>/dev/null'", + 'this is a test file')]): + self.assertEqual(['this is a test file'], + self.device.ReadFile('/read/this/test/file')) + + def testReadFile_doesNotExist(self): + with self.assertOldImplCalls( + "adb -s 0123456789abcdef shell " + "'cat \"/this/file/does.not.exist\" 2>/dev/null'", + ''): + self.device.ReadFile('/this/file/does.not.exist') + + def testReadFile_asRoot_withRoot(self): + self.device.old_interface._privileged_command_runner = ( + self.device.old_interface.RunShellCommand) + self.device.old_interface._protected_file_access_method_initialized = True + with self.assertOldImplCallsSequence([ + ("adb -s 0123456789abcdef shell " + "'cat \"/this/file/must.be.read.by.root\" 2> /dev/null'", + 'this is a test file\nread by root')]): + self.assertEqual( + ['this is a test file', 'read by root'], + self.device.ReadFile('/this/file/must.be.read.by.root', + as_root=True)) + + def testReadFile_asRoot_withSu(self): + self.device.old_interface._privileged_command_runner = ( + self.device.old_interface.RunShellCommandWithSU) + self.device.old_interface._protected_file_access_method_initialized = True + with self.assertOldImplCallsSequence([ + ("adb -s 0123456789abcdef shell " + "'su -c cat \"/this/file/can.be.read.with.su\" 2> /dev/null'", + 'this is a test file\nread with su')]): + self.assertEqual( + ['this is a test file', 'read with su'], + self.device.ReadFile('/this/file/can.be.read.with.su', + as_root=True)) + + def testReadFile_asRoot_rejected(self): + self.device.old_interface._privileged_command_runner = None + self.device.old_interface._protected_file_access_method_initialized = True + with self.assertRaises(device_errors.CommandFailedError): + self.device.ReadFile('/this/file/cannot.be.read.by.user', + as_root=True) + + def testWriteFile_basic(self): + mock_file = mock.MagicMock(spec=file) + mock_file.name = '/tmp/file/to.be.pushed' + mock_file.__enter__.return_value = mock_file + with mock.patch('tempfile.NamedTemporaryFile', + return_value=mock_file): + with self.assertOldImplCalls( + 'adb -s 0123456789abcdef push ' + '/tmp/file/to.be.pushed /test/file/written.to.device', + '100 B/s (100 bytes in 1.000s)\r\n'): + self.device.WriteFile('/test/file/written.to.device', + 'new test file contents') + mock_file.write.assert_called_once_with('new test file contents') + + def testWriteFile_asRoot_withRoot(self): + self.device.old_interface._external_storage = '/fake/storage/path' + self.device.old_interface._privileged_command_runner = ( + self.device.old_interface.RunShellCommand) + self.device.old_interface._protected_file_access_method_initialized = True + + mock_file = mock.MagicMock(spec=file) + mock_file.name = '/tmp/file/to.be.pushed' + mock_file.__enter__.return_value = mock_file + with mock.patch('tempfile.NamedTemporaryFile', + return_value=mock_file): + with self.assertOldImplCallsSequence( + cmd_ret=[ + # Create temporary contents file + (r"adb -s 0123456789abcdef shell " + "'test -e \"/fake/storage/path/temp_file-\d+-\d+\"; " + "echo \$\?'", + '1\r\n'), + # Create temporary script file + (r"adb -s 0123456789abcdef shell " + "'test -e \"/fake/storage/path/temp_file-\d+-\d+\.sh\"; " + "echo \$\?'", + '1\r\n'), + # Set contents file + (r'adb -s 0123456789abcdef push /tmp/file/to\.be\.pushed ' + '/fake/storage/path/temp_file-\d+\d+', + '100 B/s (100 bytes in 1.000s)\r\n'), + # Set script file + (r'adb -s 0123456789abcdef push /tmp/file/to\.be\.pushed ' + '/fake/storage/path/temp_file-\d+\d+', + '100 B/s (100 bytes in 1.000s)\r\n'), + # Call script + (r"adb -s 0123456789abcdef shell " + "'sh /fake/storage/path/temp_file-\d+-\d+\.sh'", ''), + # Remove device temporaries + (r"adb -s 0123456789abcdef shell " + "'rm /fake/storage/path/temp_file-\d+-\d+\.sh'", ''), + (r"adb -s 0123456789abcdef shell " + "'rm /fake/storage/path/temp_file-\d+-\d+'", '')], + comp=re.match): + self.device.WriteFile('/test/file/written.to.device', + 'new test file contents', as_root=True) + + def testWriteFile_asRoot_withSu(self): + self.device.old_interface._external_storage = '/fake/storage/path' + self.device.old_interface._privileged_command_runner = ( + self.device.old_interface.RunShellCommandWithSU) + self.device.old_interface._protected_file_access_method_initialized = True + + mock_file = mock.MagicMock(spec=file) + mock_file.name = '/tmp/file/to.be.pushed' + mock_file.__enter__.return_value = mock_file + with mock.patch('tempfile.NamedTemporaryFile', + return_value=mock_file): + with self.assertOldImplCallsSequence( + cmd_ret=[ + # Create temporary contents file + (r"adb -s 0123456789abcdef shell " + "'test -e \"/fake/storage/path/temp_file-\d+-\d+\"; " + "echo \$\?'", + '1\r\n'), + # Create temporary script file + (r"adb -s 0123456789abcdef shell " + "'test -e \"/fake/storage/path/temp_file-\d+-\d+\.sh\"; " + "echo \$\?'", + '1\r\n'), + # Set contents file + (r'adb -s 0123456789abcdef push /tmp/file/to\.be\.pushed ' + '/fake/storage/path/temp_file-\d+\d+', + '100 B/s (100 bytes in 1.000s)\r\n'), + # Set script file + (r'adb -s 0123456789abcdef push /tmp/file/to\.be\.pushed ' + '/fake/storage/path/temp_file-\d+\d+', + '100 B/s (100 bytes in 1.000s)\r\n'), + # Call script + (r"adb -s 0123456789abcdef shell " + "'su -c sh /fake/storage/path/temp_file-\d+-\d+\.sh'", ''), + # Remove device temporaries + (r"adb -s 0123456789abcdef shell " + "'rm /fake/storage/path/temp_file-\d+-\d+\.sh'", ''), + (r"adb -s 0123456789abcdef shell " + "'rm /fake/storage/path/temp_file-\d+-\d+'", '')], + comp=re.match): + self.device.WriteFile('/test/file/written.to.device', + 'new test file contents', as_root=True) + + def testWriteFile_asRoot_rejected(self): + self.device.old_interface._privileged_command_runner = None + self.device.old_interface._protected_file_access_method_initialized = True + with self.assertRaises(device_errors.CommandFailedError): + self.device.WriteFile('/test/file/no.permissions.to.write', + 'new test file contents', as_root=True) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/android/pylib/flag_changer.py b/android/pylib/flag_changer.py index c1ce16a5e..c0bcadbe3 100644 --- a/android/pylib/flag_changer.py +++ b/android/pylib/flag_changer.py @@ -32,8 +32,7 @@ class FlagChanger(object): self._cmdline_file = cmdline_file # Save the original flags. - self._orig_line = self._device.old_interface.GetFileContents( - self._cmdline_file) + self._orig_line = self._device.ReadFile(self._cmdline_file) if self._orig_line: self._orig_line = self._orig_line[0].strip() @@ -102,23 +101,16 @@ class FlagChanger(object): # The first command line argument doesn't matter as we are not actually # launching the chrome executable using this command line. cmd_line = ' '.join(['_'] + self._current_flags) - if use_root: - self._device.old_interface.SetProtectedFileContents( - self._cmdline_file, cmd_line) - file_contents = self._device.old_interface.GetProtectedFileContents( - self._cmdline_file) - else: - self._device.old_interface.SetFileContents(self._cmdline_file, cmd_line) - file_contents = self._device.old_interface.GetFileContents( - self._cmdline_file) + self._device.WriteFile( + self._cmdline_file, cmd_line, as_root=use_root) + file_contents = self._device.ReadFile( + self._cmdline_file, as_root=use_root) assert len(file_contents) == 1 and file_contents[0] == cmd_line, ( 'Failed to set the command line file at %s' % self._cmdline_file) else: self._device.RunShellCommand('rm ' + self._cmdline_file, as_root=use_root) - assert ( - not self._device.old_interface.FileExistsOnDevice( - self._cmdline_file)), ( + assert not self._device.FileExists(self._cmdline_file), ( 'Failed to remove the command line file at %s' % self._cmdline_file) @staticmethod diff --git a/android/pylib/forwarder.py b/android/pylib/forwarder.py index f0ddb2e5a..ad22838d8 100644 --- a/android/pylib/forwarder.py +++ b/android/pylib/forwarder.py @@ -290,7 +290,7 @@ class Forwarder(object): if device_serial in self._initialized_devices: return Forwarder._KillDeviceLocked(device, tool) - device.old_interface.PushIfNeeded( + device.PushChangedFiles( self._device_forwarder_path_on_host, Forwarder._DEVICE_FORWARDER_FOLDER) cmd = '%s %s' % (tool.GetUtilWrapper(), Forwarder._DEVICE_FORWARDER_PATH) @@ -328,8 +328,7 @@ class Forwarder(object): forwarder (see valgrind_tools.py). """ logging.info('Killing device_forwarder.') - if not device.old_interface.FileExistsOnDevice( - Forwarder._DEVICE_FORWARDER_PATH): + if not device.FileExists(Forwarder._DEVICE_FORWARDER_PATH): return cmd = '%s %s --kill-server' % (tool.GetUtilWrapper(), diff --git a/android/pylib/gtest/test_package_apk.py b/android/pylib/gtest/test_package_apk.py index 6374f637f..429cd2b07 100644 --- a/android/pylib/gtest/test_package_apk.py +++ b/android/pylib/gtest/test_package_apk.py @@ -44,7 +44,7 @@ class TestPackageApk(TestPackage): # GTest expects argv[0] to be the executable path. command_line_file.write(self.suite_name + ' ' + options) command_line_file.flush() - device.old_interface.PushIfNeeded( + device.PushChangedFiles( command_line_file.name, self._package_info.cmdline_file) @@ -60,7 +60,7 @@ class TestPackageApk(TestPackage): def _WatchFifo(self, device, timeout, logfile=None): for i in range(10): - if device.old_interface.FileExistsOnDevice(self._GetFifo()): + if device.FileExists(self._GetFifo()): logging.info('Fifo created.') break time.sleep(i) diff --git a/android/pylib/gtest/test_package_exe.py b/android/pylib/gtest/test_package_exe.py index a7f603073..9714c4208 100644 --- a/android/pylib/gtest/test_package_exe.py +++ b/android/pylib/gtest/test_package_exe.py @@ -37,7 +37,7 @@ class TestPackageExecutable(TestPackage): ret_code = 1 # Assume failure if we can't find it ret_code_file = tempfile.NamedTemporaryFile() try: - if not device.old_interface.Adb().Pull( + if not device.PullFile( constants.TEST_EXECUTABLE_DIR + '/' + TestPackageExecutable._TEST_RUNNER_RET_VAL_FILE, ret_code_file.name): @@ -105,7 +105,7 @@ class TestPackageExecutable(TestPackage): TestPackageExecutable._TEST_RUNNER_RET_VAL_FILE)) sh_script_file.flush() cmd_helper.RunCmd(['chmod', '+x', sh_script_file.name]) - device.old_interface.PushIfNeeded( + device.PushChangedFiles( sh_script_file.name, constants.TEST_EXECUTABLE_DIR + '/chrome_test_runner.sh') logging.info('Conents of the test runner script: ') @@ -148,4 +148,4 @@ class TestPackageExecutable(TestPackage): self.suite_name + '_stripped')) test_binary = constants.TEST_EXECUTABLE_DIR + '/' + self.suite_name - device.old_interface.PushIfNeeded(target_name, test_binary) + device.PushChangedFiles(target_name, test_binary) diff --git a/android/pylib/gtest/test_runner.py b/android/pylib/gtest/test_runner.py index cea799249..c9a60449c 100644 --- a/android/pylib/gtest/test_runner.py +++ b/android/pylib/gtest/test_runner.py @@ -71,7 +71,7 @@ class TestRunner(base_test_runner.BaseTestRunner): else: device_dir = self.device.GetExternalStoragePath() for p in os.listdir(constants.ISOLATE_DEPS_DIR): - self.device.old_interface.PushIfNeeded( + self.device.PushChangedFiles( os.path.join(constants.ISOLATE_DEPS_DIR, p), os.path.join(device_dir, p)) diff --git a/android/pylib/instrumentation/test_runner.py b/android/pylib/instrumentation/test_runner.py index 050985f36..7359c1a5f 100644 --- a/android/pylib/instrumentation/test_runner.py +++ b/android/pylib/instrumentation/test_runner.py @@ -106,7 +106,7 @@ class TestRunner(base_test_runner.BaseTestRunner): # Make sure SD card is ready. self.device.WaitUntilFullyBooted(timeout=20) for p in test_data: - self.device.old_interface.PushIfNeeded( + self.device.PushChangedFiles( os.path.join(constants.DIR_SOURCE_ROOT, p), os.path.join(self.device.GetExternalStoragePath(), p)) @@ -118,7 +118,7 @@ class TestRunner(base_test_runner.BaseTestRunner): host_src = dst_src[1] host_test_files_path = '%s/%s' % (constants.DIR_SOURCE_ROOT, host_src) if os.path.exists(host_test_files_path): - self.device.old_interface.PushIfNeeded( + self.device.PushChangedFiles( host_test_files_path, '%s/%s/%s' % ( self.device.GetExternalStoragePath(), @@ -235,7 +235,7 @@ class TestRunner(base_test_runner.BaseTestRunner): self.TearDownPerfMonitoring(test) if self.coverage_dir: - self.device.old_interface.Adb().Pull( + self.device.PullFile( self.coverage_device_file, self.coverage_host_file) self.device.RunShellCommand( 'rm -f %s' % self.coverage_device_file) @@ -266,8 +266,9 @@ class TestRunner(base_test_runner.BaseTestRunner): # Obtain the relevant perf data. The data is dumped to a # JSON formatted file. - json_string = self.device.old_interface.GetProtectedFileContents( - '/data/data/com.google.android.apps.chrome/files/PerfTestData.txt') + json_string = self.device.ReadFile( + '/data/data/com.google.android.apps.chrome/files/PerfTestData.txt', + as_root=True) if json_string: json_string = '\n'.join(json_string) diff --git a/android/pylib/perf/cache_control.py b/android/pylib/perf/cache_control.py index a065dcbb7..8065cf9ad 100644 --- a/android/pylib/perf/cache_control.py +++ b/android/pylib/perf/cache_control.py @@ -17,6 +17,5 @@ class CacheControl(object): def DropRamCaches(self): """Drops the filesystem ram caches for performance testing.""" self._device.RunShellCommand('sync', as_root=True) - self._device.old_interface.SetProtectedFileContents( - CacheControl._DROP_CACHES, '3') + self._device.WriteFile(CacheControl._DROP_CACHES, '3', as_root=True) diff --git a/android/pylib/perf/perf_control.py b/android/pylib/perf/perf_control.py index 0b32dd808..03c20bdc6 100644 --- a/android/pylib/perf/perf_control.py +++ b/android/pylib/perf/perf_control.py @@ -25,8 +25,7 @@ class PerfControl(object): self._num_cpu_cores = len(cpu_files) assert self._num_cpu_cores > 0, 'Failed to detect CPUs.' logging.info('Number of CPUs: %d', self._num_cpu_cores) - self._have_mpdecision = self._device.old_interface.FileExistsOnDevice( - '/system/bin/mpdecision') + self._have_mpdecision = self._device.FileExists('/system/bin/mpdecision') def SetHighPerfMode(self): # TODO(epenner): Enable on all devices (http://crbug.com/383566) @@ -61,16 +60,15 @@ class PerfControl(object): def _SetScalingGovernorInternal(self, value): for cpu in range(self._num_cpu_cores): scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu - if self._device.old_interface.FileExistsOnDevice(scaling_governor_file): + if self._device.FileExists(scaling_governor_file): logging.info('Writing scaling governor mode \'%s\' -> %s', value, scaling_governor_file) - self._device.old_interface.SetProtectedFileContents( - scaling_governor_file, value) + self._device.WriteFile(scaling_governor_file, value, as_root=True) def _AllCpusAreOnline(self): for cpu in range(self._num_cpu_cores): online_path = PerfControl._CPU_ONLINE_FMT % cpu - if self._device.old_interface.GetFileContents(online_path)[0] == '0': + if self._device.ReadFile(online_path)[0] == '0': return False return True @@ -100,5 +98,4 @@ class PerfControl(object): for cpu in range(self._num_cpu_cores): online_path = PerfControl._CPU_ONLINE_FMT % cpu - self._device.old_interface.SetProtectedFileContents( - online_path, '1') + self._device.WriteFile(online_path, '1', as_root=True) diff --git a/android/pylib/perf/perf_control_unittest.py b/android/pylib/perf/perf_control_unittest.py index 9333dbce8..aa31f68d0 100644 --- a/android/pylib/perf/perf_control_unittest.py +++ b/android/pylib/perf/perf_control_unittest.py @@ -30,10 +30,10 @@ class TestPerfControl(unittest.TestCase): for cpu in range(perf._num_cpu_cores): path = perf_control.PerfControl._CPU_ONLINE_FMT % cpu self.assertEquals('1', - self._device.old_interface.GetFileContents(path)[0]) + self._device.ReadFile(path)[0]) path = perf_control.PerfControl._SCALING_GOVERNOR_FMT % cpu self.assertEquals('performance', - self._device.old_interface.GetFileContents(path)[0]) + self._device.ReadFile(path)[0]) finally: perf.SetDefaultPerfMode() diff --git a/android/pylib/perf/thermal_throttle.py b/android/pylib/perf/thermal_throttle.py index 87ae9665a..a125e72fb 100644 --- a/android/pylib/perf/thermal_throttle.py +++ b/android/pylib/perf/thermal_throttle.py @@ -14,8 +14,7 @@ class OmapThrottlingDetector(object): @staticmethod def IsSupported(device): - return device.old_interface.FileExistsOnDevice( - OmapThrottlingDetector.OMAP_TEMP_FILE) + return device.FileExists(OmapThrottlingDetector.OMAP_TEMP_FILE) def __init__(self, device): self._device = device @@ -34,8 +33,7 @@ class OmapThrottlingDetector(object): return float([s for s in log_line.split() if s.isdigit()][0]) / 1000.0 def GetCurrentTemperature(self): - tempdata = self._device.old_interface.GetFileContents( - OmapThrottlingDetector.OMAP_TEMP_FILE) + tempdata = self._device.ReadFile(OmapThrottlingDetector.OMAP_TEMP_FILE) return float(tempdata[0]) / 1000.0 @@ -43,7 +41,7 @@ class ExynosThrottlingDetector(object): """Class to detect and track thermal throttling on an Exynos 5.""" @staticmethod def IsSupported(device): - return device.old_interface.FileExistsOnDevice('/sys/bus/exynos5-core') + return device.FileExists('/sys/bus/exynos5-core') def __init__(self, device): pass diff --git a/android/pylib/screenshot.py b/android/pylib/screenshot.py index a09bc3d0a..d2e7c956c 100644 --- a/android/pylib/screenshot.py +++ b/android/pylib/screenshot.py @@ -86,7 +86,6 @@ class VideoRecorder(object): Returns: Output video file name on the host. """ - self._device.old_interface.PullFileFromDevice( - self._device_file, self._host_file) + self._device.PullFile(self._device_file, self._host_file) self._device.RunShellCommand('rm -f "%s"' % self._device_file) return self._host_file diff --git a/android/pylib/uiautomator/test_package.py b/android/pylib/uiautomator/test_package.py index ecba284aa..d8558c184 100644 --- a/android/pylib/uiautomator/test_package.py +++ b/android/pylib/uiautomator/test_package.py @@ -24,5 +24,4 @@ class TestPackage(test_jar.TestJar): # Override. def Install(self, device): - device.old_interface.PushIfNeeded(self._jar_path, - constants.TEST_EXECUTABLE_DIR) + device.PushChangedFiles(self._jar_path, constants.TEST_EXECUTABLE_DIR) diff --git a/android/pylib/utils/reraiser_thread.py b/android/pylib/utils/reraiser_thread.py index 64196b26e..2964bef1e 100644 --- a/android/pylib/utils/reraiser_thread.py +++ b/android/pylib/utils/reraiser_thread.py @@ -74,9 +74,8 @@ class ReraiserThread(threading.Thread): """Overrides Thread.run() to add support for reraising exceptions.""" try: self._ret = self._func(*self._args, **self._kwargs) - except: + except: # pylint: disable=W0702 self._exc_info = sys.exc_info() - raise class ReraiserThreadGroup(object): diff --git a/android/pylib/valgrind_tools.py b/android/pylib/valgrind_tools.py index 8f845fbf1..f11ab03c6 100644 --- a/android/pylib/valgrind_tools.py +++ b/android/pylib/valgrind_tools.py @@ -39,7 +39,7 @@ def SetChromeTimeoutScale(device, scale): # Delete if scale is None/0.0/1.0 since the default timeout scale is 1.0 device.RunShellCommand('rm %s' % path) else: - device.old_interface.SetProtectedFileContents(path, '%f' % scale) + device.WriteFile(path, '%f' % scale, as_root=True) class BaseTool(object): @@ -173,7 +173,7 @@ class ValgrindTool(BaseTool): ValgrindTool.VGLOGS_DIR)) files = self.GetFilesForTool() for f in files: - self._device.old_interface.PushIfNeeded( + self._device.PushChangedFiles( os.path.join(DIR_SOURCE_ROOT, f), os.path.join(ValgrindTool.VG_DIR, os.path.basename(f))) diff --git a/android/tombstones.py b/android/tombstones.py index 1112a97c8..e7549ff73 100755 --- a/android/tombstones.py +++ b/android/tombstones.py @@ -62,8 +62,7 @@ def _GetTombstoneData(device, tombstone_file): Returns: A list of lines """ - return device.old_interface.GetProtectedFileContents( - '/data/tombstones/' + tombstone_file) + return device.ReadFile('/data/tombstones/' + tombstone_file, as_root=True) def _EraseTombstone(device, tombstone_file):