You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2013/02/07 20:23:41 UTC

svn commit: r1443675 - in /subversion/trunk/subversion/tests/cmdline: changelist_tests.py externals_tests.py svntest/verify.py

Author: julianfoad
Date: Thu Feb  7 19:23:40 2013
New Revision: 1443675

URL: http://svn.apache.org/r1443675
Log:
Further clean up the ExpectedOutput classes in the test suite.

* subversion/tests/cmdline/svntest/verify.py
  (ExpectedOutput, RegexOutput): Write proper doc strings. Give each class
    its own __init__() method. Override the matches() method instead of
    delegating to an is_equivalent_list() method.
  (UnorderedOutput): The same, and don't support the 'match_all=False' case
    as we don't need it.
  (UnorderedRegexOutput): The same, and don't use multiple inheritance as we
    don't need it.

* subversion/tests/cmdline/externals_tests.py
  (pinned_externals): Make the argument to RegexOutput() a string not a
    list, for consistency with everywhere else, as it's now enforced.

* subversion/tests/cmdline/changelist_tests.py
  (tree_conflicts_and_changelists_on_commit): Use RegexOutput instead of
    UnorderedRegexOutput because that's all we need and UnorderedRegexOutput
    no longer supports this 'match any' usage.

Modified:
    subversion/trunk/subversion/tests/cmdline/changelist_tests.py
    subversion/trunk/subversion/tests/cmdline/externals_tests.py
    subversion/trunk/subversion/tests/cmdline/svntest/verify.py

Modified: subversion/trunk/subversion/tests/cmdline/changelist_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/changelist_tests.py?rev=1443675&r1=1443674&r2=1443675&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/changelist_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/changelist_tests.py Thu Feb  7 19:23:40 2013
@@ -927,24 +927,24 @@ def tree_conflicts_and_changelists_on_co
   # Remove it, warp back, add a prop, update.
   svntest.main.run_svn(None, 'delete', C)
 
-  expected_output = svntest.verify.UnorderedRegexOutput(
-                                     ["Deleting.*" + re.escape(C)],
+  expected_output = svntest.verify.RegexOutput(
+                                     "Deleting.*" + re.escape(C),
                                      False)
   svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'commit', '-m', 'delete A/C', C)
 
-  expected_output = svntest.verify.UnorderedRegexOutput(
+  expected_output = svntest.verify.RegexOutput(
                                      "A.*" + re.escape(C), False)
   svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'update', C, "-r1")
 
-  expected_output = svntest.verify.UnorderedRegexOutput(
+  expected_output = svntest.verify.RegexOutput(
                                      ".*'propname' set on '"
                                      + re.escape(C) + "'", False)
   svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'propset', 'propname', 'propval', C)
 
-  expected_output = svntest.verify.UnorderedRegexOutput(
+  expected_output = svntest.verify.RegexOutput(
                                      "   C " + re.escape(C), False)
   svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'update', wc_dir)

Modified: subversion/trunk/subversion/tests/cmdline/externals_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/externals_tests.py?rev=1443675&r1=1443674&r2=1443675&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/externals_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/externals_tests.py Thu Feb  7 19:23:40 2013
@@ -3131,9 +3131,9 @@ def pinned_externals(sbox):
 
   repo_X_mu = repo_url + '/X/mu'
 
-  expected_output = verify.RegexOutput([
+  expected_output = verify.RegexOutput(
     '^      1 jrandom            .* mu$'
-  ])
+  )
 
   svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'list', repo_X_mu, '-v')

Modified: subversion/trunk/subversion/tests/cmdline/svntest/verify.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/svntest/verify.py?rev=1443675&r1=1443674&r2=1443675&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/svntest/verify.py (original)
+++ subversion/trunk/subversion/tests/cmdline/svntest/verify.py Thu Feb  7 19:23:40 2013
@@ -97,15 +97,19 @@ def createExpectedOutput(expected, outpu
   return expected
 
 class ExpectedOutput(object):
-  """Matches an ordered list of lines."""
+  """Matches an ordered list of lines.
+
+     If MATCH_ALL is True, the expected lines must match the actual
+     lines, one-to-one, in the same order.  If MATCH_ALL is False, the
+     expected lines must match a subset of the actual lines, one-to-one,
+     in the same order, ignoring any other actual lines among the
+     matching ones.
+  """
 
   def __init__(self, expected, match_all=True):
     """Initialize the expected output to EXPECTED which is a string, or
-    a list of strings. If MATCH_ALL is True, the
-    expected strings will be matched with the actual strings, one-to-one, in
-    the same order. If False, they will be matched with a subset of the
-    actual strings, one-to-one, in the same order, ignoring any other actual
-    strings among the matching ones."""
+       a list of strings.
+    """
     assert expected is not None
     self.expected = expected
     self.match_all = match_all
@@ -118,7 +122,7 @@ class ExpectedOutput(object):
                     "see the 'matches()' method")
 
   def matches(self, actual):
-    """Return whether SELF.expected matches ACTUAL (which may be a list
+    """Return whether SELF matches ACTUAL (which may be a list
        of newline-terminated lines, or a single string).
     """
     assert actual is not None
@@ -128,10 +132,6 @@ class ExpectedOutput(object):
     if not isinstance(actual, list):
       actual = [actual]
 
-    return self.is_equivalent_list(expected, actual)
-
-  def is_equivalent_list(self, expected, actual):
-    "Return whether EXPECTED and ACTUAL are equivalent."
     if self.match_all:
       # The EXPECTED lines must match the ACTUAL lines, one-to-one, in
       # the same order.
@@ -149,18 +149,24 @@ class ExpectedOutput(object):
     return False
 
   def display_differences(self, message, label, actual):
-    """Delegate to the display_lines() routine with the appropriate
-    args.  MESSAGE is ignored if None."""
+    """Show the differences between the expected and ACTUAL lines. Print
+       MESSAGE unless it is None, the expected lines, the ACTUAL lines,
+       and a diff, all labeled with LABEL.
+    """
     display_lines(message, self.expected, actual, label, label)
     display_lines_diff(self.expected, actual, label, label)
 
 
 class AnyOutput(ExpectedOutput):
-  """Matches any non-empty output."""
+  """Matches any non-empty output.
+  """
+
   def __init__(self):
     ExpectedOutput.__init__(self, [], False)
 
-  def is_equivalent_list(self, ignored, actual):
+  def matches(self, actual):
+    assert actual is not None
+
     if len(actual) == 0:
       # No actual output. No match.
       return False
@@ -179,86 +185,105 @@ class AnyOutput(ExpectedOutput):
 
 
 class RegexOutput(ExpectedOutput):
-  """Matches an ordered list of regular expressions."""
+  """Matches a single regular expression.
 
-  def is_equivalent_list(self, expected, actual):
-    expected_re = expected[0]
-    # If we want to check that every line matches the regexp
-    # assume they all match and look for any that don't.  If
-    # only one line matching the regexp is enough, assume none
-    # match and look for even one that does.
-    if self.match_all:
-      all_lines_match_re = True
-    else:
-      all_lines_match_re = False
+     If MATCH_ALL is true, every actual line must match the RE.  If
+     MATCH_ALL is false, at least one actual line must match the RE.  In
+     any case, there must be at least one line of actual output.
+  """
+
+  def __init__(self, expected, match_all=True):
+    "EXPECTED is a regular expression string."
+    assert isinstance(expected, str)
+    ExpectedOutput.__init__(self, expected, match_all)
+    self.expected_re = re.compile(expected)
+
+  def matches(self, actual):
+    assert actual is not None
+
+    if not isinstance(actual, list):
+      actual = [actual]
 
     # If a regex was provided assume that we require some actual output.
     # Fail if we don't have any.
     if len(actual) == 0:
       return False
 
-    for actual_line in actual:
-      if self.match_all:
-        if not re.match(expected_re, actual_line):
-          return False
-      else:
-        # As soon an actual_line matches something, then we're good.
-        if re.match(expected_re, actual_line):
-          return True
-
-    return all_lines_match_re
+    if self.match_all:
+      return all(self.expected_re.match(line) for line in actual)
+    else:
+      return any(self.expected_re.match(line) for line in actual)
 
   def display_differences(self, message, label, actual):
     display_lines(message, self.expected, actual, label + ' (regexp)', label)
 
 
 class UnorderedOutput(ExpectedOutput):
-  """Matches an unordered list of lines."""
+  """Matches an unordered list of lines.
 
-  def is_equivalent_list(self, expected, actual):
-    # Disregard the order of ACTUAL lines during comparison.
-    e_set = set(expected)
-    a_set = set(actual)
+     After removing any duplicate actual lines, each expected regular
+     expression must match one actual line, one-to-one, in any order.
 
-    if self.match_all:
-      # All expected lines must be in the actual output.
-      return e_set == a_set
+     Note that duplicate lines, whether appearing together or separated,
+     are not significant in the expected lines or in the actual lines.
+
+        expected  actual
+        [A,B]     [B,B,A]   match
+        [A,B]     [B,B]     no match
+        [A,B]     [A,B,C]   no match
+        [A]       []        no match
+  """
+
+  def __init__(self, expected):
+    assert isinstance(expected, list)
+    ExpectedOutput.__init__(self, expected)
+
+  def matches(self, actual):
+    if not isinstance(actual, list):
+      actual = [actual]
 
-    # If any of the expected lines are in the actual output, then we match.
-    return len(e_set.intersection(a_set)) > 0
+    return set(self.expected) == set(actual)
 
   def display_differences(self, message, label, actual):
     display_lines(message, self.expected, actual, label + ' (unordered)', label)
     display_lines_diff(self.expected, actual, label + ' (unordered)', label)
 
 
-class UnorderedRegexOutput(UnorderedOutput, RegexOutput):
-  """Matches an unordered list of regular expressions."""
+class UnorderedRegexOutput(ExpectedOutput):
+  """Matches an unordered list of regular expressions.
+
+     After removing any duplicate actual lines, each expected regular
+     expression must match one actual line, one-to-one, in any order.
+     If multiple expressions match the same actual line, the behaviour
+     is undefined.
+  """
+
+  def __init__(self, expected):
+    assert isinstance(expected, list)
+    ExpectedOutput.__init__(self, expected)
+    # TODO: compile the regular expressions?
+
+  def matches(self, actual):
+    assert actual is not None
+    if not isinstance(actual, list):
+      actual = [actual]
 
-  def is_equivalent_list(self, expected, actual):
     # Disregard the order of ACTUAL lines during comparison.
-    e_set = set(expected)
+    e_set = set(self.expected)
     a_set = set(actual)
 
-    if self.match_all:
-      if len(e_set) != len(a_set):
-        return False
-      for expect_re in e_set:
-        for actual_line in a_set:
-          if re.match(expect_re, actual_line):
-            a_set.remove(actual_line)
-            break
-        else:
-          # One of the regexes was not found
-          return False
-      return True
-
-    # If any of the expected regexes are in the actual output, then we match.
-    for expect_re in e_set:
+    if len(e_set) != len(a_set):
+      return False
+    for e in e_set:
+      expect_re = re.compile(e)
       for actual_line in a_set:
-        if re.match(expect_re, actual_line):
-          return True
-    return False
+        if expect_re.match(actual_line):
+          a_set.remove(actual_line)
+          break
+      else:
+        # One of the regexes was not found
+        return False
+    return True
 
   def display_differences(self, message, label, actual):
     display_lines(message, self.expected, actual,