1  import os.path
       2  from os.path import abspath
       3  import re
       4  import sys
       5  import types
       6  import pickle
       7  from test import support
       8  from test.support import import_helper
       9  
      10  import unittest
      11  import unittest.mock
      12  import unittest.test
      13  
      14  
      15  class ESC[4;38;5;81mTestableTestProgram(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestProgram):
      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  
      27  class ESC[4;38;5;81mTestDiscovery(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      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 ESC[4;38;5;81mModule(ESC[4;38;5;149mobject):
     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 ESC[4;38;5;81mModule(ESC[4;38;5;149mobject):
     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 ESC[4;38;5;81mModule(ESC[4;38;5;149mobject):
     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, 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 ESC[4;38;5;81mModule(ESC[4;38;5;149mobject):
     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 ESC[4;38;5;81mLoader(ESC[4;38;5;149mobject):
     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 ESC[4;38;5;81mLoader(ESC[4;38;5;149mobject):
     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 ESC[4;38;5;81mModule(ESC[4;38;5;149mobject):
     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          original_realpath = os.path.realpath
     727  
     728          def cleanup():
     729              os.listdir = original_listdir
     730              os.path.isfile = original_isfile
     731              os.path.isdir = original_isdir
     732              os.path.realpath = original_realpath
     733              del sys.modules['foo']
     734              if full_path in sys.path:
     735                  sys.path.remove(full_path)
     736          self.addCleanup(cleanup)
     737  
     738          def listdir(_):
     739              return ['foo.py']
     740          def isfile(_):
     741              return True
     742          def isdir(_):
     743              return True
     744          os.listdir = listdir
     745          os.path.isfile = isfile
     746          os.path.isdir = isdir
     747          if os.name == 'nt':
     748              # ntpath.realpath may inject path prefixes when failing to
     749              # resolve real files, so we substitute abspath() here instead.
     750              os.path.realpath = os.path.abspath
     751          return full_path
     752  
     753      def test_detect_module_clash(self):
     754          full_path = self.setup_module_clash()
     755          loader = unittest.TestLoader()
     756  
     757          mod_dir = os.path.abspath('bar')
     758          expected_dir = os.path.abspath('foo')
     759          msg = re.escape(r"'foo' module incorrectly imported from %r. Expected %r. "
     760                  "Is this module globally installed?" % (mod_dir, expected_dir))
     761          self.assertRaisesRegex(
     762              ImportError, '^%s$' % msg, loader.discover,
     763              start_dir='foo', pattern='foo.py'
     764          )
     765          self.assertEqual(sys.path[0], full_path)
     766  
     767      def test_module_symlink_ok(self):
     768          full_path = self.setup_module_clash()
     769  
     770          original_realpath = os.path.realpath
     771  
     772          mod_dir = os.path.abspath('bar')
     773          expected_dir = os.path.abspath('foo')
     774  
     775          def cleanup():
     776              os.path.realpath = original_realpath
     777          self.addCleanup(cleanup)
     778  
     779          def realpath(path):
     780              if path == os.path.join(mod_dir, 'foo.py'):
     781                  return os.path.join(expected_dir, 'foo.py')
     782              return path
     783          os.path.realpath = realpath
     784          loader = unittest.TestLoader()
     785          loader.discover(start_dir='foo', pattern='foo.py')
     786  
     787      def test_discovery_from_dotted_path(self):
     788          loader = unittest.TestLoader()
     789  
     790          tests = [self]
     791          expectedPath = os.path.abspath(os.path.dirname(unittest.test.__file__))
     792  
     793          self.wasRun = False
     794          def _find_tests(start_dir, pattern):
     795              self.wasRun = True
     796              self.assertEqual(start_dir, expectedPath)
     797              return tests
     798          loader._find_tests = _find_tests
     799          suite = loader.discover('unittest.test')
     800          self.assertTrue(self.wasRun)
     801          self.assertEqual(suite._tests, tests)
     802  
     803  
     804      def test_discovery_from_dotted_path_builtin_modules(self):
     805  
     806          loader = unittest.TestLoader()
     807  
     808          listdir = os.listdir
     809          os.listdir = lambda _: ['test_this_does_not_exist.py']
     810          isfile = os.path.isfile
     811          isdir = os.path.isdir
     812          os.path.isdir = lambda _: False
     813          orig_sys_path = sys.path[:]
     814          def restore():
     815              os.path.isfile = isfile
     816              os.path.isdir = isdir
     817              os.listdir = listdir
     818              sys.path[:] = orig_sys_path
     819          self.addCleanup(restore)
     820  
     821          with self.assertRaises(TypeError) as cm:
     822              loader.discover('sys')
     823          self.assertEqual(str(cm.exception),
     824                           'Can not use builtin modules '
     825                           'as dotted module names')
     826  
     827      def test_discovery_failed_discovery(self):
     828          from test.test_importlib import util
     829  
     830          loader = unittest.TestLoader()
     831          package = types.ModuleType('package')
     832  
     833          def _import(packagename, *args, **kwargs):
     834              sys.modules[packagename] = package
     835              return package
     836  
     837          with unittest.mock.patch('builtins.__import__', _import):
     838              # Since loader.discover() can modify sys.path, restore it when done.
     839              with import_helper.DirsOnSysPath():
     840                  # Make sure to remove 'package' from sys.modules when done.
     841                  with util.uncache('package'):
     842                      with self.assertRaises(TypeError) as cm:
     843                          loader.discover('package')
     844                      self.assertEqual(str(cm.exception),
     845                                       'don\'t know how to discover from {!r}'
     846                                       .format(package))
     847  
     848  
     849  if __name__ == '__main__':
     850      unittest.main()