Commit ecd1bd10 authored by Jason Madden's avatar Jason Madden

Fix an error in leakcheck that called setup and teardown too many times.

parent 236739e4
......@@ -58,6 +58,7 @@ from greentest.sysinfo import CONN_ABORTED_ERRORS
from greentest.skipping import skipOnWindows
from greentest.skipping import skipOnAppVeyor
from greentest.skipping import skipOnCI
from greentest.skipping import skipOnPyPy3OnCI
from greentest.skipping import skipOnPyPy
from greentest.skipping import skipOnPyPy3
......
......@@ -32,43 +32,44 @@ def ignores_leakcheck(func):
func.ignore_leakcheck = True
return func
# Some builtin things that we ignore
IGNORED_TYPES = (tuple, dict, types.FrameType, types.TracebackType)
try:
callback_kind = gevent.core.callback
except AttributeError:
# Must be using FFI.
from gevent._ffi.callback import callback as callback_kind
def _type_hist():
d = collections.defaultdict(int)
for x in gc.get_objects():
k = type(x)
if k in IGNORED_TYPES:
continue
if k == callback_kind and x.callback is None and x.args is None:
# these represent callbacks that have been stopped, but
# the event loop hasn't cycled around to run them. The only
# known cause of this is killing greenlets before they get a chance
# to run for the first time.
continue
d[k] += 1
return d
def _report_diff(a, b):
diff_lines = []
for k, v in sorted(a.items(), key=lambda i: i[0].__name__):
if b[k] != v:
diff_lines.append("%s: %s != %s" % (k, v, b[k]))
if not diff_lines:
return None
diff = '\n'.join(diff_lines)
return diff
def wrap_refcount(method):
if getattr(method, 'ignore_leakcheck', False):
return method
# Some builtin things that we ignore
IGNORED_TYPES = (tuple, dict, types.FrameType, types.TracebackType)
try:
callback_kind = gevent.core.callback
except AttributeError:
# Must be using FFI.
from gevent._ffi.callback import callback as callback_kind
def type_hist():
d = collections.defaultdict(int)
for x in gc.get_objects():
k = type(x)
if k in IGNORED_TYPES:
continue
if k == callback_kind and x.callback is None and x.args is None:
# these represent callbacks that have been stopped, but
# the event loop hasn't cycled around to run them. The only
# known cause of this is killing greenlets before they get a chance
# to run for the first time.
continue
d[k] += 1
return d
def report_diff(a, b):
diff_lines = []
for k, v in sorted(a.items(), key=lambda i: i[0].__name__):
if b[k] != v:
diff_lines.append("%s: %s != %s" % (k, v, b[k]))
if not diff_lines:
return None
diff = '\n'.join(diff_lines)
return diff
@wraps(method)
def wrapper(self, *args, **kwargs): # pylint:disable=too-many-branches
......@@ -78,25 +79,33 @@ def wrap_refcount(method):
deltas = []
d = None
gc.disable()
# The very first time we are called, we have already been
# self.setUp() by the test runner, so we don't need to do it again.
needs_setUp = False
try:
while True:
# Grab current snapshot
hist_before = type_hist()
hist_before = _type_hist()
d = sum(hist_before.values())
self.setUp()
if needs_setUp:
self.setUp()
self.skipTearDown = False
try:
method(self, *args, **kwargs)
finally:
self.tearDown()
self.skipTearDown = True
needs_setUp = True
# Grab post snapshot
if 'urlparse' in sys.modules:
sys.modules['urlparse'].clear_cache()
if 'urllib.parse' in sys.modules:
sys.modules['urllib.parse'].clear_cache()
hist_after = type_hist()
hist_after = _type_hist()
d = sum(hist_after.values()) - d
deltas.append(d)
......@@ -119,7 +128,7 @@ def wrap_refcount(method):
elif len(deltas) >= 4 and sum(deltas[-4:]) == 0:
break
elif len(deltas) >= 3 and deltas[-1] > 0 and deltas[-1] == deltas[-2] and deltas[-2] == deltas[-3]:
diff = report_diff(hist_before, hist_after)
diff = _report_diff(hist_before, hist_after)
raise AssertionError('refcount increased by %r\n%s' % (deltas, diff))
# OK, we don't know for sure yet. Let's search for more
if sum(deltas[-3:]) <= 0 or sum(deltas[-4:]) <= 0 or deltas[-4:].count(0) >= 2:
......@@ -128,9 +137,10 @@ def wrap_refcount(method):
else:
limit = 7
if len(deltas) >= limit:
raise AssertionError('refcount increased by %r\n%s' % (deltas, report_diff(hist_before, hist_after)))
raise AssertionError('refcount increased by %r\n%s'
% (deltas,
_report_diff(hist_before, hist_after)))
finally:
gc.enable()
self.skipTearDown = True
return wrapper
......@@ -23,13 +23,12 @@ import unittest
from greentest import sysinfo
def _identity(f):
return f
def _do_not_skip(reason):
assert reason
def dec(f):
return f
return dec
return _identity
if sysinfo.WIN:
......@@ -50,6 +49,11 @@ if sysinfo.RUNNING_ON_APPVEYOR:
else:
skipOnAppVeyor = _do_not_skip
if sysinfo.RUNNING_ON_CI:
skipOnCI = unittest.skip
else:
skipOnCI = _do_not_skip
if sysinfo.PYPY3 and sysinfo.RUNNING_ON_CI:
# Same as above, for PyPy3.3-5.5-alpha and 3.5-5.7.1-beta and 3.5-5.8
skipOnPyPy3OnCI = unittest.skip
......
......@@ -58,6 +58,7 @@ class _DelayWaitMixin(object):
# otherwise it's the raw number
seconds = getattr(timeout, 'seconds', timeout)
gevent.get_hub().loop.update_now()
start = time.time()
try:
result = self.wait(timeout)
......
......@@ -101,7 +101,7 @@ class Test_wsgiserver_ssl(Test_wsgiserver):
ssl_ctx = ssl._create_unverified_context()
@greentest.skipOnLibuvOnCIOnPyPy("Timing issues sometimes lead to a connection refused")
@greentest.skipOnCI("Timing issues sometimes lead to a connection refused")
class Test_webproxy(Test_wsgiserver):
server = 'webproxy.py'
......
......@@ -16,11 +16,15 @@ class Test(TestCase):
self.loop = config.loop(default=True)
self.timer = self.loop.timer(0.001, repeat=self.repeat)
def tearDown(self):
def cleanup(self):
# cleanup instead of tearDown to cooperate well with
# leakcheck.py
self.timer.close()
# cycle the loop so libuv close callbacks fire
self.loop.run()
self.loop.destroy()
self.timer = None
self.loop = None
self.timer = None
def f(self, x=None):
self.called.append(1)
......
......@@ -9,6 +9,7 @@ import unittest
import greentest
from functools import wraps
from greentest import six
from greentest import LARGE_TIMEOUT
# we use threading on purpose so that we can test both regular and gevent sockets with the same code
from threading import Thread as _Thread
......@@ -42,7 +43,7 @@ class TestTCP(greentest.TestCase):
__timeout__ = None
TIMEOUT_ERROR = socket.timeout
long_data = ", ".join([str(x) for x in range(20000)])
if six.PY3:
if not isinstance(long_data, bytes):
long_data = long_data.encode('ascii')
def setUp(self):
......@@ -341,7 +342,7 @@ def get_port():
class TestCreateConnection(greentest.TestCase):
__timeout__ = 5
__timeout__ = LARGE_TIMEOUT
def test_refuses(self):
with self.assertRaises(socket.error) as cm:
......
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