1
0
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:
Kevin Kubasik 2009-07-22 13:12:26 +00:00
parent 2511eb2759
commit afa7b39d6f
4 changed files with 356 additions and 12 deletions

View File

@ -24,8 +24,11 @@ class BaseCoverageRunner(object):
def __init__(self): def __init__(self):
"""Placeholder (since it is overrideable)""" """Placeholder (since it is overrideable)"""
self.cov = coverage.coverage(cover_pylib=True) self.cov = coverage.coverage(cover_pylib=True, auto_data=True)
self.cov.erase() self.cov.use_cache(True)
self.cov.load()
#self.cov.combine()
def run_tests(self, test_labels, verbosity=1, interactive=True, def run_tests(self, test_labels, verbosity=1, interactive=True,
@ -36,7 +39,7 @@ class BaseCoverageRunner(object):
""" """
#self.cov.erase() #self.cov.erase()
#Allow an on-disk cache of coverage stats. #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', []): #for e in getattr(settings, 'COVERAGE_CODE_EXCLUDES', []):
# self.cov.exclude(e) # self.cov.exclude(e)
@ -45,7 +48,7 @@ class BaseCoverageRunner(object):
brt = base_run_tests() brt = base_run_tests()
results = brt.run_tests(test_labels, verbosity, interactive, extra_tests) results = brt.run_tests(test_labels, verbosity, interactive, extra_tests)
self.cov.stop() self.cov.stop()
#self.cov.erase()
coverage_modules = [] coverage_modules = []
if test_labels: if test_labels:

332
django/test/twill_tests.py Normal file
View 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)

View File

@ -27,7 +27,7 @@ def test_loginAndSetup():
client.asserts.assertNode(link=u'Change') client.asserts.assertNode(link=u'Change')
client.asserts.assertNode(link=u'Admin_Views') client.asserts.assertNode(link=u'Admin_Views')
client.asserts.assertNode(xpath=u"//div[@id='user-tools']/strong") 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.waits.forPageLoad(timeout=u'20000')
client.type(text=u'Test Section', id=u'id_name') client.type(text=u'Test Section', id=u'id_name')
client.click(name=u'_save') client.click(name=u'_save')
@ -324,15 +324,15 @@ def test_parentChildRelationship():
client.open(url=ADMIN_URL) client.open(url=ADMIN_URL)
client.waits.forPageLoad(timeout=u'20000') 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.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[21]/td/a") client.click(xpath=u"//div[@id='content-main']/div/table/tbody/tr[22]/td/a")
client.click(name=u'_save') client.click(name=u'_save')
client.waits.forPageLoad(timeout=u'20000') client.waits.forPageLoad(timeout=u'20000')
client.click(link=u'Recommender object') client.click(link=u'Recommender object')
client.waits.forPageLoad(timeout=u'20000') client.waits.forPageLoad(timeout=u'20000')
client.click(link=u'Home') client.click(link=u'Home')
client.waits.forPageLoad(timeout=u'20000') 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.click(id=u'id_recommender')
client.select(option=u'Recommender object', id=u'id_recommender') client.select(option=u'Recommender object', id=u'id_recommender')
client.click(value=u'1') client.click(value=u'1')

View File

@ -1,4 +1,14 @@
#!/usr/bin/env python #!/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 os, sys, traceback
import unittest import unittest
import django import django
@ -11,10 +21,7 @@ try:
except NameError: except NameError:
from sets import Set as set # For Python 2.3 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' CONTRIB_DIR_NAME = 'django.contrib'
MODEL_TESTS_DIR_NAME = 'modeltests' 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. #Run the appropriate test runner based on command line params.
if do_std: if do_std:
if do_coverage: if do_coverage:
_dj_cover.save()
_dj_cover.stop()
test_runner = get_runner(settings, coverage=True, reports=True) test_runner = get_runner(settings, coverage=True, reports=True)
else: else:
test_runner = get_runner(settings, coverage=False, reports=False) test_runner = get_runner(settings, coverage=False, reports=False)