Created
January 10, 2010 17:54
-
-
Save bebraw/273648 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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