1# Copyright (c) Twisted Matrix Laboratories. 2# See LICENSE for details. 3 4import StringIO 5import gc 6import re 7import sys 8import textwrap 9import types 10 11from twisted.trial import unittest 12from twisted.trial.runner import ( 13 TrialRunner, TestSuite, DestructiveTestSuite, TestLoader) 14from twisted.trial._dist.disttrial import DistTrialRunner 15from twisted.scripts import trial 16from twisted.python import util 17from twisted.python.usage import UsageError 18from twisted.python.filepath import FilePath 19 20from twisted.trial.test.test_loader import testNames 21 22pyunit = __import__('unittest') 23 24 25def sibpath(filename): 26 """ 27 For finding files in twisted/trial/test 28 """ 29 return util.sibpath(__file__, filename) 30 31 32 33class ForceGarbageCollection(unittest.SynchronousTestCase): 34 """ 35 Tests for the --force-gc option. 36 """ 37 38 def setUp(self): 39 self.config = trial.Options() 40 self.log = [] 41 self.patch(gc, 'collect', self.collect) 42 test = pyunit.FunctionTestCase(self.simpleTest) 43 self.test = TestSuite([test, test]) 44 45 46 def simpleTest(self): 47 """ 48 A simple test method that records that it was run. 49 """ 50 self.log.append('test') 51 52 53 def collect(self): 54 """ 55 A replacement for gc.collect that logs calls to itself. 56 """ 57 self.log.append('collect') 58 59 60 def makeRunner(self): 61 """ 62 Return a L{TrialRunner} object that is safe to use in tests. 63 """ 64 runner = trial._makeRunner(self.config) 65 runner.stream = StringIO.StringIO() 66 return runner 67 68 69 def test_forceGc(self): 70 """ 71 Passing the --force-gc option to the trial script forces the garbage 72 collector to run before and after each test. 73 """ 74 self.config['force-gc'] = True 75 self.config.postOptions() 76 runner = self.makeRunner() 77 runner.run(self.test) 78 self.assertEqual(self.log, ['collect', 'test', 'collect', 79 'collect', 'test', 'collect']) 80 81 82 def test_unforceGc(self): 83 """ 84 By default, no garbage collection is forced. 85 """ 86 self.config.postOptions() 87 runner = self.makeRunner() 88 runner.run(self.test) 89 self.assertEqual(self.log, ['test', 'test']) 90 91 92 93class TestSuiteUsed(unittest.SynchronousTestCase): 94 """ 95 Check the category of tests suite used by the loader. 96 """ 97 98 def setUp(self): 99 """ 100 Create a trial configuration object. 101 """ 102 self.config = trial.Options() 103 104 105 def test_defaultSuite(self): 106 """ 107 By default, the loader should use L{DestructiveTestSuite} 108 """ 109 loader = trial._getLoader(self.config) 110 self.assertEqual(loader.suiteFactory, DestructiveTestSuite) 111 112 113 def test_untilFailureSuite(self): 114 """ 115 The C{until-failure} configuration uses the L{TestSuite} to keep 116 instances alive across runs. 117 """ 118 self.config['until-failure'] = True 119 loader = trial._getLoader(self.config) 120 self.assertEqual(loader.suiteFactory, TestSuite) 121 122 123 124class TestModuleTest(unittest.SynchronousTestCase): 125 def setUp(self): 126 self.config = trial.Options() 127 128 def tearDown(self): 129 self.config = None 130 131 def test_testNames(self): 132 """ 133 Check that the testNames helper method accurately collects the 134 names of tests in suite. 135 """ 136 self.assertEqual(testNames(self), [self.id()]) 137 138 def assertSuitesEqual(self, test1, names): 139 loader = TestLoader() 140 names1 = testNames(test1) 141 names2 = testNames(TestSuite(map(loader.loadByName, names))) 142 names1.sort() 143 names2.sort() 144 self.assertEqual(names1, names2) 145 146 def test_baseState(self): 147 self.assertEqual(0, len(self.config['tests'])) 148 149 def test_testmoduleOnModule(self): 150 """ 151 Check that --testmodule loads a suite which contains the tests 152 referred to in test-case-name inside its parameter. 153 """ 154 self.config.opt_testmodule(sibpath('moduletest.py')) 155 self.assertSuitesEqual(trial._getSuite(self.config), 156 ['twisted.trial.test.test_log']) 157 158 def test_testmoduleTwice(self): 159 """ 160 When the same module is specified with two --testmodule flags, it 161 should only appear once in the suite. 162 """ 163 self.config.opt_testmodule(sibpath('moduletest.py')) 164 self.config.opt_testmodule(sibpath('moduletest.py')) 165 self.assertSuitesEqual(trial._getSuite(self.config), 166 ['twisted.trial.test.test_log']) 167 168 def test_testmoduleOnSourceAndTarget(self): 169 """ 170 If --testmodule is specified twice, once for module A and once for 171 a module which refers to module A, then make sure module A is only 172 added once. 173 """ 174 self.config.opt_testmodule(sibpath('moduletest.py')) 175 self.config.opt_testmodule(sibpath('test_log.py')) 176 self.assertSuitesEqual(trial._getSuite(self.config), 177 ['twisted.trial.test.test_log']) 178 179 def test_testmoduleOnSelfModule(self): 180 """ 181 When given a module that refers to *itself* in the test-case-name 182 variable, check that --testmodule only adds the tests once. 183 """ 184 self.config.opt_testmodule(sibpath('moduleself.py')) 185 self.assertSuitesEqual(trial._getSuite(self.config), 186 ['twisted.trial.test.moduleself']) 187 188 def test_testmoduleOnScript(self): 189 """ 190 Check that --testmodule loads tests referred to in test-case-name 191 buffer variables. 192 """ 193 self.config.opt_testmodule(sibpath('scripttest.py')) 194 self.assertSuitesEqual(trial._getSuite(self.config), 195 ['twisted.trial.test.test_log', 196 'twisted.trial.test.test_class']) 197 198 def test_testmoduleOnNonexistentFile(self): 199 """ 200 Check that --testmodule displays a meaningful error message when 201 passed a non-existent filename. 202 """ 203 buffy = StringIO.StringIO() 204 stderr, sys.stderr = sys.stderr, buffy 205 filename = 'test_thisbetternoteverexist.py' 206 try: 207 self.config.opt_testmodule(filename) 208 self.assertEqual(0, len(self.config['tests'])) 209 self.assertEqual("File %r doesn't exist\n" % (filename,), 210 buffy.getvalue()) 211 finally: 212 sys.stderr = stderr 213 214 def test_testmoduleOnEmptyVars(self): 215 """ 216 Check that --testmodule adds no tests to the suite for modules 217 which lack test-case-name buffer variables. 218 """ 219 self.config.opt_testmodule(sibpath('novars.py')) 220 self.assertEqual(0, len(self.config['tests'])) 221 222 def test_testmoduleOnModuleName(self): 223 """ 224 Check that --testmodule does *not* support module names as arguments 225 and that it displays a meaningful error message. 226 """ 227 buffy = StringIO.StringIO() 228 stderr, sys.stderr = sys.stderr, buffy 229 moduleName = 'twisted.trial.test.test_script' 230 try: 231 self.config.opt_testmodule(moduleName) 232 self.assertEqual(0, len(self.config['tests'])) 233 self.assertEqual("File %r doesn't exist\n" % (moduleName,), 234 buffy.getvalue()) 235 finally: 236 sys.stderr = stderr 237 238 def test_parseLocalVariable(self): 239 declaration = '-*- test-case-name: twisted.trial.test.test_tests -*-' 240 localVars = trial._parseLocalVariables(declaration) 241 self.assertEqual({'test-case-name': 242 'twisted.trial.test.test_tests'}, 243 localVars) 244 245 def test_trailingSemicolon(self): 246 declaration = '-*- test-case-name: twisted.trial.test.test_tests; -*-' 247 localVars = trial._parseLocalVariables(declaration) 248 self.assertEqual({'test-case-name': 249 'twisted.trial.test.test_tests'}, 250 localVars) 251 252 def test_parseLocalVariables(self): 253 declaration = ('-*- test-case-name: twisted.trial.test.test_tests; ' 254 'foo: bar -*-') 255 localVars = trial._parseLocalVariables(declaration) 256 self.assertEqual({'test-case-name': 257 'twisted.trial.test.test_tests', 258 'foo': 'bar'}, 259 localVars) 260 261 def test_surroundingGuff(self): 262 declaration = ('## -*- test-case-name: ' 263 'twisted.trial.test.test_tests -*- #') 264 localVars = trial._parseLocalVariables(declaration) 265 self.assertEqual({'test-case-name': 266 'twisted.trial.test.test_tests'}, 267 localVars) 268 269 def test_invalidLine(self): 270 self.failUnlessRaises(ValueError, trial._parseLocalVariables, 271 'foo') 272 273 def test_invalidDeclaration(self): 274 self.failUnlessRaises(ValueError, trial._parseLocalVariables, 275 '-*- foo -*-') 276 self.failUnlessRaises(ValueError, trial._parseLocalVariables, 277 '-*- foo: bar; qux -*-') 278 self.failUnlessRaises(ValueError, trial._parseLocalVariables, 279 '-*- foo: bar: baz; qux: qax -*-') 280 281 def test_variablesFromFile(self): 282 localVars = trial.loadLocalVariables(sibpath('moduletest.py')) 283 self.assertEqual({'test-case-name': 284 'twisted.trial.test.test_log'}, 285 localVars) 286 287 def test_noVariablesInFile(self): 288 localVars = trial.loadLocalVariables(sibpath('novars.py')) 289 self.assertEqual({}, localVars) 290 291 def test_variablesFromScript(self): 292 localVars = trial.loadLocalVariables(sibpath('scripttest.py')) 293 self.assertEqual( 294 {'test-case-name': ('twisted.trial.test.test_log,' 295 'twisted.trial.test.test_class')}, 296 localVars) 297 298 def test_getTestModules(self): 299 modules = trial.getTestModules(sibpath('moduletest.py')) 300 self.assertEqual(modules, ['twisted.trial.test.test_log']) 301 302 def test_getTestModules_noVars(self): 303 modules = trial.getTestModules(sibpath('novars.py')) 304 self.assertEqual(len(modules), 0) 305 306 def test_getTestModules_multiple(self): 307 modules = trial.getTestModules(sibpath('scripttest.py')) 308 self.assertEqual(set(modules), 309 set(['twisted.trial.test.test_log', 310 'twisted.trial.test.test_class'])) 311 312 def test_looksLikeTestModule(self): 313 for filename in ['test_script.py', 'twisted/trial/test/test_script.py']: 314 self.failUnless(trial.isTestFile(filename), 315 "%r should be a test file" % (filename,)) 316 for filename in ['twisted/trial/test/moduletest.py', 317 sibpath('scripttest.py'), sibpath('test_foo.bat')]: 318 self.failIf(trial.isTestFile(filename), 319 "%r should *not* be a test file" % (filename,)) 320 321 322class WithoutModuleTests(unittest.SynchronousTestCase): 323 """ 324 Test the C{without-module} flag. 325 """ 326 327 def setUp(self): 328 """ 329 Create a L{trial.Options} object to be used in the tests, and save 330 C{sys.modules}. 331 """ 332 self.config = trial.Options() 333 self.savedModules = dict(sys.modules) 334 335 336 def tearDown(self): 337 """ 338 Restore C{sys.modules}. 339 """ 340 for module in ('imaplib', 'smtplib'): 341 if module in self.savedModules: 342 sys.modules[module] = self.savedModules[module] 343 else: 344 sys.modules.pop(module, None) 345 346 347 def _checkSMTP(self): 348 """ 349 Try to import the C{smtplib} module, and return it. 350 """ 351 import smtplib 352 return smtplib 353 354 355 def _checkIMAP(self): 356 """ 357 Try to import the C{imaplib} module, and return it. 358 """ 359 import imaplib 360 return imaplib 361 362 363 def test_disableOneModule(self): 364 """ 365 Check that after disabling a module, it can't be imported anymore. 366 """ 367 self.config.parseOptions(["--without-module", "smtplib"]) 368 self.assertRaises(ImportError, self._checkSMTP) 369 # Restore sys.modules 370 del sys.modules["smtplib"] 371 # Then the function should succeed 372 self.assertIsInstance(self._checkSMTP(), types.ModuleType) 373 374 375 def test_disableMultipleModules(self): 376 """ 377 Check that several modules can be disabled at once. 378 """ 379 self.config.parseOptions(["--without-module", "smtplib,imaplib"]) 380 self.assertRaises(ImportError, self._checkSMTP) 381 self.assertRaises(ImportError, self._checkIMAP) 382 # Restore sys.modules 383 del sys.modules["smtplib"] 384 del sys.modules["imaplib"] 385 # Then the functions should succeed 386 self.assertIsInstance(self._checkSMTP(), types.ModuleType) 387 self.assertIsInstance(self._checkIMAP(), types.ModuleType) 388 389 390 def test_disableAlreadyImportedModule(self): 391 """ 392 Disabling an already imported module should produce a warning. 393 """ 394 self.assertIsInstance(self._checkSMTP(), types.ModuleType) 395 self.assertWarns(RuntimeWarning, 396 "Module 'smtplib' already imported, disabling anyway.", 397 trial.__file__, 398 self.config.parseOptions, ["--without-module", "smtplib"]) 399 self.assertRaises(ImportError, self._checkSMTP) 400 401 402 403class CoverageTests(unittest.SynchronousTestCase): 404 """ 405 Tests for the I{coverage} option. 406 """ 407 if getattr(sys, 'gettrace', None) is None: 408 skip = ( 409 "Cannot test trace hook installation without inspection API.") 410 411 def setUp(self): 412 """ 413 Arrange for the current trace hook to be restored when the 414 test is complete. 415 """ 416 self.addCleanup(sys.settrace, sys.gettrace()) 417 418 419 def test_tracerInstalled(self): 420 """ 421 L{trial.Options} handles C{"--coverage"} by installing a trace 422 hook to record coverage information. 423 """ 424 options = trial.Options() 425 options.parseOptions(["--coverage"]) 426 self.assertEqual(sys.gettrace(), options.tracer.globaltrace) 427 428 429 def test_coverdirDefault(self): 430 """ 431 L{trial.Options.coverdir} returns a L{FilePath} based on the default 432 for the I{temp-directory} option if that option is not specified. 433 """ 434 options = trial.Options() 435 self.assertEqual( 436 options.coverdir(), 437 FilePath(".").descendant([options["temp-directory"], "coverage"])) 438 439 440 def test_coverdirOverridden(self): 441 """ 442 If a value is specified for the I{temp-directory} option, 443 L{trial.Options.coverdir} returns a child of that path. 444 """ 445 path = self.mktemp() 446 options = trial.Options() 447 options.parseOptions(["--temp-directory", path]) 448 self.assertEqual( 449 options.coverdir(), FilePath(path).child("coverage")) 450 451 452 453class OptionsTestCase(unittest.TestCase): 454 """ 455 Tests for L{trial.Options}. 456 """ 457 458 def setUp(self): 459 """ 460 Build an L{Options} object to be used in the tests. 461 """ 462 self.options = trial.Options() 463 464 465 def test_getWorkerArguments(self): 466 """ 467 C{_getWorkerArguments} discards options like C{random} as they only 468 matter in the manager, and forwards options like C{recursionlimit} or 469 C{disablegc}. 470 """ 471 self.addCleanup(sys.setrecursionlimit, sys.getrecursionlimit()) 472 if gc.isenabled(): 473 self.addCleanup(gc.enable) 474 475 self.options.parseOptions(["--recursionlimit", "2000", "--random", 476 "4", "--disablegc"]) 477 args = self.options._getWorkerArguments() 478 self.assertIn("--disablegc", args) 479 args.remove("--disablegc") 480 self.assertEqual(["--recursionlimit", "2000"], args) 481 482 483 def test_jobsConflictWithDebug(self): 484 """ 485 C{parseOptions} raises a C{UsageError} when C{--debug} is passed along 486 C{--jobs} as it's not supported yet. 487 488 @see: U{http://twistedmatrix.com/trac/ticket/5825} 489 """ 490 error = self.assertRaises( 491 UsageError, self.options.parseOptions, ["--jobs", "4", "--debug"]) 492 self.assertEqual("You can't specify --debug when using --jobs", 493 str(error)) 494 495 496 def test_jobsConflictWithProfile(self): 497 """ 498 C{parseOptions} raises a C{UsageError} when C{--profile} is passed 499 along C{--jobs} as it's not supported yet. 500 501 @see: U{http://twistedmatrix.com/trac/ticket/5827} 502 """ 503 error = self.assertRaises( 504 UsageError, self.options.parseOptions, 505 ["--jobs", "4", "--profile"]) 506 self.assertEqual("You can't specify --profile when using --jobs", 507 str(error)) 508 509 510 def test_jobsConflictWithDebugStackTraces(self): 511 """ 512 C{parseOptions} raises a C{UsageError} when C{--debug-stacktraces} is 513 passed along C{--jobs} as it's not supported yet. 514 515 @see: U{http://twistedmatrix.com/trac/ticket/5826} 516 """ 517 error = self.assertRaises( 518 UsageError, self.options.parseOptions, 519 ["--jobs", "4", "--debug-stacktraces"]) 520 self.assertEqual( 521 "You can't specify --debug-stacktraces when using --jobs", 522 str(error)) 523 524 525 def test_jobsConflictWithExitFirst(self): 526 """ 527 C{parseOptions} raises a C{UsageError} when C{--exitfirst} is passed 528 along C{--jobs} as it's not supported yet. 529 530 @see: U{http://twistedmatrix.com/trac/ticket/6436} 531 """ 532 error = self.assertRaises( 533 UsageError, self.options.parseOptions, 534 ["--jobs", "4", "--exitfirst"]) 535 self.assertEqual( 536 "You can't specify --exitfirst when using --jobs", 537 str(error)) 538 539 540 def test_orderConflictWithRandom(self): 541 """ 542 C{parseOptions} raises a C{UsageError} when C{--order} is passed along 543 with C{--random}. 544 """ 545 error = self.assertRaises( 546 UsageError, 547 self.options.parseOptions, 548 ["--order", "alphabetical", "--random", "1234"]) 549 self.assertEqual("You can't specify --random when using --order", 550 str(error)) 551 552 553 554class MakeRunnerTestCase(unittest.TestCase): 555 """ 556 Tests for the L{_makeRunner} helper. 557 """ 558 559 def setUp(self): 560 self.options = trial.Options() 561 562 def test_jobs(self): 563 """ 564 L{_makeRunner} returns a L{DistTrialRunner} instance when the C{--jobs} 565 option is passed, and passes the C{workerNumber} and C{workerArguments} 566 parameters to it. 567 """ 568 self.options.parseOptions(["--jobs", "4", "--force-gc"]) 569 runner = trial._makeRunner(self.options) 570 self.assertIsInstance(runner, DistTrialRunner) 571 self.assertEqual(4, runner._workerNumber) 572 self.assertEqual(["--force-gc"], runner._workerArguments) 573 574 575 def test_dryRunWithJobs(self): 576 """ 577 L{_makeRunner} returns a L{TrialRunner} instance in C{DRY_RUN} mode 578 when the C{--dry-run} option is passed, even if C{--jobs} is set. 579 """ 580 self.options.parseOptions(["--jobs", "4", "--dry-run"]) 581 runner = trial._makeRunner(self.options) 582 self.assertIsInstance(runner, TrialRunner) 583 self.assertEqual(TrialRunner.DRY_RUN, runner.mode) 584 585 586 def test_DebuggerNotFound(self): 587 namedAny = trial.reflect.namedAny 588 589 def namedAnyExceptdoNotFind(fqn): 590 if fqn == "doNotFind": 591 raise trial.reflect.ModuleNotFound(fqn) 592 return namedAny(fqn) 593 594 self.patch(trial.reflect, "namedAny", namedAnyExceptdoNotFind) 595 596 options = trial.Options() 597 options.parseOptions(["--debug", "--debugger", "doNotFind"]) 598 599 self.assertRaises(trial._DebuggerNotFound, trial._makeRunner, options) 600 601 602 def test_exitfirst(self): 603 """ 604 Passing C{--exitfirst} wraps the reporter with a 605 L{reporter._ExitWrapper} that stops on any non-success. 606 """ 607 self.options.parseOptions(["--exitfirst"]) 608 runner = trial._makeRunner(self.options) 609 self.assertTrue(runner._exitFirst) 610 611 612class TestRun(unittest.TestCase): 613 """ 614 Tests for the L{run} function. 615 """ 616 617 def setUp(self): 618 # don't re-parse cmdline options, because if --reactor was passed to 619 # the test run trial will try to restart the (already running) reactor 620 self.patch(trial.Options, "parseOptions", lambda self: None) 621 622 623 def test_debuggerNotFound(self): 624 """ 625 When a debugger is not found, an error message is printed to the user. 626 627 """ 628 629 def _makeRunner(*args, **kwargs): 630 raise trial._DebuggerNotFound('foo') 631 self.patch(trial, "_makeRunner", _makeRunner) 632 633 try: 634 trial.run() 635 except SystemExit as e: 636 self.assertIn("foo", str(e)) 637 else: 638 self.fail("Should have exited due to non-existent debugger!") 639 640 641 642class TestArgumentOrderTests(unittest.TestCase): 643 """ 644 Tests for the order-preserving behavior on provided command-line tests. 645 """ 646 647 def setUp(self): 648 self.config = trial.Options() 649 self.loader = TestLoader() 650 651 652 def test_preserveArgumentOrder(self): 653 """ 654 Multiple tests passed on the command line are not reordered. 655 """ 656 tests = [ 657 "twisted.trial.test.test_tests", 658 "twisted.trial.test.test_assertions", 659 "twisted.trial.test.test_deferreds", 660 ] 661 self.config.parseOptions(tests) 662 663 suite = trial._getSuite(self.config) 664 names = testNames(suite) 665 666 expectedSuite = TestSuite(map(self.loader.loadByName, tests)) 667 expectedNames = testNames(expectedSuite) 668 669 self.assertEqual(names, expectedNames) 670 671 672 673class OrderTests(unittest.TestCase): 674 """ 675 Tests for the --order option. 676 """ 677 def setUp(self): 678 self.config = trial.Options() 679 680 681 def test_alphabetical(self): 682 """ 683 --order=alphabetical causes trial to run tests alphabetically within 684 each test case. 685 """ 686 self.config.parseOptions([ 687 "--order", "alphabetical", 688 "twisted.trial.test.ordertests.FooTest"]) 689 690 loader = trial._getLoader(self.config) 691 suite = loader.loadByNames(self.config['tests']) 692 693 self.assertEqual( 694 testNames(suite), [ 695 'twisted.trial.test.ordertests.FooTest.test_first', 696 'twisted.trial.test.ordertests.FooTest.test_fourth', 697 'twisted.trial.test.ordertests.FooTest.test_second', 698 'twisted.trial.test.ordertests.FooTest.test_third']) 699 700 701 def test_alphabeticalModule(self): 702 """ 703 --order=alphabetical causes trial to run test classes within a given 704 module alphabetically. 705 """ 706 self.config.parseOptions([ 707 "--order", "alphabetical", "twisted.trial.test.ordertests"]) 708 loader = trial._getLoader(self.config) 709 suite = loader.loadByNames(self.config['tests']) 710 711 self.assertEqual( 712 testNames(suite), [ 713 'twisted.trial.test.ordertests.BarTest.test_bar', 714 'twisted.trial.test.ordertests.BazTest.test_baz', 715 'twisted.trial.test.ordertests.FooTest.test_first', 716 'twisted.trial.test.ordertests.FooTest.test_fourth', 717 'twisted.trial.test.ordertests.FooTest.test_second', 718 'twisted.trial.test.ordertests.FooTest.test_third']) 719 720 721 def test_alphabeticalPackage(self): 722 """ 723 --order=alphabetical causes trial to run test modules within a given 724 package alphabetically, with tests within each module alphabetized. 725 """ 726 self.config.parseOptions([ 727 "--order", "alphabetical", "twisted.trial.test"]) 728 loader = trial._getLoader(self.config) 729 suite = loader.loadByNames(self.config['tests']) 730 731 names = testNames(suite) 732 self.assertTrue(names, msg="Failed to load any tests!") 733 self.assertEqual(names, sorted(names)) 734 735 736 def test_toptobottom(self): 737 """ 738 --order=toptobottom causes trial to run test methods within a given 739 test case from top to bottom as they are defined in the body of the 740 class. 741 """ 742 self.config.parseOptions([ 743 "--order", "toptobottom", 744 "twisted.trial.test.ordertests.FooTest"]) 745 746 loader = trial._getLoader(self.config) 747 suite = loader.loadByNames(self.config['tests']) 748 749 self.assertEqual( 750 testNames(suite), [ 751 'twisted.trial.test.ordertests.FooTest.test_first', 752 'twisted.trial.test.ordertests.FooTest.test_second', 753 'twisted.trial.test.ordertests.FooTest.test_third', 754 'twisted.trial.test.ordertests.FooTest.test_fourth']) 755 756 757 def test_toptobottomModule(self): 758 """ 759 --order=toptobottom causes trial to run test classes within a given 760 module from top to bottom as they are defined in the module's source. 761 """ 762 self.config.parseOptions([ 763 "--order", "toptobottom", "twisted.trial.test.ordertests"]) 764 loader = trial._getLoader(self.config) 765 suite = loader.loadByNames(self.config['tests']) 766 767 self.assertEqual( 768 testNames(suite), [ 769 'twisted.trial.test.ordertests.FooTest.test_first', 770 'twisted.trial.test.ordertests.FooTest.test_second', 771 'twisted.trial.test.ordertests.FooTest.test_third', 772 'twisted.trial.test.ordertests.FooTest.test_fourth', 773 'twisted.trial.test.ordertests.BazTest.test_baz', 774 'twisted.trial.test.ordertests.BarTest.test_bar']) 775 776 777 def test_toptobottomPackage(self): 778 """ 779 --order=toptobottom causes trial to run test modules within a given 780 package alphabetically, with tests within each module run top to 781 bottom. 782 """ 783 self.config.parseOptions([ 784 "--order", "toptobottom", "twisted.trial.test"]) 785 loader = trial._getLoader(self.config) 786 suite = loader.loadByNames(self.config['tests']) 787 788 names = testNames(suite) 789 # twisted.trial.test.test_module, so split and key on the first 4 to 790 # get stable alphabetical sort on those 791 self.assertEqual( 792 names, sorted(names, key=lambda name : name.split(".")[:4]), 793 ) 794 795 796 def test_toptobottomMissingSource(self): 797 """ 798 --order=toptobottom detects the source line of methods from modules 799 whose source file is missing. 800 """ 801 tempdir = self.mktemp().encode('utf-8') 802 package = FilePath(tempdir).child(b'twisted_toptobottom_temp') 803 package.makedirs() 804 package.child(b'__init__.py').setContent(b'') 805 package.child(b'test_missing.py').setContent(textwrap.dedent(b''' 806 from twisted.trial.unittest import TestCase 807 class TestMissing(TestCase): 808 def test_second(self): pass 809 def test_third(self): pass 810 def test_fourth(self): pass 811 def test_first(self): pass 812 ''')) 813 pathEntry = package.parent().path.decode('utf-8') 814 sys.path.insert(0, pathEntry) 815 self.addCleanup(sys.path.remove, pathEntry) 816 from twisted_toptobottom_temp import test_missing 817 self.addCleanup(sys.modules.pop, 'twisted_toptobottom_temp') 818 self.addCleanup(sys.modules.pop, test_missing.__name__) 819 package.child(b'test_missing.py').remove() 820 821 self.config.parseOptions([ 822 "--order", "toptobottom", "twisted.trial.test.ordertests"]) 823 loader = trial._getLoader(self.config) 824 suite = loader.loadModule(test_missing) 825 826 self.assertEqual( 827 testNames(suite), [ 828 'twisted_toptobottom_temp.test_missing.TestMissing.test_second', 829 'twisted_toptobottom_temp.test_missing.TestMissing.test_third', 830 'twisted_toptobottom_temp.test_missing.TestMissing.test_fourth', 831 'twisted_toptobottom_temp.test_missing.TestMissing.test_first']) 832 833 834 def test_unknownOrder(self): 835 """ 836 An unknown order passed to --order raises a L{UsageError}. 837 """ 838 839 self.assertRaises( 840 UsageError, self.config.parseOptions, ["--order", "I don't exist"]) 841 842 843 844class HelpOrderTests(unittest.TestCase): 845 """ 846 Tests for the --help-orders flag. 847 """ 848 def test_help_ordersPrintsSynopsisAndQuits(self): 849 """ 850 --help-orders prints each of the available orders and then exits. 851 """ 852 self.patch(sys, "stdout", StringIO.StringIO()) 853 854 exc = self.assertRaises( 855 SystemExit, trial.Options().parseOptions, ["--help-orders"]) 856 self.assertEqual(exc.code, 0) 857 858 output = sys.stdout.getvalue() 859 860 msg = "%r with its description not properly described in %r" 861 for orderName, (orderDesc, _) in trial._runOrders.items(): 862 match = re.search( 863 "%s.*%s" % (re.escape(orderName), re.escape(orderDesc)), 864 output, 865 ) 866 867 self.assertTrue(match, msg=msg % (orderName, output)) 868