1# Lint as: python2, python3
2# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import logging, os
7
8from autotest_lib.client.bin import utils
9from autotest_lib.client.common_lib import error
10from autotest_lib.client.cros import constants
11from autotest_lib.client.cros import cros_logging
12from autotest_lib.client.cros import cros_ui
13from autotest_lib.client.cros import cryptohome
14
15
16class CrashError(error.TestError):
17    """Error raised when a pertinent process crashes while waiting on
18    a condition.
19    """
20    pass
21
22
23class UnexpectedCondition(error.TestError):
24    """Error raised when an expected precondition is not met."""
25    pass
26
27
28def process_crashed(process, log_reader):
29    """Checks the log watched by |log_reader| to see if a crash was reported
30    for |process|.
31
32    @param process: process name to look for.
33    @param log_reader: LogReader object set up to watch appropriate log file.
34
35    @return: True if so, False if not.
36    """
37    return log_reader.can_find('Received crash notification for %s' % process)
38
39
40def wait_for_condition(condition, timeout_msg, timeout, process, crash_msg):
41    """Wait for callable |condition| to return true, while checking for crashes.
42
43    Poll for |condition| to become true, for |timeout| seconds. If the timeout
44    is reached, check to see if |process| crashed while we were polling.
45    If so, raise CrashError(crash_msg). If not, raise TimeoutError(timeout_msg).
46
47    @param condition: a callable to poll on.
48    @param timeout_msg: message to put in TimeoutError before raising.
49    @param timeout: float number of seconds to poll on |condition|.
50    @param process: process name to watch for crashes while polling.
51    @param crash_msg: message to put in CrashError if polling failed and
52                      |process| crashed.
53
54    @raise: TimeoutError if timeout is reached.
55    @raise: CrashError if process crashed and the condition never fired.
56    """
57    # Mark /var/log/messages now; we'll run through all subsequent log
58    # messages if we couldn't start chrome to see if the browser crashed.
59    log_reader = cros_logging.LogReader()
60    log_reader.set_start_by_current()
61    try:
62        utils.poll_for_condition(
63            condition,
64            utils.TimeoutError(timeout_msg),
65            timeout=timeout)
66    except utils.TimeoutError as e:
67        # We could fail faster if necessary, but it'd be more complicated.
68        if process_crashed(process, log_reader):
69            logging.error(crash_msg)
70            raise CrashError(crash_msg)
71        else:
72            raise e
73
74
75def wait_for_browser(timeout=cros_ui.RESTART_UI_TIMEOUT):
76    """Wait until a Chrome process is running.
77
78    @param timeout: float number of seconds to wait.
79
80    @raise: TimeoutError: Chrome didn't start before timeout.
81    """
82    wait_for_condition(
83        lambda: os.system('pgrep ^%s$ >/dev/null' % constants.BROWSER) == 0,
84        timeout_msg='Timed out waiting for Chrome to start',
85        timeout=timeout,
86        process=constants.BROWSER,
87        crash_msg='Chrome crashed while starting up.')
88
89
90def wait_for_browser_exit(crash_msg, timeout=cros_ui.RESTART_UI_TIMEOUT):
91    """Wait for the Chrome process to exit.
92
93    @param crash_msg: Error message to include if Chrome crashed.
94    @param timeout: float number of seconds to wait.
95
96    @return: True if Chrome exited; False otherwise.
97
98    @raise: CrashError: Chrome crashed while we were waiting.
99    """
100    try:
101      wait_for_condition(
102          lambda: os.system('pgrep ^%s$ >/dev/null' % constants.BROWSER) != 0,
103          timeout_msg='Timed out waiting for Chrome to exit',
104          timeout=timeout,
105          process=constants.BROWSER,
106          crash_msg=crash_msg)
107      return True
108    except utils.TimeoutError as e:
109      return False
110
111
112def wait_for_cryptohome(user, timeout=cros_ui.RESTART_UI_TIMEOUT):
113    """Wait until cryptohome is mounted.
114
115    @param user: the user whose cryptohome the caller wants to wait for.
116    @param timeout: float number of seconds to wait.
117
118    @raise: TimeoutError: cryptohome wasn't mounted before timeout
119    """
120    wait_for_condition(
121        condition=lambda: cryptohome.is_vault_mounted(user),
122        timeout_msg='Timed out waiting for cryptohome to be mounted',
123        timeout=timeout,
124        process='cryptohomed',
125        crash_msg='cryptohomed crashed during mount attempt')
126
127
128def wait_for_ownership(timeout=constants.DEFAULT_OWNERSHIP_TIMEOUT):
129    """Wait until device owner key file exists on disk.
130
131    @param timeout: float number of seconds to wait.
132
133    @raise: TimeoutError: file didn't appear before timeout.
134    """
135    if os.access(constants.OWNER_KEY_FILE, os.F_OK):
136        raise error.TestError('Device is already owned!')
137    wait_for_condition(
138        condition=lambda: os.access(constants.OWNER_KEY_FILE, os.F_OK),
139        timeout_msg='Timed out waiting for ownership',
140        timeout=timeout,
141        process=constants.BROWSER,
142        crash_msg='Chrome crashed before ownership could be taken.')
143