Mercurial > hgsubversion
comparison tests/test_util.py @ 1056:0932bb4d8870
tests: add a metaclass for triggering stupid on a class level
We use a metaclass similar to the tests for obsolete mode. This
metaclass deliberately duplicates each test for each mode; enabling
both means that each test function is run four times.
This makes it less likely that a fix is accidentally applied to replay
mode only, as new tests will often automatically cover both
modes. However, as certiain features remain deliberately unimplemented
in stupid modes -- filemaps being a notable example -- we also add a
decorator function for marking methods testing them.
We do this for reasons of both consistency and coverage; we avoid
littering the tests with *_stupid variants, and thus are less likely
to forget adding them. I already found a couple of bugs in stupid mode
thanks to this increased coverage.
author | Dan Villiom Podlaski Christiansen <danchr@gmail.com> |
---|---|
date | Fri, 09 Aug 2013 23:34:16 +0200 |
parents | 608e7c8740af |
children | 1afd308b8f46 |
comparison
equal
deleted
inserted
replaced
1055:2d7398fffd0d | 1056:0932bb4d8870 |
---|---|
158 return | 158 return |
159 if not isinstance(option, str): | 159 if not isinstance(option, str): |
160 raise TypeError('requiresoption takes a string argument') | 160 raise TypeError('requiresoption takes a string argument') |
161 return decorator | 161 return decorator |
162 | 162 |
163 def requiresreplay(method): | |
164 '''Skip a test in stupid mode.''' | |
165 def test(self, *args, **kwargs): | |
166 if self.stupid: | |
167 if SkipTest: | |
168 raise SkipTest(message) | |
169 else: | |
170 return method(self, *args, **kwargs) | |
171 | |
172 test.__name__ = method.__name__ | |
173 return test | |
174 | |
163 def filtermanifest(manifest): | 175 def filtermanifest(manifest): |
164 return [f for f in manifest if f not in util.ignoredfiles] | 176 return [f for f in manifest if f not in util.ignoredfiles] |
165 | 177 |
166 def fileurl(path): | 178 def fileurl(path): |
167 path = os.path.abspath(path).replace(os.sep, '/') | 179 path = os.path.abspath(path).replace(os.sep, '/') |
289 | 301 |
290 assert getattr(cls, wrapper.__name__, None) is None | 302 assert getattr(cls, wrapper.__name__, None) is None |
291 | 303 |
292 setattr(cls, wrapper.__name__, wrapper) | 304 setattr(cls, wrapper.__name__, wrapper) |
293 | 305 |
306 | |
307 def _stupid_wrap(cls, name): | |
308 origfunc = getattr(cls, name) | |
309 | |
310 if not name.startswith('test_') or not origfunc: | |
311 return | |
312 | |
313 def wrapper(self, *args, **opts): | |
314 self.assertFalse(self.stupid, 'stupid mode was already active') | |
315 | |
316 self.stupid = True | |
317 | |
318 try: | |
319 origfunc(self, *args, **opts) | |
320 finally: | |
321 self.stupid = False | |
322 | |
323 wrapper.__name__ = name + ' stupid' | |
324 wrapper.__module__ = origfunc.__module__ | |
325 | |
326 if origfunc.__doc__: | |
327 firstline = origfunc.__doc__.strip().splitlines()[0] | |
328 wrapper.__doc__ = firstline + ' (stupid)' | |
329 | |
330 assert getattr(cls, wrapper.__name__, None) is None | |
331 | |
332 setattr(cls, wrapper.__name__, wrapper) | |
333 | |
294 class TestMeta(type): | 334 class TestMeta(type): |
295 def __init__(cls, *args, **opts): | 335 def __init__(cls, *args, **opts): |
296 if cls.obsolete_mode_tests: | 336 if cls.obsolete_mode_tests: |
297 for origname in dir(cls): | 337 for origname in dir(cls): |
298 _obsolete_wrap(cls, origname) | 338 _obsolete_wrap(cls, origname) |
299 | 339 |
340 if cls.stupid_mode_tests: | |
341 for origname in dir(cls): | |
342 _stupid_wrap(cls, origname) | |
343 | |
300 return super(TestMeta, cls).__init__(*args, **opts) | 344 return super(TestMeta, cls).__init__(*args, **opts) |
301 | 345 |
302 class TestBase(unittest.TestCase): | 346 class TestBase(unittest.TestCase): |
303 __metaclass__ = TestMeta | 347 __metaclass__ = TestMeta |
304 | 348 |
305 obsolete_mode_tests = False | 349 obsolete_mode_tests = False |
350 stupid_mode_tests = False | |
351 | |
352 stupid = False | |
306 | 353 |
307 def setUp(self): | 354 def setUp(self): |
308 _verify_our_modules() | 355 _verify_our_modules() |
309 | 356 |
310 # the Python 2.7 default of 640 is obnoxiously low | 357 # the Python 2.7 default of 640 is obnoxiously low |
366 setattr(ui.ui, self.patch[0].func_name, self.patch[0]) | 413 setattr(ui.ui, self.patch[0].func_name, self.patch[0]) |
367 | 414 |
368 _verify_our_modules() | 415 _verify_our_modules() |
369 | 416 |
370 def ui(self, stupid=False, layout='auto'): | 417 def ui(self, stupid=False, layout='auto'): |
371 return testui(stupid, layout) | 418 return testui(self.stupid or stupid, layout) |
372 | 419 |
373 def load_svndump(self, fixture_name): | 420 def load_svndump(self, fixture_name): |
374 '''Loads an svnadmin dump into a fresh repo. Return the svn repo | 421 '''Loads an svnadmin dump into a fresh repo. Return the svn repo |
375 path. | 422 path. |
376 ''' | 423 ''' |
418 '--layout=%s' % layout, | 465 '--layout=%s' % layout, |
419 '--startrev=%s' % startrev, | 466 '--startrev=%s' % startrev, |
420 fileurl(projectpath), | 467 fileurl(projectpath), |
421 self.wc_path, | 468 self.wc_path, |
422 ] | 469 ] |
423 if stupid: | 470 if self.stupid or stupid: |
424 cmd.append('--stupid') | 471 cmd.append('--stupid') |
425 if noupdate: | 472 if noupdate: |
426 cmd.append('--noupdate') | 473 cmd.append('--noupdate') |
427 if rev is not None: | 474 if rev is not None: |
428 cmd.append('--rev=%s' % rev) | 475 cmd.append('--rev=%s' % rev) |
478 def repo(self): | 525 def repo(self): |
479 return hg.repository(testui(), self.wc_path) | 526 return hg.repository(testui(), self.wc_path) |
480 | 527 |
481 def pushrevisions(self, stupid=False, expected_extra_back=0): | 528 def pushrevisions(self, stupid=False, expected_extra_back=0): |
482 before = repolen(self.repo) | 529 before = repolen(self.repo) |
483 self.repo.ui.setconfig('hgsubversion', 'stupid', str(stupid)) | 530 self.repo.ui.setconfig('hgsubversion', 'stupid', |
531 str(self.stupid or stupid)) | |
484 res = commands.push(self.repo.ui, self.repo) | 532 res = commands.push(self.repo.ui, self.repo) |
485 after = repolen(self.repo) | 533 after = repolen(self.repo) |
486 self.assertEqual(expected_extra_back, after - before) | 534 self.assertEqual(expected_extra_back, after - before) |
487 return res | 535 return res |
488 | 536 |