Ramkumar Ramachandra wrote on Sun, Sep 19, 2010 at 17:53:12 +0530:
> Hi,
> 
> I thought this patch might need a little bit of special attention. Let
> me know quickly if something is obviously wrong with it- as far as I
> know, all tests pass and this patch works as expected. I'm going ahead
> and adding lots of tests and fixing whatever bugs there may be shortly.
> 
> [[[
> Add some testing infrastructure to exclude comparison of certain lines
> specified by a regular expression so that svnrdump dump tests can
> report actual failures. Exercise this infrastructure in
> svnrdump_tests.py. This is necessary because there is some mismatch
> between the headers that `svnadmin dump` and `svnrdump dump` are able
> to provide.
> 
> * subversion/tests/cmdline/svntest/verify.py
> 
>   (ExpectedOutput.matches_except): Write new function that resembling
>   ExpectedOutput.display_lines with an extra except_re argument. It
>   compares EXPECTED and ACTUAL on an exact line-by-line basis,
>   excluding lines that match the regular expression specified in
>   except_re.
> 
>   (ExceptedOutput.matches): Modify function to accept one more
>   optional argument: except_re. Call matches_except if except_re is
>   not None.
> 
>   (compare_and_display_lines): Take an extra argument except_re that
>   defaults to None, and call ExcpectedOutput.matches with this
>   argument.
> 
> * subversion/tests/cmdline/svnrdump_tests.py
> 
>   (mismatched_headers_re): Declare a new global string that specifies
>   the headers in which a mismatch is expected during a dumping
>   operation.
> 
>   (run_dump_test): Pass the above regular expression string to
>   svntest.verify.compare_and_display_lines).
> 
>   (test_list): Mark copy_and_modify_dump as a passing test.
> ]]]
> 
> Index: subversion/tests/cmdline/svnrdump_tests.py
> ===================================================================
> --- subversion/tests/cmdline/svnrdump_tests.py    (revision 998490)
> +++ subversion/tests/cmdline/svnrdump_tests.py    (working copy)
> @@ -41,6 +41,11 @@ XFail = svntest.testcase.XFail
>  Item = svntest.wc.StateItem
>  Wimp = svntest.testcase.Wimp
>  
> +# Mismatched headers during dumping operation
> +mismatched_headers_re = \
> +    "Prop-delta: |Text-content-sha1: |Text-copy-source-md5: |" \
> +    "Text-copy-source-sha1: |Text-delta-base-sha1: .*"
> +

So:

* ignore sha1 because RA doesn't provide it yet
* ignore Text-copy-source-md5... why?  for the same reason?
* ignore Prop-delta... why?

(It would be good to have the answer in a comment, I think.)

>  ######################################################################
>  # Helper routines
>  
> @@ -80,7 +85,8 @@ def run_dump_test(sbox, dumpfile_name):
>  
>    # Compare the output from stdout
>    svntest.verify.compare_and_display_lines(
> -    "Dump files", "DUMP", svnadmin_dumpfile, svnrdump_dumpfile)
> +    "Dump files", "DUMP", svnadmin_dumpfile, svnrdump_dumpfile,
> +    None, mismatched_headers_re)
>  
>  def run_load_test(sbox, dumpfile_name):
>    """Load a dumpfile using 'svnrdump load', dump it with 'svnadmin
> @@ -177,7 +183,7 @@ test_list = [ None,
>                revision_0_load,
>                skeleton_load,
>                copy_and_modify_load,
> -              Wimp("Need to fix headers in RA layer", copy_and_modify_dump),
> +              copy_and_modify_dump,
>               ]
>  
>  if __name__ == '__main__':
> Index: subversion/tests/cmdline/svntest/verify.py
> ===================================================================
> --- subversion/tests/cmdline/svntest/verify.py    (revision 998490)
> +++ subversion/tests/cmdline/svntest/verify.py    (working copy)
> @@ -108,7 +108,7 @@ class ExpectedOutput:
>    def __cmp__(self, other):
>      raise 'badness'
>  
> -  def matches(self, other):
> +  def matches(self, other, except_re=None):
>      """Return whether SELF.output matches OTHER (which may be a list
>      of newline-terminated lines, or a single string).  Either value
>      may be None."""
> @@ -126,8 +126,32 @@ class ExpectedOutput:
>      if not isinstance(expected, list):
>        expected = [expected]
>  
> -    return self.is_equivalent_list(expected, actual)
> +    if except_re:
> +      return self.matches_except(expected, actual, except_re)
> +    else:
> +      return self.is_equivalent_list(expected, actual)
>  

Couldn't you redefine one of these two functions (matches_except() and
is_equivalent_list()) such that they share more code?

For example:

    def matches_except(self, expected, actual, except_re):
        # TODO: possibly use a slightly different error message
        #       if an exception is thrown
        return self.is_equivalent_list(
                      filter(lambda x: not re.match(except_re, x), expected),
                      filter(lambda x: not re.match(except_re, x), actual))

... or add some sort of optional argument to is_equivalent_list(), such
that the functionalities of both functions can be achieved by varying
the value of the optional argument.

In second thought: you could make except_re a lambda expression
(taking a single argument, the line) instead of a regex; i.e.,

    s/foo('Text-sha1:')/foo(lambda line: 'Text-sha1' not in line)/

?

> +  def matches_except(self, expected, actual, except_re):
> +    "Return whether EXPECTED and ACTUAL match except for except_re."
> +    if not self.is_regex:
> +      i_expected = 0
> +      i_actual = 0
> +      while i_expected < len(expected) and i_actual < len(actual):
> +        if re.match(except_re, actual[i_actual]):
> +          i_actual += 1
> +        elif re.match(except_re, expected[i_expected]):
> +          i_expected += 1
> +        elif expected[i_expected] == actual[i_actual]:
> +          i_expected += 1
> +          i_actual += 1
> +        else:
> +          return False
> +      if i_expected == len(expected) and i_actual == len(actual):
> +            return True
> +      return False
> +    else:
> +      raise "self.is_regex and except_re are mutually exclusive"
> +
>    def is_equivalent_list(self, expected, actual):
>      "Return whether EXPECTED and ACTUAL are equivalent."
>      if not self.is_regex:
> @@ -308,7 +332,7 @@ def display_lines(message, label, expected, actual
>        sys.stdout.write(x)
>  
>  def compare_and_display_lines(message, label, expected, actual,
> -                              raisable=None):
> +                              raisable=None, except_re=None):
>    """Compare two sets of output lines, and print them if they differ,
>    preceded by MESSAGE iff not None.  EXPECTED may be an instance of
>    ExpectedOutput (and if not, it is wrapped as such).  RAISABLE is an
> @@ -325,7 +349,7 @@ def compare_and_display_lines(message, label, expe
>      actual = [actual]
>    actual = [line for line in actual if not line.startswith('DBG:')]
>  
> -  if not expected.matches(actual):
> +  if not expected.matches(actual, except_re):
>      expected.display_differences(message, label, actual)
>      raise raisable
>  

Reply via email to