import time import libcxx.test.executor from libcxx.android import adb from lit.util import executeCommand # pylint: disable=import-error class AdbExecutor(libcxx.test.executor.RemoteExecutor): def __init__(self, serial=None): # TODO(danalbert): Should factor out the shared pieces of SSHExecutor # so we don't have this ugly parent constructor... super(AdbExecutor, self).__init__() self.serial = serial self.local_run = executeCommand def _remote_temp(self, is_dir): dir_arg = '-d' if is_dir else '' cmd = 'mktemp -q {} /data/local/tmp/libcxx.XXXXXXXXXX'.format(dir_arg) _, temp_path, err, exitCode = self._execute_command_remote([cmd]) temp_path = temp_path.strip() if exitCode != 0: raise RuntimeError(err) return temp_path def _copy_in_file(self, src, dst): # pylint: disable=no-self-use adb.push(src, dst) def _execute_command_remote(self, cmd, remote_work_dir='.', env=None): adb_cmd = ['adb', 'shell'] if self.serial: adb_cmd.extend(['-s', self.serial]) delimiter = 'x' probe_cmd = ' '.join(cmd) + '; echo {}$?'.format(delimiter) env_cmd = [] if env is not None: env_cmd = ['env'] + ['%s=%s' % (k, v) for k, v in env.items()] remote_cmd = ' '.join(env_cmd + [probe_cmd]) if remote_work_dir != '.': remote_cmd = 'cd {} && {}'.format(remote_work_dir, remote_cmd) adb_cmd.append(remote_cmd) # Tests will commonly fail with ETXTBSY. Possibly related to this bug: # https://code.google.com/p/android/issues/detail?id=65857. Work around # it by just waiting a second and then retrying. for _ in range(10): out, err, exit_code = self.local_run(adb_cmd) if 'Text file busy' in out: time.sleep(1) else: out, delim, rc_str = out.rpartition(delimiter) if delim == '': continue out = out.strip() try: exit_code = int(rc_str) break except ValueError: continue return adb_cmd, out, err, exit_code