diff options
| author | Julien Phalip <jphalip@gmail.com> | 2011-12-22 08:33:58 +0000 |
|---|---|---|
| committer | Julien Phalip <jphalip@gmail.com> | 2011-12-22 08:33:58 +0000 |
| commit | 2f02a05ffb45be68b4164b4785ff1826833150a3 (patch) | |
| tree | d51f7454aeb97a5c35b3045d5d5413691aaf1d00 /tests | |
| parent | 45e3dff5ac697f16829697bc2a899eaeac8986ea (diff) | |
Fixed #2879 -- Added support for the integration with Selenium and other in-browser testing frameworks. Also added the first Selenium tests for `contrib.admin`. Many thanks to everyone for their contributions and feedback: Mikeal Rogers, Dirk Datzert, mir, Simon G., Almad, Russell Keith-Magee, Denis Golomazov, devin, robertrv, andrewbadr, Idan Gazit, voidspace, Tom Christie, hjwp2, Adam Nelson, Jannis Leidel, Anssi Kääriäinen, Preston Holmes, Bruno Renié and Jacob Kaplan-Moss.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17241 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/regressiontests/admin_inlines/admin.py | 5 | ||||
| -rw-r--r-- | tests/regressiontests/admin_inlines/models.py | 10 | ||||
| -rw-r--r-- | tests/regressiontests/admin_inlines/tests.py | 106 | ||||
| -rw-r--r-- | tests/regressiontests/admin_scripts/tests.py | 45 | ||||
| -rw-r--r-- | tests/regressiontests/admin_widgets/tests.py | 50 | ||||
| -rw-r--r-- | tests/regressiontests/servers/fixtures/testdata.json | 16 | ||||
| -rw-r--r-- | tests/regressiontests/servers/media/example_media_file.txt | 1 | ||||
| -rw-r--r-- | tests/regressiontests/servers/models.py | 5 | ||||
| -rw-r--r-- | tests/regressiontests/servers/static/example_static_file.txt | 1 | ||||
| -rw-r--r-- | tests/regressiontests/servers/tests.py | 151 | ||||
| -rw-r--r-- | tests/regressiontests/servers/urls.py | 12 | ||||
| -rw-r--r-- | tests/regressiontests/servers/views.py | 17 | ||||
| -rwxr-xr-x | tests/runtests.py | 62 |
13 files changed, 461 insertions, 20 deletions
diff --git a/tests/regressiontests/admin_inlines/admin.py b/tests/regressiontests/admin_inlines/admin.py index 4edd361d09..508f30223f 100644 --- a/tests/regressiontests/admin_inlines/admin.py +++ b/tests/regressiontests/admin_inlines/admin.py @@ -109,6 +109,10 @@ class SottoCapoInline(admin.TabularInline): model = SottoCapo +class ProfileInline(admin.TabularInline): + model = Profile + extra = 1 + site.register(TitleCollection, inlines=[TitleInline]) # Test bug #12561 and #12778 # only ModelAdmin media @@ -124,3 +128,4 @@ site.register(Fashionista, inlines=[InlineWeakness]) site.register(Holder4, Holder4Admin) site.register(Author, AuthorAdmin) site.register(CapoFamiglia, inlines=[ConsigliereInline, SottoCapoInline]) +site.register(ProfileCollection, inlines=[ProfileInline])
\ No newline at end of file diff --git a/tests/regressiontests/admin_inlines/models.py b/tests/regressiontests/admin_inlines/models.py index 748280d8ab..f2add00288 100644 --- a/tests/regressiontests/admin_inlines/models.py +++ b/tests/regressiontests/admin_inlines/models.py @@ -136,3 +136,13 @@ class Consigliere(models.Model): class SottoCapo(models.Model): name = models.CharField(max_length=100) capo_famiglia = models.ForeignKey(CapoFamiglia, related_name='+') + +# Other models + +class ProfileCollection(models.Model): + pass + +class Profile(models.Model): + collection = models.ForeignKey(ProfileCollection, blank=True, null=True) + first_name = models.CharField(max_length=100) + last_name = models.CharField(max_length=100)
\ No newline at end of file diff --git a/tests/regressiontests/admin_inlines/tests.py b/tests/regressiontests/admin_inlines/tests.py index c2e3bbc397..7b417d8995 100644 --- a/tests/regressiontests/admin_inlines/tests.py +++ b/tests/regressiontests/admin_inlines/tests.py @@ -1,5 +1,6 @@ from __future__ import absolute_import +from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.contrib.admin.helpers import InlineAdminForm from django.contrib.auth.models import User, Permission from django.contrib.contenttypes.models import ContentType @@ -8,7 +9,8 @@ from django.test import TestCase # local test models from .admin import InnerInline from .models import (Holder, Inner, Holder2, Inner2, Holder3, Inner3, Person, - OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book) + OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book, Profile, + ProfileCollection) class TestInline(TestCase): @@ -380,3 +382,105 @@ class TestInlinePermissions(TestCase): self.assertContains(response, 'value="4" id="id_inner2_set-TOTAL_FORMS"') self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="%i"' % self.inner2_id) self.assertContains(response, 'id="id_inner2_set-0-DELETE"') + +class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase): + webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver' + fixtures = ['admin-views-users.xml'] + urls = "regressiontests.admin_inlines.urls" + + def test_add_inlines(self): + """ + Ensure that the "Add another XXX" link correctly adds items to the + inline form. + """ + self.admin_login(username='super', password='secret') + self.selenium.get('%s%s' % (self.live_server_url, + '/admin/admin_inlines/profilecollection/add/')) + + # Check that there's only one inline to start with and that it has the + # correct ID. + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + '#profile_set-group table tr.dynamic-profile_set')), 1) + self.failUnlessEqual(self.selenium.find_element_by_css_selector( + '.dynamic-profile_set:nth-of-type(1)').get_attribute('id'), + 'profile_set-0') + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-0 input[name=profile_set-0-first_name]')), 1) + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-0 input[name=profile_set-0-last_name]')), 1) + + # Add an inline + self.selenium.find_element_by_link_text('Add another Profile').click() + + # Check that the inline has been added, that it has the right id, and + # that it contains the right fields. + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + '#profile_set-group table tr.dynamic-profile_set')), 2) + self.failUnlessEqual(self.selenium.find_element_by_css_selector( + '.dynamic-profile_set:nth-of-type(2)').get_attribute('id'), 'profile_set-1') + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1 input[name=profile_set-1-first_name]')), 1) + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1 input[name=profile_set-1-last_name]')), 1) + + # Let's add another one to be sure + self.selenium.find_element_by_link_text('Add another Profile').click() + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + '#profile_set-group table tr.dynamic-profile_set')), 3) + self.failUnlessEqual(self.selenium.find_element_by_css_selector( + '.dynamic-profile_set:nth-of-type(3)').get_attribute('id'), 'profile_set-2') + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2 input[name=profile_set-2-first_name]')), 1) + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2 input[name=profile_set-2-last_name]')), 1) + + # Enter some data and click 'Save' + self.selenium.find_element_by_name('profile_set-0-first_name').send_keys('0 first name 1') + self.selenium.find_element_by_name('profile_set-0-last_name').send_keys('0 last name 2') + self.selenium.find_element_by_name('profile_set-1-first_name').send_keys('1 first name 1') + self.selenium.find_element_by_name('profile_set-1-last_name').send_keys('1 last name 2') + self.selenium.find_element_by_name('profile_set-2-first_name').send_keys('2 first name 1') + self.selenium.find_element_by_name('profile_set-2-last_name').send_keys('2 last name 2') + self.selenium.find_element_by_xpath('//input[@value="Save"]').click() + + # Check that the objects have been created in the database + self.assertEqual(ProfileCollection.objects.all().count(), 1) + self.assertEqual(Profile.objects.all().count(), 3) + + def test_delete_inlines(self): + self.admin_login(username='super', password='secret') + self.selenium.get('%s%s' % (self.live_server_url, + '/admin/admin_inlines/profilecollection/add/')) + + # Add a few inlines + self.selenium.find_element_by_link_text('Add another Profile').click() + self.selenium.find_element_by_link_text('Add another Profile').click() + self.selenium.find_element_by_link_text('Add another Profile').click() + self.selenium.find_element_by_link_text('Add another Profile').click() + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + '#profile_set-group table tr.dynamic-profile_set')), 5) + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-0')), 1) + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1')), 1) + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2')), 1) + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-3')), 1) + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-4')), 1) + + # Click on a few delete buttons + self.selenium.find_element_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1 td.delete a').click() + self.selenium.find_element_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2 td.delete a').click() + # Verify that they're gone and that the IDs have been re-sequenced + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + '#profile_set-group table tr.dynamic-profile_set')), 3) + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-0')), 1) + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1')), 1) + self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector( + 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2')), 1) diff --git a/tests/regressiontests/admin_scripts/tests.py b/tests/regressiontests/admin_scripts/tests.py index 11beddb7e0..72d47adce1 100644 --- a/tests/regressiontests/admin_scripts/tests.py +++ b/tests/regressiontests/admin_scripts/tests.py @@ -13,6 +13,7 @@ import re from django import conf, bin, get_version from django.conf import settings +from django.test.simple import DjangoTestSuiteRunner from django.utils import unittest @@ -1058,6 +1059,50 @@ class ManageValidate(AdminScriptTestCase): self.assertOutput(out, '0 errors found') +class CustomTestRunner(DjangoTestSuiteRunner): + + def __init__(self, *args, **kwargs): + assert 'liveserver' not in kwargs + super(CustomTestRunner, self).__init__(*args, **kwargs) + + def run_tests(self, test_labels, extra_tests=None, **kwargs): + pass + +class ManageTestCommand(AdminScriptTestCase): + def setUp(self): + from django.core.management.commands.test import Command as TestCommand + self.cmd = TestCommand() + + def test_liveserver(self): + """ + Ensure that the --liveserver option sets the environment variable + correctly. + Refs #2879. + """ + + # Backup original state + address_predefined = 'DJANGO_LIVE_TEST_SERVER_ADDRESS' in os.environ + old_address = os.environ.get('DJANGO_LIVE_TEST_SERVER_ADDRESS') + + self.cmd.handle(verbosity=0, testrunner='regressiontests.admin_scripts.tests.CustomTestRunner') + + # Original state hasn't changed + self.assertEqual('DJANGO_LIVE_TEST_SERVER_ADDRESS' in os.environ, address_predefined) + self.assertEqual(os.environ.get('DJANGO_LIVE_TEST_SERVER_ADDRESS'), old_address) + + self.cmd.handle(verbosity=0, testrunner='regressiontests.admin_scripts.tests.CustomTestRunner', + liveserver='blah') + + # Variable was correctly set + self.assertEqual(os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'], 'blah') + + # Restore original state + if address_predefined: + os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = old_address + else: + del os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] + + class ManageRunserver(AdminScriptTestCase): def setUp(self): from django.core.management.commands.runserver import BaseRunserverCommand diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py index 37fa7bc8ef..e28df32577 100644 --- a/tests/regressiontests/admin_widgets/tests.py +++ b/tests/regressiontests/admin_widgets/tests.py @@ -7,6 +7,7 @@ from django import forms from django.conf import settings from django.contrib import admin from django.contrib.admin import widgets +from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.core.files.storage import default_storage from django.core.files.uploadedfile import SimpleUploadedFile from django.db.models import DateField @@ -407,3 +408,52 @@ class RelatedFieldWidgetWrapperTests(DjangoTestCase): # Used to fail with a name error. w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site) self.assertFalse(w.can_add_related) + + +class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase): + webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver' + fixtures = ['admin-widgets-users.xml'] + urls = "regressiontests.admin_widgets.urls" + + def test_show_hide_date_time_picker_widgets(self): + """ + Ensure that pressing the ESC key closes the date and time picker + widgets. + Refs #17064. + """ + from selenium.webdriver.common.keys import Keys + + self.admin_login(username='super', password='secret', login_url='/') + # Open a page that has a date and time picker widgets + self.selenium.get('%s%s' % (self.live_server_url, + '/admin_widgets/member/add/')) + + # First, with the date picker widget --------------------------------- + # Check that the date picker is hidden + self.assertEqual( + self.get_css_value('#calendarbox0', 'display'), 'none') + # Click the calendar icon + self.selenium.find_element_by_id('calendarlink0').click() + # Check that the date picker is visible + self.assertEqual( + self.get_css_value('#calendarbox0', 'display'), 'block') + # Press the ESC key + self.selenium.find_element_by_tag_name('html').send_keys([Keys.ESCAPE]) + # Check that the date picker is hidden again + self.assertEqual( + self.get_css_value('#calendarbox0', 'display'), 'none') + + # Then, with the time picker widget ---------------------------------- + # Check that the time picker is hidden + self.assertEqual( + self.get_css_value('#clockbox0', 'display'), 'none') + # Click the time icon + self.selenium.find_element_by_id('clocklink0').click() + # Check that the time picker is visible + self.assertEqual( + self.get_css_value('#clockbox0', 'display'), 'block') + # Press the ESC key + self.selenium.find_element_by_tag_name('html').send_keys([Keys.ESCAPE]) + # Check that the time picker is hidden again + self.assertEqual( + self.get_css_value('#clockbox0', 'display'), 'none') diff --git a/tests/regressiontests/servers/fixtures/testdata.json b/tests/regressiontests/servers/fixtures/testdata.json new file mode 100644 index 0000000000..d81b2253d2 --- /dev/null +++ b/tests/regressiontests/servers/fixtures/testdata.json @@ -0,0 +1,16 @@ +[ + { + "pk": 1, + "model": "servers.person", + "fields": { + "name": "jane" + } + }, + { + "pk": 2, + "model": "servers.person", + "fields": { + "name": "robert" + } + } +]
\ No newline at end of file diff --git a/tests/regressiontests/servers/media/example_media_file.txt b/tests/regressiontests/servers/media/example_media_file.txt new file mode 100644 index 0000000000..dd2dda94e3 --- /dev/null +++ b/tests/regressiontests/servers/media/example_media_file.txt @@ -0,0 +1 @@ +example media file diff --git a/tests/regressiontests/servers/models.py b/tests/regressiontests/servers/models.py index e69de29bb2..6e1414ae19 100644 --- a/tests/regressiontests/servers/models.py +++ b/tests/regressiontests/servers/models.py @@ -0,0 +1,5 @@ +from django.db import models + + +class Person(models.Model): + name = models.CharField(max_length=256) diff --git a/tests/regressiontests/servers/static/example_static_file.txt b/tests/regressiontests/servers/static/example_static_file.txt new file mode 100644 index 0000000000..b5d8342212 --- /dev/null +++ b/tests/regressiontests/servers/static/example_static_file.txt @@ -0,0 +1 @@ +example static file diff --git a/tests/regressiontests/servers/tests.py b/tests/regressiontests/servers/tests.py index b9d24ba571..ff49c74b9d 100644 --- a/tests/regressiontests/servers/tests.py +++ b/tests/regressiontests/servers/tests.py @@ -3,13 +3,17 @@ Tests for django.core.servers. """ import os from urlparse import urljoin +import urllib2 import django from django.conf import settings -from django.test import TestCase +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase, LiveServerTestCase from django.core.handlers.wsgi import WSGIHandler -from django.core.servers.basehttp import AdminMediaHandler +from django.core.servers.basehttp import AdminMediaHandler, WSGIServerException +from django.test.utils import override_settings +from .models import Person class AdminMediaHandlerTests(TestCase): @@ -68,3 +72,146 @@ class AdminMediaHandlerTests(TestCase): continue self.fail('URL: %s should have caused a ValueError exception.' % url) + + +TEST_ROOT = os.path.dirname(__file__) +TEST_SETTINGS = { + 'MEDIA_URL': '/media/', + 'MEDIA_ROOT': os.path.join(TEST_ROOT, 'media'), + 'STATIC_URL': '/static/', + 'STATIC_ROOT': os.path.join(TEST_ROOT, 'static'), +} + + +class LiveServerBase(LiveServerTestCase): + urls = 'regressiontests.servers.urls' + fixtures = ['testdata.json'] + + @classmethod + def setUpClass(cls): + # Override settings + cls.settings_override = override_settings(**TEST_SETTINGS) + cls.settings_override.enable() + super(LiveServerBase, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + # Restore original settings + cls.settings_override.disable() + super(LiveServerBase, cls).tearDownClass() + + def urlopen(self, url): + server_address = os.environ.get( + 'DJANGO_LIVE_TEST_SERVER_ADDRESS', 'localhost:8081') + base = 'http://%s' % server_address + return urllib2.urlopen(base + url) + + +class LiveServerAddress(LiveServerBase): + """ + Ensure that the address set in the environment variable is valid. + Refs #2879. + """ + + @classmethod + def setUpClass(cls): + # Backup original environment variable + address_predefined = 'DJANGO_LIVE_TEST_SERVER_ADDRESS' in os.environ + old_address = os.environ.get('DJANGO_LIVE_TEST_SERVER_ADDRESS') + + # Just the host is not accepted + os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'localhost' + try: + super(LiveServerAddress, cls).setUpClass() + raise Exception("The line above should have raised an exception") + except ImproperlyConfigured: + pass + + # The host must be valid + os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'blahblahblah:8081' + try: + super(LiveServerAddress, cls).setUpClass() + raise Exception("The line above should have raised an exception") + except WSGIServerException: + pass + + # If contrib.staticfiles isn't configured properly, the exception + # should bubble up to the main thread. + old_STATIC_URL = TEST_SETTINGS['STATIC_URL'] + TEST_SETTINGS['STATIC_URL'] = None + os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'localhost:8081' + try: + super(LiveServerAddress, cls).setUpClass() + raise Exception("The line above should have raised an exception") + except ImproperlyConfigured: + pass + TEST_SETTINGS['STATIC_URL'] = old_STATIC_URL + + # Restore original environment variable + if address_predefined: + os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = old_address + else: + del os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] + + def test_test_test(self): + # Intentionally empty method so that the test is picked up by the + # test runner and the overriden setUpClass() method is executed. + pass + +class LiveServerViews(LiveServerBase): + def test_404(self): + """ + Ensure that the LiveServerTestCase serves 404s. + Refs #2879. + """ + try: + self.urlopen('/') + except urllib2.HTTPError, err: + self.assertEquals(err.code, 404, 'Expected 404 response') + else: + self.fail('Expected 404 response') + + def test_view(self): + """ + Ensure that the LiveServerTestCase serves views. + Refs #2879. + """ + f = self.urlopen('/example_view/') + self.assertEquals(f.read(), 'example view') + + def test_static_files(self): + """ + Ensure that the LiveServerTestCase serves static files. + Refs #2879. + """ + f = self.urlopen('/static/example_static_file.txt') + self.assertEquals(f.read(), 'example static file\n') + + def test_media_files(self): + """ + Ensure that the LiveServerTestCase serves media files. + Refs #2879. + """ + f = self.urlopen('/media/example_media_file.txt') + self.assertEquals(f.read(), 'example media file\n') + + +class LiveServerDatabase(LiveServerBase): + + def test_fixtures_loaded(self): + """ + Ensure that fixtures are properly loaded and visible to the + live server thread. + Refs #2879. + """ + f = self.urlopen('/model_view/') + self.assertEquals(f.read().splitlines(), ['jane', 'robert']) + + def test_database_writes(self): + """ + Ensure that data written to the database by a view can be read. + Refs #2879. + """ + self.urlopen('/create_model_instance/') + names = [person.name for person in Person.objects.all()] + self.assertEquals(names, ['jane', 'robert', 'emily']) diff --git a/tests/regressiontests/servers/urls.py b/tests/regressiontests/servers/urls.py new file mode 100644 index 0000000000..c8ca1ac7a4 --- /dev/null +++ b/tests/regressiontests/servers/urls.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +from django.conf.urls import patterns, url + +from . import views + + +urlpatterns = patterns('', + url(r'^example_view/$', views.example_view), + url(r'^model_view/$', views.model_view), + url(r'^create_model_instance/$', views.create_model_instance), +)
\ No newline at end of file diff --git a/tests/regressiontests/servers/views.py b/tests/regressiontests/servers/views.py new file mode 100644 index 0000000000..94a4f2d9ef --- /dev/null +++ b/tests/regressiontests/servers/views.py @@ -0,0 +1,17 @@ +from django.http import HttpResponse +from .models import Person + + +def example_view(request): + return HttpResponse('example view') + + +def model_view(request): + people = Person.objects.all() + return HttpResponse('\n'.join([person.name for person in people])) + + +def create_model_instance(request): + person = Person(name='emily') + person.save() + return HttpResponse('')
\ No newline at end of file diff --git a/tests/runtests.py b/tests/runtests.py index c38c4c2202..73c35a50c1 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -49,7 +49,10 @@ def geodjango(settings): def get_test_modules(): modules = [] - for loc, dirpath in (MODEL_TESTS_DIR_NAME, MODEL_TEST_DIR), (REGRESSION_TESTS_DIR_NAME, REGRESSION_TEST_DIR), (CONTRIB_DIR_NAME, CONTRIB_DIR): + for loc, dirpath in ( + (MODEL_TESTS_DIR_NAME, MODEL_TEST_DIR), + (REGRESSION_TESTS_DIR_NAME, REGRESSION_TEST_DIR), + (CONTRIB_DIR_NAME, CONTRIB_DIR)): for f in os.listdir(dirpath): if (f.startswith('__init__') or f.startswith('.') or @@ -150,7 +153,8 @@ def django_tests(verbosity, interactive, failfast, test_labels): settings.TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner' TestRunner = get_runner(settings) - test_runner = TestRunner(verbosity=verbosity, interactive=interactive, failfast=failfast) + test_runner = TestRunner(verbosity=verbosity, interactive=interactive, + failfast=failfast) failures = test_runner.run_tests(test_labels, extra_tests=extra_tests) teardown(state) @@ -175,7 +179,8 @@ def bisect_tests(bisection_label, options, test_labels): except ValueError: pass - subprocess_args = [sys.executable, __file__, '--settings=%s' % options.settings] + subprocess_args = [ + sys.executable, __file__, '--settings=%s' % options.settings] if options.failfast: subprocess_args.append('--failfast') if options.verbosity: @@ -235,7 +240,8 @@ def paired_tests(paired_test, options, test_labels): except ValueError: pass - subprocess_args = [sys.executable, __file__, '--settings=%s' % options.settings] + subprocess_args = [ + sys.executable, __file__, '--settings=%s' % options.settings] if options.failfast: subprocess_args.append('--failfast') if options.verbosity: @@ -244,7 +250,8 @@ def paired_tests(paired_test, options, test_labels): subprocess_args.append('--noinput') for i, label in enumerate(test_labels): - print '***** %d of %d: Check test pairing with %s' % (i+1, len(test_labels), label) + print '***** %d of %d: Check test pairing with %s' % ( + i+1, len(test_labels), label) failures = subprocess.call(subprocess_args + [label, paired_test]) if failures: print '***** Found problem pair with',label @@ -257,19 +264,36 @@ if __name__ == "__main__": from optparse import OptionParser usage = "%prog [options] [module module module ...]" parser = OptionParser(usage=usage) - parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='1', + parser.add_option( + '-v','--verbosity', action='store', dest='verbosity', default='1', type='choice', choices=['0', '1', '2', '3'], - help='Verbosity level; 0=minimal output, 1=normal output, 2=all output') - parser.add_option('--noinput', action='store_false', dest='interactive', default=True, + help='Verbosity level; 0=minimal output, 1=normal output, 2=all ' + 'output') + parser.add_option( + '--noinput', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind.') - parser.add_option('--failfast', action='store_true', dest='failfast', default=False, - help='Tells Django to stop running the test suite after first failed test.') - parser.add_option('--settings', - help='Python path to settings module, e.g. "myproject.settings". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.') - parser.add_option('--bisect', action='store', dest='bisect', default=None, - help="Bisect the test suite to discover a test that causes a test failure when combined with the named test.") - parser.add_option('--pair', action='store', dest='pair', default=None, - help="Run the test suite in pairs with the named test to find problem pairs.") + parser.add_option( + '--failfast', action='store_true', dest='failfast', default=False, + help='Tells Django to stop running the test suite after first failed ' + 'test.') + parser.add_option( + '--settings', + help='Python path to settings module, e.g. "myproject.settings". If ' + 'this isn\'t provided, the DJANGO_SETTINGS_MODULE environment ' + 'variable will be used.') + parser.add_option( + '--bisect', action='store', dest='bisect', default=None, + help='Bisect the test suite to discover a test that causes a test ' + 'failure when combined with the named test.') + parser.add_option( + '--pair', action='store', dest='pair', default=None, + help='Run the test suite in pairs with the named test to find problem ' + 'pairs.') + parser.add_option( + '--liveserver', action='store', dest='liveserver', default=None, + help='Overrides the default address where the live server (used with ' + 'LiveServerTestCase) is expected to run from. The default value ' + 'is localhost:8081.'), options, args = parser.parse_args() if options.settings: os.environ['DJANGO_SETTINGS_MODULE'] = options.settings @@ -279,11 +303,15 @@ if __name__ == "__main__": else: options.settings = os.environ['DJANGO_SETTINGS_MODULE'] + if options.liveserver is not None: + os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = options.liveserver + if options.bisect: bisect_tests(options.bisect, options, args) elif options.pair: paired_tests(options.pair, options, args) else: - failures = django_tests(int(options.verbosity), options.interactive, options.failfast, args) + failures = django_tests(int(options.verbosity), options.interactive, + options.failfast, args) if failures: sys.exit(bool(failures)) |
