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