mirror of
https://github.com/django/django.git
synced 2025-07-05 02:09:13 +00:00
[gsoc2009-testing] Finally fixed the coverage issue by tracing all imports to a file, then loading on test run. Also, this has a copy of the python twill runner, still working on the DSL version
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/test-improvements@11294 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
2511eb2759
commit
afa7b39d6f
@ -24,8 +24,11 @@ class BaseCoverageRunner(object):
|
||||
|
||||
def __init__(self):
|
||||
"""Placeholder (since it is overrideable)"""
|
||||
self.cov = coverage.coverage(cover_pylib=True)
|
||||
self.cov.erase()
|
||||
self.cov = coverage.coverage(cover_pylib=True, auto_data=True)
|
||||
self.cov.use_cache(True)
|
||||
self.cov.load()
|
||||
#self.cov.combine()
|
||||
|
||||
|
||||
|
||||
def run_tests(self, test_labels, verbosity=1, interactive=True,
|
||||
@ -36,7 +39,7 @@ class BaseCoverageRunner(object):
|
||||
"""
|
||||
#self.cov.erase()
|
||||
#Allow an on-disk cache of coverage stats.
|
||||
self.cov.use_cache(0)
|
||||
#self.cov.use_cache(0)
|
||||
#for e in getattr(settings, 'COVERAGE_CODE_EXCLUDES', []):
|
||||
# self.cov.exclude(e)
|
||||
|
||||
@ -45,7 +48,7 @@ class BaseCoverageRunner(object):
|
||||
brt = base_run_tests()
|
||||
results = brt.run_tests(test_labels, verbosity, interactive, extra_tests)
|
||||
self.cov.stop()
|
||||
|
||||
#self.cov.erase()
|
||||
|
||||
coverage_modules = []
|
||||
if test_labels:
|
||||
|
332
django/test/twill_tests.py
Normal file
332
django/test/twill_tests.py
Normal file
@ -0,0 +1,332 @@
|
||||
"""
|
||||
|
||||
This code is originally by miracle2k:
|
||||
http://bitbucket.org/miracle2k/djutils/src/97f92c32c621/djutils/test/twill.py
|
||||
|
||||
|
||||
Integrates the twill web browsing scripting language with Django.
|
||||
|
||||
Provides too main functions, ``setup()`` and ``teardown``, that hook
|
||||
(and unhook) a certain host name to the WSGI interface of your Django
|
||||
app, making it possible to test your site using twill without actually
|
||||
going through TCP/IP.
|
||||
|
||||
It also changes the twill browsing behaviour, so that relative urls
|
||||
per default point to the intercept (e.g. your Django app), so long
|
||||
as you don't browse away from that host. Further, you are allowed to
|
||||
specify the target url as arguments to Django's ``reverse()``.
|
||||
|
||||
Usage:
|
||||
|
||||
from test_utils.utils import twill_runner as twill
|
||||
twill.setup()
|
||||
try:
|
||||
twill.go('/') # --> Django WSGI
|
||||
twill.code(200)
|
||||
|
||||
twill.go('http://google.com')
|
||||
twill.go('/services') # --> http://google.com/services
|
||||
|
||||
twill.go('/list', default=True) # --> back to Django WSGI
|
||||
|
||||
twill.go('proj.app.views.func',
|
||||
args=[1,2,3])
|
||||
finally:
|
||||
twill.teardown()
|
||||
|
||||
For more information about twill, see:
|
||||
http://twill.idyll.org/
|
||||
"""
|
||||
|
||||
# allows us to import global twill as opposed to this module
|
||||
from __future__ import absolute_import
|
||||
|
||||
# TODO: import all names with a _-prefix to keep the namespace clean with the twill stuff?
|
||||
import urlparse
|
||||
import cookielib
|
||||
|
||||
import twill
|
||||
import twill.commands
|
||||
import twill.browser
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.servers.basehttp import AdminMediaHandler
|
||||
from django.core.handlers.wsgi import WSGIHandler
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpRequest
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.contrib import auth
|
||||
|
||||
from django.core.management.commands.test_windmill import ServerContainer, attempt_import
|
||||
|
||||
# make available through this module
|
||||
from twill.commands import *
|
||||
|
||||
__all__ = ('INSTALLED', 'setup', 'teardown', 'reverse',) + tuple(twill.commands.__all__)
|
||||
|
||||
|
||||
DEFAULT_HOST = '127.0.0.1'
|
||||
DEFAULT_PORT = 9090
|
||||
INSTALLED = SortedDict() # keep track of the installed hooks
|
||||
|
||||
|
||||
def setup(host=None, port=None, allow_xhtml=True, propagate=True):
|
||||
"""Install the WSGI hook for ``host`` and ``port``.
|
||||
|
||||
The default values will be used if host or port are not specified.
|
||||
|
||||
``allow_xhtml`` enables a workaround for the "not viewer HTML"
|
||||
error when browsing sites that are determined to be XHTML, e.g.
|
||||
featuring xhtml-ish mimetypes.
|
||||
|
||||
Unless ``propagate specifies otherwise``, the
|
||||
``DEBUG_PROPAGATE_EXCEPTIONS`` will be enabled for better debugging:
|
||||
when using twill, we don't really want to see 500 error pages,
|
||||
but rather directly the exceptions that occured on the view side.
|
||||
|
||||
Multiple calls to this function will only result in one handler
|
||||
for each host/port combination being installed.
|
||||
"""
|
||||
|
||||
host = host or DEFAULT_HOST
|
||||
port = port or DEFAULT_PORT
|
||||
key = (host, port)
|
||||
|
||||
if not key in INSTALLED:
|
||||
# installer wsgi handler
|
||||
app = AdminMediaHandler(WSGIHandler())
|
||||
twill.add_wsgi_intercept(host, port, lambda: app)
|
||||
|
||||
# start browser fresh
|
||||
browser = get_browser()
|
||||
browser.diverged = False
|
||||
|
||||
# enable xhtml mode if requested
|
||||
_enable_xhtml(browser, allow_xhtml)
|
||||
|
||||
# init debug propagate setting, and remember old value
|
||||
if propagate:
|
||||
old_propgate_setting = settings.DEBUG_PROPAGATE_EXCEPTIONS
|
||||
settings.DEBUG_PROPAGATE_EXCEPTIONS = True
|
||||
else:
|
||||
old_propgate_setting = None
|
||||
|
||||
INSTALLED[key] = (app, old_propgate_setting)
|
||||
return browser
|
||||
return False
|
||||
|
||||
|
||||
def teardown(host=None, port=None):
|
||||
"""Remove an installed WSGI hook for ``host`` and ```port``.
|
||||
|
||||
If no host or port is passed, the default values will be assumed.
|
||||
If no hook is installed for the defaults, and both the host and
|
||||
port are missing, the last hook installed will be removed.
|
||||
|
||||
Returns True if a hook was removed, otherwise False.
|
||||
"""
|
||||
|
||||
both_missing = not host and not port
|
||||
host = host or DEFAULT_HOST
|
||||
port = port or DEFAULT_PORT
|
||||
key = (host, port)
|
||||
|
||||
key_to_delete = None
|
||||
if key in INSTALLED:
|
||||
key_to_delete = key
|
||||
if not key in INSTALLED and both_missing and len(INSTALLED) > 0:
|
||||
host, port = key_to_delete = INSTALLED.keys()[-1]
|
||||
|
||||
if key_to_delete:
|
||||
_, old_propagate = INSTALLED[key_to_delete]
|
||||
del INSTALLED[key_to_delete]
|
||||
result = True
|
||||
if old_propagate is not None:
|
||||
settings.DEBUG_PROPAGATE_EXCEPTIONS = old_propagate
|
||||
else:
|
||||
result = False
|
||||
|
||||
# note that our return value is just a guess according to our
|
||||
# own records, we pass the request on to twill in any case
|
||||
twill.remove_wsgi_intercept(host, port)
|
||||
return result
|
||||
|
||||
|
||||
def _enable_xhtml(browser, enable):
|
||||
"""Twill (darcs from 19-09-2008) does not work with documents
|
||||
identifying themselves as XHTML.
|
||||
|
||||
This is a workaround.
|
||||
"""
|
||||
factory = browser._browser._factory
|
||||
factory.basic_factory._response_type_finder._allow_xhtml = \
|
||||
factory.soup_factory._response_type_finder._allow_xhtml = \
|
||||
enable
|
||||
|
||||
|
||||
class _EasyTwillBrowser(twill.browser.TwillBrowser):
|
||||
"""Custom version of twill's browser class that defaults relative
|
||||
URLs to the last installed hook, if available.
|
||||
|
||||
It also supports reverse resolving, and some additional commands.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.diverged = False
|
||||
self._testing_ = False
|
||||
super(_EasyTwillBrowser, self).__init__(*args, **kwargs)
|
||||
|
||||
def go(self, url, args=None, kwargs=None, default=None):
|
||||
assert not ((args or kwargs) and default==False)
|
||||
|
||||
if args or kwargs:
|
||||
url = reverse(url, args=args, kwargs=kwargs)
|
||||
default = True # default is implied
|
||||
|
||||
if INSTALLED:
|
||||
netloc = '%s:%s' % INSTALLED.keys()[-1]
|
||||
urlbits = urlparse.urlsplit(url)
|
||||
if not urlbits[0]:
|
||||
if default:
|
||||
# force "undiverge"
|
||||
self.diverged = False
|
||||
if not self.diverged:
|
||||
url = urlparse.urlunsplit(('http', netloc)+urlbits[2:])
|
||||
else:
|
||||
self.diverged = True
|
||||
|
||||
if self._testing_: # hack that makes it simple for us to test this
|
||||
return url
|
||||
return super(_EasyTwillBrowser, self).go(url)
|
||||
|
||||
def login(self, **credentials):
|
||||
"""Log the user with the given credentials into your Django
|
||||
site.
|
||||
|
||||
To further simplify things, rather than giving the credentials,
|
||||
you may pass a ``user`` parameter with the ``User`` instance you
|
||||
want to login. Note that in this case the user will not be
|
||||
further validated, i.e. it is possible to login an inactive user
|
||||
this way.
|
||||
|
||||
This works regardless of the url currently browsed, but does
|
||||
require the WSGI intercept to be setup.
|
||||
|
||||
Returns ``True`` if login was possible; ``False`` if the
|
||||
provided credentials are incorrect, or the user is inactive,
|
||||
or if the sessions framework is not available.
|
||||
|
||||
Based on ``django.test.client.Client.logout``.
|
||||
|
||||
Note: A ``twill.reload()`` will not refresh the cookies sent
|
||||
with the request, so your login will not have any effect there.
|
||||
This is different for ``logout``, since it actually invalidates
|
||||
the session server-side, thus making the current key invalid.
|
||||
"""
|
||||
|
||||
if not 'django.contrib.sessions' in settings.INSTALLED_APPS:
|
||||
return False
|
||||
|
||||
host, port = INSTALLED.keys()[-1]
|
||||
|
||||
# determine the user we want to login
|
||||
user = credentials.pop('user', None)
|
||||
if user:
|
||||
# Login expects the user object to reference it's backend.
|
||||
# Since we're not going through ``authenticate``, we'll
|
||||
# have to do this ourselves.
|
||||
backend = auth.get_backends()[0]
|
||||
user.backend = user.backend = "%s.%s" % (
|
||||
backend.__module__, backend.__class__.__name__)
|
||||
else:
|
||||
user = auth.authenticate(**credentials)
|
||||
if not user or not user.is_active:
|
||||
return False
|
||||
|
||||
# create a fake request to use with ``auth.login``
|
||||
request = HttpRequest()
|
||||
request.session = __import__(settings.SESSION_ENGINE, {}, {}, ['']).SessionStore()
|
||||
auth.login(request, user)
|
||||
request.session.save()
|
||||
|
||||
# set the cookie to represent the session
|
||||
self.cj.set_cookie(cookielib.Cookie(
|
||||
version=None,
|
||||
name=settings.SESSION_COOKIE_NAME,
|
||||
value=request.session.session_key,
|
||||
port=str(port), # must be a string
|
||||
port_specified = False,
|
||||
domain=host, #settings.SESSION_COOKIE_DOMAIN,
|
||||
domain_specified=True,
|
||||
domain_initial_dot=False,
|
||||
path='/',
|
||||
path_specified=True,
|
||||
secure=settings.SESSION_COOKIE_SECURE or None,
|
||||
expires=None,
|
||||
discard=None,
|
||||
comment=None,
|
||||
comment_url=None,
|
||||
rest=None
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
def logout(self):
|
||||
"""Log the current user out of your Django site.
|
||||
|
||||
This works regardless of the url currently browsed, but does
|
||||
require the WSGI intercept to be setup.
|
||||
|
||||
Based on ``django.test.client.Client.logout``.
|
||||
"""
|
||||
host, port = INSTALLED.keys()[-1]
|
||||
for cookie in self.cj:
|
||||
if cookie.name == settings.SESSION_COOKIE_NAME \
|
||||
and cookie.domain==host \
|
||||
and (not cookie.port or str(cookie.port)==str(port)):
|
||||
session = __import__(settings.SESSION_ENGINE, {}, {}, ['']).SessionStore()
|
||||
session.delete(session_key=cookie.value)
|
||||
self.cj.clear(cookie.domain, cookie.path, cookie.name)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def go(*args, **kwargs):
|
||||
# replace the default ``go`` to make the additional
|
||||
# arguments that our custom browser provides available.
|
||||
browser = get_browser()
|
||||
browser.go(*args, **kwargs)
|
||||
return browser.get_url()
|
||||
|
||||
def login(*args, **kwargs):
|
||||
return get_browser().login(*args, **kwargs)
|
||||
|
||||
def logout(*args, **kwargs):
|
||||
return get_browser().logout(*args, **kwargs)
|
||||
|
||||
def reset_browser(*args, **kwargs):
|
||||
# replace the default ``reset_browser`` to ensure
|
||||
# that our custom browser class is used
|
||||
result = twill.commands.reset_browser(*args, **kwargs)
|
||||
twill.commands.browser = _EasyTwillBrowser()
|
||||
return result
|
||||
|
||||
# Monkey-patch our custom browser into twill; this will be global, but
|
||||
# will only have an actual effect when intercepts are installed through
|
||||
# our module (via ``setup``).
|
||||
# Unfortunately, twill pretty much forces us to use the same global
|
||||
# state it does itself, lest us reimplement everything from
|
||||
# ``twill.commands``. It's a bit of a shame, we could provide dedicated
|
||||
# browser instances for each call to ``setup()``.
|
||||
reset_browser()
|
||||
|
||||
|
||||
def url(should_be=None):
|
||||
"""Like the default ``url()``, but can be called without arguments,
|
||||
in which case it returns the current url.
|
||||
"""
|
||||
|
||||
if should_be is None:
|
||||
return get_browser().get_url()
|
||||
else:
|
||||
return twill.commands.url(should_be)
|
@ -27,7 +27,7 @@ def test_loginAndSetup():
|
||||
client.asserts.assertNode(link=u'Change')
|
||||
client.asserts.assertNode(link=u'Admin_Views')
|
||||
client.asserts.assertNode(xpath=u"//div[@id='user-tools']/strong")
|
||||
client.click(xpath=u"//div[@id='content-main']/div/table/tbody/tr[22]/td/a")
|
||||
client.click(xpath=u"//div[@id='content-main']/div/table/tbody/tr[23]/td/a")
|
||||
client.waits.forPageLoad(timeout=u'20000')
|
||||
client.type(text=u'Test Section', id=u'id_name')
|
||||
client.click(name=u'_save')
|
||||
@ -324,15 +324,15 @@ def test_parentChildRelationship():
|
||||
|
||||
client.open(url=ADMIN_URL)
|
||||
client.waits.forPageLoad(timeout=u'20000')
|
||||
client.waits.forElement(xpath=u"//div[@id='content-main']/div/table/tbody/tr[21]/td/a", timeout=u'8000')
|
||||
client.click(xpath=u"//div[@id='content-main']/div/table/tbody/tr[21]/td/a")
|
||||
client.waits.forElement(xpath=u"//div[@id='content-main']/div/table/tbody/tr[22]/td/a", timeout=u'8000')
|
||||
client.click(xpath=u"//div[@id='content-main']/div/table/tbody/tr[22]/td/a")
|
||||
client.click(name=u'_save')
|
||||
client.waits.forPageLoad(timeout=u'20000')
|
||||
client.click(link=u'Recommender object')
|
||||
client.waits.forPageLoad(timeout=u'20000')
|
||||
client.click(link=u'Home')
|
||||
client.waits.forPageLoad(timeout=u'20000')
|
||||
client.click(xpath=u"//div[@id='content-main']/div/table/tbody/tr[20]/td/a")
|
||||
client.click(xpath=u"//div[@id='content-main']/div/table/tbody/tr[21]/td/a")
|
||||
client.click(id=u'id_recommender')
|
||||
client.select(option=u'Recommender object', id=u'id_recommender')
|
||||
client.click(value=u'1')
|
||||
|
@ -1,4 +1,14 @@
|
||||
#!/usr/bin/env python
|
||||
try:
|
||||
import coverage
|
||||
global _dj_cover
|
||||
_dj_cover = coverage.coverage(cover_pylib=True, auto_data=True)
|
||||
_dj_cover.erase()
|
||||
_dj_cover.use_cache(True)
|
||||
_dj_cover.start()
|
||||
except Exception, e:
|
||||
print "coverage.py module not available"
|
||||
|
||||
import os, sys, traceback
|
||||
import unittest
|
||||
import django
|
||||
@ -11,10 +21,7 @@ try:
|
||||
except NameError:
|
||||
from sets import Set as set # For Python 2.3
|
||||
|
||||
try:
|
||||
import coverage
|
||||
except Exception, e:
|
||||
print "coverage.py module not available"
|
||||
|
||||
|
||||
CONTRIB_DIR_NAME = 'django.contrib'
|
||||
MODEL_TESTS_DIR_NAME = 'modeltests'
|
||||
@ -202,6 +209,8 @@ def django_tests(verbosity, interactive, test_labels):
|
||||
#Run the appropriate test runner based on command line params.
|
||||
if do_std:
|
||||
if do_coverage:
|
||||
_dj_cover.save()
|
||||
_dj_cover.stop()
|
||||
test_runner = get_runner(settings, coverage=True, reports=True)
|
||||
else:
|
||||
test_runner = get_runner(settings, coverage=False, reports=False)
|
||||
|
Loading…
x
Reference in New Issue
Block a user