test_discovery.py 32 KB

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