Johan B.W. de Vries a110fe8405 Notes
2025-08-02 12:17:55 +02:00

154 lines
4.4 KiB
Python

import functools
import json
import sys
from typing import Any
import marko
import marko.md_renderer
def get_tests(template):
test_data = None
for el in template.children:
if isinstance(el, marko.block.BlankLine):
continue
if isinstance(el, marko.block.Heading):
if test_data is not None:
yield test_data
test_data = []
test_data.append(el)
continue
if test_data is not None:
test_data.append(el)
if test_data is not None:
yield test_data
def apply_settings(settings, txt):
for k, v in settings.items():
if k in ('CODE_HEADER', 'PYTHON'):
continue
txt = txt.replace(f'${k}', v)
return txt
def generate_assertion_expect(result, arg, given=None):
given = given or []
result.append('result = Suite(code_py).run_code(' + ', '.join(repr(x) for x in given) + ')')
result.append(f'assert {repr(arg)} == result.returned_value')
def generate_assertion_expect_type_error(result, error_msg):
result.append(f'with pytest.raises(Type5SolverException, match={error_msg!r}):')
result.append(' Suite(code_py).run_code()')
def json_does_not_support_byte_or_tuple_values_fix(inp: Any):
if isinstance(inp, (int, float, )):
return inp
if isinstance(inp, str):
if inp.startswith('bytes:'):
return inp[6:].encode()
return inp
if isinstance(inp, list):
return tuple(map(json_does_not_support_byte_or_tuple_values_fix, inp))
if isinstance(inp, dict):
return {
key: json_does_not_support_byte_or_tuple_values_fix(val)
for key, val in inp.items()
}
raise NotImplementedError(inp)
def generate_assertions(settings, result_code):
result = []
locals_ = {
'TYPE': settings['TYPE'],
'TYPE_NAME': settings['TYPE_NAME'],
'expect': functools.partial(generate_assertion_expect, result),
'expect_type_error': functools.partial(generate_assertion_expect_type_error, result),
}
if 'PYTHON' in settings:
locals_.update(json_does_not_support_byte_or_tuple_values_fix(settings['PYTHON']))
if 'VAL0' not in locals_:
locals_['VAL0'] = eval(settings['VAL0'])
exec(result_code, {}, locals_)
return ' ' + '\n '.join(result) + '\n'
def generate_code(markdown, template, settings):
type_name = settings['TYPE_NAME']
print('"""')
print('AUTO GENERATED')
print()
print('TEMPLATE:', sys.argv[1])
print('SETTINGS:', sys.argv[2])
print('"""')
print('import pytest')
print()
print('from phasm.type5.solver import Type5SolverException')
print()
print('from ..helpers import Suite')
print()
print()
for test in get_tests(template):
assert len(test) == 4, test
heading, paragraph, code_block1, code_block2 = test
assert isinstance(heading, marko.block.Heading)
assert isinstance(paragraph, marko.block.Paragraph)
assert isinstance(code_block1, marko.block.FencedCode)
assert isinstance(code_block2, marko.block.FencedCode)
test_id = apply_settings(settings, heading.children[0].children)
user_story = apply_settings(settings, markdown.renderer.render(paragraph))
inp_code = apply_settings(settings, code_block1.children[0].children)
result_code = markdown.renderer.render_children(code_block2)
print('@pytest.mark.integration_test')
print(f'def test_{type_name}_{test_id}():')
print(' """')
print(' ' + user_story.strip().replace('\n', '\n '))
print(' """')
print(' code_py = """')
if 'CODE_HEADER' in settings:
for lin in settings['CODE_HEADER']:
print(lin)
print()
print(inp_code.rstrip('\n'))
print('"""')
print()
print(generate_assertions(settings, result_code))
print()
def main():
markdown = marko.Markdown(
renderer=marko.md_renderer.MarkdownRenderer,
)
with open(sys.argv[1], 'r', encoding='utf-8') as fil:
template = markdown.parse(fil.read())
with open(sys.argv[2], 'r', encoding='utf-8') as fil:
settings = json.load(fil)
if 'TYPE_NAME' not in settings:
settings['TYPE_NAME'] = settings['TYPE']
generate_code(markdown, template, settings)
if __name__ == '__main__':
main()