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
192            setattr(program, attr, None)
193            program.parseArgs([None])
194            self.assertIs(getattr(program, attr), False)
195
196            false = []
197            setattr(program, attr, false)
198            program.parseArgs([None])
199            self.assertIs(getattr(program, attr), false)
200
201            true = [42]
202            setattr(program, attr, true)
203            program.parseArgs([None])
204            self.assertIs(getattr(program, attr), true)
205
206            short_opt = '-%s' % arg[0]
207            long_opt = '--%s' % arg
208            for opt in short_opt, long_opt:
209                setattr(program, attr, None)
210                program.parseArgs([None, opt])
211                self.assertIs(getattr(program, attr), True)
212
213                setattr(program, attr, False)
214                with support.captured_stderr() as stderr, \
215                    self.assertRaises(SystemExit) as cm:
216                    program.parseArgs([None, opt])
217                self.assertEqual(cm.exception.args, (2,))
218
219                setattr(program, attr, True)
220                with support.captured_stderr() as stderr, \
221                    self.assertRaises(SystemExit) as cm:
222                    program.parseArgs([None, opt])
223                self.assertEqual(cm.exception.args, (2,))
224
225    def testWarning(self):
226        """Test the warnings argument"""
227        # see #10535
228        class FakeTP(unittest.TestProgram):
229            def parseArgs(self, *args, **kw): pass
230            def runTests(self, *args, **kw): pass
231        warnoptions = sys.warnoptions[:]
232        try:
233            sys.warnoptions[:] = []
234            # no warn options, no arg -> default
235            self.assertEqual(FakeTP().warnings, 'default')
236            # no warn options, w/ arg -> arg value
237            self.assertEqual(FakeTP(warnings='ignore').warnings, 'ignore')
238            sys.warnoptions[:] = ['somevalue']
239            # warn options, no arg -> None
240            # warn options, w/ arg -> arg value
241            self.assertEqual(FakeTP().warnings, None)
242            self.assertEqual(FakeTP(warnings='ignore').warnings, 'ignore')
243        finally:
244            sys.warnoptions[:] = warnoptions
245
246    def testRunTestsRunnerClass(self):
247        program = self.program
248
249        program.testRunner = FakeRunner
250        program.verbosity = 'verbosity'
251        program.failfast = 'failfast'
252        program.buffer = 'buffer'
253        program.warnings = 'warnings'
254
255        program.runTests()
256
257        self.assertEqual(FakeRunner.initArgs, {'verbosity': 'verbosity',
258                                                'failfast': 'failfast',
259                                                'buffer': 'buffer',
260                                                'tb_locals': False,
261                                                'warnings': 'warnings'})
262        self.assertEqual(FakeRunner.test, 'test')
263        self.assertIs(program.result, RESULT)
264
265    def testRunTestsRunnerInstance(self):
266        program = self.program
267
268        program.testRunner = FakeRunner()
269        FakeRunner.initArgs = None
270
271        program.runTests()
272
273        # A new FakeRunner should not have been instantiated
274        self.assertIsNone(FakeRunner.initArgs)
275
276        self.assertEqual(FakeRunner.test, 'test')
277        self.assertIs(program.result, RESULT)
278
279    def test_locals(self):
280        program = self.program
281
282        program.testRunner = FakeRunner
283        program.parseArgs([None, '--locals'])
284        self.assertEqual(True, program.tb_locals)
285        program.runTests()
286        self.assertEqual(FakeRunner.initArgs, {'buffer': False,
287                                               'failfast': False,
288                                               'tb_locals': True,
289                                               'verbosity': 1,
290                                               'warnings': None})
291
292    def testRunTestsOldRunnerClass(self):
293        program = self.program
294
295        # Two TypeErrors are needed to fall all the way back to old-style
296        # runners - one to fail tb_locals, one to fail buffer etc.
297        FakeRunner.raiseError = 2
298        program.testRunner = FakeRunner
299        program.verbosity = 'verbosity'
300        program.failfast = 'failfast'
301        program.buffer = 'buffer'
302        program.test = 'test'
303
304        program.runTests()
305
306        # If initialising raises a type error it should be retried
307        # without the new keyword arguments
308        self.assertEqual(FakeRunner.initArgs, {})
309        self.assertEqual(FakeRunner.test, 'test')
310        self.assertIs(program.result, RESULT)
311
312    def testCatchBreakInstallsHandler(self):
313        module = sys.modules['unittest.main']
314        original = module.installHandler
315        def restore():
316            module.installHandler = original
317        self.addCleanup(restore)
318
319        self.installed = False
320        def fakeInstallHandler():
321            self.installed = True
322        module.installHandler = fakeInstallHandler
323
324        program = self.program
325        program.catchbreak = True
326
327        program.testRunner = FakeRunner
328
329        program.runTests()
330        self.assertTrue(self.installed)
331
332    def _patch_isfile(self, names, exists=True):
333        def isfile(path):
334            return path in names
335        original = os.path.isfile
336        os.path.isfile = isfile
337        def restore():
338            os.path.isfile = original
339        self.addCleanup(restore)
340
341
342    def testParseArgsFileNames(self):
343        # running tests with filenames instead of module names
344        program = self.program
345        argv = ['progname', 'foo.py', 'bar.Py', 'baz.PY', 'wing.txt']
346        self._patch_isfile(argv)
347
348        program.createTests = lambda: None
349        program.parseArgs(argv)
350
351        # note that 'wing.txt' is not a Python file so the name should
352        # *not* be converted to a module name
353        expected = ['foo', 'bar', 'baz', 'wing.txt']
354        self.assertEqual(program.testNames, expected)
355
356
357    def testParseArgsFilePaths(self):
358        program = self.program
359        argv = ['progname', 'foo/bar/baz.py', 'green\\red.py']
360        self._patch_isfile(argv)
361
362        program.createTests = lambda: None
363        program.parseArgs(argv)
364
365        expected = ['foo.bar.baz', 'green.red']
366        self.assertEqual(program.testNames, expected)
367
368
369    def testParseArgsNonExistentFiles(self):
370        program = self.program
371        argv = ['progname', 'foo/bar/baz.py', 'green\\red.py']
372        self._patch_isfile([])
373
374        program.createTests = lambda: None
375        program.parseArgs(argv)
376
377        self.assertEqual(program.testNames, argv[1:])
378
379    def testParseArgsAbsolutePathsThatCanBeConverted(self):
380        cur_dir = os.getcwd()
381        program = self.program
382        def _join(name):
383            return os.path.join(cur_dir, name)
384        argv = ['progname', _join('foo/bar/baz.py'), _join('green\\red.py')]
385        self._patch_isfile(argv)
386
387        program.createTests = lambda: None
388        program.parseArgs(argv)
389
390        expected = ['foo.bar.baz', 'green.red']
391        self.assertEqual(program.testNames, expected)
392
393    def testParseArgsAbsolutePathsThatCannotBeConverted(self):
394        program = self.program
395        # even on Windows '/...' is considered absolute by os.path.abspath
396        argv = ['progname', '/foo/bar/baz.py', '/green/red.py']
397        self._patch_isfile(argv)
398
399        program.createTests = lambda: None
400        program.parseArgs(argv)
401
402        self.assertEqual(program.testNames, argv[1:])
403
404        # it may be better to use platform specific functions to normalise paths
405        # rather than accepting '.PY' and '\' as file separator on Linux / Mac
406        # it would also be better to check that a filename is a valid module
407        # identifier (we have a regex for this in loader.py)
408        # for invalid filenames should we raise a useful error rather than
409        # leaving the current error message (import of filename fails) in place?
410
411    def testParseArgsSelectedTestNames(self):
412        program = self.program
413        argv = ['progname', '-k', 'foo', '-k', 'bar', '-k', '*pat*']
414
415        program.createTests = lambda: None
416        program.parseArgs(argv)
417
418        self.assertEqual(program.testNamePatterns, ['*foo*', '*bar*', '*pat*'])
419
420    def testSelectedTestNamesFunctionalTest(self):
421        def run_unittest(args):
422            p = subprocess.Popen([sys.executable, '-m', 'unittest'] + args,
423                stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, cwd=os.path.dirname(__file__))
424            with p:
425                _, stderr = p.communicate()
426            return stderr.decode()
427
428        t = '_test_warnings'
429        self.assertIn('Ran 7 tests', run_unittest([t]))
430        self.assertIn('Ran 7 tests', run_unittest(['-k', 'TestWarnings', t]))
431        self.assertIn('Ran 7 tests', run_unittest(['discover', '-p', '*_test*', '-k', 'TestWarnings']))
432        self.assertIn('Ran 2 tests', run_unittest(['-k', 'f', t]))
433        self.assertIn('Ran 7 tests', run_unittest(['-k', 't', t]))
434        self.assertIn('Ran 3 tests', run_unittest(['-k', '*t', t]))
435        self.assertIn('Ran 7 tests', run_unittest(['-k', '*test_warnings.*Warning*', t]))
436        self.assertIn('Ran 1 test', run_unittest(['-k', '*test_warnings.*warning*', t]))
437
438
439if __name__ == '__main__':
440    unittest.main()
441