[Python-checkins] r87003 - in python/branches/py3k: Doc/library/unittest.rst Lib/unittest/main.py Lib/unittest/test/test_program.py Misc/NEWS

michael.foord python-checkins at python.org
Sat Dec 4 02:11:21 CET 2010


Author: michael.foord
Date: Sat Dec  4 02:11:21 2010
New Revision: 87003

Log:
Issue 10620: Specifying test modules by path instead of module name to 'python -m unittest'

Modified:
   python/branches/py3k/Doc/library/unittest.rst
   python/branches/py3k/Lib/unittest/main.py
   python/branches/py3k/Lib/unittest/test/test_program.py
   python/branches/py3k/Misc/NEWS

Modified: python/branches/py3k/Doc/library/unittest.rst
==============================================================================
--- python/branches/py3k/Doc/library/unittest.rst	(original)
+++ python/branches/py3k/Doc/library/unittest.rst	Sat Dec  4 02:11:21 2010
@@ -204,6 +204,16 @@
 You can pass in a list with any combination of module names, and fully
 qualified class or method names.
 
+Test modules can be specified by file path as well::
+
+   python -m unittest tests/test_something.py
+
+This allows you to use the shell filename completion to specify the test module.
+The file specified must still be importable as a module. The path is converted
+to a module name by removing the '.py' and converting path separators into '.'.
+If you want to execute a test file that isn't importable as a module you should
+execute the file directly instead.
+
 You can run tests with more detail (higher verbosity) by passing in the -v flag::
 
    python -m unittest -v test_module

Modified: python/branches/py3k/Lib/unittest/main.py
==============================================================================
--- python/branches/py3k/Lib/unittest/main.py	(original)
+++ python/branches/py3k/Lib/unittest/main.py	Sat Dec  4 02:11:21 2010
@@ -58,7 +58,24 @@
                                                in MyTestCase
 """
 
+def _convert_name(name):
+    # on Linux / Mac OS X 'foo.PY' is not importable, but on
+    # Windows it is. Simpler to do a case insensitive match
+    # a better check would be to check that the name is a
+    # valid Python module name.
+    if os.path.isfile(name) and name.lower().endswith('.py'):
+        if os.path.isabs(name):
+            rel_path = os.path.relpath(name, os.getcwd())
+            if os.path.isabs(rel_path) or rel_path.startswith(os.pardir):
+                return name
+            name = rel_path
+        # on Windows both '\' and '/' are used as path
+        # separators. Better to replace both than rely on os.path.sep
+        return name[:-3].replace('\\', '.').replace('/', '.')
+    return name
 
+def _convert_names(names):
+    return [_convert_name(name) for name in names]
 
 class TestProgram(object):
     """A command-line program that runs a set of tests; this is primarily
@@ -153,7 +170,7 @@
                 # createTests will load tests from self.module
                 self.testNames = None
             elif len(args) > 0:
-                self.testNames = args
+                self.testNames = _convert_names(args)
                 if __name__ == '__main__':
                     # to support python -m unittest ...
                     self.module = None

Modified: python/branches/py3k/Lib/unittest/test/test_program.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_program.py	(original)
+++ python/branches/py3k/Lib/unittest/test/test_program.py	Sat Dec  4 02:11:21 2010
@@ -273,6 +273,85 @@
         program.runTests()
         self.assertTrue(self.installed)
 
+    def _patch_isfile(self, names, exists=True):
+        def isfile(path):
+            return path in names
+        original = os.path.isfile
+        os.path.isfile = isfile
+        def restore():
+            os.path.isfile = original
+        self.addCleanup(restore)
+
+
+    def testParseArgsFileNames(self):
+        # running tests with filenames instead of module names
+        program = self.program
+        argv = ['progname', 'foo.py', 'bar.Py', 'baz.PY', 'wing.txt']
+        self._patch_isfile(argv)
+
+        program.createTests = lambda: None
+        program.parseArgs(argv)
+
+        # note that 'wing.txt' is not a Python file so the name should
+        # *not* be converted to a module name
+        expected = ['foo', 'bar', 'baz', 'wing.txt']
+        self.assertEqual(program.testNames, expected)
+
+
+    def testParseArgsFilePaths(self):
+        program = self.program
+        argv = ['progname', 'foo/bar/baz.py', 'green\\red.py']
+        self._patch_isfile(argv)
+
+        program.createTests = lambda: None
+        program.parseArgs(argv)
+
+        expected = ['foo.bar.baz', 'green.red']
+        self.assertEqual(program.testNames, expected)
+
+
+    def testParseArgsNonExistentFiles(self):
+        program = self.program
+        argv = ['progname', 'foo/bar/baz.py', 'green\\red.py']
+        self._patch_isfile([])
+
+        program.createTests = lambda: None
+        program.parseArgs(argv)
+
+        self.assertEqual(program.testNames, argv[1:])
+
+    def testParseArgsAbsolutePathsThatCanBeConverted(self):
+        cur_dir = os.getcwd()
+        program = self.program
+        def _join(name):
+            return os.path.join(cur_dir, name)
+        argv = ['progname', _join('foo/bar/baz.py'), _join('green\\red.py')]
+        self._patch_isfile(argv)
+
+        program.createTests = lambda: None
+        program.parseArgs(argv)
+
+        expected = ['foo.bar.baz', 'green.red']
+        self.assertEqual(program.testNames, expected)
+
+    def testParseArgsAbsolutePathsThatCannotBeConverted(self):
+        program = self.program
+        # will this test work on Windows? (is '/...' considered absolute?)
+        argv = ['progname', '/foo/bar/baz.py', '/green/red.py']
+        self._patch_isfile(argv)
+
+        program.createTests = lambda: None
+        program.parseArgs(argv)
+
+        self.assertEqual(program.testNames, argv[1:])
+
+        # it may be better to use platform specific functions to normalise paths
+        # rather than accepting '.PY' and '\' as file seprator on Linux / Mac
+        # it would also be better to check that a filename is a valid module
+        # identifier (we have a regex for this in loader.py)
+        # for invalid filenames should we raise a useful error rather than
+        # leaving the current error message (import of filename fails) in place?
+
 
 if __name__ == '__main__':
     unittest.main()

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Sat Dec  4 02:11:21 2010
@@ -37,6 +37,13 @@
 Library
 -------
 
+- Issue 10620: `python -m unittest` can accept file paths instead of module
+  names for running specific tests.
+
+- Issue #9424: Deprecate the `unittest.TestCase` methods `assertEquals`,
+  `assertNotEquals`, `assertAlmostEquals`, `assertNotAlmostEquals` and `assert_`
+  and replace them with the correct methods in the Python test suite.
+
 - Issue #10272: The ssl module now raises socket.timeout instead of a generic
   SSLError on socket timeouts.
 
@@ -236,10 +243,6 @@
 - `python -m test` can be used to run the test suite as well as
   `python -m test.regrtest`.
 
-- Issue #9424: Deprecate the `unittest.TestCase` methods `assertEquals`,
-  `assertNotEquals`, `assertAlmostEquals`, `assertNotAlmostEquals` and `assert_`
-  and replace them with the correct methods in the Python test suite.
-
 - Do not fail test_socket when the IP address of the local hostname cannot be
   looked up.
 


More information about the Python-checkins mailing list