1import os.path 2from os.path import abspath 3import re 4import sys 5import types 6import pickle 7from test import support 8from test.support import import_helper 9 10import unittest 11import unittest.mock 12import test.test_unittest 13 14 15class TestableTestProgram(unittest.TestProgram): 16 module = None 17 exit = True 18 defaultTest = failfast = catchbreak = buffer = None 19 verbosity = 1 20 progName = '' 21 testRunner = testLoader = None 22 23 def __init__(self): 24 pass 25 26 27class TestDiscovery(unittest.TestCase): 28 29 # Heavily mocked tests so I can avoid hitting the filesystem 30 def test_get_name_from_path(self): 31 loader = unittest.TestLoader() 32 loader._top_level_dir = '/foo' 33 name = loader._get_name_from_path('/foo/bar/baz.py') 34 self.assertEqual(name, 'bar.baz') 35 36 if not __debug__: 37 # asserts are off 38 return 39 40 with self.assertRaises(AssertionError): 41 loader._get_name_from_path('/bar/baz.py') 42 43 def test_find_tests(self): 44 loader = unittest.TestLoader() 45 46 original_listdir = os.listdir 47 def restore_listdir(): 48 os.listdir = original_listdir 49 original_isfile = os.path.isfile 50 def restore_isfile(): 51 os.path.isfile = original_isfile 52 original_isdir = os.path.isdir 53 def restore_isdir(): 54 os.path.isdir = original_isdir 55 56 path_lists = [['test2.py', 'test1.py', 'not_a_test.py', 'test_dir', 57 'test.foo', 'test-not-a-module.py', 'another_dir'], 58 ['test4.py', 'test3.py', ]] 59 os.listdir = lambda path: path_lists.pop(0) 60 self.addCleanup(restore_listdir) 61 62 def isdir(path): 63 return path.endswith('dir') 64 os.path.isdir = isdir 65 self.addCleanup(restore_isdir) 66 67 def isfile(path): 68 # another_dir is not a package and so shouldn't be recursed into 69 return not path.endswith('dir') and not 'another_dir' in path 70 os.path.isfile = isfile 71 self.addCleanup(restore_isfile) 72 73 loader._get_module_from_name = lambda path: path + ' module' 74 orig_load_tests = loader.loadTestsFromModule 75 def loadTestsFromModule(module, pattern=None): 76 # This is where load_tests is called. 77 base = orig_load_tests(module, pattern=pattern) 78 return base + [module + ' tests'] 79 loader.loadTestsFromModule = loadTestsFromModule 80 loader.suiteClass = lambda thing: thing 81 82 top_level = os.path.abspath('/foo') 83 loader._top_level_dir = top_level 84 suite = list(loader._find_tests(top_level, 'test*.py')) 85 86 # The test suites found should be sorted alphabetically for reliable 87 # execution order. 88 expected = [[name + ' module tests'] for name in 89 ('test1', 'test2', 'test_dir')] 90 expected.extend([[('test_dir.%s' % name) + ' module tests'] for name in 91 ('test3', 'test4')]) 92 self.assertEqual(suite, expected) 93 94 def test_find_tests_socket(self): 95 # A socket is neither a directory nor a regular file. 96 # https://bugs.python.org/issue25320 97 loader = unittest.TestLoader() 98 99 original_listdir = os.listdir 100 def restore_listdir(): 101 os.listdir = original_listdir 102 original_isfile = os.path.isfile 103 def restore_isfile(): 104 os.path.isfile = original_isfile 105 original_isdir = os.path.isdir 106 def restore_isdir(): 107 os.path.isdir = original_isdir 108 109 path_lists = [['socket']] 110 os.listdir = lambda path: path_lists.pop(0) 111 self.addCleanup(restore_listdir) 112 113 os.path.isdir = lambda path: False 114 self.addCleanup(restore_isdir) 115 116 os.path.isfile = lambda path: False 117 self.addCleanup(restore_isfile) 118 119 loader._get_module_from_name = lambda path: path + ' module' 120 orig_load_tests = loader.loadTestsFromModule 121 def loadTestsFromModule(module, pattern=None): 122 # This is where load_tests is called. 123 base = orig_load_tests(module, pattern=pattern) 124 return base + [module + ' tests'] 125 loader.loadTestsFromModule = loadTestsFromModule 126 loader.suiteClass = lambda thing: thing 127 128 top_level = os.path.abspath('/foo') 129 loader._top_level_dir = top_level 130 suite = list(loader._find_tests(top_level, 'test*.py')) 131 132 self.assertEqual(suite, []) 133 134 def test_find_tests_with_package(self): 135 loader = unittest.TestLoader() 136 137 original_listdir = os.listdir 138 def restore_listdir(): 139 os.listdir = original_listdir 140 original_isfile = os.path.isfile 141 def restore_isfile(): 142 os.path.isfile = original_isfile 143 original_isdir = os.path.isdir 144 def restore_isdir(): 145 os.path.isdir = original_isdir 146 147 directories = ['a_directory', 'test_directory', 'test_directory2'] 148 path_lists = [directories, [], [], []] 149 os.listdir = lambda path: path_lists.pop(0) 150 self.addCleanup(restore_listdir) 151 152 os.path.isdir = lambda path: True 153 self.addCleanup(restore_isdir) 154 155 os.path.isfile = lambda path: os.path.basename(path) not in directories 156 self.addCleanup(restore_isfile) 157 158 class Module(object): 159 paths = [] 160 load_tests_args = [] 161 162 def __init__(self, path): 163 self.path = path 164 self.paths.append(path) 165 if os.path.basename(path) == 'test_directory': 166 def load_tests(loader, tests, pattern): 167 self.load_tests_args.append((loader, tests, pattern)) 168 return [self.path + ' load_tests'] 169 self.load_tests = load_tests 170 171 def __eq__(self, other): 172 return self.path == other.path 173 174 loader._get_module_from_name = lambda name: Module(name) 175 orig_load_tests = loader.loadTestsFromModule 176 def loadTestsFromModule(module, pattern=None): 177 # This is where load_tests is called. 178 base = orig_load_tests(module, pattern=pattern) 179 return base + [module.path + ' module tests'] 180 loader.loadTestsFromModule = loadTestsFromModule 181 loader.suiteClass = lambda thing: thing 182 183 loader._top_level_dir = '/foo' 184 # this time no '.py' on the pattern so that it can match 185 # a test package 186 suite = list(loader._find_tests('/foo', 'test*')) 187 188 # We should have loaded tests from the a_directory and test_directory2 189 # directly and via load_tests for the test_directory package, which 190 # still calls the baseline module loader. 191 self.assertEqual(suite, 192 [['a_directory module tests'], 193 ['test_directory load_tests', 194 'test_directory module tests'], 195 ['test_directory2 module tests']]) 196 197 198 # The test module paths should be sorted for reliable execution order 199 self.assertEqual(Module.paths, 200 ['a_directory', 'test_directory', 'test_directory2']) 201 202 # load_tests should have been called once with loader, tests and pattern 203 # (but there are no tests in our stub module itself, so that is [] at 204 # the time of call). 205 self.assertEqual(Module.load_tests_args, 206 [(loader, [], 'test*')]) 207 208 def test_find_tests_default_calls_package_load_tests(self): 209 loader = unittest.TestLoader() 210 211 original_listdir = os.listdir 212 def restore_listdir(): 213 os.listdir = original_listdir 214 original_isfile = os.path.isfile 215 def restore_isfile(): 216 os.path.isfile = original_isfile 217 original_isdir = os.path.isdir 218 def restore_isdir(): 219 os.path.isdir = original_isdir 220 221 directories = ['a_directory', 'test_directory', 'test_directory2'] 222 path_lists = [directories, [], [], []] 223 os.listdir = lambda path: path_lists.pop(0) 224 self.addCleanup(restore_listdir) 225 226 os.path.isdir = lambda path: True 227 self.addCleanup(restore_isdir) 228 229 os.path.isfile = lambda path: os.path.basename(path) not in directories 230 self.addCleanup(restore_isfile) 231 232 class Module(object): 233 paths = [] 234 load_tests_args = [] 235 236 def __init__(self, path): 237 self.path = path 238 self.paths.append(path) 239 if os.path.basename(path) == 'test_directory': 240 def load_tests(loader, tests, pattern): 241 self.load_tests_args.append((loader, tests, pattern)) 242 return [self.path + ' load_tests'] 243 self.load_tests = load_tests 244 245 def __eq__(self, other): 246 return self.path == other.path 247 248 loader._get_module_from_name = lambda name: Module(name) 249 orig_load_tests = loader.loadTestsFromModule 250 def loadTestsFromModule(module, pattern=None): 251 # This is where load_tests is called. 252 base = orig_load_tests(module, pattern=pattern) 253 return base + [module.path + ' module tests'] 254 loader.loadTestsFromModule = loadTestsFromModule 255 loader.suiteClass = lambda thing: thing 256 257 loader._top_level_dir = '/foo' 258 # this time no '.py' on the pattern so that it can match 259 # a test package 260 suite = list(loader._find_tests('/foo', 'test*.py')) 261 262 # We should have loaded tests from the a_directory and test_directory2 263 # directly and via load_tests for the test_directory package, which 264 # still calls the baseline module loader. 265 self.assertEqual(suite, 266 [['a_directory module tests'], 267 ['test_directory load_tests', 268 'test_directory module tests'], 269 ['test_directory2 module tests']]) 270 # The test module paths should be sorted for reliable execution order 271 self.assertEqual(Module.paths, 272 ['a_directory', 'test_directory', 'test_directory2']) 273 274 275 # load_tests should have been called once with loader, tests and pattern 276 self.assertEqual(Module.load_tests_args, 277 [(loader, [], 'test*.py')]) 278 279 def test_find_tests_customize_via_package_pattern(self): 280 # This test uses the example 'do-nothing' load_tests from 281 # https://docs.python.org/3/library/unittest.html#load-tests-protocol 282 # to make sure that that actually works. 283 # Housekeeping 284 original_listdir = os.listdir 285 def restore_listdir(): 286 os.listdir = original_listdir 287 self.addCleanup(restore_listdir) 288 original_isfile = os.path.isfile 289 def restore_isfile(): 290 os.path.isfile = original_isfile 291 self.addCleanup(restore_isfile) 292 original_isdir = os.path.isdir 293 def restore_isdir(): 294 os.path.isdir = original_isdir 295 self.addCleanup(restore_isdir) 296 self.addCleanup(sys.path.remove, abspath('/foo')) 297 298 # Test data: we expect the following: 299 # a listdir to find our package, and isfile and isdir checks on it. 300 # a module-from-name call to turn that into a module 301 # followed by load_tests. 302 # then our load_tests will call discover() which is messy 303 # but that finally chains into find_tests again for the child dir - 304 # which is why we don't have an infinite loop. 305 # We expect to see: 306 # the module load tests for both package and plain module called, 307 # and the plain module result nested by the package module load_tests 308 # indicating that it was processed and could have been mutated. 309 vfs = {abspath('/foo'): ['my_package'], 310 abspath('/foo/my_package'): ['__init__.py', 'test_module.py']} 311 def list_dir(path): 312 return list(vfs[path]) 313 os.listdir = list_dir 314 os.path.isdir = lambda path: not path.endswith('.py') 315 os.path.isfile = lambda path: path.endswith('.py') 316 317 class Module(object): 318 paths = [] 319 load_tests_args = [] 320 321 def __init__(self, path): 322 self.path = path 323 self.paths.append(path) 324 if path.endswith('test_module'): 325 def load_tests(loader, tests, pattern): 326 self.load_tests_args.append((loader, tests, pattern)) 327 return [self.path + ' load_tests'] 328 else: 329 def load_tests(loader, tests, pattern): 330 self.load_tests_args.append((loader, tests, pattern)) 331 # top level directory cached on loader instance 332 __file__ = '/foo/my_package/__init__.py' 333 this_dir = os.path.dirname(__file__) 334 pkg_tests = loader.discover( 335 start_dir=this_dir, pattern=pattern) 336 return [self.path + ' load_tests', tests 337 ] + pkg_tests 338 self.load_tests = load_tests 339 340 def __eq__(self, other): 341 return self.path == other.path 342 343 loader = unittest.TestLoader() 344 loader._get_module_from_name = lambda name: Module(name) 345 loader.suiteClass = lambda thing: thing 346 347 loader._top_level_dir = abspath('/foo') 348 # this time no '.py' on the pattern so that it can match 349 # a test package 350 suite = list(loader._find_tests(abspath('/foo'), 'test*.py')) 351 352 # We should have loaded tests from both my_package and 353 # my_package.test_module, and also run the load_tests hook in both. 354 # (normally this would be nested TestSuites.) 355 self.assertEqual(suite, 356 [['my_package load_tests', [], 357 ['my_package.test_module load_tests']]]) 358 # Parents before children. 359 self.assertEqual(Module.paths, 360 ['my_package', 'my_package.test_module']) 361 362 # load_tests should have been called twice with loader, tests and pattern 363 self.assertEqual(Module.load_tests_args, 364 [(loader, [], 'test*.py'), 365 (loader, [], 'test*.py')]) 366 367 def test_discover(self): 368 loader = unittest.TestLoader() 369 370 original_isfile = os.path.isfile 371 original_isdir = os.path.isdir 372 def restore_isfile(): 373 os.path.isfile = original_isfile 374 375 os.path.isfile = lambda path: False 376 self.addCleanup(restore_isfile) 377 378 orig_sys_path = sys.path[:] 379 def restore_path(): 380 sys.path[:] = orig_sys_path 381 self.addCleanup(restore_path) 382 383 full_path = os.path.abspath(os.path.normpath('/foo')) 384 with self.assertRaises(ImportError): 385 loader.discover('/foo/bar', top_level_dir='/foo') 386 387 self.assertEqual(loader._top_level_dir, full_path) 388 self.assertIn(full_path, sys.path) 389 390 os.path.isfile = lambda path: True 391 os.path.isdir = lambda path: True 392 393 def restore_isdir(): 394 os.path.isdir = original_isdir 395 self.addCleanup(restore_isdir) 396 397 _find_tests_args = [] 398 def _find_tests(start_dir, pattern): 399 _find_tests_args.append((start_dir, pattern)) 400 return ['tests'] 401 loader._find_tests = _find_tests 402 loader.suiteClass = str 403 404 suite = loader.discover('/foo/bar/baz', 'pattern', '/foo/bar') 405 406 top_level_dir = os.path.abspath('/foo/bar') 407 start_dir = os.path.abspath('/foo/bar/baz') 408 self.assertEqual(suite, "['tests']") 409 self.assertEqual(loader._top_level_dir, os.path.abspath('/foo')) 410 self.assertEqual(_find_tests_args, [(start_dir, 'pattern')]) 411 self.assertIn(top_level_dir, sys.path) 412 413 def test_discover_should_not_persist_top_level_dir_between_calls(self): 414 original_isfile = os.path.isfile 415 original_isdir = os.path.isdir 416 original_sys_path = sys.path[:] 417 def restore(): 418 os.path.isfile = original_isfile 419 os.path.isdir = original_isdir 420 sys.path[:] = original_sys_path 421 self.addCleanup(restore) 422 423 os.path.isfile = lambda path: True 424 os.path.isdir = lambda path: True 425 loader = unittest.TestLoader() 426 loader.suiteClass = str 427 dir = '/foo/bar' 428 top_level_dir = '/foo' 429 430 loader.discover(dir, top_level_dir=top_level_dir) 431 self.assertEqual(loader._top_level_dir, None) 432 433 loader._top_level_dir = dir2 = '/previous/dir' 434 loader.discover(dir, top_level_dir=top_level_dir) 435 self.assertEqual(loader._top_level_dir, dir2) 436 437 def test_discover_start_dir_is_package_calls_package_load_tests(self): 438 # This test verifies that the package load_tests in a package is indeed 439 # invoked when the start_dir is a package (and not the top level). 440 # http://bugs.python.org/issue22457 441 442 # Test data: we expect the following: 443 # an isfile to verify the package, then importing and scanning 444 # as per _find_tests' normal behaviour. 445 # We expect to see our load_tests hook called once. 446 vfs = {abspath('/toplevel'): ['startdir'], 447 abspath('/toplevel/startdir'): ['__init__.py']} 448 def list_dir(path): 449 return list(vfs[path]) 450 self.addCleanup(setattr, os, 'listdir', os.listdir) 451 os.listdir = list_dir 452 self.addCleanup(setattr, os.path, 'isfile', os.path.isfile) 453 os.path.isfile = lambda path: path.endswith('.py') 454 self.addCleanup(setattr, os.path, 'isdir', os.path.isdir) 455 os.path.isdir = lambda path: not path.endswith('.py') 456 self.addCleanup(sys.path.remove, abspath('/toplevel')) 457 458 class Module(object): 459 paths = [] 460 load_tests_args = [] 461 462 def __init__(self, path): 463 self.path = path 464 465 def load_tests(self, loader, tests, pattern): 466 return ['load_tests called ' + self.path] 467 468 def __eq__(self, other): 469 return self.path == other.path 470 471 loader = unittest.TestLoader() 472 loader._get_module_from_name = lambda name: Module(name) 473 loader.suiteClass = lambda thing: thing 474 475 suite = loader.discover('/toplevel/startdir', top_level_dir='/toplevel') 476 477 # We should have loaded tests from the package __init__. 478 # (normally this would be nested TestSuites.) 479 self.assertEqual(suite, 480 [['load_tests called startdir']]) 481 482 def setup_import_issue_tests(self, fakefile): 483 listdir = os.listdir 484 os.listdir = lambda _: [fakefile] 485 isfile = os.path.isfile 486 os.path.isfile = lambda _: True 487 orig_sys_path = sys.path[:] 488 def restore(): 489 os.path.isfile = isfile 490 os.listdir = listdir 491 sys.path[:] = orig_sys_path 492 self.addCleanup(restore) 493 494 def setup_import_issue_package_tests(self, vfs): 495 self.addCleanup(setattr, os, 'listdir', os.listdir) 496 self.addCleanup(setattr, os.path, 'isfile', os.path.isfile) 497 self.addCleanup(setattr, os.path, 'isdir', os.path.isdir) 498 self.addCleanup(sys.path.__setitem__, slice(None), list(sys.path)) 499 def list_dir(path): 500 return list(vfs[path]) 501 os.listdir = list_dir 502 os.path.isdir = lambda path: not path.endswith('.py') 503 os.path.isfile = lambda path: path.endswith('.py') 504 505 def test_discover_with_modules_that_fail_to_import(self): 506 loader = unittest.TestLoader() 507 508 self.setup_import_issue_tests('test_this_does_not_exist.py') 509 510 suite = loader.discover('.') 511 self.assertIn(os.getcwd(), sys.path) 512 self.assertEqual(suite.countTestCases(), 1) 513 # Errors loading the suite are also captured for introspection. 514 self.assertNotEqual([], loader.errors) 515 self.assertEqual(1, len(loader.errors)) 516 error = loader.errors[0] 517 self.assertTrue( 518 'Failed to import test module: test_this_does_not_exist' in error, 519 'missing error string in %r' % error) 520 test = list(list(suite)[0])[0] # extract test from suite 521 522 with self.assertRaises(ImportError): 523 test.test_this_does_not_exist() 524 525 def test_discover_with_init_modules_that_fail_to_import(self): 526 vfs = {abspath('/foo'): ['my_package'], 527 abspath('/foo/my_package'): ['__init__.py', 'test_module.py']} 528 self.setup_import_issue_package_tests(vfs) 529 import_calls = [] 530 def _get_module_from_name(name): 531 import_calls.append(name) 532 raise ImportError("Cannot import Name") 533 loader = unittest.TestLoader() 534 loader._get_module_from_name = _get_module_from_name 535 suite = loader.discover(abspath('/foo')) 536 537 self.assertIn(abspath('/foo'), sys.path) 538 self.assertEqual(suite.countTestCases(), 1) 539 # Errors loading the suite are also captured for introspection. 540 self.assertNotEqual([], loader.errors) 541 self.assertEqual(1, len(loader.errors)) 542 error = loader.errors[0] 543 self.assertTrue( 544 'Failed to import test module: my_package' in error, 545 'missing error string in %r' % error) 546 test = list(list(suite)[0])[0] # extract test from suite 547 with self.assertRaises(ImportError): 548 test.my_package() 549 self.assertEqual(import_calls, ['my_package']) 550 551 # Check picklability 552 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 553 pickle.loads(pickle.dumps(test, proto)) 554 555 def test_discover_with_module_that_raises_SkipTest_on_import(self): 556 if not unittest.BaseTestSuite._cleanup: 557 raise unittest.SkipTest("Suite cleanup is disabled") 558 559 loader = unittest.TestLoader() 560 561 def _get_module_from_name(name): 562 raise unittest.SkipTest('skipperoo') 563 loader._get_module_from_name = _get_module_from_name 564 565 self.setup_import_issue_tests('test_skip_dummy.py') 566 567 suite = loader.discover('.') 568 self.assertEqual(suite.countTestCases(), 1) 569 570 result = unittest.TestResult() 571 suite.run(result) 572 self.assertEqual(len(result.skipped), 1) 573 574 # Check picklability 575 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 576 pickle.loads(pickle.dumps(suite, proto)) 577 578 def test_discover_with_init_module_that_raises_SkipTest_on_import(self): 579 if not unittest.BaseTestSuite._cleanup: 580 raise unittest.SkipTest("Suite cleanup is disabled") 581 582 vfs = {abspath('/foo'): ['my_package'], 583 abspath('/foo/my_package'): ['__init__.py', 'test_module.py']} 584 self.setup_import_issue_package_tests(vfs) 585 import_calls = [] 586 def _get_module_from_name(name): 587 import_calls.append(name) 588 raise unittest.SkipTest('skipperoo') 589 loader = unittest.TestLoader() 590 loader._get_module_from_name = _get_module_from_name 591 suite = loader.discover(abspath('/foo')) 592 593 self.assertIn(abspath('/foo'), sys.path) 594 self.assertEqual(suite.countTestCases(), 1) 595 result = unittest.TestResult() 596 suite.run(result) 597 self.assertEqual(len(result.skipped), 1) 598 self.assertEqual(result.testsRun, 1) 599 self.assertEqual(import_calls, ['my_package']) 600 601 # Check picklability 602 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 603 pickle.loads(pickle.dumps(suite, proto)) 604 605 def test_command_line_handling_parseArgs(self): 606 program = TestableTestProgram() 607 608 args = [] 609 program._do_discovery = args.append 610 program.parseArgs(['something', 'discover']) 611 self.assertEqual(args, [[]]) 612 613 args[:] = [] 614 program.parseArgs(['something', 'discover', 'foo', 'bar']) 615 self.assertEqual(args, [['foo', 'bar']]) 616 617 def test_command_line_handling_discover_by_default(self): 618 program = TestableTestProgram() 619 620 args = [] 621 program._do_discovery = args.append 622 program.parseArgs(['something']) 623 self.assertEqual(args, [[]]) 624 self.assertEqual(program.verbosity, 1) 625 self.assertIs(program.buffer, False) 626 self.assertIs(program.catchbreak, False) 627 self.assertIs(program.failfast, False) 628 629 def test_command_line_handling_discover_by_default_with_options(self): 630 program = TestableTestProgram() 631 632 args = [] 633 program._do_discovery = args.append 634 program.parseArgs(['something', '-v', '-b', '-v', '-c', '-f']) 635 self.assertEqual(args, [[]]) 636 self.assertEqual(program.verbosity, 2) 637 self.assertIs(program.buffer, True) 638 self.assertIs(program.catchbreak, True) 639 self.assertIs(program.failfast, True) 640 641 642 def test_command_line_handling_do_discovery_too_many_arguments(self): 643 program = TestableTestProgram() 644 program.testLoader = None 645 646 with support.captured_stderr() as stderr, \ 647 self.assertRaises(SystemExit) as cm: 648 # too many args 649 program._do_discovery(['one', 'two', 'three', 'four']) 650 self.assertEqual(cm.exception.args, (2,)) 651 self.assertIn('usage:', stderr.getvalue()) 652 653 654 def test_command_line_handling_do_discovery_uses_default_loader(self): 655 program = object.__new__(unittest.TestProgram) 656 program._initArgParsers() 657 658 class Loader(object): 659 args = [] 660 def discover(self, start_dir, pattern, top_level_dir): 661 self.args.append((start_dir, pattern, top_level_dir)) 662 return 'tests' 663 664 program.testLoader = Loader() 665 program._do_discovery(['-v']) 666 self.assertEqual(Loader.args, [('.', 'test*.py', None)]) 667 668 def test_command_line_handling_do_discovery_calls_loader(self): 669 program = TestableTestProgram() 670 671 class Loader(object): 672 args = [] 673 def discover(self, start_dir, pattern, top_level_dir): 674 self.args.append((start_dir, pattern, top_level_dir)) 675 return 'tests' 676 677 program._do_discovery(['-v'], Loader=Loader) 678 self.assertEqual(program.verbosity, 2) 679 self.assertEqual(program.test, 'tests') 680 self.assertEqual(Loader.args, [('.', 'test*.py', None)]) 681 682 Loader.args = [] 683 program = TestableTestProgram() 684 program._do_discovery(['--verbose'], Loader=Loader) 685 self.assertEqual(program.test, 'tests') 686 self.assertEqual(Loader.args, [('.', 'test*.py', None)]) 687 688 Loader.args = [] 689 program = TestableTestProgram() 690 program._do_discovery([], Loader=Loader) 691 self.assertEqual(program.test, 'tests') 692 self.assertEqual(Loader.args, [('.', 'test*.py', None)]) 693 694 Loader.args = [] 695 program = TestableTestProgram() 696 program._do_discovery(['fish'], Loader=Loader) 697 self.assertEqual(program.test, 'tests') 698 self.assertEqual(Loader.args, [('fish', 'test*.py', None)]) 699 700 Loader.args = [] 701 program = TestableTestProgram() 702 program._do_discovery(['fish', 'eggs'], Loader=Loader) 703 self.assertEqual(program.test, 'tests') 704 self.assertEqual(Loader.args, [('fish', 'eggs', None)]) 705 706 Loader.args = [] 707 program = TestableTestProgram() 708 program._do_discovery(['fish', 'eggs', 'ham'], Loader=Loader) 709 self.assertEqual(program.test, 'tests') 710 self.assertEqual(Loader.args, [('fish', 'eggs', 'ham')]) 711 712 Loader.args = [] 713 program = TestableTestProgram() 714 program._do_discovery(['-s', 'fish'], Loader=Loader) 715 self.assertEqual(program.test, 'tests') 716 self.assertEqual(Loader.args, [('fish', 'test*.py', None)]) 717 718 Loader.args = [] 719 program = TestableTestProgram() 720 program._do_discovery(['-t', 'fish'], Loader=Loader) 721 self.assertEqual(program.test, 'tests') 722 self.assertEqual(Loader.args, [('.', 'test*.py', 'fish')]) 723 724 Loader.args = [] 725 program = TestableTestProgram() 726 program._do_discovery(['-p', 'fish'], Loader=Loader) 727 self.assertEqual(program.test, 'tests') 728 self.assertEqual(Loader.args, [('.', 'fish', None)]) 729 self.assertFalse(program.failfast) 730 self.assertFalse(program.catchbreak) 731 732 Loader.args = [] 733 program = TestableTestProgram() 734 program._do_discovery(['-p', 'eggs', '-s', 'fish', '-v', '-f', '-c'], 735 Loader=Loader) 736 self.assertEqual(program.test, 'tests') 737 self.assertEqual(Loader.args, [('fish', 'eggs', None)]) 738 self.assertEqual(program.verbosity, 2) 739 self.assertTrue(program.failfast) 740 self.assertTrue(program.catchbreak) 741 742 def setup_module_clash(self): 743 class Module(object): 744 __file__ = 'bar/foo.py' 745 sys.modules['foo'] = Module 746 full_path = os.path.abspath('foo') 747 original_listdir = os.listdir 748 original_isfile = os.path.isfile 749 original_isdir = os.path.isdir 750 original_realpath = os.path.realpath 751 752 def cleanup(): 753 os.listdir = original_listdir 754 os.path.isfile = original_isfile 755 os.path.isdir = original_isdir 756 os.path.realpath = original_realpath 757 del sys.modules['foo'] 758 if full_path in sys.path: 759 sys.path.remove(full_path) 760 self.addCleanup(cleanup) 761 762 def listdir(_): 763 return ['foo.py'] 764 def isfile(_): 765 return True 766 def isdir(_): 767 return True 768 os.listdir = listdir 769 os.path.isfile = isfile 770 os.path.isdir = isdir 771 if os.name == 'nt': 772 # ntpath.realpath may inject path prefixes when failing to 773 # resolve real files, so we substitute abspath() here instead. 774 os.path.realpath = os.path.abspath 775 return full_path 776 777 def test_detect_module_clash(self): 778 full_path = self.setup_module_clash() 779 loader = unittest.TestLoader() 780 781 mod_dir = os.path.abspath('bar') 782 expected_dir = os.path.abspath('foo') 783 msg = re.escape(r"'foo' module incorrectly imported from %r. Expected %r. " 784 "Is this module globally installed?" % (mod_dir, expected_dir)) 785 self.assertRaisesRegex( 786 ImportError, '^%s$' % msg, loader.discover, 787 start_dir='foo', pattern='foo.py' 788 ) 789 self.assertEqual(sys.path[0], full_path) 790 791 def test_module_symlink_ok(self): 792 full_path = self.setup_module_clash() 793 794 original_realpath = os.path.realpath 795 796 mod_dir = os.path.abspath('bar') 797 expected_dir = os.path.abspath('foo') 798 799 def cleanup(): 800 os.path.realpath = original_realpath 801 self.addCleanup(cleanup) 802 803 def realpath(path): 804 if path == os.path.join(mod_dir, 'foo.py'): 805 return os.path.join(expected_dir, 'foo.py') 806 return path 807 os.path.realpath = realpath 808 loader = unittest.TestLoader() 809 loader.discover(start_dir='foo', pattern='foo.py') 810 811 def test_discovery_from_dotted_path(self): 812 loader = unittest.TestLoader() 813 814 tests = [self] 815 expectedPath = os.path.abspath(os.path.dirname(test.test_unittest.__file__)) 816 817 self.wasRun = False 818 def _find_tests(start_dir, pattern): 819 self.wasRun = True 820 self.assertEqual(start_dir, expectedPath) 821 return tests 822 loader._find_tests = _find_tests 823 suite = loader.discover('test.test_unittest') 824 self.assertTrue(self.wasRun) 825 self.assertEqual(suite._tests, tests) 826 827 828 def test_discovery_from_dotted_path_builtin_modules(self): 829 830 loader = unittest.TestLoader() 831 832 listdir = os.listdir 833 os.listdir = lambda _: ['test_this_does_not_exist.py'] 834 isfile = os.path.isfile 835 isdir = os.path.isdir 836 os.path.isdir = lambda _: False 837 orig_sys_path = sys.path[:] 838 def restore(): 839 os.path.isfile = isfile 840 os.path.isdir = isdir 841 os.listdir = listdir 842 sys.path[:] = orig_sys_path 843 self.addCleanup(restore) 844 845 with self.assertRaises(TypeError) as cm: 846 loader.discover('sys') 847 self.assertEqual(str(cm.exception), 848 'Can not use builtin modules ' 849 'as dotted module names') 850 851 def test_discovery_failed_discovery(self): 852 from test.test_importlib import util 853 854 loader = unittest.TestLoader() 855 package = types.ModuleType('package') 856 857 def _import(packagename, *args, **kwargs): 858 sys.modules[packagename] = package 859 return package 860 861 with unittest.mock.patch('builtins.__import__', _import): 862 # Since loader.discover() can modify sys.path, restore it when done. 863 with import_helper.DirsOnSysPath(): 864 # Make sure to remove 'package' from sys.modules when done. 865 with util.uncache('package'): 866 with self.assertRaises(TypeError) as cm: 867 loader.discover('package') 868 self.assertEqual(str(cm.exception), 869 'don\'t know how to discover from {!r}' 870 .format(package)) 871 872 873if __name__ == '__main__': 874 unittest.main() 875