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 |
