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 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_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" 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)