1import os.path 2from os.path import abspath 3import re 4import sys 5import types 6import pickle 7from test import support 8import test.test_importlib.util 9 10import unittest 11import unittest.mock 12import unittest.test 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, namespace=None): 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, top_level_dir) 410 self.assertEqual(_find_tests_args, [(start_dir, 'pattern')]) 411 self.assertIn(top_level_dir, sys.path) 412 413 def test_discover_start_dir_is_package_calls_package_load_tests(self): 414 # This test verifies that the package load_tests in a package is indeed 415 # invoked when the start_dir is a package (and not the top level). 416 # http://bugs.python.org/issue22457 417 418 # Test data: we expect the following: 419 # an isfile to verify the package, then importing and scanning 420 # as per _find_tests' normal behaviour. 421 # We expect to see our load_tests hook called once. 422 vfs = {abspath('/toplevel'): ['startdir'], 423 abspath('/toplevel/startdir'): ['__init__.py']} 424 def list_dir(path): 425 return list(vfs[path]) 426 self.addCleanup(setattr, os, 'listdir', os.listdir) 427 os.listdir = list_dir 428 self.addCleanup(setattr, os.path, 'isfile', os.path.isfile) 429 os.path.isfile = lambda path: path.endswith('.py') 430 self.addCleanup(setattr, os.path, 'isdir', os.path.isdir) 431 os.path.isdir = lambda path: not path.endswith('.py') 432 self.addCleanup(sys.path.remove, abspath('/toplevel')) 433 434 class Module(object): 435 paths = [] 436 load_tests_args = [] 437 438 def __init__(self, path): 439 self.path = path 440 441 def load_tests(self, loader, tests, pattern): 442 return ['load_tests called ' + self.path] 443 444 def __eq__(self, other): 445 return self.path == other.path 446 447 loader = unittest.TestLoader() 448 loader._get_module_from_name = lambda name: Module(name) 449 loader.suiteClass = lambda thing: thing 450 451 suite = loader.discover('/toplevel/startdir', top_level_dir='/toplevel') 452 453 # We should have loaded tests from the package __init__. 454 # (normally this would be nested TestSuites.) 455 self.assertEqual(suite, 456 [['load_tests called startdir']]) 457 458 def setup_import_issue_tests(self, fakefile): 459 listdir = os.listdir 460 os.listdir = lambda _: [fakefile] 461 isfile = os.path.isfile 462 os.path.isfile = lambda _: True 463 orig_sys_path = sys.path[:] 464 def restore(): 465 os.path.isfile = isfile 466 os.listdir = listdir 467 sys.path[:] = orig_sys_path 468 self.addCleanup(restore) 469 470 def setup_import_issue_package_tests(self, vfs): 471 self.addCleanup(setattr, os, 'listdir', os.listdir) 472 self.addCleanup(setattr, os.path, 'isfile', os.path.isfile) 473 self.addCleanup(setattr, os.path, 'isdir', os.path.isdir) 474 self.addCleanup(sys.path.__setitem__, slice(None), list(sys.path)) 475 def list_dir(path): 476 return list(vfs[path]) 477 os.listdir = list_dir 478 os.path.isdir = lambda path: not path.endswith('.py') 479 os.path.isfile = lambda path: path.endswith('.py') 480 481 def test_discover_with_modules_that_fail_to_import(self): 482 loader = unittest.TestLoader() 483 484 self.setup_import_issue_tests('test_this_does_not_exist.py') 485 486 suite = loader.discover('.') 487 self.assertIn(os.getcwd(), sys.path) 488 self.assertEqual(suite.countTestCases(), 1) 489 # Errors loading the suite are also captured for introspection. 490 self.assertNotEqual([], loader.errors) 491 self.assertEqual(1, len(loader.errors)) 492 error = loader.errors[0] 493 self.assertTrue( 494 'Failed to import test module: test_this_does_not_exist' in error, 495 'missing error string in %r' % error) 496 test = list(list(suite)[0])[0] # extract test from suite 497 498 with self.assertRaises(ImportError): 499 test.test_this_does_not_exist() 500 501 def test_discover_with_init_modules_that_fail_to_import(self): 502 vfs = {abspath('/foo'): ['my_package'], 503 abspath('/foo/my_package'): ['__init__.py', 'test_module.py']} 504 self.setup_import_issue_package_tests(vfs) 505 import_calls = [] 506 def _get_module_from_name(name): 507 import_calls.append(name) 508 raise ImportError("Cannot import Name") 509 loader = unittest.TestLoader() 510 loader._get_module_from_name = _get_module_from_name 511 suite = loader.discover(abspath('/foo')) 512 513 self.assertIn(abspath('/foo'), sys.path) 514 self.assertEqual(suite.countTestCases(), 1) 515 # Errors loading the suite are also captured for introspection. 516 self.assertNotEqual([], loader.errors) 517 self.assertEqual(1, len(loader.errors)) 518 error = loader.errors[0] 519 self.assertTrue( 520 'Failed to import test module: my_package' in error, 521 'missing error string in %r' % error) 522 test = list(list(suite)[0])[0] # extract test from suite 523 with self.assertRaises(ImportError): 524 test.my_package() 525 self.assertEqual(import_calls, ['my_package']) 526 527 # Check picklability 528 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 529 pickle.loads(pickle.dumps(test, proto)) 530 531 def test_discover_with_module_that_raises_SkipTest_on_import(self): 532 if not unittest.BaseTestSuite._cleanup: 533 raise unittest.SkipTest("Suite cleanup is disabled") 534 535 loader = unittest.TestLoader() 536 537 def _get_module_from_name(name): 538 raise unittest.SkipTest('skipperoo') 539 loader._get_module_from_name = _get_module_from_name 540 541 self.setup_import_issue_tests('test_skip_dummy.py') 542 543 suite = loader.discover('.') 544 self.assertEqual(suite.countTestCases(), 1) 545 546 result = unittest.TestResult() 547 suite.run(result) 548 self.assertEqual(len(result.skipped), 1) 549 550 # Check picklability 551 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 552 pickle.loads(pickle.dumps(suite, proto)) 553 554 def test_discover_with_init_module_that_raises_SkipTest_on_import(self): 555 if not unittest.BaseTestSuite._cleanup: 556 raise unittest.SkipTest("Suite cleanup is disabled") 557 558 vfs = {abspath('/foo'): ['my_package'], 559 abspath('/foo/my_package'): ['__init__.py', 'test_module.py']} 560 self.setup_import_issue_package_tests(vfs) 561 import_calls = [] 562 def _get_module_from_name(name): 563 import_calls.append(name) 564 raise unittest.SkipTest('skipperoo') 565 loader = unittest.TestLoader() 566 loader._get_module_from_name = _get_module_from_name 567 suite = loader.discover(abspath('/foo')) 568 569 self.assertIn(abspath('/foo'), sys.path) 570 self.assertEqual(suite.countTestCases(), 1) 571 result = unittest.TestResult() 572 suite.run(result) 573 self.assertEqual(len(result.skipped), 1) 574 self.assertEqual(result.testsRun, 1) 575 self.assertEqual(import_calls, ['my_package']) 576 577 # Check picklability 578 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 579 pickle.loads(pickle.dumps(suite, proto)) 580 581 def test_command_line_handling_parseArgs(self): 582 program = TestableTestProgram() 583 584 args = [] 585 program._do_discovery = args.append 586 program.parseArgs(['something', 'discover']) 587 self.assertEqual(args, [[]]) 588 589 args[:] = [] 590 program.parseArgs(['something', 'discover', 'foo', 'bar']) 591 self.assertEqual(args, [['foo', 'bar']]) 592 593 def test_command_line_handling_discover_by_default(self): 594 program = TestableTestProgram() 595 596 args = [] 597 program._do_discovery = args.append 598 program.parseArgs(['something']) 599 self.assertEqual(args, [[]]) 600 self.assertEqual(program.verbosity, 1) 601 self.assertIs(program.buffer, False) 602 self.assertIs(program.catchbreak, False) 603 self.assertIs(program.failfast, False) 604 605 def test_command_line_handling_discover_by_default_with_options(self): 606 program = TestableTestProgram() 607 608 args = [] 609 program._do_discovery = args.append 610 program.parseArgs(['something', '-v', '-b', '-v', '-c', '-f']) 611 self.assertEqual(args, [[]]) 612 self.assertEqual(program.verbosity, 2) 613 self.assertIs(program.buffer, True) 614 self.assertIs(program.catchbreak, True) 615 self.assertIs(program.failfast, True) 616 617 618 def test_command_line_handling_do_discovery_too_many_arguments(self): 619 program = TestableTestProgram() 620 program.testLoader = None 621 622 with support.captured_stderr() as stderr, \ 623 self.assertRaises(SystemExit) as cm: 624 # too many args 625 program._do_discovery(['one', 'two', 'three', 'four']) 626 self.assertEqual(cm.exception.args, (2,)) 627 self.assertIn('usage:', stderr.getvalue()) 628 629 630 def test_command_line_handling_do_discovery_uses_default_loader(self): 631 program = object.__new__(unittest.TestProgram) 632 program._initArgParsers() 633 634 class Loader(object): 635 args = [] 636 def discover(self, start_dir, pattern, top_level_dir): 637 self.args.append((start_dir, pattern, top_level_dir)) 638 return 'tests' 639 640 program.testLoader = Loader() 641 program._do_discovery(['-v']) 642 self.assertEqual(Loader.args, [('.', 'test*.py', None)]) 643 644 def test_command_line_handling_do_discovery_calls_loader(self): 645 program = TestableTestProgram() 646 647 class Loader(object): 648 args = [] 649 def discover(self, start_dir, pattern, top_level_dir): 650 self.args.append((start_dir, pattern, top_level_dir)) 651 return 'tests' 652 653 program._do_discovery(['-v'], Loader=Loader) 654 self.assertEqual(program.verbosity, 2) 655 self.assertEqual(program.test, 'tests') 656 self.assertEqual(Loader.args, [('.', 'test*.py', None)]) 657 658 Loader.args = [] 659 program = TestableTestProgram() 660 program._do_discovery(['--verbose'], Loader=Loader) 661 self.assertEqual(program.test, 'tests') 662 self.assertEqual(Loader.args, [('.', 'test*.py', None)]) 663 664 Loader.args = [] 665 program = TestableTestProgram() 666 program._do_discovery([], Loader=Loader) 667 self.assertEqual(program.test, 'tests') 668 self.assertEqual(Loader.args, [('.', 'test*.py', None)]) 669 670 Loader.args = [] 671 program = TestableTestProgram() 672 program._do_discovery(['fish'], Loader=Loader) 673 self.assertEqual(program.test, 'tests') 674 self.assertEqual(Loader.args, [('fish', 'test*.py', None)]) 675 676 Loader.args = [] 677 program = TestableTestProgram() 678 program._do_discovery(['fish', 'eggs'], Loader=Loader) 679 self.assertEqual(program.test, 'tests') 680 self.assertEqual(Loader.args, [('fish', 'eggs', None)]) 681 682 Loader.args = [] 683 program = TestableTestProgram() 684 program._do_discovery(['fish', 'eggs', 'ham'], Loader=Loader) 685 self.assertEqual(program.test, 'tests') 686 self.assertEqual(Loader.args, [('fish', 'eggs', 'ham')]) 687 688 Loader.args = [] 689 program = TestableTestProgram() 690 program._do_discovery(['-s', 'fish'], Loader=Loader) 691 self.assertEqual(program.test, 'tests') 692 self.assertEqual(Loader.args, [('fish', 'test*.py', None)]) 693 694 Loader.args = [] 695 program = TestableTestProgram() 696 program._do_discovery(['-t', 'fish'], Loader=Loader) 697 self.assertEqual(program.test, 'tests') 698 self.assertEqual(Loader.args, [('.', 'test*.py', 'fish')]) 699 700 Loader.args = [] 701 program = TestableTestProgram() 702 program._do_discovery(['-p', 'fish'], Loader=Loader) 703 self.assertEqual(program.test, 'tests') 704 self.assertEqual(Loader.args, [('.', 'fish', None)]) 705 self.assertFalse(program.failfast) 706 self.assertFalse(program.catchbreak) 707 708 Loader.args = [] 709 program = TestableTestProgram() 710 program._do_discovery(['-p', 'eggs', '-s', 'fish', '-v', '-f', '-c'], 711 Loader=Loader) 712 self.assertEqual(program.test, 'tests') 713 self.assertEqual(Loader.args, [('fish', 'eggs', None)]) 714 self.assertEqual(program.verbosity, 2) 715 self.assertTrue(program.failfast) 716 self.assertTrue(program.catchbreak) 717 718 def setup_module_clash(self): 719 class Module(object): 720 __file__ = 'bar/foo.py' 721 sys.modules['foo'] = Module 722 full_path = os.path.abspath('foo') 723 original_listdir = os.listdir 724 original_isfile = os.path.isfile 725 original_isdir = os.path.isdir 726 727 def cleanup(): 728 os.listdir = original_listdir 729 os.path.isfile = original_isfile 730 os.path.isdir = original_isdir 731 del sys.modules['foo'] 732 if full_path in sys.path: 733 sys.path.remove(full_path) 734 self.addCleanup(cleanup) 735 736 def listdir(_): 737 return ['foo.py'] 738 def isfile(_): 739 return True 740 def isdir(_): 741 return True 742 os.listdir = listdir 743 os.path.isfile = isfile 744 os.path.isdir = isdir 745 return full_path 746 747 def test_detect_module_clash(self): 748 full_path = self.setup_module_clash() 749 loader = unittest.TestLoader() 750 751 mod_dir = os.path.abspath('bar') 752 expected_dir = os.path.abspath('foo') 753 msg = re.escape(r"'foo' module incorrectly imported from %r. Expected %r. " 754 "Is this module globally installed?" % (mod_dir, expected_dir)) 755 self.assertRaisesRegex( 756 ImportError, '^%s$' % msg, loader.discover, 757 start_dir='foo', pattern='foo.py' 758 ) 759 self.assertEqual(sys.path[0], full_path) 760 761 def test_module_symlink_ok(self): 762 full_path = self.setup_module_clash() 763 764 original_realpath = os.path.realpath 765 766 mod_dir = os.path.abspath('bar') 767 expected_dir = os.path.abspath('foo') 768 769 def cleanup(): 770 os.path.realpath = original_realpath 771 self.addCleanup(cleanup) 772 773 def realpath(path): 774 if path == os.path.join(mod_dir, 'foo.py'): 775 return os.path.join(expected_dir, 'foo.py') 776 return path 777 os.path.realpath = realpath 778 loader = unittest.TestLoader() 779 loader.discover(start_dir='foo', pattern='foo.py') 780 781 def test_discovery_from_dotted_path(self): 782 loader = unittest.TestLoader() 783 784 tests = [self] 785 expectedPath = os.path.abspath(os.path.dirname(unittest.test.__file__)) 786 787 self.wasRun = False 788 def _find_tests(start_dir, pattern, namespace=None): 789 self.wasRun = True 790 self.assertEqual(start_dir, expectedPath) 791 return tests 792 loader._find_tests = _find_tests 793 suite = loader.discover('unittest.test') 794 self.assertTrue(self.wasRun) 795 self.assertEqual(suite._tests, tests) 796 797 798 def test_discovery_from_dotted_path_builtin_modules(self): 799 800 loader = unittest.TestLoader() 801 802 listdir = os.listdir 803 os.listdir = lambda _: ['test_this_does_not_exist.py'] 804 isfile = os.path.isfile 805 isdir = os.path.isdir 806 os.path.isdir = lambda _: False 807 orig_sys_path = sys.path[:] 808 def restore(): 809 os.path.isfile = isfile 810 os.path.isdir = isdir 811 os.listdir = listdir 812 sys.path[:] = orig_sys_path 813 self.addCleanup(restore) 814 815 with self.assertRaises(TypeError) as cm: 816 loader.discover('sys') 817 self.assertEqual(str(cm.exception), 818 'Can not use builtin modules ' 819 'as dotted module names') 820 821 def test_discovery_from_dotted_namespace_packages(self): 822 loader = unittest.TestLoader() 823 824 package = types.ModuleType('package') 825 package.__path__ = ['/a', '/b'] 826 package.__spec__ = types.SimpleNamespace( 827 loader=None, 828 submodule_search_locations=['/a', '/b'] 829 ) 830 831 def _import(packagename, *args, **kwargs): 832 sys.modules[packagename] = package 833 return package 834 835 _find_tests_args = [] 836 def _find_tests(start_dir, pattern, namespace=None): 837 _find_tests_args.append((start_dir, pattern)) 838 return ['%s/tests' % start_dir] 839 840 loader._find_tests = _find_tests 841 loader.suiteClass = list 842 843 with unittest.mock.patch('builtins.__import__', _import): 844 # Since loader.discover() can modify sys.path, restore it when done. 845 with support.DirsOnSysPath(): 846 # Make sure to remove 'package' from sys.modules when done. 847 with test.test_importlib.util.uncache('package'): 848 suite = loader.discover('package') 849 850 self.assertEqual(suite, ['/a/tests', '/b/tests']) 851 852 def test_discovery_failed_discovery(self): 853 loader = unittest.TestLoader() 854 package = types.ModuleType('package') 855 856 def _import(packagename, *args, **kwargs): 857 sys.modules[packagename] = package 858 return package 859 860 with unittest.mock.patch('builtins.__import__', _import): 861 # Since loader.discover() can modify sys.path, restore it when done. 862 with support.DirsOnSysPath(): 863 # Make sure to remove 'package' from sys.modules when done. 864 with test.test_importlib.util.uncache('package'): 865 with self.assertRaises(TypeError) as cm: 866 loader.discover('package') 867 self.assertEqual(str(cm.exception), 868 'don\'t know how to discover from {!r}' 869 .format(package)) 870 871 872if __name__ == '__main__': 873 unittest.main() 874