import pytest import os from os import path import shutil from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept from core.concept import Concept, PROPERTIES_TO_SERIALIZE, Property from core.sheerka import Sheerka, ExecutionContext from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator from parsers.BaseParser import BaseParser from parsers.ConceptLexerParser import Sequence, ZeroOrMore, StrMatch, OrderedChoice, Optional, ConceptMatch, \ ConceptLexerParser from sdp.sheerkaDataProvider import SheerkaDataProvider, Event 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 get_sheerka(use_dict=True, skip_builtins_in_db=True): root = "mem://" if use_dict else root_folder sheerka = Sheerka(skip_builtins_in_db=skip_builtins_in_db) sheerka.initialize(root) return sheerka def get_context(sheerka): return ExecutionContext("test", Event(), 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 @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.evaluate_user_input(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.evaluate_user_input(text) assert len(res) == 1 assert res[0].status assert res[0].value == Concept(name="one", body=1).init_key() # by default, the concept is returned 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.evaluate_user_input("un") return_value = res[0].value assert len(res) == 1 assert res[0].status assert return_value == Concept(name="un", body=Concept(name="one").init_key()).init_key() 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.evaluate_user_input(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.evaluate_user_input(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.metadata.id = "1001" expected.metadata.desc = None expected.init_key() sheerka = get_sheerka() res = sheerka.evaluate_user_input(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 PROPERTIES_TO_SERIALIZE: assert getattr(concept_saved.metadata, prop) == getattr(expected.metadata, prop) assert concept_saved.key in sheerka.concepts_cache assert sheerka.sdp.io.exists( sheerka.sdp.io.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, thanks to the source code '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.evaluate_user_input("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.metadata.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 PROPERTIES_TO_SERIALIZE: assert getattr(concept_saved.metadata, prop) == getattr(expected.metadata, prop) assert concept_saved.key in sheerka.concepts_cache assert sheerka.sdp.io.exists( sheerka.sdp.io.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.evaluate_user_input(text) res = sheerka.evaluate_user_input(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.evaluate_user_input(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.evaluate_user_input("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.evaluate_user_input("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.evaluate_user_input("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.evaluate_user_input("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.body assert len(concepts) == 2 sorted_values = sorted(concepts, key=lambda x: x.value.body) assert sorted_values[0].value.body == "hello another value" assert sorted_values[1].value.body == "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.evaluate_user_input("hello 'foo'") assert len(res) == 1 assert res[0].status assert res[0].value.body == "hello foo" # I don't know yet the one to choose assert res[0].who == sheerka.get_evaluator_name(MultipleSameSuccessEvaluator.NAME) def test_i_can_create_concepts_with_python_code_as_body(): sheerka = get_sheerka() context = get_context(sheerka) sheerka.create_new_concept(context, Concept(name="concepts", body="sheerka.concepts()")) res = sheerka.evaluate_user_input("concepts") assert len(res) == 1 assert res[0].status assert isinstance(res[0].value.body, list) def test_i_can_create_concept_with_bnf_definition(): sheerka = get_sheerka(False, False) a = Concept("a") sheerka.add_in_cache(a) sheerka.concepts_definition_cache = {a: OrderedChoice("one", "two")} res = sheerka.evaluate_user_input("def concept plus from bnf a ('plus' plus)?") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT) saved_concept = sheerka.sdp.get_safe(sheerka.CONCEPTS_ENTRY, "plus") assert saved_concept.key == "plus" assert saved_concept.metadata.definition == "a ('plus' plus)?" assert "a" in saved_concept.props assert "plus" in saved_concept.props saved_definitions = sheerka.sdp.get_safe(sheerka.CONCEPTS_DEFINITIONS_ENTRY) expected_bnf = Sequence( ConceptMatch("a", rule_name="a"), Optional(Sequence(StrMatch("plus"), ConceptMatch("plus", rule_name="plus")))) assert saved_definitions[saved_concept] == expected_bnf new_concept = res[0].value.body assert new_concept.metadata.name == "plus" assert new_concept.metadata.definition == "a ('plus' plus)?" assert new_concept.bnf == expected_bnf assert "a" in new_concept.props assert "plus" in new_concept.props def test_i_can_eval_bnf_definitions(): sheerka = get_sheerka() concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' | 'two'")[0].body.body res = sheerka.evaluate_user_input("one") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, concept_a) def test_i_can_eval_bnf_definitions_with_variables(): sheerka = get_sheerka() concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' | 'two'")[0].body.body concept_b = sheerka.evaluate_user_input("def concept b from bnf a 'three'")[0].body.body res = sheerka.evaluate_user_input("one three") assert len(res) == 1 assert res[0].status return_value = res[0].value assert sheerka.isinstance(return_value, concept_b) assert return_value.props["a"] == Property("a", sheerka.new(concept_a.key, body="one")) def test_i_can_eval_bnf_definitions_from_separate_instances(): """ Same test then before, but make sure that the BNF are correctly persisted and loaded """ sheerka = get_sheerka(False) concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' 'two'")[0].body.body res = get_sheerka(False).evaluate_user_input("one two") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, concept_a) # add another bnf definition concept_b = sheerka.evaluate_user_input("def concept b from bnf a 'three'")[0].body.body res = get_sheerka(False).evaluate_user_input("one two") # previous one still works assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, concept_a) res = get_sheerka(False).evaluate_user_input("one two three") # new one works assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, concept_b) def test_i_can_say_that_a_concept_isa_another_concept(): sheerka = get_sheerka() sheerka.evaluate_user_input("def concept foo") sheerka.evaluate_user_input("def concept bar") res = sheerka.evaluate_user_input("foo isa bar") assert res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.SUCCESS) def test_eval_does_not_break_valid_result(): sheerka = get_sheerka() sheerka.evaluate_user_input("def concept one as 1") sheerka.evaluate_user_input("def concept two as 2") res = sheerka.evaluate_user_input("one + two") assert len(res) == 1 assert res[0].status assert res[0].body == 3 res = sheerka.evaluate_user_input("eval one + two") assert len(res) == 1 assert res[0].status assert res[0].body == 3