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