def Run(self, test, device, flags=None, **kwargs): extras = dict(self._extras) with device_temp_file.DeviceTempFile(device.adb) as command_line_file: device.WriteFile(command_line_file.name, '_ %s' % flags if flags else '_') extras[_EXTRA_COMMAND_LINE_FILE] = command_line_file.name with device_temp_file.DeviceTempFile(device.adb) as test_list_file: if test: device.WriteFile(test_list_file.name, '\n'.join(test)) extras[_EXTRA_TEST_LIST] = test_list_file.name return device.StartInstrumentation( self._component, extras=extras, raw=False, **kwargs)
def CalculateDeviceMd5Sums(paths, device): """Calculates the MD5 sum value for all items in |paths|. Args: paths: A list of device paths to md5sum. Returns: A list of named tuples with 'hash' and 'path' attributes. """ if isinstance(paths, basestring): paths = [paths] if not device.FileExists(MD5SUM_DEVICE_BIN_PATH): device.adb.Push( os.path.join(constants.GetOutDirectory(), 'md5sum_dist'), MD5SUM_DEVICE_LIB_PATH) out = [] with tempfile.NamedTemporaryFile() as md5sum_script_file: with device_temp_file.DeviceTempFile(device) as md5sum_device_script_file: md5sum_script = ( MD5SUM_DEVICE_SCRIPT_FORMAT.format( path=p, md5sum_lib=MD5SUM_DEVICE_LIB_PATH, md5sum_bin=MD5SUM_DEVICE_BIN_PATH) for p in paths) md5sum_script_file.write('; '.join(md5sum_script)) md5sum_script_file.flush() device.adb.Push(md5sum_script_file.name, md5sum_device_script_file.name) out = device.RunShellCommand(['sh', md5sum_device_script_file.name]) return [HashAndPath(*l.split(None, 1)) for l in out]
def RunWithFlags(self, device, flags, **kwargs): cmd = [ self._test_run.GetTool(device).GetTestWrapper(), self._exe_device_path, flags, ] cwd = constants.TEST_EXECUTABLE_DIR env = { 'LD_LIBRARY_PATH': '%s/%s_deps' % (constants.TEST_EXECUTABLE_DIR, self._exe_file_name), } try: gcov_strip_depth = os.environ['NATIVE_COVERAGE_DEPTH_STRIP'] external = device.GetExternalStoragePath() env['GCOV_PREFIX'] = '%s/gcov' % external env['GCOV_PREFIX_STRIP'] = gcov_strip_depth except (device_errors.CommandFailedError, KeyError): pass # TODO(jbudorick): Switch to just RunShellCommand once perezju@'s CL # for long shell commands lands. with device_temp_file.DeviceTempFile(device.adb) as script_file: script_contents = ' '.join(cmd) logging.info('script contents: %r' % script_contents) device.WriteFile(script_file.name, script_contents) output = device.RunShellCommand(['sh', script_file.name], cwd=cwd, env=env, **kwargs) return output
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 in the current directory 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. """ if not host_path: host_path = os.path.abspath('screenshot-%s.png' % _GetTimeStamp()) with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp: self.RunShellCommand( ['/system/bin/screencap', '-p', device_tmp.name], check_return=True) self.PullFile(device_tmp.name, host_path) return host_path
def CalculateDeviceMd5Sums(paths, device): """Calculates the MD5 sum value for all items in |paths|. Args: paths: A list of device paths to md5sum. Returns: A dict mapping paths to their respective md5sum checksums. """ if isinstance(paths, basestring): paths = [paths] if not device.FileExists(MD5SUM_DEVICE_BIN_PATH): md5sum_dist_path = os.path.join(constants.GetOutDirectory(), 'md5sum_dist') if not os.path.exists(md5sum_dist_path): raise IOError('File not built: %s' % md5sum_dist_path) device.adb.Push(md5sum_dist_path, MD5SUM_DEVICE_LIB_PATH) out = [] with tempfile.NamedTemporaryFile() as md5sum_script_file: with device_temp_file.DeviceTempFile( device.adb) as md5sum_device_script_file: md5sum_script = (MD5SUM_DEVICE_SCRIPT_FORMAT.format( path=p, md5sum_lib=MD5SUM_DEVICE_LIB_PATH, md5sum_bin=MD5SUM_DEVICE_BIN_PATH) for p in paths) md5sum_script_file.write('; '.join(md5sum_script)) md5sum_script_file.flush() device.adb.Push(md5sum_script_file.name, md5sum_device_script_file.name) out = device.RunShellCommand( ['sh', md5sum_device_script_file.name]) return _ParseMd5SumOutput(out)
def RunWithFlags(self, device, flags, **kwargs): with device_temp_file.DeviceTempFile(device.adb) as command_line_file: device.WriteFile(command_line_file.name, '_ %s' % flags) return device.StartInstrumentation( self._component, extras={_EXTRA_COMMAND_LINE_FILE: command_line_file.name}, raw=False, **kwargs)
def testTempFileNameAlreadyExists(self): with self.assertCalls( self.mockShellCall('test -d /data/local/tmp'), self.mockExistsTest(True), self.mockExistsTest(True), self.mockExistsTest(True), self.mockExistsTest(False), self.mockShellCall('touch '), self.mockShellCall('rm -f ')): with device_temp_file.DeviceTempFile(self.adb) as tmpfile: logging.debug('Temp file name: %s' % tmpfile.name)
def testTempFileLifecycle(self): with self.assertCalls( self.mockShellCall('test -d /data/local/tmp'), self.mockExistsTest(False), self.mockShellCall('touch ')): tempFileContextManager = device_temp_file.DeviceTempFile(self.adb) with mock.patch.object(self.adb, 'Shell'): with tempFileContextManager as tmpfile: logging.debug('Temp file name: %s' % tmpfile.name) self.assertEquals(0, self.adb.Shell.call_count) self.assertEquals(1, self.adb.Shell.call_count) args, _ = self.adb.Shell.call_args self.assertTrue(args[0].startswith('rm -f '))
def RunWithFlags(self, device, flags, **kwargs): with device_temp_file.DeviceTempFile(device.adb) as command_line_file: device.WriteFile(command_line_file.name, '_ %s' % flags) extras = { _EXTRA_COMMAND_LINE_FILE: command_line_file.name, } if self._enable_test_server_spawner: extras[_EXTRA_ENABLE_TEST_SERVER_SPAWNER] = '1' return device.StartInstrumentation(self._component, extras=extras, raw=False, **kwargs)
def WriteFile(self, device_path, contents, as_root=False, force_push=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 privileges (if available). force_push: A boolean indicating whether to force the operation to be performed by pushing a file to the device. The default is, when the contents are short, to pass the contents using a shell script instead. 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 len(contents) < 512 and not force_push: cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), cmd_helper.SingleQuote(device_path)) self.RunShellCommand(cmd, as_root=as_root, check_return=True) else: with tempfile.NamedTemporaryFile() as host_temp: host_temp.write(contents) host_temp.flush() if as_root and self.NeedsSU(): with device_temp_file.DeviceTempFile( self.adb) as device_temp: self.adb.Push(host_temp.name, device_temp.name) # Here we need 'cp' rather than 'mv' because the temp and # destination files might be on different file systems (e.g. # on internal storage and an external sd card) self.RunShellCommand( ['cp', device_temp.name, device_path], as_root=True, check_return=True) else: self.adb.Push(host_temp.name, device_path)
def __init__(self, device, perf_binary, categories): self._device = device self._output_file = device_temp_file.DeviceTempFile( self._device.adb, prefix='perf_output') self._log_file = tempfile.TemporaryFile() # TODO(jbudorick) Look at providing a way to unhandroll this once the # adb rewrite has fully landed. device_param = (['-s', str(self._device)] if str(self._device) else []) cmd = ['adb'] + device_param + \ ['shell', perf_binary, 'record', '--output', self._output_file.name] + _PERF_OPTIONS if categories: cmd += ['--event', ','.join(categories)] self._perf_control = perf_control.PerfControl(self._device) self._perf_control.SetPerfProfilingMode() self._perf_process = subprocess.Popen(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
def GetPsOutput(self, columns, pid=None): assert columns == ['pid', 'name'] or columns == ['pid'], \ 'Only know how to return pid and name. Requested: ' + columns command = 'ps' if pid: command += ' -p %d' % pid with device_temp_file.DeviceTempFile(self._device.adb) as ps_out: command += ' > %s' % ps_out.name self._device.RunShellCommand(command) # Get rid of trailing new line and header. ps = self._device.ReadFile(ps_out.name).split('\n')[1:-1] output = [] for line in ps: data = line.split() curr_pid = data[1] curr_name = data[-1] if columns == ['pid', 'name']: output.append([curr_pid, curr_name]) else: output.append([curr_pid]) return output
def WriteFile(self, device_path, contents, as_root=False, force_push=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 privileges (if available). force_push: A boolean indicating whether to force the operation to be performed by pushing a file to the device. The default is, when the contents are short, to pass the contents using a shell script instead. 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 not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH: # If the contents are small, for efficieny we write the contents with # a shell command rather than pushing a file. cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), cmd_helper.SingleQuote(device_path)) self.RunShellCommand(cmd, as_root=as_root, check_return=True) elif as_root and self.NeedsSU(): # Adb does not allow to "push with su", so we first push to a temp file # on a safe location, and then copy it to the desired location with su. with device_temp_file.DeviceTempFile(self.adb) as device_temp: self._WriteFileWithPush(device_temp.name, contents) # Here we need 'cp' rather than 'mv' because the temp and # destination files might be on different file systems (e.g. # on internal storage and an external sd card). self.RunShellCommand(['cp', device_temp.name, device_path], as_root=True, check_return=True) else: # If root is not needed, we can push directly to the desired location. self._WriteFileWithPush(device_path, contents)
def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, as_root=False, single_line=False, timeout=None, retries=None): """Run an ADB shell command. The command to run |cmd| should be a sequence of program arguments or else a single string. When |cmd| is a sequence, it is assumed to contain the name of the command to run followed by its arguments. In this case, arguments are passed to the command exactly as given, without any further processing by the shell. This allows to easily pass arguments containing spaces or special characters without having to worry about getting quoting right. Whenever possible, it is recomended to pass |cmd| as a sequence. When |cmd| is given as a string, it will be interpreted and run by the shell on the device. This behaviour is consistent with that of command runners in cmd_helper as well as Python's own subprocess.Popen. TODO(perezju) Change the default of |check_return| to True when callers have switched to the new behaviour. Args: cmd: A string with the full command to run on the device, or a sequence containing the command and its arguments. check_return: A boolean indicating whether or not the return code should be checked. cwd: The device directory in which the command should be run. env: The environment variables with which the command should be run. as_root: A boolean indicating whether the shell command should be run with root privileges. single_line: A boolean indicating if only a single line of output is expected. timeout: timeout in seconds retries: number of retries Returns: If single_line is False, the output of the command as a list of lines, otherwise, a string with the unique line of output emmited by the command (with the optional newline at the end stripped). Raises: AdbCommandFailedError if check_return is True and the exit code of the command run on the device is non-zero. CommandFailedError if single_line is True but the output contains two or more lines. CommandTimeoutError on timeout. DeviceUnreachableError on missing device. """ def env_quote(key, value): if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): raise KeyError('Invalid shell variable name %r' % key) # using double quotes here to allow interpolation of shell variables return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) def do_run(cmd): try: return self.adb.Shell(cmd) except device_errors.AdbCommandFailedError as exc: if check_return: raise else: return exc.output if not isinstance(cmd, basestring): cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) if env: env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) cmd = '%s %s' % (env, cmd) if cwd: cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) if as_root and self.NeedsSU(): # "su -c sh -c" allows using shell features in |cmd| cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) if timeout is None: timeout = self._default_timeout if len(cmd) < self._MAX_ADB_COMMAND_LENGTH: output = do_run(cmd) else: with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: self._WriteFileWithPush(script.name, cmd) logging.info('Large shell command will be run from file: %s ...', cmd[:100]) output = do_run('sh %s' % script.name_quoted) output = output.splitlines() if single_line: if not output: return '' elif len(output) == 1: return output[0] else: msg = 'one line of output was expected, but got: %s' raise device_errors.CommandFailedError(msg % output, str(self)) else: return output