Commit c37a1a90 authored by Jason Madden's avatar Jason Madden

Refactor test__all__ to make it easier to spot errors. PyPy 7.2 on Windows has...

Refactor test__all__ to make it easier to spot errors. PyPy 7.2 on Windows has a failure with it; this won't fix it but is the first step. Make PyPy on windows non-optional so we catch such failures easier.
parent 03874007
......@@ -90,9 +90,9 @@ environment:
# PYTHON_ARCH: "32"
# PYTHON_EXE: python
matrix:
allow_failures:
- PYTHON_ID: "pypy"
# matrix:
# allow_failures:
# - PYTHON_ID: "pypy"
install:
# If there is a newer build queued for the same PR, cancel this one.
......@@ -162,6 +162,7 @@ test_script:
# Run the project tests
- if not "%GWHEEL_ONLY%"=="true" %PYEXE% -c "import gevent.core; print(gevent.core.loop)"
- if not "%GWHEEL_ONLY%"=="true" %PYEXE% -c "import gevent; print(gevent.config.settings['resolver'].get_options())"
- if not "%GWHEEL_ONLY%"=="true" %PYEXE% -c "from gevent._compat import get_clock_info; print(get_clock_info('perf_counter'))"
- if not "%GWHEEL_ONLY%"=="true" %PYEXE% -mgevent.tests --config known_failures.py --quiet
after_test:
......
......@@ -5,6 +5,7 @@ Waiting for I/O completion.
from __future__ import absolute_import, division, print_function
import sys
import select as __select__
from gevent.event import Event
from gevent.hub import _get_hub_noargs as get_hub
......@@ -15,7 +16,7 @@ from gevent._util import copy_globals
from gevent._util import _NONE
from errno import EINTR
from select import select as _real_original_select
_real_original_select = __select__.select
if sys.platform.startswith('win32'):
def _original_select(r, w, x, t):
# windows can't handle three empty lists, but we've always
......@@ -26,21 +27,21 @@ if sys.platform.startswith('win32'):
else:
_original_select = _real_original_select
try:
from select import POLLIN, POLLOUT, POLLNVAL
__implements__ = ['select', 'poll']
except ImportError:
POLLIN = 1
POLLOUT = 4
POLLNVAL = 32
__implements__ = ['select']
# These will be replaced by copy_globals
POLLIN = 1
POLLOUT = 4
POLLNVAL = 32
__implements__ = [
'select',
]
if hasattr(__select__, 'poll'):
__implements__.append('poll')
else:
__extra__ = [
'poll',
]
__all__ = ['error'] + __implements__
if 'poll' not in __all__:
__all__.append('poll')
import select as __select__
error = __select__.error
......
# Check __all__, __implements__, __extensions__, __imports__ of the modules
from __future__ import print_function
from __future__ import absolute_import
import functools
import sys
import unittest
import types
......@@ -55,6 +58,15 @@ NO_ALL = {
ALLOW_IMPLEMENTS = [
'gevent._queue',
# 'gevent.resolver.dnspython',
# 'gevent.resolver_thread',
# 'gevent.resolver.blocking',
# 'gevent.resolver_ares',
# 'gevent.server',
# 'gevent._resolver.hostfile',
# 'gevent.util',
# 'gevent.threadpool',
# 'gevent.timeout',
]
# A list of modules that may contain things that aren't actually, technically,
......@@ -68,76 +80,109 @@ if sys.platform.startswith('win'):
_MISSING = '<marker object>'
def _create_tests(cls):
path = modname = orig_modname = None
for path, modname in modules.walk_modules(include_so=False, recursive=True, check_optional=False):
orig_modname = modname
test_name = 'test_%s' % orig_modname.replace('.', '_')
def skip_if_no_stdlib_counterpart(f):
@functools.wraps(f)
def m(self):
if not self.stdlib_module:
self.skipTest("Need stdlib counterpart to %s" % self.modname)
f(self)
modname = modname.replace('gevent.', '').split('.')[0]
return m
fn = lambda self, n=orig_modname: self._test(n)
class AbstractTestMixin(object):
modname = None
stdlib_has_all = False
stdlib_all = None
stdlib_name = None
stdlib_module = None
if not modname: # pragma: no cover
# With walk_modules, can we even get here?
fn = unittest.skip(
"No such module '%s' at '%s'" % (orig_modname, path))(fn)
@classmethod
def setUpClass(cls):
modname = cls.modname
if modname.endswith(PLATFORM_SPECIFIC_SUFFIXES):
raise unittest.SkipTest("Module %s is platform specific" % modname)
setattr(cls, test_name, fn)
return cls
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
try:
cls.module = importlib.import_module(modname)
except ImportError:
if modname in modules.OPTIONAL_MODULES:
msg = "Unable to import %s" % modname
warnings.warn(msg) # make the testrunner print it
raise unittest.SkipTest(msg)
raise
@_create_tests
class Test(unittest.TestCase):
cls.__implements__ = getattr(cls.module, '__implements__', None)
cls.__imports__ = getattr(cls.module, '__imports__', [])
cls.__extensions__ = getattr(cls.module, '__extensions__', [])
stdlib_has_all = False
stdlib_all = ()
stdlib_name = None
stdlib_module = None
module = None
modname = None
__implements__ = __extensions__ = __imports__ = ()
cls.stdlib_name = MAPPING.get(modname)
def check_all(self):
# Check that __all__ is present and does not contain invalid entries
if cls.stdlib_name is not None:
try:
cls.stdlib_module = __import__(cls.stdlib_name)
except ImportError:
pass
else:
cls.stdlib_has_all = True
cls.stdlib_all = getattr(cls.stdlib_module, '__all__', None)
if cls.stdlib_all is None:
cls.stdlib_has_all = False
cls.stdlib_all = [
name
for name in dir(cls.stdlib_module)
if not name.startswith('_')
and not isinstance(getattr(cls.stdlib_module, name), types.ModuleType)
]
def skipIfNoAll(self):
if not hasattr(self.module, '__all__'):
self.assertIn(self.modname, NO_ALL)
return
self.skipTest("%s Needs __all__" % self.modname)
def test_all(self):
# Check that __all__ is present and does not contain invalid entries
self.skipIfNoAll()
names = {}
six.exec_("from %s import *" % self.modname, names)
names.pop('__builtins__', None)
self.assertEqual(sorted(names), sorted(self.module.__all__))
def check_all_formula(self):
def test_all_formula(self):
self.skipIfNoAll()
# Check __all__ = __implements__ + __extensions__ + __imported__
all_calculated = self.__implements__ + self.__imports__ + self.__extensions__
self.assertEqual(sorted(all_calculated), sorted(self.module.__all__))
def check_implements_presence_justified(self):
# This is disabled because it was previously being skipped entirely
# back when we had to call things manually. In that time, it drifted
# out of sync. It should be enabled again and problems corrected.
all_calculated = (
tuple(self.__implements__ or ())
+ tuple(self.__imports__ or ())
+ tuple(self.__extensions__ or ())
)
try:
self.assertEqual(sorted(all_calculated),
sorted(self.module.__all__))
except AssertionError:
self.skipTest("Module %s fails the all formula; fix it" % self.modname)
def test_implements_presence_justified(self):
# Check that __implements__ is present only if the module is modeled
# after a module from stdlib (like gevent.socket).
if self.modname in ALLOW_IMPLEMENTS:
return
if self.__implements__ is not None and self.stdlib_module is None:
raise AssertionError('%r has __implements__ but no stdlib counterpart (%s)'
% (self.modname, self.stdlib_name))
def set_stdlib_all(self):
self.assertIsNotNone(self.stdlib_module)
self.stdlib_has_all = True
self.stdlib_all = getattr(self.stdlib_module, '__all__', None)
if self.stdlib_all is None:
self.stdlib_has_all = False
self.stdlib_all = dir(self.stdlib_module)
self.stdlib_all = [name for name in self.stdlib_all if not name.startswith('_')]
self.stdlib_all = [name for name in self.stdlib_all if not isinstance(getattr(self.stdlib_module, name), types.ModuleType)]
def check_implements_subset_of_stdlib_all(self):
raise AssertionError(
'%s (%r) has __implements__ (%s) but no stdlib counterpart (%s)'
% (self.modname, self.module, self.__implements__, self.stdlib_name))
@skip_if_no_stdlib_counterpart
def test_implements_subset_of_stdlib_all(self):
# Check that __implements__ + __imports__ is a subset of the
# corresponding standard module __all__ or dir()
for name in self.__implements__ + self.__imports__:
for name in tuple(self.__implements__ or ()) + tuple(self.__imports__):
if name in self.stdlib_all:
continue
if name in COULD_BE_MISSING.get(self.stdlib_name, ()):
......@@ -146,11 +191,12 @@ class Test(unittest.TestCase):
continue
raise AssertionError('%r is not found in %r.__all__ nor in dir(%r)' % (name, self.stdlib_module, self.stdlib_module))
def check_implements_actually_implements(self):
@skip_if_no_stdlib_counterpart
def test_implements_actually_implements(self):
# Check that the module actually implements the entries from
# __implements__
for name in self.__implements__:
for name in self.__implements__ or ():
item = getattr(self.module, name)
try:
stdlib_item = getattr(self.stdlib_module, name)
......@@ -159,7 +205,8 @@ class Test(unittest.TestCase):
if name not in COULD_BE_MISSING.get(self.stdlib_name, []):
raise
def check_imports_actually_imports(self):
@skip_if_no_stdlib_counterpart
def test_imports_actually_imports(self):
# Check that the module actually imports the entries from
# __imports__
for name in self.__imports__:
......@@ -167,7 +214,8 @@ class Test(unittest.TestCase):
stdlib_item = getattr(self.stdlib_module, name)
self.assertIs(item, stdlib_item)
def check_extensions_actually_extend(self):
@skip_if_no_stdlib_counterpart
def test_extensions_actually_extend(self):
# Check that the module actually defines new entries in
# __extensions__
......@@ -177,7 +225,8 @@ class Test(unittest.TestCase):
if hasattr(self.stdlib_module, name):
raise AssertionError("'%r' is not an extension, it is found in %r" % (name, self.stdlib_module))
def check_completeness(self): # pylint:disable=too-many-branches
@skip_if_no_stdlib_counterpart
def test_completeness(self): # pylint:disable=too-many-branches
# Check that __all__ (or dir()) of the corresponsing stdlib is
# a subset of __all__ of this module
......@@ -220,55 +269,27 @@ are missing from %r:
%r''' % (self.stdlib_module, self.module, missed)
raise AssertionError(msg)
def _test(self, modname):
if modname.endswith(PLATFORM_SPECIFIC_SUFFIXES):
return
self.modname = modname
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
try:
self.module = importlib.import_module(modname)
except ImportError:
if modname in modules.OPTIONAL_MODULES:
msg = "Unable to import %s" % modname
warnings.warn(msg) # make the testrunner print it
raise unittest.SkipTest(msg)
raise
self.check_all()
self.__implements__ = getattr(self.module, '__implements__', None)
self.__imports__ = getattr(self.module, '__imports__', [])
self.__extensions__ = getattr(self.module, '__extensions__', [])
self.stdlib_name = MAPPING.get(modname)
self.stdlib_module = None
if self.stdlib_name is not None:
try:
self.stdlib_module = __import__(self.stdlib_name)
except ImportError:
pass
self.check_implements_presence_justified()
if self.stdlib_module is None:
return
# use __all__ as __implements__
if self.__implements__ is None:
self.__implements__ = sorted(self.module.__all__)
self.set_stdlib_all()
self.check_implements_subset_of_stdlib_all()
self.check_implements_actually_implements()
self.check_imports_actually_imports()
self.check_extensions_actually_extend()
self.check_completeness()
def _create_tests():
for _, modname in modules.walk_modules(include_so=False, recursive=True,
check_optional=False):
if modname.endswith(PLATFORM_SPECIFIC_SUFFIXES):
continue
orig_modname = modname
modname_no_period = orig_modname.replace('.', '_')
cls = type(
'Test_' + modname_no_period,
(AbstractTestMixin, unittest.TestCase),
{
'__module__': __name__,
'modname': orig_modname
}
)
globals()[cls.__name__] = cls
_create_tests()
if __name__ == "__main__":
unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment