Ticket #2879: django_live_server.diff

File django_live_server.diff, 8.0 KB (added by devin, 7 years ago)

fixes way server handles error on startup

  • django/test/testcases.py

     
     1import os
     2import sys
    13import re
     4import socket
     5import threading
    26import unittest
    37from urlparse import urlsplit, urlunsplit
    48
    59from django.http import QueryDict
    610from django.db import transaction
    711from django.core import mail
     12from django.core.handlers.wsgi import WSGIHandler
    813from django.core.management import call_command
     14from django.core.management.color import color_style
     15from django.core.servers.basehttp import WSGIServer, WSGIServerException, \
     16    AdminMediaHandler, WSGIRequestHandler
    917from django.test import _doctest as doctest
    1018from django.test.client import Client
     19from django.test.utils import create_test_db
    1120
    1221normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
    1322
     
    4756        # side effects on other tests.
    4857        transaction.rollback_unless_managed()
    4958
     59class StoppableWSGIServer(WSGIServer):
     60    """ WSGIServer with short timout, so that server thread can stop this server. """
     61     
     62    def server_bind(self):
     63        """ Sets timeout to 1 second. """
     64        WSGIServer.server_bind(self)
     65        self.socket.settimeout(1)
     66   
     67    def get_request(self):
     68        """ Checks for timeout when getting request. """
     69        try:
     70            sock, address = self.socket.accept()
     71            sock.settimeout(None)
     72            return (sock, address)
     73        except socket.timeout:
     74            raise
     75
     76class TestServerThread(threading.Thread):
     77    """ Thread for running a http server while tests are running. """
     78    def __init__(self, address, port):
     79        self.address = address
     80        self.port = port
     81        self._stopevent = threading.Event()
     82        self.started = threading.Event()
     83        self.error = None
     84        super(TestServerThread, self).__init__()
     85
     86    def run(self):
     87        """ Sets up test server and database and loops over handling http requests. """
     88        try:
     89            handler = AdminMediaHandler(WSGIHandler())
     90            server_address = (self.address, self.port)
     91            httpd = StoppableWSGIServer(server_address, WSGIRequestHandler)
     92            httpd.set_app(handler)
     93            self.started.set()
     94        except WSGIServerException, e:
     95            # Use helpful error messages instead of ugly tracebacks.
     96            self.error = e
     97            self.started.set()
     98            return
     99       
     100        # Must do database stuff in this new thread if database in memory.
     101        from django.conf import settings
     102        if settings.DATABASE_ENGINE == "sqlite3" \
     103            and (not settings.TEST_DATABASE_NAME or settings.TEST_DATABASE_NAME == ":memory:"):
     104            db_name = create_test_db(0)
     105            # Import the fixture data into the test database.
     106            if hasattr(self, 'fixtures'):
     107                # We have to use this slightly awkward syntax due to the fact
     108                # that we're using *args and **kwargs together.
     109                call_command('loaddata', *self.fixtures, **{'verbosity': 0})
     110           
     111        # Loop until we get a stop event
     112        while not self._stopevent.isSet():
     113            httpd.handle_request()
     114       
     115    def join(self, timeout=None):
     116        """ Stop the thread and wait for it to end. """
     117        self._stopevent.set()
     118        threading.Thread.join(self, timeout)
     119
    50120class TestCase(unittest.TestCase):
    51121    def _pre_setup(self):
    52122        """Performs any pre-test setup. This includes:
     
    79149            result.addError(self, sys.exc_info())
    80150            return
    81151        super(TestCase, self).__call__(result)
     152   
     153    def start_test_server(self, address='localhost', port=8000):
     154        """
     155        Creates a live test server object (instance of WSGIServer).
     156        """
     157        self.server_thread = TestServerThread(address, port)
     158        self.server_thread.start()
     159        self.server_thread.started.wait()
     160        if self.server_thread.error:
     161            raise self.server_thread.error
     162   
     163    def stop_test_server(self):
     164        if self.server_thread:
     165            self.server_thread.join()
    82166
    83167    def assertRedirects(self, response, expected_url, status_code=302,
    84168                        target_status_code=200, host=None):
  • tests/regressiontests/test_client_regress/models.py

     
    66from django.core.urlresolvers import reverse
    77from django.core.exceptions import SuspiciousOperation
    88import os
     9import urllib
    910
    1011class AssertContainsTests(TestCase):
    1112    def test_contains(self):
     
    318319            self.client.get("/test_client_regress/staff_only/")
    319320        except SuspiciousOperation:
    320321            self.fail("Staff should be able to visit this page")
     322
     323class TestServerTests(TestCase):
     324    def setUp(self):
     325        self.start_test_server(address='localhost', port=8000)
     326
     327    def tearDown(self):
     328        self.stop_test_server()
     329
     330    def test_server_up(self):
     331        url = urllib.urlopen('http://localhost:8000/')
     332        self.assertEqual(url.read(), 'Django Internal Tests: 404 Error')
     333        url.close()
     334
     335    def test_serve_page(self):
     336        url = urllib.urlopen('http://localhost:8000/accounts/login/')
     337        # Just make sure this isn't a 404, and we've gotten something.
     338        self.assertNotEqual(url.read(), 'Django Internal Tests: 404 Error')
     339        url.close()
  • AUTHORS

     
    269269    Robert Myers <myer0052@gmail.com>
    270270    Nebojša Dorđević
    271271    Doug Napoleone <doug@dougma.com>
     272    Devin Naquin <devin@disqus.com>
    272273    Gopal Narayanan <gopastro@gmail.com>
    273274    Fraser Nevett <mail@nevett.org>
    274275    Sam Newman <http://www.magpiebrain.com/>
  • docs/testing.txt

     
    797797.. _dumpdata documentation: ../django-admin/#dumpdata-appname-appname
    798798.. _loaddata documentation: ../django-admin/#loaddata-fixture-fixture
    799799
     800Running tests with a live test server
     801~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     802
     803**New in Django development version**
     804
     805When running tests that use in-browser frameworks such as Twill_ and
     806Selenium_, it's necessary to have a running test server. Django's custom ``TestCase`` class supports starting a live server for these purposes.
     807
     808``start_test_server(address='localhost', port=8000)``
     809    Starts a test server at ``address`` on ``port``. This should be done in the
     810        ``setUp()`` method of a subclass of ``TestCase``. The server then can be accessed
     811        at http://address:port.
     812
     813``stop_test_server()``
     814        Stops the test server that was started with ``start_test_server``. This must be
     815        done before ``start_test_server`` can be called again, so this should be done in
     816        the ``tearDown()`` method of a subclass of ``TestCase``.
     817
     818This can be used to start a server that can then be accessed by Twill, Selenium or another in-browser test framework. For example::
     819
     820        from django.test.testcases import TestCase
     821        from selenium import selenium
     822       
     823        class TestLogin(TestCase):
     824                fixtures = ['login']
     825               
     826                def setUp(self):
     827                        # start test server and tell selenium where to find it
     828                        self.start_test_server('localhost', 8000)
     829                        self.selenium = selenium(addr, '4444', \
     830                                '*pifirefox', 'http://localhost:8000')
     831
     832                def tearDown(self):
     833                        # stop server and selenium
     834                        self.selenium.stop()
     835                        self.stop_test_server()
     836
     837                def testLogin(self):
     838                        self.selenium.open('/admin/')
     839                        self.selenium.type('username','admin')
     840                        self.selenium.type('password','password')
     841                        self.selenium.click("//input[@value='Log in']")
     842
     843.. _Twill: http://twill.idyll.org/
     844.. _Selenium: http://www.openqa.org/selenium/
     845
    800846Emptying the test outbox
    801847~~~~~~~~~~~~~~~~~~~~~~~~
    802848
Back to Top