Source

hgsubversion-queue / verify-test.diff

Full commit
# HG changeset patch
# User Dan Villiom Podlaski Christiansen <danchr@gmail.com>
# Date 1323817678 -3600
# Node ID 0cc046e3b3af81cda55617c59573b9d5b51a27e1
# Parent  a2c6380cd301e741d3ad4b2203f5fb7867d439ce
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.

diff --git a/hgsubversion/svncommands.py b/hgsubversion/svncommands.py
--- a/hgsubversion/svncommands.py
+++ b/hgsubversion/svncommands.py
@@ -55,6 +55,10 @@ def verify(ui, repo, args=None, **opts):
         util.progress(ui, 'verify', i)
         if type != 'f':
             continue
+        elif fn not in ctx:
+            ui.write('extra file: %s\n' % fn)
+            result = 1
+            continue
         svnfiles.add(fn)
         fp = fn
         if branchpath:
@@ -65,16 +69,16 @@ 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:
-        missing = set(hgfiles).symmetric_difference(svnfiles)
-        ui.write('missing files: %s\n' % (', '.join(missing)))
+    missing = set(ctx) - util.ignoredfiles - svnfiles
+    for fn in missing:
+        ui.write('missing file: %s\n' % fn)
         result = 1
 
     return result
diff --git a/tests/comprehensive/test_stupid_pull.py b/tests/comprehensive/test_stupid_pull.py
--- 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.
diff --git a/tests/comprehensive/test_verify.py b/tests/comprehensive/test_verify.py
--- a/tests/comprehensive/test_verify.py
+++ b/tests/comprehensive/test_verify.py
@@ -36,6 +36,7 @@ skipall = set([
 ])
 skipstandard = set([
     'subdir_is_file_prefix.svndump',
+    'corrupt.svndump',
 ])
 
 attrs = {'_do_case': _do_case}
diff --git a/tests/fixtures/correct.svndump b/tests/fixtures/correct.svndump
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
+
diff --git a/tests/fixtures/corrupt.svndump b/tests/fixtures/corrupt.svndump
new file mode 100644
--- /dev/null
+++ b/tests/fixtures/corrupt.svndump
@@ -0,0 +1,88 @@
+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: extra-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
+Text-content-length: 11
+Content-length: 11
+
+Executable
+
+
+Node-path: regular-file
+Node-kind: file
+Node-action: add
+Prop-content-length: 59
+Text-content-length: 13
+Content-length: 72
+
+K 11
+svn:special
+V 1
+*
+K 14
+svn:executable
+V 1
+*
+PROPS-END
+Bad contents.
+
+
+Node-path: symlink
+Node-kind: file
+Node-action: add
+Text-content-length: 1
+Content-length: 1
+
+A
+
+
+Node-path: another-regular-file
+Node-kind: file
+Node-action: add
+Text-content-length: 0
+Content-length: 0
+
+
diff --git a/tests/run.py b/tests/run.py
--- a/tests/run.py
+++ b/tests/run.py
@@ -8,6 +8,7 @@ test_util.SkipTest = None
 
 def tests():
     import test_binaryfiles
+    import test_corruption
     import test_diff
     import test_externals
     import test_fetch_branches
diff --git a/tests/test_corruption.py b/tests/test_corruption.py
new file mode 100644
--- /dev/null
+++ b/tests/test_corruption.py
@@ -0,0 +1,51 @@
+import test_util
+
+import os, sys, cStringIO, difflib
+import unittest
+
+from mercurial import commands
+from mercurial import hg
+from mercurial import node
+from mercurial import ui
+
+from hgsubversion import svncommands
+
+class TestCorruption(test_util.TestBase):
+
+    def test_verify(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',
+            'extra file: extra-file',
+            'wrong flags for: executable-file',
+            'wrong flags for: symlink',
+            'wrong flags for: regular-file',
+            'difference in: another-regular-file',
+            'difference in: regular-file',
+            'missing file: empty-file',
+        ])
+
+        self.assertEqual((FAILURE, expected), (code, actual))
+
+def suite():
+    return unittest.TestLoader().loadTestsFromTestCase(TestCorruption)
diff --git a/tests/test_rebuildmeta.py b/tests/test_rebuildmeta.py
--- 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)
diff --git a/tests/test_startrev.py b/tests/test_startrev.py
--- a/tests/test_startrev.py
+++ b/tests/test_startrev.py
@@ -37,6 +37,8 @@ def buildmethod(case, name, subdir, stup
 nofiles = set([
     'binaryfiles.svndump',
     'emptyrepo.svndump',
+    'correct.svndump',
+    'corrupt.svndump',
 ])
 
 # these fixtures contain no files in trunk at HEAD and would result in an empty
@@ -45,6 +47,7 @@ subdirmap = {
     'commit-to-tag.svndump': '/branches/magic',
     'pushexternals.svndump': '',
     'tag_name_same_as_branch.svndump': '/branches/magic',
+    'subdir_is_file_prefix.svndump': '',
 }
 
 attrs = {'_do_case': _do_case,