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 |
