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))
|
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
|
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)
|
venv/bin/pytest tests $(TEST_FLAGS)
|
||||||
|
|
||||||
lint: venv/.done
|
lint: venv/.done
|
||||||
@ -39,12 +39,15 @@ venv/.done: requirements.txt
|
|||||||
venv/bin/python3 -m pip install -r $^
|
venv/bin/python3 -m pip install -r $^
|
||||||
touch $@
|
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
|
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_u32.json > $@
|
venv/bin/python3 tests/integration/test_lang/generator.py tests/integration/test_lang/generator.md tests/integration/test_lang/generator_$*.json > $@
|
||||||
|
|
||||||
clean-examples:
|
clean-examples:
|
||||||
rm -f examples/*.wat examples/*.wasm examples/*.wat.html examples/*.py.html
|
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
|
.SECONDARY: # Keep intermediate files
|
||||||
|
|
||||||
.PHONY: examples
|
.PHONY: examples
|
||||||
|
|||||||
@ -392,10 +392,10 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
if isinstance(self.type3, types.AppliedType3):
|
if isinstance(self.type3, types.AppliedType3):
|
||||||
if self.type3.base == types.tuple:
|
if self.type3.base == types.tuple:
|
||||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
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):
|
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 = []
|
res = []
|
||||||
|
|
||||||
@ -412,13 +412,13 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
if self.type3.base == types.static_array:
|
if self.type3.base == types.static_array:
|
||||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
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 2 == len(self.type3.args)
|
||||||
assert isinstance(self.type3.args[1], types.IntType3)
|
assert isinstance(self.type3.args[1], types.IntType3)
|
||||||
|
|
||||||
if self.type3.args[1].value != len(self.literal.value):
|
if self.type3.args[1].value != len(self.literal.value):
|
||||||
return Error('Member count mismatch')
|
return Error('Member count mismatch', comment=self.comment)
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,10 @@ def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase
|
|||||||
|
|
||||||
def constant(ctx: Context, inp: ourlang.Constant) -> ConstraintGenerator:
|
def constant(ctx: Context, inp: ourlang.Constant) -> ConstraintGenerator:
|
||||||
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)):
|
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
|
return
|
||||||
|
|
||||||
raise NotImplementedError(constant, inp)
|
raise NotImplementedError(constant, inp)
|
||||||
|
|||||||
@ -1,15 +1,28 @@
|
|||||||
# module_constant_def_ok
|
# 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
|
```py
|
||||||
CONSTANT: $TYPE = $VAL0
|
CONSTANT: $TYPE = $VAL0
|
||||||
|
|
||||||
@exported
|
@exported
|
||||||
def testEntry() -> i32:
|
def testEntry() -> i32:
|
||||||
return 0
|
return 9
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
expect(9)
|
||||||
```
|
```
|
||||||
|
|
||||||
# module_constant_def_bad
|
# 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
|
```py
|
||||||
CONSTANT: $OTHER_TYPE = $VAL0
|
CONSTANT: $OTHER_TYPE = $VAL0
|
||||||
|
|
||||||
@ -17,3 +30,16 @@ CONSTANT: $OTHER_TYPE = $VAL0
|
|||||||
def testEntry() -> i32:
|
def testEntry() -> i32:
|
||||||
return 0
|
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 json
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import marko
|
import marko
|
||||||
|
import marko.md_renderer
|
||||||
|
|
||||||
def get_tests(template):
|
def get_tests(template):
|
||||||
test_data = None
|
test_data = None
|
||||||
@ -28,7 +30,30 @@ def apply_settings(settings, txt):
|
|||||||
txt = txt.replace(f'${k}', v)
|
txt = txt.replace(f'${k}', v)
|
||||||
return txt
|
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']
|
type_name = settings['TYPE_NAME']
|
||||||
|
|
||||||
print('"""')
|
print('"""')
|
||||||
@ -39,37 +64,45 @@ def generate_code(template, settings):
|
|||||||
print('"""')
|
print('"""')
|
||||||
print('import pytest')
|
print('import pytest')
|
||||||
print()
|
print()
|
||||||
|
print('from phasm.type3.entry import Type3Exception')
|
||||||
|
print()
|
||||||
print('from ..helpers import Suite')
|
print('from ..helpers import Suite')
|
||||||
print()
|
print()
|
||||||
|
|
||||||
for test in get_tests(template):
|
for test in get_tests(template):
|
||||||
assert len(test) == 2, test
|
assert len(test) == 4, test
|
||||||
heading, code_block = test
|
heading, paragraph, code_block1, code_block2 = test
|
||||||
|
|
||||||
assert isinstance(heading, marko.block.Heading)
|
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)
|
test_id = apply_settings(settings, heading.children[0].children)
|
||||||
code = apply_settings(settings, code_block.children[0].children)
|
user_story = apply_settings(settings, markdown.renderer.render(paragraph))
|
||||||
|
inp_code = apply_settings(settings, code_block1.children[0].children)
|
||||||
code = code.rstrip('\n')
|
|
||||||
|
|
||||||
|
result_code = markdown.renderer.render_children(code_block2)
|
||||||
|
|
||||||
print('@pytest.mark.integration_test')
|
print('@pytest.mark.integration_test')
|
||||||
print(f'def test_{type_name}_{test_id}():')
|
print(f'def test_{type_name}_{test_id}():')
|
||||||
|
print(' """')
|
||||||
|
print(' ' + user_story.replace('\n', '\n '))
|
||||||
|
print(' """')
|
||||||
print(' code_py = """')
|
print(' code_py = """')
|
||||||
print(code)
|
print(inp_code.rstrip('\n'))
|
||||||
print('"""')
|
print('"""')
|
||||||
print()
|
print()
|
||||||
print(' result = Suite(code_py).run_code()')
|
print(generate_assertions(settings, result_code))
|
||||||
print()
|
|
||||||
print(' assert 24 == result.returned_value')
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
markdown = marko.Markdown(
|
||||||
|
renderer=marko.md_renderer.MarkdownRenderer,
|
||||||
|
)
|
||||||
with open(sys.argv[1], 'r', encoding='utf-8') as fil:
|
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:
|
with open(sys.argv[2], 'r', encoding='utf-8') as fil:
|
||||||
settings = json.load(fil)
|
settings = json.load(fil)
|
||||||
@ -79,7 +112,7 @@ def main():
|
|||||||
|
|
||||||
settings['OTHER_TYPE'] = '(u32, )'
|
settings['OTHER_TYPE'] = '(u32, )'
|
||||||
|
|
||||||
generate_code(template, settings)
|
generate_code(markdown, template, settings)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
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