import ast import pytest import os from os import path import shutil from core import utils from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept from core.concept import Concept, ConceptParts from core.sheerka import Sheerka, ExecutionContext from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator from parsers.DefaultParser import DefaultParser from parsers.PythonParser import PythonParser from sdp.sheerkaDataProvider import SheerkaDataProvider, SheerkaDataProviderDuplicateKeyError tests_root = path.abspath("../build/tests") root_folder = "init_folder" @pytest.fixture(autouse=True) def init_test(): if path.exists(tests_root): shutil.rmtree(tests_root) if not path.exists(tests_root): os.makedirs(tests_root) current_pwd = os.getcwd() os.chdir(tests_root) yield None os.chdir(current_pwd) def test_root_folder_is_created_after_initialization(): return_value = Sheerka().initialize(root_folder) assert return_value.status, "initialisation should be successful" assert os.path.exists(root_folder), "init folder should be created" def test_i_can_list_builtin_concepts(): sheerka = get_sheerka() builtins = list(sheerka.get_builtins_classes_as_dict()) assert str(BuiltinConcepts.ERROR) in builtins assert str(BuiltinConcepts.RETURN_VALUE) in builtins def test_builtin_concepts_are_initialized(): sheerka = get_sheerka() assert len(sheerka.concepts_cache) == len(BuiltinConcepts) for concept_name in BuiltinConcepts: assert str(concept_name) in sheerka.concepts_cache assert sheerka.sdp.get_safe(sheerka.CONCEPTS_ENTRY, str(concept_name)) is not None for key, concept_class in sheerka.get_builtins_classes_as_dict().items(): assert isinstance(sheerka.concepts_cache[key], concept_class) def test_builtin_concepts_can_be_updated(): sheerka = get_sheerka() loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA) loaded_sheerka.desc = "I have a description" sheerka.sdp.modify("Test", sheerka.CONCEPTS_ENTRY, loaded_sheerka.key, loaded_sheerka) sheerka = get_sheerka() loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA) assert loaded_sheerka.desc == "I have a description" def test_i_can_add_a_concept(): sheerka = get_sheerka() concept = get_default_concept() res = sheerka.create_new_concept(get_context(sheerka), concept) assert res.status assert sheerka.isinstance(res.value, BuiltinConcepts.NEW_CONCEPT) concept_found = res.value.body for prop in Concept.props_to_serialize: assert getattr(concept_found, prop) == getattr(concept, prop) assert concept_found.key == "__var__0 + __var__1" assert concept_found.id == "1001" assert concept.key in sheerka.concepts_cache assert path.exists(sheerka.sdp.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_found.get_digest())) def test_i_cannot_add_the_same_concept_twice(): """ Checks that duplicated concepts are managed by sheerka, not by sheerka.sdp :return: """ sheerka = get_sheerka() concept = get_default_concept() sheerka.create_new_concept(get_context(sheerka), concept) res = sheerka.create_new_concept(get_context(sheerka), concept) assert not res.status assert sheerka.isinstance(res.value, BuiltinConcepts.ERROR) assert res.value.body.args[0] == "Duplicate object." def test_i_can_get_a_builtin_concept_by_their_enum_or_the_string(): """ Checks that a concept can be found its name even when there are variables in the name (ex 'hello + a' or 'a + b' ) :return: """ sheerka = get_sheerka() for key in sheerka.get_builtins_classes_as_dict(): assert sheerka.get(key) is not None assert sheerka.get(str(key)) is not None def test_i_can_get_new_concept(): sheerka = get_sheerka() concept = get_default_concept() sheerka.create_new_concept(get_context(sheerka), concept) from_cache = sheerka.get(concept.key) assert from_cache is not None assert from_cache.key == concept.key assert from_cache == concept def test_i_first_look_in_local_cache(): sheerka = get_sheerka() concept = get_default_concept() sheerka.create_new_concept(get_context(sheerka), concept) sheerka.concepts_cache[concept.key].pre = "I have modified the concept in cache" from_cache = sheerka.get(concept.key) assert from_cache is not None assert from_cache.key == concept.key assert from_cache.pre == "I have modified the concept in cache" def test_i_can_get_a_known_concept_when_not_in_cache(): """ When not in cache, uses sdp :return: """ sheerka = get_sheerka() concept = get_default_concept() sheerka.create_new_concept(get_context(sheerka), concept) sheerka.concepts_cache = {} # reset the cache loaded = sheerka.get(concept.key) assert loaded is not None assert loaded == concept def test_i_can_get_list_of_concept_when_same_key_when_no_cache(): sheerka = get_sheerka() concept1 = get_default_concept() concept2 = get_default_concept() concept2.body = "a+b" res1 = sheerka.create_new_concept(get_context(sheerka), concept1) res2 = sheerka.create_new_concept(get_context(sheerka), concept2) assert res1.value.body.key == res2.value.body.key # same key sheerka.concepts_cache = {} # reset the cache from_cache = sheerka.get(concept1.key) assert len(from_cache) == 2 assert from_cache[0] == concept1 assert from_cache[1] == concept2 def test_i_can_get_list_of_concept_when_same_key_when_cache(): sheerka = get_sheerka() concept1 = get_default_concept() concept2 = get_default_concept() concept2.body = "a+b" res1 = sheerka.create_new_concept(get_context(sheerka), concept1) res2 = sheerka.create_new_concept(get_context(sheerka), concept2) assert res1.value.body.key == res2.value.body.key # same key # sheerka.concepts_cache = {} # Do not reset the cache from_cache = sheerka.get(concept1.key) assert len(from_cache) == 2 assert from_cache[0] == concept1 assert from_cache[1] == concept2 def test_unknown_concept_is_return_when_the_concept_is_not_found(): sheerka = get_sheerka() loaded = sheerka.get("fake_key") assert loaded is not None assert sheerka.isinstance(loaded, BuiltinConcepts.UNKNOWN_CONCEPT) assert loaded.body == "fake_key" def test_i_can_instantiate_a_builtin_concept_when_it_has_its_own_class(): sheerka = get_sheerka() ret = sheerka.new(BuiltinConcepts.RETURN_VALUE, who="who", status="status", value="value", message="message") assert isinstance(ret, ReturnValueConcept) assert ret.key == str(BuiltinConcepts.RETURN_VALUE) assert ret.who == "who" assert ret.status == "status" assert ret.value == "value" assert ret.message == "message" def test_i_can_instantiate_a_builtin_concept_when_no_specific_class(): sheerka = get_sheerka() ret = sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, body="fake_concept") assert isinstance(ret, Concept) assert ret.key == str(BuiltinConcepts.UNKNOWN_CONCEPT) assert ret.body == "fake_concept" def test_i_can_instantiate_a_concept(): sheerka = get_sheerka() concept = get_default_concept() sheerka.create_new_concept(get_context(sheerka), concept) new = sheerka.new(concept.key, a=10, b="value") assert sheerka.isinstance(new, concept) for prop in Concept.props_to_serialize: assert getattr(new, prop) == getattr(concept, prop) assert new.props["a"].value == 10 assert new.props["b"].value == "value" def test_instances_are_different_when_asking_for_new(): sheerka = get_sheerka() concept = get_default_concept() sheerka.create_new_concept(get_context(sheerka), concept) new1 = sheerka.new(concept.key, a=10, b="value") new2 = sheerka.new(concept.key, a=10, b="value") assert new1 == new2 assert id(new1) != id(new2) def test_i_get_the_same_instance_when_is_unique_is_true(): sheerka = get_sheerka() concept = get_unique_concept() sheerka.create_new_concept(get_context(sheerka), concept) new1 = sheerka.new(concept.key, a=10, b="value") new2 = sheerka.new(concept.key, a=10, b="value") assert new1 == new2 assert id(new1) == id(new2) def test_i_cannot_instantiate_an_unknown_concept(): sheerka = get_sheerka() new = sheerka.new("fake_concept") assert sheerka.isinstance(new, BuiltinConcepts.UNKNOWN_CONCEPT) assert new.body == "fake_concept" def test_i_cannot_instantiate_when_properties_are_not_recognized(): sheerka = get_sheerka() concept = get_default_concept() sheerka.create_new_concept(get_context(sheerka), concept) new = sheerka.new(concept.key, a=10, c="value") assert sheerka.isinstance(new, BuiltinConcepts.UNKNOWN_PROPERTY) assert new.property_name == "c" assert sheerka.isinstance(new.concept, concept) def test_i_can_use_expect_one_when_empty(): sheerka = get_sheerka() res = sheerka.expect_one(get_context(sheerka), []) assert not res.status assert sheerka.isinstance(res.value, BuiltinConcepts.IS_EMPTY) def test_i_can_use_expect_one_when_too_many_success(): sheerka = get_sheerka() items = [ ReturnValueConcept("who", True, None), ReturnValueConcept("who", True, None), ] res = sheerka.expect_one(get_context(sheerka), items) assert not res.status assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_SUCCESS) assert res.value.obj == items def test_i_can_use_expect_when_only_errors_1(): sheerka = get_sheerka() items = [ ReturnValueConcept("who", False, None), ] res = sheerka.expect_one(get_context(sheerka), items) assert not res.status assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS) assert res.value.obj == items def test_i_can_use_expect_when_only_errors_2(): sheerka = get_sheerka() items = [ ReturnValueConcept("who", False, None), ReturnValueConcept("who", False, None), ] res = sheerka.expect_one(get_context(sheerka), items) assert not res.status assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS) assert res.value.obj == items def test_i_can_use_expect_one_when_one_success_1(): sheerka = get_sheerka() items = [ ReturnValueConcept("who", True, None), ] res = sheerka.expect_one(get_context(sheerka), items) assert res.status assert res == items[0] def test_i_can_use_expect_one_when_one_success_2(): sheerka = get_sheerka() items = [ ReturnValueConcept("who", False, None), ReturnValueConcept("who", True, None), ReturnValueConcept("who", False, None), ] res = sheerka.expect_one(get_context(sheerka), items) assert res.status assert res == items[1] def test_i_can_use_expect_one_when_not_a_list_true(): sheerka = get_sheerka() res = sheerka.expect_one(get_context(sheerka), ReturnValueConcept("who", True, None)) assert res.status assert res == ReturnValueConcept("who", True, None) def test_i_can_use_expect_one_when_not_a_list_false(): sheerka = get_sheerka() res = sheerka.expect_one(get_context(sheerka), ReturnValueConcept("who", False, None)) assert not res.status assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS) assert res.value.obj == [ReturnValueConcept("who", False, None)] @pytest.mark.parametrize("text, expected", [ ("1 + 1", 2), ("sheerka.test()", 'I have access to Sheerka !') ]) def test_i_can_eval_python_expressions_with_no_variable(text, expected): sheerka = get_sheerka() res = sheerka.eval(text) assert len(res) == 1 assert res[0].status assert res[0].value == expected def test_i_can_eval_concept_with_python_body(): sheerka = get_sheerka() concept = Concept(name="one", body="1") sheerka.add_in_cache(concept) text = "one" res = sheerka.eval(text) assert len(res) == 1 assert res[0].status assert res[0].value == 1 def test_i_can_eval_concept_with_concept_body(): sheerka = get_sheerka() concept_one = Concept(name="one") concept_un = Concept(name="un", body="one") sheerka.add_in_cache(concept_one) sheerka.add_in_cache(concept_un) res = sheerka.eval("un") return_value = res[0].value assert len(res) == 1 assert res[0].status assert sheerka.isinstance(return_value, concept_one) def test_i_can_eval_concept_with_no_body(): sheerka = get_sheerka() concept = Concept(name="one") sheerka.add_in_cache(concept) text = "one" res = sheerka.eval(text) assert len(res) == 1 assert res[0].status assert res[0].value == concept assert id(res[0].value) != id(concept) def test_is_unique_property_is_used_when_evaluating(): sheerka = get_sheerka() concept = Concept(name="one", is_unique=True) sheerka.add_in_cache(concept) text = "one" res = sheerka.eval(text) assert len(res) == 1 assert res[0].status assert res[0].value == concept assert id(res[0].value) == id(concept) def test_i_can_eval_def_concept_request(): text = """ def concept a + b where isinstance(a, int) and isinstance(b, int) pre isinstance(a, int) and isinstance(b, int) post isinstance(res, int) as: def func(x,y): return x+y func(a,b) """ expected = get_default_concept() expected.id = "1001" expected.desc = None expected.init_key() sheerka = get_sheerka() res = sheerka.eval(text) assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT) concept_saved = res[0].value.body for prop in Concept.props_to_serialize: assert getattr(concept_saved, prop) == getattr(expected, prop) assert concept_saved.key in sheerka.concepts_cache assert path.exists(sheerka.sdp.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_saved.get_digest())) def test_i_can_eval_def_concept_part_when_one_part_is_a_ref_of_another_concept(): """ In this test, we test that the properties of 'concept a xx b' (which are 'a' and 'b') are correctly detected, because of the concept 'a plus b' in its body :return: """ sheerka = get_sheerka() # concept 'a plus b' is known concept_a_plus_b = Concept(name="a plus b").set_prop("a").set_prop("b") sheerka.add_in_cache(concept_a_plus_b) res = sheerka.eval("def concept a xx b as a plus b") expected = Concept(name="a xx b", body="a plus b").set_prop("a").set_prop("b").init_key() expected.id = "1001" assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT) concept_saved = res[0].value.body for prop in Concept.props_to_serialize: assert getattr(concept_saved, prop) == getattr(expected, prop) assert concept_saved.key in sheerka.concepts_cache assert path.exists(sheerka.sdp.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_saved.get_digest())) def test_i_cannot_eval_the_same_def_concept_twice(): text = """ def concept a + b where isinstance(a, int) and isinstance(b, int) pre isinstance(a, int) and isinstance(b, int) post isinstance(res, int) as: def func(x,y): return x+y func(a,b) """ sheerka = get_sheerka() sheerka.eval(text) res = sheerka.eval(text) assert len(res) == 1 assert not res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.CONCEPT_ALREADY_DEFINED) @pytest.mark.parametrize("text", [ "", " ", "\n", ]) def test_i_can_eval_a_empty_input(text): sheerka = get_sheerka() res = sheerka.eval(text) assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.NOP) def test_i_can_eval_concept_with_variable(): sheerka = get_sheerka() concept_hello = Concept(name="hello a").set_prop("a") concept_foo = Concept(name="foo") sheerka.add_in_cache(concept_hello) sheerka.add_in_cache(concept_foo) res = sheerka.eval("hello foo") return_value = res[0].value assert len(res) == 1 assert res[0].status assert sheerka.isinstance(return_value, concept_hello) assert return_value.props["a"].value == concept_foo def test_i_can_eval_concept_with_variable_and_python_as_body(): sheerka = get_sheerka() sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").set_prop("a")) sheerka.add_in_cache(Concept(name="foo", body="'foo'")) res = sheerka.eval("hello foo") assert len(res) == 1 assert res[0].status assert res[0].value, "hello foo" def test_i_can_eval_duplicate_concepts_with_same_value(): sheerka = get_sheerka() sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").set_prop("a")) sheerka.add_in_cache(Concept(name="hello foo", body="'hello foo'")) sheerka.add_in_cache(Concept(name="foo", body="'foo'")) res = sheerka.eval("hello foo") assert len(res) == 1 assert res[0].status assert res[0].value, "hello foo" assert res[0].who == sheerka.get_evaluator_name(MultipleSameSuccessEvaluator.NAME) def test_i_cannot_manage_duplicate_concepts_when_the_values_are_different(): sheerka = get_sheerka() sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").set_prop("a")) sheerka.add_in_cache(Concept(name="hello foo", body="'hello foo'")) sheerka.add_in_cache(Concept(name="foo", body="'another value'")) res = sheerka.eval("hello foo") assert len(res) == 1 assert not res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.TOO_MANY_SUCCESS) concepts = res[0].value.obj assert len(concepts) == 2 sorted_values = sorted(concepts, key=lambda x: x.value) assert sorted_values[0].value == "hello another value" assert sorted_values[1].value == "hello foo" def test_i_can_manage_concepts_with_the_same_key_when_values_are_the_same(): sheerka = get_sheerka() context = get_context(sheerka) sheerka.create_new_concept(context, Concept(name="hello a", body="'hello ' + a").set_prop("a")) sheerka.create_new_concept(context, Concept(name="hello b", body="'hello ' + b").set_prop("b")) res = sheerka.eval("hello 'foo'") assert len(res) == 1 assert res[0].status assert res[0].value, "hello foo" assert res[0].who == sheerka.get_evaluator_name(MultipleSameSuccessEvaluator.NAME) def get_sheerka(): sheerka = Sheerka() sheerka.initialize(root_folder) return sheerka def get_context(sheerka): return ExecutionContext("test", "xxx", sheerka) def get_default_concept(): concept = Concept( name="a + b", where="isinstance(a, int) and isinstance(b, int)", pre="isinstance(a, int) and isinstance(b, int)", post="isinstance(res, int)", body="def func(x,y):\n return x+y\nfunc(a,b)", desc="specific description") concept.set_prop("a", "value1") concept.set_prop("b", "value2") return concept def get_unique_concept(): return Concept(name="unique", is_unique=True)