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