0.2.0 - docstring support

This commit is contained in:
Gabriel Falcao 2012-08-30 23:18:45 -04:00
parent 48eaa947b6
commit 7a7628abba
4 changed files with 146 additions and 59 deletions

View File

@ -38,7 +38,7 @@ def get_packages():
return packages return packages
setup(name='steadymark', setup(name='steadymark',
version='0.1.4', version='0.2.0',
description=(u'Markdown-based test runner for python. ' description=(u'Markdown-based test runner for python. '
'Good for github projects'), 'Good for github projects'),
author=u'Gabriel Falcao', author=u'Gabriel Falcao',

View File

@ -35,8 +35,7 @@ from doctest import (
DocTestParser, DocTestParser,
Example, Example,
DocTest, DocTest,
DocTestRunner, DebugRunner,
TestResults,
) )
from datetime import datetime from datetime import datetime
@ -49,6 +48,11 @@ from misaka import (
) )
class SteadyMarkDoctestRunner(DebugRunner):
def report_unexpected_exception(self, out, test, example, exc_info):
raise exc_info
class MarkdownTest(object): class MarkdownTest(object):
def __init__(self, title, raw_code): def __init__(self, title, raw_code):
self.title = title self.title = title
@ -71,23 +75,33 @@ class MarkdownTest(object):
else: else:
self.code = compile(raw_code, "@STEADYMARK@", "exec") self.code = compile(raw_code, "@STEADYMARK@", "exec")
def _run_raw(self):
return eval(self.code)
def _run_doctest(self):
if not isinstance(self.code, DocTest):
raise TypeError(
"Attempt to run a non-doctest as doctest: %r" % self.code)
runner = SteadyMarkDoctestRunner(verbose=False)
return runner.run(self.code)
def run(self): def run(self):
before = datetime.now() before = datetime.now()
failure = None failure = None
if isinstance(self.code, DocTest): result = None
runner = DocTestRunner()
result = runner.run(self.code) try:
after = datetime.now() if isinstance(self.code, DocTest):
return result, before, after result = self._run_doctest()
else: else:
try: result = self._run_raw()
eval(self.code) except:
except: failure = sys.exc_info()
failure = sys.exc_info()
after = datetime.now() after = datetime.now()
return failure, before, after return result, failure, before, after
class SteadyMark(BaseRenderer): class SteadyMark(BaseRenderer):
@ -131,10 +145,6 @@ class SteadyMark(BaseRenderer):
md.render(markdown) md.render(markdown)
return renderer return renderer
def run(self):
for test in self.tests:
test.run()
class Runner(object): class Runner(object):
def __init__(self, filename=None, text=u''): def __init__(self, filename=None, text=u''):
@ -150,6 +160,10 @@ class Runner(object):
self.filename = filename self.filename = filename
self.text = text self.text = text
def print_white(self, text, indentation=0):
for line in text.splitlines():
print "{1}\033[1;37m{0}\033[0m".format(line, ' ' * indentation)
def print_red(self, text, indentation=0): def print_red(self, text, indentation=0):
for line in text.splitlines(): for line in text.splitlines():
print "{1}\033[1;31m{0}\033[0m".format(line, ' ' * indentation) print "{1}\033[1;31m{0}\033[0m".format(line, ' ' * indentation)
@ -158,69 +172,73 @@ class Runner(object):
for line in text.splitlines(): for line in text.splitlines():
print "{1}\033[1;32m{0}\033[0m".format(line, ' ' * indentation) print "{1}\033[1;32m{0}\033[0m".format(line, ' ' * indentation)
def print_yellow(self, text, indentation=0):
for line in text.splitlines():
print "{1}\033[1;33m{0}\033[0m".format(line, ' ' * indentation)
def format_ms(self, ms): def format_ms(self, ms):
ms = int(ms) ms = int(ms)
if ms < 1000:
return ""
return "\033[1;33m{0}ms\033[0m".format(ms) return "\033[1;33m{0}ms\033[0m".format(ms)
def format_traceback(self, title, failure): def format_traceback(self, test, failure):
exc, exc_instance, tb = failure exc, exc_instance, tb = failure
# formatted_tb = traceback.format_exc(exc_instance).strip()
# if 'None' == formatted_tb:
formatted_tb = ''.join(traceback.format_tb(tb))
formatted_tb = formatted_tb.replace(
u'File "@STEADYMARK@',
u'In the test "@STEADYMARK@',
)
formatted_tb = formatted_tb.replace(
u'@STEADYMARK@', unicode(test.title))
formatted_tb = traceback.format_exc(exc_instance).strip() return u'{0} \033[1;36m{1}\n{2}\n'.format(
if 'None' == formatted_tb:
formatted_tb = ''.join(traceback.format_tb(tb))
formatted_tb = formatted_tb.replace(
u'File "@STEADYMARK@',
u'In the test "@STEADYMARK@',
)
formatted_tb = formatted_tb.replace(
u'@STEADYMARK@', unicode(title))
return u'{0} {1}\n{2}'.format(
exc.__name__, exc.__name__,
exc_instance, exc_instance,
formatted_tb, formatted_tb,
) )
def report_doctest_result(self, test, result, before, after): def report_success(self, test, shift, ms):
# shift = before - after self.print_green('\xe2\x9c\x93 {0}'.format(ms))
# ms = self.format_ms(shift.microseconds / 1000) print
# import ipdb;ipdb.set_trace()
pass
def report_raw_test_result(self, test, failure, before, after):
shift = before - after
ms = self.format_ms(shift.microseconds / 1000)
lines = test.raw_code.splitlines()
if not failure:
return self.print_green('\xe2\x9c\x93 {0}'.format(ms))
def report_failure(self, test, failure, shift, ms):
self.print_red('\xe2\x9c\x97 {0}'.format(ms)) self.print_red('\xe2\x9c\x97 {0}'.format(ms))
exc, exc_instance, tb = failure exc, exc_instance, tb = failure
formatted_tb = self.format_traceback(test.title, failure) formatted_tb = self.format_traceback(test, failure)
tb = tb.tb_next self.print_red(formatted_tb, indentation=2)
if tb:
line = lines[tb.tb_lineno - 1] header = "original code:"
self.print_red("{0} {1}".format(formatted_tb, line)) header_length = len(header)
elif issubclass(exc, SyntaxError): self.print_white("*" * header_length)
self.print_red(formatted_tb, indentation=2) self.print_white(header)
self.print_white("*" * header_length)
self.print_yellow(test.raw_code, indentation=2)
print
def report_test_result(self, test, failure, before, after):
shift = before - after
ms = self.format_ms(shift.microseconds / 1000)
if not failure:
return self.report_success(test, shift, ms)
return self.report_failure(test, failure, shift, ms)
def run(self): def run(self):
if self.filename: if self.filename:
print "Running tests from {0}".format(self.filename) print "Running tests from {0}".format(self.filename)
for test in self.steadymark.tests: for test in self.steadymark.tests:
sys.stdout.write("{0} ".format(test.title)) title = "{0} ".format(test.title)
result, before, after = test.run() title_length = len(title)
if isinstance(result, TestResults): print "." * title_length
self.report_doctest_result(test, result, before, after) sys.stdout.write(title)
else: result, failure, before, after = test.run()
self.report_raw_test_result(test, result, before, after) self.report_test_result(test, failure, before, after)
return self.steadymark return self.steadymark

View File

@ -23,3 +23,4 @@
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE. # OTHER DEALINGS IN THE SOFTWARE.
import sure

68
tests/unit/test_runner.py Normal file
View File

@ -0,0 +1,68 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# <steadymark - markdown-based test runner for python>
# Copyright (C) <2012> Gabriel Falcão <gabriel@nacaolivre.org>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
from steadymark import SteadyMark
def test_find_doctest_code_with_titles():
(u"SteadyMark should find doctest and use the "
"previous header as title")
md = u"""# test 1
a paragraph
```python
>>> raise TypeError('boom')
```
"""
sm = SteadyMark.inspect(md)
test1 = sm.tests[0]
result, (_, failure, tb), before, after = test1.run()
test1.title.should.equal("test 1")
failure.should.be.a(TypeError)
"boom".should.be.within(unicode(failure))
def test_find_python_code_with_titles():
(u"SteadyMark should find python code and use the "
"previous header as title")
md = u"""# test 1
a paragraph
```python
raise ValueError('boom')
```
"""
sm = SteadyMark.inspect(md)
test1 = sm.tests[0]
result, (_, failure, tb), before, after = test1.run()
test1.title.should.equal("test 1")
failure.should.be.a(ValueError)
"boom".should.be.within(unicode(failure))