# -*- 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'
|