changeset 892:3bfb7e985c47

svn verify: add a test for corrupt repositories. This case contains a couple of unlikely (but not impossible) failure cases that the code previously did not handle. The verifier is updated to address these, and the output made a bit more consistent.
author Dan Villiom Podlaski Christiansen <danchr@gmail.com>
date Wed, 14 Dec 2011 00:07:58 +0100
parents 7a98fbadcae9
children 295a8b48e4e2
files hgsubversion/svncommands.py tests/comprehensive/test_stupid_pull.py tests/comprehensive/test_verify_and_startrev.py tests/fixtures/correct.svndump tests/fixtures/corrupt.svndump tests/test_rebuildmeta.py tests/test_utility_commands.py
diffstat 7 files changed, 259 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/hgsubversion/svncommands.py
+++ b/hgsubversion/svncommands.py
@@ -66,24 +66,21 @@ def verify(ui, repo, args=None, **opts):
         except error.LookupError:
             result = 1
             continue
-        dmatch = fctx.data() == data
-        mmatch = fctx.flags() == mode
-        if not (dmatch and mmatch):
-            ui.write('difference in file %s\n' % fn)
+        if not fctx.data() == data:
+            ui.write('difference in: %s\n' % fn)
+            result = 1
+        if not fctx.flags() == mode:
+            ui.write('wrong flags for: %s\n' % fn)
             result = 1
 
     hgfiles = set(ctx) - util.ignoredfiles
     if hgfiles != svnfiles:
         unexpected = hgfiles - svnfiles
-        if unexpected:
-            ui.write('unexpected files:\n')
-            for f in sorted(unexpected):
-                ui.write('  %s\n' % f)
+        for f in sorted(unexpected):
+            ui.write('unexpected file: %s\n' % f)
         missing = svnfiles - hgfiles
-        if missing:
-            ui.write('missing files:\n')
-            for f in sorted(missing):
-                ui.write('  %s\n' % f)
+        for f in sorted(missing):
+            ui.write('missing file: %s\n' % f)
         result = 1
 
     util.progress(ui, 'verify', None)
--- a/tests/comprehensive/test_stupid_pull.py
+++ b/tests/comprehensive/test_stupid_pull.py
@@ -45,6 +45,8 @@ def buildmethod(case, name, layout):
 attrs = {'_do_case': _do_case,
          }
 for case in (f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')):
+    if case == 'corrupt.svndump':
+        continue
     name = 'test_' + case[:-len('.svndump')]
     # Automatic layout branchtag collision exposes a minor defect
     # here, but since it isn't a regression we suppress the test case.
--- a/tests/comprehensive/test_verify_and_startrev.py
+++ b/tests/comprehensive/test_verify_and_startrev.py
@@ -21,6 +21,8 @@ from hgsubversion import svncommands
     'binaryfiles.svndump',
     'binaryfiles-broken.svndump',
     'emptyrepo.svndump',
+    'correct.svndump',
+    'corrupt.svndump',
 ])
 
 _skipall = set([
@@ -29,6 +31,8 @@ from hgsubversion import svncommands
 
 _skipstandard = set([
     'subdir_is_file_prefix.svndump',
+    'correct.svndump',
+    'corrupt.svndump',
 ])
 
 def _do_case(self, name, stupid, layout):
new file mode 100644
--- /dev/null
+++ b/tests/fixtures/correct.svndump
@@ -0,0 +1,103 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 00000000-0000-0000-0000-000000000000
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2010-11-30T15:10:25.898546Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 100
+Content-length: 100
+
+K 7
+svn:log
+V 0
+
+K 10
+svn:author
+V 6
+danchr
+K 8
+svn:date
+V 27
+2010-11-30T15:16:01.077550Z
+PROPS-END
+
+Node-path: empty-file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: executable-file
+Node-kind: file
+Node-action: add
+Prop-content-length: 36
+Text-content-length: 11
+Text-content-md5: 01839ba8c81c3b2c7486607e0c683e62
+Text-content-sha1: 5e70f8a25fe8ad4ad971bfd3388c258b019268d4
+Content-length: 47
+
+K 14
+svn:executable
+V 1
+*
+PROPS-END
+Executable
+
+
+Node-path: regular-file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 10
+Text-content-md5: 2e01b7f4ab0c18c05a3059eb2e2420d9
+Text-content-sha1: 6e530e985be313a43dc9734251656be8f0c94ab8
+Content-length: 20
+
+PROPS-END
+Contents.
+
+
+Node-path: another-regular-file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 10
+Text-content-md5: 2e01b7f4ab0c18c05a3059eb2e2420d9
+Text-content-sha1: 6e530e985be313a43dc9734251656be8f0c94ab8
+Content-length: 20
+
+PROPS-END
+Contents.
+
+
+Node-path: symlink
+Node-kind: file
+Node-action: add
+Prop-content-length: 33
+Text-content-length: 6
+Text-content-md5: 654580f41818cd6f51408c7cbd313728
+Text-content-sha1: 130b8faaf3e1acc1b95f77ac835e9c8b6eee5c96
+Content-length: 39
+
+K 11
+svn:special
+V 1
+*
+PROPS-END
+link A
+
new file mode 100644
--- /dev/null
+++ b/tests/fixtures/corrupt.svndump
@@ -0,0 +1,97 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 00000000-0000-0000-0000-000000000000
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2010-11-30T15:10:25.898546Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 100
+Content-length: 100
+
+K 10
+svn:author
+V 6
+danchr
+K 8
+svn:date
+V 27
+2010-11-30T15:16:01.077550Z
+K 7
+svn:log
+V 0
+
+PROPS-END
+
+Node-path: another-regular-file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: executable-file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 11
+Text-content-md5: 01839ba8c81c3b2c7486607e0c683e62
+Text-content-sha1: 5e70f8a25fe8ad4ad971bfd3388c258b019268d4
+Content-length: 21
+
+PROPS-END
+Executable
+
+
+Node-path: missing-file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: regular-file
+Node-kind: file
+Node-action: add
+Prop-content-length: 33
+Text-content-length: 18
+Text-content-md5: adf66a0cec83e25644c63f3c3007ae7c
+Text-content-sha1: 047e6e482d0c9cb812f89d18a9f07a43caab76bb
+Content-length: 51
+
+K 11
+svn:special
+V 1
+*
+PROPS-END
+link Bad contents.
+
+Node-path: symlink
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 1
+Text-content-md5: 7fc56270e7a70fa81a5935b72eacbe29
+Text-content-sha1: 6dcd4ce23d88e2ee9568ba546c007c63d9131c1b
+Content-length: 11
+
+PROPS-END
+A
+
--- a/tests/test_rebuildmeta.py
+++ b/tests/test_rebuildmeta.py
@@ -101,11 +101,16 @@ def buildmethod(case, name, stupid, sing
     return m
 
 
+skip = set([
+    'project_root_not_repo_root.svndump',
+    'corrupt.svndump',
+])
+
 attrs = {'_do_case': _do_case,
          }
 for case in [f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')]:
     # this fixture results in an empty repository, don't use it
-    if case == 'project_root_not_repo_root.svndump':
+    if case in skip:
         continue
     bname = 'test_' + case[:-len('.svndump')]
     attrs[bname] = buildmethod(case, bname, False, False)
--- a/tests/test_utility_commands.py
+++ b/tests/test_utility_commands.py
@@ -260,13 +260,46 @@ class UtilityTests(test_util.TestBase):
         output = re.sub(r'file://\S+', 'file://', output)
         self.assertMultiLineEqual("""\
 verifying d51f46a715a1 against file://
-difference in file binary2
-unexpected files:
-  binary1
-missing files:
-  binary3
+difference in: binary2
+unexpected file: binary1
+missing file: binary3
 """, output)
 
+    def test_svnverify_corruption(self):
+        SUCCESS = 0
+        FAILURE = 1
+
+        repo, repo_path = self.load_and_fetch('correct.svndump', layout='single',
+                                              subdir='')
+
+        ui = self.ui()
+
+        self.assertEqual(SUCCESS, svncommands.verify(ui, self.repo, rev='tip'))
+
+        corrupt_source = test_util.fileurl(self.load_svndump('corrupt.svndump'))
+
+        repo.ui.setconfig('paths', 'default', corrupt_source)
+
+        ui.pushbuffer()
+        code = svncommands.verify(ui, repo, rev='tip')
+        actual = ui.popbuffer()
+
+        actual = actual.replace(corrupt_source, '$REPO')
+        actual = set(actual.splitlines())
+
+        expected = set([
+            'verifying 78e965230a13 against $REPO@1',
+            'missing file: missing-file',
+            'wrong flags for: executable-file',
+            'wrong flags for: symlink',
+            'wrong flags for: regular-file',
+            'difference in: another-regular-file',
+            'difference in: regular-file',
+            'unexpected file: empty-file',
+        ])
+
+        self.assertEqual((FAILURE, expected), (code, actual))
+
 def suite():
     all_tests = [unittest.TestLoader().loadTestsFromTestCase(UtilityTests),
           ]