281 lines
10 KiB
Python
281 lines
10 KiB
Python
import pytest
|
|
|
|
from common.global_symbols import NotInit
|
|
from core.concept import DefinitionType
|
|
from parsers.ConceptDefinitionParser import ConceptDefinition, ConceptDefinitionParser
|
|
from parsers.ParserInput import ParserInput
|
|
from parsers.parser_utils import ParsingError, UnexpectedEof, UnexpectedToken
|
|
from parsers.tokenizer import Keywords, Token, TokenKind
|
|
|
|
|
|
def get_parser_input(text):
|
|
pi = ParserInput(text)
|
|
assert pi.init()
|
|
|
|
return pi
|
|
|
|
|
|
class TestRecognizeDefConcept:
|
|
@pytest.fixture()
|
|
def parser(self, sheerka):
|
|
return ConceptDefinitionParser()
|
|
|
|
@pytest.mark.parametrize("text", [
|
|
"",
|
|
" "])
|
|
def test_i_can_detect_empty_input(self, parser, text):
|
|
pi = get_parser_input(text)
|
|
res = parser.parse(pi)
|
|
|
|
assert res is None
|
|
assert parser.error_sink == [UnexpectedEof(Keywords.DEF, None)]
|
|
|
|
def test_must_start_with_def_keyword(self, parser):
|
|
pi = get_parser_input("hello")
|
|
|
|
res = parser.parse(pi)
|
|
assert res is None
|
|
assert parser.error_sink == [UnexpectedToken(Token(TokenKind.IDENTIFIER, "hello", 0, 1, 1), Keywords.DEF)]
|
|
|
|
@pytest.mark.parametrize("text, expected", [
|
|
("def concept hello", ConceptDefinition(name="hello")),
|
|
("def concept hello ", ConceptDefinition(name="hello")),
|
|
("def concept a + b", ConceptDefinition(name="a + b")),
|
|
("def concept a+b", ConceptDefinition(name="a + b")),
|
|
("def concept 'a+b'+c", ConceptDefinition(name="'a+b' + c")),
|
|
('def concept "a+b"+c', ConceptDefinition(name="a+b + c")),
|
|
('def concept "as if"', ConceptDefinition(name="as if")),
|
|
("def concept 'as if'", ConceptDefinition(name="'as if'")),
|
|
("def concept 'as' \"if\"", ConceptDefinition(name="'as' if")),
|
|
('def concept \'as\' "if"', ConceptDefinition(name="'as' if")),
|
|
])
|
|
def test_i_can_parse_def_concept_name(self, parser, text, expected):
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
assert actual == expected
|
|
|
|
def test_concept_name_is_mandatory(self, parser):
|
|
pi = get_parser_input("def concept as foo")
|
|
actual = parser.parse(pi)
|
|
|
|
assert len(parser.error_sink) == 1
|
|
assert isinstance(parser.error_sink[0], ParsingError)
|
|
assert parser.error_sink[0].message == "Name is mandatory."
|
|
assert actual is None
|
|
|
|
def test_new_line_is_not_allowed_in_concept_name(self, parser):
|
|
pi = get_parser_input("def concept complicated \n name as foo")
|
|
actual = parser.parse(pi)
|
|
|
|
assert len(parser.error_sink) == 1
|
|
assert isinstance(parser.error_sink[0], ParsingError)
|
|
assert parser.error_sink[0].message == "Newlines are not allowed in name."
|
|
assert actual is None
|
|
|
|
@pytest.mark.parametrize("text, part", [
|
|
("def concept foo as where True", "as"),
|
|
("def concept foo where as 1 + 1", "where"),
|
|
("def concept foo pre as 1 + 1", "pre"),
|
|
("def concept foo post as 1 + 1", "post"),
|
|
("def concept foo ret as 1 + 1", "ret"),
|
|
])
|
|
def test_empty_declarations_are_not_allowed(self, parser, text, part):
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
|
|
assert actual is None
|
|
assert len(parser.error_sink) == 1
|
|
assert isinstance(parser.error_sink[0], ParsingError)
|
|
assert parser.error_sink[0].message == f"Empty '{part}' declaration."
|
|
|
|
def test_empty_parts_are_not_initialized(self, parser):
|
|
pi = get_parser_input("def concept foo")
|
|
actual = parser.parse(pi)
|
|
|
|
assert isinstance(actual, ConceptDefinition)
|
|
assert actual.body is ""
|
|
assert actual.where is ""
|
|
assert actual.pre is ""
|
|
assert actual.post is ""
|
|
assert actual.ret is ""
|
|
|
|
def test_i_can_manage_all_parts(self, parser):
|
|
concept_def = "def concept foo"
|
|
concept_def += " where my where clause"
|
|
concept_def += " pre my pre clause"
|
|
concept_def += " as my body"
|
|
concept_def += " ret my return value"
|
|
concept_def += " post my post condition"
|
|
pi = get_parser_input(concept_def)
|
|
actual = parser.parse(pi)
|
|
|
|
assert isinstance(actual, ConceptDefinition)
|
|
assert actual.body == "my body"
|
|
assert actual.where == "my where clause"
|
|
assert actual.pre == "my pre clause"
|
|
assert actual.post == "my post condition"
|
|
assert actual.ret == "my return value"
|
|
|
|
@pytest.mark.parametrize("body", [
|
|
"c:#1001: is an int",
|
|
"c:one: is an int",
|
|
"'one' is an int",
|
|
'"one" is an in',
|
|
])
|
|
def test_i_can_manage_special_tokens_in_part(self, parser, body):
|
|
text = f"def concept foo as {body}"
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
|
|
assert isinstance(actual, ConceptDefinition)
|
|
assert actual.body == body
|
|
|
|
@pytest.mark.parametrize("text, expected_type, expected_definition, ", [
|
|
("def concept foo from def 'hello world'", DefinitionType.DEFAULT, "'hello world'"),
|
|
("def concept foo from 'hello world'", DefinitionType.DEFAULT, "'hello world'"),
|
|
("def concept foo from bnf my bnf definition", DefinitionType.BNF, "my bnf definition"),
|
|
])
|
|
def test_i_can_set_concept_definition(self, parser, text, expected_type, expected_definition):
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
|
|
assert isinstance(actual, ConceptDefinition)
|
|
assert actual.definition_type == expected_type
|
|
assert actual.definition == expected_definition
|
|
|
|
@pytest.mark.parametrize("text", [
|
|
"def concept foo from where True",
|
|
"def concept foo from bnf where True",
|
|
"def concept foo from def where True",
|
|
"def concept foo from bnf",
|
|
"def concept foo from def ",
|
|
])
|
|
def test_empy_definition_are_not_allowed(self, parser, text):
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
|
|
assert actual is None
|
|
assert parser.error_sink[0].message == "Empty 'from' declaration."
|
|
|
|
def test_i_can_parse_multiline_definition(self, parser):
|
|
text = """
|
|
def concept add one to a as
|
|
def func(x):
|
|
return x+1
|
|
func(a)
|
|
"""
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
|
|
assert isinstance(actual, ConceptDefinition)
|
|
assert actual.body == "def func(x):\n return x+1\nfunc(a)"
|
|
|
|
def test_i_can_parse_indention_mode(self, parser):
|
|
text = """
|
|
def concept add one to a as:
|
|
def func(x):
|
|
return x+1
|
|
func(a)
|
|
"""
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
|
|
assert isinstance(actual, ConceptDefinition)
|
|
assert actual.body == "def func(x):\n return x+1\nfunc(a)"
|
|
|
|
def test_i_can_detect_invalid_indentation(self, parser):
|
|
text = """
|
|
def concept add one to a as:
|
|
def func(x):
|
|
return x+1
|
|
func(a)
|
|
"""
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
|
|
assert actual is None
|
|
assert len(parser.error_sink) > 0
|
|
|
|
def test_i_can_can_use_colon_to_protect_keywords(self, parser):
|
|
text = """
|
|
def concept today as:
|
|
from datetime import date
|
|
today = date.today()
|
|
from:
|
|
give me the date !
|
|
"""
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
|
|
assert isinstance(actual, ConceptDefinition)
|
|
assert actual.body == "from datetime import date\ntoday = date.today()"
|
|
assert actual.definition == "give me the date !"
|
|
|
|
def test_i_can_parse_bnf_concept_with_regex(self, parser):
|
|
text = "def concept sha512 from bnf number | r'[a-f0-9]+' | (number r'[a-f0-9]+')+"
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
|
|
assert isinstance(actual, ConceptDefinition)
|
|
assert actual.definition == "number | r'[a-f0-9]+' | (number r'[a-f0-9]+')+"
|
|
|
|
@pytest.mark.parametrize("text, expected", [
|
|
("def concept foo auto_eval True", True),
|
|
("def concept foo auto_eval true", True),
|
|
("def concept foo auto_eval False", False),
|
|
("def concept foo auto_eval false", False),
|
|
])
|
|
def test_i_can_parse_auto_eval(self, parser, text, expected):
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
|
|
assert isinstance(actual, ConceptDefinition)
|
|
assert actual.auto_eval == expected
|
|
|
|
def test_auto_eval_is_set_to_false_by_default(self, parser):
|
|
pi = get_parser_input("def concept foo")
|
|
actual = parser.parse(pi)
|
|
|
|
assert actual.auto_eval is False
|
|
|
|
def test_empty_auto_eval_is_not_allowed(self, parser):
|
|
pi = get_parser_input("def concept foo auto_eval as 1")
|
|
actual = parser.parse(pi)
|
|
|
|
assert actual is None
|
|
assert parser.error_sink[0].message == "Empty 'auto_eval' declaration."
|
|
|
|
def test_i_cannot_parse_wrong_value(self, parser):
|
|
pi = get_parser_input("def concept foo auto_eval wrong_value")
|
|
actual = parser.parse(pi)
|
|
|
|
assert actual is None
|
|
assert parser.error_sink[0].message == "Invalid 'auto_eval' declaration (wrong_value is not recognized)"
|
|
|
|
@pytest.mark.parametrize("text, expected", [
|
|
("def concept foo def_var var", [("var", NotInit)]),
|
|
("def concept foo def_var var1 def_var var2", [("var1", NotInit), ("var2", NotInit)]),
|
|
("def concept foo def_var var1 var2", [("var1", NotInit), ("var2", NotInit)]),
|
|
("def concept foo def_var var1, var2", [("var1", NotInit), ("var2", NotInit)]),
|
|
("def concept foo def_var var1=10", [("var1", 10)]),
|
|
("def concept foo def_var var1 = 10", [("var1", 10)]),
|
|
("def concept foo def_var var1 = 'hello'", [("var1", "'hello'")]),
|
|
("def concept foo def_var var1 = hello", [("var1", "hello")]),
|
|
("def concept foo def_var var1, var2 = 10", [("var1", NotInit), ("var2", 10)]),
|
|
("def concept foo def_var var1='hello', var2 = 10", [("var1", "'hello'"), ("var2", 10)]),
|
|
("def concept foo def_var var1='hello' var2 = 10", [("var1", "'hello'"), ("var2", 10)]),
|
|
])
|
|
def test_i_can_parse_variable_definitions(self, parser, text, expected):
|
|
pi = get_parser_input(text)
|
|
actual = parser.parse(pi)
|
|
|
|
assert isinstance(actual, ConceptDefinition)
|
|
assert actual.def_var == expected
|
|
|
|
def test_empty_def_var_is_not_allowed(self, parser):
|
|
pi = get_parser_input("def concept foo def_var as 1")
|
|
actual = parser.parse(pi)
|
|
|
|
assert actual is None
|
|
assert parser.error_sink[0].message == "Empty 'def_var' declaration."
|