summaryrefslogtreecommitdiff
path: root/paste/tests
diff options
context:
space:
mode:
authorianb <devnull@localhost>2005-04-22 03:18:20 +0000
committerianb <devnull@localhost>2005-04-22 03:18:20 +0000
commit36593a8d40cd1417d2927a78cdbe621090b6e8a5 (patch)
tree74dc42b93bb512593869fd57f8be99c198d8c108 /paste/tests
parent7752a3102fc8aa68ee7e349495bc2e9345c77c00 (diff)
downloadpaste-36593a8d40cd1417d2927a78cdbe621090b6e8a5.tar.gz
Renamed package itself
Diffstat (limited to 'paste/tests')
-rw-r--r--paste/tests/__init__.py1
-rwxr-xr-xpaste/tests/doctest_webapp.py403
-rwxr-xr-xpaste/tests/echotest.py109
-rw-r--r--paste/tests/fixture.py307
-rw-r--r--paste/tests/pyconfig_data/context.py8
-rw-r--r--paste/tests/pyconfig_data/deriv.conf3
-rw-r--r--paste/tests/pyconfig_data/nest1.conf3
-rw-r--r--paste/tests/pyconfig_data/nest2.conf2
-rw-r--r--paste/tests/pyconfig_data/one.py2
-rw-r--r--paste/tests/test_authentication.py76
-rw-r--r--paste/tests/test_error_middleware.py63
-rw-r--r--paste/tests/test_pyconfig.py46
-rw-r--r--paste/tests/test_urlparser.py83
-rw-r--r--paste/tests/urlparser_data/__init__.py1
-rw-r--r--paste/tests/urlparser_data/deep/index.html1
-rw-r--r--paste/tests/urlparser_data/deep/sub/Main.txt1
-rw-r--r--paste/tests/urlparser_data/find_file/index.txt1
-rw-r--r--paste/tests/urlparser_data/find_file/test2.html1
-rw-r--r--paste/tests/urlparser_data/hook/__init__.py10
-rw-r--r--paste/tests/urlparser_data/hook/app.py5
-rw-r--r--paste/tests/urlparser_data/hook/index.py4
-rw-r--r--paste/tests/urlparser_data/not_found/__init__.py1
-rw-r--r--paste/tests/urlparser_data/not_found/recur/__init__.py9
-rw-r--r--paste/tests/urlparser_data/not_found/recur/isfound.txt1
-rw-r--r--paste/tests/urlparser_data/not_found/simple/__init__.py3
-rw-r--r--paste/tests/urlparser_data/not_found/simple/found.txt1
-rw-r--r--paste/tests/urlparser_data/not_found/user/__init__.py12
-rw-r--r--paste/tests/urlparser_data/not_found/user/list.py3
-rw-r--r--paste/tests/urlparser_data/python/__init__.py1
-rw-r--r--paste/tests/urlparser_data/python/simpleapp.py6
-rw-r--r--paste/tests/urlparser_data/python/stream.py7
-rw-r--r--paste/tests/urlparser_data/python/sub/__init__.py1
-rw-r--r--paste/tests/urlparser_data/python/sub/simpleapp.py6
33 files changed, 1181 insertions, 0 deletions
diff --git a/paste/tests/__init__.py b/paste/tests/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/paste/tests/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/paste/tests/doctest_webapp.py b/paste/tests/doctest_webapp.py
new file mode 100755
index 0000000..b0b786c
--- /dev/null
+++ b/paste/tests/doctest_webapp.py
@@ -0,0 +1,403 @@
+#!/usr/bin/env python2.4
+"""
+These are functions for use when doctest-testing a document.
+"""
+
+import subprocess
+import doctest
+import os
+import sys
+import shutil
+import re
+import cgi
+import rfc822
+from cStringIO import StringIO
+from paste import server
+from paste import wsgilib
+from paste.util.thirdparty import add_package
+add_package('PySourceColor')
+import PySourceColor
+
+
+here = os.path.abspath(__file__)
+paste_parent = os.path.dirname(
+ os.path.dirname(os.path.dirname(here)))
+
+def run(command):
+ """
+ Runs the string command, prints any output.
+ """
+ proc = subprocess.Popen(command, shell=True,
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE, env=_make_env())
+ data = proc.stdout.read()
+ proc.wait()
+ while data.endswith('\n') or data.endswith('\r'):
+ data = data[:-1]
+ if data:
+ data = '\n'.join(
+ [l for l in data.splitlines() if l])
+ print data
+
+def _make_env():
+ env = os.environ.copy()
+ env['PATH'] = (env.get('PATH', '')
+ + ':'
+ + os.path.join(paste_parent, 'scripts')
+ + ':'
+ + os.path.join(paste_parent, 'paste', '3rd-party',
+ 'sqlobject-files', 'scripts'))
+ env['PYTHONPATH'] = (env.get('PYTHONPATH', '')
+ + ':'
+ + paste_parent)
+ return env
+
+def clear_dir(dir):
+ """
+ Clears (deletes) the given directory
+ """
+ shutil.rmtree(dir, True)
+
+def ls(dir=None, recurse=False, indent=0):
+ """
+ Show a directory listing
+ """
+ dir = dir or os.getcwd()
+ fns = os.listdir(dir)
+ fns.sort()
+ for fn in fns:
+ full = os.path.join(dir, fn)
+ if os.path.isdir(full):
+ fn = fn + '/'
+ print ' '*indent + fn
+ if os.path.isdir(full) and recurse:
+ ls(dir=full, recurse=True, indent=indent+2)
+
+def make_app(dir):
+ os.chdir(dir)
+ sys.path.append(os.path.dirname(dir))
+ conf, app = server.load_commandline((), allow_reload=False)
+ assert conf is not None, (
+ "server.load_commandline requested exit with code %r"
+ % app)
+ return app
+
+default_app = None
+default_url = None
+
+def set_default_app(app, url):
+ global default_app
+ global default_url
+ default_app = app
+ default_url = url
+
+def resource_filename(fn):
+ """
+ Returns the filename of the resource -- generally in the directory
+ resources/DocumentName/fn
+ """
+ return os.path.join(
+ os.path.dirname(sys.testing_document_filename),
+ 'resources',
+ os.path.splitext(os.path.basename(sys.testing_document_filename))[0],
+ fn)
+
+def show(path_info, example_name):
+ fn = resource_filename(example_name + '.html')
+ out = StringIO()
+ assert default_app is not None, (
+ "No default_app set")
+ url = default_url + path_info
+ out.write('<span class="doctest-url"><a href="%s">%s</a></span><br>\n'
+ % (url, url))
+ out.write('<div class="doctest-example">\n')
+ proc = subprocess.Popen(
+ ['wsgi-server', '--server=console', '--no-verbose',
+ '--url=' + path_info],
+ stderr=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ env=_make_env())
+ stdout, errors = proc.communicate()
+ stdout = StringIO(stdout)
+ headers = rfc822.Message(stdout)
+ content = stdout.read()
+ for header, value in headers.items():
+ if header.lower() == 'status' and int(value.split()[0]) == 200:
+ continue
+ if header.lower() in ('content-type', 'content-length'):
+ continue
+ if (header.lower() == 'set-cookie'
+ and value.startswith('_SID_')):
+ continue
+ out.write('<span class="doctest-header">%s: %s</span><br>\n'
+ % (header, value))
+ lines = [l for l in content.splitlines() if l.strip()]
+ for line in lines:
+ out.write(line + '\n')
+ if errors:
+ out.write('<pre class="doctest-errors">%s</pre>'
+ % errors)
+ out.write('</div>\n')
+ result = out.getvalue()
+ if not os.path.exists(fn):
+ f = open(fn, 'wb')
+ f.write(result)
+ f.close()
+ else:
+ f = open(fn, 'rb')
+ expected = f.read()
+ f.close()
+ if not html_matches(expected, result):
+ print 'Pages did not match. Expected from %s:' % fn
+ print '-'*60
+ print expected
+ print '='*60
+ print 'Actual output:'
+ print '-'*60
+ print result
+
+def html_matches(pattern, text):
+ return True
+ regex = re.escape(pattern)
+ regex = regex.replace(r'\.\.\.', '.*')
+ regex = re.sub(r'0x[0-9a-f]+', '.*', regex)
+ regex = '^%s$' % regex
+ return re.search(regex, text)
+
+def create_file(path, version, data):
+ if data.startswith('\n'):
+ data = data[1:]
+ lines = data.splitlines()
+ new_lines = []
+ for line in lines:
+ if line.rstrip() == '.':
+ new_lines.append('')
+ else:
+ new_lines.append(line)
+ data = '\n'.join(new_lines) + '\n'
+ write_data(path, data)
+ show_file(path, version)
+
+def show_file(path, version):
+ ext = os.path.splitext(path)[1]
+ f = open(path, 'rb')
+ data = f.read()
+ f.close()
+ if ext == '.py':
+ html = ('<div class="source-code">%s</div>'
+ % PySourceColor.str2html(data, PySourceColor.dark))
+ else:
+ html = '<pre class="source-code">%s</pre>' % cgi.escape(data, 1)
+ html = '<span class="source-filename">%s</span><br>%s' % (
+ path, html)
+ write_data(resource_filename('%s.%s.gen.html' % (path, version)),
+ html)
+
+def call_source_highlight(input, format):
+ proc = subprocess.Popen(['source-highlight', '--out-format=html',
+ '--no-doc', '--css=none',
+ '--src-lang=%s' % format], shell=False,
+ stdout=subprocess.PIPE)
+ stdout, stderr = proc.communicate(input)
+ result = stdout
+ proc.wait()
+ return result
+
+
+def write_data(path, data):
+ dir = os.path.dirname(os.path.abspath(path))
+ if not os.path.exists(dir):
+ os.makedirs(dir)
+ f = open(path, 'wb')
+ f.write(data)
+ f.close()
+
+
+def change_file(path, changes):
+ f = open(path, 'rb')
+ lines = f.readlines()
+ f.close()
+ for change_type, line, text in changes:
+ if change_type == 'insert':
+ lines[line:line] = [text]
+ elif change_type == 'delete':
+ lines[line:text] = []
+ else:
+ assert 0, (
+ "Unknown change_type: %r" % change_type)
+ f = open(path, 'wb')
+ f.write(''.join(lines))
+ f.close()
+
+class LongFormDocTestParser(doctest.DocTestParser):
+
+ """
+ This parser recognizes some reST comments as commands, without
+ prompts or expected output, like:
+
+ .. run:
+
+ do_this(...
+ ...)
+ """
+
+ _EXAMPLE_RE = re.compile(r"""
+ # Source consists of a PS1 line followed by zero or more PS2 lines.
+ (?: (?P<source>
+ (?:^(?P<indent> [ ]*) >>> .*) # PS1 line
+ (?:\n [ ]* \.\.\. .*)*) # PS2 lines
+ \n?
+ # Want consists of any non-blank lines that do not start with PS1.
+ (?P<want> (?:(?![ ]*$) # Not a blank line
+ (?![ ]*>>>) # Not a line starting with PS1
+ .*$\n? # But any other line
+ )*))
+ |
+ (?: # This is for longer commands that are prefixed with a reST
+ # comment like '.. run:' (two colons makes that a directive).
+ # These commands cannot have any output.
+
+ (?:^\.\.[ ]*(?P<run>run):[ ]*\n) # Leading command/command
+ (?:[ ]*\n)? # Blank line following
+ (?P<runsource>
+ (?:(?P<runindent> [ ]+)[^ ].*$)
+ (?:\n [ ]+ .*)*)
+ )
+ |
+ (?: # This is for shell commands
+
+ (?P<shellsource>
+ (?:^(P<shellindent> [ ]*) [$] .*) # Shell line
+ (?:\n [ ]* [>] .*)*) # Continuation
+ \n?
+ # Want consists of any non-blank lines that do not start with $
+ (?P<shellwant> (?:(?![ ]*$)
+ (?![ ]*[$]$)
+ .*$\n?
+ )*))
+ """, re.MULTILINE | re.VERBOSE)
+
+ def _parse_example(self, m, name, lineno):
+ r"""
+ Given a regular expression match from `_EXAMPLE_RE` (`m`),
+ return a pair `(source, want)`, where `source` is the matched
+ example's source code (with prompts and indentation stripped);
+ and `want` is the example's expected output (with indentation
+ stripped).
+
+ `name` is the string's name, and `lineno` is the line number
+ where the example starts; both are used for error messages.
+
+ >>> def parseit(s):
+ ... p = LongFormDocTestParser()
+ ... return p._parse_example(p._EXAMPLE_RE.search(s), '<string>', 1)
+ >>> parseit('>>> 1\n1')
+ ('1', {}, '1', None)
+ >>> parseit('>>> (1\n... +1)\n2')
+ ('(1\n+1)', {}, '2', None)
+ >>> parseit('.. run:\n\n test1\n test2\n')
+ ('test1\ntest2', {}, '', None)
+ """
+ # Get the example's indentation level.
+ runner = m.group('run') or ''
+ indent = len(m.group('%sindent' % runner))
+
+ # Divide source into lines; check that they're properly
+ # indented; and then strip their indentation & prompts.
+ source_lines = m.group('%ssource' % runner).split('\n')
+ if runner:
+ self._check_prefix(source_lines[1:], ' '*indent, name, lineno)
+ else:
+ self._check_prompt_blank(source_lines, indent, name, lineno)
+ self._check_prefix(source_lines[2:], ' '*indent + '.', name, lineno)
+ if runner:
+ source = '\n'.join([sl[indent:] for sl in source_lines])
+ else:
+ source = '\n'.join([sl[indent+4:] for sl in source_lines])
+
+ if runner:
+ want = ''
+ exc_msg = None
+ else:
+ # Divide want into lines; check that it's properly indented; and
+ # then strip the indentation. Spaces before the last newline should
+ # be preserved, so plain rstrip() isn't good enough.
+ want = m.group('want')
+ want_lines = want.split('\n')
+ if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
+ del want_lines[-1] # forget final newline & spaces after it
+ self._check_prefix(want_lines, ' '*indent, name,
+ lineno + len(source_lines))
+ want = '\n'.join([wl[indent:] for wl in want_lines])
+
+ # If `want` contains a traceback message, then extract it.
+ m = self._EXCEPTION_RE.match(want)
+ if m:
+ exc_msg = m.group('msg')
+ else:
+ exc_msg = None
+
+ # Extract options from the source.
+ options = self._find_options(source, name, lineno)
+
+ return source, options, want, exc_msg
+
+
+ def parse(self, string, name='<string>'):
+ """
+ Divide the given string into examples and intervening text,
+ and return them as a list of alternating Examples and strings.
+ Line numbers for the Examples are 0-based. The optional
+ argument `name` is a name identifying this string, and is only
+ used for error messages.
+ """
+ string = string.expandtabs()
+ # If all lines begin with the same indentation, then strip it.
+ min_indent = self._min_indent(string)
+ if min_indent > 0:
+ string = '\n'.join([l[min_indent:] for l in string.split('\n')])
+
+ output = []
+ charno, lineno = 0, 0
+ # Find all doctest examples in the string:
+ for m in self._EXAMPLE_RE.finditer(string):
+ # Add the pre-example text to `output`.
+ output.append(string[charno:m.start()])
+ # Update lineno (lines before this example)
+ lineno += string.count('\n', charno, m.start())
+ # Extract info from the regexp match.
+ (source, options, want, exc_msg) = \
+ self._parse_example(m, name, lineno)
+ # Create an Example, and add it to the list.
+ if not self._IS_BLANK_OR_COMMENT(source):
+ # @@: Erg, this is the only line I need to change...
+ output.append( doctest.Example(source, want, exc_msg,
+ lineno=lineno,
+ indent=min_indent+len(m.group('indent') or m.group('runindent')),
+ options=options) )
+ # Update lineno (lines inside this example)
+ lineno += string.count('\n', m.start(), m.end())
+ # Update charno.
+ charno = m.end()
+ # Add any remaining post-example text to `output`.
+ output.append(string[charno:])
+ return output
+
+
+
+if __name__ == '__main__':
+ import sys
+ if sys.argv[1:] and sys.argv[1] == 'doctest':
+ doctest.testmod()
+ sys.exit()
+ if not paste_parent in sys.path:
+ sys.path.append(paste_parent)
+ for fn in sys.argv[1:]:
+ fn = os.path.abspath(fn)
+ # @@: OK, ick; but this module gets loaded twice
+ sys.testing_document_filename = fn
+ doctest.testfile(fn, module_relative=False,
+ parser=LongFormDocTestParser())
+ new = os.path.splitext(fn)[0] + '.html'
+ assert new != fn
+ os.system('rest2html %s > %s' % (fn, new))
diff --git a/paste/tests/echotest.py b/paste/tests/echotest.py
new file mode 100755
index 0000000..885d571
--- /dev/null
+++ b/paste/tests/echotest.py
@@ -0,0 +1,109 @@
+"""
+Tests a WSGI stack, using urllib. Queries the echo application.
+"""
+
+import unittest
+import urlparse
+import urllib
+import os
+import sys
+
+class EchoTest(unittest.TestCase):
+
+ def url(self):
+ if not os.environ.get('ECHO_URL'):
+ print 'You must set $ECHO_URL'
+ sys.exit(1)
+ url = URL(os.environ['ECHO_URL'])
+ return url
+
+class TestEnviron(EchoTest):
+
+ def setUp(self):
+ self.page = self.url().fetch(environ='true')
+ self.environ = parse_environ(self.page)
+
+ def testRequiredKeys(self):
+ environ = self.environ
+ url = self.url()
+ required_keys = 'REQUEST_METHOD SCRIPT_NAME PATH_INFO QUERY_STRING SERVER_NAME SERVER_PORT wsgi.errors wsgi.input wsgi.multiprocess wsgi.multithread wsgi.version'
+ for key in required_keys.split():
+ assert environ.has_key(key), "Key %r missing from %r" % (key, environ)
+ self.assertEqual(environ['PATH_INFO'], '')
+ self.assertEqual(environ['SCRIPT_NAME'], url.path)
+ self.assertEqual(environ['REQUEST_METHOD'], 'GET')
+ self.assertEqual(environ['QUERY_STRING'], 'environ=true')
+ self.assertEqual(environ['SERVER_PORT'], str(url.port))
+ self.assertEqual(environ['SERVER_NAME'], url.host)
+ self.assertEqual(environ['HTTP_HOST'], url.location)
+ assert environ['HTTP_USER_AGENT'].startswith('Python-urllib/'), \
+ "HTTP_USER_AGENT should start with 'Python-urllib/': %r" % environ['HTTP_USER_AGENT']
+
+ def testPathInfo(self):
+ sub = self.url() / ''
+ environ = parse_environ(sub.fetch(environ='true'))
+ self.assertEqual(environ['PATH_INFO'], '/')
+ self.assertEqual(environ['SCRIPT_NAME'], self.url().path)
+ sub = self.url() / 'test'
+ environ = parse_environ(sub.fetch(environ='true'))
+ self.assertEqual(environ['PATH_INFO'], '/test')
+ self.assertEqual(environ['SCRIPT_NAME'], self.url().path)
+
+ def test_message(self):
+ data = self.url().fetch(message='test')
+ self.assertEqual(data, 'test')
+ data = self.url().fetch(message='')
+ self.assertEqual(data, '')
+
+############################################################
+## Utility functions
+############################################################
+
+def parse_environ(page):
+ """
+ Parses the environment that echo prints (not perfect, but good
+ enough).
+ """
+ environ = {}
+ for line in page.splitlines():
+ if '=' not in line:
+ # ignore second line of long lines
+ continue
+ name, value = line.split('=', 1)
+ environ[name] = value
+ return environ
+
+class URL:
+
+ def __init__(self, url_string):
+ self.url_string = url_string
+ (self.scheme, self.location, self.path, self.query,
+ self.fragment) = urlparse.urlsplit(url_string)
+ if ':' in self.location:
+ self.host, self.port = location.split(':', 1)
+ else:
+ self.host = self.location
+ if self.scheme == 'http':
+ self.port = '80'
+ elif self.scheme == 'https':
+ self.port = '443'
+ else:
+ assert 0, "Unknown scheme: %r" % scheme
+ self.port = int(self.port)
+
+ def fetch(self, **kw):
+ query = '&'.join(['%s=%s' % (urllib.quote(k), urllib.quote(v))
+ for k, v in kw.items()])
+ url = self.url_string
+ if query:
+ url += '?' + query
+ f = urllib.urlopen(url)
+ page = f.read()
+ f.close()
+ return page
+
+ def __div__(self, path_part):
+ return self.__class__(self.url_string + '/' + path_part)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/paste/tests/fixture.py b/paste/tests/fixture.py
new file mode 100644
index 0000000..b69c88c
--- /dev/null
+++ b/paste/tests/fixture.py
@@ -0,0 +1,307 @@
+import sys
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+import types
+import re
+from py.test.collect import Module, PyCollector
+from paste.util import thirdparty
+doctest = thirdparty.load_new_module('doctest', (2, 4))
+from paste import wsgilib
+from paste import lint
+
+class NoDefault:
+ pass
+
+class Dummy(object):
+
+ def __init__(self, **kw):
+ for name, value in kw.items():
+ if name.startswith('method_'):
+ name = name[len('method_'):]
+ value = DummyMethod(value)
+ setattr(self, name, value)
+
+class DummyMethod(object):
+
+ def __init__(self, return_value):
+ self.return_value = return_value
+
+ def __call__(self, *args, **kw):
+ return self.return_value
+
+class ParamCollector(PyCollector):
+
+ def collect_function(self, extpy):
+ if not extpy.check(func=1, basestarts='test_'):
+ return
+ func = extpy.resolve()
+ if hasattr(func, 'params'):
+ params = func.params
+ for i, param in enumerate(params):
+ item = self.Item(extpy, *param)
+ item.name = item.name + '.%i' % i
+ yield item
+ else:
+ yield self.Item(extpy)
+
+class DoctestCollector(PyCollector):
+
+ def __init__(self, extpy_or_module):
+ if isinstance(extpy_or_module, types.ModuleType):
+ self.module = extpy_or_module
+ self.extpy = None
+ else:
+ self.extpy = extpy_or_module
+ self.module = self.extpy.getpymodule()
+
+ def __call__(self, extpy):
+ # we throw it away, because this has been set up to explicitly
+ # check another module; maybe this isn't clean
+ if self.extpy is None:
+ self.extpy = extpy
+ return self
+
+ def __iter__(self):
+ finder = doctest.DocTestFinder()
+ tests = finder.find(self.module)
+ for t in tests:
+ yield DoctestItem(self.extpy, t)
+
+class DoctestItem(DoctestCollector.Item):
+
+ def __init__(self, extpy, doctestitem, *args):
+ self.extpy = extpy
+ self.doctestitem = doctestitem
+ self.name = extpy.basename
+ self.args = args
+
+ def execute(self, driver):
+ runner = doctest.DocTestRunner()
+ driver.setup_path(self.extpy)
+ target, teardown = driver.setup_method(self.extpy)
+ try:
+ (failed, tried), run_output = capture_stdout(runner.run, self.doctestitem)
+ if failed:
+ raise self.Failed(msg=run_output, tbindex=-2)
+
+ finally:
+ if teardown:
+ teardown(target)
+
+def capture_stdout(func, *args, **kw):
+ newstdout = StringIO()
+ oldstdout = sys.stdout
+ sys.stdout = newstdout
+ try:
+ result = func(*args, **kw)
+ finally:
+ sys.stdout = oldstdout
+ return result, newstdout.getvalue()
+
+def assert_error(func, *args, **kw):
+ kw.setdefault('error', Exception)
+ kw.setdefault('text_re', None)
+ error = kw.pop('error')
+ text_re = kw.pop('text_re')
+ if text_re and isinstance(text_re, str):
+ real_text_re = re.compile(text_re, re.S)
+ else:
+ real_text_re = text_re
+ try:
+ value = func(*args, **kw)
+ except error, e:
+ if real_text_re and not real_text_re.search(str(e)):
+ assert False, (
+ "Exception did not match pattern; exception:\n %r;\n"
+ "pattern:\n %r"
+ % (str(e), text_re))
+ except Exception, e:
+ assert False, (
+ "Exception type %s should have been raised; got %s instead (%s)"
+ % (error, e.__class__, e))
+ else:
+ assert False, (
+ "Exception was expected, instead successfully returned %r"
+ % (value))
+
+def sorted(l):
+ l = list(l)
+ l.sort()
+ return l
+
+
+def fake_request(application, path_info='', use_lint=True, **environ):
+ """
+ Runs the application in a fake environment, returning a response object
+ """
+ if use_lint:
+ application = lint.middleware(application)
+ status, headers, body, errors = wsgilib.raw_interactive(
+ application, path_info, **environ)
+ res = FakeResponse(status, headers, body, errors)
+ if res.errors:
+ print 'Errors:'
+ print res.errors
+ return res
+
+class FakeResponse(object):
+
+ def __init__(self, status, headers, body, errors):
+ self.status = status
+ self.headers = headers
+ self.body = body
+ self.errors = errors
+
+ def status_int__get(self):
+ return int(self.status.split()[0])
+ status_int = property(status_int__get)
+
+ def all_ok(self):
+ """
+ Asserts that there were no errors and the status was 200 OK
+ """
+ assert not self.errors, (
+ "Response had errors: %s" % self.errors)
+ assert self.status_int == 200, (
+ "Response did not return 200 OK: %r" % self.status)
+
+ def header(self, name, default=NoDefault):
+ """
+ Returns the named header; an error if there is not exactly one
+ matching header (unless you give a default -- always an error if
+ there is more than one header)
+ """
+ found = None
+ for cur_name, value in self.headers:
+ if cur_name.lower() == name.lower():
+ assert not found, (
+ "Ambiguous header: %s matches %r and %r"
+ % (name, found, value))
+ found = value
+ if found is None:
+ if default is NoDefault:
+ raise KeyError(
+ "No header found: %r (from %s)"
+ % (name, ', '.join([n for n, v in self.headers])))
+ else:
+ return default
+ return found
+
+ def all_headers(self, name):
+ """
+ Gets all headers, returns as a list
+ """
+ found = []
+ for cur_name, value in self.headers:
+ if cur_name.lower() == name.lower():
+ found.append(value)
+ return found
+
+ def __contains__(self, s):
+ return self.body.find(s) != -1
+
+ def __repr__(self):
+ return '<Response %s %r>' % (self.status, self.body[:20])
+
+ def __str__(self):
+ return 'Response: %s\n%s\n%s' % (
+ self.status,
+ '\n'.join(['%s: %s' % (n, v) for n, v in self.headers]),
+ self.body)
+
+class Dummy_smtplib(object):
+
+ existing = None
+
+ def __init__(self, server):
+ assert not self.existing, (
+ "smtplib.SMTP() called again before Dummy_smtplib.existing.reset() "
+ "called.")
+ self.server = server
+ self.open = True
+ self.__class__.existing = self
+
+ def quit(self):
+ assert self.open, (
+ "Called %s.quit() twice" % self)
+ self.open = False
+
+ def sendmail(self, from_address, to_addresses, msg):
+ self.from_address = from_address
+ self.to_addresses = to_addresses
+ self.message = msg
+
+ def install(cls):
+ smtplib.SMTP = cls
+
+ install = classmethod(install)
+
+ def reset(self):
+ assert not self.open, (
+ "SMTP connection not quit")
+ self.__class__.existing = None
+
+class FakeFilesystem(object):
+
+ def __init__(self):
+ self.files = {}
+
+ def make_file(self, filename, content):
+ self.files[filename] = content
+
+ def open(self, filename, mode='r'):
+ if not self.files.has_key(filename):
+ raise IOError("[FakeFS] No such file or directory: %r" % filename)
+
+
+class FakeFile(object):
+
+ def __init__(self, filename, content=None):
+ self.filename = filename
+ self.content = content
+
+ def open(self, mode):
+ if mode == 'r' or mode == 'rb':
+ if self.content is None:
+ raise IOError("[FakeFS] No such file or directory: %r" % filename)
+ return ReaderFile(self)
+ elif mode == 'w' or mode == 'wb':
+ return WriterFile(self)
+ else:
+ assert 0, "Mode %r not yet implemented" % mode
+
+class ReaderFile(object):
+
+ def __init__(self, file):
+ self.file = file
+ self.stream = StringIO(self.file.content)
+ self.open = True
+
+ def read(self, *args):
+ return self.stream.read(*args)
+
+ def close(self):
+ assert self.open, (
+ "Closing open file")
+ self.open = False
+
+class WriterFile(object):
+
+ def __init__(self, file):
+ self.file = file
+ self.stream = StringIO()
+ self.open = True
+
+ def write(self, arg):
+ self.stream.write(arg)
+
+ def close(self):
+ assert self.open, (
+ "Closing an open file")
+ self.open = False
+
+
+
+
diff --git a/paste/tests/pyconfig_data/context.py b/paste/tests/pyconfig_data/context.py
new file mode 100644
index 0000000..73fa626
--- /dev/null
+++ b/paste/tests/pyconfig_data/context.py
@@ -0,0 +1,8 @@
+def simplehook():
+ return 'calc'
+simplehook.config_hook = True
+
+def complexhook(context):
+ return context
+complexhook.config_hook = True
+
diff --git a/paste/tests/pyconfig_data/deriv.conf b/paste/tests/pyconfig_data/deriv.conf
new file mode 100644
index 0000000..c7cdf3c
--- /dev/null
+++ b/paste/tests/pyconfig_data/deriv.conf
@@ -0,0 +1,3 @@
+import os
+
+test1 = test1 + '+another'
diff --git a/paste/tests/pyconfig_data/nest1.conf b/paste/tests/pyconfig_data/nest1.conf
new file mode 100644
index 0000000..4eb06c6
--- /dev/null
+++ b/paste/tests/pyconfig_data/nest1.conf
@@ -0,0 +1,3 @@
+a = 1
+b = 'shadow'
+
diff --git a/paste/tests/pyconfig_data/nest2.conf b/paste/tests/pyconfig_data/nest2.conf
new file mode 100644
index 0000000..c70b217
--- /dev/null
+++ b/paste/tests/pyconfig_data/nest2.conf
@@ -0,0 +1,2 @@
+b = 2
+c = 3
diff --git a/paste/tests/pyconfig_data/one.py b/paste/tests/pyconfig_data/one.py
new file mode 100644
index 0000000..e03cf8b
--- /dev/null
+++ b/paste/tests/pyconfig_data/one.py
@@ -0,0 +1,2 @@
+name1 = 'n1'
+name2 = 'n2'
diff --git a/paste/tests/test_authentication.py b/paste/tests/test_authentication.py
new file mode 100644
index 0000000..f26e5a4
--- /dev/null
+++ b/paste/tests/test_authentication.py
@@ -0,0 +1,76 @@
+from paste import wsgilib
+from paste import login
+from fixture import *
+
+from_cmdline = 0
+
+def application(environ, start_response):
+ if environ.has_key('REMOTE_USER'):
+ start_response('200 OK', [('Content-type', 'text/plain')])
+ return ['Logged in: ' + environ['REMOTE_USER']]
+ else:
+ start_response('401 Unauthorized',
+ [('Content-type', 'text/plain')])
+ return ['Not logged in.']
+
+class AuthTest(login.Authenticator):
+ def check_auth(self, username, password):
+ return username == password
+
+def report(res):
+ if from_cmdline:
+ print res
+
+# @@ this should be part of a test fixture, I think
+def mk_basic_auth_app(**kw):
+ kw['http_login'] = True
+ kw['authenticator'] = AuthTest
+ app = login.middleware(application, **kw)
+ return app
+
+def test_basicauth_noauth():
+ res = fake_request(mk_basic_auth_app(), '/')
+ assert res.status_int == 401
+ report(res)
+
+def run_userpass(user, password):
+ userpass = user + ':' + password
+ env = {'HTTP_AUTHORIZATION' : 'Basic ' + userpass.encode('base64')}
+ return fake_request(mk_basic_auth_app(), '/', **env)
+
+def test_basicauth_okuser():
+ res = run_userpass('test', 'test') # should succeed
+ assert res.status_int == 200
+ report(res)
+
+def test_basicauth_baduser():
+ res = run_userpass('test', 'badpass') # should succeed
+ assert res.status_int == 401
+ report(res)
+
+def test_basicauth_cookie():
+ res = run_userpass('test', 'test') # should succeed
+ assert res.status_int == 200
+ report(res)
+ cookie_val = res.header('SET-COOKIE')
+ print "cookie value", cookie_val
+ app = mk_basic_auth_app()
+ env = {'HTTP_COOKIE': cookie_val}
+ res = fake_request(mk_basic_auth_app(), '/', **env)
+ report(res)
+ assert res.status_int == 200
+
+ # ensure that secret is actually used
+ res = fake_request(mk_basic_auth_app(secret='bogus'),
+ '/', **env)
+ report(res)
+ assert res.status_int == 401
+
+if __name__ == '__main__':
+ from_cmdline = 1
+ test_basicauth_noauth()
+ test_basicauth_okuser()
+ test_basicauth_baduser()
+ test_basicauth_cookie()
+
+
diff --git a/paste/tests/test_error_middleware.py b/paste/tests/test_error_middleware.py
new file mode 100644
index 0000000..f7731ae
--- /dev/null
+++ b/paste/tests/test_error_middleware.py
@@ -0,0 +1,63 @@
+from fixture import *
+from paste.error_middleware import ErrorMiddleware
+from paste import lint
+
+def do_request(app, expect_status=500):
+ res = fake_request(ErrorMiddleware(lint.middleware(app)),
+ **{'paste.config': {'debug': True}})
+ assert res.status_int == expect_status
+ return res
+
+def bad_app():
+ "No argument list!"
+ return None
+
+def start_response_app(environ, start_response):
+ "raise error before start_response"
+ raise ValueError("hi")
+
+def after_start_response_app(environ, start_response):
+ start_response("200 OK", [('Content-type', 'text/plain')])
+ raise ValueError('error2')
+
+def iter_app(environ, start_response):
+ start_response("200 OK", [('Content-type', 'text/plain')])
+ return yielder(['this', ' is ', ' a', None])
+
+def yielder(args):
+ for arg in args:
+ if arg is None:
+ raise ValueError("None raises error")
+ yield arg
+
+def test_makes_exception():
+ res = do_request(bad_app)
+ print res
+ assert '<html' in res
+ assert 'bad_app() takes no arguments (2 given' in res
+ assert 'iterator = application(environ, start_response_wrapper)' in res
+ assert 'lint.py' in res
+ assert 'error_middleware.py' in res
+
+def test_start_res():
+ res = do_request(start_response_app)
+ print res
+ assert 'ValueError: hi' in res
+ assert 'test_error_middleware.py' in res
+ assert 'line 17 in <tt>start_response_app</tt>' in res
+
+def test_after_start():
+ res = do_request(after_start_response_app, 200)
+ print res
+ assert 'ValueError: error2' in res
+ assert 'line 21' in res
+
+def test_iter_app():
+ res = do_request(iter_app, 200)
+ print res
+ assert 'None raises error' in res
+ assert 'yielder' in res
+
+
+
+
diff --git a/paste/tests/test_pyconfig.py b/paste/tests/test_pyconfig.py
new file mode 100644
index 0000000..1b93d02
--- /dev/null
+++ b/paste/tests/test_pyconfig.py
@@ -0,0 +1,46 @@
+import os
+from wsgikit import pyconfig
+from py.test import raises
+
+def path(name):
+ return os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ 'pyconfig_data', name)
+
+def test_load():
+ conf = pyconfig.load(path('one.py'))
+ assert conf['name1'] == 'n1'
+ assert conf['name2'] == "n2"
+ raises(KeyError, "conf['name3']")
+
+def test_nest():
+ conf = pyconfig.load(path('nest1.conf'))
+ conf.load(path('nest2.conf'))
+ assert conf['a'] == 1
+ assert conf['b'] == 2
+ assert conf['c'] == 3
+
+def test_derivative():
+ conf = pyconfig.Config()
+ conf.load_dict({'test1': 'a'})
+ assert conf['test1'] == 'a'
+ conf.load(path('deriv.conf'))
+ assert conf['test1'] == 'a+another'
+ conf = pyconfig.Config()
+ conf.load_dict({'test1': 'b'})
+ conf.load(path('deriv.conf'))
+ assert conf['test1'] == 'b+another'
+ assert not conf.has_key('os')
+
+def test_command():
+ conf = pyconfig.load(path('one.py'))
+ extra = conf.load_commandline(
+ ['-h', '--host', 'localhost', '--port=8080', 'arg1', '-f', 'arg2'],
+ bool_options=['help', 'verbose'],
+ aliases={'f': 'config_file', 'h': 'help', 'v': 'verbose'})
+ assert extra == ['arg1']
+ assert conf['name1'] == 'n1'
+ assert conf['host'] == 'localhost'
+ assert conf['port'] == 8080
+ assert conf['config_file'] == 'arg2'
+ raises(KeyError, "conf['h']")
+ raises(KeyError, "conf['f']")
diff --git a/paste/tests/test_urlparser.py b/paste/tests/test_urlparser.py
new file mode 100644
index 0000000..b41ecfa
--- /dev/null
+++ b/paste/tests/test_urlparser.py
@@ -0,0 +1,83 @@
+from paste.urlparser import *
+from fixture import fake_request
+
+
+def path(name):
+ return os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ 'urlparser_data', name)
+
+def make_parser(name):
+ return URLParser(path(name), name, {'index_names': ['index', 'Main']})
+
+def test_find_file():
+ p = make_parser('find_file')
+ res = fake_request(p, '/')
+ assert 'index1' in res
+ assert res.header('content-type') == 'text/plain'
+ res = fake_request(p, '/index')
+ assert 'index1' in res
+ assert res.header('content-type') == 'text/plain'
+ res = fake_request(p, '/index.txt')
+ assert 'index1' in res
+ assert res.header('content-type') == 'text/plain'
+ res = fake_request(p, '/test2.html')
+ assert 'test2' in res
+ assert res.header('content-type') == 'text/html'
+
+def test_deep():
+ p = make_parser('deep')
+ res = fake_request(p, '/')
+ assert 'index2' in res
+ res = fake_request(p, '/sub')
+ assert res.status_int == 301
+ print res
+ assert res.header('location') == 'http://localhost/sub/'
+ assert 'href="http://localhost/sub/"' in res
+ res = fake_request(p, '/sub/')
+ assert 'index3' in res
+
+def test_python():
+ p = make_parser('python')
+ res = fake_request(p, '/simpleapp')
+ res.all_ok()
+ assert 'test1' in res
+ assert res.header('test-header') == 'TEST!'
+ assert res.header('content-type') == 'text/html'
+ res = fake_request(p, '/stream')
+ res.all_ok()
+ assert 'test2' in res
+ res = fake_request(p, '/sub/simpleapp')
+ res.all_ok()
+ assert 'subsimple' in res
+
+def test_hook():
+ p = make_parser('hook')
+ res = fake_request(p, '/bob/app')
+ res.all_ok()
+ assert 'user: bob' in res
+ res = fake_request(p, '/tim/')
+ res.all_ok()
+ assert 'index: tim' in res
+
+def test_not_found_hook():
+ p = make_parser('not_found')
+ res = fake_request(p, '/simple/notfound')
+ assert res.status_int == 200
+ assert 'not found' in res
+ res = fake_request(p, '/simple/found')
+ res.all_ok()
+ assert 'is found' in res
+ res = fake_request(p, '/recur/__notfound')
+ assert res.status_int == 404
+ # @@: It's unfortunate that the original path doesn't actually show up
+ assert '/recur/notfound' in res
+ res = fake_request(p, '/recur/__isfound')
+ assert res.status_int == 200
+ assert 'is found' in res
+ res = fake_request(p, '/user/list')
+ res.all_ok()
+ assert 'user: None' in res
+ res = fake_request(p, '/user/bob/list')
+ assert res.status_int == 200
+ assert 'user: bob' in res
+
diff --git a/paste/tests/urlparser_data/__init__.py b/paste/tests/urlparser_data/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/paste/tests/urlparser_data/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/paste/tests/urlparser_data/deep/index.html b/paste/tests/urlparser_data/deep/index.html
new file mode 100644
index 0000000..8913442
--- /dev/null
+++ b/paste/tests/urlparser_data/deep/index.html
@@ -0,0 +1 @@
+index2
diff --git a/paste/tests/urlparser_data/deep/sub/Main.txt b/paste/tests/urlparser_data/deep/sub/Main.txt
new file mode 100644
index 0000000..ec42756
--- /dev/null
+++ b/paste/tests/urlparser_data/deep/sub/Main.txt
@@ -0,0 +1 @@
+index3
diff --git a/paste/tests/urlparser_data/find_file/index.txt b/paste/tests/urlparser_data/find_file/index.txt
new file mode 100644
index 0000000..6be29bc
--- /dev/null
+++ b/paste/tests/urlparser_data/find_file/index.txt
@@ -0,0 +1 @@
+index1
diff --git a/paste/tests/urlparser_data/find_file/test2.html b/paste/tests/urlparser_data/find_file/test2.html
new file mode 100644
index 0000000..180cf83
--- /dev/null
+++ b/paste/tests/urlparser_data/find_file/test2.html
@@ -0,0 +1 @@
+test2
diff --git a/paste/tests/urlparser_data/hook/__init__.py b/paste/tests/urlparser_data/hook/__init__.py
new file mode 100644
index 0000000..9b1055c
--- /dev/null
+++ b/paste/tests/urlparser_data/hook/__init__.py
@@ -0,0 +1,10 @@
+from paste import wsgilib
+
+def urlparser_hook(environ):
+ first, rest = wsgilib.path_info_split(environ.get('PATH_INFO', ''))
+ if not first:
+ # No username
+ return
+ environ['app.user'] = first
+ environ['SCRIPT_NAME'] += '/' + first
+ environ['PATH_INFO'] = rest
diff --git a/paste/tests/urlparser_data/hook/app.py b/paste/tests/urlparser_data/hook/app.py
new file mode 100644
index 0000000..d2714e5
--- /dev/null
+++ b/paste/tests/urlparser_data/hook/app.py
@@ -0,0 +1,5 @@
+def application(environ, start_response):
+ start_response('200 OK', [('Content-type', 'text/html')])
+ return ['user: %s' % environ['app.user']]
+
+
diff --git a/paste/tests/urlparser_data/hook/index.py b/paste/tests/urlparser_data/hook/index.py
new file mode 100644
index 0000000..49e89f0
--- /dev/null
+++ b/paste/tests/urlparser_data/hook/index.py
@@ -0,0 +1,4 @@
+def application(environ, start_response):
+ start_response('200 OK', [('Content-type', 'text/html')])
+ return ['index: %s' % environ['app.user']]
+
diff --git a/paste/tests/urlparser_data/not_found/__init__.py b/paste/tests/urlparser_data/not_found/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/paste/tests/urlparser_data/not_found/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/paste/tests/urlparser_data/not_found/recur/__init__.py b/paste/tests/urlparser_data/not_found/recur/__init__.py
new file mode 100644
index 0000000..48205a5
--- /dev/null
+++ b/paste/tests/urlparser_data/not_found/recur/__init__.py
@@ -0,0 +1,9 @@
+def not_found_hook(environ, start_response):
+ urlparser = environ['paste.urlparser.not_found_parser']
+ path = environ.get('PATH_INFO', '')
+ if not path:
+ return urlparser.not_found(environ, start_response)
+ # Strip off leading _'s
+ path = '/' + path.lstrip('/').lstrip('_')
+ environ['PATH_INFO'] = path
+ return urlparser(environ, start_response)
diff --git a/paste/tests/urlparser_data/not_found/recur/isfound.txt b/paste/tests/urlparser_data/not_found/recur/isfound.txt
new file mode 100644
index 0000000..c8b8fab
--- /dev/null
+++ b/paste/tests/urlparser_data/not_found/recur/isfound.txt
@@ -0,0 +1 @@
+is found
diff --git a/paste/tests/urlparser_data/not_found/simple/__init__.py b/paste/tests/urlparser_data/not_found/simple/__init__.py
new file mode 100644
index 0000000..f1e7faa
--- /dev/null
+++ b/paste/tests/urlparser_data/not_found/simple/__init__.py
@@ -0,0 +1,3 @@
+def not_found_hook(environ, start_response):
+ start_response('200 OK', [('Content-type', 'text/plain')])
+ return ['not found']
diff --git a/paste/tests/urlparser_data/not_found/simple/found.txt b/paste/tests/urlparser_data/not_found/simple/found.txt
new file mode 100644
index 0000000..c8b8fab
--- /dev/null
+++ b/paste/tests/urlparser_data/not_found/simple/found.txt
@@ -0,0 +1 @@
+is found
diff --git a/paste/tests/urlparser_data/not_found/user/__init__.py b/paste/tests/urlparser_data/not_found/user/__init__.py
new file mode 100644
index 0000000..c47f88e
--- /dev/null
+++ b/paste/tests/urlparser_data/not_found/user/__init__.py
@@ -0,0 +1,12 @@
+from paste import wsgilib
+
+def not_found_hook(environ, start_response):
+ urlparser = environ['paste.urlparser.not_found_parser']
+ first, rest = wsgilib.path_info_split(environ.get('PATH_INFO', ''))
+ if not first:
+ # No username
+ return
+ environ['app.user'] = first
+ environ['SCRIPT_NAME'] += '/' + first
+ environ['PATH_INFO'] = rest
+ return urlparser(environ, start_response)
diff --git a/paste/tests/urlparser_data/not_found/user/list.py b/paste/tests/urlparser_data/not_found/user/list.py
new file mode 100644
index 0000000..f6228f0
--- /dev/null
+++ b/paste/tests/urlparser_data/not_found/user/list.py
@@ -0,0 +1,3 @@
+def application(environ, start_response):
+ start_response('200 OK', [('Content-type', 'text/plain')])
+ return ['user: %s' % environ.get('app.user')]
diff --git a/paste/tests/urlparser_data/python/__init__.py b/paste/tests/urlparser_data/python/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/paste/tests/urlparser_data/python/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/paste/tests/urlparser_data/python/simpleapp.py b/paste/tests/urlparser_data/python/simpleapp.py
new file mode 100644
index 0000000..cbef9f1
--- /dev/null
+++ b/paste/tests/urlparser_data/python/simpleapp.py
@@ -0,0 +1,6 @@
+def application(environ, start_response):
+ start_response('200 OK', [('Content-type', 'text/html'),
+ ('test-header', 'TEST!')])
+ return ['test1']
+
+
diff --git a/paste/tests/urlparser_data/python/stream.py b/paste/tests/urlparser_data/python/stream.py
new file mode 100644
index 0000000..121b4d1
--- /dev/null
+++ b/paste/tests/urlparser_data/python/stream.py
@@ -0,0 +1,7 @@
+def stream():
+ def app(environ, start_response):
+ writer = start_response('200 OK', [('Content-type', 'text/html')])
+ writer('te')
+ writer('st')
+ return ['2']
+ return app
diff --git a/paste/tests/urlparser_data/python/sub/__init__.py b/paste/tests/urlparser_data/python/sub/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/paste/tests/urlparser_data/python/sub/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/paste/tests/urlparser_data/python/sub/simpleapp.py b/paste/tests/urlparser_data/python/sub/simpleapp.py
new file mode 100644
index 0000000..fd90966
--- /dev/null
+++ b/paste/tests/urlparser_data/python/sub/simpleapp.py
@@ -0,0 +1,6 @@
+def application(environ, start_response):
+ start_response('200 OK', [('Content-type', 'text/html'),
+ ('test-header', 'TEST!')])
+ return ['subsimple']
+
+