| # Copyright (c) 2012 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 common, logging, os, time |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.common_lib import utils |
| from autotest_lib.client.cros import constants |
| |
| # Log messages used to signal when we're restarting UI. Used to detect |
| # crashes by cros_ui_test.UITest. |
| UI_RESTART_ATTEMPT_MSG = 'cros_ui.py: Attempting StopSession...' |
| UI_RESTART_COMPLETE_MSG = 'cros_ui.py: StopSession complete.' |
| RESTART_UI_TIMEOUT = 90 # longer because we may be crash dumping now. |
| |
| |
| def get_chrome_session_ident(host=None): |
| """Return an identifier that changes whenever Chrome restarts. |
| |
| This function returns a value that is unique to the most |
| recently started Chrome process; the returned value changes |
| each time Chrome restarts and displays the login screen. The |
| change in the value can be used to detect a successful Chrome |
| restart. |
| |
| Note that uniqueness is only guaranteed until the host reboots. |
| |
| Args: |
| host: If not None, a host object on which to test Chrome |
| state, rather than running commands on the local host. |
| |
| """ |
| if host: |
| return host.run(constants.LOGIN_PROMPT_STATUS_COMMAND).stdout |
| return utils.run(constants.LOGIN_PROMPT_STATUS_COMMAND).stdout |
| |
| |
| def wait_for_chrome_ready(old_session, host=None, |
| timeout=RESTART_UI_TIMEOUT): |
| """Wait until a new Chrome login prompt is on screen and ready. |
| |
| The standard formula to check whether the prompt has appeared yet |
| is with a pattern like the following: |
| |
| session = get_chrome_session_ident() |
| logout() |
| wait_for_chrome_ready(session) |
| |
| Args: |
| old_session: identifier for the login prompt prior to |
| restarting Chrome. |
| host: If not None, a host object on which to test Chrome |
| state, rather than running commands on the local host. |
| timeout: float number of seconds to wait |
| |
| Raises: |
| TimeoutError: Login prompt didn't get up before timeout |
| |
| """ |
| utils.poll_for_condition( |
| condition=lambda: old_session != get_chrome_session_ident(host), |
| exception=utils.TimeoutError('Timed out waiting for login prompt'), |
| timeout=timeout, sleep_interval=1.0) |
| |
| |
| def stop_and_wait_for_chrome_to_exit(timeout_secs=40): |
| """Stops the UI and waits for chrome to exit. |
| |
| Stops the UI and waits for all chrome processes to exit or until |
| timeout_secs is reached. |
| |
| Args: |
| timeout_secs: float number of seconds to wait. |
| |
| Returns: |
| True upon successfully stopping the UI and all chrome processes exiting. |
| False otherwise. |
| """ |
| status = stop(allow_fail=True) |
| if status: |
| logging.error('stop ui returned non-zero status: %s', status) |
| return False |
| start_time = time.time() |
| while time.time() - start_time < timeout_secs: |
| status = utils.system('pgrep chrome', ignore_status=True) |
| if status == 1: return True |
| time.sleep(1) |
| logging.error('stop ui failed to stop chrome within %s seconds', |
| timeout_secs) |
| return False |
| |
| |
| def stop(allow_fail=False): |
| return utils.stop_service("ui", ignore_status=allow_fail) |
| |
| |
| def start(allow_fail=False, wait_for_login_prompt=True): |
| """Start the login manager and wait for the prompt to show up.""" |
| session = get_chrome_session_ident() |
| result = utils.start_service("ui", ignore_status=allow_fail) |
| # If allow_fail is set, the caller might be calling us when the UI job |
| # is already running. In that case, the above command fails. |
| if result == 0 and wait_for_login_prompt: |
| wait_for_chrome_ready(session) |
| return result |
| |
| |
| def restart(report_stop_failure=False): |
| """Restart the session manager. |
| |
| - If the user is logged in, the session will be terminated. |
| - If the UI is currently down, just go ahead and bring it up unless the |
| caller has requested that a failure to stop be reported. |
| - To ensure all processes are up and ready, this function will wait |
| for the login prompt to show up and be marked as visible. |
| |
| @param report_stop_failure: False by default, set to True if you care about |
| the UI being up at the time of call and |
| successfully torn down by this call. |
| """ |
| session = get_chrome_session_ident() |
| |
| # Log what we're about to do to /var/log/messages. Used to log crashes later |
| # in cleanup by cros_ui_test.UITest. |
| utils.system('logger "%s"' % UI_RESTART_ATTEMPT_MSG) |
| |
| try: |
| if stop(allow_fail=not report_stop_failure) != 0: |
| raise error.TestError('Could not stop session') |
| start(wait_for_login_prompt=False) |
| # Wait for login prompt to appear to indicate that all processes are |
| # up and running again. |
| wait_for_chrome_ready(session) |
| finally: |
| utils.system('logger "%s"' % UI_RESTART_COMPLETE_MSG) |
| |
| |
| def nuke(): |
| """Nuke the login manager, waiting for it to restart.""" |
| restart(lambda: utils.nuke_process_by_name(constants.SESSION_MANAGER)) |
| |
| |
| def is_up(): |
| """Return True if the UI is up, False if not.""" |
| return utils.get_service_pid('ui')!=0 |
| |
| |
| def clear_respawn_state(): |
| """Removes bookkeeping related to respawning crashed UI.""" |
| for filename in [constants.UI_RESPAWN_TIMESTAMPS_FILE, |
| constants.UI_TOO_CRASHY_TIMESTAMPS_FILE]: |
| try: |
| os.unlink(filename) |
| except OSError: |
| pass # It's already gone. |