test_discovery.py 32 KB

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