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: .*" + ###################################################################### # 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) + 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