test/py: use " for docstrings
[oweals/u-boot.git] / test / py / u_boot_console_base.py
index 520f9a9e9f311d1b66d022ae14016344ee4b0e0f..c500fb3ae5353a42eeb496eb698c4e19942e7adc 100644 (file)
@@ -14,6 +14,7 @@ import os
 import pytest
 import re
 import sys
 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]*)')
 
 # 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):
 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
     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
 
     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):
         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
     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):
 
     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.
 
 
         Can only usefully be called by sub-classes.
 
@@ -64,7 +65,7 @@ class ConsoleBase(object):
 
         Returns:
             Nothing.
 
         Returns:
             Nothing.
-        '''
+        """
 
         self.log = log
         self.config = config
 
         self.log = log
         self.config = config
@@ -85,10 +86,9 @@ class ConsoleBase(object):
 
         self.at_prompt = False
         self.at_prompt_logevt = None
 
         self.at_prompt = False
         self.at_prompt_logevt = None
-        self.ram_base = None
 
     def close(self):
 
     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
 
         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.
 
         Returns:
             Nothing.
-        '''
+        """
 
         if self.p:
             self.p.close()
 
         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):
 
     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.
 
 
         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.
                 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:
 
         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 = []
 
         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_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)
             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):
             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".
 
         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.
 
         Returns:
             Nothing.
-        '''
+        """
 
 
+        self.log.action('Sending Ctrl-C')
         self.run_command(chr(3), wait_for_echo=False, send_nl=False)
 
         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):
     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.
 
         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.
 
         Returns:
             Nothing.
-        '''
+        """
 
         if self.p:
             return
 
         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.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.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:
             if build_idx == -1:
-                self.u_boot_version_string = self.u_boot_main_signon
+                self.u_boot_version_string = signon
             else:
             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])
             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):
             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.
 
         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.
 
         Returns:
             Nothing.
-        '''
+        """
 
         try:
             if self.p:
 
         try:
             if self.p:
@@ -293,7 +342,7 @@ class ConsoleBase(object):
         self.p = None
 
     def validate_version_string_in_text(self, text):
         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.
 
         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.
 
         Returns:
             Nothing. An exception is raised if the validation fails.
-        '''
+        """
 
         assert(self.u_boot_version_string in text)
 
     def disable_check(self, check_type):
 
         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.
 
         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.
 
         Returns:
             A context manager object.
-        '''
+        """
 
         return ConsoleDisableCheck(self, check_type)
 
         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