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