More framework

This commit is contained in:
Johan B.W. de Vries 2023-11-11 13:45:03 +01:00
parent ef00b3a91c
commit ff0286bcf6
6 changed files with 92 additions and 22 deletions

View File

@ -24,7 +24,7 @@ WASM2C := $(WABT_DIR)/bin/wasm2c
examples: venv/.done $(subst .py,.wasm,$(wildcard examples/*.py)) $(subst .py,.wat.html,$(wildcard examples/*.py)) $(subst .py,.py.html,$(wildcard examples/*.py))
venv/bin/python3 -m http.server --directory examples
test: venv/.done tests/integration/test_lang/test_generated_u32.py
test: venv/.done $(subst .json,.py,$(subst /generator_,/test_generated_,$(wildcard tests/integration/test_lang/generator_*.json)))
venv/bin/pytest tests $(TEST_FLAGS)
lint: venv/.done
@ -39,12 +39,15 @@ venv/.done: requirements.txt
venv/bin/python3 -m pip install -r $^
touch $@
tests/integration/test_lang/test_generated_u32.py: venv/.done tests/integration/test_lang/generator.py tests/integration/test_lang/generator.md tests/integration/test_lang/generator_u32.json
venv/bin/python3 tests/integration/test_lang/generator.py tests/integration/test_lang/generator.md tests/integration/test_lang/generator_u32.json > $@
tests/integration/test_lang/test_generated_%.py: venv/.done tests/integration/test_lang/generator.py tests/integration/test_lang/generator.md tests/integration/test_lang/generator_%.json
venv/bin/python3 tests/integration/test_lang/generator.py tests/integration/test_lang/generator.md tests/integration/test_lang/generator_$*.json > $@
clean-examples:
rm -f examples/*.wat examples/*.wasm examples/*.wat.html examples/*.py.html
clean-generated-tests:
rm -f tests/integration/test_lang/test_generated_*.py
.SECONDARY: # Keep intermediate files
.PHONY: examples

View File

@ -392,10 +392,10 @@ class LiteralFitsConstraint(ConstraintBase):
if isinstance(self.type3, types.AppliedType3):
if self.type3.base == types.tuple:
if not isinstance(self.literal, ourlang.ConstantTuple):
return Error('Must be tuple')
return Error('Must be tuple', comment=self.comment)
if len(self.type3.args) != len(self.literal.value):
return Error('Tuple element count mismatch')
return Error('Tuple element count mismatch', comment=self.comment)
res = []
@ -412,13 +412,13 @@ class LiteralFitsConstraint(ConstraintBase):
if self.type3.base == types.static_array:
if not isinstance(self.literal, ourlang.ConstantTuple):
return Error('Must be tuple')
return Error('Must be tuple', comment=self.comment)
assert 2 == len(self.type3.args)
assert isinstance(self.type3.args[1], types.IntType3)
if self.type3.args[1].value != len(self.literal.value):
return Error('Member count mismatch')
return Error('Member count mismatch', comment=self.comment)
res = []

View File

@ -26,7 +26,10 @@ def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase
def constant(ctx: Context, inp: ourlang.Constant) -> ConstraintGenerator:
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)):
yield LiteralFitsConstraint(inp.type3, inp)
yield LiteralFitsConstraint(
inp.type3, inp,
comment='The given literal must fit the expected type'
)
return
raise NotImplementedError(constant, inp)

View File

@ -1,15 +1,28 @@
# module_constant_def_ok
As a developer
I want to define $TYPE module constants
In order to make hardcoded values more visible
and to make it easier to change hardcoded values
```py
CONSTANT: $TYPE = $VAL0
@exported
def testEntry() -> i32:
return 0
return 9
```
```py
expect(9)
```
# module_constant_def_bad
As a developer
I want to receive a type error on an invalid assignment on a $TYPE module constant
In order to make debugging easier
```py
CONSTANT: $OTHER_TYPE = $VAL0
@ -17,3 +30,16 @@ CONSTANT: $OTHER_TYPE = $VAL0
def testEntry() -> i32:
return 0
```
```py
if TYPE_NAME.startswith('tuple_'):
expect_type_error(
'Tuple element count mismatch',
'The given literal must fit the expected type',
)
else:
expect_type_error(
'Must be tuple',
'The given literal must fit the expected type',
)
```

View File

@ -1,7 +1,9 @@
import functools
import json
import sys
import marko
import marko.md_renderer
def get_tests(template):
test_data = None
@ -28,7 +30,30 @@ def apply_settings(settings, txt):
txt = txt.replace(f'${k}', v)
return txt
def generate_code(template, settings):
def generate_assertion_expect(result, arg):
result.append('result = Suite(code_py).run_code()')
result.append(f'assert {repr(arg)} == result.returned_value')
def generate_assertion_expect_type_error(result, error_msg, error_comment = None):
result.append('with pytest.raises(Type3Exception) as exc_info:')
result.append(' Suite(code_py).run_code()')
result.append(f'assert {repr(error_msg)} == exc_info.value.args[0][0].msg')
result.append(f'assert {repr(error_comment)} == exc_info.value.args[0][0].comment')
def generate_assertions(settings, result_code):
result = []
locals_ = {
'TYPE_NAME': settings['TYPE_NAME'],
'expect': functools.partial(generate_assertion_expect, result),
'expect_type_error': functools.partial(generate_assertion_expect_type_error, result),
}
exec(result_code, {}, locals_)
return ' ' + '\n '.join(result) + '\n'
def generate_code(markdown, template, settings):
type_name = settings['TYPE_NAME']
print('"""')
@ -39,37 +64,45 @@ def generate_code(template, settings):
print('"""')
print('import pytest')
print()
print('from phasm.type3.entry import Type3Exception')
print()
print('from ..helpers import Suite')
print()
for test in get_tests(template):
assert len(test) == 2, test
heading, code_block = test
assert len(test) == 4, test
heading, paragraph, code_block1, code_block2 = test
assert isinstance(heading, marko.block.Heading)
assert isinstance(code_block, marko.block.FencedCode)
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)
code = apply_settings(settings, code_block.children[0].children)
code = code.rstrip('\n')
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.replace('\n', '\n '))
print(' """')
print(' code_py = """')
print(code)
print(inp_code.rstrip('\n'))
print('"""')
print()
print(' result = Suite(code_py).run_code()')
print()
print(' assert 24 == result.returned_value')
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 = marko.Markdown().parse(fil.read())
template = markdown.parse(fil.read())
with open(sys.argv[2], 'r', encoding='utf-8') as fil:
settings = json.load(fil)
@ -79,7 +112,7 @@ def main():
settings['OTHER_TYPE'] = '(u32, )'
generate_code(template, settings)
generate_code(markdown, template, settings)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,5 @@
{
"TYPE_NAME": "tuple_u64_u32_u8",
"TYPE": "(u64, u32, u8, )",
"VAL0": "(1000000, 1000, 1, )"
}