Commit dca3321f authored by Stefan H. Holek's avatar Stefan H. Holek

Add doctest support and remove some b/w compatiblity cruft.

parent 77464615
......@@ -135,18 +135,3 @@ class PortalTestCase(base.TestCase):
'''Logs out.'''
noSecurityManager()
# b/w compatibility methods
def _setRoles(self, roles, name=user_name):
self.setRoles(roles, name)
def _setPermissions(self, permissions, role='Member'):
self.setPermissions(permissions, role)
def _login(self, name=user_name):
self.login(name)
def _logout(self):
self.logout()
# b/w compatibility names
_portal_name = portal_name
......@@ -100,17 +100,6 @@ class ZopeTestCase(base.TestCase):
'''Logs out.'''
noSecurityManager()
# b/w compatibility methods
def _setRoles(self, roles, name=user_name):
self.setRoles(roles, name)
def _setPermissions(self, permissions, role=user_role):
self.setPermissions(permissions, role)
def _login(self, name=user_name):
self.login(name)
def _logout(self):
self.logout()
class FunctionalTestCase(functional.Functional, ZopeTestCase):
'''Base class for functional Zope tests'''
......@@ -119,11 +108,6 @@ class FunctionalTestCase(functional.Functional, ZopeTestCase):
ZopeTestCase.__implements__)
# b/w compatibility names
_folder_name = folder_name
_user_name = user_name
_user_role = user_role
_standard_permissions = standard_permissions
from base import app
from base import close
from base import closeConnections
......
......@@ -30,18 +30,9 @@ from profiler import Profiled
from sandbox import Sandboxed
from functional import Functional
from warnhook import WarningsHook
from ZODB.tests.warnhook import WarningsHook
from unittest import main
# TODO
#from doctest import ZopeDocFileSuite
#from doctest import FunctionalDocFileSuite
# b/w compatibility names
_folder_name = folder_name
_user_name = user_name
_user_role = user_role
_standard_permissions = standard_permissions
_portal_name = portal_name
from base import closeConnections
from ztc_doctest import ZopeDocFileSuite
from ztc_doctest import FunctionalDocFileSuite
0.9.7 (Zope 2.8 edition)
- Renamed 'doctest' sub-package to 'ztc_doctest' because of name-shadowing
issues uncovered during integration into Zope 2.8. This should not
affect existing tests as user code is not expected to import from sub-
packages anyway.
- Removed warnhook.py, we now use the one from ZODB.tests.
- Removed doctest.py, we now use the one from zope.testing.
0.9.6
- Dropped support for Zope 2.5 as it lacks the setSecurityManager() API.
- Moved interfaces from doc section to interfaces.py module.
......
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import warnings
class WarningsHook:
"""Hook to capture warnings generated by Python.
The function warnings.showwarning() is designed to be hooked by
application code, allowing the application to customize the way it
handles warnings.
This hook captures the unformatted warning information and stored
it in a list. A test can inspect this list after the test is over.
Issues:
The warnings module has lots of delicate internal state. If
a warning has been reported once, it won't be reported again. It
may be necessary to extend this class with a mechanism for
modifying the internal state so that we can be guaranteed a
warning will be reported.
If Python is run with a warnings filter, e.g. python -Werror,
then a test that is trying to inspect a particular warning will
fail. Perhaps this class can be extended to install more-specific
filters the test to work anyway.
"""
def __init__(self):
self.original = None
self.warnings = []
def install(self):
self.original = warnings.showwarning
warnings.showwarning = self.showwarning
def uninstall(self):
assert self.original is not None
warnings.showwarning = self.original
self.original = None
def showwarning(self, message, category, filename, lineno):
self.warnings.append((str(message), category, filename, lineno))
def clear(self):
self.warnings = []
====================
Functional doc tests
====================
Doctests are a way to write tests while documenting the thing that is
tested at the same time. As an example, this file both documents
functional doc tests *and* tests them.
Doctests look like regular interactive interpreter sessions. That
makes them very easy to create. Doctests can either occur in an
object's or method's docstring or in a separate file. Use either
``DocTestSuite`` or ``DocFileSuite`` in these cases.
Creating functional doctests
----------------------------
Creating functional doctests is just as easy. Obviously, you cannot
simply use an interpreter shell for the initial test creation.
Instead, you can use the `tcpwatch` program to record browser sessions
and turn them into tests:
1. Start out with a clean ZODB database.
- Create a folder named `test_folder_1_` in the root folder.
- Create a user in the root user folder called `test_user_1_` with
the password `secret`.
- Create a role `test_role_1_` and grant the role to the test
user. Grant the permissions 'Access contents information' and
'View' to the role.
2. Install tcpwatch. You can get a recent version from Zope CVS:
http://cvs.zope.org/Packages/tcpwatch/
3. Create a temporary directory to record tcpwatch output.
4. Run tcpwatch using:
tcpwatch.py -L 8081:8080 -s -r tmpdir
(the ports are the listening port and forwarded-to port; the
second port must match the Zope configuration)
5. In a browser, connect to the listening port and do whatever needs
to be recorded.
6. Shut down tcpwatch.
7. Run the script at Zope/lib/python/Testing/ZopeTestCase/doctest/dochttp.py
python2.3 dochttp.py tmpdir > .../mytest.txt
8. Edit the generated text file to add explanations and elide
uninteresting portions of the output.
9. In a functional test module (usually ftests.py), import
``FunctionalDocFileSuite`` and instantiate it, passing the name of the
text file containing the test. For example:
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from Testing import ZopeTestCase
from unittest import TestSuite
from Testing.ZopeTestCase import FunctionalDocFileSuite
def test_suite():
suite = TestSuite()
suite.addTest(FunctionalDocFileSuite('FunctionalDocTest.txt'))
return suite
if __name__ == '__main__':
framework()
Examples
--------
Test Publish Document
>>> print http(r"""
... GET /test_folder_1_/index_html HTTP/1.1
... """, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: 5
Content-Type: text/plain
<BLANKLINE>
index
Test Publish Script
>>> print http(r"""
... GET /test_folder_1_/script HTTP/1.1
... """, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: 1
Content-Type: text/plain
<BLANKLINE>
1
Test Publish Script with Argument
>>> print http(r"""
... GET /test_folder_1_/script?a:int=2 HTTP/1.1
... """, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: 1
Content-Type: text/plain
<BLANKLINE>
3
Test Server Error
>>> print http(r"""
... GET /test_folder_1_/script?a=2 HTTP/1.1
... """, handle_errors=False)
HTTP/1.1 500 Internal Server Error
...Content-Type: text/html...exceptions.TypeError...
Test Unauthorized
>>> self.folder.index_html.manage_permission('View', ['Owner'])
>>> print http(r"""
... GET /test_folder_1_/index_html HTTP/1.1
... """, handle_errors=False)
HTTP/1.1 401 Unauthorized
...
Test Basic Authentication
>>> from AccessControl.Permissions import manage_properties
>>> self.setPermissions([manage_properties])
>>> print http(r"""
... GET /test_folder_1_/index_html/change_title?title=Foo HTTP/1.1
... Authorization: Basic %s
... """ % user_auth, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: 0
...
>>> self.folder.index_html.title_or_id()
'Foo'
Test passing in non-base64-encoded login/pass
>>> from Testing.ZopeTestCase import user_name, user_password
>>> print http(r"""
... GET /test_folder_1_/index_html/change_title?title=Baz HTTP/1.1
... Authorization: Basic %s:%s
... """ % (user_name, user_password), handle_errors=False)
HTTP/1.1 200 OK
Content-Length: 0
...
>>> self.folder.index_html.title_or_id()
'Baz'
Doctest support for ZopeTestCase
================================
Backport of functional doc tests from Zope 3 by
Sidnei da Silva. See 'FunctionalDocTest.txt' for
documentation.
You can learn more about doc tests here:
http://docs.python.org/lib/module-doctest.html
Dummy Test
==========
This dummy test is just used to check that passing in a ``test_class``
that doesn't subclass ``ZopeTestCase.Functional`` still works but
issues a warning.
>>> from Testing import ZopeTestCase
>>> class NoFunctionalTest(ZopeTestCase.ZopeTestCase): pass
>>> hook = ZopeTestCase.WarningsHook()
>>> hook.install()
>>> test = ZopeTestCase.FunctionalDocFileSuite('WarningsTest.txt',
... package='Testing.ZopeTestCase.ztc_doctest',
... test_class=NoFunctionalTest)
>>> len(hook.warnings)
1
>>> message, category, filename, lineno = hook.warnings[0]
>>> message
"The test_class you are using doesn't subclass from ZopeTestCase.Functional. Please fix that."
>>> category.__name__
'UserWarning'
We have to uninstall the hook so that other warnings don't get lost.
>>> hook.uninstall()
==============
Zope doc tests
==============
Doctests are a way to write tests while documenting the thing that is
tested at the same time. As an example, this file both documents
Zope doc tests *and* tests them.
Doctests look like regular interactive interpreter sessions. That
makes them very easy to create. Doctests can either occur in an
object's or method's docstring or in a separate file. Use either
``DocTestSuite`` or ``DocFileSuite`` in these cases.
Creating Zope doc tests
-----------------------
Creating doc tests for Zope is easy. While we cannot simply use an
interpeter shell to write our tests, we can reuse the ZopeTestCase
infrastructure to set up the necessary environment.
1. Create a text file, just like this one, containing prose
documentation interspersed with doctest ``Examples``.
2. In a test module import ``ZopeDocFileSuite`` and instantiate it,
passing the name of the text file containing the tests.
For example:
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from Testing import ZopeTestCase
from unittest import TestSuite
from Testing.ZopeTestCase import ZopeDocFileSuite
def test_suite():
suite = TestSuite()
suite.addTest(ZopeDocFileSuite('ZopeDocTest.txt'))
return suite
if __name__ == '__main__':
framework()
Examples
--------
Here we are going to demonstrate that everything we know about
ZopeTestCase is still true for doc tests. In particular, the default
fixture is set up for doc tests just like for unit tests.
>>> from Testing.ZopeTestCase import folder_name, user_name
>>> from AccessControl import getSecurityManager
There should be a folder:
>>> folder_name in self.app.objectIds()
True
Containing a user folder:
>>> 'acl_users' in self.folder.objectIds()
True
Containing the default user:
>>> user = self.folder.acl_users.getUserById(user_name)
>>> user is None
False
The default user should be logged in:
>>> getSecurityManager().getUser().getId() == user_name
True
The custom setUp method should have been run as well. See
testZopeDocTest.py for its definition.
>>> 'object' in self.folder.objectIds()
True
Now let's manipulate our test objects a bit:
>>> ob = self.folder.object
>>> print ob.title_or_id()
object
>>> ob.manage_changeProperties(title='Foo')
>>> print ob.title_or_id()
Foo
>>> self.folder.manage_delObjects('object')
>>> 'object' in self.folder.objectIds()
False
from zope.testing.doctest import *
from functional import *
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Convert an http tcpwatch session to a doctest
$Id: dochttp.py,v 1.2 2004/10/21 21:19:09 shh42 Exp $
"""
import errno
import optparse
import os
import re
import rfc822
import sys
usage = """usage: %prog <options> directory
Convert an http tcpwatch recorded sesssion to a doctest file, which is
written to standard output.
"""
parser = optparse.OptionParser(usage)
parser.add_option("-p", "--prefix", default="watch",
help="Prefix for recorded tcpwatch session files")
parser.add_option("-U", "--skip-url", action="append",
help="Regular expression for URLs to skip")
parser.add_option("-E", "--skip-extension", action="append",
help="URL file-extension to skip")
parser.add_option("-e", "--extension", action="append",
help="URL file-extension to include")
parser.add_option("-I", "--skip-request-header", action="append",
help="Request header to skip")
parser.add_option("-O", "--skip-response-header", action="append",
help="Response header to skip")
parser.add_option("-r", "--clean-redirects", action="store_true",
help="Strip content from redirect responses",
default=False)
default_options = [
'-e', 'html',
'-I', 'Accept-Charset', '-I', 'Accept-Encoding', '-I', 'Accept-Language',
'-I', 'Accept', '-I', 'Connection', '-I', 'Host', '-I', 'Keep-Alive',
'-I', 'User-Agent',
'-O', 'Date', '-O', 'Server', '-O', 'X-Content-Type-Warning',
'-O', 'X-Powered-By',
]
def dochttp(args=sys.argv[1:], default=None):
"""Convert a tcpwatch recorded sesssion to a doctest file"""
if default is None:
default = default_options
options, args = parser.parse_args(default+args)
try:
directory, = args
except:
parser.print_help()
sys.exit(1)
skip_extensions = options.skip_extension or ()
extensions = [ext for ext in (options.extension or ())
if ext not in skip_extensions]
skip_urls = [re.compile(pattern) for pattern in (options.skip_url or ())]
names = [name[:-len(".request")]
for name in os.listdir(directory)
if name.startswith(options.prefix) and name.endswith('.request')
]
names.sort()
extre = re.compile("[.](\w+)$")
for name in names:
requests = Requests(
open(os.path.join(directory, name + ".request"), 'rb'),
options.skip_request_header,
)
responses = Responses(
open(os.path.join(directory, name + ".response"), 'rb'),
options.skip_response_header,
)
# We use map so as *not* to truncate at shortest input.
# We want an error if the number of requests and responses
# is different.
for request, response in map(None, requests, responses):
assert (request and response) or not (request or response)
path = request.path
ext = extre.search(path)
if ext:
ext = ext.group(1)
if extensions:
if ext not in extensions:
continue
else:
if ext in skip_extensions:
continue
for skip_url in skip_urls:
if skip_url.search(request.path):
break
else:
try:
output_test(request, response, options.clean_redirects)
except IOError, e:
if e.errno == errno.EPIPE:
return
raise
def output_test(request, response, clean_redirects=False):
print
print
print ' >>> print http(r"""'
print ' ...', '\n ... '.join(request.lines())+'""")'
if response.code in (301, 302, 303) and clean_redirects:
if response.headers:
for i in range(len(response.headers)):
h, v = response.headers[i]
if h == "Content-Length":
response.headers[i] = (h, "...")
lines = response.header_lines()
if lines:
lines.append("...")
else:
lines = response.lines()
print ' ', '\n '.join([line.rstrip() and line or '<BLANKLINE>'
for line in lines])
class Message:
start = ''
def __init__(self, file, skip_headers):
start = file.readline().rstrip()
if start:
self.start = start
if start.startswith("HTTP/"):
# This is a response; extract the response code:
self.code = int(start.split()[1])
headers = [split_header(header)
for header in rfc822.Message(file).headers
]
headers = [
('-'.join([s.capitalize() for s in name.split('-')]),
v.rstrip()
)
for (name, v) in headers
if name.lower() not in skip_headers
]
self.headers = headers
content_length = int(dict(headers).get('Content-Length', '0'))
if content_length:
self.body = file.read(content_length).split('\n')
else:
self.body = []
def __nonzero__(self):
return bool(self.start)
def lines(self):
output = self.header_lines()
if output:
output.extend(self.body)
return output
def header_lines(self):
if self.start:
output = [self.start]
headers = ["%s: %s" % (name, v) for (name, v) in self.headers]
headers.sort()
output.extend(headers)
output.append('')
else:
output = []
return output
headerre = re.compile('(\S+): (.+)$')
def split_header(header):
return headerre.match(header).group(1, 2)
def messages(cls, file, skip_headers):
skip_headers = [name.lower() for name in (skip_headers or ())]
while 1:
message = cls(file, skip_headers)
if message:
yield message
else:
break
class Request(Message):
path = ''
def __init__(self, file, skip_headers):
Message.__init__(self, file, skip_headers)
if self.start:
self.command, self.path, self.protocol = self.start.split()
def Requests(file, skip_headers):
return messages(Request, file, skip_headers)
def Responses(file, skip_headers):
return messages(Message, file, skip_headers)
main = dochttp
if __name__ == '__main__':
main()
##############################################################################
#
# ZopeTestCase
#
# COPY THIS FILE TO YOUR 'tests' DIRECTORY.
#
# This version of framework.py will use the SOFTWARE_HOME
# environment variable to locate Zope and the Testing package.
#
# If the tests are run in an INSTANCE_HOME installation of Zope,
# Products.__path__ and sys.path with be adjusted to include the
# instance's Products and lib/python directories respectively.
#
# If you explicitly set INSTANCE_HOME prior to running the tests,
# auto-detection is disabled and the specified path will be used
# instead.
#
# If the 'tests' directory contains a custom_zodb.py file, INSTANCE_HOME
# will be adjusted to use it.
#
# If you set the ZEO_INSTANCE_HOME environment variable a ZEO setup
# is assumed, and you can attach to a running ZEO server (via the
# instance's custom_zodb.py).
#
##############################################################################
#
# The following code should be at the top of every test module:
#
# import os, sys
# if __name__ == '__main__':
# execfile(os.path.join(sys.path[0], 'framework.py'))
#
# ...and the following at the bottom:
#
# if __name__ == '__main__':
# framework()
#
##############################################################################
__version__ = '0.2.3'
# Save start state
#
__SOFTWARE_HOME = os.environ.get('SOFTWARE_HOME', '')
__INSTANCE_HOME = os.environ.get('INSTANCE_HOME', '')
if __SOFTWARE_HOME.endswith(os.sep):
__SOFTWARE_HOME = os.path.dirname(__SOFTWARE_HOME)
if __INSTANCE_HOME.endswith(os.sep):
__INSTANCE_HOME = os.path.dirname(__INSTANCE_HOME)
# Find and import the Testing package
#
if not sys.modules.has_key('Testing'):
p0 = sys.path[0]
if p0 and __name__ == '__main__':
os.chdir(p0)
p0 = ''
s = __SOFTWARE_HOME
p = d = s and s or os.getcwd()
while d:
if os.path.isdir(os.path.join(p, 'Testing')):
zope_home = os.path.dirname(os.path.dirname(p))
sys.path[:1] = [p0, p, zope_home]
break
p, d = s and ('','') or os.path.split(p)
else:
print 'Unable to locate Testing package.',
print 'You might need to set SOFTWARE_HOME.'
sys.exit(1)
import Testing, unittest
execfile(os.path.join(os.path.dirname(Testing.__file__), 'common.py'))
# Include ZopeTestCase support
#
if 1: # Create a new scope
p = os.path.join(os.path.dirname(Testing.__file__), 'ZopeTestCase')
if not os.path.isdir(p):
print 'Unable to locate ZopeTestCase package.',
print 'You might need to install ZopeTestCase.'
sys.exit(1)
ztc_common = 'ztc_common.py'
ztc_common_global = os.path.join(p, ztc_common)
f = 0
if os.path.exists(ztc_common_global):
execfile(ztc_common_global)
f = 1
if os.path.exists(ztc_common):
execfile(ztc_common)
f = 1
if not f:
print 'Unable to locate %s.' % ztc_common
sys.exit(1)
# Debug
#
print 'SOFTWARE_HOME: %s' % os.environ.get('SOFTWARE_HOME', 'Not set')
print 'INSTANCE_HOME: %s' % os.environ.get('INSTANCE_HOME', 'Not set')
sys.stdout.flush()
#
# Support for functional doc tests
#
# $Id: functional.py,v 1.7 2005/02/04 21:32:33 shh42 Exp $
import sys
import re
import base64
import warnings
from zope.testing import doctest
from Testing.ZopeTestCase import Functional
from Testing.ZopeTestCase import FunctionalTestCase
from Testing.ZopeTestCase import ZopeTestCase
from Testing.ZopeTestCase import user_name
from Testing.ZopeTestCase import user_password
from Testing.ZopeTestCase.sandbox import AppZapper
from Testing.ZopeTestCase.functional import ResponseWrapper
class HTTPHeaderOutput:
# zope.interface.implements(zope.server.interfaces.IHeaderOutput)
def __init__(self, protocol, omit):
self.headers = {}
self.headersl = []
self.protocol = protocol
self.omit = omit
def setResponseStatus(self, status, reason):
self.status, self.reason = status, reason
def setResponseHeaders(self, mapping):
self.headers.update(dict(
[('-'.join([s.capitalize() for s in name.split('-')]), v)
for name, v in mapping.items()
if name.lower() not in self.omit]
))
def appendResponseHeaders(self, lst):
headers = [split_header(header) for header in lst]
self.headersl.extend(
[('-'.join([s.capitalize() for s in name.split('-')]), v)
for name, v in headers
if name.lower() not in self.omit]
)
def __str__(self):
out = ["%s: %s" % header for header in self.headers.items()]
out.extend(["%s: %s" % header for header in self.headersl])
out.sort()
out.insert(0, "%s %s %s" % (self.protocol, self.status, self.reason))
return '\n'.join(out)
class DocResponseWrapper(ResponseWrapper):
"""Response Wrapper for use in doc tests
"""
def __init__(self, response, outstream, path, header_output):
ResponseWrapper.__init__(self, response, outstream, path)
self.header_output = header_output
def __str__(self):
body = self.getBody()
if body:
return "%s\n\n%s" % (self.header_output, body)
return "%s\n" % (self.header_output)
headerre = re.compile('(\S+): (.+)$')
def split_header(header):
return headerre.match(header).group(1, 2)
basicre = re.compile('Basic (.+)?:(.+)?$')
def auth_header(header):
match = basicre.match(header)
if match:
import base64
u, p = match.group(1, 2)
if u is None:
u = ''
if p is None:
p = ''
auth = base64.encodestring('%s:%s' % (u, p))
return 'Basic %s' % auth[:-1]
return header
def getRootFolder():
return AppZapper().app()
def sync():
getRootFolder()._p_jar.sync()
def http(request_string, handle_errors=True):
"""Execute an HTTP request string via the publisher
This is used for HTTP doc tests.
"""
import urllib
import rfc822
from cStringIO import StringIO
from ZPublisher.Response import Response
from ZPublisher.Test import publish_module
from AccessControl.SecurityManagement import getSecurityManager
from AccessControl.SecurityManagement import setSecurityManager
# Save current Security Manager
old_sm = getSecurityManager()
# Commit work done by previous python code.
get_transaction().commit()
# Discard leading white space to make call layout simpler
request_string = request_string.lstrip()
# split off and parse the command line
l = request_string.find('\n')
command_line = request_string[:l].rstrip()
request_string = request_string[l+1:]
method, path, protocol = command_line.split()
path = urllib.unquote(path)
instream = StringIO(request_string)
env = {"HTTP_HOST": 'localhost',
"HTTP_REFERER": 'localhost',
"REQUEST_METHOD": method,
"SERVER_PROTOCOL": protocol,
}
p = path.split('?')
if len(p) == 1:
env['PATH_INFO'] = p[0]
elif len(p) == 2:
[env['PATH_INFO'], env['QUERY_STRING']] = p
else:
raise TypeError, ''
header_output = HTTPHeaderOutput(
protocol, ('x-content-type-warning', 'x-powered-by',
'bobo-exception-type', 'bobo-exception-file',
'bobo-exception-value', 'bobo-exception-line'))
headers = [split_header(header)
for header in rfc822.Message(instream).headers]
for name, value in headers:
name = ('_'.join(name.upper().split('-')))
if name not in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
name = 'HTTP_' + name
env[name] = value.rstrip()
if env.has_key('HTTP_AUTHORIZATION'):
env['HTTP_AUTHORIZATION'] = auth_header(env['HTTP_AUTHORIZATION'])
outstream = StringIO()
response = Response(stdout=outstream, stderr=sys.stderr)
publish_module('Zope2', stdin=instream,
response=response,
environ=env)
header_output.setResponseStatus(response.getStatus(), response.errmsg)
header_output.setResponseHeaders(response.headers)
# Restore previous security manager, which may have been changed
# by calling the publish method above
setSecurityManager(old_sm)
# Sync connection
sync()
return DocResponseWrapper(response, outstream, path, header_output)
def FunctionalDocFileSuite(*paths, **kw):
globs = kw.setdefault('globs', {})
globs['http'] = http
globs['getRootFolder'] = getRootFolder
globs['sync'] = sync
globs['user_auth'] = base64.encodestring('%s:%s' % (user_name, user_password))
test_class = kw.get('test_class', FunctionalTestCase)
# If the passed-in test_class doesn't subclass Functional,
# we mix it in for you, but we will issue a warning.
if not issubclass(test_class, Functional):
name = test_class.__name__
warnings.warn(("The test_class you are using doesn't "
"subclass from ZopeTestCase.Functional. "
"Please fix that."), UserWarning, 2)
if not 'Functional' in name:
name = 'Functional%s' % name
test_class = type(name, (Functional, test_class), {})
kw['test_class'] = test_class
return ZopeDocFileSuite(*paths, **kw)
def ZopeDocFileSuite(*paths, **kw):
globs = kw.setdefault('globs', {})
test_class = kw.get('test_class', ZopeTestCase)
# If the test_class does not have a runTest method, we add
# a dummy attribute so that TestCase construction works.
if not hasattr(test_class, 'runTest'):
setattr(test_class, 'runTest', None)
# Create a TestCase instance which will be used to
# execute the setUp and tearDown methods, as well as
# be passed into the test globals as 'self'.
test_instance = test_class()
if 'test_class' in kw:
del kw['test_class']
kw['package'] = doctest._normalize_module(kw.get('package'), depth=3)
kwsetUp = kw.get('setUp')
def setUp(test):
test.globs['test'] = test
test.globs['self'] = test_instance
test_instance.setUp()
if kwsetUp is not None:
kwsetUp(test_instance)
kw['setUp'] = setUp
kwtearDown = kw.get('tearDown')
def tearDown(test):
if kwtearDown is not None:
kwtearDown(test_instance)
test_instance.tearDown()
kw['tearDown'] = tearDown
if 'optionflags' not in kw:
kw['optionflags'] = (doctest.ELLIPSIS
| doctest.REPORT_NDIFF
| doctest.NORMALIZE_WHITESPACE)
return doctest.DocFileSuite(*paths, **kw)
__all__ = [
'FunctionalDocFileSuite',
'ZopeDocFileSuite',
]
GET /@@contents.html HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
GET /@@contents.html HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
GET /@@/pl.gif HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:04 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@singleBranchTree.xml HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
GET /@@/mi.gif HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/
Authorization: Basic bWdyOm1ncnB3
GET /++etc++site/@@manage HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/
Authorization: Basic bWdyOm1ncnB3
GET /@@/site_management.css HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/css,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/++etc++site/@@tasks.html
Authorization: Basic bWdyOm1ncnB3
GET /@@/zope3.css HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/css,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@/onlinehelp.js HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@/xmltree.js HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@/favicon.png HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@/zope3logo.gif HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:04 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@singleBranchTree.xml HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
GET /@@/zope-app-folder-interfaces-IFolder-zmi_icon.gif HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:08 GMT
Authorization: Basic bWdyOm1ncnB3
GET / HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
GET /@@children.xml HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
GET /@@/zope-app-site-interfaces-ISiteManager-zmi_icon.gif HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/
Authorization: Basic bWdyOm1ncnB3
GET /++etc++site/@@tasks.html HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/
Authorization: Basic bWdyOm1ncnB3
GET /++etc++site/@@singleBranchTree.xml HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
#
# Runs all tests in the current directory [and below]
#
# Execute like:
# python runalltests.py [-R]
#
# Alternatively use the testrunner:
# python /path/to/Zope/bin/testrunner.py -qa
#
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
import unittest, imp
TestRunner = unittest.TextTestRunner
suite = unittest.TestSuite()
def visitor(recursive, dir, names):
tests = [n[:-3] for n in names if n.startswith('test') and n.endswith('.py')]
for test in tests:
saved_syspath = sys.path[:]
sys.path.insert(0, dir)
try:
fp, path, desc = imp.find_module(test, [dir])
m = imp.load_module(test, fp, path, desc)
if hasattr(m, 'test_suite'):
suite.addTest(m.test_suite())
finally:
fp.close()
sys.path[:] = saved_syspath
if not recursive:
names[:] = []
if __name__ == '__main__':
os.path.walk(os.curdir, visitor, '-R' in sys.argv)
TestRunner().run(suite)
#
# Test for auth_header
#
# $Id: testAuthHeaderTest.py,v 1.2 2005/01/30 14:30:12 shh42 Exp $
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from Testing import ZopeTestCase
from Testing.ZopeTestCase.ztc_doctest import functional
class AuthHeaderTestCase(ZopeTestCase.TestCase):
def test_auth_encoded(self):
auth_header = functional.auth_header
header = 'Basic Z2xvYmFsbWdyOmdsb2JhbG1ncnB3'
self.assertEquals(auth_header(header), header)
def test_auth_non_encoded(self):
auth_header = functional.auth_header
header = 'Basic globalmgr:globalmgrpw'
expected = 'Basic Z2xvYmFsbWdyOmdsb2JhbG1ncnB3'
self.assertEquals(auth_header(header), expected)
def test_auth_non_encoded_empty(self):
auth_header = functional.auth_header
header = 'Basic globalmgr:'
expected = 'Basic Z2xvYmFsbWdyOg=='
self.assertEquals(auth_header(header), expected)
header = 'Basic :pass'
expected = 'Basic OnBhc3M='
self.assertEquals(auth_header(header), expected)
def test_auth_non_encoded_colon(self):
auth_header = functional.auth_header
header = 'Basic globalmgr:pass:pass'
expected = 'Basic Z2xvYmFsbWdyOnBhc3M6cGFzcw=='
self.assertEquals(auth_header(header), expected)
def test_suite():
from unittest import TestSuite, makeSuite
suite = TestSuite()
suite.addTest(makeSuite(AuthHeaderTestCase))
return suite
if __name__ == '__main__':
framework()
#
# Example functional doctest
#
# $Id: testFunctionalDocTest.py,v 1.7 2005/02/16 14:21:59 shh42 Exp $
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from Testing import ZopeTestCase
ZopeTestCase.installProduct('PythonScripts')
package = 'Testing.ZopeTestCase.ztc_doctest'
def setUp(self):
'''This method will run after the test_class' setUp.
'''
self.folder.addDTMLDocument('index_html', file='index')
self.folder.manage_addProduct['PythonScripts'].manage_addPythonScript('script')
self.folder.script.ZPythonScript_edit(params='a=0', body='return a+1')
change_title = '''<dtml-call "manage_changeProperties(title=REQUEST.get('title'))">'''
self.folder.addDTMLMethod('change_title', file=change_title)
def test_suite():
from unittest import TestSuite
from Testing.ZopeTestCase import FunctionalDocFileSuite
return TestSuite((
FunctionalDocFileSuite('FunctionalDocTest.txt', package=package, setUp=setUp),
))
if __name__ == '__main__':
framework()
#
# Example functional doctest
#
# $Id: testWarningsTest.py,v 1.4 2005/02/16 14:21:59 shh42 Exp $
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from Testing import ZopeTestCase
package = 'Testing.ZopeTestCase.ztc_doctest'
def test_suite():
from unittest import TestSuite
from Testing.ZopeTestCase import FunctionalDocFileSuite
return TestSuite((
FunctionalDocFileSuite('WarningsTest.txt', package=package),
))
if __name__ == '__main__':
framework()
#
# Example Zope doctest
#
# $Id: testZopeDocTest.py,v 1.2 2005/02/16 14:21:59 shh42 Exp $
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from Testing import ZopeTestCase
package = 'Testing.ZopeTestCase.ztc_doctest'
def setUp(self):
'''This method will run after the test_class' setUp.
'''
self.folder.manage_addFolder('object', '')
def test_suite():
from unittest import TestSuite
from Testing.ZopeTestCase import ZopeDocFileSuite
return TestSuite((
ZopeDocFileSuite('ZopeDocTest.txt', package=package, setUp=setUp),
))
if __name__ == '__main__':
framework()
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Test tcpdoc
$Id: test_dochttp.py,v 1.2 2004/10/21 21:19:09 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
import unittest
from Testing import ZopeTestCase
from dochttp import dochttp
directory = os.path.join(os.path.split(ZopeTestCase.__file__)[0],
'ztc_doctest', 'recorded')
expected = r'''
>>> print http(r"""
... GET /@@contents.html HTTP/1.1
... """)
HTTP/1.1 401 Unauthorized
Content-Length: 89
Content-Type: text/html;charset=utf-8
Www-Authenticate: basic realm=zope
<BLANKLINE>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en">
<BLANKLINE>
...
<BLANKLINE>
</html>
<BLANKLINE>
<BLANKLINE>
>>> print http(r"""
... GET /@@contents.html HTTP/1.1
... Authorization: Basic bWdyOm1ncnB3
... """)
HTTP/1.1 200 Ok
Content-Length: 89
Content-Type: text/html;charset=utf-8
<BLANKLINE>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en">
<BLANKLINE>
...
<BLANKLINE>
</html>
<BLANKLINE>
<BLANKLINE>
>>> print http(r"""
... GET /++etc++site/@@manage HTTP/1.1
... Authorization: Basic bWdyOm1ncnB3
... Referer: http://localhost:8081/
... """)
HTTP/1.1 303 See Other
Content-Length: 0
Content-Type: text/plain;charset=utf-8
Location: @@tasks.html
<BLANKLINE>
>>> print http(r"""
... GET / HTTP/1.1
... Authorization: Basic bWdyOm1ncnB3
... """)
HTTP/1.1 200 Ok
Content-Length: 89
Content-Type: text/html;charset=utf-8
<BLANKLINE>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en">
<BLANKLINE>
...
<BLANKLINE>
</html>
<BLANKLINE>
<BLANKLINE>
>>> print http(r"""
... GET /++etc++site/@@tasks.html HTTP/1.1
... Authorization: Basic bWdyOm1ncnB3
... Referer: http://localhost:8081/
... """)
HTTP/1.1 200 Ok
Content-Length: 89
Content-Type: text/html;charset=utf-8
<BLANKLINE>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en">
<BLANKLINE>
...
<BLANKLINE>
</html>
<BLANKLINE>
<BLANKLINE>
'''
class Test(unittest.TestCase):
def test_dochttp(self):
import sys, StringIO
old = sys.stdout
sys.stdout = StringIO.StringIO()
dochttp(['-p', 'test', directory])
got = sys.stdout.getvalue()
sys.stdout = old
self.assert_(got == expected)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(Test))
return suite
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment