Skip to content

Instantly share code, notes, and snippets.

@bebraw
Created January 10, 2010 17:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bebraw/273648 to your computer and use it in GitHub Desktop.
Save bebraw/273648 to your computer and use it in GitHub Desktop.
from collections import deque
class InputError(Exception):
pass
class MatchError(Exception):
pass
class OutputError(Exception):
pass
class Line:
def __init__(self, content):
self.content = content
def __str__(self):
return self.content
class EllipsisOutput:
class Content:
def __eq__(self, other):
return True
def __init__(self):
self.content = self.Content()
class Input(Line):
pass
class Output(Line):
pass
class ScenarioTester:
def __init__(self, app_class):
self.app = app_class()
self.app.input = self._input
self.app.output = self._output
self.lines = deque()
def test(self, scenario):
self.parse(scenario)
self.app.run()
def parse(self, scenario):
self.lines.clear()
for line in scenario.split('\n'):
parsed_line = self._parse_line(line)
if parsed_line:
self.lines.append(parsed_line)
def _parse_line(self, line):
if len(line.strip()) == 0:
return
prefix = '>>> '
if line.startswith(prefix):
content = line.strip(prefix)
return Input(content)
elif line.startswith('...'):
return EllipsisOutput()
else:
content = line
return Output(content)
def _input(self):
if len(self.lines) == 0:
raise SystemExit
current_line = self.lines.popleft()
if isinstance(current_line, Input):
return str(current_line)
else:
raise InputError, 'Expected input but got output instead!' + \
' Failed at line "%s".' % current_line
def _output(self, result):
line = self.lines.popleft()
if isinstance(line, Output):
content = line.content
if content != str(result):
raise MatchError, "Output content didn't match!" + \
" Expected %s (%s) but got %s (%s) instead." \
% (content, type(content), result, type(result))
elif isinstance(line, EllipsisOutput):
pass
else:
raise OutputError, 'Expected output but got input instead!' + \
' Failed at line "%s". Result: %s.' % (line, result)
from placidity.scenario_tester import EllipsisOutput, Input, InputError, \
MatchError, Output, OutputError, ScenarioTester
from py.test import raises
class AbstractApplication:
def run(self):
try:
while True:
input = self.input()
result = self.interpret(input)
if result:
self.output(result)
except SystemExit:
pass
class TestScenarioTester:
def test_passing_test(self):
class Application(AbstractApplication):
def interpret(self, input):
if input == 'a':
return 4
scenario = '''
>>> a = 4
>>> a
4
'''
scenario_tester = ScenarioTester(Application)
scenario_tester.parse(scenario)
lines = scenario_tester.lines
assert len(lines) == 3
self.assert_line(lines, 1, Input, 'a = 4')
self.assert_line(lines, 2, Input, 'a')
self.assert_line(lines, 3, Output, '4')
# this should not trigger any asserts
scenario_tester.test(scenario)
def test_ellipsis(self):
class Application(AbstractApplication):
def interpret(self, input):
if input == 'a':
return 5
scenario = '''
>>> a = 5
>>> a
...
'''
scenario_tester = ScenarioTester(Application)
scenario_tester.parse(scenario)
lines = scenario_tester.lines
assert len(lines) == 3
self.assert_line(lines, 1, Input, 'a = 5')
self.assert_line(lines, 2, Input, 'a')
self.assert_line(lines, 3, EllipsisOutput, None)
scenario_tester.test(scenario)
def test_input_fail(self):
class Application(AbstractApplication):
def interpret(self, input):
pass
scenario = '''
fail
'''
scenario_tester = ScenarioTester(Application)
scenario_tester.parse(scenario)
lines = scenario_tester.lines
assert len(lines) == 1
self.assert_line(lines, 1, Output, 'fail')
raises(InputError, scenario_tester.test, scenario)
def test_match_fail(self):
class Application(AbstractApplication):
def interpret(self, input):
if input == 'a':
return 42
scenario = '''
>>> a = 4
>>> a
5
'''
scenario_tester = ScenarioTester(Application)
scenario_tester.parse(scenario)
lines = scenario_tester.lines
assert len(lines) == 3
self.assert_line(lines, 1, Input, 'a = 4')
self.assert_line(lines, 2, Input, 'a')
self.assert_line(lines, 3, Output, '5')
raises(MatchError, scenario_tester.test, scenario)
def test_output_fail(self):
class Application(AbstractApplication):
def interpret(self, input):
return 42
scenario = '''
>>> fail
>>> fail
'''
scenario_tester = ScenarioTester(Application)
scenario_tester.parse(scenario)
lines = scenario_tester.lines
assert len(lines) == 2
self.assert_line(lines, 1, Input, 'fail')
self.assert_line(lines, 2, Input, 'fail')
raises(OutputError, scenario_tester.test, scenario)
def assert_line(self, lines, line_number, line_type, line_content):
line = lines[line_number - 1]
assert isinstance(line, line_type)
assert line.content == line_content
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment