test/py: check for bad patterns everywhere we wait
[platform/kernel/u-boot.git] / test / py / u_boot_console_base.py
index 51163bc..71a00e8 100644 (file)
@@ -23,13 +23,24 @@ pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
 pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
 pattern_error_notification = re.compile('## Error: ')
 
 pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
 pattern_error_notification = re.compile('## Error: ')
 
+PAT_ID = 0
+PAT_RE = 1
+
+bad_pattern_defs = (
+    ('spl_signon', pattern_u_boot_spl_signon),
+    ('main_signon', pattern_u_boot_main_signon),
+    ('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
+    ('unknown_command', pattern_unknown_command),
+    ('error_notification', pattern_error_notification),
+)
+
 class ConsoleDisableCheck(object):
 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
@@ -37,18 +48,20 @@ class ConsoleDisableCheck(object):
 
     def __enter__(self):
         self.console.disable_check_count[self.check_type] += 1
 
     def __enter__(self):
         self.console.disable_check_count[self.check_type] += 1
+        self.console.eval_bad_patterns()
 
     def __exit__(self, extype, value, traceback):
         self.console.disable_check_count[self.check_type] -= 1
 
     def __exit__(self, extype, value, traceback):
         self.console.disable_check_count[self.check_type] -= 1
+        self.console.eval_bad_patterns()
 
 class ConsoleBase(object):
 
 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.
 
@@ -65,7 +78,7 @@ class ConsoleBase(object):
 
         Returns:
             Nothing.
 
         Returns:
             Nothing.
-        '''
+        """
 
         self.log = log
         self.config = config
 
         self.log = log
         self.config = config
@@ -77,18 +90,20 @@ class ConsoleBase(object):
         self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
         self.prompt_escaped = re.escape(self.prompt)
         self.p = None
         self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
         self.prompt_escaped = re.escape(self.prompt)
         self.p = None
-        self.disable_check_count = {
-            'spl_signon': 0,
-            'main_signon': 0,
-            'unknown_command': 0,
-            'error_notification': 0,
-        }
+        self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
+        self.eval_bad_patterns()
 
         self.at_prompt = False
         self.at_prompt_logevt = None
 
 
         self.at_prompt = False
         self.at_prompt_logevt = None
 
+    def eval_bad_patterns(self):
+        self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
+            if self.disable_check_count[pat[PAT_ID]] == 0]
+        self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
+            if self.disable_check_count[pat[PAT_ID]] == 0]
+
     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 +114,7 @@ class ConsoleBase(object):
 
         Returns:
             Nothing.
 
         Returns:
             Nothing.
-        '''
+        """
 
         if self.p:
             self.p.close()
 
         if self.p:
             self.p.close()
@@ -107,7 +122,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,27 +157,12 @@ 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.
-        '''
+        """
 
         if self.at_prompt and \
                 self.at_prompt_logevt != self.logstream.logfile.cur_evt:
             self.logstream.write(self.prompt, implicit=True)
 
 
         if self.at_prompt and \
                 self.at_prompt_logevt != self.logstream.logfile.cur_evt:
             self.logstream.write(self.prompt, implicit=True)
 
-        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)
-            bad_pattern_ids.append('SPL signon')
-        if self.disable_check_count['main_signon'] == 0:
-            bad_patterns.append(self.u_boot_main_signon_escaped)
-            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('Unknown command')
-        if self.disable_check_count['error_notification'] == 0:
-            bad_patterns.append(pattern_error_notification)
-            bad_pattern_ids.append('Error notification')
         try:
             self.at_prompt = False
             if send_nl:
         try:
             self.at_prompt = False
             if send_nl:
@@ -176,18 +176,18 @@ class ConsoleBase(object):
                     continue
                 chunk = re.escape(chunk)
                 chunk = chunk.replace('\\\n', '[\r\n]')
                     continue
                 chunk = re.escape(chunk)
                 chunk = chunk.replace('\\\n', '[\r\n]')
-                m = self.p.expect([chunk] + bad_patterns)
+                m = self.p.expect([chunk] + self.bad_patterns)
                 if m != 0:
                     self.at_prompt = False
                     raise Exception('Bad pattern found on console: ' +
                 if m != 0:
                     self.at_prompt = False
                     raise Exception('Bad pattern found on console: ' +
-                                    bad_pattern_ids[m - 1])
+                                    self.bad_pattern_ids[m - 1])
             if not wait_for_prompt:
                 return
             if not wait_for_prompt:
                 return
-            m = self.p.expect([self.prompt_escaped] + bad_patterns)
+            m = self.p.expect([self.prompt_escaped] + self.bad_patterns)
             if m != 0:
                 self.at_prompt = False
                 raise Exception('Bad pattern found on console: ' +
             if m != 0:
                 self.at_prompt = False
                 raise Exception('Bad pattern found on console: ' +
-                                bad_pattern_ids[m - 1])
+                                self.bad_pattern_ids[m - 1])
             self.at_prompt = True
             self.at_prompt_logevt = self.logstream.logfile.cur_evt
             # Only strip \r\n; space/TAB might be significant if testing
             self.at_prompt = True
             self.at_prompt_logevt = self.logstream.logfile.cur_evt
             # Only strip \r\n; space/TAB might be significant if testing
@@ -199,7 +199,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".
@@ -209,13 +209,13 @@ 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)
 
     def wait_for(self, text):
 
         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.
+        """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
 
         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
@@ -227,14 +227,17 @@ class ConsoleBase(object):
 
         Returns:
             Nothing.
 
         Returns:
             Nothing.
-        '''
+        """
 
         if type(text) == type(''):
             text = re.escape(text)
 
         if type(text) == type(''):
             text = re.escape(text)
-        self.p.expect([text])
+        m = self.p.expect([text] + self.bad_patterns)
+        if m != 0:
+            raise Exception('Bad pattern found on console: ' +
+                            self.bad_pattern_ids[m - 1])
 
     def drain_console(self):
 
     def drain_console(self):
-        '''Read from and log the U-Boot console for a short time.
+        """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
 
         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
@@ -249,7 +252,7 @@ class ConsoleBase(object):
 
         Returns:
             Nothing.
 
         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
 
         # 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
@@ -271,7 +274,7 @@ class ConsoleBase(object):
             self.p.timeout = orig_timeout
 
     def ensure_spawned(self):
             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.
 
         This may require spawning a new Sandbox process or resetting target
         hardware, as defined by the implementation sub-class.
@@ -283,7 +286,7 @@ class ConsoleBase(object):
 
         Returns:
             Nothing.
 
         Returns:
             Nothing.
-        '''
+        """
 
         if self.p:
             return
 
         if self.p:
             return
@@ -298,26 +301,30 @@ class ConsoleBase(object):
             self.p.timeout = 30000
             self.p.logfile_read = self.logstream
             if self.config.buildconfig.get('CONFIG_SPL', False) == 'y':
             self.p.timeout = 30000
             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:')
+                m = self.p.expect([pattern_u_boot_spl_signon] + self.bad_patterns)
+                if m != 0:
+                    raise Exception('Bad pattern found on console: ' +
+                                    self.bad_pattern_ids[m - 1])
+            m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns)
+            if m != 0:
+                raise Exception('Bad pattern found on console: ' +
+                                self.bad_pattern_ids[m - 1])
+            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:
             while True:
-                match = self.p.expect([self.prompt_escaped,
-                                       pattern_stop_autoboot_prompt])
-                if match == 1:
+                m = self.p.expect([self.prompt_escaped,
+                    pattern_stop_autoboot_prompt] + self.bad_patterns)
+                if m == 0:
+                    break
+                if m == 1:
                     self.p.send(chr(3)) # CTRL-C
                     continue
                     self.p.send(chr(3)) # CTRL-C
                     continue
-                break
+                raise Exception('Bad pattern found on console: ' +
+                                self.bad_pattern_ids[m - 2])
             self.at_prompt = True
             self.at_prompt_logevt = self.logstream.logfile.cur_evt
         except Exception as ex:
             self.at_prompt = True
             self.at_prompt_logevt = self.logstream.logfile.cur_evt
         except Exception as ex:
@@ -326,7 +333,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.
@@ -338,7 +345,7 @@ class ConsoleBase(object):
 
         Returns:
             Nothing.
 
         Returns:
             Nothing.
-        '''
+        """
 
         try:
             if self.p:
 
         try:
             if self.p:
@@ -348,7 +355,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.
@@ -358,12 +365,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.
@@ -374,6 +381,6 @@ class ConsoleBase(object):
 
         Returns:
             A context manager object.
 
         Returns:
             A context manager object.
-        '''
+        """
 
         return ConsoleDisableCheck(self, check_type)
 
         return ConsoleDisableCheck(self, check_type)