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