11cb0ef41Sopenharmony_ci# Copyright 2017 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci# found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_cifrom contextlib import contextmanager
61cb0ef41Sopenharmony_ciimport os
71cb0ef41Sopenharmony_ciimport re
81cb0ef41Sopenharmony_ciimport signal
91cb0ef41Sopenharmony_ciimport subprocess
101cb0ef41Sopenharmony_ciimport sys
111cb0ef41Sopenharmony_ciimport threading
121cb0ef41Sopenharmony_ciimport time
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_cifrom ..local.android import (
151cb0ef41Sopenharmony_ci    android_driver, CommandFailedException, TimeoutException)
161cb0ef41Sopenharmony_cifrom ..local import utils
171cb0ef41Sopenharmony_cifrom ..objects import output
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ciBASE_DIR = os.path.normpath(
201cb0ef41Sopenharmony_ci    os.path.join(os.path.dirname(os.path.abspath(__file__)), '..' , '..', '..'))
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_ciSEM_INVALID_VALUE = -1
231cb0ef41Sopenharmony_ciSEM_NOGPFAULTERRORBOX = 0x0002  # Microsoft Platform SDK WinBase.h
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_cidef setup_testing():
271cb0ef41Sopenharmony_ci  """For testing only: We use threading under the hood instead of
281cb0ef41Sopenharmony_ci  multiprocessing to make coverage work. Signal handling is only supported
291cb0ef41Sopenharmony_ci  in the main thread, so we disable it for testing.
301cb0ef41Sopenharmony_ci  """
311cb0ef41Sopenharmony_ci  signal.signal = lambda *_: None
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ciclass AbortException(Exception):
351cb0ef41Sopenharmony_ci  """Indicates early abort on SIGINT, SIGTERM or internal hard timeout."""
361cb0ef41Sopenharmony_ci  pass
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci@contextmanager
401cb0ef41Sopenharmony_cidef handle_sigterm(process, abort_fun, enabled):
411cb0ef41Sopenharmony_ci  """Call`abort_fun` on sigterm and restore previous handler to prevent
421cb0ef41Sopenharmony_ci  erroneous termination of an already terminated process.
431cb0ef41Sopenharmony_ci
441cb0ef41Sopenharmony_ci  Args:
451cb0ef41Sopenharmony_ci    process: The process to terminate.
461cb0ef41Sopenharmony_ci    abort_fun: Function taking two parameters: the process to terminate and
471cb0ef41Sopenharmony_ci        an array with a boolean for storing if an abort occured.
481cb0ef41Sopenharmony_ci    enabled: If False, this wrapper will be a no-op.
491cb0ef41Sopenharmony_ci  """
501cb0ef41Sopenharmony_ci  # Variable to communicate with the signal handler.
511cb0ef41Sopenharmony_ci  abort_occured = [False]
521cb0ef41Sopenharmony_ci  def handler(signum, frame):
531cb0ef41Sopenharmony_ci    abort_fun(process, abort_occured)
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci  if enabled:
561cb0ef41Sopenharmony_ci    previous = signal.signal(signal.SIGTERM, handler)
571cb0ef41Sopenharmony_ci  try:
581cb0ef41Sopenharmony_ci    yield
591cb0ef41Sopenharmony_ci  finally:
601cb0ef41Sopenharmony_ci    if enabled:
611cb0ef41Sopenharmony_ci      signal.signal(signal.SIGTERM, previous)
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ci  if abort_occured[0]:
641cb0ef41Sopenharmony_ci    raise AbortException()
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ciclass BaseCommand(object):
681cb0ef41Sopenharmony_ci  def __init__(self, shell, args=None, cmd_prefix=None, timeout=60, env=None,
691cb0ef41Sopenharmony_ci               verbose=False, resources_func=None, handle_sigterm=False):
701cb0ef41Sopenharmony_ci    """Initialize the command.
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci    Args:
731cb0ef41Sopenharmony_ci      shell: The name of the executable (e.g. d8).
741cb0ef41Sopenharmony_ci      args: List of args to pass to the executable.
751cb0ef41Sopenharmony_ci      cmd_prefix: Prefix of command (e.g. a wrapper script).
761cb0ef41Sopenharmony_ci      timeout: Timeout in seconds.
771cb0ef41Sopenharmony_ci      env: Environment dict for execution.
781cb0ef41Sopenharmony_ci      verbose: Print additional output.
791cb0ef41Sopenharmony_ci      resources_func: Callable, returning all test files needed by this command.
801cb0ef41Sopenharmony_ci      handle_sigterm: Flag indicating if SIGTERM will be used to terminate the
811cb0ef41Sopenharmony_ci          underlying process. Should not be used from the main thread, e.g. when
821cb0ef41Sopenharmony_ci          using a command to list tests.
831cb0ef41Sopenharmony_ci    """
841cb0ef41Sopenharmony_ci    assert(timeout > 0)
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci    self.shell = shell
871cb0ef41Sopenharmony_ci    self.args = args or []
881cb0ef41Sopenharmony_ci    self.cmd_prefix = cmd_prefix or []
891cb0ef41Sopenharmony_ci    self.timeout = timeout
901cb0ef41Sopenharmony_ci    self.env = env or {}
911cb0ef41Sopenharmony_ci    self.verbose = verbose
921cb0ef41Sopenharmony_ci    self.handle_sigterm = handle_sigterm
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci  def execute(self):
951cb0ef41Sopenharmony_ci    if self.verbose:
961cb0ef41Sopenharmony_ci      print('# %s' % self)
971cb0ef41Sopenharmony_ci
981cb0ef41Sopenharmony_ci    process = self._start_process()
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci    with handle_sigterm(process, self._abort, self.handle_sigterm):
1011cb0ef41Sopenharmony_ci      # Variable to communicate with the timer.
1021cb0ef41Sopenharmony_ci      timeout_occured = [False]
1031cb0ef41Sopenharmony_ci      timer = threading.Timer(
1041cb0ef41Sopenharmony_ci          self.timeout, self._abort, [process, timeout_occured])
1051cb0ef41Sopenharmony_ci      timer.start()
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci      start_time = time.time()
1081cb0ef41Sopenharmony_ci      stdout, stderr = process.communicate()
1091cb0ef41Sopenharmony_ci      duration = time.time() - start_time
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci      timer.cancel()
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ci    return output.Output(
1141cb0ef41Sopenharmony_ci      process.returncode,
1151cb0ef41Sopenharmony_ci      timeout_occured[0],
1161cb0ef41Sopenharmony_ci      stdout.decode('utf-8', 'replace'),
1171cb0ef41Sopenharmony_ci      stderr.decode('utf-8', 'replace'),
1181cb0ef41Sopenharmony_ci      process.pid,
1191cb0ef41Sopenharmony_ci      duration
1201cb0ef41Sopenharmony_ci    )
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ci  def _start_process(self):
1231cb0ef41Sopenharmony_ci    try:
1241cb0ef41Sopenharmony_ci      return subprocess.Popen(
1251cb0ef41Sopenharmony_ci        args=self._get_popen_args(),
1261cb0ef41Sopenharmony_ci        stdout=subprocess.PIPE,
1271cb0ef41Sopenharmony_ci        stderr=subprocess.PIPE,
1281cb0ef41Sopenharmony_ci        env=self._get_env(),
1291cb0ef41Sopenharmony_ci      )
1301cb0ef41Sopenharmony_ci    except Exception as e:
1311cb0ef41Sopenharmony_ci      sys.stderr.write('Error executing: %s\n' % self)
1321cb0ef41Sopenharmony_ci      raise e
1331cb0ef41Sopenharmony_ci
1341cb0ef41Sopenharmony_ci  def _get_popen_args(self):
1351cb0ef41Sopenharmony_ci    return self._to_args_list()
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ci  def _get_env(self):
1381cb0ef41Sopenharmony_ci    env = os.environ.copy()
1391cb0ef41Sopenharmony_ci    env.update(self.env)
1401cb0ef41Sopenharmony_ci    # GTest shard information is read by the V8 tests runner. Make sure it
1411cb0ef41Sopenharmony_ci    # doesn't leak into the execution of gtests we're wrapping. Those might
1421cb0ef41Sopenharmony_ci    # otherwise apply a second level of sharding and as a result skip tests.
1431cb0ef41Sopenharmony_ci    env.pop('GTEST_TOTAL_SHARDS', None)
1441cb0ef41Sopenharmony_ci    env.pop('GTEST_SHARD_INDEX', None)
1451cb0ef41Sopenharmony_ci    return env
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci  def _kill_process(self, process):
1481cb0ef41Sopenharmony_ci    raise NotImplementedError()
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_ci  def _abort(self, process, abort_called):
1511cb0ef41Sopenharmony_ci    abort_called[0] = True
1521cb0ef41Sopenharmony_ci    started_as = self.to_string(relative=True)
1531cb0ef41Sopenharmony_ci    process_text = 'process %d started as:\n  %s\n' % (process.pid, started_as)
1541cb0ef41Sopenharmony_ci    try:
1551cb0ef41Sopenharmony_ci      print('Attempting to kill ' + process_text)
1561cb0ef41Sopenharmony_ci      sys.stdout.flush()
1571cb0ef41Sopenharmony_ci      self._kill_process(process)
1581cb0ef41Sopenharmony_ci    except OSError as e:
1591cb0ef41Sopenharmony_ci      print(e)
1601cb0ef41Sopenharmony_ci      print('Unruly ' + process_text)
1611cb0ef41Sopenharmony_ci      sys.stdout.flush()
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci  def __str__(self):
1641cb0ef41Sopenharmony_ci    return self.to_string()
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci  def to_string(self, relative=False):
1671cb0ef41Sopenharmony_ci    def escape(part):
1681cb0ef41Sopenharmony_ci      # Escape spaces. We may need to escape more characters for this to work
1691cb0ef41Sopenharmony_ci      # properly.
1701cb0ef41Sopenharmony_ci      if ' ' in part:
1711cb0ef41Sopenharmony_ci        return '"%s"' % part
1721cb0ef41Sopenharmony_ci      return part
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci    parts = map(escape, self._to_args_list())
1751cb0ef41Sopenharmony_ci    cmd = ' '.join(parts)
1761cb0ef41Sopenharmony_ci    if relative:
1771cb0ef41Sopenharmony_ci      cmd = cmd.replace(os.getcwd() + os.sep, '')
1781cb0ef41Sopenharmony_ci    return cmd
1791cb0ef41Sopenharmony_ci
1801cb0ef41Sopenharmony_ci  def _to_args_list(self):
1811cb0ef41Sopenharmony_ci    return self.cmd_prefix + [self.shell] + self.args
1821cb0ef41Sopenharmony_ci
1831cb0ef41Sopenharmony_ci
1841cb0ef41Sopenharmony_ciclass PosixCommand(BaseCommand):
1851cb0ef41Sopenharmony_ci  # TODO(machenbach): Use base process start without shell once
1861cb0ef41Sopenharmony_ci  # https://crbug.com/v8/8889 is resolved.
1871cb0ef41Sopenharmony_ci  def _start_process(self):
1881cb0ef41Sopenharmony_ci    def wrapped(arg):
1891cb0ef41Sopenharmony_ci      if set('() \'"') & set(arg):
1901cb0ef41Sopenharmony_ci        return "'%s'" % arg.replace("'", "'\"'\"'")
1911cb0ef41Sopenharmony_ci      return arg
1921cb0ef41Sopenharmony_ci    try:
1931cb0ef41Sopenharmony_ci      return subprocess.Popen(
1941cb0ef41Sopenharmony_ci        args=' '.join(map(wrapped, self._get_popen_args())),
1951cb0ef41Sopenharmony_ci        stdout=subprocess.PIPE,
1961cb0ef41Sopenharmony_ci        stderr=subprocess.PIPE,
1971cb0ef41Sopenharmony_ci        env=self._get_env(),
1981cb0ef41Sopenharmony_ci        shell=True,
1991cb0ef41Sopenharmony_ci        # Make the new shell create its own process group. This allows to kill
2001cb0ef41Sopenharmony_ci        # all spawned processes reliably (https://crbug.com/v8/8292).
2011cb0ef41Sopenharmony_ci        preexec_fn=os.setsid,
2021cb0ef41Sopenharmony_ci      )
2031cb0ef41Sopenharmony_ci    except Exception as e:
2041cb0ef41Sopenharmony_ci      sys.stderr.write('Error executing: %s\n' % self)
2051cb0ef41Sopenharmony_ci      raise e
2061cb0ef41Sopenharmony_ci
2071cb0ef41Sopenharmony_ci  def _kill_process(self, process):
2081cb0ef41Sopenharmony_ci    # Kill the whole process group (PID == GPID after setsid).
2091cb0ef41Sopenharmony_ci    os.killpg(process.pid, signal.SIGKILL)
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci
2121cb0ef41Sopenharmony_cidef taskkill_windows(process, verbose=False, force=True):
2131cb0ef41Sopenharmony_ci  force_flag = ' /F' if force else ''
2141cb0ef41Sopenharmony_ci  tk = subprocess.Popen(
2151cb0ef41Sopenharmony_ci      'taskkill /T%s /PID %d' % (force_flag, process.pid),
2161cb0ef41Sopenharmony_ci      stdout=subprocess.PIPE,
2171cb0ef41Sopenharmony_ci      stderr=subprocess.PIPE,
2181cb0ef41Sopenharmony_ci  )
2191cb0ef41Sopenharmony_ci  stdout, stderr = tk.communicate()
2201cb0ef41Sopenharmony_ci  if verbose:
2211cb0ef41Sopenharmony_ci    print('Taskkill results for %d' % process.pid)
2221cb0ef41Sopenharmony_ci    print(stdout)
2231cb0ef41Sopenharmony_ci    print(stderr)
2241cb0ef41Sopenharmony_ci    print('Return code: %d' % tk.returncode)
2251cb0ef41Sopenharmony_ci    sys.stdout.flush()
2261cb0ef41Sopenharmony_ci
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ciclass WindowsCommand(BaseCommand):
2291cb0ef41Sopenharmony_ci  def _start_process(self, **kwargs):
2301cb0ef41Sopenharmony_ci    # Try to change the error mode to avoid dialogs on fatal errors. Don't
2311cb0ef41Sopenharmony_ci    # touch any existing error mode flags by merging the existing error mode.
2321cb0ef41Sopenharmony_ci    # See http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx.
2331cb0ef41Sopenharmony_ci    def set_error_mode(mode):
2341cb0ef41Sopenharmony_ci      prev_error_mode = SEM_INVALID_VALUE
2351cb0ef41Sopenharmony_ci      try:
2361cb0ef41Sopenharmony_ci        import ctypes
2371cb0ef41Sopenharmony_ci        prev_error_mode = (
2381cb0ef41Sopenharmony_ci            ctypes.windll.kernel32.SetErrorMode(mode))  #@UndefinedVariable
2391cb0ef41Sopenharmony_ci      except ImportError:
2401cb0ef41Sopenharmony_ci        pass
2411cb0ef41Sopenharmony_ci      return prev_error_mode
2421cb0ef41Sopenharmony_ci
2431cb0ef41Sopenharmony_ci    error_mode = SEM_NOGPFAULTERRORBOX
2441cb0ef41Sopenharmony_ci    prev_error_mode = set_error_mode(error_mode)
2451cb0ef41Sopenharmony_ci    set_error_mode(error_mode | prev_error_mode)
2461cb0ef41Sopenharmony_ci
2471cb0ef41Sopenharmony_ci    try:
2481cb0ef41Sopenharmony_ci      return super(WindowsCommand, self)._start_process(**kwargs)
2491cb0ef41Sopenharmony_ci    finally:
2501cb0ef41Sopenharmony_ci      if prev_error_mode != SEM_INVALID_VALUE:
2511cb0ef41Sopenharmony_ci        set_error_mode(prev_error_mode)
2521cb0ef41Sopenharmony_ci
2531cb0ef41Sopenharmony_ci  def _get_popen_args(self):
2541cb0ef41Sopenharmony_ci    return subprocess.list2cmdline(self._to_args_list())
2551cb0ef41Sopenharmony_ci
2561cb0ef41Sopenharmony_ci  def _kill_process(self, process):
2571cb0ef41Sopenharmony_ci    taskkill_windows(process, self.verbose)
2581cb0ef41Sopenharmony_ci
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_ciclass AndroidCommand(BaseCommand):
2611cb0ef41Sopenharmony_ci  # This must be initialized before creating any instances of this class.
2621cb0ef41Sopenharmony_ci  driver = None
2631cb0ef41Sopenharmony_ci
2641cb0ef41Sopenharmony_ci  def __init__(self, shell, args=None, cmd_prefix=None, timeout=60, env=None,
2651cb0ef41Sopenharmony_ci               verbose=False, resources_func=None, handle_sigterm=False):
2661cb0ef41Sopenharmony_ci    """Initialize the command and all files that need to be pushed to the
2671cb0ef41Sopenharmony_ci    Android device.
2681cb0ef41Sopenharmony_ci    """
2691cb0ef41Sopenharmony_ci    self.shell_name = os.path.basename(shell)
2701cb0ef41Sopenharmony_ci    self.shell_dir = os.path.dirname(shell)
2711cb0ef41Sopenharmony_ci    self.files_to_push = (resources_func or (lambda: []))()
2721cb0ef41Sopenharmony_ci
2731cb0ef41Sopenharmony_ci    # Make all paths in arguments relative and also prepare files from arguments
2741cb0ef41Sopenharmony_ci    # for pushing to the device.
2751cb0ef41Sopenharmony_ci    rel_args = []
2761cb0ef41Sopenharmony_ci    find_path_re = re.compile(r'.*(%s/[^\'"]+).*' % re.escape(BASE_DIR))
2771cb0ef41Sopenharmony_ci    for arg in (args or []):
2781cb0ef41Sopenharmony_ci      match = find_path_re.match(arg)
2791cb0ef41Sopenharmony_ci      if match:
2801cb0ef41Sopenharmony_ci        self.files_to_push.append(match.group(1))
2811cb0ef41Sopenharmony_ci      rel_args.append(
2821cb0ef41Sopenharmony_ci          re.sub(r'(.*)%s/(.*)' % re.escape(BASE_DIR), r'\1\2', arg))
2831cb0ef41Sopenharmony_ci
2841cb0ef41Sopenharmony_ci    super(AndroidCommand, self).__init__(
2851cb0ef41Sopenharmony_ci        shell, args=rel_args, cmd_prefix=cmd_prefix, timeout=timeout, env=env,
2861cb0ef41Sopenharmony_ci        verbose=verbose, handle_sigterm=handle_sigterm)
2871cb0ef41Sopenharmony_ci
2881cb0ef41Sopenharmony_ci  def execute(self, **additional_popen_kwargs):
2891cb0ef41Sopenharmony_ci    """Execute the command on the device.
2901cb0ef41Sopenharmony_ci
2911cb0ef41Sopenharmony_ci    This pushes all required files to the device and then runs the command.
2921cb0ef41Sopenharmony_ci    """
2931cb0ef41Sopenharmony_ci    if self.verbose:
2941cb0ef41Sopenharmony_ci      print('# %s' % self)
2951cb0ef41Sopenharmony_ci
2961cb0ef41Sopenharmony_ci    self.driver.push_executable(self.shell_dir, 'bin', self.shell_name)
2971cb0ef41Sopenharmony_ci
2981cb0ef41Sopenharmony_ci    for abs_file in self.files_to_push:
2991cb0ef41Sopenharmony_ci      abs_dir = os.path.dirname(abs_file)
3001cb0ef41Sopenharmony_ci      file_name = os.path.basename(abs_file)
3011cb0ef41Sopenharmony_ci      rel_dir = os.path.relpath(abs_dir, BASE_DIR)
3021cb0ef41Sopenharmony_ci      self.driver.push_file(abs_dir, file_name, rel_dir)
3031cb0ef41Sopenharmony_ci
3041cb0ef41Sopenharmony_ci    start_time = time.time()
3051cb0ef41Sopenharmony_ci    return_code = 0
3061cb0ef41Sopenharmony_ci    timed_out = False
3071cb0ef41Sopenharmony_ci    try:
3081cb0ef41Sopenharmony_ci      stdout = self.driver.run(
3091cb0ef41Sopenharmony_ci          'bin', self.shell_name, self.args, '.', self.timeout, self.env)
3101cb0ef41Sopenharmony_ci    except CommandFailedException as e:
3111cb0ef41Sopenharmony_ci      return_code = e.status
3121cb0ef41Sopenharmony_ci      stdout = e.output
3131cb0ef41Sopenharmony_ci    except TimeoutException as e:
3141cb0ef41Sopenharmony_ci      return_code = 1
3151cb0ef41Sopenharmony_ci      timed_out = True
3161cb0ef41Sopenharmony_ci      # Sadly the Android driver doesn't provide output on timeout.
3171cb0ef41Sopenharmony_ci      stdout = ''
3181cb0ef41Sopenharmony_ci
3191cb0ef41Sopenharmony_ci    duration = time.time() - start_time
3201cb0ef41Sopenharmony_ci    return output.Output(
3211cb0ef41Sopenharmony_ci        return_code,
3221cb0ef41Sopenharmony_ci        timed_out,
3231cb0ef41Sopenharmony_ci        stdout,
3241cb0ef41Sopenharmony_ci        '',  # No stderr available.
3251cb0ef41Sopenharmony_ci        -1,  # No pid available.
3261cb0ef41Sopenharmony_ci        duration,
3271cb0ef41Sopenharmony_ci    )
3281cb0ef41Sopenharmony_ci
3291cb0ef41Sopenharmony_ci
3301cb0ef41Sopenharmony_ciCommand = None
3311cb0ef41Sopenharmony_cidef setup(target_os, device):
3321cb0ef41Sopenharmony_ci  """Set the Command class to the OS-specific version."""
3331cb0ef41Sopenharmony_ci  global Command
3341cb0ef41Sopenharmony_ci  if target_os == 'android':
3351cb0ef41Sopenharmony_ci    AndroidCommand.driver = android_driver(device)
3361cb0ef41Sopenharmony_ci    Command = AndroidCommand
3371cb0ef41Sopenharmony_ci  elif target_os == 'windows':
3381cb0ef41Sopenharmony_ci    Command = WindowsCommand
3391cb0ef41Sopenharmony_ci  else:
3401cb0ef41Sopenharmony_ci    Command = PosixCommand
3411cb0ef41Sopenharmony_ci
3421cb0ef41Sopenharmony_cidef tear_down():
3431cb0ef41Sopenharmony_ci  """Clean up after using commands."""
3441cb0ef41Sopenharmony_ci  if Command == AndroidCommand:
3451cb0ef41Sopenharmony_ci    AndroidCommand.driver.tear_down()
346