1import io
2
3import os
4import sys
5import subprocess
6from test import support
7import unittest
8import unittest.test
9
10
11class Test_TestProgram(unittest.TestCase):
12
13    def test_discovery_from_dotted_path(self):
14        loader = unittest.TestLoader()
15
16        tests = [self]
17        expectedPath = os.path.abspath(os.path.dirname(unittest.test.__file__))
18
19        self.wasRun = False
20        def _find_tests(start_dir, pattern):
21            self.wasRun = True
22            self.assertEqual(start_dir, expectedPath)
23            return tests
24        loader._find_tests = _find_tests
25        suite = loader.discover('unittest.test')
26        self.assertTrue(self.wasRun)
27        self.assertEqual(suite._tests, tests)
28
29    # Horrible white box test
30    def testNoExit(self):
31        result = object()
32        test = object()
33
34        class FakeRunner(object):
35            def run(self, test):
36                self.test = test
37                return result
38
39        runner = FakeRunner()
40
41        oldParseArgs = unittest.TestProgram.parseArgs
42        def restoreParseArgs():
43            unittest.TestProgram.parseArgs = oldParseArgs
44        unittest.TestProgram.parseArgs = lambda *args: None
45        self.addCleanup(restoreParseArgs)
46
47        def removeTest():
48            del unittest.TestProgram.test
49        unittest.TestProgram.test = test
50        self.addCleanup(removeTest)
51
52        program = unittest.TestProgram(testRunner=runner, exit=False, verbosity=2)
53
54        self.assertEqual(program.result, result)
55        self.assertEqual(runner.test, test)
56        self.assertEqual(program.verbosity, 2)
57
58    class FooBar(unittest.TestCase):
59        def testPass(self):
60            assert True
61        def testFail(self):
62            assert False
63
64    class FooBarLoader(unittest.TestLoader):
65        """Test loader that returns a suite containing FooBar."""
66        def loadTestsFromModule(self, module):
67            return self.suiteClass(
68                [self.loadTestsFromTestCase(Test_TestProgram.FooBar)])
69
70        def loadTestsFromNames(self, names, module):
71            return self.suiteClass(
72                [self.loadTestsFromTestCase(Test_TestProgram.FooBar)])
73
74    def test_defaultTest_with_string(self):
75        class FakeRunner(object):
76            def run(self, test):
77                self.test = test
78                return True
79
80        old_argv = sys.argv
81        sys.argv = ['faketest']
82        runner = FakeRunner()
83        program = unittest.TestProgram(testRunner=runner, exit=False,
84                                       defaultTest='unittest.test',
85                                       testLoader=self.FooBarLoader())
86        sys.argv = old_argv
87        self.assertEqual(('unittest.test',), program.testNames)
88
89    def test_defaultTest_with_iterable(self):
90        class FakeRunner(object):
91            def run(self, test):
92                self.test = test
93                return True
94
95        old_argv = sys.argv
96        sys.argv = ['faketest']
97        runner = FakeRunner()
98        program = unittest.TestProgram(
99            testRunner=runner, exit=False,
100            defaultTest=['unittest.test', 'unittest.test2'],
101            testLoader=self.FooBarLoader())
102        sys.argv = old_argv
103        self.assertEqual(['unittest.test', 'unittest.test2'],
104                          program.testNames)
105
106    def test_NonExit(self):
107        program = unittest.main(exit=False,
108                                argv=["foobar"],
109                                testRunner=unittest.TextTestRunner(stream=io.StringIO()),
110                                testLoader=self.FooBarLoader())
111        self.assertTrue(hasattr(program, 'result'))
112
113
114    def test_Exit(self):
115        self.assertRaises(
116            SystemExit,
117            unittest.main,
118            argv=["foobar"],
119            testRunner=unittest.TextTestRunner(stream=io.StringIO()),
120            exit=True,
121            testLoader=self.FooBarLoader())
122
123
124    def test_ExitAsDefault(self):
125        self.assertRaises(
126            SystemExit,
127            unittest.main,
128            argv=["foobar"],
129            testRunner=unittest.TextTestRunner(stream=io.StringIO()),
130            testLoader=self.FooBarLoader())
131
132
133class InitialisableProgram(unittest.TestProgram):
134    exit = False
135    result = None
136    verbosity = 1
137    defaultTest = None
138    tb_locals = False
139    testRunner = None
140    testLoader = unittest.defaultTestLoader
141    module = '__main__'
142    progName = 'test'
143    test = 'test'
144    def __init__(self, *args):
145        pass
146
147RESULT = object()
148
149class FakeRunner(object):
150    initArgs = None
151    test = None
152    raiseError = 0
153
154    def __init__(self, **kwargs):
155        FakeRunner.initArgs = kwargs
156        if FakeRunner.raiseError:
157            FakeRunner.raiseError -= 1
158            raise TypeError
159
160    def run(self, test):
161        FakeRunner.test = test
162        return RESULT
163
164
165class TestCommandLineArgs(unittest.TestCase):
166
167    def setUp(self):
168        self.program = InitialisableProgram()
169        self.program.createTests = lambda: None
170        FakeRunner.initArgs = None
171        FakeRunner.test = None
172        FakeRunner.raiseError = 0
173
174    def testVerbosity(self):
175        program = self.program
176
177        for opt in '-q', '--quiet':
178            program.verbosity = 1
179            program.parseArgs([None, opt])
180            self.assertEqual(program.verbosity, 0)
181
182        for opt in '-v', '--verbose':
183            program.verbosity = 1
184            program.parseArgs([None, opt])
185            self.assertEqual(program.verbosity, 2)
186
187    def testBufferCatchFailfast(self):
188        program = self.program
189        for arg, attr in (('buffer', 'buffer'), ('failfast', 'failfast'),
190                      ('catch', 'catchbreak')):
191            if attr == 'catch' and not hasInstallHandler:
192                continue
193
194            setattr(program, attr, None)
195            program.parseArgs([None])
196            self.assertIs(getattr(program, attr), False)
197
198            false = []
199            setattr(program, attr, false)
200            program.parseArgs([None])
201            self.assertIs(getattr(program, attr), false)
202
203            true = [42]
204            setattr(program, attr, true)
205            program.parseArgs([None])
206            self.assertIs(getattr(program, attr), true)
207
208            short_opt = '-%s' % arg[0]
209            long_opt = '--%s' % arg
210            for opt in short_opt, long_opt:
211                setattr(program, attr, None)
212                program.parseArgs([None, opt])
213                self.assertIs(getattr(program, attr), True)
214
215                setattr(program, attr, False)
216                with support.captured_stderr() as stderr, \
217                    self.assertRaises(SystemExit) as cm:
218                    program.parseArgs([None, opt])
219                self.assertEqual(cm.exception.args, (2,))
220
221                setattr(program, attr, True)
222                with support.captured_stderr() as stderr, \
223                    self.assertRaises(SystemExit) as cm:
224                    program.parseArgs([None, opt])
225                self.assertEqual(cm.exception.args, (2,))
226
227    def testWarning(self):
228        """Test the warnings argument"""
229        # see #10535
230        class FakeTP(unittest.TestProgram):
231            def parseArgs(self, *args, **kw): pass
232            def runTests(self, *args, **kw): pass
233        warnoptions = sys.warnoptions[:]
234        try:
235            sys.warnoptions[:] = []
236            # no warn options, no arg -> default
237            self.assertEqual(FakeTP().warnings, 'default')
238            # no warn options, w/ arg -> arg value
239            self.assertEqual(FakeTP(warnings='ignore').warnings, 'ignore')
240            sys.warnoptions[:] = ['somevalue']
241            # warn options, no arg -> None
242            # warn options, w/ arg -> arg value
243            self.assertEqual(FakeTP().warnings, None)
244            self.assertEqual(FakeTP(warnings='ignore').warnings, 'ignore')
245        finally:
246            sys.warnoptions[:] = warnoptions
247
248    def testRunTestsRunnerClass(self):
249        program = self.program
250
251        program.testRunner = FakeRunner
252        program.verbosity = 'verbosity'
253        program.failfast = 'failfast'
254        program.buffer = 'buffer'
255        program.warnings = 'warnings'
256
257        program.runTests()
258
259        self.assertEqual(FakeRunner.initArgs, {'verbosity': 'verbosity',
260                                                'failfast': 'failfast',
261                                                'buffer': 'buffer',
262                                                'tb_locals': False,
263                                                'warnings': 'warnings'})
264        self.assertEqual(FakeRunner.test, 'test')
265        self.assertIs(program.result, RESULT)
266
267    def testRunTestsRunnerInstance(self):
268        program = self.program
269
270        program.testRunner = FakeRunner()
271        FakeRunner.initArgs = None
272
273        program.runTests()
274
275        # A new FakeRunner should not have been instantiated
276        self.assertIsNone(FakeRunner.initArgs)
277
278        self.assertEqual(FakeRunner.test, 'test')
279        self.assertIs(program.result, RESULT)
280
281    def test_locals(self):
282        program = self.program
283
284        program.testRunner = FakeRunner
285        program.parseArgs([None, '--locals'])
286        self.assertEqual(True, program.tb_locals)
287        program.runTests()
288        self.assertEqual(FakeRunner.initArgs, {'buffer': False,
289                                               'failfast': False,
290                                               'tb_locals': True,
291                                               'verbosity': 1,
292                                               'warnings': None})
293
294    def testRunTestsOldRunnerClass(self):
295        program = self.program
296
297        # Two TypeErrors are needed to fall all the way back to old-style
298        # runners - one to fail tb_locals, one to fail buffer etc.
299        FakeRunner.raiseError = 2
300        program.testRunner = FakeRunner
301        program.verbosity = 'verbosity'
302        program.failfast = 'failfast'
303        program.buffer = 'buffer'
304        program.test = 'test'
305
306        program.runTests()
307
308        # If initialising raises a type error it should be retried
309        # without the new keyword arguments
310        self.assertEqual(FakeRunner.initArgs, {})
311        self.assertEqual(FakeRunner.test, 'test')
312        self.assertIs(program.result, RESULT)
313
314    def testCatchBreakInstallsHandler(self):
315        module = sys.modules['unittest.main']
316        original = module.installHandler
317        def restore():
318            module.installHandler = original
319        self.addCleanup(restore)
320
321        self.installed = False
322        def fakeInstallHandler():
323            self.installed = True
324        module.installHandler = fakeInstallHandler
325
326        program = self.program
327        program.catchbreak = True
328
329        program.testRunner = FakeRunner
330
331        program.runTests()
332        self.assertTrue(self.installed)
333
334    def _patch_isfile(self, names, exists=True):
335        def isfile(path):
336            return path in names
337        original = os.path.isfile
338        os.path.isfile = isfile
339        def restore():
340            os.path.isfile = original
341        self.addCleanup(restore)
342
343
344    def testParseArgsFileNames(self):
345        # running tests with filenames instead of module names
346        program = self.program
347        argv = ['progname', 'foo.py', 'bar.Py', 'baz.PY', 'wing.txt']
348        self._patch_isfile(argv)
349
350        program.createTests = lambda: None
351        program.parseArgs(argv)
352
353        # note that 'wing.txt' is not a Python file so the name should
354        # *not* be converted to a module name
355        expected = ['foo', 'bar', 'baz', 'wing.txt']
356        self.assertEqual(program.testNames, expected)
357
358
359    def testParseArgsFilePaths(self):
360        program = self.program
361        argv = ['progname', 'foo/bar/baz.py', 'green\\red.py']
362        self._patch_isfile(argv)
363
364        program.createTests = lambda: None
365        program.parseArgs(argv)
366
367        expected = ['foo.bar.baz', 'green.red']
368        self.assertEqual(program.testNames, expected)
369
370
371    def testParseArgsNonExistentFiles(self):
372        program = self.program
373        argv = ['progname', 'foo/bar/baz.py', 'green\\red.py']
374        self._patch_isfile([])
375
376        program.createTests = lambda: None
377        program.parseArgs(argv)
378
379        self.assertEqual(program.testNames, argv[1:])
380
381    def testParseArgsAbsolutePathsThatCanBeConverted(self):
382        cur_dir = os.getcwd()
383        program = self.program
384        def _join(name):
385            return os.path.join(cur_dir, name)
386        argv = ['progname', _join('foo/bar/baz.py'), _join('green\\red.py')]
387        self._patch_isfile(argv)
388
389        program.createTests = lambda: None
390        program.parseArgs(argv)
391
392        expected = ['foo.bar.baz', 'green.red']
393        self.assertEqual(program.testNames, expected)
394
395    def testParseArgsAbsolutePathsThatCannotBeConverted(self):
396        program = self.program
397        # even on Windows '/...' is considered absolute by os.path.abspath
398        argv = ['progname', '/foo/bar/baz.py', '/green/red.py']
399        self._patch_isfile(argv)
400
401        program.createTests = lambda: None
402        program.parseArgs(argv)
403
404        self.assertEqual(program.testNames, argv[1:])
405
406        # it may be better to use platform specific functions to normalise paths
407        # rather than accepting '.PY' and '\' as file separator on Linux / Mac
408        # it would also be better to check that a filename is a valid module
409        # identifier (we have a regex for this in loader.py)
410        # for invalid filenames should we raise a useful error rather than
411        # leaving the current error message (import of filename fails) in place?
412
413    def testParseArgsSelectedTestNames(self):
414        program = self.program
415        argv = ['progname', '-k', 'foo', '-k', 'bar', '-k', '*pat*']
416
417        program.createTests = lambda: None
418        program.parseArgs(argv)
419
420        self.assertEqual(program.testNamePatterns, ['*foo*', '*bar*', '*pat*'])
421
422    def testSelectedTestNamesFunctionalTest(self):
423        def run_unittest(args):
424            p = subprocess.Popen([sys.executable, '-m', 'unittest'] + args,
425                stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, cwd=os.path.dirname(__file__))
426            with p:
427                _, stderr = p.communicate()
428            return stderr.decode()
429
430        t = '_test_warnings'
431        self.assertIn('Ran 7 tests', run_unittest([t]))
432        self.assertIn('Ran 7 tests', run_unittest(['-k', 'TestWarnings', t]))
433        self.assertIn('Ran 7 tests', run_unittest(['discover', '-p', '*_test*', '-k', 'TestWarnings']))
434        self.assertIn('Ran 2 tests', run_unittest(['-k', 'f', t]))
435        self.assertIn('Ran 7 tests', run_unittest(['-k', 't', t]))
436        self.assertIn('Ran 3 tests', run_unittest(['-k', '*t', t]))
437        self.assertIn('Ran 7 tests', run_unittest(['-k', '*test_warnings.*Warning*', t]))
438        self.assertIn('Ran 1 test', run_unittest(['-k', '*test_warnings.*warning*', t]))
439
440
441if __name__ == '__main__':
442    unittest.main()
443