blob: 1db5da4c1e10c8c1214f97828ad13eae77f2057d [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0
Stephen Warren10e50632016-01-15 11:15:24 -07002# Copyright (c) 2015 Stephen Warren
3# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
Stephen Warren10e50632016-01-15 11:15:24 -07004
5# Common logic to interact with U-Boot via the console. This class provides
6# the interface that tests use to execute U-Boot shell commands and wait for
7# their results. Sub-classes exist to perform board-type-specific setup
8# operations, such as spawning a sub-process for Sandbox, or attaching to the
9# serial console of real hardware.
10
11import multiplexed_log
12import os
13import pytest
14import re
15import sys
Stephen Warren97a54662016-01-22 12:30:09 -070016import u_boot_spawn
Stephen Warren10e50632016-01-15 11:15:24 -070017
18# Regexes for text we expect U-Boot to send to the console.
Heiko Schocherce251232018-12-05 11:29:54 +010019pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))')
Yan Liu365a7532020-07-21 11:12:05 -040020pattern_u_boot_spl2_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))')
Stephen Warren5af83c42016-02-05 18:04:43 -070021pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}[^\r\n]*\\))')
Stephen Warren10e50632016-01-15 11:15:24 -070022pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
23pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
24pattern_error_notification = re.compile('## Error: ')
Stephen Warren3bd79d32016-01-27 23:57:50 -070025pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ###')
Stephen Warren10e50632016-01-15 11:15:24 -070026
Stephen Warren1115a972016-01-27 23:57:48 -070027PAT_ID = 0
28PAT_RE = 1
29
30bad_pattern_defs = (
31 ('spl_signon', pattern_u_boot_spl_signon),
Yan Liu365a7532020-07-21 11:12:05 -040032 ('spl2_signon', pattern_u_boot_spl2_signon),
Stephen Warren1115a972016-01-27 23:57:48 -070033 ('main_signon', pattern_u_boot_main_signon),
34 ('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
35 ('unknown_command', pattern_unknown_command),
36 ('error_notification', pattern_error_notification),
Stephen Warren3bd79d32016-01-27 23:57:50 -070037 ('error_please_reset', pattern_error_please_reset),
Stephen Warren1115a972016-01-27 23:57:48 -070038)
39
Stephen Warren10e50632016-01-15 11:15:24 -070040class ConsoleDisableCheck(object):
Stephen Warren75e731e2016-01-26 13:41:30 -070041 """Context manager (for Python's with statement) that temporarily disables
Stephen Warren10e50632016-01-15 11:15:24 -070042 the specified console output error check. This is useful when deliberately
43 executing a command that is known to trigger one of the error checks, in
44 order to test that the error condition is actually raised. This class is
45 used internally by ConsoleBase::disable_check(); it is not intended for
Stephen Warren75e731e2016-01-26 13:41:30 -070046 direct usage."""
Stephen Warren10e50632016-01-15 11:15:24 -070047
48 def __init__(self, console, check_type):
49 self.console = console
50 self.check_type = check_type
51
52 def __enter__(self):
53 self.console.disable_check_count[self.check_type] += 1
Stephen Warren1115a972016-01-27 23:57:48 -070054 self.console.eval_bad_patterns()
Stephen Warren10e50632016-01-15 11:15:24 -070055
56 def __exit__(self, extype, value, traceback):
57 self.console.disable_check_count[self.check_type] -= 1
Stephen Warren1115a972016-01-27 23:57:48 -070058 self.console.eval_bad_patterns()
Stephen Warren10e50632016-01-15 11:15:24 -070059
Michal Simek6b463182016-05-19 07:57:41 +020060class ConsoleSetupTimeout(object):
61 """Context manager (for Python's with statement) that temporarily sets up
62 timeout for specific command. This is useful when execution time is greater
63 then default 30s."""
64
65 def __init__(self, console, timeout):
66 self.p = console.p
67 self.orig_timeout = self.p.timeout
68 self.p.timeout = timeout
69
70 def __enter__(self):
71 return self
72
73 def __exit__(self, extype, value, traceback):
74 self.p.timeout = self.orig_timeout
75
Stephen Warren10e50632016-01-15 11:15:24 -070076class ConsoleBase(object):
Stephen Warren75e731e2016-01-26 13:41:30 -070077 """The interface through which test functions interact with the U-Boot
Stephen Warren10e50632016-01-15 11:15:24 -070078 console. This primarily involves executing shell commands, capturing their
79 results, and checking for common error conditions. Some common utilities
Stephen Warren75e731e2016-01-26 13:41:30 -070080 are also provided too."""
Stephen Warren10e50632016-01-15 11:15:24 -070081
82 def __init__(self, log, config, max_fifo_fill):
Stephen Warren75e731e2016-01-26 13:41:30 -070083 """Initialize a U-Boot console connection.
Stephen Warren10e50632016-01-15 11:15:24 -070084
85 Can only usefully be called by sub-classes.
86
87 Args:
88 log: A mulptiplex_log.Logfile object, to which the U-Boot output
89 will be logged.
90 config: A configuration data structure, as built by conftest.py.
91 max_fifo_fill: The maximum number of characters to send to U-Boot
92 command-line before waiting for U-Boot to echo the characters
93 back. For UART-based HW without HW flow control, this value
94 should be set less than the UART RX FIFO size to avoid
95 overflow, assuming that U-Boot can't keep up with full-rate
96 traffic at the baud rate.
97
98 Returns:
99 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700100 """
Stephen Warren10e50632016-01-15 11:15:24 -0700101
102 self.log = log
103 self.config = config
104 self.max_fifo_fill = max_fifo_fill
105
106 self.logstream = self.log.get_stream('console', sys.stdout)
107
108 # Array slice removes leading/trailing quotes
109 self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
Stephen Warren6d083402016-08-16 19:58:59 -0600110 self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE)
Stephen Warren10e50632016-01-15 11:15:24 -0700111 self.p = None
Stephen Warren1115a972016-01-27 23:57:48 -0700112 self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
113 self.eval_bad_patterns()
Stephen Warren10e50632016-01-15 11:15:24 -0700114
115 self.at_prompt = False
116 self.at_prompt_logevt = None
Stephen Warren10e50632016-01-15 11:15:24 -0700117
Stephen Warren1115a972016-01-27 23:57:48 -0700118 def eval_bad_patterns(self):
119 self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
120 if self.disable_check_count[pat[PAT_ID]] == 0]
121 self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
122 if self.disable_check_count[pat[PAT_ID]] == 0]
123
Stephen Warren10e50632016-01-15 11:15:24 -0700124 def close(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700125 """Terminate the connection to the U-Boot console.
Stephen Warren10e50632016-01-15 11:15:24 -0700126
127 This function is only useful once all interaction with U-Boot is
128 complete. Once this function is called, data cannot be sent to or
129 received from U-Boot.
130
131 Args:
132 None.
133
134 Returns:
135 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700136 """
Stephen Warren10e50632016-01-15 11:15:24 -0700137
138 if self.p:
139 self.p.close()
140 self.logstream.close()
141
142 def run_command(self, cmd, wait_for_echo=True, send_nl=True,
143 wait_for_prompt=True):
Stephen Warren75e731e2016-01-26 13:41:30 -0700144 """Execute a command via the U-Boot console.
Stephen Warren10e50632016-01-15 11:15:24 -0700145
146 The command is always sent to U-Boot.
147
148 U-Boot echoes any command back to its output, and this function
149 typically waits for that to occur. The wait can be disabled by setting
150 wait_for_echo=False, which is useful e.g. when sending CTRL-C to
151 interrupt a long-running command such as "ums".
152
153 Command execution is typically triggered by sending a newline
154 character. This can be disabled by setting send_nl=False, which is
155 also useful when sending CTRL-C.
156
157 This function typically waits for the command to finish executing, and
158 returns the console output that it generated. This can be disabled by
159 setting wait_for_prompt=False, which is useful when invoking a long-
160 running command such as "ums".
161
162 Args:
163 cmd: The command to send.
Heinrich Schuchardtbec160a2017-09-14 12:27:07 +0200164 wait_for_echo: Boolean indicating whether to wait for U-Boot to
Stephen Warren10e50632016-01-15 11:15:24 -0700165 echo the command text back to its output.
166 send_nl: Boolean indicating whether to send a newline character
167 after the command string.
168 wait_for_prompt: Boolean indicating whether to wait for the
169 command prompt to be sent by U-Boot. This typically occurs
170 immediately after the command has been executed.
171
172 Returns:
173 If wait_for_prompt == False:
174 Nothing.
175 Else:
176 The output from U-Boot during command execution. In other
177 words, the text U-Boot emitted between the point it echod the
178 command string and emitted the subsequent command prompts.
Stephen Warren75e731e2016-01-26 13:41:30 -0700179 """
Stephen Warren10e50632016-01-15 11:15:24 -0700180
Stephen Warren10e50632016-01-15 11:15:24 -0700181 if self.at_prompt and \
182 self.at_prompt_logevt != self.logstream.logfile.cur_evt:
183 self.logstream.write(self.prompt, implicit=True)
184
Stephen Warren10e50632016-01-15 11:15:24 -0700185 try:
186 self.at_prompt = False
187 if send_nl:
188 cmd += '\n'
189 while cmd:
190 # Limit max outstanding data, so UART FIFOs don't overflow
191 chunk = cmd[:self.max_fifo_fill]
192 cmd = cmd[self.max_fifo_fill:]
193 self.p.send(chunk)
194 if not wait_for_echo:
195 continue
196 chunk = re.escape(chunk)
197 chunk = chunk.replace('\\\n', '[\r\n]')
Stephen Warren1115a972016-01-27 23:57:48 -0700198 m = self.p.expect([chunk] + self.bad_patterns)
Stephen Warren10e50632016-01-15 11:15:24 -0700199 if m != 0:
200 self.at_prompt = False
201 raise Exception('Bad pattern found on console: ' +
Stephen Warren1115a972016-01-27 23:57:48 -0700202 self.bad_pattern_ids[m - 1])
Stephen Warren10e50632016-01-15 11:15:24 -0700203 if not wait_for_prompt:
204 return
Stephen Warren6d083402016-08-16 19:58:59 -0600205 m = self.p.expect([self.prompt_compiled] + self.bad_patterns)
Stephen Warren10e50632016-01-15 11:15:24 -0700206 if m != 0:
207 self.at_prompt = False
208 raise Exception('Bad pattern found on console: ' +
Stephen Warren1115a972016-01-27 23:57:48 -0700209 self.bad_pattern_ids[m - 1])
Stephen Warren10e50632016-01-15 11:15:24 -0700210 self.at_prompt = True
211 self.at_prompt_logevt = self.logstream.logfile.cur_evt
212 # Only strip \r\n; space/TAB might be significant if testing
213 # indentation.
214 return self.p.before.strip('\r\n')
215 except Exception as ex:
216 self.log.error(str(ex))
217 self.cleanup_spawn()
218 raise
Stephen Warrenb1c556a2017-10-27 11:04:08 -0600219 finally:
220 self.log.timestamp()
Stephen Warren10e50632016-01-15 11:15:24 -0700221
Simon Glass2436bb02016-07-03 09:40:42 -0600222 def run_command_list(self, cmds):
223 """Run a list of commands.
224
225 This is a helper function to call run_command() with default arguments
226 for each command in a list.
227
228 Args:
Simon Glassd5deca02016-07-31 17:35:04 -0600229 cmd: List of commands (each a string).
Simon Glass2436bb02016-07-03 09:40:42 -0600230 Returns:
Simon Glass2ca73112016-07-31 17:35:09 -0600231 A list of output strings from each command, one element for each
232 command.
Simon Glass2436bb02016-07-03 09:40:42 -0600233 """
Simon Glass2ca73112016-07-31 17:35:09 -0600234 output = []
Simon Glass2436bb02016-07-03 09:40:42 -0600235 for cmd in cmds:
Simon Glass2ca73112016-07-31 17:35:09 -0600236 output.append(self.run_command(cmd))
Simon Glass2436bb02016-07-03 09:40:42 -0600237 return output
238
Stephen Warren10e50632016-01-15 11:15:24 -0700239 def ctrlc(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700240 """Send a CTRL-C character to U-Boot.
Stephen Warren10e50632016-01-15 11:15:24 -0700241
242 This is useful in order to stop execution of long-running synchronous
243 commands such as "ums".
244
245 Args:
246 None.
247
248 Returns:
249 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700250 """
Stephen Warren10e50632016-01-15 11:15:24 -0700251
Stephen Warrena88c4172016-01-22 12:30:10 -0700252 self.log.action('Sending Ctrl-C')
Stephen Warren10e50632016-01-15 11:15:24 -0700253 self.run_command(chr(3), wait_for_echo=False, send_nl=False)
254
Stephen Warrenef824f52016-01-22 12:30:12 -0700255 def wait_for(self, text):
Stephen Warren75e731e2016-01-26 13:41:30 -0700256 """Wait for a pattern to be emitted by U-Boot.
Stephen Warrenef824f52016-01-22 12:30:12 -0700257
258 This is useful when a long-running command such as "dfu" is executing,
259 and it periodically emits some text that should show up at a specific
260 location in the log file.
261
262 Args:
263 text: The text to wait for; either a string (containing raw text,
264 not a regular expression) or an re object.
265
266 Returns:
267 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700268 """
Stephen Warrenef824f52016-01-22 12:30:12 -0700269
270 if type(text) == type(''):
271 text = re.escape(text)
Stephen Warren68a9bb62016-01-27 23:57:49 -0700272 m = self.p.expect([text] + self.bad_patterns)
273 if m != 0:
274 raise Exception('Bad pattern found on console: ' +
275 self.bad_pattern_ids[m - 1])
Stephen Warrenef824f52016-01-22 12:30:12 -0700276
Stephen Warren97a54662016-01-22 12:30:09 -0700277 def drain_console(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700278 """Read from and log the U-Boot console for a short time.
Stephen Warren97a54662016-01-22 12:30:09 -0700279
280 U-Boot's console output is only logged when the test code actively
281 waits for U-Boot to emit specific data. There are cases where tests
282 can fail without doing this. For example, if a test asks U-Boot to
283 enable USB device mode, then polls until a host-side device node
284 exists. In such a case, it is useful to log U-Boot's console output
285 in case U-Boot printed clues as to why the host-side even did not
286 occur. This function will do that.
287
288 Args:
289 None.
290
291 Returns:
292 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700293 """
Stephen Warren97a54662016-01-22 12:30:09 -0700294
295 # If we are already not connected to U-Boot, there's nothing to drain.
296 # This should only happen when a previous call to run_command() or
297 # wait_for() failed (and hence the output has already been logged), or
298 # the system is shutting down.
299 if not self.p:
300 return
301
302 orig_timeout = self.p.timeout
303 try:
304 # Drain the log for a relatively short time.
305 self.p.timeout = 1000
306 # Wait for something U-Boot will likely never send. This will
307 # cause the console output to be read and logged.
308 self.p.expect(['This should never match U-Boot output'])
Stephen Warren677b9cc2018-09-20 16:55:03 -0600309 except:
310 # We expect a timeout, since U-Boot won't print what we waited
311 # for. Squash it when it happens.
312 #
313 # Squash any other exception too. This function is only used to
314 # drain (and log) the U-Boot console output after a failed test.
315 # The U-Boot process will be restarted, or target board reset, once
316 # this function returns. So, we don't care about detecting any
317 # additional errors, so they're squashed so that the rest of the
318 # post-test-failure cleanup code can continue operation, and
319 # correctly terminate any log sections, etc.
Stephen Warren97a54662016-01-22 12:30:09 -0700320 pass
321 finally:
322 self.p.timeout = orig_timeout
323
Stephen Warren10e50632016-01-15 11:15:24 -0700324 def ensure_spawned(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700325 """Ensure a connection to a correctly running U-Boot instance.
Stephen Warren10e50632016-01-15 11:15:24 -0700326
327 This may require spawning a new Sandbox process or resetting target
328 hardware, as defined by the implementation sub-class.
329
330 This is an internal function and should not be called directly.
331
332 Args:
333 None.
334
335 Returns:
336 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700337 """
Stephen Warren10e50632016-01-15 11:15:24 -0700338
339 if self.p:
340 return
341 try:
Stephen Warren80eea632016-02-11 11:46:12 -0700342 self.log.start_section('Starting U-Boot')
Stephen Warren10e50632016-01-15 11:15:24 -0700343 self.at_prompt = False
Stephen Warren10e50632016-01-15 11:15:24 -0700344 self.p = self.get_spawn()
345 # Real targets can take a long time to scroll large amounts of
346 # text if LCD is enabled. This value may need tweaking in the
347 # future, possibly per-test to be optimal. This works for 'help'
348 # on board 'seaboard'.
Stephen Warren33db1ee2016-02-04 16:11:50 -0700349 if not self.config.gdbserver:
350 self.p.timeout = 30000
Stephen Warren10e50632016-01-15 11:15:24 -0700351 self.p.logfile_read = self.logstream
Heiko Schocher48d5a7e2016-02-17 18:32:51 +0100352 bcfg = self.config.buildconfig
353 config_spl = bcfg.get('config_spl', 'n') == 'y'
354 config_spl_serial_support = bcfg.get('config_spl_serial_support',
355 'n') == 'y'
Michal Simek777526a2016-02-25 14:58:24 +0100356 env_spl_skipped = self.config.env.get('env__spl_skipped',
357 False)
Yan Liu365a7532020-07-21 11:12:05 -0400358 env_spl2_skipped = self.config.env.get('env__spl2_skipped',
359 True)
Michal Simek777526a2016-02-25 14:58:24 +0100360 if config_spl and config_spl_serial_support and not env_spl_skipped:
Heiko Schocher48d5a7e2016-02-17 18:32:51 +0100361 m = self.p.expect([pattern_u_boot_spl_signon] +
362 self.bad_patterns)
Stephen Warren68a9bb62016-01-27 23:57:49 -0700363 if m != 0:
Simon Glassba83e942016-07-04 11:58:38 -0600364 raise Exception('Bad pattern found on SPL console: ' +
Stephen Warren68a9bb62016-01-27 23:57:49 -0700365 self.bad_pattern_ids[m - 1])
Yan Liu365a7532020-07-21 11:12:05 -0400366 if not env_spl2_skipped:
367 m = self.p.expect([pattern_u_boot_spl2_signon] +
368 self.bad_patterns)
369 if m != 0:
370 raise Exception('Bad pattern found on SPL2 console: ' +
371 self.bad_pattern_ids[m - 1])
Stephen Warren68a9bb62016-01-27 23:57:49 -0700372 m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns)
373 if m != 0:
374 raise Exception('Bad pattern found on console: ' +
375 self.bad_pattern_ids[m - 1])
Stephen Warren5af83c42016-02-05 18:04:43 -0700376 self.u_boot_version_string = self.p.after
Stephen Warren10e50632016-01-15 11:15:24 -0700377 while True:
Stephen Warren6d083402016-08-16 19:58:59 -0600378 m = self.p.expect([self.prompt_compiled,
Stephen Warren68a9bb62016-01-27 23:57:49 -0700379 pattern_stop_autoboot_prompt] + self.bad_patterns)
380 if m == 0:
381 break
382 if m == 1:
Stephen Warren850e3f72016-02-15 17:39:38 -0700383 self.p.send(' ')
Stephen Warren10e50632016-01-15 11:15:24 -0700384 continue
Stephen Warren68a9bb62016-01-27 23:57:49 -0700385 raise Exception('Bad pattern found on console: ' +
386 self.bad_pattern_ids[m - 2])
Stephen Warren10e50632016-01-15 11:15:24 -0700387 self.at_prompt = True
388 self.at_prompt_logevt = self.logstream.logfile.cur_evt
389 except Exception as ex:
390 self.log.error(str(ex))
391 self.cleanup_spawn()
392 raise
Stephen Warren80eea632016-02-11 11:46:12 -0700393 finally:
Stephen Warrenb1c556a2017-10-27 11:04:08 -0600394 self.log.timestamp()
Stephen Warren80eea632016-02-11 11:46:12 -0700395 self.log.end_section('Starting U-Boot')
Stephen Warren10e50632016-01-15 11:15:24 -0700396
397 def cleanup_spawn(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700398 """Shut down all interaction with the U-Boot instance.
Stephen Warren10e50632016-01-15 11:15:24 -0700399
400 This is used when an error is detected prior to re-establishing a
401 connection with a fresh U-Boot instance.
402
403 This is an internal function and should not be called directly.
404
405 Args:
406 None.
407
408 Returns:
409 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700410 """
Stephen Warren10e50632016-01-15 11:15:24 -0700411
412 try:
413 if self.p:
414 self.p.close()
415 except:
416 pass
417 self.p = None
418
Simon Glass37c2ce12016-07-31 17:35:08 -0600419 def restart_uboot(self):
420 """Shut down and restart U-Boot."""
421 self.cleanup_spawn()
422 self.ensure_spawned()
423
Simon Glass9bc20832016-07-04 11:58:39 -0600424 def get_spawn_output(self):
425 """Return the start-up output from U-Boot
426
427 Returns:
428 The output produced by ensure_spawed(), as a string.
429 """
430 if self.p:
431 return self.p.get_expect_output()
432 return None
433
Stephen Warren10e50632016-01-15 11:15:24 -0700434 def validate_version_string_in_text(self, text):
Stephen Warren75e731e2016-01-26 13:41:30 -0700435 """Assert that a command's output includes the U-Boot signon message.
Stephen Warren10e50632016-01-15 11:15:24 -0700436
437 This is primarily useful for validating the "version" command without
438 duplicating the signon text regex in a test function.
439
440 Args:
441 text: The command output text to check.
442
443 Returns:
444 Nothing. An exception is raised if the validation fails.
Stephen Warren75e731e2016-01-26 13:41:30 -0700445 """
Stephen Warren10e50632016-01-15 11:15:24 -0700446
447 assert(self.u_boot_version_string in text)
448
449 def disable_check(self, check_type):
Stephen Warren75e731e2016-01-26 13:41:30 -0700450 """Temporarily disable an error check of U-Boot's output.
Stephen Warren10e50632016-01-15 11:15:24 -0700451
452 Create a new context manager (for use with the "with" statement) which
453 temporarily disables a particular console output error check.
454
455 Args:
456 check_type: The type of error-check to disable. Valid values may
457 be found in self.disable_check_count above.
458
459 Returns:
460 A context manager object.
Stephen Warren75e731e2016-01-26 13:41:30 -0700461 """
Stephen Warren10e50632016-01-15 11:15:24 -0700462
463 return ConsoleDisableCheck(self, check_type)
Michal Simek6b463182016-05-19 07:57:41 +0200464
465 def temporary_timeout(self, timeout):
466 """Temporarily set up different timeout for commands.
467
468 Create a new context manager (for use with the "with" statement) which
469 temporarily change timeout.
470
471 Args:
472 timeout: Time in milliseconds.
473
474 Returns:
475 A context manager object.
476 """
477
478 return ConsoleSetupTimeout(self, timeout)