xref: /aosp_15_r20/external/autotest/client/cros/cros_ui.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Liimport common, logging, os, time
6*9c5db199SXin Li
7*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
8*9c5db199SXin Lifrom autotest_lib.client.common_lib import utils
9*9c5db199SXin Lifrom autotest_lib.client.cros import constants
10*9c5db199SXin Li
11*9c5db199SXin Li# Log messages used to signal when we're restarting UI. Used to detect
12*9c5db199SXin Li# crashes by cros_ui_test.UITest.
13*9c5db199SXin LiUI_RESTART_ATTEMPT_MSG = 'cros_ui.py: Attempting StopSession...'
14*9c5db199SXin LiUI_RESTART_COMPLETE_MSG = 'cros_ui.py: StopSession complete.'
15*9c5db199SXin LiRESTART_UI_TIMEOUT = 90  # longer because we may be crash dumping now.
16*9c5db199SXin Li
17*9c5db199SXin Li
18*9c5db199SXin Lidef get_chrome_session_ident(host=None):
19*9c5db199SXin Li    """Return an identifier that changes whenever Chrome restarts.
20*9c5db199SXin Li
21*9c5db199SXin Li    This function returns a value that is unique to the most
22*9c5db199SXin Li    recently started Chrome process; the returned value changes
23*9c5db199SXin Li    each time Chrome restarts and displays the login screen.  The
24*9c5db199SXin Li    change in the value can be used to detect a successful Chrome
25*9c5db199SXin Li    restart.
26*9c5db199SXin Li
27*9c5db199SXin Li    Note that uniqueness is only guaranteed until the host reboots.
28*9c5db199SXin Li
29*9c5db199SXin Li    Args:
30*9c5db199SXin Li        host:  If not None, a host object on which to test Chrome
31*9c5db199SXin Li            state, rather than running commands on the local host.
32*9c5db199SXin Li
33*9c5db199SXin Li    """
34*9c5db199SXin Li    if host:
35*9c5db199SXin Li        return host.run(constants.LOGIN_PROMPT_STATUS_COMMAND).stdout
36*9c5db199SXin Li    return utils.run(constants.LOGIN_PROMPT_STATUS_COMMAND).stdout
37*9c5db199SXin Li
38*9c5db199SXin Li
39*9c5db199SXin Lidef wait_for_chrome_ready(old_session, host=None,
40*9c5db199SXin Li                          timeout=RESTART_UI_TIMEOUT):
41*9c5db199SXin Li    """Wait until a new Chrome login prompt is on screen and ready.
42*9c5db199SXin Li
43*9c5db199SXin Li    The standard formula to check whether the prompt has appeared yet
44*9c5db199SXin Li    is with a pattern like the following:
45*9c5db199SXin Li
46*9c5db199SXin Li       session = get_chrome_session_ident()
47*9c5db199SXin Li       logout()
48*9c5db199SXin Li       wait_for_chrome_ready(session)
49*9c5db199SXin Li
50*9c5db199SXin Li    Args:
51*9c5db199SXin Li        old_session:  identifier for the login prompt prior to
52*9c5db199SXin Li            restarting Chrome.
53*9c5db199SXin Li        host:  If not None, a host object on which to test Chrome
54*9c5db199SXin Li            state, rather than running commands on the local host.
55*9c5db199SXin Li        timeout: float number of seconds to wait
56*9c5db199SXin Li
57*9c5db199SXin Li    Raises:
58*9c5db199SXin Li        TimeoutError: Login prompt didn't get up before timeout
59*9c5db199SXin Li
60*9c5db199SXin Li    """
61*9c5db199SXin Li    utils.poll_for_condition(
62*9c5db199SXin Li        condition=lambda: old_session != get_chrome_session_ident(host),
63*9c5db199SXin Li        exception=utils.TimeoutError('Timed out waiting for login prompt'),
64*9c5db199SXin Li        timeout=timeout, sleep_interval=1.0)
65*9c5db199SXin Li
66*9c5db199SXin Li
67*9c5db199SXin Lidef stop_and_wait_for_chrome_to_exit(timeout_secs=40):
68*9c5db199SXin Li    """Stops the UI and waits for chrome to exit.
69*9c5db199SXin Li
70*9c5db199SXin Li    Stops the UI and waits for all chrome processes to exit or until
71*9c5db199SXin Li    timeout_secs is reached.
72*9c5db199SXin Li
73*9c5db199SXin Li    Args:
74*9c5db199SXin Li        timeout_secs: float number of seconds to wait.
75*9c5db199SXin Li
76*9c5db199SXin Li    Returns:
77*9c5db199SXin Li        True upon successfully stopping the UI and all chrome processes exiting.
78*9c5db199SXin Li        False otherwise.
79*9c5db199SXin Li    """
80*9c5db199SXin Li    status = stop(allow_fail=True)
81*9c5db199SXin Li    if status:
82*9c5db199SXin Li        logging.error('stop ui returned non-zero status: %s', status)
83*9c5db199SXin Li        return False
84*9c5db199SXin Li    start_time = time.time()
85*9c5db199SXin Li    while time.time() - start_time < timeout_secs:
86*9c5db199SXin Li        status = utils.system('pgrep chrome', ignore_status=True)
87*9c5db199SXin Li        if status == 1: return True
88*9c5db199SXin Li        time.sleep(1)
89*9c5db199SXin Li    logging.error('stop ui failed to stop chrome within %s seconds',
90*9c5db199SXin Li                  timeout_secs)
91*9c5db199SXin Li    return False
92*9c5db199SXin Li
93*9c5db199SXin Li
94*9c5db199SXin Lidef stop(allow_fail=False):
95*9c5db199SXin Li    return utils.stop_service("ui", ignore_status=allow_fail)
96*9c5db199SXin Li
97*9c5db199SXin Li
98*9c5db199SXin Lidef start(allow_fail=False, wait_for_login_prompt=True):
99*9c5db199SXin Li    """Start the login manager and wait for the prompt to show up."""
100*9c5db199SXin Li    session = get_chrome_session_ident()
101*9c5db199SXin Li    result = utils.start_service("ui", ignore_status=allow_fail)
102*9c5db199SXin Li    # If allow_fail is set, the caller might be calling us when the UI job
103*9c5db199SXin Li    # is already running. In that case, the above command fails.
104*9c5db199SXin Li    if result == 0 and wait_for_login_prompt:
105*9c5db199SXin Li        wait_for_chrome_ready(session)
106*9c5db199SXin Li    return result
107*9c5db199SXin Li
108*9c5db199SXin Li
109*9c5db199SXin Lidef restart(report_stop_failure=False):
110*9c5db199SXin Li    """Restart the session manager.
111*9c5db199SXin Li
112*9c5db199SXin Li    - If the user is logged in, the session will be terminated.
113*9c5db199SXin Li    - If the UI is currently down, just go ahead and bring it up unless the
114*9c5db199SXin Li      caller has requested that a failure to stop be reported.
115*9c5db199SXin Li    - To ensure all processes are up and ready, this function will wait
116*9c5db199SXin Li      for the login prompt to show up and be marked as visible.
117*9c5db199SXin Li
118*9c5db199SXin Li    @param report_stop_failure: False by default, set to True if you care about
119*9c5db199SXin Li                                the UI being up at the time of call and
120*9c5db199SXin Li                                successfully torn down by this call.
121*9c5db199SXin Li    """
122*9c5db199SXin Li    session = get_chrome_session_ident()
123*9c5db199SXin Li
124*9c5db199SXin Li    # Log what we're about to do to /var/log/messages. Used to log crashes later
125*9c5db199SXin Li    # in cleanup by cros_ui_test.UITest.
126*9c5db199SXin Li    utils.system('logger "%s"' % UI_RESTART_ATTEMPT_MSG)
127*9c5db199SXin Li
128*9c5db199SXin Li    try:
129*9c5db199SXin Li        if stop(allow_fail=not report_stop_failure) != 0:
130*9c5db199SXin Li            raise error.TestError('Could not stop session')
131*9c5db199SXin Li        start(wait_for_login_prompt=False)
132*9c5db199SXin Li        # Wait for login prompt to appear to indicate that all processes are
133*9c5db199SXin Li        # up and running again.
134*9c5db199SXin Li        wait_for_chrome_ready(session)
135*9c5db199SXin Li    finally:
136*9c5db199SXin Li        utils.system('logger "%s"' % UI_RESTART_COMPLETE_MSG)
137*9c5db199SXin Li
138*9c5db199SXin Li
139*9c5db199SXin Lidef nuke():
140*9c5db199SXin Li    """Nuke the login manager, waiting for it to restart."""
141*9c5db199SXin Li    restart(lambda: utils.nuke_process_by_name(constants.SESSION_MANAGER))
142*9c5db199SXin Li
143*9c5db199SXin Li
144*9c5db199SXin Lidef is_up():
145*9c5db199SXin Li    """Return True if the UI is up, False if not."""
146*9c5db199SXin Li    return utils.get_service_pid('ui')!=0
147*9c5db199SXin Li
148*9c5db199SXin Li
149*9c5db199SXin Lidef clear_respawn_state():
150*9c5db199SXin Li    """Removes bookkeeping related to respawning crashed UI."""
151*9c5db199SXin Li    for filename in [constants.UI_RESPAWN_TIMESTAMPS_FILE,
152*9c5db199SXin Li                     constants.UI_TOO_CRASHY_TIMESTAMPS_FILE]:
153*9c5db199SXin Li        try:
154*9c5db199SXin Li            os.unlink(filename)
155*9c5db199SXin Li        except OSError:
156*9c5db199SXin Li            pass  # It's already gone.
157