bebraw
1/8/2010 - 3:18 PM

application.py

from placidity.scenario_tester import Input, InputError, MatchError, \
Output, OutputError, ScenarioTester
from py.test import raises

class AbstractApplication:
    def run(self):
        while True:
            input = self.input()

            if input == 'quit':
                break

            result = self.interpret(input)

            if result:
                self.output(result)

class TestScenarioTester:
    def test_passing_test(self):
        class Application(AbstractApplication):
            def interpret(self, input):
                if input == 'a':
                    return '4'

        scenario = '''
>>> a = 4
>>> a
4
>>> quit
'''

        scenario_tester = ScenarioTester(Application)
        scenario_tester.parse(scenario)
        lines = scenario_tester.lines

        assert len(lines) == 4
        self.assert_line(lines, 1, Input, 'a = 4')
        self.assert_line(lines, 2, Input, 'a')
        self.assert_line(lines, 3, Output, '4')
        self.assert_line(lines, 4, Input, 'quit')

        # this should not trigger any asserts
        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
from mock import patch
from placidity.application import Application
from placidity.scenario_tester import ScenarioTester

class TestApplication:
    scenario_tester = ScenarioTester(Application)

    @patch('__builtin__.raw_input')
    def test_input_prefix(self, input_mock):
        # Note that prefix has to be tested separately as scenario tester
        # operates only on pure input. It just happens to use the same syntax
        # for input.
        app = Application()

        app.input()

        input_mock.assert_called_with('>>> ')

    def test_math(self):
        scenario = '''
>>> a = 5
>>> b = 10
>>> a + b
15
>>> 15 * 3
45
'''

        self.scenario_tester.test(scenario)

    def test_variables(self):
        scenario = '''
>>> a = 5
>>> b = 10
>>> a
5
>>> c
null
>>> vars
Stored variables:
a=5
b=10
>>> clean
>>> vars
No stored variables
'''

        self.scenario_tester.test(scenario)

    def test_help(self):
        scenario = '''
>>> help clean
Cleans up stored variables
'''

        self.scenario_tester.test(scenario)
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 Input(Line):
    pass

class Output(Line):
    pass

def parse_line(line):
    if len(line.strip()) == 0:
        return

    prefix = '>>> '
    if line.startswith(prefix):
        content = line.strip(prefix)
        return Input(content)
    else:
        content = line
        return Output(content)

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 = parse_line(line)

            if parsed_line:
                self.lines.append(parsed_line)

    def _input(self):
        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):
        current_line = self.lines.popleft()

        if isinstance(current_line, Output):
            content = current_line.content

            if content != result:
                raise MatchError, "Output content didn't match!" + \
                    " Expected %s (%s) but got %s (%s) instead." \
                    % (content, type(content), result, type(result))
        else:
            raise OutputError, 'Expected output but got input instead!' + \
                ' Failed at line "%s". Result: %s.' % (current_line, result)
from file import PluginDirectory
from interpreter import Interpreter
from plugin_loader import PluginLoader
 
class Application:
 
    def run(self):
        plugin_loader = PluginLoader()
        plugin_directory = PluginDirectory()
        commands = plugin_loader.load(plugin_directory)
        interpreter = Interpreter(commands)
 
        while True:
            input = self.input()
            result = interpreter.interpret(input)

            self.output(result)

    def input(self):
        return raw_input('>>> ')

    def output(self, result):
        print result