# -*- coding: utf-8 -*- """ tests.debug ~~~~~~~~~~~ Tests some debug utilities. :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import sys import re import io import pytest import requests from werkzeug.debug import get_machine_id from werkzeug.debug.repr import debug_repr, DebugReprGenerator, \ dump, helper from werkzeug.debug.console import HTMLStringO from werkzeug.debug.tbtools import Traceback from werkzeug._compat import PY2 class TestDebugRepr(object): def test_basic_repr(self): assert debug_repr([]) == u'[]' assert debug_repr([1, 2]) == \ u'[1, 2]' assert debug_repr([1, 'test']) == \ u'[1, \'test\']' assert debug_repr([None]) == \ u'[None]' def test_string_repr(self): assert debug_repr('') == u'\'\'' assert debug_repr('foo') == u'\'foo\'' assert debug_repr('s' * 80) == u'\''\ + 's' * 70 + ''\ + 's' * 10 + '\'' assert debug_repr('<' * 80) == u'\''\ + '<' * 70 + ''\ + '<' * 10 + '\'' def test_sequence_repr(self): assert debug_repr(list(range(20))) == ( u'[0, 1, ' u'2, 3, ' u'4, 5, ' u'6, 7, ' u'8, ' u'9, 10, ' u'11, 12, ' u'13, 14, ' u'15, 16, ' u'17, 18, ' u'19]' ) def test_mapping_repr(self): assert debug_repr({}) == u'{}' assert debug_repr({'foo': 42}) == ( u'{\'foo\'' u': 42' u'}' ) assert debug_repr(dict(zip(range(10), [None] * 10))) == ( u'{0: None, 1: None, 2: None, 3: None, 4: None, 5: None, 6: None, 7: None, 8: None, 9: None}' # noqa ) assert debug_repr((1, 'zwei', u'drei')) == ( u'(1, \'' u'zwei\', %s\'drei\')' ) % ('u' if PY2 else '') def test_custom_repr(self): class Foo(object): def __repr__(self): return '' assert debug_repr(Foo()) == \ '<Foo 42>' def test_list_subclass_repr(self): class MyList(list): pass assert debug_repr(MyList([1, 2])) == ( u'tests.test_debug.MyList([' u'1, 2])' ) def test_regex_repr(self): assert debug_repr(re.compile(r'foo\d')) == \ u're.compile(r\'foo\\d\')' # No ur'' in Py3 # http://bugs.python.org/issue15096 assert debug_repr(re.compile(u'foo\\d')) == ( u're.compile(%sr\'foo\\d\')' % ('u' if PY2 else '') ) def test_set_repr(self): assert debug_repr(frozenset('x')) == \ u'frozenset([\'x\'])' assert debug_repr(set('x')) == \ u'set([\'x\'])' def test_recursive_repr(self): a = [1] a.append(a) assert debug_repr(a) == u'[1, [...]]' def test_broken_repr(self): class Foo(object): def __repr__(self): raise Exception('broken!') assert debug_repr(Foo()) == ( u'<broken repr (Exception: ' u'broken!)>' ) class Foo(object): x = 42 y = 23 def __init__(self): self.z = 15 class TestDebugHelpers(object): def test_object_dumping(self): drg = DebugReprGenerator() out = drg.dump_object(Foo()) assert re.search('Details for tests.test_debug.Foo object at', out) assert re.search('x.*42', out, flags=re.DOTALL) assert re.search('y.*23', out, flags=re.DOTALL) assert re.search('z.*15', out, flags=re.DOTALL) out = drg.dump_object({'x': 42, 'y': 23}) assert re.search('Contents of', out) assert re.search('x.*42', out, flags=re.DOTALL) assert re.search('y.*23', out, flags=re.DOTALL) out = drg.dump_object({'x': 42, 'y': 23, 23: 11}) assert not re.search('Contents of', out) out = drg.dump_locals({'x': 42, 'y': 23}) assert re.search('Local variables in frame', out) assert re.search('x.*42', out, flags=re.DOTALL) assert re.search('y.*23', out, flags=re.DOTALL) def test_debug_dump(self): old = sys.stdout sys.stdout = HTMLStringO() try: dump([1, 2, 3]) x = sys.stdout.reset() dump() y = sys.stdout.reset() finally: sys.stdout = old assert 'Details for list object at' in x assert '1' in x assert 'Local variables in frame' in y assert 'x' in y assert 'old' in y def test_debug_help(self): old = sys.stdout sys.stdout = HTMLStringO() try: helper([1, 2, 3]) x = sys.stdout.reset() finally: sys.stdout = old assert 'Help on list object' in x assert '__delitem__' in x class TestTraceback(object): def test_log(self): try: 1 / 0 except ZeroDivisionError: traceback = Traceback(*sys.exc_info()) buffer_ = io.BytesIO() if PY2 else io.StringIO() traceback.log(buffer_) assert buffer_.getvalue().strip() == traceback.plaintext.strip() def test_sourcelines_encoding(self): source = (u'# -*- coding: latin1 -*-\n\n' u'def foo():\n' u' """höhö"""\n' u' 1 / 0\n' u'foo()').encode('latin1') code = compile(source, filename='lol.py', mode='exec') try: eval(code) except ZeroDivisionError: traceback = Traceback(*sys.exc_info()) frames = traceback.frames assert len(frames) == 3 assert frames[1].filename == 'lol.py' assert frames[2].filename == 'lol.py' class Loader(object): def get_source(self, module): return source frames[1].loader = frames[2].loader = Loader() assert frames[1].sourcelines == frames[2].sourcelines assert [line.code for line in frames[1].get_annotated_lines()] == \ [line.code for line in frames[2].get_annotated_lines()] assert u'höhö' in frames[1].sourcelines[3] def test_filename_encoding(self, tmpdir, monkeypatch): moduledir = tmpdir.mkdir('föö') moduledir.join('bar.py').write('def foo():\n 1/0\n') monkeypatch.syspath_prepend(str(moduledir)) import bar try: bar.foo() except ZeroDivisionError: traceback = Traceback(*sys.exc_info()) assert u'föö' in u'\n'.join(frame.render() for frame in traceback.frames) def test_get_machine_id(): rv = get_machine_id() assert isinstance(rv, bytes) @pytest.mark.parametrize('crash', (True, False)) def test_basic(dev_server, crash): server = dev_server(''' from werkzeug.debug import DebuggedApplication @DebuggedApplication def app(environ, start_response): if {crash}: 1 / 0 start_response('200 OK', [('Content-Type', 'text/html')]) return [b'hello'] '''.format(crash=crash)) r = requests.get(server.url) assert r.status_code == 500 if crash else 200 if crash: assert 'The debugger caught an exception in your WSGI application' \ in r.text else: assert r.text == 'hello'