Mercurial > hgsubversion
comparison tests/test_util.py @ 1106:5cb6c95e0283 stable
Merge default and stable so I can do stable releases again.
author | Augie Fackler <raf@durin42.com> |
---|---|
date | Tue, 11 Feb 2014 12:48:49 -0500 |
parents | 4a92eb1484ba |
children | a22d4972e01f |
comparison
equal
deleted
inserted
replaced
1020:b5b1fce26f1f | 1106:5cb6c95e0283 |
---|---|
20 from mercurial import context | 20 from mercurial import context |
21 from mercurial import dispatch as dispatchmod | 21 from mercurial import dispatch as dispatchmod |
22 from mercurial import hg | 22 from mercurial import hg |
23 from mercurial import i18n | 23 from mercurial import i18n |
24 from mercurial import node | 24 from mercurial import node |
25 from mercurial import scmutil | |
25 from mercurial import ui | 26 from mercurial import ui |
26 from mercurial import util | 27 from mercurial import util |
27 from mercurial import extensions | 28 from mercurial import extensions |
29 | |
30 try: | |
31 from mercurial import obsolete | |
32 except ImportError: | |
33 obsolete = None | |
28 | 34 |
29 try: | 35 try: |
30 SkipTest = unittest.SkipTest | 36 SkipTest = unittest.SkipTest |
31 except AttributeError: | 37 except AttributeError: |
32 try: | 38 try: |
98 'project_name_with_space.svndump': '/project name', | 104 'project_name_with_space.svndump': '/project name', |
99 'non_ascii_path_1.svndump': '/b\xC3\xB8b', | 105 'non_ascii_path_1.svndump': '/b\xC3\xB8b', |
100 'non_ascii_path_2.svndump': '/b%C3%B8b', | 106 'non_ascii_path_2.svndump': '/b%C3%B8b', |
101 'subdir_is_file_prefix.svndump': '/flaf', | 107 'subdir_is_file_prefix.svndump': '/flaf', |
102 } | 108 } |
109 # map defining the layouts of the fixtures we can use with custom layout | |
110 # these are really popular layouts, so I gave them names | |
111 trunk_only = { | |
112 'default': 'trunk', | |
113 } | |
114 trunk_dev_branch = { | |
115 'default': 'trunk', | |
116 'dev_branch': 'branches/dev_branch', | |
117 } | |
118 custom = { | |
119 'addspecial.svndump': { | |
120 'default': 'trunk', | |
121 'foo': 'branches/foo', | |
122 }, | |
123 'binaryfiles.svndump': trunk_only, | |
124 'branch_create_with_dir_delete.svndump': trunk_dev_branch, | |
125 'branch_delete_parent_dir.svndump': trunk_dev_branch, | |
126 'branchmap.svndump': { | |
127 'default': 'trunk', | |
128 'badname': 'branches/badname', | |
129 'feature': 'branches/feature', | |
130 }, | |
131 'branch_prop_edit.svndump': trunk_dev_branch, | |
132 'branch_rename_to_trunk.svndump': { | |
133 'default': 'trunk', | |
134 'dev_branch': 'branches/dev_branch', | |
135 'old_trunk': 'branches/old_trunk', | |
136 }, | |
137 'copies.svndump': trunk_only, | |
138 'copybeforeclose.svndump': { | |
139 'default': 'trunk', | |
140 'test': 'branches/test' | |
141 }, | |
142 'delentries.svndump': trunk_only, | |
143 'delete_restore_trunk.svndump': trunk_only, | |
144 'empty_dir_in_trunk_not_repo_root.svndump': trunk_only, | |
145 'executebit.svndump': trunk_only, | |
146 'filecase.svndump': trunk_only, | |
147 'file_not_in_trunk_root.svndump': trunk_only, | |
148 'project_name_with_space.svndump': trunk_dev_branch, | |
149 'pushrenames.svndump': trunk_only, | |
150 'rename_branch_parent_dir.svndump': trunk_dev_branch, | |
151 'renamedproject.svndump': { | |
152 'default': 'trunk', | |
153 'branch': 'branches/branch', | |
154 }, | |
155 'renames.svndump': { | |
156 'default': 'trunk', | |
157 'branch1': 'branches/branch1', | |
158 }, | |
159 'replace_branch_with_branch.svndump': { | |
160 'default': 'trunk', | |
161 'branch1': 'branches/branch1', | |
162 'branch2': 'branches/branch2', | |
163 }, | |
164 'replace_trunk_with_branch.svndump': { | |
165 'default': 'trunk', | |
166 'test': 'branches/test', | |
167 }, | |
168 'revert.svndump': trunk_only, | |
169 'siblingbranchfix.svndump': { | |
170 'default': 'trunk', | |
171 'wrongbranch': 'branches/wrongbranch', | |
172 }, | |
173 'simple_branch.svndump': { | |
174 'default': 'trunk', | |
175 'the_branch': 'branches/the_branch', | |
176 }, | |
177 'spaces-in-path.svndump': trunk_dev_branch, | |
178 'symlinks.svndump': trunk_only, | |
179 'truncatedhistory.svndump': trunk_only, | |
180 'unorderedbranch.svndump': { | |
181 'default': 'trunk', | |
182 'branch': 'branches/branch', | |
183 }, | |
184 'unrelatedbranch.svndump': { | |
185 'default': 'trunk', | |
186 'branch1': 'branches/branch1', | |
187 'branch2': 'branches/branch2', | |
188 }, | |
189 } | |
103 | 190 |
104 FIXTURES = os.path.join(os.path.abspath(os.path.dirname(__file__)), | 191 FIXTURES = os.path.join(os.path.abspath(os.path.dirname(__file__)), |
105 'fixtures') | 192 'fixtures') |
106 | 193 |
107 def getlocalpeer(repo): | 194 def getlocalpeer(repo): |
108 localrepo = getattr(repo, 'local', lambda: repo)() | 195 localrepo = getattr(repo, 'local', lambda: repo)() |
109 if isinstance(localrepo, bool): | 196 if isinstance(localrepo, bool): |
110 localrepo = repo | 197 localrepo = repo |
111 return localrepo | 198 return localrepo |
199 | |
200 def repolen(repo): | |
201 """Naively calculate the amount of available revisions in a repository. | |
202 | |
203 this is usually equal to len(repo) -- except in the face of | |
204 obsolete revisions. | |
205 """ | |
206 # kind of nasty way of calculating the length, but fortunately, | |
207 # our test repositories tend to be rather small | |
208 return len([r for r in repo]) | |
112 | 209 |
113 def _makeskip(name, message): | 210 def _makeskip(name, message): |
114 if SkipTest: | 211 if SkipTest: |
115 def skip(*args, **kwargs): | 212 def skip(*args, **kwargs): |
116 raise SkipTest(message) | 213 raise SkipTest(message) |
142 return | 239 return |
143 if not isinstance(option, str): | 240 if not isinstance(option, str): |
144 raise TypeError('requiresoption takes a string argument') | 241 raise TypeError('requiresoption takes a string argument') |
145 return decorator | 242 return decorator |
146 | 243 |
244 def requiresreplay(method): | |
245 '''Skip a test in stupid mode.''' | |
246 def test(self, *args, **kwargs): | |
247 if self.stupid: | |
248 if SkipTest: | |
249 raise SkipTest("test requires replay mode") | |
250 else: | |
251 return method(self, *args, **kwargs) | |
252 | |
253 test.__name__ = method.__name__ | |
254 return test | |
255 | |
147 def filtermanifest(manifest): | 256 def filtermanifest(manifest): |
148 return [f for f in manifest if f not in util.ignoredfiles] | 257 return [f for f in manifest if f not in util.ignoredfiles] |
149 | 258 |
150 def fileurl(path): | 259 def fileurl(path): |
151 path = os.path.abspath(path).replace(os.sep, '/') | 260 path = os.path.abspath(path).replace(os.sep, '/') |
168 u.setconfig('hgsubversion', 'startrev', startrev) | 277 u.setconfig('hgsubversion', 'startrev', startrev) |
169 return u | 278 return u |
170 | 279 |
171 def dispatch(cmd): | 280 def dispatch(cmd): |
172 cmd = getattr(dispatchmod, 'request', lambda x: x)(cmd) | 281 cmd = getattr(dispatchmod, 'request', lambda x: x)(cmd) |
173 dispatchmod.dispatch(cmd) | 282 return dispatchmod.dispatch(cmd) |
174 | 283 |
175 def rmtree(path): | 284 def rmtree(path): |
176 # Read-only files cannot be removed under Windows | 285 # Read-only files cannot be removed under Windows |
177 for root, dirs, files in os.walk(path): | 286 for root, dirs, files in os.walk(path): |
178 for f in files: | 287 for f in files: |
238 stdout, stderr = p.communicate() | 347 stdout, stderr = p.communicate() |
239 if p.returncode: | 348 if p.returncode: |
240 raise Exception('svn ls failed on %s: %r' % (path, stderr)) | 349 raise Exception('svn ls failed on %s: %r' % (path, stderr)) |
241 return stdout.strip() | 350 return stdout.strip() |
242 | 351 |
352 | |
353 def _obsolete_wrap(cls, name): | |
354 origfunc = getattr(cls, name) | |
355 | |
356 if not name.startswith('test_') or not origfunc: | |
357 return | |
358 | |
359 if not obsolete: | |
360 wrapper = _makeskip(name, 'obsolete not available') | |
361 else: | |
362 def wrapper(self, *args, **opts): | |
363 self.assertFalse(obsolete._enabled, 'obsolete was already active') | |
364 | |
365 obsolete._enabled = True | |
366 | |
367 try: | |
368 origfunc(self, *args, **opts) | |
369 self.assertTrue(obsolete._enabled, 'obsolete remains active') | |
370 finally: | |
371 obsolete._enabled = False | |
372 | |
373 if not wrapper: | |
374 return | |
375 | |
376 wrapper.__name__ = name + ' obsolete' | |
377 wrapper.__module__ = origfunc.__module__ | |
378 | |
379 if origfunc.__doc__: | |
380 firstline = origfunc.__doc__.strip().splitlines()[0] | |
381 wrapper.__doc__ = firstline + ' (obsolete)' | |
382 | |
383 assert getattr(cls, wrapper.__name__, None) is None | |
384 | |
385 setattr(cls, wrapper.__name__, wrapper) | |
386 | |
387 | |
388 def _stupid_wrap(cls, name): | |
389 origfunc = getattr(cls, name) | |
390 | |
391 if not name.startswith('test_') or not origfunc: | |
392 return | |
393 | |
394 def wrapper(self, *args, **opts): | |
395 self.assertFalse(self.stupid, 'stupid mode was already active') | |
396 | |
397 self.stupid = True | |
398 | |
399 try: | |
400 origfunc(self, *args, **opts) | |
401 finally: | |
402 self.stupid = False | |
403 | |
404 wrapper.__name__ = name + ' stupid' | |
405 wrapper.__module__ = origfunc.__module__ | |
406 | |
407 if origfunc.__doc__: | |
408 firstline = origfunc.__doc__.strip().splitlines()[0] | |
409 wrapper.__doc__ = firstline + ' (stupid)' | |
410 | |
411 assert getattr(cls, wrapper.__name__, None) is None | |
412 | |
413 setattr(cls, wrapper.__name__, wrapper) | |
414 | |
415 class TestMeta(type): | |
416 def __init__(cls, *args, **opts): | |
417 if cls.obsolete_mode_tests: | |
418 for origname in dir(cls): | |
419 _obsolete_wrap(cls, origname) | |
420 | |
421 if cls.stupid_mode_tests: | |
422 for origname in dir(cls): | |
423 _stupid_wrap(cls, origname) | |
424 | |
425 return super(TestMeta, cls).__init__(*args, **opts) | |
426 | |
243 class TestBase(unittest.TestCase): | 427 class TestBase(unittest.TestCase): |
428 __metaclass__ = TestMeta | |
429 | |
430 obsolete_mode_tests = False | |
431 stupid_mode_tests = False | |
432 | |
433 stupid = False | |
434 | |
244 def setUp(self): | 435 def setUp(self): |
245 _verify_our_modules() | 436 _verify_our_modules() |
437 if 'hgsubversion' in sys.modules: | |
438 sys.modules['hgext_hgsubversion'] = sys.modules['hgsubversion'] | |
246 | 439 |
247 # the Python 2.7 default of 640 is obnoxiously low | 440 # the Python 2.7 default of 640 is obnoxiously low |
248 self.maxDiff = 4096 | 441 self.maxDiff = 4096 |
249 | 442 |
250 self.oldenv = dict([(k, os.environ.get(k, None),) for k in | 443 self.oldenv = dict([(k, os.environ.get(k, None),) for k in |
254 i18n.t = gettext.translation('hg', i18n.localedir, fallback=True) | 447 i18n.t = gettext.translation('hg', i18n.localedir, fallback=True) |
255 | 448 |
256 self.oldwd = os.getcwd() | 449 self.oldwd = os.getcwd() |
257 self.tmpdir = tempfile.mkdtemp( | 450 self.tmpdir = tempfile.mkdtemp( |
258 'svnwrap_test', dir=os.environ.get('HGSUBVERSION_TEST_TEMP', None)) | 451 'svnwrap_test', dir=os.environ.get('HGSUBVERSION_TEST_TEMP', None)) |
452 os.chdir(self.tmpdir) | |
259 self.hgrc = os.path.join(self.tmpdir, '.hgrc') | 453 self.hgrc = os.path.join(self.tmpdir, '.hgrc') |
260 os.environ['HGRCPATH'] = self.hgrc | 454 os.environ['HGRCPATH'] = self.hgrc |
455 scmutil._rcpath = None | |
261 rc = open(self.hgrc, 'w') | 456 rc = open(self.hgrc, 'w') |
457 rc.write('[ui]\nusername=test-user\n') | |
262 for l in '[extensions]', 'hgsubversion=': | 458 for l in '[extensions]', 'hgsubversion=': |
263 print >> rc, l | 459 print >> rc, l |
264 | 460 |
265 self.repocount = 0 | 461 self.repocount = 0 |
266 self.wc_path = '%s/testrepo_wc' % self.tmpdir | 462 self.wc_path = '%s/testrepo_wc' % self.tmpdir |
277 # setups. | 473 # setups. |
278 self.patch = (ui.ui.write_err, ui.ui.write) | 474 self.patch = (ui.ui.write_err, ui.ui.write) |
279 setattr(ui.ui, self.patch[0].func_name, self.patch[1]) | 475 setattr(ui.ui, self.patch[0].func_name, self.patch[1]) |
280 | 476 |
281 def setup_svn_config(self, config): | 477 def setup_svn_config(self, config): |
282 with open(self.config_dir + '/config', 'w') as c: | 478 c = open(self.config_dir + '/config', 'w') |
479 try: | |
283 c.write(config) | 480 c.write(config) |
481 finally: | |
482 c.close() | |
284 | 483 |
285 def _makerepopath(self): | 484 def _makerepopath(self): |
286 self.repocount += 1 | 485 self.repocount += 1 |
287 return '%s/testrepo-%d' % (self.tmpdir, self.repocount) | 486 return '%s/testrepo-%d' % (self.tmpdir, self.repocount) |
288 | 487 |
297 os.chdir(self.oldwd) | 496 os.chdir(self.oldwd) |
298 setattr(ui.ui, self.patch[0].func_name, self.patch[0]) | 497 setattr(ui.ui, self.patch[0].func_name, self.patch[0]) |
299 | 498 |
300 _verify_our_modules() | 499 _verify_our_modules() |
301 | 500 |
302 def ui(self, stupid=False, layout='auto'): | 501 def ui(self, layout='auto'): |
303 return testui(stupid, layout) | 502 return testui(self.stupid, layout) |
304 | 503 |
305 def load_svndump(self, fixture_name): | 504 def load_svndump(self, fixture_name): |
306 '''Loads an svnadmin dump into a fresh repo. Return the svn repo | 505 '''Loads an svnadmin dump into a fresh repo. Return the svn repo |
307 path. | 506 path. |
308 ''' | 507 ''' |
331 # it seems to work | 530 # it seems to work |
332 for entry in tarball: | 531 for entry in tarball: |
333 tarball.extract(entry, path) | 532 tarball.extract(entry, path) |
334 return path | 533 return path |
335 | 534 |
336 def fetch(self, repo_path, subdir=None, stupid=False, layout='auto', | 535 def fetch(self, repo_path, subdir=None, layout='auto', |
337 startrev=0, externals=None, noupdate=True, dest=None, rev=None, | 536 startrev=0, externals=None, noupdate=True, dest=None, rev=None, |
338 config=None): | 537 config=None): |
339 if layout == 'single': | 538 if layout == 'single': |
340 if subdir is None: | 539 if subdir is None: |
341 subdir = 'trunk' | 540 subdir = 'trunk' |
350 '--layout=%s' % layout, | 549 '--layout=%s' % layout, |
351 '--startrev=%s' % startrev, | 550 '--startrev=%s' % startrev, |
352 fileurl(projectpath), | 551 fileurl(projectpath), |
353 self.wc_path, | 552 self.wc_path, |
354 ] | 553 ] |
355 if stupid: | 554 if self.stupid: |
356 cmd.append('--stupid') | 555 cmd.append('--stupid') |
357 if noupdate: | 556 if noupdate: |
358 cmd.append('--noupdate') | 557 cmd.append('--noupdate') |
359 if rev is not None: | 558 if rev is not None: |
360 cmd.append('--rev=%s' % rev) | 559 cmd.append('--rev=%s' % rev) |
362 if externals: | 561 if externals: |
363 config['hgsubversion.externals'] = str(externals) | 562 config['hgsubversion.externals'] = str(externals) |
364 for k,v in reversed(sorted(config.iteritems())): | 563 for k,v in reversed(sorted(config.iteritems())): |
365 cmd[:0] = ['--config', '%s=%s' % (k, v)] | 564 cmd[:0] = ['--config', '%s=%s' % (k, v)] |
366 | 565 |
367 dispatch(cmd) | 566 r = dispatch(cmd) |
567 assert not r, 'fetch of %s failed' % projectpath | |
368 | 568 |
369 return hg.repository(testui(), self.wc_path) | 569 return hg.repository(testui(), self.wc_path) |
370 | 570 |
371 def load_and_fetch(self, fixture_name, *args, **opts): | 571 def load_and_fetch(self, fixture_name, *args, **opts): |
372 if fixture_name.endswith('.svndump'): | 572 if fixture_name.endswith('.svndump'): |
407 # define this as a property so that it reloads anytime we need it | 607 # define this as a property so that it reloads anytime we need it |
408 @property | 608 @property |
409 def repo(self): | 609 def repo(self): |
410 return hg.repository(testui(), self.wc_path) | 610 return hg.repository(testui(), self.wc_path) |
411 | 611 |
412 def pushrevisions(self, stupid=False, expected_extra_back=0): | 612 def pushrevisions(self, expected_extra_back=0): |
413 before = len(self.repo) | 613 before = repolen(self.repo) |
414 self.repo.ui.setconfig('hgsubversion', 'stupid', str(stupid)) | 614 self.repo.ui.setconfig('hgsubversion', 'stupid', str(self.stupid)) |
415 res = commands.push(self.repo.ui, self.repo) | 615 res = commands.push(self.repo.ui, self.repo) |
416 after = len(self.repo) | 616 after = repolen(self.repo) |
417 self.assertEqual(expected_extra_back, after - before) | 617 self.assertEqual(expected_extra_back, after - before) |
418 return res | 618 return res |
419 | 619 |
420 def svnco(self, repo_path, svnpath, rev, path): | 620 def svnco(self, repo_path, svnpath, rev, path): |
421 path = os.path.join(self.wc_path, path) | 621 path = os.path.join(self.wc_path, path) |
528 _ui = ui.ui() | 728 _ui = ui.ui() |
529 _ui.setconfig('extensions', 'graphlog', '') | 729 _ui.setconfig('extensions', 'graphlog', '') |
530 extensions.loadall(_ui) | 730 extensions.loadall(_ui) |
531 graphlog = extensions.find('graphlog') | 731 graphlog = extensions.find('graphlog') |
532 templ = """\ | 732 templ = """\ |
533 changeset: {rev}:{node|short} | 733 changeset: {rev}:{node|short} (r{svnrev}) |
534 branch: {branches} | 734 branch: {branches} |
535 tags: {tags} | 735 tags: {tags} |
536 summary: {desc|firstline} | 736 summary: {desc|firstline} |
537 files: {files} | 737 files: {files} |
538 | 738 |
541 graphlog.graphlog(_ui, repo, rev=None, template=templ) | 741 graphlog.graphlog(_ui, repo, rev=None, template=templ) |
542 return _ui.popbuffer() | 742 return _ui.popbuffer() |
543 | 743 |
544 def draw(self, repo): | 744 def draw(self, repo): |
545 sys.stdout.write(self.getgraph(repo)) | 745 sys.stdout.write(self.getgraph(repo)) |
546 |