Added chicken and egg recursion detection
This commit is contained in:
+1
-1
@@ -48,7 +48,7 @@ class BaseTest:
|
||||
|
||||
@staticmethod
|
||||
def pretval(concept, source=None, parser="parsers.name"):
|
||||
"""ParserResult ret_val"""
|
||||
"""ParserResult ret_val (p stands for ParserResult)"""
|
||||
return ReturnValueConcept(
|
||||
"some_name",
|
||||
True,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import PROPERTIES_TO_SERIALIZE
|
||||
from core.concept import PROPERTIES_TO_SERIALIZE, Concept
|
||||
from sdp.sheerkaDataProvider import SheerkaDataProvider
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
@@ -80,12 +80,10 @@ class TestSheerkaCreateNewConcept(TestUsingMemoryBasedSheerka):
|
||||
sheerka.cache_by_key = {} # reset the cache
|
||||
loaded = sheerka.get(concept.key)
|
||||
|
||||
assert loaded is not None
|
||||
assert loaded == concept
|
||||
|
||||
# I can also get it by its id
|
||||
loaded = sheerka.sdp.get(sheerka.CONCEPTS_BY_ID_ENTRY, concept.id)
|
||||
assert loaded is not None
|
||||
assert loaded == concept
|
||||
|
||||
def test_i_can_get_a_concept_by_its_id(self):
|
||||
@@ -96,7 +94,6 @@ class TestSheerkaCreateNewConcept(TestUsingMemoryBasedSheerka):
|
||||
sheerka.cache_by_key = {} # reset the cache
|
||||
loaded = sheerka.get_by_id(concept.id)
|
||||
|
||||
assert loaded is not None
|
||||
assert loaded == concept
|
||||
|
||||
def test_i_can_get_list_of_concept_when_same_key_when_no_cache(self):
|
||||
@@ -178,3 +175,12 @@ class TestSheerkaCreateNewConcept(TestUsingMemoryBasedSheerka):
|
||||
|
||||
result = sheerka.get(concept1.key, "wrong id")
|
||||
assert sheerka.isinstance(result, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
|
||||
def test_concept_that_references_itself_is_correctly_created(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = Concept("foo", body="foo")
|
||||
|
||||
res = sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert res.status
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, ParserResultConcept
|
||||
from core.concept import Concept, simplec, DoNotResolve, ConceptParts, Property
|
||||
from core.concept import Concept, simplec, DoNotResolve, ConceptParts, Property, InfiniteRecursionResolved
|
||||
from parsers.PythonParser import PythonNode
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
@@ -369,3 +369,91 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.WHERE_CLAUSE_FAILED)
|
||||
assert evaluated.body == concept
|
||||
|
||||
def test_i_can_detect_infinite_recursion_with_numeric_constant(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
one_str = Concept("one", body="1")
|
||||
one_digit = Concept("1", body="one")
|
||||
|
||||
sheerka.add_in_cache(one_str)
|
||||
sheerka.add_in_cache(one_digit)
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), one_digit)
|
||||
assert evaluated.key == one_digit.key
|
||||
assert evaluated.body == InfiniteRecursionResolved(1)
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), one_str)
|
||||
assert evaluated.key == one_str.key
|
||||
assert evaluated.body == InfiniteRecursionResolved(1)
|
||||
|
||||
def test_i_can_detect_infinite_recursion_with_boolean_constant(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
true_str = Concept("true", body="True")
|
||||
true_bool = Concept("True", body="true")
|
||||
|
||||
sheerka.add_in_cache(true_str)
|
||||
sheerka.add_in_cache(true_bool)
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), true_str)
|
||||
assert evaluated.key == true_str.key
|
||||
assert evaluated.body == InfiniteRecursionResolved(True)
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), true_bool)
|
||||
assert evaluated.key == true_bool.key
|
||||
assert evaluated.body == InfiniteRecursionResolved(True)
|
||||
|
||||
def test_i_can_detect_infinite_recursion_with_constant_with_more_concepts(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
c1 = sheerka.add_in_cache(Concept("one", body="1"))
|
||||
c2 = sheerka.add_in_cache(Concept("1", body="2"))
|
||||
c3 = sheerka.add_in_cache(Concept("2", body="3"))
|
||||
c4 = sheerka.add_in_cache(Concept("3", body="one"))
|
||||
|
||||
for concept in (c1, c2, c3, c4):
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == InfiniteRecursionResolved(3)
|
||||
|
||||
def test_i_can_detect_infinite_recursion_when_no_constant(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
foo = sheerka.add_in_cache(Concept("foo", body="bar"))
|
||||
bar = sheerka.add_in_cache(Concept("bar", body="baz"))
|
||||
baz = sheerka.add_in_cache(Concept("baz", body="qux"))
|
||||
qux = sheerka.add_in_cache(Concept("qux", body="foo"))
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), foo)
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
assert evaluated.body == {foo, bar, baz, qux}
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), bar)
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
assert evaluated.body == {foo, bar, baz, qux}
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), baz)
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
assert evaluated.body == {foo, bar, baz, qux}
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), qux)
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
assert evaluated.body == {foo, bar, baz, qux}
|
||||
|
||||
def test_i_can_detect_auto_recursion(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
foo = sheerka.add_in_cache(Concept("foo", body="foo"))
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), foo)
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
assert evaluated.body == {foo}
|
||||
|
||||
def test_i_can_manage_auto_recursion_when_constant(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
one = Concept("1", body="1")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), one)
|
||||
assert evaluated.key == one.key
|
||||
assert evaluated.body == 1
|
||||
|
||||
@@ -242,3 +242,41 @@ class TestSheerka(TestUsingFileBasedSheerka):
|
||||
# only test a random one, it will be the same for the others
|
||||
sheerka = self.get_sheerka()
|
||||
assert not sheerka.is_success(sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS))
|
||||
|
||||
def test_cache_is_updated_after_get(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
# updated when by_key returns one element
|
||||
sheerka.create_new_concept(self.get_context(sheerka), Concept("foo", body="1"))
|
||||
sheerka.reset_cache()
|
||||
sheerka.get("foo")
|
||||
assert "foo" in sheerka.cache_by_key
|
||||
assert "1001" in sheerka.cache_by_id
|
||||
|
||||
# updated when by_key returns two elements
|
||||
sheerka.create_new_concept(self.get_context(sheerka), Concept("foo", body="2"))
|
||||
sheerka.reset_cache()
|
||||
sheerka.get("foo")
|
||||
assert "foo" in sheerka.cache_by_key
|
||||
assert "1001" in sheerka.cache_by_id
|
||||
assert "1002" in sheerka.cache_by_id
|
||||
|
||||
# updated when by_id
|
||||
sheerka.reset_cache()
|
||||
sheerka.get_by_id("1001")
|
||||
assert "1001" in sheerka.cache_by_id
|
||||
assert "foo" not in sheerka.cache_by_key # cache_by_key not updated as "1001" is not the only one
|
||||
|
||||
def test_i_can_get_by_key_several_times(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.create_new_concept(self.get_context(sheerka), Concept("foo", body="1"))
|
||||
sheerka.create_new_concept(self.get_context(sheerka), Concept("foo", body="2"))
|
||||
|
||||
sheerka.reset_cache()
|
||||
sheerka.get("foo", "1001") # only one element requested. But the cache must be updated with two elements
|
||||
|
||||
# let's check it
|
||||
concepts = sheerka.get("foo")
|
||||
assert len(concepts) == 2
|
||||
assert concepts[0].id == "1001"
|
||||
assert concepts[1].id == "1002"
|
||||
|
||||
@@ -69,9 +69,8 @@ class TestAddConceptEvaluator(TestUsingMemoryBasedSheerka):
|
||||
return ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=concept))
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=DefConceptNode([]))),
|
||||
True),
|
||||
(ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=DefConceptNode([]))),
|
||||
True),
|
||||
(ReturnValueConcept(BaseParser.PREFIX + "some_name", False, ParserResultConcept(value=DefConceptNode([]))),
|
||||
False),
|
||||
(ReturnValueConcept(BaseParser.PREFIX + "some_name", True, "not a ParserResultConcept"), False),
|
||||
@@ -133,11 +132,17 @@ class TestAddConceptEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
assert from_db.compiled == {} # ast is not saved in db
|
||||
|
||||
def test_i_can_get_props_from_python_node(self):
|
||||
def test_i_can_get_props_from_python_node_when_long_name(self):
|
||||
ret_val = self.get_concept_part("isinstance(a, str)")
|
||||
context = self.get_context()
|
||||
|
||||
assert AddConceptEvaluator.get_props(context.sheerka, ret_val, ["a"]) == ["a"]
|
||||
assert AddConceptEvaluator.get_props(context.sheerka, ret_val, ["a", "b"]) == ["a"]
|
||||
|
||||
def test_i_cannot_get_props_from_python_node_when_name_has_only_one_token(self):
|
||||
ret_val = self.get_concept_part("isinstance(a, str)")
|
||||
context = self.get_context()
|
||||
|
||||
assert AddConceptEvaluator.get_props(context.sheerka, ret_val, ["a"]) == []
|
||||
|
||||
def test_i_can_get_props_from_another_concept(self):
|
||||
concept = Concept("hello").def_prop("a").def_prop("b")
|
||||
@@ -153,3 +158,16 @@ class TestAddConceptEvaluator(TestUsingMemoryBasedSheerka):
|
||||
ret_val = self.get_return_value("mult (('+'|'-') add)?", parsing_expression)
|
||||
|
||||
assert AddConceptEvaluator.get_props(self.get_context(), ret_val, []) == ["add", "mult"]
|
||||
|
||||
def test_concept_that_references_itself_is_correctly_created(self):
|
||||
context = self.get_context()
|
||||
def_concept_as_return_value = self.get_def_concept("foo", body="foo")
|
||||
|
||||
ret_val = AddConceptEvaluator().eval(context, def_concept_as_return_value)
|
||||
|
||||
assert ret_val.status
|
||||
new_concept = ret_val.body.body
|
||||
assert new_concept.name == 'foo'
|
||||
assert new_concept.metadata.body == 'foo'
|
||||
assert new_concept.props == {}
|
||||
assert new_concept.metadata.props == []
|
||||
|
||||
@@ -103,7 +103,7 @@ class TestAddConceptEvaluator(TestUsingMemoryBasedSheerka):
|
||||
assert context.sheerka.isinstance(result.value, BuiltinConcepts.CONCEPT_EVAL_ERROR)
|
||||
assert result.value.concept == concept_plus
|
||||
assert result.value.property_name == "b"
|
||||
assert context.sheerka.isinstance(result.value.error, BuiltinConcepts.TOO_MANY_ERRORS)
|
||||
assert context.sheerka.isinstance(result.value.error, BuiltinConcepts.ERROR)
|
||||
|
||||
def test_that_error_concepts_are_transformed_into_errors_only_if_the_key_is_different(self):
|
||||
context = self.get_context()
|
||||
|
||||
@@ -671,3 +671,28 @@ as:
|
||||
|
||||
res = sheerka.evaluate_user_input("eval twenty three")
|
||||
assert len(res) > 1
|
||||
|
||||
def test_i_can_detect_when_only_one_evaluator_is_in_error(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
sheerka.evaluate_user_input("def concept 1 as one")
|
||||
res = sheerka.evaluate_user_input("1")
|
||||
assert len(res) == 1
|
||||
assert not res[0].status
|
||||
assert sheerka.isinstance(res[0].body, BuiltinConcepts.CONCEPT_EVAL_ERROR)
|
||||
|
||||
def test_i_can_manage_some_type_of_infinite_recursion(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
sheerka.evaluate_user_input("def concept one as 1")
|
||||
sheerka.evaluate_user_input("def concept 1 as one")
|
||||
res = sheerka.evaluate_user_input("one + 1")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 2
|
||||
|
||||
res = sheerka.evaluate_user_input("1 + 1")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 2
|
||||
|
||||
|
||||
Reference in New Issue
Block a user