From bece837829eafbc22f2598dadf82c9a8364b085a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nad=C3=A8ge=20Michel?= Date: Tue, 6 Dec 2016 20:38:43 +0100 Subject: [PATCH] Fixed #20238 -- Added threading support to LiveServerTestCase. --- django/core/servers/basehttp.py | 5 +++++ django/test/testcases.py | 4 ++-- docs/releases/2.0.txt | 2 +- tests/servers/tests.py | 16 ++++++++++++++++ tests/servers/urls.py | 3 +++ tests/servers/views.py | 17 +++++++++++++++++ 6 files changed, 44 insertions(+), 3 deletions(-) diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index 68ef2a74b7..5c281b9c24 100644 --- a/django/core/servers/basehttp.py +++ b/django/core/servers/basehttp.py @@ -77,6 +77,11 @@ class WSGIServer(simple_server.WSGIServer): super().handle_error(request, client_address) +class ThreadedWSGIServer(socketserver.ThreadingMixIn, WSGIServer): + """A threaded version of the WSGIServer""" + pass + + class ServerHandler(simple_server.ServerHandler): def handle_error(self): # Ignore broken pipe errors, otherwise pass on diff --git a/django/test/testcases.py b/django/test/testcases.py index 0f879d4222..7955488f48 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -21,7 +21,7 @@ from django.core.handlers.wsgi import WSGIHandler, get_path_info from django.core.management import call_command from django.core.management.color import no_style from django.core.management.sql import emit_post_migrate_signal -from django.core.servers.basehttp import WSGIRequestHandler, WSGIServer +from django.core.servers.basehttp import ThreadedWSGIServer, WSGIRequestHandler from django.db import DEFAULT_DB_ALIAS, connection, connections, transaction from django.forms.fields import CharField from django.http import QueryDict @@ -1248,7 +1248,7 @@ class LiveServerThread(threading.Thread): connections.close_all() def _create_server(self, port): - return WSGIServer((self.host, port), QuietWSGIRequestHandler, allow_reuse_address=False) + return ThreadedWSGIServer((self.host, port), QuietWSGIRequestHandler, allow_reuse_address=False) def terminate(self): if hasattr(self, 'httpd'): diff --git a/docs/releases/2.0.txt b/docs/releases/2.0.txt index 6b775bec80..818a8ada87 100644 --- a/docs/releases/2.0.txt +++ b/docs/releases/2.0.txt @@ -193,7 +193,7 @@ Templates Tests ~~~~~ -* ... +* Added threading support to :class:`~django.test.LiveServerTestCase`. URLs ~~~~ diff --git a/tests/servers/tests.py b/tests/servers/tests.py index 5052f57908..b114b968df 100644 --- a/tests/servers/tests.py +++ b/tests/servers/tests.py @@ -130,3 +130,19 @@ class LiveServerPort(LiveServerBase): finally: if hasattr(TestCase, 'server_thread'): TestCase.server_thread.terminate() + + +class LiverServerThreadedTests(LiveServerBase): + """If LiverServerTestCase isn't threaded, these tests will hang.""" + + def test_view_calls_subview(self): + url = '/subview_calling_view/?%s' % urlencode({'url': self.live_server_url}) + with self.urlopen(url) as f: + self.assertEqual(f.read(), b'subview calling view: subview') + + def test_check_model_instance_from_subview(self): + url = '/check_model_instance_from_subview/?%s' % urlencode({ + 'url': self.live_server_url, + }) + with self.urlopen(url) as f: + self.assertIn(b'emily', f.read()) diff --git a/tests/servers/urls.py b/tests/servers/urls.py index 868ecc34f9..4963bde357 100644 --- a/tests/servers/urls.py +++ b/tests/servers/urls.py @@ -7,4 +7,7 @@ urlpatterns = [ url(r'^model_view/$', views.model_view), url(r'^create_model_instance/$', views.create_model_instance), url(r'^environ_view/$', views.environ_view), + url(r'^subview_calling_view/$', views.subview_calling_view), + url(r'^subview/$', views.subview), + url(r'^check_model_instance_from_subview/$', views.check_model_instance_from_subview), ] diff --git a/tests/servers/views.py b/tests/servers/views.py index f1fb8714c5..3fa99380b1 100644 --- a/tests/servers/views.py +++ b/tests/servers/views.py @@ -1,3 +1,5 @@ +from urllib.request import urlopen + from django.http import HttpResponse from .models import Person @@ -20,3 +22,18 @@ def create_model_instance(request): def environ_view(request): return HttpResponse("\n".join("%s: %r" % (k, v) for k, v in request.environ.items())) + + +def subview(request): + return HttpResponse('subview') + + +def subview_calling_view(request): + response = urlopen(request.GET['url'] + '/subview/') + return HttpResponse('subview calling view: {}'.format(response.read().decode())) + + +def check_model_instance_from_subview(request): + urlopen(request.GET['url'] + '/create_model_instance/') + response = urlopen(request.GET['url'] + '/model_view/') + return HttpResponse('subview calling view: {}'.format(response.read().decode()))