[Python-checkins] cpython (3.4): Issue #22060: Clean up/simplify test_ctypes, use test discovery

zach.ware python-checkins at python.org
Fri Aug 8 20:35:43 CEST 2014


http://hg.python.org/cpython/rev/fc99cf3615bb
changeset:   92041:fc99cf3615bb
branch:      3.4
parent:      92039:35db84720d8d
user:        Zachary Ware <zachary.ware at gmail.com>
date:        Fri Aug 08 13:32:16 2014 -0500
summary:
  Issue #22060: Clean up/simplify test_ctypes, use test discovery

files:
  Lib/ctypes/test/__init__.py        |  216 +----------------
  Lib/ctypes/test/__main__.py        |    4 +
  Lib/ctypes/test/runtests.py        |   19 -
  Lib/ctypes/test/test_find.py       |   72 ++--
  Lib/ctypes/test/test_loading.py    |   41 +-
  Lib/ctypes/test/test_python_api.py |    5 -
  Lib/ctypes/test/test_win32.py      |    2 -
  Lib/test/test_ctypes.py            |   12 +-
  Misc/NEWS                          |    3 +
  9 files changed, 73 insertions(+), 301 deletions(-)


diff --git a/Lib/ctypes/test/__init__.py b/Lib/ctypes/test/__init__.py
--- a/Lib/ctypes/test/__init__.py
+++ b/Lib/ctypes/test/__init__.py
@@ -1,216 +1,14 @@
-import os, sys, unittest, getopt, time
+import os
+import unittest
+from test import support
 
-use_resources = []
-
-import ctypes
+# skip tests if _ctypes was not built
+ctypes = support.import_module('ctypes')
 ctypes_symbols = dir(ctypes)
 
 def need_symbol(name):
     return unittest.skipUnless(name in ctypes_symbols,
                                '{!r} is required'.format(name))
 
-
-class ResourceDenied(unittest.SkipTest):
-    """Test skipped because it requested a disallowed resource.
-
-    This is raised when a test calls requires() for a resource that
-    has not be enabled.  Resources are defined by test modules.
-    """
-
-def is_resource_enabled(resource):
-    """Test whether a resource is enabled.
-
-    If the caller's module is __main__ then automatically return True."""
-    if sys._getframe().f_back.f_globals.get("__name__") == "__main__":
-        return True
-    result = use_resources is not None and \
-           (resource in use_resources or "*" in use_resources)
-    if not result:
-        _unavail[resource] = None
-    return result
-
-_unavail = {}
-def requires(resource, msg=None):
-    """Raise ResourceDenied if the specified resource is not available.
-
-    If the caller's module is __main__ then automatically return True."""
-    # see if the caller's module is __main__ - if so, treat as if
-    # the resource was set
-    if sys._getframe().f_back.f_globals.get("__name__") == "__main__":
-        return
-    if not is_resource_enabled(resource):
-        if msg is None:
-            msg = "Use of the `%s' resource not enabled" % resource
-        raise ResourceDenied(msg)
-
-def find_package_modules(package, mask):
-    import fnmatch
-    if (package.__loader__ is not None and
-            hasattr(package.__loader__, '_files')):
-        path = package.__name__.replace(".", os.path.sep)
-        mask = os.path.join(path, mask)
-        for fnm in package.__loader__._files.keys():
-            if fnmatch.fnmatchcase(fnm, mask):
-                yield os.path.splitext(fnm)[0].replace(os.path.sep, ".")
-    else:
-        path = package.__path__[0]
-        for fnm in os.listdir(path):
-            if fnmatch.fnmatchcase(fnm, mask):
-                yield "%s.%s" % (package.__name__, os.path.splitext(fnm)[0])
-
-def get_tests(package, mask, verbosity, exclude=()):
-    """Return a list of skipped test modules, and a list of test cases."""
-    tests = []
-    skipped = []
-    for modname in find_package_modules(package, mask):
-        if modname.split(".")[-1] in exclude:
-            skipped.append(modname)
-            if verbosity > 1:
-                print("Skipped %s: excluded" % modname, file=sys.stderr)
-            continue
-        try:
-            mod = __import__(modname, globals(), locals(), ['*'])
-        except (ResourceDenied, unittest.SkipTest) as detail:
-            skipped.append(modname)
-            if verbosity > 1:
-                print("Skipped %s: %s" % (modname, detail), file=sys.stderr)
-            continue
-        for name in dir(mod):
-            if name.startswith("_"):
-                continue
-            o = getattr(mod, name)
-            if type(o) is type(unittest.TestCase) and issubclass(o, unittest.TestCase):
-                tests.append(o)
-    return skipped, tests
-
-def usage():
-    print(__doc__)
-    return 1
-
-def test_with_refcounts(runner, verbosity, testcase):
-    """Run testcase several times, tracking reference counts."""
-    import gc
-    import ctypes
-    ptc = ctypes._pointer_type_cache.copy()
-    cfc = ctypes._c_functype_cache.copy()
-    wfc = ctypes._win_functype_cache.copy()
-
-    # when searching for refcount leaks, we have to manually reset any
-    # caches that ctypes has.
-    def cleanup():
-        ctypes._pointer_type_cache = ptc.copy()
-        ctypes._c_functype_cache = cfc.copy()
-        ctypes._win_functype_cache = wfc.copy()
-        gc.collect()
-
-    test = unittest.makeSuite(testcase)
-    for i in range(5):
-        rc = sys.gettotalrefcount()
-        runner.run(test)
-        cleanup()
-    COUNT = 5
-    refcounts = [None] * COUNT
-    for i in range(COUNT):
-        rc = sys.gettotalrefcount()
-        runner.run(test)
-        cleanup()
-        refcounts[i] = sys.gettotalrefcount() - rc
-    if filter(None, refcounts):
-        print("%s leaks:\n\t" % testcase, refcounts)
-    elif verbosity:
-        print("%s: ok." % testcase)
-
-class TestRunner(unittest.TextTestRunner):
-    def run(self, test, skipped):
-        "Run the given test case or test suite."
-        # Same as unittest.TextTestRunner.run, except that it reports
-        # skipped tests.
-        result = self._makeResult()
-        startTime = time.time()
-        test(result)
-        stopTime = time.time()
-        timeTaken = stopTime - startTime
-        result.printErrors()
-        self.stream.writeln(result.separator2)
-        run = result.testsRun
-        if _unavail: #skipped:
-            requested = list(_unavail.keys())
-            requested.sort()
-            self.stream.writeln("Ran %d test%s in %.3fs (%s module%s skipped)" %
-                                (run, run != 1 and "s" or "", timeTaken,
-                                 len(skipped),
-                                 len(skipped) != 1 and "s" or ""))
-            self.stream.writeln("Unavailable resources: %s" % ", ".join(requested))
-        else:
-            self.stream.writeln("Ran %d test%s in %.3fs" %
-                                (run, run != 1 and "s" or "", timeTaken))
-        self.stream.writeln()
-        if not result.wasSuccessful():
-            self.stream.write("FAILED (")
-            failed, errored = map(len, (result.failures, result.errors))
-            if failed:
-                self.stream.write("failures=%d" % failed)
-            if errored:
-                if failed: self.stream.write(", ")
-                self.stream.write("errors=%d" % errored)
-            self.stream.writeln(")")
-        else:
-            self.stream.writeln("OK")
-        return result
-
-
-def main(*packages):
-    try:
-        opts, args = getopt.getopt(sys.argv[1:], "rqvu:x:")
-    except getopt.error:
-        return usage()
-
-    verbosity = 1
-    search_leaks = False
-    exclude = []
-    for flag, value in opts:
-        if flag == "-q":
-            verbosity -= 1
-        elif flag == "-v":
-            verbosity += 1
-        elif flag == "-r":
-            try:
-                sys.gettotalrefcount
-            except AttributeError:
-                print("-r flag requires Python debug build", file=sys.stderr)
-                return -1
-            search_leaks = True
-        elif flag == "-u":
-            use_resources.extend(value.split(","))
-        elif flag == "-x":
-            exclude.extend(value.split(","))
-
-    mask = "test_*.py"
-    if args:
-        mask = args[0]
-
-    for package in packages:
-        run_tests(package, mask, verbosity, search_leaks, exclude)
-
-
-def run_tests(package, mask, verbosity, search_leaks, exclude):
-    skipped, testcases = get_tests(package, mask, verbosity, exclude)
-    runner = TestRunner(verbosity=verbosity)
-
-    suites = [unittest.makeSuite(o) for o in testcases]
-    suite = unittest.TestSuite(suites)
-    result = runner.run(suite, skipped)
-
-    if search_leaks:
-        # hunt for refcount leaks
-        runner = BasicTestRunner()
-        for t in testcases:
-            test_with_refcounts(runner, verbosity, t)
-
-    return bool(result.errors)
-
-class BasicTestRunner:
-    def run(self, test):
-        result = unittest.TestResult()
-        test(result)
-        return result
+def load_tests(*args):
+    return support.load_package_tests(os.path.dirname(__file__), *args)
diff --git a/Lib/ctypes/test/__main__.py b/Lib/ctypes/test/__main__.py
new file mode 100644
--- /dev/null
+++ b/Lib/ctypes/test/__main__.py
@@ -0,0 +1,4 @@
+from ctypes.test import load_tests
+import unittest
+
+unittest.main()
diff --git a/Lib/ctypes/test/runtests.py b/Lib/ctypes/test/runtests.py
deleted file mode 100644
--- a/Lib/ctypes/test/runtests.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""Usage: runtests.py [-q] [-r] [-v] [-u resources] [mask]
-
-Run all tests found in this directory, and print a summary of the results.
-Command line flags:
-  -q     quiet mode: don't print anything while the tests are running
-  -r     run tests repeatedly, look for refcount leaks
-  -u<resources>
-         Add resources to the lits of allowed resources. '*' allows all
-         resources.
-  -v     verbose mode: print the test currently executed
-  -x<test1[,test2...]>
-         Exclude specified tests.
-  mask   mask to select filenames containing testcases, wildcards allowed
-"""
-import sys
-import ctypes.test
-
-if __name__ == "__main__":
-    sys.exit(ctypes.test.main(ctypes.test))
diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py
--- a/Lib/ctypes/test/test_find.py
+++ b/Lib/ctypes/test/test_find.py
@@ -1,60 +1,58 @@
 import unittest
 import os
 import sys
+import test.support
 from ctypes import *
 from ctypes.util import find_library
-from ctypes.test import is_resource_enabled
-
-if sys.platform == "win32":
-    lib_gl = find_library("OpenGL32")
-    lib_glu = find_library("Glu32")
-    lib_gle = None
-elif sys.platform == "darwin":
-    lib_gl = lib_glu = find_library("OpenGL")
-    lib_gle = None
-else:
-    lib_gl = find_library("GL")
-    lib_glu = find_library("GLU")
-    lib_gle = find_library("gle")
-
-## print, for debugging
-if is_resource_enabled("printing"):
-    if lib_gl or lib_glu or lib_gle:
-        print("OpenGL libraries:")
-        for item in (("GL", lib_gl),
-                     ("GLU", lib_glu),
-                     ("gle", lib_gle)):
-            print("\t", item)
-
 
 # On some systems, loading the OpenGL libraries needs the RTLD_GLOBAL mode.
 class Test_OpenGL_libs(unittest.TestCase):
-    def setUp(self):
-        self.gl = self.glu = self.gle = None
+    @classmethod
+    def setUpClass(cls):
+        lib_gl = lib_glu = lib_gle = None
+        if sys.platform == "win32":
+            lib_gl = find_library("OpenGL32")
+            lib_glu = find_library("Glu32")
+        elif sys.platform == "darwin":
+            lib_gl = lib_glu = find_library("OpenGL")
+        else:
+            lib_gl = find_library("GL")
+            lib_glu = find_library("GLU")
+            lib_gle = find_library("gle")
+
+        ## print, for debugging
+        if test.support.verbose:
+            print("OpenGL libraries:")
+            for item in (("GL", lib_gl),
+                         ("GLU", lib_glu),
+                         ("gle", lib_gle)):
+                print("\t", item)
+
+        cls.gl = cls.glu = cls.gle = None
         if lib_gl:
-            self.gl = CDLL(lib_gl, mode=RTLD_GLOBAL)
+            cls.gl = CDLL(lib_gl, mode=RTLD_GLOBAL)
         if lib_glu:
-            self.glu = CDLL(lib_glu, RTLD_GLOBAL)
+            cls.glu = CDLL(lib_glu, RTLD_GLOBAL)
         if lib_gle:
             try:
-                self.gle = CDLL(lib_gle)
+                cls.gle = CDLL(lib_gle)
             except OSError:
                 pass
 
-    @unittest.skipUnless(lib_gl, 'lib_gl not available')
     def test_gl(self):
-        if self.gl:
-            self.gl.glClearIndex
+        if self.gl is None:
+            self.skipTest('lib_gl not available')
+        self.gl.glClearIndex
 
-    @unittest.skipUnless(lib_glu, 'lib_glu not available')
     def test_glu(self):
-        if self.glu:
-            self.glu.gluBeginCurve
+        if self.glu is None:
+            self.skipTest('lib_glu not available')
+        self.glu.gluBeginCurve
 
-    @unittest.skipUnless(lib_gle, 'lib_gle not available')
     def test_gle(self):
-        if self.gle:
-            self.gle.gleGetJoinStyle
+        if self.gle is None:
+            self.skipTest('lib_gle not available')
+        self.gle.gleGetJoinStyle
 
 # On platforms where the default shared library suffix is '.so',
 # at least some libraries can be loaded as attributes of the cdll
diff --git a/Lib/ctypes/test/test_loading.py b/Lib/ctypes/test/test_loading.py
--- a/Lib/ctypes/test/test_loading.py
+++ b/Lib/ctypes/test/test_loading.py
@@ -1,37 +1,42 @@
 from ctypes import *
-import sys, unittest
 import os
+import sys
+import unittest
+import test.support
 from ctypes.util import find_library
-from ctypes.test import is_resource_enabled
 
 libc_name = None
-if os.name == "nt":
-    libc_name = find_library("c")
-elif os.name == "ce":
-    libc_name = "coredll"
-elif sys.platform == "cygwin":
-    libc_name = "cygwin1.dll"
-else:
-    libc_name = find_library("c")
 
-if is_resource_enabled("printing"):
-    print("libc_name is", libc_name)
+def setUpModule():
+    global libc_name
+    if os.name == "nt":
+        libc_name = find_library("c")
+    elif os.name == "ce":
+        libc_name = "coredll"
+    elif sys.platform == "cygwin":
+        libc_name = "cygwin1.dll"
+    else:
+        libc_name = find_library("c")
+
+    if test.support.verbose:
+        print("libc_name is", libc_name)
 
 class LoaderTest(unittest.TestCase):
 
     unknowndll = "xxrandomnamexx"
 
-    @unittest.skipUnless(libc_name is not None, 'could not find libc')
     def test_load(self):
+        if libc_name is None:
+            self.skipTest('could not find libc')
         CDLL(libc_name)
         CDLL(os.path.basename(libc_name))
         self.assertRaises(OSError, CDLL, self.unknowndll)
 
-    @unittest.skipUnless(libc_name is not None, 'could not find libc')
-    @unittest.skipUnless(libc_name is not None and
-                         os.path.basename(libc_name) == "libc.so.6",
-                         'wrong libc path for test')
     def test_load_version(self):
+        if libc_name is None:
+            self.skipTest('could not find libc')
+        if os.path.basename(libc_name) != 'libc.so.6':
+            self.skipTest('wrong libc path for test')
         cdll.LoadLibrary("libc.so.6")
         # linux uses version, libc 9 should not exist
         self.assertRaises(OSError, cdll.LoadLibrary, "libc.so.9")
@@ -48,7 +53,7 @@
                          'test specific to Windows (NT/CE)')
     def test_load_library(self):
         self.assertIsNotNone(libc_name)
-        if is_resource_enabled("printing"):
+        if test.support.verbose:
             print(find_library("kernel32"))
             print(find_library("user32"))
 
diff --git a/Lib/ctypes/test/test_python_api.py b/Lib/ctypes/test/test_python_api.py
--- a/Lib/ctypes/test/test_python_api.py
+++ b/Lib/ctypes/test/test_python_api.py
@@ -1,7 +1,6 @@
 from ctypes import *
 import unittest, sys
 from test import support
-from ctypes.test import requires
 
 ################################################################
 # This section should be moved into ctypes\__init__.py, when it's ready.
@@ -39,12 +38,8 @@
         del pyob
         self.assertEqual(grc(s), refcnt)
 
-    # This test is unreliable, because it is possible that code in
-    # unittest changes the refcount of the '42' integer.  So, it
-    # is disabled by default.
     @support.refcount_test
     def test_PyLong_Long(self):
-        requires("refcount")
         ref42 = grc(42)
         pythonapi.PyLong_FromLong.restype = py_object
         self.assertEqual(pythonapi.PyLong_FromLong(42), 42)
diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py
--- a/Lib/ctypes/test/test_win32.py
+++ b/Lib/ctypes/test/test_win32.py
@@ -1,7 +1,6 @@
 # Windows specific tests
 
 from ctypes import *
-from ctypes.test import requires
 import unittest, sys
 from test import support
 
@@ -42,7 +41,6 @@
     @unittest.skipIf(sys.executable.endswith('_d.exe'),
                      "SEH not enabled in debug builds")
     def test_SEH(self):
-        requires("SEH")
         # Call functions with invalid arguments, and make sure
         # that access violations are trapped and raise an
         # exception.
diff --git a/Lib/test/test_ctypes.py b/Lib/test/test_ctypes.py
--- a/Lib/test/test_ctypes.py
+++ b/Lib/test/test_ctypes.py
@@ -1,16 +1,6 @@
 import unittest
 
-from test.support import import_module
-
-# Skip tests if _ctypes module was not built.
-import_module('_ctypes')
-
-import ctypes.test
-
-def load_tests(*args):
-    skipped, testcases = ctypes.test.get_tests(ctypes.test, "test_*.py", verbosity=0)
-    suites = [unittest.makeSuite(t) for t in testcases]
-    return unittest.TestSuite(suites)
+from ctypes.test import load_tests
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -243,6 +243,9 @@
 Tests
 -----
 
+- Issue #22060: test_ctypes has been somewhat cleaned up and simplified; it
+  now uses unittest test discovery to find its tests.
+
 - Issue #22104: regrtest.py no longer holds a reference to the suite of tests
   loaded from test modules that don't define test_main().
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list