mirror of https://github.com/python/cpython
Closes #22060: Merge with 3.4
This commit is contained in:
commit
d630e0070f
|
@ -1,216 +1,14 @@
|
||||||
import os, sys, unittest, getopt, time
|
import os
|
||||||
|
import unittest
|
||||||
|
from test import support
|
||||||
|
|
||||||
use_resources = []
|
# skip tests if _ctypes was not built
|
||||||
|
ctypes = support.import_module('ctypes')
|
||||||
import ctypes
|
|
||||||
ctypes_symbols = dir(ctypes)
|
ctypes_symbols = dir(ctypes)
|
||||||
|
|
||||||
def need_symbol(name):
|
def need_symbol(name):
|
||||||
return unittest.skipUnless(name in ctypes_symbols,
|
return unittest.skipUnless(name in ctypes_symbols,
|
||||||
'{!r} is required'.format(name))
|
'{!r} is required'.format(name))
|
||||||
|
|
||||||
|
def load_tests(*args):
|
||||||
class ResourceDenied(unittest.SkipTest):
|
return support.load_package_tests(os.path.dirname(__file__), *args)
|
||||||
"""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
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
from ctypes.test import load_tests
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
unittest.main()
|
|
@ -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))
|
|
|
@ -1,60 +1,58 @@
|
||||||
import unittest
|
import unittest
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import test.support
|
||||||
from ctypes import *
|
from ctypes import *
|
||||||
from ctypes.util import find_library
|
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.
|
# On some systems, loading the OpenGL libraries needs the RTLD_GLOBAL mode.
|
||||||
class Test_OpenGL_libs(unittest.TestCase):
|
class Test_OpenGL_libs(unittest.TestCase):
|
||||||
def setUp(self):
|
@classmethod
|
||||||
self.gl = self.glu = self.gle = None
|
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:
|
if lib_gl:
|
||||||
self.gl = CDLL(lib_gl, mode=RTLD_GLOBAL)
|
cls.gl = CDLL(lib_gl, mode=RTLD_GLOBAL)
|
||||||
if lib_glu:
|
if lib_glu:
|
||||||
self.glu = CDLL(lib_glu, RTLD_GLOBAL)
|
cls.glu = CDLL(lib_glu, RTLD_GLOBAL)
|
||||||
if lib_gle:
|
if lib_gle:
|
||||||
try:
|
try:
|
||||||
self.gle = CDLL(lib_gle)
|
cls.gle = CDLL(lib_gle)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@unittest.skipUnless(lib_gl, 'lib_gl not available')
|
|
||||||
def test_gl(self):
|
def test_gl(self):
|
||||||
if self.gl:
|
if self.gl is None:
|
||||||
self.gl.glClearIndex
|
self.skipTest('lib_gl not available')
|
||||||
|
self.gl.glClearIndex
|
||||||
|
|
||||||
@unittest.skipUnless(lib_glu, 'lib_glu not available')
|
|
||||||
def test_glu(self):
|
def test_glu(self):
|
||||||
if self.glu:
|
if self.glu is None:
|
||||||
self.glu.gluBeginCurve
|
self.skipTest('lib_glu not available')
|
||||||
|
self.glu.gluBeginCurve
|
||||||
|
|
||||||
@unittest.skipUnless(lib_gle, 'lib_gle not available')
|
|
||||||
def test_gle(self):
|
def test_gle(self):
|
||||||
if self.gle:
|
if self.gle is None:
|
||||||
self.gle.gleGetJoinStyle
|
self.skipTest('lib_gle not available')
|
||||||
|
self.gle.gleGetJoinStyle
|
||||||
|
|
||||||
# On platforms where the default shared library suffix is '.so',
|
# On platforms where the default shared library suffix is '.so',
|
||||||
# at least some libraries can be loaded as attributes of the cdll
|
# at least some libraries can be loaded as attributes of the cdll
|
||||||
|
|
|
@ -1,37 +1,42 @@
|
||||||
from ctypes import *
|
from ctypes import *
|
||||||
import sys, unittest
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
import test.support
|
||||||
from ctypes.util import find_library
|
from ctypes.util import find_library
|
||||||
from ctypes.test import is_resource_enabled
|
|
||||||
|
|
||||||
libc_name = None
|
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"):
|
def setUpModule():
|
||||||
print("libc_name is", libc_name)
|
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):
|
class LoaderTest(unittest.TestCase):
|
||||||
|
|
||||||
unknowndll = "xxrandomnamexx"
|
unknowndll = "xxrandomnamexx"
|
||||||
|
|
||||||
@unittest.skipUnless(libc_name is not None, 'could not find libc')
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
|
if libc_name is None:
|
||||||
|
self.skipTest('could not find libc')
|
||||||
CDLL(libc_name)
|
CDLL(libc_name)
|
||||||
CDLL(os.path.basename(libc_name))
|
CDLL(os.path.basename(libc_name))
|
||||||
self.assertRaises(OSError, CDLL, self.unknowndll)
|
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):
|
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")
|
cdll.LoadLibrary("libc.so.6")
|
||||||
# linux uses version, libc 9 should not exist
|
# linux uses version, libc 9 should not exist
|
||||||
self.assertRaises(OSError, cdll.LoadLibrary, "libc.so.9")
|
self.assertRaises(OSError, cdll.LoadLibrary, "libc.so.9")
|
||||||
|
@ -48,7 +53,7 @@ class LoaderTest(unittest.TestCase):
|
||||||
'test specific to Windows (NT/CE)')
|
'test specific to Windows (NT/CE)')
|
||||||
def test_load_library(self):
|
def test_load_library(self):
|
||||||
self.assertIsNotNone(libc_name)
|
self.assertIsNotNone(libc_name)
|
||||||
if is_resource_enabled("printing"):
|
if test.support.verbose:
|
||||||
print(find_library("kernel32"))
|
print(find_library("kernel32"))
|
||||||
print(find_library("user32"))
|
print(find_library("user32"))
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from ctypes import *
|
from ctypes import *
|
||||||
import unittest, sys
|
import unittest, sys
|
||||||
from test import support
|
from test import support
|
||||||
from ctypes.test import requires
|
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# This section should be moved into ctypes\__init__.py, when it's ready.
|
# This section should be moved into ctypes\__init__.py, when it's ready.
|
||||||
|
@ -39,12 +38,8 @@ class PythonAPITestCase(unittest.TestCase):
|
||||||
del pyob
|
del pyob
|
||||||
self.assertEqual(grc(s), refcnt)
|
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
|
@support.refcount_test
|
||||||
def test_PyLong_Long(self):
|
def test_PyLong_Long(self):
|
||||||
requires("refcount")
|
|
||||||
ref42 = grc(42)
|
ref42 = grc(42)
|
||||||
pythonapi.PyLong_FromLong.restype = py_object
|
pythonapi.PyLong_FromLong.restype = py_object
|
||||||
self.assertEqual(pythonapi.PyLong_FromLong(42), 42)
|
self.assertEqual(pythonapi.PyLong_FromLong(42), 42)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# Windows specific tests
|
# Windows specific tests
|
||||||
|
|
||||||
from ctypes import *
|
from ctypes import *
|
||||||
from ctypes.test import requires
|
|
||||||
import unittest, sys
|
import unittest, sys
|
||||||
from test import support
|
from test import support
|
||||||
|
|
||||||
|
@ -42,7 +41,6 @@ class FunctionCallTestCase(unittest.TestCase):
|
||||||
@unittest.skipIf(sys.executable.endswith('_d.exe'),
|
@unittest.skipIf(sys.executable.endswith('_d.exe'),
|
||||||
"SEH not enabled in debug builds")
|
"SEH not enabled in debug builds")
|
||||||
def test_SEH(self):
|
def test_SEH(self):
|
||||||
requires("SEH")
|
|
||||||
# Call functions with invalid arguments, and make sure
|
# Call functions with invalid arguments, and make sure
|
||||||
# that access violations are trapped and raise an
|
# that access violations are trapped and raise an
|
||||||
# exception.
|
# exception.
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from test.support import import_module
|
from ctypes.test import load_tests
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -819,6 +819,9 @@ Documentation
|
||||||
Tests
|
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
|
- Issue #22104: regrtest.py no longer holds a reference to the suite of tests
|
||||||
loaded from test modules that don't define test_main().
|
loaded from test modules that don't define test_main().
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue