X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=test%2Fpy%2Fu_boot_console_base.py;h=c500fb3ae5353a42eeb496eb698c4e19942e7adc;hb=e8debf394fbba594fcfc267c61f8c6bbca395b06;hp=520f9a9e9f311d1b66d022ae14016344ee4b0e0f;hpb=6905f4d3c7be46fed4859f51f0a8f9a1107c22e7;p=oweals%2Fu-boot.git diff --git a/test/py/u_boot_console_base.py b/test/py/u_boot_console_base.py index 520f9a9e9f..c500fb3ae5 100644 --- a/test/py/u_boot_console_base.py +++ b/test/py/u_boot_console_base.py @@ -14,6 +14,7 @@ import os import pytest import re import sys +import u_boot_spawn # Regexes for text we expect U-Boot to send to the console. pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}-[^\r\n]*)') @@ -23,12 +24,12 @@ pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'') pattern_error_notification = re.compile('## Error: ') class ConsoleDisableCheck(object): - '''Context manager (for Python's with statement) that temporarily disables + """Context manager (for Python's with statement) that temporarily disables the specified console output error check. This is useful when deliberately executing a command that is known to trigger one of the error checks, in order to test that the error condition is actually raised. This class is used internally by ConsoleBase::disable_check(); it is not intended for - direct usage.''' + direct usage.""" def __init__(self, console, check_type): self.console = console @@ -41,13 +42,13 @@ class ConsoleDisableCheck(object): self.console.disable_check_count[self.check_type] -= 1 class ConsoleBase(object): - '''The interface through which test functions interact with the U-Boot + """The interface through which test functions interact with the U-Boot console. This primarily involves executing shell commands, capturing their results, and checking for common error conditions. Some common utilities - are also provided too.''' + are also provided too.""" def __init__(self, log, config, max_fifo_fill): - '''Initialize a U-Boot console connection. + """Initialize a U-Boot console connection. Can only usefully be called by sub-classes. @@ -64,7 +65,7 @@ class ConsoleBase(object): Returns: Nothing. - ''' + """ self.log = log self.config = config @@ -85,10 +86,9 @@ class ConsoleBase(object): self.at_prompt = False self.at_prompt_logevt = None - self.ram_base = None def close(self): - '''Terminate the connection to the U-Boot console. + """Terminate the connection to the U-Boot console. This function is only useful once all interaction with U-Boot is complete. Once this function is called, data cannot be sent to or @@ -99,7 +99,7 @@ class ConsoleBase(object): Returns: Nothing. - ''' + """ if self.p: self.p.close() @@ -107,7 +107,7 @@ class ConsoleBase(object): def run_command(self, cmd, wait_for_echo=True, send_nl=True, wait_for_prompt=True): - '''Execute a command via the U-Boot console. + """Execute a command via the U-Boot console. The command is always sent to U-Boot. @@ -142,9 +142,7 @@ class ConsoleBase(object): The output from U-Boot during command execution. In other words, the text U-Boot emitted between the point it echod the command string and emitted the subsequent command prompts. - ''' - - self.ensure_spawned() + """ if self.at_prompt and \ self.at_prompt_logevt != self.logstream.logfile.cur_evt: @@ -152,12 +150,11 @@ class ConsoleBase(object): bad_patterns = [] bad_pattern_ids = [] - if (self.disable_check_count['spl_signon'] == 0 and - self.u_boot_spl_signon): - bad_patterns.append(self.u_boot_spl_signon_escaped) + if (self.disable_check_count['spl_signon'] == 0): + bad_patterns.append(pattern_u_boot_spl_signon) bad_pattern_ids.append('SPL signon') if self.disable_check_count['main_signon'] == 0: - bad_patterns.append(self.u_boot_main_signon_escaped) + bad_patterns.append(pattern_u_boot_main_signon) bad_pattern_ids.append('U-Boot main signon') if self.disable_check_count['unknown_command'] == 0: bad_patterns.append(pattern_unknown_command) @@ -201,7 +198,7 @@ class ConsoleBase(object): raise def ctrlc(self): - '''Send a CTRL-C character to U-Boot. + """Send a CTRL-C character to U-Boot. This is useful in order to stop execution of long-running synchronous commands such as "ums". @@ -211,12 +208,69 @@ class ConsoleBase(object): Returns: Nothing. - ''' + """ + self.log.action('Sending Ctrl-C') self.run_command(chr(3), wait_for_echo=False, send_nl=False) + def wait_for(self, text): + """Wait for a pattern to be emitted by U-Boot. + + This is useful when a long-running command such as "dfu" is executing, + and it periodically emits some text that should show up at a specific + location in the log file. + + Args: + text: The text to wait for; either a string (containing raw text, + not a regular expression) or an re object. + + Returns: + Nothing. + """ + + if type(text) == type(''): + text = re.escape(text) + self.p.expect([text]) + + def drain_console(self): + """Read from and log the U-Boot console for a short time. + + U-Boot's console output is only logged when the test code actively + waits for U-Boot to emit specific data. There are cases where tests + can fail without doing this. For example, if a test asks U-Boot to + enable USB device mode, then polls until a host-side device node + exists. In such a case, it is useful to log U-Boot's console output + in case U-Boot printed clues as to why the host-side even did not + occur. This function will do that. + + Args: + None. + + Returns: + Nothing. + """ + + # If we are already not connected to U-Boot, there's nothing to drain. + # This should only happen when a previous call to run_command() or + # wait_for() failed (and hence the output has already been logged), or + # the system is shutting down. + if not self.p: + return + + orig_timeout = self.p.timeout + try: + # Drain the log for a relatively short time. + self.p.timeout = 1000 + # Wait for something U-Boot will likely never send. This will + # cause the console output to be read and logged. + self.p.expect(['This should never match U-Boot output']) + except u_boot_spawn.Timeout: + pass + finally: + self.p.timeout = orig_timeout + def ensure_spawned(self): - '''Ensure a connection to a correctly running U-Boot instance. + """Ensure a connection to a correctly running U-Boot instance. This may require spawning a new Sandbox process or resetting target hardware, as defined by the implementation sub-class. @@ -228,7 +282,7 @@ class ConsoleBase(object): Returns: Nothing. - ''' + """ if self.p: return @@ -244,18 +298,13 @@ class ConsoleBase(object): self.p.logfile_read = self.logstream if self.config.buildconfig.get('CONFIG_SPL', False) == 'y': self.p.expect([pattern_u_boot_spl_signon]) - self.u_boot_spl_signon = self.p.after - self.u_boot_spl_signon_escaped = re.escape(self.p.after) - else: - self.u_boot_spl_signon = None self.p.expect([pattern_u_boot_main_signon]) - self.u_boot_main_signon = self.p.after - self.u_boot_main_signon_escaped = re.escape(self.p.after) - build_idx = self.u_boot_main_signon.find(', Build:') + signon = self.p.after + build_idx = signon.find(', Build:') if build_idx == -1: - self.u_boot_version_string = self.u_boot_main_signon + self.u_boot_version_string = signon else: - self.u_boot_version_string = self.u_boot_main_signon[:build_idx] + self.u_boot_version_string = signon[:build_idx] while True: match = self.p.expect([self.prompt_escaped, pattern_stop_autoboot_prompt]) @@ -271,7 +320,7 @@ class ConsoleBase(object): raise def cleanup_spawn(self): - '''Shut down all interaction with the U-Boot instance. + """Shut down all interaction with the U-Boot instance. This is used when an error is detected prior to re-establishing a connection with a fresh U-Boot instance. @@ -283,7 +332,7 @@ class ConsoleBase(object): Returns: Nothing. - ''' + """ try: if self.p: @@ -293,7 +342,7 @@ class ConsoleBase(object): self.p = None def validate_version_string_in_text(self, text): - '''Assert that a command's output includes the U-Boot signon message. + """Assert that a command's output includes the U-Boot signon message. This is primarily useful for validating the "version" command without duplicating the signon text regex in a test function. @@ -303,12 +352,12 @@ class ConsoleBase(object): Returns: Nothing. An exception is raised if the validation fails. - ''' + """ assert(self.u_boot_version_string in text) def disable_check(self, check_type): - '''Temporarily disable an error check of U-Boot's output. + """Temporarily disable an error check of U-Boot's output. Create a new context manager (for use with the "with" statement) which temporarily disables a particular console output error check. @@ -319,42 +368,6 @@ class ConsoleBase(object): Returns: A context manager object. - ''' + """ return ConsoleDisableCheck(self, check_type) - - def find_ram_base(self): - '''Find the running U-Boot's RAM location. - - Probe the running U-Boot to determine the address of the first bank - of RAM. This is useful for tests that test reading/writing RAM, or - load/save files that aren't associated with some standard address - typically represented in an environment variable such as - ${kernel_addr_r}. The value is cached so that it only needs to be - actively read once. - - Args: - None. - - Returns: - The address of U-Boot's first RAM bank, as an integer. - ''' - - if self.config.buildconfig.get('config_cmd_bdi', 'n') != 'y': - pytest.skip('bdinfo command not supported') - if self.ram_base == -1: - pytest.skip('Previously failed to find RAM bank start') - if self.ram_base is not None: - return self.ram_base - - with self.log.section('find_ram_base'): - response = self.run_command('bdinfo') - for l in response.split('\n'): - if '-> start' in l: - self.ram_base = int(l.split('=')[1].strip(), 16) - break - if self.ram_base is None: - self.ram_base = -1 - raise Exception('Failed to find RAM bank start in `bdinfo`') - - return self.ram_base