Home | History | Annotate | Download | only in test
      1 import os
      2 import re
      3 import sys
      4 
      5 import unittest
      6 import unittest.test
      7 
      8 
      9 class TestDiscovery(unittest.TestCase):
     10 
     11     # Heavily mocked tests so I can avoid hitting the filesystem
     12     def test_get_name_from_path(self):
     13         loader = unittest.TestLoader()
     14 
     15         loader._top_level_dir = '/foo'
     16         name = loader._get_name_from_path('/foo/bar/baz.py')
     17         self.assertEqual(name, 'bar.baz')
     18 
     19         if not __debug__:
     20             # asserts are off
     21             return
     22 
     23         with self.assertRaises(AssertionError):
     24             loader._get_name_from_path('/bar/baz.py')
     25 
     26     def test_find_tests(self):
     27         loader = unittest.TestLoader()
     28 
     29         original_listdir = os.listdir
     30         def restore_listdir():
     31             os.listdir = original_listdir
     32         original_isfile = os.path.isfile
     33         def restore_isfile():
     34             os.path.isfile = original_isfile
     35         original_isdir = os.path.isdir
     36         def restore_isdir():
     37             os.path.isdir = original_isdir
     38 
     39         path_lists = [['test1.py', 'test2.py', 'not_a_test.py', 'test_dir',
     40                        'test.foo', 'test-not-a-module.py', 'another_dir'],
     41                       ['test3.py', 'test4.py', ]]
     42         os.listdir = lambda path: path_lists.pop(0)
     43         self.addCleanup(restore_listdir)
     44 
     45         def isdir(path):
     46             return path.endswith('dir')
     47         os.path.isdir = isdir
     48         self.addCleanup(restore_isdir)
     49 
     50         def isfile(path):
     51             # another_dir is not a package and so shouldn't be recursed into
     52             return not path.endswith('dir') and not 'another_dir' in path
     53         os.path.isfile = isfile
     54         self.addCleanup(restore_isfile)
     55 
     56         loader._get_module_from_name = lambda path: path + ' module'
     57         loader.loadTestsFromModule = lambda module: module + ' tests'
     58 
     59         top_level = os.path.abspath('/foo')
     60         loader._top_level_dir = top_level
     61         suite = list(loader._find_tests(top_level, 'test*.py'))
     62 
     63         expected = [name + ' module tests' for name in
     64                     ('test1', 'test2')]
     65         expected.extend([('test_dir.%s' % name) + ' module tests' for name in
     66                     ('test3', 'test4')])
     67         self.assertEqual(suite, expected)
     68 
     69     def test_find_tests_with_package(self):
     70         loader = unittest.TestLoader()
     71 
     72         original_listdir = os.listdir
     73         def restore_listdir():
     74             os.listdir = original_listdir
     75         original_isfile = os.path.isfile
     76         def restore_isfile():
     77             os.path.isfile = original_isfile
     78         original_isdir = os.path.isdir
     79         def restore_isdir():
     80             os.path.isdir = original_isdir
     81 
     82         directories = ['a_directory', 'test_directory', 'test_directory2']
     83         path_lists = [directories, [], [], []]
     84         os.listdir = lambda path: path_lists.pop(0)
     85         self.addCleanup(restore_listdir)
     86 
     87         os.path.isdir = lambda path: True
     88         self.addCleanup(restore_isdir)
     89 
     90         os.path.isfile = lambda path: os.path.basename(path) not in directories
     91         self.addCleanup(restore_isfile)
     92 
     93         class Module(object):
     94             paths = []
     95             load_tests_args = []
     96 
     97             def __init__(self, path):
     98                 self.path = path
     99                 self.paths.append(path)
    100                 if os.path.basename(path) == 'test_directory':
    101                     def load_tests(loader, tests, pattern):
    102                         self.load_tests_args.append((loader, tests, pattern))
    103                         return 'load_tests'
    104                     self.load_tests = load_tests
    105 
    106             def __eq__(self, other):
    107                 return self.path == other.path
    108 
    109             # Silence py3k warning
    110             __hash__ = None
    111 
    112         loader._get_module_from_name = lambda name: Module(name)
    113         def loadTestsFromModule(module, use_load_tests):
    114             if use_load_tests:
    115                 raise self.failureException('use_load_tests should be False for packages')
    116             return module.path + ' module tests'
    117         loader.loadTestsFromModule = loadTestsFromModule
    118 
    119         loader._top_level_dir = '/foo'
    120         # this time no '.py' on the pattern so that it can match
    121         # a test package
    122         suite = list(loader._find_tests('/foo', 'test*'))
    123 
    124         # We should have loaded tests from the test_directory package by calling load_tests
    125         # and directly from the test_directory2 package
    126         self.assertEqual(suite,
    127                          ['load_tests', 'test_directory2' + ' module tests'])
    128         self.assertEqual(Module.paths, ['test_directory', 'test_directory2'])
    129 
    130         # load_tests should have been called once with loader, tests and pattern
    131         self.assertEqual(Module.load_tests_args,
    132                          [(loader, 'test_directory' + ' module tests', 'test*')])
    133 
    134     def test_discover(self):
    135         loader = unittest.TestLoader()
    136 
    137         original_isfile = os.path.isfile
    138         original_isdir = os.path.isdir
    139         def restore_isfile():
    140             os.path.isfile = original_isfile
    141 
    142         os.path.isfile = lambda path: False
    143         self.addCleanup(restore_isfile)
    144 
    145         orig_sys_path = sys.path[:]
    146         def restore_path():
    147             sys.path[:] = orig_sys_path
    148         self.addCleanup(restore_path)
    149 
    150         full_path = os.path.abspath(os.path.normpath('/foo'))
    151         with self.assertRaises(ImportError):
    152             loader.discover('/foo/bar', top_level_dir='/foo')
    153 
    154         self.assertEqual(loader._top_level_dir, full_path)
    155         self.assertIn(full_path, sys.path)
    156 
    157         os.path.isfile = lambda path: True
    158         os.path.isdir = lambda path: True
    159 
    160         def restore_isdir():
    161             os.path.isdir = original_isdir
    162         self.addCleanup(restore_isdir)
    163 
    164         _find_tests_args = []
    165         def _find_tests(start_dir, pattern):
    166             _find_tests_args.append((start_dir, pattern))
    167             return ['tests']
    168         loader._find_tests = _find_tests
    169         loader.suiteClass = str
    170 
    171         suite = loader.discover('/foo/bar/baz', 'pattern', '/foo/bar')
    172 
    173         top_level_dir = os.path.abspath('/foo/bar')
    174         start_dir = os.path.abspath('/foo/bar/baz')
    175         self.assertEqual(suite, "['tests']")
    176         self.assertEqual(loader._top_level_dir, top_level_dir)
    177         self.assertEqual(_find_tests_args, [(start_dir, 'pattern')])
    178         self.assertIn(top_level_dir, sys.path)
    179 
    180     def test_discover_with_modules_that_fail_to_import(self):
    181         loader = unittest.TestLoader()
    182 
    183         listdir = os.listdir
    184         os.listdir = lambda _: ['test_this_does_not_exist.py']
    185         isfile = os.path.isfile
    186         os.path.isfile = lambda _: True
    187         orig_sys_path = sys.path[:]
    188         def restore():
    189             os.path.isfile = isfile
    190             os.listdir = listdir
    191             sys.path[:] = orig_sys_path
    192         self.addCleanup(restore)
    193 
    194         suite = loader.discover('.')
    195         self.assertIn(os.getcwd(), sys.path)
    196         self.assertEqual(suite.countTestCases(), 1)
    197         test = list(list(suite)[0])[0] # extract test from suite
    198 
    199         with self.assertRaises(ImportError):
    200             test.test_this_does_not_exist()
    201 
    202     def test_command_line_handling_parseArgs(self):
    203         # Haha - take that uninstantiable class
    204         program = object.__new__(unittest.TestProgram)
    205 
    206         args = []
    207         def do_discovery(argv):
    208             args.extend(argv)
    209         program._do_discovery = do_discovery
    210         program.parseArgs(['something', 'discover'])
    211         self.assertEqual(args, [])
    212 
    213         program.parseArgs(['something', 'discover', 'foo', 'bar'])
    214         self.assertEqual(args, ['foo', 'bar'])
    215 
    216     def test_command_line_handling_do_discovery_too_many_arguments(self):
    217         class Stop(Exception):
    218             pass
    219         def usageExit():
    220             raise Stop
    221 
    222         program = object.__new__(unittest.TestProgram)
    223         program.usageExit = usageExit
    224         program.testLoader = None
    225 
    226         with self.assertRaises(Stop):
    227             # too many args
    228             program._do_discovery(['one', 'two', 'three', 'four'])
    229 
    230 
    231     def test_command_line_handling_do_discovery_uses_default_loader(self):
    232         program = object.__new__(unittest.TestProgram)
    233 
    234         class Loader(object):
    235             args = []
    236             def discover(self, start_dir, pattern, top_level_dir):
    237                 self.args.append((start_dir, pattern, top_level_dir))
    238                 return 'tests'
    239 
    240         program.testLoader = Loader()
    241         program._do_discovery(['-v'])
    242         self.assertEqual(Loader.args, [('.', 'test*.py', None)])
    243 
    244     def test_command_line_handling_do_discovery_calls_loader(self):
    245         program = object.__new__(unittest.TestProgram)
    246 
    247         class Loader(object):
    248             args = []
    249             def discover(self, start_dir, pattern, top_level_dir):
    250                 self.args.append((start_dir, pattern, top_level_dir))
    251                 return 'tests'
    252 
    253         program._do_discovery(['-v'], Loader=Loader)
    254         self.assertEqual(program.verbosity, 2)
    255         self.assertEqual(program.test, 'tests')
    256         self.assertEqual(Loader.args, [('.', 'test*.py', None)])
    257 
    258         Loader.args = []
    259         program = object.__new__(unittest.TestProgram)
    260         program._do_discovery(['--verbose'], Loader=Loader)
    261         self.assertEqual(program.test, 'tests')
    262         self.assertEqual(Loader.args, [('.', 'test*.py', None)])
    263 
    264         Loader.args = []
    265         program = object.__new__(unittest.TestProgram)
    266         program._do_discovery([], Loader=Loader)
    267         self.assertEqual(program.test, 'tests')
    268         self.assertEqual(Loader.args, [('.', 'test*.py', None)])
    269 
    270         Loader.args = []
    271         program = object.__new__(unittest.TestProgram)
    272         program._do_discovery(['fish'], Loader=Loader)
    273         self.assertEqual(program.test, 'tests')
    274         self.assertEqual(Loader.args, [('fish', 'test*.py', None)])
    275 
    276         Loader.args = []
    277         program = object.__new__(unittest.TestProgram)
    278         program._do_discovery(['fish', 'eggs'], Loader=Loader)
    279         self.assertEqual(program.test, 'tests')
    280         self.assertEqual(Loader.args, [('fish', 'eggs', None)])
    281 
    282         Loader.args = []
    283         program = object.__new__(unittest.TestProgram)
    284         program._do_discovery(['fish', 'eggs', 'ham'], Loader=Loader)
    285         self.assertEqual(program.test, 'tests')
    286         self.assertEqual(Loader.args, [('fish', 'eggs', 'ham')])
    287 
    288         Loader.args = []
    289         program = object.__new__(unittest.TestProgram)
    290         program._do_discovery(['-s', 'fish'], Loader=Loader)
    291         self.assertEqual(program.test, 'tests')
    292         self.assertEqual(Loader.args, [('fish', 'test*.py', None)])
    293 
    294         Loader.args = []
    295         program = object.__new__(unittest.TestProgram)
    296         program._do_discovery(['-t', 'fish'], Loader=Loader)
    297         self.assertEqual(program.test, 'tests')
    298         self.assertEqual(Loader.args, [('.', 'test*.py', 'fish')])
    299 
    300         Loader.args = []
    301         program = object.__new__(unittest.TestProgram)
    302         program._do_discovery(['-p', 'fish'], Loader=Loader)
    303         self.assertEqual(program.test, 'tests')
    304         self.assertEqual(Loader.args, [('.', 'fish', None)])
    305         self.assertFalse(program.failfast)
    306         self.assertFalse(program.catchbreak)
    307 
    308         Loader.args = []
    309         program = object.__new__(unittest.TestProgram)
    310         program._do_discovery(['-p', 'eggs', '-s', 'fish', '-v', '-f', '-c'],
    311                               Loader=Loader)
    312         self.assertEqual(program.test, 'tests')
    313         self.assertEqual(Loader.args, [('fish', 'eggs', None)])
    314         self.assertEqual(program.verbosity, 2)
    315         self.assertTrue(program.failfast)
    316         self.assertTrue(program.catchbreak)
    317 
    318     def setup_module_clash(self):
    319         class Module(object):
    320             __file__ = 'bar/foo.py'
    321         sys.modules['foo'] = Module
    322         full_path = os.path.abspath('foo')
    323         original_listdir = os.listdir
    324         original_isfile = os.path.isfile
    325         original_isdir = os.path.isdir
    326 
    327         def cleanup():
    328             os.listdir = original_listdir
    329             os.path.isfile = original_isfile
    330             os.path.isdir = original_isdir
    331             del sys.modules['foo']
    332             if full_path in sys.path:
    333                 sys.path.remove(full_path)
    334         self.addCleanup(cleanup)
    335 
    336         def listdir(_):
    337             return ['foo.py']
    338         def isfile(_):
    339             return True
    340         def isdir(_):
    341             return True
    342         os.listdir = listdir
    343         os.path.isfile = isfile
    344         os.path.isdir = isdir
    345         return full_path
    346 
    347     def test_detect_module_clash(self):
    348         full_path = self.setup_module_clash()
    349         loader = unittest.TestLoader()
    350 
    351         mod_dir = os.path.abspath('bar')
    352         expected_dir = os.path.abspath('foo')
    353         msg = re.escape(r"'foo' module incorrectly imported from %r. Expected %r. "
    354                 "Is this module globally installed?" % (mod_dir, expected_dir))
    355         self.assertRaisesRegexp(
    356             ImportError, '^%s$' % msg, loader.discover,
    357             start_dir='foo', pattern='foo.py'
    358         )
    359         self.assertEqual(sys.path[0], full_path)
    360 
    361     def test_module_symlink_ok(self):
    362         full_path = self.setup_module_clash()
    363 
    364         original_realpath = os.path.realpath
    365 
    366         mod_dir = os.path.abspath('bar')
    367         expected_dir = os.path.abspath('foo')
    368 
    369         def cleanup():
    370             os.path.realpath = original_realpath
    371         self.addCleanup(cleanup)
    372 
    373         def realpath(path):
    374             if path == os.path.join(mod_dir, 'foo.py'):
    375                 return os.path.join(expected_dir, 'foo.py')
    376             return path
    377         os.path.realpath = realpath
    378         loader = unittest.TestLoader()
    379         loader.discover(start_dir='foo', pattern='foo.py')
    380 
    381     def test_discovery_from_dotted_path(self):
    382         loader = unittest.TestLoader()
    383 
    384         tests = [self]
    385         expectedPath = os.path.abspath(os.path.dirname(unittest.test.__file__))
    386 
    387         self.wasRun = False
    388         def _find_tests(start_dir, pattern):
    389             self.wasRun = True
    390             self.assertEqual(start_dir, expectedPath)
    391             return tests
    392         loader._find_tests = _find_tests
    393         suite = loader.discover('unittest.test')
    394         self.assertTrue(self.wasRun)
    395         self.assertEqual(suite._tests, tests)
    396 
    397 
    398 if __name__ == '__main__':
    399     unittest.main()
    400