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