diff options
Diffstat (limited to 'cherrypy/test/test_refleaks.py')
-rwxr-xr-x | cherrypy/test/test_refleaks.py | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/cherrypy/test/test_refleaks.py b/cherrypy/test/test_refleaks.py new file mode 100755 index 0000000..4df1f08 --- /dev/null +++ b/cherrypy/test/test_refleaks.py @@ -0,0 +1,119 @@ +"""Tests for refleaks.""" + +import gc +from cherrypy._cpcompat import HTTPConnection, HTTPSConnection, ntob +import threading + +import cherrypy +from cherrypy import _cprequest + + +data = object() + +def get_instances(cls): + return [x for x in gc.get_objects() if isinstance(x, cls)] + + +from cherrypy.test import helper + + +class ReferenceTests(helper.CPWebCase): + + def setup_server(): + + class Root: + def index(self, *args, **kwargs): + cherrypy.request.thing = data + return "Hello world!" + index.exposed = True + + def gc_stats(self): + output = ["Statistics:"] + + # Uncollectable garbage + + # gc_collect isn't perfectly synchronous, because it may + # break reference cycles that then take time to fully + # finalize. Call it twice and hope for the best. + gc.collect() + unreachable = gc.collect() + if unreachable: + output.append("\n%s unreachable objects:" % unreachable) + trash = {} + for x in gc.garbage: + trash[type(x)] = trash.get(type(x), 0) + 1 + trash = [(v, k) for k, v in trash.items()] + trash.sort() + for pair in trash: + output.append(" " + repr(pair)) + + # Request references + reqs = get_instances(_cprequest.Request) + lenreqs = len(reqs) + if lenreqs < 2: + output.append("\nMissing Request reference. Should be 1 in " + "this request thread and 1 in the main thread.") + elif lenreqs > 2: + output.append("\nToo many Request references (%r)." % lenreqs) + for req in reqs: + output.append("Referrers for %s:" % repr(req)) + for ref in gc.get_referrers(req): + if ref is not reqs: + output.append(" %s" % repr(ref)) + + # Response references + resps = get_instances(_cprequest.Response) + lenresps = len(resps) + if lenresps < 2: + output.append("\nMissing Response reference. Should be 1 in " + "this request thread and 1 in the main thread.") + elif lenresps > 2: + output.append("\nToo many Response references (%r)." % lenresps) + for resp in resps: + output.append("Referrers for %s:" % repr(resp)) + for ref in gc.get_referrers(resp): + if ref is not resps: + output.append(" %s" % repr(ref)) + + return "\n".join(output) + gc_stats.exposed = True + + cherrypy.tree.mount(Root()) + setup_server = staticmethod(setup_server) + + + def test_threadlocal_garbage(self): + success = [] + + def getpage(): + host = '%s:%s' % (self.interface(), self.PORT) + if self.scheme == 'https': + c = HTTPSConnection(host) + else: + c = HTTPConnection(host) + try: + c.putrequest('GET', '/') + c.endheaders() + response = c.getresponse() + body = response.read() + self.assertEqual(response.status, 200) + self.assertEqual(body, ntob("Hello world!")) + finally: + c.close() + success.append(True) + + ITERATIONS = 25 + ts = [] + for _ in range(ITERATIONS): + t = threading.Thread(target=getpage) + ts.append(t) + t.start() + + for t in ts: + t.join() + + self.assertEqual(len(success), ITERATIONS) + + self.getPage("/gc_stats") + self.assertBody("Statistics:") + |