diff options
author | Marc Abramowitz <marc@marc-abramowitz.com> | 2016-03-07 14:05:52 -0800 |
---|---|---|
committer | Marc Abramowitz <marc@marc-abramowitz.com> | 2016-03-07 14:05:52 -0800 |
commit | 42b22881290e00e06b840dee1e42f0f5ef044d47 (patch) | |
tree | b4fef928625acd3e8ee45ccaa8c7a6c9810b3601 /tests/test_exceptions | |
download | paste-git-tox_add_py35.tar.gz |
tox.ini: Add py35 to envlisttox_add_py35
Diffstat (limited to 'tests/test_exceptions')
-rw-r--r-- | tests/test_exceptions/__init__.py | 1 | ||||
-rw-r--r-- | tests/test_exceptions/test_error_middleware.py | 109 | ||||
-rw-r--r-- | tests/test_exceptions/test_formatter.py | 183 | ||||
-rw-r--r-- | tests/test_exceptions/test_httpexceptions.py | 97 | ||||
-rw-r--r-- | tests/test_exceptions/test_reporter.py | 50 |
5 files changed, 440 insertions, 0 deletions
diff --git a/tests/test_exceptions/__init__.py b/tests/test_exceptions/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/tests/test_exceptions/__init__.py @@ -0,0 +1 @@ +# diff --git a/tests/test_exceptions/test_error_middleware.py b/tests/test_exceptions/test_error_middleware.py new file mode 100644 index 0000000..95ab177 --- /dev/null +++ b/tests/test_exceptions/test_error_middleware.py @@ -0,0 +1,109 @@ +from paste.fixture import * +from paste.exceptions.errormiddleware import ErrorMiddleware +from paste import lint +from paste.util.quoting import strip_html +# +# For some strange reason, these 4 lines cannot be removed or the regression +# test breaks; is it counting the number of lines in the file somehow? +# + +def do_request(app, expect_status=500): + app = lint.middleware(app) + app = ErrorMiddleware(app, {}, debug=True) + app = clear_middleware(app) + testapp = TestApp(app) + res = testapp.get('', status=expect_status, + expect_errors=True) + return res + +def clear_middleware(app): + """ + The fixture sets paste.throw_errors, which suppresses exactly what + we want to test in this case. This wrapper also strips exc_info + on the *first* call to start_response (but not the second, or + subsequent calls. + """ + def clear_throw_errors(environ, start_response): + headers_sent = [] + def replacement(status, headers, exc_info=None): + if headers_sent: + return start_response(status, headers, exc_info) + headers_sent.append(True) + return start_response(status, headers) + if 'paste.throw_errors' in environ: + del environ['paste.throw_errors'] + return app(environ, replacement) + return clear_throw_errors + + +############################################################ +## Applications that raise exceptions +############################################################ + +def bad_app(): + "No argument list!" + return None + +def unicode_bad_app(environ, start_response): + raise ValueError(u"\u1000") + +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([b'this', b' is ', b' a', None]) + +def yielder(args): + for arg in args: + if arg is None: + raise ValueError("None raises error") + yield arg + +############################################################ +## Tests +############################################################ + +def test_makes_exception(): + res = do_request(bad_app) + assert '<html' in res + res = strip_html(str(res)) + if six.PY3: + assert 'bad_app() takes 0 positional arguments but 2 were given' in res + else: + assert 'bad_app() takes no arguments (2 given' in res, repr(res) + assert 'iterator = application(environ, start_response_wrapper)' in res + assert 'paste.lint' in res + assert 'paste.exceptions.errormiddleware' in res + +def test_unicode_exception(): + res = do_request(unicode_bad_app) + + +def test_start_res(): + res = do_request(start_response_app) + res = strip_html(str(res)) + assert 'ValueError: hi' in res + assert 'test_error_middleware' in res + assert ':52 in start_response_app' in res + +def test_after_start(): + res = do_request(after_start_response_app, 200) + res = strip_html(str(res)) + #print res + assert 'ValueError: error2' in res + +def test_iter_app(): + res = do_request(lint.middleware(iter_app), 200) + #print res + assert 'None raises error' in res + assert 'yielder' in res + + + + diff --git a/tests/test_exceptions/test_formatter.py b/tests/test_exceptions/test_formatter.py new file mode 100644 index 0000000..9c53a9a --- /dev/null +++ b/tests/test_exceptions/test_formatter.py @@ -0,0 +1,183 @@ +from paste.exceptions import formatter +from paste.exceptions import collector +import sys +import os +import difflib + +class Mock(object): + def __init__(self, **kw): + for name, value in kw.items(): + setattr(self, name, value) + +class Supplement(Mock): + + object = 'test_object' + source_url = 'http://whatever.com' + info = 'This is some supplemental information' + args = () + def getInfo(self): + return self.info + + def __call__(self, *args): + self.args = args + return self + +class BadSupplement(Supplement): + + def getInfo(self): + raise ValueError("This supplemental info is buggy") + +def call_error(sup): + 1 + 2 + __traceback_supplement__ = (sup, ()) + assert 0, "I am an error" + +def raise_error(sup='default'): + if sup == 'default': + sup = Supplement() + for i in range(10): + __traceback_info__ = i + if i == 5: + call_error(sup=sup) + +def hide(t, inner, *args, **kw): + __traceback_hide__ = t + return inner(*args, **kw) + +def pass_through(info, inner, *args, **kw): + """ + To add another frame to the call; detectable because + __tracback_info__ is set to `info` + """ + __traceback_info__ = info + return inner(*args, **kw) + +def format(type='html', **ops): + data = collector.collect_exception(*sys.exc_info()) + report = getattr(formatter, 'format_' + type)(data, **ops) + return report + +formats = ('text', 'html') + +def test_excersize(): + for f in formats: + try: + raise_error() + except: + format(f) + +def test_content(): + for f in formats: + try: + raise_error() + except: + result = format(f) + print(result) + assert 'test_object' in result + assert 'http://whatever.com' in result + assert 'This is some supplemental information' in result + assert 'raise_error' in result + assert 'call_error' in result + assert '5' in result + assert 'test_content' in result + else: + assert 0 + +def test_trim(): + current = os.path.abspath(os.getcwd()) + for f in formats: + try: + raise_error() + except: + result = format(f, trim_source_paths=[(current, '.')]) + assert current not in result + assert ('%stest_formatter.py' % os.sep) in result, ValueError(repr(result)) + else: + assert 0 + +def test_hide(): + for f in formats: + try: + hide(True, raise_error) + except: + result = format(f) + print(result) + assert 'in hide_inner' not in result + assert 'inner(*args, **kw)' not in result + else: + assert 0 + +def print_diff(s1, s2): + differ = difflib.Differ() + result = list(differ.compare(s1.splitlines(), s2.splitlines())) + print('\n'.join(result)) + +def test_hide_supppressed(): + """ + When an error occurs and __traceback_stop__ is true for the + erroneous frame, then that setting should be ignored. + """ + for f in ['html']: #formats: + results = [] + for hide_value in (False, 'after'): + try: + pass_through( + 'a', + hide, + hide_value, + pass_through, + 'b', + raise_error) + except: + results.append(format(f)) + else: + assert 0 + if results[0] != results[1]: + print_diff(results[0], results[1]) + assert 0 + +def test_hide_after(): + for f in formats: + try: + pass_through( + 'AABB', + hide, 'after', + pass_through, 'CCDD', + # A little whitespace to keep this line out of the + # content part of the report + + + hide, 'reset', + raise_error) + except: + result = format(f) + assert 'AABB' in result + assert 'CCDD' not in result + assert 'raise_error' in result + else: + assert 0 + +def test_hide_before(): + for f in formats: + try: + pass_through( + 'AABB', + hide, 'before', + raise_error) + except: + result = format(f) + print(result) + assert 'AABB' not in result + assert 'raise_error' in result + else: + assert 0 + +def test_make_wrappable(): + assert '<wbr>' in formatter.make_wrappable('x'*1000) + # I'm just going to test that this doesn't excede the stack limit: + formatter.make_wrappable(';'*2000) + assert (formatter.make_wrappable('this that the other') + == 'this that the other') + assert (formatter.make_wrappable('this that ' + ('x'*50) + ';' + ('y'*50) + ' and the other') + == 'this that '+('x'*50) + ';<wbr>' + ('y'*50) + ' and the other') + diff --git a/tests/test_exceptions/test_httpexceptions.py b/tests/test_exceptions/test_httpexceptions.py new file mode 100644 index 0000000..24e00dd --- /dev/null +++ b/tests/test_exceptions/test_httpexceptions.py @@ -0,0 +1,97 @@ +# (c) 2005 Ian Bicking, Clark C. Evans and contributors +# This module is part of the Python Paste Project and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +""" +WSGI Exception Middleware + +Regression Test Suite +""" +from nose.tools import assert_raises +from paste.httpexceptions import * +from paste.response import header_value +import six + + +def test_HTTPMove(): + """ make sure that location is a mandatory attribute of Redirects """ + assert_raises(AssertionError, HTTPFound) + assert_raises(AssertionError, HTTPTemporaryRedirect, + headers=[('l0cation','/bing')]) + assert isinstance(HTTPMovedPermanently("This is a message", + headers=[('Location','/bing')]) + ,HTTPRedirection) + assert isinstance(HTTPUseProxy(headers=[('LOCATION','/bing')]) + ,HTTPRedirection) + assert isinstance(HTTPFound('/foobar'),HTTPRedirection) + +def test_badapp(): + """ verify that the middleware handles previously-started responses """ + def badapp(environ, start_response): + start_response("200 OK",[]) + raise HTTPBadRequest("Do not do this at home.") + newapp = HTTPExceptionHandler(badapp) + assert b'Bad Request' in b''.join(newapp({'HTTP_ACCEPT': 'text/html'}, + (lambda a, b, c=None: None))) + +def test_unicode(): + """ verify unicode output """ + tstr = u"\0xCAFE" + def badapp(environ, start_response): + start_response("200 OK",[]) + raise HTTPBadRequest(tstr) + newapp = HTTPExceptionHandler(badapp) + assert tstr.encode("utf-8") in b''.join(newapp({'HTTP_ACCEPT': + 'text/html'}, + (lambda a, b, c=None: None))) + assert tstr.encode("utf-8") in b''.join(newapp({'HTTP_ACCEPT': + 'text/plain'}, + (lambda a, b, c=None: None))) + +def test_template(): + """ verify that html() and plain() output methods work """ + e = HTTPInternalServerError() + e.template = 'A %(ping)s and <b>%(pong)s</b> message.' + assert str(e).startswith("500 Internal Server Error") + assert e.plain({'ping': 'fun', 'pong': 'happy'}) == ( + '500 Internal Server Error\r\n' + 'A fun and happy message.\r\n') + assert '<p>A fun and <b>happy</b> message.</p>' in \ + e.html({'ping': 'fun', 'pong': 'happy'}) + +def test_redapp(): + """ check that redirect returns the correct, expected results """ + saved = [] + def saveit(status, headers, exc_info = None): + saved.append((status,headers)) + def redapp(environ, start_response): + raise HTTPFound("/bing/foo") + app = HTTPExceptionHandler(redapp) + result = list(app({'HTTP_ACCEPT': 'text/html'},saveit)) + assert b'<a href="/bing/foo">' in result[0] + assert "302 Found" == saved[0][0] + if six.PY3: + assert "text/html; charset=utf8" == header_value(saved[0][1], 'content-type') + else: + assert "text/html" == header_value(saved[0][1], 'content-type') + assert "/bing/foo" == header_value(saved[0][1],'location') + result = list(app({'HTTP_ACCEPT': 'text/plain'},saveit)) + assert "text/plain; charset=utf8" == header_value(saved[1][1],'content-type') + assert "/bing/foo" == header_value(saved[1][1],'location') + +def test_misc(): + assert get_exception(301) == HTTPMovedPermanently + redirect = HTTPFound("/some/path") + assert isinstance(redirect,HTTPException) + assert isinstance(redirect,HTTPRedirection) + assert not isinstance(redirect,HTTPError) + notfound = HTTPNotFound() + assert isinstance(notfound,HTTPException) + assert isinstance(notfound,HTTPError) + assert isinstance(notfound,HTTPClientError) + assert not isinstance(notfound,HTTPServerError) + notimpl = HTTPNotImplemented() + assert isinstance(notimpl,HTTPException) + assert isinstance(notimpl,HTTPError) + assert isinstance(notimpl,HTTPServerError) + assert not isinstance(notimpl,HTTPClientError) + diff --git a/tests/test_exceptions/test_reporter.py b/tests/test_exceptions/test_reporter.py new file mode 100644 index 0000000..a40666e --- /dev/null +++ b/tests/test_exceptions/test_reporter.py @@ -0,0 +1,50 @@ +import sys +import os +from paste.exceptions.reporter import * +from paste.exceptions import collector + +def setup_file(fn, content=None): + dir = os.path.join(os.path.dirname(__file__), 'reporter_output') + fn = os.path.join(dir, fn) + if os.path.exists(dir): + if os.path.exists(fn): + os.unlink(fn) + else: + os.mkdir(dir) + if content is not None: + f = open(fn, 'wb') + f.write(content) + f.close() + return fn + +def test_logger(): + fn = setup_file('test_logger.log') + rep = LogReporter( + filename=fn, + show_hidden_frames=False) + try: + int('a') + except: + exc_data = collector.collect_exception(*sys.exc_info()) + else: + assert 0 + rep.report(exc_data) + content = open(fn).read() + assert len(content.splitlines()) == 4, len(content.splitlines()) + assert 'ValueError' in content + assert 'int' in content + assert 'test_reporter.py' in content + assert 'test_logger' in content + + try: + 1 / 0 + except: + exc_data = collector.collect_exception(*sys.exc_info()) + else: + assert 0 + rep.report(exc_data) + content = open(fn).read() + print(content) + assert len(content.splitlines()) == 8 + assert 'ZeroDivisionError' in content + |