Browse Source

[1938] clarify and more rationalize the "only_new" ver of wait_for variants.

previously the semantics was a bit ambiguous and resulted in some
less predictable behavior. (and, non suprisingly, some usage cases
in features seem to be wrong about "wait for new").
JINMEI Tatuya 12 years ago
parent
commit
029681ebd0
2 changed files with 52 additions and 29 deletions
  1. 8 4
      tests/lettuce/features/terrain/steps.py
  2. 44 25
      tests/lettuce/features/terrain/terrain.py

+ 8 - 4
tests/lettuce/features/terrain/steps.py

@@ -37,8 +37,10 @@ def wait_for_stderr_message(step, times, new, process_name, message, not_message
     output.
     Parameter:
     times: Check for the string this many times.
-    new: (' new', optional): Only check the output printed since last time
-                             this step was used for this process.
+    new: (' new', optional): Only check the output from the process that has
+                             not been covered in previous calls to this
+                             function.  See RunningProcess._wait_for_output_str
+                             for details.
     process_name ('<name> stderr'): Name of the process to check the output of.
     message ('message <message>'): Output (part) to wait for.
     not_message ('not <message>'): Output (part) to wait for, and fail
@@ -60,8 +62,10 @@ def wait_for_stdout_message(step, times, new, process_name, message, not_message
     output.
     Parameter:
     times: Check for the string this many times.
-    new: (' new', optional): Only check the output printed since last time
-                             this step was used for this process.
+    new: (' new', optional): Only check the output from the process that has
+                             not been covered in previous calls to this
+                             function.  See RunningProcess._wait_for_output_str
+                             for details.
     process_name ('<name> stderr'): Name of the process to check the output of.
     message ('message <message>'): Output (part) to wait for, and succeed.
     not_message ('not <message>'): Output (part) to wait for, and fail

+ 44 - 25
tests/lettuce/features/terrain/terrain.py

@@ -114,6 +114,10 @@ class RunningProcess:
         self._create_filenames()
         self._start_process(args)
 
+        # used in _wait_for_output_str, map from (filename, (strings))
+        # to a file offset.
+        self.__file_offsets = {}
+
     def _start_process(self, args):
         """
         Start the process.
@@ -194,11 +198,29 @@ class RunningProcess:
         os.remove(self.stderr_filename)
         os.remove(self.stdout_filename)
 
-    def _wait_for_output_str(self, filename, running_file, strings, only_new, matches = 1):
-        """
-        Wait for a line of output in this process. This will (if only_new is
-        False) first check all previous output from the process, and if not
-        found, check all output since the last time this method was called.
+    def _wait_for_output_str(self, filename, running_file, strings, only_new,
+                             matches=1):
+        """
+        Wait for a line of output in this process. This will (if
+        only_new is False) check all output from the process including
+        that may have been checked before.  If only_new is True, it
+        only checks output that has not been covered in previous calls
+        to this method for the file (if there was no such previous call to
+        this method, it works same as the case of only_new=False).
+
+        Care should be taken if only_new is to be set to True, as it may cause
+        counter-intuitive results.  For example, assume the file is expected
+        to contain a line that has XXX and another line has YYY, but the
+        ordering is not predictable.  If this method is called with XXX as
+        the search string, but the line containing YYY appears before the
+        target line, this method remembers the point in the file beyond
+        the line that has XXX.  If a next call to this method specifies
+        YYY as the search string with only_new being True, the search will
+        fail.  If the same string is expected to appear multiple times
+        and you want to catch the latest one, a more reliable way is to
+        specify the match number and set only_new to False, if the number
+        of matches is predictable.
+
         For each line in the output, the given strings array is checked. If
         any output lines checked contains one of the strings in the strings
         array, that string (not the line!) is returned.
@@ -206,33 +228,34 @@ class RunningProcess:
         filename: The filename to read previous output from, if applicable.
         running_file: The open file to read new output from.
         strings: Array of strings to look for.
-        only_new: If true, only check output since last time this method was
-                  called. If false, first check earlier output.
+        only_new: See above.
         matches: Check for the string this many times.
         Returns a tuple containing the matched string, and the complete line
         it was found in.
         Fails if none of the strings was read after 10 seconds
         (OUTPUT_WAIT_INTERVAL * OUTPUT_WAIT_MAX_INTERVALS).
         """
+        # Identify the start offset of search.  if only_new=True, start from
+        # the farthest point we've reached in the file; otherwise start from
+        # the beginning.
+        if not filename in self.__file_offsets:
+            self.__file_offsets[filename] = 0
+        offset = self.__file_offsets[filename] if only_new else 0
+        running_file.seek(offset)
+
         match_count = 0
-        if not only_new:
-            full_file = open(filename, "r")
-            for line in full_file:
-                for string in strings:
-                    if line.find(string) != -1:
-                        match_count += 1
-                        if match_count >= matches:
-                            full_file.close()
-                            return (string, line)
         wait_count = 0
         while wait_count < OUTPUT_WAIT_MAX_INTERVALS:
-            where = running_file.tell()
             line = running_file.readline()
+            where = running_file.tell()
             if line:
                 for string in strings:
                     if line.find(string) != -1:
                         match_count += 1
                         if match_count >= matches:
+                            # If we've gone further, update the recorded offset
+                            if where > self.__file_offsets[filename]:
+                                self.__file_offsets[filename] = where
                             return (string, line)
             else:
                 wait_count += 1
@@ -245,8 +268,7 @@ class RunningProcess:
         Wait for one of the given strings in this process's stderr output.
         Parameters:
         strings: Array of strings to look for.
-        only_new: If true, only check output since last time this method was
-                  called. If false, first check earlier output.
+        only_new: See _wait_for_output_str.
         matches: Check for the string this many times.
         Returns a tuple containing the matched string, and the complete line
         it was found in.
@@ -261,8 +283,7 @@ class RunningProcess:
         Wait for one of the given strings in this process's stdout output.
         Parameters:
         strings: Array of strings to look for.
-        only_new: If true, only check output since last time this method was
-                  called. If false, first check earlier output.
+        only_new: See _wait_for_output_str.
         matches: Check for the string this many times.
         Returns a tuple containing the matched string, and the complete line
         it was found in.
@@ -342,8 +363,7 @@ class RunningProcesses:
         Parameters:
         process_name: The name of the process to check the stderr output of.
         strings: Array of strings to look for.
-        only_new: If true, only check output since last time this method was
-                  called. If false, first check earlier output.
+        only_new: See _wait_for_output_str.
         matches: Check for the string this many times.
         Returns the matched string.
         Fails if none of the strings was read after 10 seconds
@@ -362,8 +382,7 @@ class RunningProcesses:
         Parameters:
         process_name: The name of the process to check the stdout output of.
         strings: Array of strings to look for.
-        only_new: If true, only check output since last time this method was
-                  called. If false, first check earlier output.
+        only_new: See _wait_for_output_str.
         matches: Check for the string this many times.
         Returns the matched string.
         Fails if none of the strings was read after 10 seconds