More framework
This commit is contained in:
parent
ef00b3a91c
commit
ff0286bcf6
9
Makefile
9
Makefile
@ -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
|
||||
|
||||
@ -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 = []
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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',
|
||||
)
|
||||
```
|
||||
|
||||
@ -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()
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"TYPE_NAME": "tuple_u64_u32_u8",
|
||||
"TYPE": "(u64, u32, u8, )",
|
||||
"VAL0": "(1000000, 1000, 1, )"
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user