Mercurial > jouvence
diff tests/conftest.py @ 0:243401c49520
Initial commit.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Mon, 02 Jan 2017 12:30:49 -0800 |
parents | |
children | 74b83e3d921e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/conftest.py Mon Jan 02 12:30:49 2017 -0800 @@ -0,0 +1,134 @@ +import sys +import logging +import yaml +import pytest +from fontaine.document import ( + FontaineSceneElement, + TYPE_ACTION, TYPE_CHARACTER, TYPE_DIALOG, TYPE_PARENTHETICAL) +from fontaine.parser import FontaineParser, FontaineParserError + + +def pytest_addoption(parser): + parser.addoption( + '--log-debug', + action='store_true', + help="Sets the Fontaine logger to output debug info to stdout.") + + +def pytest_configure(config): + if config.getoption('--log-debug'): + hdl = logging.StreamHandler(stream=sys.stdout) + logging.getLogger('fontaine').addHandler(hdl) + logging.getLogger('fontaine').setLevel(logging.DEBUG) + + +def pytest_collect_file(parent, path): + if path.ext == '.yaml' and path.basename.startswith("test"): + return FontaineScriptTestFile(path, parent) + return None + + +def assert_scenes(actual, scenes): + assert len(actual) == len(scenes) + for a, e in zip(actual, scenes): + assert_scene(a, e[0], e[1:]) + + +def assert_scene(actual, header, paragraphs): + if header is not None: + assert actual.header == header + assert len(actual.paragraphs) == len(paragraphs) + for a, e in zip(actual.paragraphs, paragraphs): + assert_paragraph(a, e) + + +def assert_paragraph(actual, expected): + if isinstance(expected, str): + assert isinstance(actual, FontaineSceneElement) + assert actual.type == TYPE_ACTION + assert actual.text == expected + elif isinstance(expected, FontaineSceneElement): + assert isinstance(actual, FontaineSceneElement) + assert actual.type == expected.type + assert actual.text == expected.text + else: + raise NotImplementedError("Don't know what this is: %s" % expected) + + +def _c(name): + return FontaineSceneElement(TYPE_CHARACTER, name) + + +def _p(text): + return FontaineSceneElement(TYPE_PARENTHETICAL, text) + + +def _d(text): + return FontaineSceneElement(TYPE_DIALOG, text) + + +class FontaineScriptTestFile(pytest.File): + def collect(self): + spec = yaml.load_all(self.fspath.open(encoding='utf8')) + for i, item in enumerate(spec): + name = '%s_%d' % (self.fspath.basename, i) + if 'test_name' in item: + name += '_%s' % item['test_name'] + yield FontaineScriptTestItem(name, self, item) + + +class FontaineScriptTestItem(pytest.Item): + def __init__(self, name, parent, spec): + super().__init__(name, parent) + self.spec = spec + + def reportinfo(self): + return self.fspath, 0, "fontaine script test: %s" % self.name + + def runtest(self): + intext = self.spec.get('in') + expected = self.spec.get('out') + title = self.spec.get('title') + if intext is None or expected is None: + raise Exception("No 'in' or 'out' specified.") + + parser = FontaineParser() + doc = parser.parseString(intext) + if title is not None: + assert title == doc.title_values + assert_scenes(doc.scenes, make_scenes(expected)) + + def repr_failure(self, excinfo): + if isinstance(excinfo.value, FontaineParserError): + return ('\n'.join( + ['Parser error:', str(excinfo.value)])) + return super().repr_failure(excinfo) + + +def make_scenes(spec): + if not isinstance(spec, list): + raise Exception("Script specs must be lists.") + + out = [] + cur_header = None + cur_paras = [] + + for item in spec: + token = item[:1] + if token == '.': + if cur_header or cur_paras: + out.append([cur_header] + cur_paras) + cur_header = item[1:] + elif token == '!': + cur_paras.append(item[1:]) + elif token == '@': + cur_paras.append(_c(item[1:])) + elif token == '=': + cur_paras.append(_d(item[1:])) + elif token == '_': + cur_paras.append(_p(item[1:])) + else: + raise Exception("Unknown token: %s" % token) + if cur_header or cur_paras: + out.append([cur_header] + cur_paras) + return out