blob: de5b4328899bab22d975f0b876f7a20bb909424a [file] [log] [blame]
# Copyright 2018 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
from autotest_lib.server import autotest
from autotest_lib.server.cros import tradefed_constants as constants
class ChromeLogin(object):
"""Context manager to handle Chrome login state."""
def need_reboot(self):
"""Marks state as "dirty" - reboot needed during/after test."""
logging.info('Will reboot DUT when Chrome stops.')
self._need_reboot = True
def need_restart(self):
"""Marks state as "dirty" - restart needed after test."""
self._need_restart = True
def __init__(self, host, kwargs):
"""Initializes the _ChromeLogin object.
@param reboot: indicate if a reboot before destruction is required.
@param restart: indicate if a restart before destruction is required.
@param board: optional parameter to extend timeout for login for slow
DUTs. Used in particular for virtual machines.
"""
self._host = host
self._cts_helper_kwargs = kwargs
# We will override reboot/restart options to some degree. Keep track
# of them in a local copy.
self._need_reboot = False
if kwargs.get('reboot'):
self.need_reboot()
self._need_restart = False
if kwargs.get('restart'):
self.need_restart()
self._timeout = constants.LOGIN_DEFAULT_TIMEOUT
board = kwargs.get('board')
if board in constants.LOGIN_BOARD_TIMEOUT:
self._timeout = constants.LOGIN_BOARD_TIMEOUT[board]
def _cmd_builder(self, verbose=False):
"""Gets remote command to start browser with ARC enabled."""
# If autotest is not installed on the host, as with moblab at times,
# getting the autodir will raise an exception.
cmd = autotest.Autotest.get_installed_autodir(self._host)
cmd += '/bin/autologin.py --arc'
if self._cts_helper_kwargs.get('dont_override_profile'):
logging.info('Using --dont_override_profile to start Chrome.')
cmd += ' --dont_override_profile'
else:
logging.info('Not using --dont_override_profile to start Chrome.')
if not verbose:
cmd += ' > /dev/null 2>&1'
return cmd
def login(self, timeout=None, raise_exception=False, verbose=False):
"""Logs into Chrome."""
if not timeout:
timeout = self._timeout
try:
# We used to call cheets_StartAndroid, but it is a little faster to
# call a script on the DUT. This also saves CPU time on the server.
self._host.run(
self._cmd_builder(),
ignore_status=False,
verbose=verbose,
timeout=timeout)
return True
except autotest.AutodirNotFoundError:
# Autotest is not installed (can happen on moblab after image
# install). Run dummy_Pass to foce autotest install, before trying
# to login again.
logging.warning(
'Autotest not installed, forcing install using dummy_Pass...')
try:
autotest.Autotest(self._host).run_timed_test(
'dummy_Pass',
timeout=2 * timeout,
check_client_result=True,
**self._cts_helper_kwargs)
self._host.run(
self._cmd_builder(),
ignore_status=False,
verbose=verbose,
timeout=timeout)
return True
except:
# We were unable to start the browser/Android. Maybe we can
# salvage the DUT by rebooting. This can hide some failures.
self.reboot()
if raise_exception:
raise
except:
# We were unable to start the browser/Android. Maybe we can
# salvage the DUT by rebooting. This can hide some failures.
self.reboot()
if raise_exception:
raise
return False
def enter(self):
"""Logs into Chrome with retry."""
timeout = self._timeout
logging.info('Ensure Android is running (timeout=%d)...', timeout)
if not self.login(timeout=timeout):
timeout *= 2
# The DUT reboots after unsuccessful login, try with more time.
logging.info('Retrying failed login (timeout=%d)...', timeout)
self.login(timeout=timeout, raise_exception=True, verbose=True)
return self
def __enter__(self):
"""Logs into Chrome with retry."""
return self.enter()
def exit(self, exc_type=None, exc_value=None, traceback=None):
"""On exit restart the browser or reboot the machine.
@param exc_type: Exception type if an exception is raised from the
with-block.
@param exc_value: Exception instance if an exception is raised from
the with-block.
@param traceback: Stack trace info if an exception is raised from
the with-block.
@return None, indicating not to ignore an exception from the with-block
if raised.
"""
if not self._need_reboot:
logging.info('Skipping reboot, restarting browser.')
try:
self.restart()
except:
logging.error('Restarting browser has failed.')
self.need_reboot()
if self._need_reboot:
self.reboot(exc_type, exc_value, traceback)
def __exit__(self, exc_type, exc_value, traceback):
"""On exit restart the browser or reboot the machine.
@param exc_type: Exception type if an exception is raised from the
with-block.
@param exc_value: Exception instance if an exception is raised from
the with-block.
@param traceback: Stack trace info if an exception is raised from
the with-block.
@return None, indicating not to ignore an exception from the with-block
if raised.
"""
self.exit(exc_type, exc_value, traceback)
def restart(self):
"""Restart Chrome browser."""
# We clean up /tmp (which is memory backed) from crashes and
# other files. A reboot would have cleaned /tmp as well.
# TODO(ihf): Remove "start ui" which is a nicety to non-ARC tests (i.e.
# now we wait on login screen, but login() above will 'stop ui' again
# before launching Chrome with ARC enabled).
script = 'stop ui'
script += '&& find /tmp/ -mindepth 1 -delete '
script += '&& start ui'
self._host.run(script, ignore_status=False, verbose=False, timeout=120)
def reboot(self, exc_type=None, exc_value=None, traceback=None):
"""Reboot the machine.
@param exc_type: Exception type if an exception is raised from the
with-block.
@param exc_value: Exception instance if an exception is raised from
the with-block.
@param traceback: Stack trace info if an exception is raised from
the with-block.
@return None, indicating not to ignore an exception from the with-block
if raised.
"""
logging.info('Rebooting...')
try:
self._host.reboot()
self._need_reboot = False
except Exception:
if exc_type is None:
raise
# If an exception is raise from the with-block, just record the
# exception for the rebooting to avoid ignoring the original
# exception.
logging.exception('Rebooting failed.')