15d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albertimport time
25d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert
35d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albertimport libcxx.test.executor
45d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert
55d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albertfrom libcxx.android import adb
65d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albertfrom lit.util import executeCommand  # pylint: disable=import-error
75d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert
85d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert
95d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albertclass AdbExecutor(libcxx.test.executor.RemoteExecutor):
105d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert    def __init__(self, serial=None):
115d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        # TODO(danalbert): Should factor out the shared pieces of SSHExecutor
125d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        # so we don't have this ugly parent constructor...
135d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        super(AdbExecutor, self).__init__()
145d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        self.serial = serial
155d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        self.local_run = executeCommand
165d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert
175d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert    def _remote_temp(self, is_dir):
185d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        dir_arg = '-d' if is_dir else ''
195d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        cmd = 'mktemp -q {} /data/local/tmp/libcxx.XXXXXXXXXX'.format(dir_arg)
205d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        temp_path, err, exitCode = self._execute_command_remote([cmd])
215d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        temp_path = temp_path.strip()
225d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        if exitCode != 0:
235d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert            raise RuntimeError(err)
245d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        return temp_path
255d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert
265d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert    def _copy_in_file(self, src, dst):  # pylint: disable=no-self-use
275d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        adb.push(src, dst)
285d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert
295d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert    def _execute_command_remote(self, cmd, remote_work_dir='.', env=None):
305d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        adb_cmd = ['adb', 'shell']
315d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        if self.serial:
325d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert            adb_cmd.extend(['-s', self.serial])
335d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        if env:
345d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert            env_cmd = ['env'] + ['%s=%s' % (k, v) for k, v in env.items()]
355d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        else:
365d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert            env_cmd = []
375d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        remote_cmd = ' '.join(env_cmd + cmd + ['; echo $?'])
385d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        if remote_work_dir != '.':
395d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert            remote_cmd = 'cd {} && {}'.format(remote_work_dir, remote_cmd)
405d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        adb_cmd.append(remote_cmd)
415d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert
425d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        # Tests will commonly fail with ETXTBSY. Possibly related to this bug:
435d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        # https://code.google.com/p/android/issues/detail?id=65857. Work around
445d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        # it by just waiting a second and then retrying.
455d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        for _ in range(10):
465d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert            out, err, exit_code = self.local_run(adb_cmd)
475d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert            if 'Text file busy' in out:
485d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert                time.sleep(1)
495d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert            else:
505d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert                out = out.strip().split('\r\n')
515d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert                status_line = out[-1:][0]
525d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert                out = '\n'.join(out[:-1])
535d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert                exit_code = int(status_line)
545d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert                break
555d8e4e3581eab9235b8f9c4887f4a7e79f54d769Dan Albert        return out, err, exit_code
56