diff options
author | Qijiang Fan <fqj1994@gmail.com> | 2012-11-15 17:11:37 +0800 |
---|---|---|
committer | Qijiang Fan <fqj1994@gmail.com> | 2012-11-15 17:11:37 +0800 |
commit | 206ab31e9e0aae6866a27511724298dd00a7d90e (patch) | |
tree | 651928c31b80fee0b7cd4a6213e8c7642e615a7a /tests | |
parent | 41ab46d551a1287a4dfb60d04e132af468923d95 (diff) |
Upstream version 1.5
Diffstat (limited to 'tests')
39 files changed, 2560 insertions, 197 deletions
diff --git a/tests/comprehensive/test_stupid_pull.py b/tests/comprehensive/test_stupid_pull.py index 17e8122..fcc1ed6 100644 --- a/tests/comprehensive/test_stupid_pull.py +++ b/tests/comprehensive/test_stupid_pull.py @@ -45,7 +45,9 @@ 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')): - name = 'test_' + case[:-len('.svndump')] + if case == 'corrupt.svndump': + continue + name = 'test_' + case[:-len('.svndump')].replace('-', '_') # Automatic layout branchtag collision exposes a minor defect # here, but since it isn't a regression we suppress the test case. if case != 'branchtagcollision.svndump': diff --git a/tests/comprehensive/test_verify.py b/tests/comprehensive/test_verify.py deleted file mode 100644 index 65bdfa9..0000000 --- a/tests/comprehensive/test_verify.py +++ /dev/null @@ -1,55 +0,0 @@ -import os -import pickle -import sys -import unittest - -# wrapped in a try/except because of weirdness in how -# run.py works as compared to nose. -try: - import test_util -except ImportError: - sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) - import test_util - -from mercurial import hg -from mercurial import ui - -from hgsubversion import svncommands - -def _do_case(self, name, stupid, layout): - subdir = test_util.subdir.get(name, '') - repo = self._load_fixture_and_fetch(name, subdir=subdir, stupid=stupid, layout=layout) - assert len(self.repo) > 0 - for i in repo: - ctx = repo[i] - self.assertEqual(svncommands.verify(repo.ui, repo, rev=ctx.node()), 0) - -def buildmethod(case, name, stupid, layout): - m = lambda self: self._do_case(case, stupid, layout) - m.__name__ = name - bits = case, stupid and 'stupid' or 'real', layout - m.__doc__ = 'Test verify on %s with %s replay. (%s)' % bits - return m - -attrs = {'_do_case': _do_case} -fixtures = [f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')] -for case in fixtures: - # this fixture results in an empty repository, don't use it - if case == 'project_root_not_repo_root.svndump': - continue - bname = 'test_' + case[:-len('.svndump')] - attrs[bname] = buildmethod(case, bname, False, 'standard') - name = bname + '_stupid' - attrs[name] = buildmethod(case, name, True, 'standard') - name = bname + '_single' - attrs[name] = buildmethod(case, name, False, 'single') - # Disabled because the "stupid and real are the same" tests - # verify this plus even more. - # name = bname + '_single_stupid' - # attrs[name] = buildmethod(case, name, True, 'single') - -VerifyTests = type('VerifyTests', (test_util.TestBase,), attrs) - -def suite(): - all_tests = [unittest.TestLoader().loadTestsFromTestCase(VerifyTests)] - return unittest.TestSuite(all_tests) diff --git a/tests/comprehensive/test_verify_and_startrev.py b/tests/comprehensive/test_verify_and_startrev.py new file mode 100644 index 0000000..0834caf --- /dev/null +++ b/tests/comprehensive/test_verify_and_startrev.py @@ -0,0 +1,116 @@ +import os +import pickle +import sys +import unittest + +# wrapped in a try/except because of weirdness in how +# run.py works as compared to nose. +try: + import test_util +except ImportError: + sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) + import test_util + +from mercurial import hg +from mercurial import ui + +from hgsubversion import verify + +# these fixtures contain no files at HEAD and would result in empty clones +_skipshallow = set([ + 'binaryfiles.svndump', + 'binaryfiles-broken.svndump', + 'emptyrepo.svndump', + 'correct.svndump', + 'corrupt.svndump', +]) + +_skipall = set([ + 'project_root_not_repo_root.svndump', + 'movetotrunk.svndump', +]) + +_skipstandard = set([ + 'subdir_is_file_prefix.svndump', + 'correct.svndump', + 'corrupt.svndump', + 'emptyrepo2.svndump', +]) + +def _do_case(self, name, stupid, layout): + subdir = test_util.subdir.get(name, '') + repo, svnpath = self.load_and_fetch(name, subdir=subdir, stupid=stupid, + layout=layout) + assert len(self.repo) > 0 + for i in repo: + ctx = repo[i] + self.assertEqual(verify.verify(repo.ui, repo, rev=ctx.node(), + stupid=True), 0) + self.assertEqual(verify.verify(repo.ui, repo, rev=ctx.node(), + stupid=False), 0) + + # check a startrev clone + if layout == 'single' and name not in _skipshallow: + self.wc_path += '_shallow' + shallowrepo = self.fetch(svnpath, subdir=subdir, stupid=stupid, + layout='single', startrev='HEAD') + + self.assertEqual(len(shallowrepo), 1, + "shallow clone should have just one revision, not %d" + % len(shallowrepo)) + + fulltip = repo['tip'] + shallowtip = shallowrepo['tip'] + + repo.ui.pushbuffer() + self.assertEqual(0, verify.verify(repo.ui, shallowrepo, + rev=shallowtip.node(), + stupid=True)) + self.assertEqual(0, verify.verify(repo.ui, shallowrepo, + rev=shallowtip.node(), + stupid=False)) + + stupidui = ui.ui(repo.ui) + stupidui.config('hgsubversion', 'stupid', True) + self.assertEqual(verify.verify(stupidui, repo, rev=ctx.node(), + stupid=True), 0) + self.assertEqual(verify.verify(stupidui, repo, rev=ctx.node(), + stupid=False), 0) + + # viewing diff's of lists of files is easier on the eyes + self.assertMultiLineEqual('\n'.join(fulltip), '\n'.join(shallowtip), + repo.ui.popbuffer()) + + for f in fulltip: + self.assertMultiLineEqual(fulltip[f].data(), shallowtip[f].data()) + + +def buildmethod(case, name, stupid, layout): + m = lambda self: self._do_case(case, stupid, layout) + m.__name__ = name + bits = case, stupid and 'stupid' or 'real', layout + m.__doc__ = 'Test verify on %s with %s replay. (%s)' % bits + return m + +attrs = {'_do_case': _do_case} +fixtures = [f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')] +for case in fixtures: + if case in _skipall: + continue + bname = 'test_' + case[:-len('.svndump')] + if case not in _skipstandard: + attrs[bname] = buildmethod(case, bname, False, 'standard') + name = bname + '_stupid' + attrs[name] = buildmethod(case, name, True, 'standard') + name = bname + '_single' + attrs[name] = buildmethod(case, name, False, 'single') + # Disabled because the "stupid and real are the same" tests + # verify this plus even more. + # name = bname + '_single_stupid' + # attrs[name] = buildmethod(case, name, True, 'single') + +VerifyTests = type('VerifyTests', (test_util.TestBase,), attrs) + +def suite(): + all_tests = [unittest.TestLoader().loadTestsFromTestCase(VerifyTests)] + return unittest.TestSuite(all_tests) diff --git a/tests/fixtures/addspecial.sh b/tests/fixtures/addspecial.sh new file mode 100644 index 0000000..1d242bc --- /dev/null +++ b/tests/fixtures/addspecial.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +mkdir temp +cd temp + +svnadmin create repo +svn co file://`pwd`/repo wc +cd wc + +mkdir -p trunk branches +svn add trunk branches +svn ci -m'initial structure' +cd trunk +echo a>a +svn add a +svn ci -mci1 a +cd .. +svn up +svn cp trunk branches/foo +svn ci -m'branch foo' +cd branches/foo +ln -s a fnord +svn add fnord +svn ci -msymlink fnord +mkdir 'spacy name' +echo a > 'spacy name/spacy file' +svn add 'spacy name' +svn ci -mspacy 'spacy name' +svn up +echo b > 'spacy name/surprise ~' +svn add 'spacy name/surprise ~' +svn ci -mtilde 'spacy name' +svn up ../.. +echo foo > exe +chmod +x exe +svn add exe +svn ci -mexecutable exe +svn up ../.. +cd ../../trunk +svn merge ../branches/foo +svn ci -mmerge +svn up + +pwd +cd ../../.. +svnadmin dump temp/repo > addspecial.svndump +echo +echo 'Complete.' +echo 'You probably want to clean up temp now.' +echo 'Dump in addspecial.svndump' +exit 0 diff --git a/tests/fixtures/addspecial.svndump b/tests/fixtures/addspecial.svndump new file mode 100644 index 0000000..6cc3e4c --- /dev/null +++ b/tests/fixtures/addspecial.svndump @@ -0,0 +1,302 @@ +SVN-fs-dump-format-version: 2 + +UUID: 01df53ad-5d72-4756-8742-f669dc98f791 + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2012-05-13T22:22:43.218190Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 118 +Content-length: 118 + +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-13T22:22:44.112163Z +K 7 +svn:log +V 17 +initial structure +PROPS-END + +Node-path: branches +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Revision-number: 2 +Prop-content-length: 103 +Content-length: 103 + +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-13T22:22:45.111247Z +K 7 +svn:log +V 3 +ci1 +PROPS-END + +Node-path: trunk/a +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 2 +Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 +Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b +Content-length: 12 + +PROPS-END +a + + +Revision-number: 3 +Prop-content-length: 111 +Content-length: 111 + +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-13T22:22:48.110257Z +K 7 +svn:log +V 10 +branch foo +PROPS-END + +Node-path: branches/foo +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: trunk + + +Revision-number: 4 +Prop-content-length: 107 +Content-length: 107 + +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-13T22:22:49.115096Z +K 7 +svn:log +V 7 +symlink +PROPS-END + +Node-path: branches/foo/fnord +Node-kind: file +Node-action: add +Prop-content-length: 33 +Text-content-length: 6 +Text-content-md5: c118dba188202a1efc975bef6064180b +Text-content-sha1: 41f94e4692313bf7f7c92aa600002f1dff93d6bf +Content-length: 39 + +K 11 +svn:special +V 1 +* +PROPS-END +link a + +Revision-number: 5 +Prop-content-length: 105 +Content-length: 105 + +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-13T22:22:50.119266Z +K 7 +svn:log +V 5 +spacy +PROPS-END + +Node-path: branches/foo/spacy name +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: branches/foo/spacy name/spacy file +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 2 +Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 +Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b +Content-length: 12 + +PROPS-END +a + + +Revision-number: 6 +Prop-content-length: 105 +Content-length: 105 + +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-13T22:22:52.123367Z +K 7 +svn:log +V 5 +tilde +PROPS-END + +Node-path: branches/foo/spacy name/surprise ~ +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 2 +Text-content-md5: 3b5d5c3712955042212316173ccf37be +Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b +Content-length: 12 + +PROPS-END +b + + +Revision-number: 7 +Prop-content-length: 111 +Content-length: 111 + +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-13T22:22:54.129462Z +K 7 +svn:log +V 10 +executable +PROPS-END + +Node-path: branches/foo/exe +Node-kind: file +Node-action: add +Prop-content-length: 36 +Text-content-length: 4 +Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 +Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 +Content-length: 40 + +K 14 +svn:executable +V 1 +* +PROPS-END +foo + + +Revision-number: 8 +Prop-content-length: 105 +Content-length: 105 + +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-13T22:22:57.111370Z +K 7 +svn:log +V 5 +merge +PROPS-END + +Node-path: trunk +Node-kind: dir +Node-action: change +Prop-content-length: 52 +Content-length: 52 + +K 13 +svn:mergeinfo +V 17 +/branches/foo:3-7 +PROPS-END + + +Node-path: trunk/exe +Node-kind: file +Node-action: add +Node-copyfrom-rev: 7 +Node-copyfrom-path: branches/foo/exe +Text-copy-source-md5: d3b07384d113edec49eaa6238ad5ff00 +Text-copy-source-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 + + +Node-path: trunk/fnord +Node-kind: file +Node-action: add +Node-copyfrom-rev: 7 +Node-copyfrom-path: branches/foo/fnord +Text-copy-source-md5: c118dba188202a1efc975bef6064180b +Text-copy-source-sha1: 41f94e4692313bf7f7c92aa600002f1dff93d6bf + + +Node-path: trunk/spacy name +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 7 +Node-copyfrom-path: branches/foo/spacy name + + diff --git a/tests/fixtures/copies.sh b/tests/fixtures/copies.sh new file mode 100755 index 0000000..4709bc9 --- /dev/null +++ b/tests/fixtures/copies.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Generate copies.svndump +# + +rm -rf temp +mkdir temp +cd temp +mkdir -p import/trunk/dir +echo a > import/trunk/dir/a + +svnadmin create testrepo +svnurl=file://`pwd`/testrepo +svn import import $svnurl -m init + +svn co $svnurl project +cd project +svn cp trunk/dir trunk/dir2 +echo b >> trunk/dir2/a +svn ci -m 'copy/edit trunk/dir/a' +svn up +svn cp trunk/dir2 trunk/dir3 +svn ci -m 'copy dir2 to dir3' +svn rm trunk/dir3/a +svn cp trunk/dir2/a trunk/dir3/a +svn ci -m 'copy and remove' +cd .. + +svnadmin dump testrepo > ../copies.svndump diff --git a/tests/fixtures/copies.svndump b/tests/fixtures/copies.svndump new file mode 100644 index 0000000..76cfd97 --- /dev/null +++ b/tests/fixtures/copies.svndump @@ -0,0 +1,158 @@ +SVN-fs-dump-format-version: 2 + +UUID: 6f377846-a035-4244-a154-e87a9351a653 + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2012-10-15T19:02:56.936694Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 105 +Content-length: 105 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-15T19:02:56.958201Z +K 7 +svn:log +V 4 +init +PROPS-END + +Node-path: trunk +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk/dir +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk/dir/a +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 2 +Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 +Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b +Content-length: 12 + +PROPS-END +a + + +Revision-number: 2 +Prop-content-length: 123 +Content-length: 123 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-15T19:02:58.046478Z +K 7 +svn:log +V 21 +copy/edit trunk/dir/a +PROPS-END + +Node-path: trunk/dir2 +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk/dir + + +Node-path: trunk/dir2/a +Node-kind: file +Node-action: change +Text-content-length: 4 +Text-content-md5: dd8c6a395b5dd36c56d23275028f526c +Text-content-sha1: 05dec960e24d918b8a73a1c53bcbbaac2ee5c2e0 +Content-length: 4 + +a +b + + +Revision-number: 3 +Prop-content-length: 119 +Content-length: 119 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-15T19:03:01.045897Z +K 7 +svn:log +V 17 +copy dir2 to dir3 +PROPS-END + +Node-path: trunk/dir3 +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: trunk/dir2 + + +Revision-number: 4 +Prop-content-length: 117 +Content-length: 117 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-15T19:03:03.046654Z +K 7 +svn:log +V 15 +copy and remove +PROPS-END + +Node-path: trunk/dir3/a +Node-kind: file +Node-action: delete + +Node-path: trunk/dir3/a +Node-kind: file +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: trunk/dir2/a +Text-copy-source-md5: dd8c6a395b5dd36c56d23275028f526c +Text-copy-source-sha1: 05dec960e24d918b8a73a1c53bcbbaac2ee5c2e0 + + + + diff --git a/tests/fixtures/correct.svndump b/tests/fixtures/correct.svndump new file mode 100644 index 0000000..9e24c3c --- /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 index 0000000..bcc0941 --- /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 + diff --git a/tests/fixtures/delete_restore_trunk.sh b/tests/fixtures/delete_restore_trunk.sh new file mode 100755 index 0000000..340b5ef --- /dev/null +++ b/tests/fixtures/delete_restore_trunk.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -e +mkdir temp +cd temp +svnadmin create repo +svn co file://`pwd`/repo wc +cd wc +mkdir branches trunk tags +svn add * +svn ci -m 'btt' +echo foo > trunk/foo +svn add trunk/foo +svn ci -m 'add file' +svn up +svn rm trunk +svn ci -m 'delete trunk' +svn up +cd .. +svn cp -m 'restore trunk' file://`pwd`/repo/trunk@2 file://`pwd`/repo/trunk +cd wc +svn up +echo bar >> trunk/foo +svn ci -m 'append to file' +svn up +cd ../.. +svnadmin dump temp/repo > delete_restore_trunk.svndump +echo +echo 'Complete.' +echo 'You probably want to clean up temp now.' +echo 'Dump in branch_delete_parent_dir.svndump' +exit 0 diff --git a/tests/fixtures/delete_restore_trunk.svndump b/tests/fixtures/delete_restore_trunk.svndump new file mode 100644 index 0000000..2d95813 --- /dev/null +++ b/tests/fixtures/delete_restore_trunk.svndump @@ -0,0 +1,167 @@ +SVN-fs-dump-format-version: 2 + +UUID: fca176f4-a346-479b-ae2c-78c8442c3809 + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2012-05-16T22:55:55.613464Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 103 +Content-length: 103 + +K 7 +svn:log +V 3 +btt +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-16T22:55:56.081065Z +PROPS-END + +Node-path: branches +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: tags +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Revision-number: 2 +Prop-content-length: 108 +Content-length: 108 + +K 7 +svn:log +V 8 +add file +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-16T22:55:57.071178Z +PROPS-END + +Node-path: trunk/foo +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 4 +Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 +Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 +Content-length: 14 + +PROPS-END +foo + + +Revision-number: 3 +Prop-content-length: 113 +Content-length: 113 + +K 7 +svn:log +V 12 +delete trunk +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-16T22:55:59.058026Z +PROPS-END + +Node-path: trunk +Node-action: delete + + +Revision-number: 4 +Prop-content-length: 114 +Content-length: 114 + +K 7 +svn:log +V 13 +restore trunk +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-16T22:56:01.055887Z +PROPS-END + +Node-path: trunk +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: trunk + + +Revision-number: 5 +Prop-content-length: 115 +Content-length: 115 + +K 7 +svn:log +V 14 +append to file +K 10 +svn:author +V 6 +bryano +K 8 +svn:date +V 27 +2012-05-16T22:56:02.060991Z +PROPS-END + +Node-path: trunk/foo +Node-kind: file +Node-action: change +Text-content-length: 8 +Text-content-md5: f47c75614087a8dd938ba4acff252494 +Text-content-sha1: 4e48e2c9a3d2ca8a708cb0cc545700544efb5021 +Content-length: 8 + +foo +bar + + diff --git a/tests/fixtures/emptyrepo2.sh b/tests/fixtures/emptyrepo2.sh new file mode 100755 index 0000000..a29afdd --- /dev/null +++ b/tests/fixtures/emptyrepo2.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# +# Create emptyrepo2.svndump +# +# The generated repository contains a sequence of empty revisions +# created with a combination of svnsync and filtering + +mkdir temp +cd temp + +mkdir project-orig +cd project-orig +mkdir -p sub/trunk other +echo a > other/a +cd .. + +svnadmin create testrepo +svnurl=file://`pwd`/testrepo +svn import project-orig $svnurl -m init + +svn co $svnurl project +cd project +echo a >> other/a +svn ci -m othera +echo a >> other/a +svn ci -m othera2 +echo b > sub/trunk/a +svn add sub/trunk/a +svn ci -m adda +cd .. + +svnadmin create testrepo2 +cat > testrepo2/hooks/pre-revprop-change <<EOF +#!/bin/sh +exit 0 +EOF +chmod +x testrepo2/hooks/pre-revprop-change + +svnurl2=file://`pwd`/testrepo2 +svnsync init --username svnsync $svnurl2 $svnurl/sub +svnsync sync $svnurl2 + +svnadmin dump testrepo2 > ../emptyrepo2.svndump + diff --git a/tests/fixtures/emptyrepo2.svndump b/tests/fixtures/emptyrepo2.svndump new file mode 100644 index 0000000..3f6e553 --- /dev/null +++ b/tests/fixtures/emptyrepo2.svndump @@ -0,0 +1,129 @@ +SVN-fs-dump-format-version: 2 + +UUID: 293d1f29-635d-48b8-9cdf-468fd987067a + +Revision-number: 0 +Prop-content-length: 261 +Content-length: 261 + +K 8 +svn:date +V 27 +2012-10-03T18:58:42.535317Z +K 17 +svn:sync-from-url +V 74 +file:///Users/pmezard/dev/hg/hgsubversion/tests/fixtures/temp/testrepo/sub +K 18 +svn:sync-from-uuid +V 36 +241badf9-093f-4e71-8a58-1028abf52758 +K 24 +svn:sync-last-merged-rev +V 1 +4 +PROPS-END + +Revision-number: 1 +Prop-content-length: 105 +Content-length: 105 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-03T18:58:42.556405Z +K 7 +svn:log +V 4 +init +PROPS-END + +Node-path: sub +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: sub/trunk +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Revision-number: 2 +Prop-content-length: 107 +Content-length: 107 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-03T18:58:43.040912Z +K 7 +svn:log +V 6 +othera +PROPS-END + +Revision-number: 3 +Prop-content-length: 108 +Content-length: 108 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-03T18:58:44.042124Z +K 7 +svn:log +V 7 +othera2 +PROPS-END + +Revision-number: 4 +Prop-content-length: 105 +Content-length: 105 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-03T18:58:45.053459Z +K 7 +svn:log +V 4 +adda +PROPS-END + +Node-path: sub/trunk/a +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 2 +Text-content-md5: 3b5d5c3712955042212316173ccf37be +Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b +Content-length: 12 + +PROPS-END +b + + diff --git a/tests/fixtures/invalid_utf8.sh b/tests/fixtures/invalid_utf8.sh new file mode 100755 index 0000000..76dd9f8 --- /dev/null +++ b/tests/fixtures/invalid_utf8.sh @@ -0,0 +1,34 @@ +#!/bin/bash +#-*- coding: utf-8 -*- +# +# Generate invalid_utf8.svndump +# + +#check svnadmin version, must be >= 1.7 +SVNVERSION=$(svnadmin --version | head -n 1 | cut -d \ -f 3) +if [[ "$SVNVERSION" < '1.7' ]] ; then + echo "You MUST have svn 1.7 or above to use this script" + exit 1 +fi + +set -x + +TMPDIR=$(mktemp -d) +WD=$(pwd) + +cd $TMPDIR + +svnadmin create failrepo +svn co file://$PWD/failrepo fail +( + cd fail + touch A + svn add A + svn ci -m blabargrod +) +svnadmin --pre-1.6-compatible create invalid_utf8 +svnadmin dump failrepo | \ + sed "s/blabargrod/$(echo blåbærgrød | iconv -f utf-8 -t latin1)/g" | \ + svnadmin load --bypass-prop-validation invalid_utf8 + +tar cz -C invalid_utf8 -f "$WD"/invalid_utf8.tar.gz . diff --git a/tests/fixtures/invalid_utf8.tar.gz b/tests/fixtures/invalid_utf8.tar.gz Binary files differnew file mode 100644 index 0000000..537021f --- /dev/null +++ b/tests/fixtures/invalid_utf8.tar.gz diff --git a/tests/fixtures/movetotrunk.sh b/tests/fixtures/movetotrunk.sh new file mode 100755 index 0000000..b27572c --- /dev/null +++ b/tests/fixtures/movetotrunk.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Generate movetotrunk.svndump +# + +mkdir temp +cd temp + +mkdir project-orig +cd project-orig +cd .. + +svnadmin create testrepo +svnurl=file://`pwd`/testrepo +svn mkdir --parents $svnurl/sub1/sub2 -m subpaths +svn import project-orig $svnurl/sub1/sub2 -m "init project" + +svn co $svnurl/sub1/sub2 project +cd project +echo a > a +svn add a +mkdir dir +echo b > dir/b +svn add dir +svn ci -m adda +svn up +mkdir trunk +svn add trunk +svn mv a trunk/a +svn mv dir trunk/dir +svn ci -m 'move to trunk' +cd .. + +svnadmin dump testrepo > ../movetotrunk.svndump diff --git a/tests/fixtures/movetotrunk.svndump b/tests/fixtures/movetotrunk.svndump new file mode 100644 index 0000000..928a8f3 --- /dev/null +++ b/tests/fixtures/movetotrunk.svndump @@ -0,0 +1,154 @@ +SVN-fs-dump-format-version: 2 + +UUID: bb3f8dfd-83a8-4fe0-b57e-00a3838532ab + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2012-10-20T20:23:15.254324Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 109 +Content-length: 109 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-20T20:23:15.271492Z +K 7 +svn:log +V 8 +subpaths +PROPS-END + +Node-path: sub1 +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: sub1/sub2 +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Revision-number: 2 +Prop-content-length: 105 +Content-length: 105 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-20T20:23:16.068226Z +K 7 +svn:log +V 4 +adda +PROPS-END + +Node-path: sub1/sub2/a +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 2 +Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 +Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b +Content-length: 12 + +PROPS-END +a + + +Node-path: sub1/sub2/dir +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: sub1/sub2/dir/b +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 2 +Text-content-md5: 3b5d5c3712955042212316173ccf37be +Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b +Content-length: 12 + +PROPS-END +b + + +Revision-number: 3 +Prop-content-length: 115 +Content-length: 115 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-20T20:23:20.043626Z +K 7 +svn:log +V 13 +move to trunk +PROPS-END + +Node-path: sub1/sub2/trunk +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: sub1/sub2/trunk/a +Node-kind: file +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: sub1/sub2/a +Text-copy-source-md5: 60b725f10c9c85c70d97880dfe8191b3 +Text-copy-source-sha1: 3f786850e387550fdab836ed7e6dc881de23001b + + +Node-path: sub1/sub2/trunk/dir +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: sub1/sub2/dir + + +Node-path: sub1/sub2/dir +Node-action: delete + + +Node-path: sub1/sub2/a +Node-action: delete + + diff --git a/tests/fixtures/revert.sh b/tests/fixtures/revert.sh new file mode 100755 index 0000000..aaab485 --- /dev/null +++ b/tests/fixtures/revert.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# +# Generate revert.svndump +# + +rm -rf temp +mkdir temp +cd temp +mkdir -p import/trunk/dir +cd import/trunk +echo a > a +echo b > dir/b +cd ../.. + +svnadmin create testrepo +svnurl=file://`pwd`/testrepo +svn import import $svnurl -m init + +svn co $svnurl project +cd project +echo a >> trunk/a +echo b >> trunk/dir/b +svn ci -m changefiles +svn up +# Test directory revert +svn rm trunk +svn cp $svnurl/trunk@1 trunk +svn st +svn ci -m revert +svn up +# Test file revert +svn rm trunk/a +svn rm trunk/dir/b +svn cp $svnurl/trunk/a@2 trunk/a +svn cp $svnurl/trunk/dir/b@2 trunk/dir/b +svn ci -m revert2 +cd .. + +svnadmin dump testrepo > ../revert.svndump diff --git a/tests/fixtures/revert.svndump b/tests/fixtures/revert.svndump new file mode 100644 index 0000000..2a368ae --- /dev/null +++ b/tests/fixtures/revert.svndump @@ -0,0 +1,197 @@ +SVN-fs-dump-format-version: 2 + +UUID: 307f02f4-2d74-44cb-98a4-4e162241d396 + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2012-10-06T08:50:46.559327Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 105 +Content-length: 105 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-06T08:50:46.581582Z +K 7 +svn:log +V 4 +init +PROPS-END + +Node-path: trunk +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk/a +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 2 +Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 +Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b +Content-length: 12 + +PROPS-END +a + + +Node-path: trunk/dir +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk/dir/b +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 2 +Text-content-md5: 3b5d5c3712955042212316173ccf37be +Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b +Content-length: 12 + +PROPS-END +b + + +Revision-number: 2 +Prop-content-length: 113 +Content-length: 113 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-06T08:50:47.048033Z +K 7 +svn:log +V 11 +changefiles +PROPS-END + +Node-path: trunk/a +Node-kind: file +Node-action: change +Text-content-length: 4 +Text-content-md5: 0d227f1abf8c2932d342e9b99cc957eb +Text-content-sha1: d7c8127a20a396cff08af086a1c695b0636f0c29 +Content-length: 4 + +a +a + + +Node-path: trunk/dir/b +Node-kind: file +Node-action: change +Text-content-length: 4 +Text-content-md5: 06ac26ed8b614fc0b141e4542aa067c2 +Text-content-sha1: f6980469e74f7125178e88ec571e06fe6ce86e95 +Content-length: 4 + +b +b + + +Revision-number: 3 +Prop-content-length: 107 +Content-length: 107 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-06T08:50:50.058224Z +K 7 +svn:log +V 6 +revert +PROPS-END + +Node-path: trunk +Node-kind: dir +Node-action: delete + +Node-path: trunk +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk + + + + +Revision-number: 4 +Prop-content-length: 108 +Content-length: 108 + +K 10 +svn:author +V 7 +pmezard +K 8 +svn:date +V 27 +2012-10-06T08:50:54.047396Z +K 7 +svn:log +V 7 +revert2 +PROPS-END + +Node-path: trunk/a +Node-kind: file +Node-action: delete + +Node-path: trunk/a +Node-kind: file +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: trunk/a +Text-copy-source-md5: 0d227f1abf8c2932d342e9b99cc957eb +Text-copy-source-sha1: d7c8127a20a396cff08af086a1c695b0636f0c29 + + + + +Node-path: trunk/dir/b +Node-kind: file +Node-action: delete + +Node-path: trunk/dir/b +Node-kind: file +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: trunk/dir/b +Text-copy-source-md5: 06ac26ed8b614fc0b141e4542aa067c2 +Text-copy-source-sha1: f6980469e74f7125178e88ec571e06fe6ce86e95 + + + + diff --git a/tests/fixtures/subdir_is_file_prefix.svndump b/tests/fixtures/subdir_is_file_prefix.svndump new file mode 100644 index 0000000..c09b45a --- /dev/null +++ b/tests/fixtures/subdir_is_file_prefix.svndump @@ -0,0 +1,72 @@ +SVN-fs-dump-format-version: 2 + +UUID: 924a052a-5e5a-4a8e-a677-da5565bec340 + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2011-03-04T12:33:29.342045Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 123 +Content-length: 123 + +K 7 +svn:log +V 22 +Create directory flaf. +K 10 +svn:author +V 6 +danchr +K 8 +svn:date +V 27 +2011-03-04T12:34:00.349950Z +PROPS-END + +Node-path: flaf +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Revision-number: 2 +Prop-content-length: 138 +Content-length: 138 + +K 7 +svn:log +V 37 +Create the file flaf.txt within flaf. +K 10 +svn:author +V 6 +danchr +K 8 +svn:date +V 27 +2011-03-04T12:45:01.701033Z +PROPS-END + +Node-path: flaf/flaf.txt +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 15 +Text-content-md5: 8c0059c8f7998e8003836b8e8fcb74d7 +Text-content-sha1: b7d680bc5411f46395c4ef267001e1a307d7b0d5 +Content-length: 25 + +PROPS-END +Goodbye world. + + diff --git a/tests/run.py b/tests/run.py index 98cf0a6..3832633 100644 --- a/tests/run.py +++ b/tests/run.py @@ -18,26 +18,29 @@ def tests(): import test_fetch_renames import test_fetch_symlinks import test_fetch_truncated + import test_hooks import test_pull + import test_pull_fallback import test_push_command import test_push_renames import test_push_dirs import test_push_eol + import test_push_autoprops import test_rebuildmeta import test_single_dir_clone - import test_startrev import test_svnwrap import test_tags import test_template_keywords import test_utility_commands import test_unaffected_core + import test_updatemeta import test_urls sys.path.append(os.path.dirname(__file__)) sys.path.append(os.path.join(os.path.dirname(__file__), 'comprehensive')) import test_stupid_pull - import test_verify + import test_verify_and_startrev return locals() diff --git a/tests/test_fetch_branches.py b/tests/test_fetch_branches.py index 7a9287c..18fdd40 100644 --- a/tests/test_fetch_branches.py +++ b/tests/test_fetch_branches.py @@ -58,8 +58,9 @@ class TestFetchBranches(test_util.TestBase): self.test_unorderedbranch(True) def test_renamed_branch_to_trunk(self, stupid=False): + config = {'hgsubversion.failonmissing': 'true'} repo = self._load_fixture_and_fetch('branch_rename_to_trunk.svndump', - stupid=stupid) + stupid=stupid, config=config) self.assertEqual(repo['default'].parents()[0].branch(), 'dev_branch') self.assert_('iota' in repo['default']) self.assertEqual(repo['old_trunk'].parents()[0].branch(), 'default') diff --git a/tests/test_fetch_command.py b/tests/test_fetch_command.py index b4e3fce..b1683fc 100644 --- a/tests/test_fetch_command.py +++ b/tests/test_fetch_command.py @@ -8,6 +8,7 @@ from mercurial import commands from mercurial import hg from mercurial import node from mercurial import ui +from mercurial import encoding class TestBasicRepoLayout(test_util.TestBase): @@ -94,13 +95,16 @@ class TestBasicRepoLayout(test_util.TestBase): assert 'README' not in repo assert '../branches' not in repo - def test_files_copied_from_outside_btt(self): + def test_files_copied_from_outside_btt(self, stupid=False): repo = self._load_fixture_and_fetch( - 'test_files_copied_from_outside_btt.svndump') + 'test_files_copied_from_outside_btt.svndump', stupid=stupid) self.assertEqual(node.hex(repo['tip'].node()), '3c78170e30ddd35f2c32faa0d8646ab75bba4f73') self.assertEqual(len(repo.changelog), 2) + def test_files_copied_from_outside_btt_stupid(self): + self.test_files_copied_from_outside_btt(stupid=True) + def test_file_renamed_in_from_outside_btt(self): repo = self._load_fixture_and_fetch( 'file_renamed_in_from_outside_btt.svndump') @@ -175,7 +179,8 @@ class TestBasicRepoLayout(test_util.TestBase): self.assertEqual(repo[r].hex(), repo2[r].hex()) def test_path_quoting_stupid(self): - self.test_path_quoting(True) + repo = self.test_path_quoting(True) + def test_identical_fixtures(self): '''ensure that the non_ascii_path_N fixtures are identical''' @@ -186,6 +191,14 @@ class TestBasicRepoLayout(test_util.TestBase): self.assertMultiLineEqual(open(fixturepaths[0]).read(), open(fixturepaths[1]).read()) + def test_invalid_message(self): + repo = self._load_fixture_and_fetch('invalid_utf8.tar.gz') + # changelog returns descriptions in local encoding + desc = encoding.fromlocal(repo[0].description()) + self.assertEqual(desc.decode('utf8'), + u'bl\xe5b\xe6rgr\xf8d') + + class TestStupidPull(test_util.TestBase): def test_stupid(self): repo = self._load_fixture_and_fetch('two_heads.svndump', stupid=True) @@ -216,6 +229,63 @@ class TestStupidPull(test_util.TestBase): self.assertEqual(node.hex(repo['tip'].node()), '1a6c3f30911d57abb67c257ec0df3e7bc44786f7') + def test_empty_repo(self, stupid=False): + # This used to crash HgEditor because it could be closed without + # having been initialized again. + self._load_fixture_and_fetch('emptyrepo2.svndump', stupid=stupid) + + def test_empty_repo_stupid(self): + self.test_empty_repo(stupid=True) + + def test_fetch_revert(self, stupid=False): + repo = self._load_fixture_and_fetch('revert.svndump', stupid=stupid) + graph = self.getgraph(repo) + refgraph = """\ +o changeset: 3:937dcd1206d4 +| branch: +| tags: tip +| summary: revert2 +| files: a dir/b +| +o changeset: 2:9317a748b7c3 +| branch: +| tags: +| summary: revert +| files: a dir/b +| +o changeset: 1:243259a4138a +| branch: +| tags: +| summary: changefiles +| files: a dir/b +| +o changeset: 0:ab86791fc857 + branch: + tags: + summary: init + files: a dir/b +""" + self.assertEqual(refgraph.strip(), graph.strip()) + + def test_fetch_revert_stupid(self): + self.test_fetch_revert(stupid=True) + + def test_fetch_movetotrunk(self, stupid=False): + repo = self._load_fixture_and_fetch('movetotrunk.svndump', + stupid=stupid, subdir='sub1/sub2') + graph = self.getgraph(repo) + refgraph = """\ +o changeset: 0:02996a5980ba + branch: + tags: tip + summary: move to trunk + files: a dir/b +""" + self.assertEqual(refgraph.strip(), graph.strip()) + + def test_fetch_movetotrunk_stupid(self): + self.test_fetch_movetotrunk(stupid=True) + def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestBasicRepoLayout), unittest.TestLoader().loadTestsFromTestCase(TestStupidPull), diff --git a/tests/test_fetch_mappings.py b/tests/test_fetch_mappings.py index 02727f6..267515c 100644 --- a/tests/test_fetch_mappings.py +++ b/tests/test_fetch_mappings.py @@ -13,6 +13,7 @@ from mercurial import util as hgutil from hgsubversion import maps from hgsubversion import svncommands from hgsubversion import util +from hgsubversion import verify class MapTests(test_util.TestBase): @property @@ -100,49 +101,43 @@ class MapTests(test_util.TestBase): all_tests = set(test) self.assertEqual(fromself.symmetric_difference(all_tests), set()) - def test_file_map(self, stupid=False): - repo_path = self.load_svndump('replace_trunk_with_branch.svndump') + def _loadwithfilemap(self, svndump, filemapcontent, stupid=False, + failonmissing=True): + repo_path = self.load_svndump(svndump) filemap = open(self.filemap, 'w') - filemap.write("include alpha\n") + filemap.write(filemapcontent) filemap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'filemap', self.filemap) + ui.setconfig('hgsubversion', 'failoninvalidreplayfile', 'true') + ui.setconfig('hgsubversion', 'failonmissing', failonmissing) commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, filemap=self.filemap) - self.assertEqual(node.hex(self.repo[0].node()), '88e2c7492d83e4bf30fbb2dcbf6aa24d60ac688d') - self.assertEqual(node.hex(self.repo['default'].node()), 'e524296152246b3837fe9503c83b727075835155') + return self.repo + + def test_file_map(self, stupid=False): + repo = self._loadwithfilemap('replace_trunk_with_branch.svndump', + "include alpha\n", stupid) + self.assertEqual(node.hex(repo[0].node()), '88e2c7492d83e4bf30fbb2dcbf6aa24d60ac688d') + self.assertEqual(node.hex(repo['default'].node()), 'e524296152246b3837fe9503c83b727075835155') def test_file_map_stupid(self): # TODO: re-enable test if we ever reinstate this feature self.assertRaises(hgutil.Abort, self.test_file_map, True) def test_file_map_exclude(self, stupid=False): - repo_path = self.load_svndump('replace_trunk_with_branch.svndump') - filemap = open(self.filemap, 'w') - filemap.write("exclude alpha\n") - filemap.close() - ui = self.ui(stupid) - ui.setconfig('hgsubversion', 'filemap', self.filemap) - commands.clone(ui, test_util.fileurl(repo_path), - self.wc_path, filemap=self.filemap) - self.assertEqual(node.hex(self.repo[0].node()), '2c48f3525926ab6c8b8424bcf5eb34b149b61841') - self.assertEqual(node.hex(self.repo['default'].node()), 'b37a3c0297b71f989064d9b545b5a478bbed7cc1') + repo = self._loadwithfilemap('replace_trunk_with_branch.svndump', + "exclude alpha\n", stupid) + self.assertEqual(node.hex(repo[0].node()), '2c48f3525926ab6c8b8424bcf5eb34b149b61841') + self.assertEqual(node.hex(repo['default'].node()), 'b37a3c0297b71f989064d9b545b5a478bbed7cc1') def test_file_map_exclude_stupid(self): # TODO: re-enable test if we ever reinstate this feature self.assertRaises(hgutil.Abort, self.test_file_map_exclude, True) def test_file_map_rule_order(self): - repo_path = self.load_svndump('replace_trunk_with_branch.svndump') - filemap = open(self.filemap, 'w') - filemap.write("exclude alpha\n") - filemap.write("include .\n") - filemap.write("exclude gamma\n") - filemap.close() - ui = self.ui(False) - ui.setconfig('hgsubversion', 'filemap', self.filemap) - commands.clone(ui, test_util.fileurl(repo_path), - self.wc_path, filemap=self.filemap) + repo = self._loadwithfilemap('replace_trunk_with_branch.svndump', + "exclude alpha\ninclude .\nexclude gamma\n") # The exclusion of alpha is overridden by the later rule to # include all of '.', whereas gamma should remain excluded # because it's excluded after the root directory. @@ -151,6 +146,33 @@ class MapTests(test_util.TestBase): self.assertEqual(self.repo['default'].manifest().keys(), ['alpha', 'beta']) + def test_file_map_copy(self): + # Exercise excluding files copied from a non-excluded directory. + # There will be missing files as we are copying from an excluded + # directory. + repo = self._loadwithfilemap('copies.svndump', "exclude dir2\n", + failonmissing=False) + self.assertEqual(['dir/a', 'dir3/a'], list(repo[2])) + + def test_file_map_exclude_copy_source_and_dest(self): + # dir3 is excluded and copied from dir2 which is also excluded. + # dir3 files should not be marked as missing and fetched. + repo = self._loadwithfilemap('copies.svndump', + "exclude dir2\nexclude dir3\n") + self.assertEqual(['dir/a'], list(repo[2])) + + def test_file_map_include_file_exclude_dir(self): + # dir3 is excluded but we want dir3/a, which is also copied from + # an exluded dir2. dir3/a should be fetched. + repo = self._loadwithfilemap('copies.svndump', + "include .\nexclude dir2\nexclude dir3\ninclude dir3/a\n", + failonmissing=False) + self.assertEqual(['dir/a', 'dir3/a'], list(repo[2])) + + def test_file_map_delete_dest(self): + repo = self._loadwithfilemap('copies.svndump', 'exclude dir3\n') + self.assertEqual(['dir/a', 'dir2/a'], list(repo[3])) + def test_branchmap(self, stupid=False): repo_path = self.load_svndump('branchmap.svndump') branchmap = open(self.branchmap, 'w') @@ -244,6 +266,8 @@ class MapTests(test_util.TestBase): ui = self.ui(stupid) src, dest = test_util.hgclone(ui, self.wc_path, self.wc_path + '_clone', update=False) + src = test_util.getlocalpeer(src) + dest = test_util.getlocalpeer(dest) svncommands.rebuildmeta(ui, dest, args=[test_util.fileurl(repo_path)]) @@ -270,7 +294,7 @@ class MapTests(test_util.TestBase): repo = self.repo for r in repo: - self.assertEquals(svncommands.verify(ui, repo, rev=r), 0) + self.assertEquals(verify.verify(ui, repo, rev=r), 0) def test_branchmap_verify_stupid(self): '''test verify on a branchmapped clone (stupid)''' diff --git a/tests/test_fetch_renames.py b/tests/test_fetch_renames.py index 64b69d3..625e48d 100644 --- a/tests/test_fetch_renames.py +++ b/tests/test_fetch_renames.py @@ -14,8 +14,11 @@ class TestFetchRenames(test_util.TestBase): w('%s: %r %r\n' % (f, fctx.data(), fctx.renamed())) def _test_rename(self, stupid): - repo = self._load_fixture_and_fetch('renames.svndump', stupid=stupid) - # self._debug_print_copies(repo) + config = { + 'hgsubversion.filestoresize': '0', + } + repo = self._load_fixture_and_fetch('renames.svndump', stupid=stupid, + config=config) # Map revnum to mappings of dest name to (source name, dest content) copies = { diff --git a/tests/test_fetch_symlinks.py b/tests/test_fetch_symlinks.py index 7f9252b..42d5518 100644 --- a/tests/test_fetch_symlinks.py +++ b/tests/test_fetch_symlinks.py @@ -50,7 +50,17 @@ class TestFetchSymlinks(test_util.TestBase): def test_symlinks_stupid(self): self.test_symlinks(True) +class TestMergeSpecial(test_util.TestBase): + def test_special(self): + repo = self._load_fixture_and_fetch('addspecial.svndump', + subdir='trunk') + ctx = repo['tip'] + self.assertEqual(ctx['fnord'].flags(), 'l') + self.assertEqual(ctx['exe'].flags(), 'x') + def suite(): - all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestFetchSymlinks), - ] + all_tests = [ + unittest.TestLoader().loadTestsFromTestCase(TestFetchSymlinks), + unittest.TestLoader().loadTestsFromTestCase(TestMergeSpecial), + ] return unittest.TestSuite(all_tests) diff --git a/tests/test_helpers.py b/tests/test_helpers.py new file mode 100644 index 0000000..ebcb381 --- /dev/null +++ b/tests/test_helpers.py @@ -0,0 +1,37 @@ +import os, sys, unittest + +_rootdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, _rootdir) + +from hgsubversion import editor + +class TestHelpers(unittest.TestCase): + def test_filestore(self): + fs = editor.FileStore(2) + fs.setfile('a', 'a') + fs.setfile('b', 'b') + self.assertEqual('a', fs._data.get('a')) + self.assertEqual('b', fs._data.get('b')) + + fs.delfile('b') + self.assertRaises(IOError, lambda: fs.getfile('b')) + fs.setfile('bb', 'bb') + self.assertTrue('bb' in fs._files) + self.assertTrue('bb' not in fs._data) + self.assertEqual('bb', fs.getfile('bb')) + + fs.delfile('bb') + self.assertTrue('bb' not in fs._files) + self.assertEqual([], os.listdir(fs._tempdir)) + self.assertRaises(IOError, lambda: fs.getfile('bb')) + + fs.setfile('bb', 'bb') + self.assertEqual(1, len(os.listdir(fs._tempdir))) + fs.popfile('bb') + self.assertEqual([], os.listdir(fs._tempdir)) + self.assertRaises(editor.EditingError, lambda: fs.getfile('bb')) + +def suite(): + return unittest.TestSuite([ + unittest.TestLoader().loadTestsFromTestCase(TestHelpers), + ]) diff --git a/tests/test_hooks.py b/tests/test_hooks.py new file mode 100644 index 0000000..bff3730 --- /dev/null +++ b/tests/test_hooks.py @@ -0,0 +1,48 @@ +import sys +import test_util +import unittest + +from mercurial import hg +from mercurial import commands + +class TestHooks(test_util.TestBase): + def setUp(self): + super(TestHooks, self).setUp() + + def _loadupdate(self, fixture_name, *args, **kwargs): + kwargs = kwargs.copy() + kwargs.update(stupid=False, noupdate=False) + repo, repo_path = self.load_and_fetch(fixture_name, *args, **kwargs) + return repo, repo_path + + def test_updatemetahook(self): + repo, repo_path = self._loadupdate('single_rev.svndump') + state = repo.parents() + self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed'}) + commands.pull(self.repo.ui, self.repo) + + # Clone to a new repository and add a hook + new_wc_path = "%s-2" % self.wc_path + commands.clone(self.repo.ui, self.wc_path, new_wc_path) + newrepo = hg.repository(test_util.testui(), new_wc_path) + newrepo.ui.setconfig('hooks', 'changegroup.meta', + 'python:hgsubversion.hooks.updatemeta.hook') + + # Commit a rev that should trigger svn meta update + self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed Again'}) + commands.pull(self.repo.ui, self.repo) + + self.called = False + import hgsubversion.svncommands + oldupdatemeta = hgsubversion.svncommands.updatemeta + def _updatemeta(ui, repo, args=[]): + self.called = True + hgsubversion.svncommands.updatemeta = _updatemeta + + # Pull and make sure our updatemeta function gets called + commands.pull(newrepo.ui, newrepo) + hgsubversion.svncommands.updatemeta = oldupdatemeta + self.assertTrue(self.called) + +def suite(): + return unittest.findTestCases(sys.modules[__name__]) diff --git a/tests/test_pull.py b/tests/test_pull.py index 556fbf1..ebfe85a 100644 --- a/tests/test_pull.py +++ b/tests/test_pull.py @@ -6,14 +6,16 @@ from mercurial import node from mercurial import ui from mercurial import util as hgutil from mercurial import commands +from hgsubversion import verify class TestPull(test_util.TestBase): def setUp(self): super(TestPull, self).setUp() - def _loadupdate(self, fixture_name): - repo, repo_path = self.load_and_fetch(fixture_name, stupid=False, - noupdate=False) + def _loadupdate(self, fixture_name, *args, **kwargs): + kwargs = kwargs.copy() + kwargs.update(stupid=False, noupdate=False) + repo, repo_path = self.load_and_fetch(fixture_name, *args, **kwargs) return repo, repo_path def test_nochanges(self): @@ -58,6 +60,26 @@ class TestPull(test_util.TestBase): commands.pull(repo.ui, repo) self.assertEqual(oldheads, map(node.hex, repo.heads())) + def test_skip_basic(self): + repo, repo_path = self._loadupdate('single_rev.svndump') + self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed'}) + self.add_svn_rev(repo_path, {'trunk/beta': 'More changed'}) + self.add_svn_rev(repo_path, {'trunk/gamma': 'Even more changeder'}) + repo.ui.setconfig('hgsubversion', 'unsafeskip', '3 4') + commands.pull(repo.ui, repo) + tip = repo['tip'].rev() + self.assertEqual(tip, 1) + self.assertEquals(verify.verify(repo.ui, repo, rev=tip), 1) + + def test_skip_delete_restore(self): + repo, repo_path = self._loadupdate('delete_restore_trunk.svndump', + rev=2) + repo.ui.setconfig('hgsubversion', 'unsafeskip', '3 4') + commands.pull(repo.ui, repo) + tip = repo['tip'].rev() + self.assertEqual(tip, 1) + self.assertEquals(verify.verify(repo.ui, repo, rev=tip), 0) + def suite(): import unittest, sys return unittest.findTestCases(sys.modules[__name__]) diff --git a/tests/test_pull_fallback.py b/tests/test_pull_fallback.py new file mode 100644 index 0000000..e543d4e --- /dev/null +++ b/tests/test_pull_fallback.py @@ -0,0 +1,106 @@ +import test_util + +import re +import mercurial +from mercurial import commands +from hgsubversion import stupid +from hgsubversion import svnwrap +from hgsubversion import wrappers + +class TestPullFallback(test_util.TestBase): + def setUp(self): + super(TestPullFallback, self).setUp() + + def _loadupdate(self, fixture_name, *args, **kwargs): + kwargs = kwargs.copy() + kwargs.update(noupdate=False) + repo, repo_path = self.load_and_fetch(fixture_name, *args, **kwargs) + return repo, repo_path + + def test_stupid_fallback_to_stupid_fullrevs(self): + return + to_patch = { + 'mercurial.patch.patchbackend': _patchbackend_raise, + 'stupid.diff_branchrev': stupid.diff_branchrev, + 'stupid.fetch_branchrev': stupid.fetch_branchrev, + } + + expected_calls = { + 'mercurial.patch.patchbackend': 1, + 'stupid.diff_branchrev': 1, + 'stupid.fetch_branchrev': 1, + } + + repo, repo_path = self._loadupdate( + 'single_rev.svndump', stupid=True) + + # Passing stupid=True doesn't seem to be working - force it + repo.ui.setconfig('hgsubversion', 'stupid', "true") + state = repo.parents() + + calls, replaced = _monkey_patch(to_patch) + + try: + self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed'}) + commands.pull(self.repo.ui, repo, update=True) + self.failIfEqual(state, repo.parents()) + self.assertTrue('tip' in repo[None].tags()) + self.assertEqual(expected_calls, calls) + + finally: + _monkey_unpatch(replaced) + +def _monkey_patch(to_patch, start=None): + if start is None: + import sys + start = sys.modules[__name__] + + calls = {} + replaced = {} + + for path, replacement in to_patch.iteritems(): + obj = start + owner, attr = path.rsplit('.', 1) + + for a in owner.split('.', -1): + obj = getattr(obj, a) + + replaced[path] = getattr(obj, attr) + calls[path] = 0 + + def outer(path=path, calls=calls, replacement=replacement): + def wrapper(*p, **kw): + calls[path] += 1 + return replacement(*p, **kw) + + return wrapper + + setattr(obj, attr, outer()) + + return calls, replaced + +def _monkey_unpatch(to_patch, start=None): + if start is None: + import sys + start = sys.modules[__name__] + + replaced = {} + + for path, replacement in to_patch.iteritems(): + obj = start + owner, attr = path.rsplit('.', 1) + + for a in owner.split('.', -1): + obj = getattr(obj, a) + + replaced[path] = getattr(obj, attr) + setattr(obj, attr, replacement) + + return replaced + +def _patchbackend_raise(*p, **kw): + raise mercurial.patch.PatchError("patch failed") + +def suite(): + import unittest, sys + return unittest.findTestCases(sys.modules[__name__]) diff --git a/tests/test_push_autoprops.py b/tests/test_push_autoprops.py new file mode 100644 index 0000000..3dfba47 --- /dev/null +++ b/tests/test_push_autoprops.py @@ -0,0 +1,107 @@ +import subprocess +import sys +import unittest +import os + +import test_util + +from hgsubversion import svnwrap + +class PushAutoPropsTests(test_util.TestBase): + def setUp(self): + test_util.TestBase.setUp(self) + repo, self.repo_path = self.load_and_fetch('emptyrepo.svndump') + + def test_push_honors_svn_autoprops(self): + self.setup_svn_config( + "[miscellany]\n" + "enable-auto-props = yes\n" + "[auto-props]\n" + "*.py = test:prop=success\n") + changes = [('test.py', 'test.py', 'echo hallo')] + self.commitchanges(changes) + self.pushrevisions(True) + prop_val = test_util.svnpropget( + self.repo_path, "trunk/test.py", 'test:prop') + self.assertEqual('success', prop_val) + + +class AutoPropsConfigTest(test_util.TestBase): + def test_use_autoprops_for_matching_file_when_enabled(self): + self.setup_svn_config( + "[miscellany]\n" + "enable-auto-props = yes\n" + "[auto-props]\n" + "*.py = test:prop=success\n") + props = self.new_autoprops_config().properties('xxx/test.py') + self.assertEqual({ 'test:prop': 'success'}, props) + + def new_autoprops_config(self): + return svnwrap.AutoPropsConfig(self.config_dir) + + def test_ignore_nonexisting_config(self): + config_file = os.path.join(self.config_dir, 'config') + os.remove(config_file) + self.assertTrue(not os.path.exists(config_file)) + props = self.new_autoprops_config().properties('xxx/test.py') + self.assertEqual({}, props) + + def test_ignore_autoprops_when_file_doesnt_match(self): + self.setup_svn_config( + "[miscellany]\n" + "enable-auto-props = yes\n" + "[auto-props]\n" + "*.py = test:prop=success\n") + props = self.new_autoprops_config().properties('xxx/test.sh') + self.assertEqual({}, props) + + def test_ignore_autoprops_when_disabled(self): + self.setup_svn_config( + "[miscellany]\n" + "#enable-auto-props = yes\n" + "[auto-props]\n" + "*.py = test:prop=success\n") + props = self.new_autoprops_config().properties('xxx/test.py') + self.assertEqual({}, props) + + def test_combine_properties_of_multiple_matches(self): + self.setup_svn_config( + "[miscellany]\n" + "enable-auto-props = yes\n" + "[auto-props]\n" + "*.py = test:prop=success\n" + "test.* = test:prop2=success\n") + props = self.new_autoprops_config().properties('xxx/test.py') + self.assertEqual({ + 'test:prop': 'success', 'test:prop2': 'success'}, props) + + +class ParseAutoPropsTests(test_util.TestBase): + def test_property_value_is_optional(self): + props = svnwrap.parse_autoprops("svn:executable") + self.assertEqual({'svn:executable': ''}, props) + props = svnwrap.parse_autoprops("svn:executable=") + self.assertEqual({'svn:executable': ''}, props) + + def test_property_value_may_be_quoted(self): + props = svnwrap.parse_autoprops("svn:eol-style=\" native \"") + self.assertEqual({'svn:eol-style': ' native '}, props) + props = svnwrap.parse_autoprops("svn:eol-style=' native '") + self.assertEqual({'svn:eol-style': ' native '}, props) + + def test_surrounding_whitespaces_are_ignored(self): + props = svnwrap.parse_autoprops(" svn:eol-style = native ") + self.assertEqual({'svn:eol-style': 'native'}, props) + + def test_multiple_properties_are_separated_by_semicolon(self): + props = svnwrap.parse_autoprops( + "svn:eol-style=native;svn:executable=true\n") + self.assertEqual({ + 'svn:eol-style': 'native', + 'svn:executable': 'true'}, + props) + + +def suite(): + return unittest.findTestCases(sys.modules[__name__]) + diff --git a/tests/test_push_command.py b/tests/test_push_command.py index 8342100..1c41596 100644 --- a/tests/test_push_command.py +++ b/tests/test_push_command.py @@ -134,7 +134,7 @@ class PushTests(test_util.TestBase): finally: if sys.version_info >= (2,6): svnserve.kill() - else: + else: test_util.kill_process(svnserve) def test_push_over_svnserve(self): @@ -487,7 +487,40 @@ class PushTests(test_util.TestBase): 'Outgoing changesets parent is not at subversion ' 'HEAD\n' '(pull again and rebase on a newer revision)') + # verify that any pending transactions on the server got cleaned up + self.assertEqual([], os.listdir( + os.path.join(self.tmpdir, 'testrepo-1', 'db', 'transactions'))) + def test_push_encoding(self): + self.test_push_two_revs() + # Writing then rebasing UTF-8 filenames in a cp1252 windows console + # used to fail because hg internal encoding was being changed during + # the interactions with subversion, *and during the rebase*, which + # confused the dirstate and made it believe the file was deleted. + fn = 'pi\xc3\xa8ce/test' + changes = [(fn, fn, 'a')] + par = self.repo['tip'].rev() + self.commitchanges(changes, parent=par) + self.pushrevisions() + + def test_push_emptying_changeset(self): + r = self.repo['tip'] + changes = [ + ('alpha', None, None), + ('beta', None, None), + ] + parent = self.repo['tip'].rev() + self.commitchanges(changes, parent=parent) + self.pushrevisions() + self.assertEqual({}, self.repo['tip'].manifest()) + + # Try to re-add a file after emptying the branch + changes = [ + ('alpha', 'alpha', 'alpha'), + ] + self.commitchanges(changes, parent=self.repo['tip'].rev()) + self.pushrevisions() + self.assertEqual(['alpha'], list(self.repo['tip'].manifest())) def suite(): test_classes = [PushTests, ] diff --git a/tests/test_rebuildmeta.py b/tests/test_rebuildmeta.py index bee8e37..5e2de4b 100644 --- a/tests/test_rebuildmeta.py +++ b/tests/test_rebuildmeta.py @@ -34,6 +34,8 @@ def _do_case(self, name, stupid, single): wc2_path = self.wc_path + '_clone' u = ui.ui() src, dest = test_util.hgclone(u, self.wc_path, wc2_path, update=False) + src = test_util.getlocalpeer(src) + dest = test_util.getlocalpeer(dest) # insert a wrapper that prevents calling changectx.children() def failfn(orig, ctx): @@ -51,9 +53,48 @@ def _do_case(self, name, stupid, single): # remove the wrapper context.changectx.children = origchildren + self._run_assertions(name, stupid, single, src, dest, u) + + wc3_path = self.wc_path + '_partial' + src, dest = test_util.hgclone(u, + self.wc_path, + wc3_path, + update=False, + rev=[0]) + srcrepo = test_util.getlocalpeer(src) + dest = test_util.getlocalpeer(dest) + + # insert a wrapper that prevents calling changectx.children() + extensions.wrapfunction(context.changectx, 'children', failfn) + + try: + svncommands.rebuildmeta(u, dest, + args=[test_util.fileurl(repo_path + + subdir), ]) + finally: + # remove the wrapper + context.changectx.children = origchildren + + dest.pull(src) + + # insert a wrapper that prevents calling changectx.children() + extensions.wrapfunction(context.changectx, 'children', failfn) + try: + svncommands.updatemeta(u, dest, + args=[test_util.fileurl(repo_path + + subdir), ]) + finally: + # remove the wrapper + context.changectx.children = origchildren + + self._run_assertions(name, stupid, single, srcrepo, dest, u) + + +def _run_assertions(self, name, stupid, single, src, dest, u): + self.assertTrue(os.path.isdir(os.path.join(src.path, 'svn')), 'no .hg/svn directory in the source!') - self.assertTrue(os.path.isdir(os.path.join(src.path, 'svn')), + self.assertTrue(os.path.isdir(os.path.join(dest.path, 'svn')), 'no .hg/svn directory in the destination!') dest = hg.repository(u, os.path.dirname(dest.path)) for tf in ('lastpulled', 'rev_map', 'uuid', 'tagmap', 'layout', 'subdir',): @@ -68,7 +109,7 @@ def _do_case(self, name, stupid, single): self.assertNotEqual(old, new, 'rebuildmeta unexpected match on youngest rev!') continue - self.assertMultiLineEqual(old, new) + self.assertMultiLineEqual(old, new, tf + ' differs') self.assertEqual(src.branchtags(), dest.branchtags()) srcbi = pickle.load(open(os.path.join(src.path, 'svn', 'branch_info'))) destbi = pickle.load(open(os.path.join(dest.path, 'svn', 'branch_info'))) @@ -101,11 +142,17 @@ def buildmethod(case, name, stupid, single): return m +skip = set([ + 'project_root_not_repo_root.svndump', + 'corrupt.svndump', +]) + attrs = {'_do_case': _do_case, + '_run_assertions': _run_assertions, } 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_single_dir_clone.py b/tests/test_single_dir_clone.py index 52ebe0f..7c3cb3e 100644 --- a/tests/test_single_dir_clone.py +++ b/tests/test_single_dir_clone.py @@ -44,6 +44,15 @@ class TestSingleDir(test_util.TestBase): self.assertEqual(repo.branchtags().keys(), ['default', ]) self.assertEqual(repo['default'].manifest().keys(), oldmanifest) + def test_clone_subdir_is_file_prefix(self, stupid=False): + FIXTURE = 'subdir_is_file_prefix.svndump' + repo = self._load_fixture_and_fetch(FIXTURE, + stupid=stupid, + layout='single', + subdir=test_util.subdir[FIXTURE]) + self.assertEqual(repo.branchtags().keys(), ['default']) + self.assertEqual(repo['tip'].manifest().keys(), ['flaf.txt']) + def test_externals_single(self): repo = self._load_fixture_and_fetch('externals.svndump', stupid=False, diff --git a/tests/test_startrev.py b/tests/test_startrev.py deleted file mode 100644 index e544bff..0000000 --- a/tests/test_startrev.py +++ /dev/null @@ -1,71 +0,0 @@ -import test_util - -import os -import unittest - -def _do_case(self, name, subdir, stupid): - wc_base = self.wc_path - self.wc_path = wc_base + '_full' - headclone = self._load_fixture_and_fetch(name, subdir=subdir, stupid=stupid, - layout='single', startrev='HEAD') - self.wc_path = wc_base + '_head' - fullclone = self._load_fixture_and_fetch(name, subdir=subdir, stupid=stupid, - layout='single') - - fulltip = fullclone['tip'] - headtip = headclone['tip'] - # viewing diff's of lists of files is easier on the eyes - self.assertMultiLineEqual('\n'.join(fulltip), '\n'.join(headtip)) - - for f in fulltip: - self.assertMultiLineEqual(fulltip[f].data(), headtip[f].data()) - - self.assertNotEqual(len(fullclone), 0, "full clone shouldn't be empty") - self.assertEqual(len(headclone), 1, - "shallow clone should have just one revision, not %d" - % len(headclone)) - -def buildmethod(case, name, subdir, stupid): - m = lambda self: self._do_case(case, subdir.strip('/'), stupid) - m.__name__ = name - m.__doc__ = ('Test clone with startrev on %s%s with %s replay.' % - (case, subdir, (stupid and 'stupid') or 'real')) - return m - - -# these fixtures contain no files at HEAD and would result in empty clones -nofiles = set([ - 'binaryfiles.svndump', - 'binaryfiles-broken.svndump', - 'emptyrepo.svndump', -]) - -# these fixtures contain no files in trunk at HEAD and would result in an empty -# shallow clone if cloning trunk, so we use another subdirectory -subdirmap = { - 'commit-to-tag.svndump': '/branches/magic', - 'pushexternals.svndump': '', - 'tag_name_same_as_branch.svndump': '/branches/magic', -} - -attrs = {'_do_case': _do_case, - } - -for case in [f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')]: - if case in nofiles: - continue - - subdir = test_util.subdir.get(case, '') + subdirmap.get(case, '/trunk') - - bname = 'test_' + case[:-len('.svndump')] - attrs[bname] = buildmethod(case, bname, subdir, False) - name = bname + '_stupid' - attrs[name] = buildmethod(case, name, subdir, True) - -StartRevTests = type('StartRevTests', (test_util.TestBase,), attrs) - - -def suite(): - all_tests = [unittest.TestLoader().loadTestsFromTestCase(StartRevTests), - ] - return unittest.TestSuite(all_tests) diff --git a/tests/test_tags.py b/tests/test_tags.py index 630e039..29afcdb 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -112,6 +112,7 @@ rename a tag "You should check that before assuming issues with this test.\n") wc2_path = self.wc_path + '2' src, dest = test_util.hgclone(repo.ui, self.wc_path, wc2_path, update=False) + dest = test_util.getlocalpeer(dest) svncommands.rebuildmeta(repo.ui, dest, args=[test_util.fileurl(repo_path), ]) diff --git a/tests/test_updatemeta.py b/tests/test_updatemeta.py new file mode 100644 index 0000000..37bb1f8 --- /dev/null +++ b/tests/test_updatemeta.py @@ -0,0 +1,81 @@ +import test_util + +import os +import pickle +import unittest +import test_rebuildmeta + +from mercurial import context +from mercurial import extensions +from mercurial import hg +from mercurial import ui + +from hgsubversion import svncommands +from hgsubversion import svnmeta + + + +def _do_case(self, name, stupid, single): + subdir = test_util.subdir.get(name, '') + layout = 'auto' + if single: + layout = 'single' + repo, repo_path = self.load_and_fetch(name, subdir=subdir, stupid=stupid, + layout=layout) + assert len(self.repo) > 0 + wc2_path = self.wc_path + '_clone' + u = ui.ui() + src, dest = test_util.hgclone(u, self.wc_path, wc2_path, update=False) + src = test_util.getlocalpeer(src) + dest = test_util.getlocalpeer(dest) + + # insert a wrapper that prevents calling changectx.children() + def failfn(orig, ctx): + self.fail('calling %s is forbidden; it can cause massive slowdowns ' + 'when rebuilding large repositories' % orig) + + origchildren = getattr(context.changectx, 'children') + extensions.wrapfunction(context.changectx, 'children', failfn) + + # test updatemeta on an empty repo + try: + svncommands.updatemeta(u, dest, + args=[test_util.fileurl(repo_path + + subdir), ]) + finally: + # remove the wrapper + context.changectx.children = origchildren + + self._run_assertions(name, stupid, single, src, dest, u) + + +def _run_assertions(self, name, stupid, single, src, dest, u): + test_rebuildmeta._run_assertions(self, name, stupid, single, src, dest, u) + + +skip = set([ + 'project_root_not_repo_root.svndump', + 'corrupt.svndump', +]) + +attrs = {'_do_case': _do_case, + '_run_assertions': _run_assertions, + } +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 in skip: + continue + bname = 'test_' + case[:-len('.svndump')] + attrs[bname] = test_rebuildmeta.buildmethod(case, bname, False, False) + name = bname + '_stupid' + attrs[name] = test_rebuildmeta.buildmethod(case, name, True, False) + name = bname + '_single' + attrs[name] = test_rebuildmeta.buildmethod(case, name, False, True) + +UpdateMetaTests = type('UpdateMetaTests', (test_util.TestBase,), attrs) + + +def suite(): + all_tests = [unittest.TestLoader().loadTestsFromTestCase(UpdateMetaTests), + ] + return unittest.TestSuite(all_tests) diff --git a/tests/test_util.py b/tests/test_util.py index 0343fe2..c2c67de 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -7,6 +7,7 @@ import shutil import stat import subprocess import sys +import tarfile import tempfile import unittest import urllib @@ -37,6 +38,7 @@ except AttributeError: SkipTest = None from hgsubversion import util +from hgsubversion import svnwrap # Documentation for Subprocess.Popen() says: # "Note that on Windows, you cannot set close_fds to true and @@ -96,11 +98,17 @@ subdir = {'truncatedhistory.svndump': '/project2', 'project_name_with_space.svndump': '/project name', 'non_ascii_path_1.svndump': '/b\xC3\xB8b', 'non_ascii_path_2.svndump': '/b%C3%B8b', + 'subdir_is_file_prefix.svndump': '/flaf', } FIXTURES = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'fixtures') +def getlocalpeer(repo): + localrepo = getattr(repo, 'local', lambda: repo)() + if isinstance(localrepo, bool): + localrepo = repo + return localrepo def _makeskip(name, message): if SkipTest: @@ -143,7 +151,10 @@ def fileurl(path): path = os.path.abspath(path).replace(os.sep, '/') drive, path = os.path.splitdrive(path) if drive: - drive = '/' + drive + # In svn 1.7, the swig svn wrapper returns local svn URLs + # with an uppercase drive letter, try to match that to + # simplify svn info tests. + drive = '/' + drive.upper() url = 'file://%s%s' % (drive, path) return url @@ -158,11 +169,8 @@ def testui(stupid=False, layout='auto', startrev=0): return u def dispatch(cmd): - try: - req = dispatchmod.request(cmd) - dispatchmod.dispatch(req) - except AttributeError, e: - dispatchmod.dispatch(cmd) + cmd = getattr(dispatchmod, 'request', lambda x: x)(cmd) + dispatchmod.dispatch(cmd) def rmtree(path): # Read-only files cannot be removed under Windows @@ -198,12 +206,12 @@ def _verify_our_modules(): 'from the wrong path!' ) -def hgclone(ui, source, dest, update=True): +def hgclone(ui, source, dest, update=True, rev=None): if getattr(hg, 'peer', None): # Since 1.9 (d976542986d2) - src, dest = hg.clone(ui, {}, source, dest, update=update) + src, dest = hg.clone(ui, {}, source, dest, update=update, rev=rev) else: - src, dest = hg.clone(ui, source, dest, update=update) + src, dest = hg.clone(ui, source, dest, update=update, rev=rev) return src, dest def svnls(repo_path, path, rev='HEAD'): @@ -236,6 +244,9 @@ class TestBase(unittest.TestCase): def setUp(self): _verify_our_modules() + # the Python 2.7 default of 640 is obnoxiously low + self.maxDiff = 4096 + self.oldenv = dict([(k, os.environ.get(k, None),) for k in ('LANG', 'LC_ALL', 'HGRCPATH',)]) self.oldt = i18n.t @@ -255,6 +266,10 @@ class TestBase(unittest.TestCase): self.wc_path = '%s/testrepo_wc' % self.tmpdir self.svn_wc = None + self.config_dir = self.tmpdir + svnwrap.common._svn_config_dir = self.config_dir + self.setup_svn_config('') + # Previously, we had a MockUI class that wrapped ui, and giving access # to the stream. The ui.pushbuffer() and ui.popbuffer() can be used # instead. Using the regular UI class, with all stderr redirected to @@ -263,6 +278,10 @@ class TestBase(unittest.TestCase): self.patch = (ui.ui.write_err, ui.ui.write) setattr(ui.ui, self.patch[0].func_name, self.patch[1]) + def setup_svn_config(self, config): + with open(self.config_dir + '/config', 'w') as c: + c.write(config) + def _makerepopath(self): self.repocount += 1 return '%s/testrepo-%d' % (self.tmpdir, self.repocount) @@ -297,15 +316,31 @@ class TestBase(unittest.TestCase): proc.communicate() return path - def load_and_fetch(self, fixture_name, subdir=None, stupid=False, - layout='auto', startrev=0, externals=None, - noupdate=True): + def load_repo_tarball(self, fixture_name): + '''Extracts a tarball of an svn repo and returns the svn repo path.''' + path = self._makerepopath() + assert not os.path.exists(path) + os.mkdir(path) + tarball = tarfile.open(os.path.join(FIXTURES, fixture_name)) + # This is probably somewhat fragile, but I'm not sure how to + # do better in particular, I think it assumes that the tar + # entries are in the right order and that directories appear + # before their contents. This is a valid assummption for sane + # tarballs, from what I can tell. In particular, for a simple + # tarball of a svn repo with paths relative to the repo root, + # it seems to work + for entry in tarball: + tarball.extract(entry, path) + return path + + def fetch(self, repo_path, subdir=None, stupid=False, layout='auto', + startrev=0, externals=None, noupdate=True, dest=None, rev=None, + config=None): if layout == 'single': if subdir is None: subdir = 'trunk' elif subdir is None: subdir = '' - repo_path = self.load_svndump(fixture_name) projectpath = repo_path if subdir: projectpath += '/' + subdir @@ -321,12 +356,27 @@ class TestBase(unittest.TestCase): cmd.append('--stupid') if noupdate: cmd.append('--noupdate') + if rev is not None: + cmd.append('--rev=%s' % rev) + config = dict(config or {}) if externals: - cmd[:0] = ['--config', 'hgsubversion.externals=%s' % externals] + config['hgsubversion.externals'] = str(externals) + for k,v in reversed(sorted(config.iteritems())): + cmd[:0] = ['--config', '%s=%s' % (k, v)] dispatch(cmd) - return hg.repository(testui(), self.wc_path), repo_path + return hg.repository(testui(), self.wc_path) + + def load_and_fetch(self, fixture_name, *args, **opts): + if fixture_name.endswith('.svndump'): + repo_path = self.load_svndump(fixture_name) + elif fixture_name.endswith('tar.gz'): + repo_path = self.load_repo_tarball(fixture_name) + else: + assert False, 'Unknown fixture type' + + return self.fetch(repo_path, *args, **opts), repo_path def _load_fixture_and_fetch(self, *args, **kwargs): repo, repo_path = self.load_and_fetch(*args, **kwargs) @@ -470,7 +520,7 @@ class TestBase(unittest.TestCase): msg = '%s\n%s' % (msg or '', diff) raise self.failureException, msg - def draw(self, repo): + def getgraph(self, repo): """Helper function displaying a repository graph, especially useful when debugging comprehensive tests. """ @@ -487,4 +537,10 @@ summary: {desc|firstline} files: {files} """ + _ui.pushbuffer() graphlog.graphlog(_ui, repo, rev=None, template=templ) + return _ui.popbuffer() + + def draw(self, repo): + sys.stdout.write(self.getgraph(repo)) + diff --git a/tests/test_utility_commands.py b/tests/test_utility_commands.py index 10926e2..338dc82 100644 --- a/tests/test_utility_commands.py +++ b/tests/test_utility_commands.py @@ -14,6 +14,7 @@ from mercurial import util as hgutil from hgsubversion import util from hgsubversion import svncommands +from hgsubversion import verify from hgsubversion import wrappers expected_info_output = '''URL: %(repourl)s/%(branch)s @@ -66,6 +67,23 @@ class UtilityTests(test_util.TestBase): 'rev': 5, }) self.assertMultiLineEqual(actual, expected) + destpath = self.wc_path + '_clone' + test_util.hgclone(u, self.repo, destpath) + repo2 = hg.repository(u, destpath) + repo2.ui.setconfig('paths', 'default-push', + self.repo.ui.config('paths', 'default')) + hg.update(repo2, 'default') + svncommands.rebuildmeta(u, repo2, []) + u.pushbuffer() + svncommands.info(u, repo2) + actual = u.popbuffer() + expected = (expected_info_output % + {'date': '2008-10-08 01:39:29 +0000 (Wed, 08 Oct 2008)', + 'repourl': repourl(repo_path), + 'branch': 'trunk', + 'rev': 6, + }) + self.assertMultiLineEqual(actual, expected) def test_info_single(self): repo, repo_path = self.load_and_fetch('two_heads.svndump', subdir='trunk') @@ -245,28 +263,82 @@ class UtilityTests(test_util.TestBase): authors=author_path) self.assertMultiLineEqual(open(author_path).read(), 'Augie=\nevil=\n') - def test_svnverify(self): + def test_svnverify(self, stupid=False): repo, repo_path = self.load_and_fetch('binaryfiles.svndump', - noupdate=False) - ret = svncommands.verify(self.ui(), repo, [], rev=1) + noupdate=False, stupid=stupid) + ret = verify.verify(self.ui(), repo, [], rev=1, stupid=stupid) self.assertEqual(0, ret) repo_path = self.load_svndump('binaryfiles-broken.svndump') u = self.ui() u.pushbuffer() - ret = svncommands.verify(u, repo, [test_util.fileurl(repo_path)], - rev=1) + ret = verify.verify(u, repo, [test_util.fileurl(repo_path)], + rev=1, stupid=stupid) output = u.popbuffer() self.assertEqual(1, ret) output = re.sub(r'file://\S+', 'file://', output) - self.assertEqual("""\ + 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_stupid(self): + self.test_svnverify(True) + + def test_corruption(self, stupid=False): + SUCCESS = 0 + FAILURE = 1 + + repo, repo_path = self.load_and_fetch('correct.svndump', layout='single', + subdir='', stupid=stupid) + + ui = self.ui() + + self.assertEqual(SUCCESS, verify.verify(ui, self.repo, rev='tip', + stupid=stupid)) + + corrupt_source = test_util.fileurl(self.load_svndump('corrupt.svndump')) + + repo.ui.setconfig('paths', 'default', corrupt_source) + + ui.pushbuffer() + code = verify.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 test_corruption_stupid(self): + self.test_corruption(True) + + def test_svnrebuildmeta(self): + otherpath = self.load_svndump('binaryfiles-broken.svndump') + otherurl = test_util.fileurl(otherpath) + self.load_and_fetch('replace_trunk_with_branch.svndump') + # rebuildmeta with original repo + svncommands.rebuildmeta(self.ui(), repo=self.repo, args=[]) + # rebuildmeta with unrelated repo + self.assertRaises(hgutil.Abort, + svncommands.rebuildmeta, + self.ui(), repo=self.repo, args=[otherurl]) + # rebuildmeta --unsafe-skip-uuid-check with unrelated repo + svncommands.rebuildmeta(self.ui(), repo=self.repo, args=[otherurl], + unsafe_skip_uuid_check=True) + def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(UtilityTests), ] |