First version of explain. Creating a new parser was a wrong approach. Need to reimplement
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from core.builtin_concepts import ParserResultConcept, ReturnValueConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.sheerka.ExecutionContext import ExecutionContext
|
||||
from evaluators.ExplainEvaluator import ExplainEvaluator
|
||||
from parsers.ExplainParser import ExplanationNode, RecurseDefNode, FormatLNode, UnionNode, FilterNode, FormatDNode
|
||||
from parsers.ExpressionParser import PropertyEqualsNode, PropertyEqualsSequenceNode, TrueNode, IsaNode
|
||||
from printer.FormatInstructions import FormatDetailDesc, FormatDetailType
|
||||
from pytest import fixture
|
||||
from sdp.sheerkaDataProvider import Event
|
||||
from sdp.sheerkaSerializer import Serializer, SerializerContext
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
@fixture(scope="module")
|
||||
def serializer():
|
||||
"""
|
||||
Return a :class:`sdp.sheerkaSerializer.Serializer` instance for the module
|
||||
"""
|
||||
return Serializer()
|
||||
|
||||
|
||||
class EC:
|
||||
"""
|
||||
Helper to create execution context (AKA execution result)
|
||||
"""
|
||||
|
||||
def __init__(self, children=None, **props):
|
||||
self.props = props
|
||||
self.children = children
|
||||
|
||||
|
||||
def get_return_value(expr):
|
||||
if isinstance(expr, ExplanationNode):
|
||||
value = expr
|
||||
else:
|
||||
value = ExplanationNode("xxx_test_explain_evaluator_xxx", "", expr=expr)
|
||||
|
||||
return ReturnValueConcept(
|
||||
"TestEvaluator",
|
||||
True,
|
||||
ParserResultConcept(parser="parser", value=value))
|
||||
|
||||
|
||||
def create_executions_results(context, list_of_ecs):
|
||||
def update(execution_context, ec):
|
||||
for prop_name, pro_value in ec.props.items():
|
||||
setattr(execution_context, prop_name, pro_value)
|
||||
|
||||
if ec.children:
|
||||
for child_ec in ec.children:
|
||||
child_execution_context = execution_context.push("TestEvaluator")
|
||||
update(child_execution_context, child_ec)
|
||||
|
||||
res = []
|
||||
for ec in list_of_ecs:
|
||||
execution_context = ExecutionContext("TestEvaluator", context.event, context.sheerka)
|
||||
update(execution_context, ec)
|
||||
res.append(execution_context)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def get_execution_result_from_file(sheerka, digest, serializer):
|
||||
target_path = os.path.join("../_fixture/", digest) + "_result"
|
||||
with open(target_path, "rb") as f:
|
||||
context = SerializerContext(sheerka=sheerka)
|
||||
return serializer.deserialize(f, context)
|
||||
|
||||
|
||||
def get_execution_result_from_list(executions_result):
|
||||
return executions_result
|
||||
|
||||
|
||||
class TestExplainEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@staticmethod
|
||||
def init_evaluator_with_file(self, serializer):
|
||||
sheerka = self.get_sheerka()
|
||||
context = self.get_context(sheerka)
|
||||
evaluator = ExplainEvaluator()
|
||||
evaluator.get_execution_result = lambda s, d: get_execution_result_from_file(s, d, serializer)
|
||||
|
||||
return sheerka, context, evaluator
|
||||
|
||||
def init_evaluator_with_list(self, list_of_ecs):
|
||||
sheerka = self.get_sheerka()
|
||||
context = self.get_context(sheerka)
|
||||
evaluator = ExplainEvaluator()
|
||||
|
||||
executions_result = create_executions_results(context, list_of_ecs)
|
||||
evaluator.get_execution_result = lambda s, d: get_execution_result_from_list(executions_result)
|
||||
|
||||
return sheerka, context, evaluator, executions_result
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=ExplanationNode("", ""))), True),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value="other thing")), False),
|
||||
(ReturnValueConcept("some_name", False, "not relevant"), False),
|
||||
(ReturnValueConcept("some_name", True, Concept()), False)
|
||||
])
|
||||
def test_i_can_match(self, ret_val, expected):
|
||||
context = self.get_context()
|
||||
assert ExplainEvaluator().matches(context, ret_val) == expected
|
||||
|
||||
def test_i_can_eval_in_list(self, serializer):
|
||||
sheerka, context, evaluator, execution_results = self.init_evaluator_with_list(
|
||||
[
|
||||
EC(desc="correct desc"),
|
||||
EC(desc="wrong desc"),
|
||||
]
|
||||
)
|
||||
ret_val = get_return_value(UnionNode(
|
||||
[
|
||||
FilterNode(TrueNode()),
|
||||
FilterNode(PropertyEqualsNode("desc", "correct desc")),
|
||||
]))
|
||||
|
||||
res = evaluator.eval(context, ret_val)
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.EXPLANATION)
|
||||
|
||||
filtered = res.body.body
|
||||
assert filtered == [execution_results[0]]
|
||||
|
||||
def test_i_can_eval_in_children(self):
|
||||
sheerka, context, evaluator, execution_results = self.init_evaluator_with_list(
|
||||
[
|
||||
EC(desc="wrong desc", children=[EC(desc="wrong sub"), EC(desc="good sub")]),
|
||||
EC(desc="wrong desc", children=[EC(desc="good sub")]),
|
||||
]
|
||||
)
|
||||
ret_val = get_return_value(UnionNode(
|
||||
[
|
||||
FilterNode(TrueNode()),
|
||||
FilterNode(PropertyEqualsNode("desc", "good sub")),
|
||||
]))
|
||||
|
||||
res = evaluator.eval(context, ret_val)
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.EXPLANATION)
|
||||
|
||||
filtered = res.body.body
|
||||
assert filtered == [
|
||||
execution_results[0].children[1],
|
||||
execution_results[1].children[0],
|
||||
]
|
||||
|
||||
def test_i_can_evaluate_multiple_filter_node(self):
|
||||
sheerka, context, evaluator, execution_results = self.init_evaluator_with_list(
|
||||
[
|
||||
EC(desc="parent1", _id=1, children=[EC(desc="wrong sub"), EC(desc="good sub")]),
|
||||
EC(desc="parent2", children=[EC(desc="wrong sub"), EC(desc="good sub")]),
|
||||
EC(desc="good sub")
|
||||
])
|
||||
ret_val = get_return_value(UnionNode(
|
||||
[
|
||||
FilterNode(TrueNode()),
|
||||
FilterNode(PropertyEqualsNode("id", "1")),
|
||||
FilterNode(PropertyEqualsNode("desc", "good sub")),
|
||||
]))
|
||||
|
||||
res = evaluator.eval(context, ret_val)
|
||||
assert res.status
|
||||
assert len(res.body) == 2
|
||||
|
||||
assert sheerka.isinstance(res.body[0], BuiltinConcepts.EXPLANATION)
|
||||
assert sheerka.isinstance(res.body[1], BuiltinConcepts.EXPLANATION)
|
||||
|
||||
assert res.body[0].body == [execution_results[0]]
|
||||
assert res.body[1].body == [
|
||||
execution_results[0].children[1],
|
||||
execution_results[1].children[1],
|
||||
execution_results[2]
|
||||
]
|
||||
|
||||
def test_i_can_eval_parent_and_child(self):
|
||||
sheerka, context, evaluator, execution_results = self.init_evaluator_with_list(
|
||||
[
|
||||
EC(desc="parent1", children=[EC(desc="wrong sub"), EC(desc="good sub")]),
|
||||
EC(desc="parent2", children=[EC(desc="wrong sub"), EC(desc="good sub")]),
|
||||
EC(desc="good sub")
|
||||
]
|
||||
)
|
||||
ret_val = get_return_value(UnionNode(
|
||||
[
|
||||
FilterNode(TrueNode()),
|
||||
FilterNode(PropertyEqualsSequenceNode(["desc", "desc"], ["parent1", "good sub"])),
|
||||
]))
|
||||
|
||||
res = evaluator.eval(context, ret_val)
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.EXPLANATION)
|
||||
|
||||
filtered = res.body.body
|
||||
assert filtered == [
|
||||
execution_results[0].children[1],
|
||||
]
|
||||
|
||||
def test_i_correctly_create_format_instructions(self):
|
||||
sheerka, context, evaluator, execution_results = self.init_evaluator_with_list([])
|
||||
ret_val = get_return_value(UnionNode(
|
||||
[
|
||||
FilterNode(TrueNode(), [
|
||||
RecurseDefNode(2),
|
||||
FormatLNode("abc"),
|
||||
FormatDNode({"a": "{a}", "b": "{b}"})
|
||||
]),
|
||||
]))
|
||||
|
||||
res = evaluator.eval(context, ret_val)
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.EXPLANATION)
|
||||
|
||||
instructions = res.body.instructions
|
||||
assert instructions.recursive_props == {"children": 2}
|
||||
assert instructions.format_l == {'core.sheerka.ExecutionContext.ExecutionContext': 'abc'}
|
||||
assert instructions.format_d == [FormatDetailDesc(
|
||||
IsaNode(ExecutionContext),
|
||||
FormatDetailType.Props_In_Line,
|
||||
{"a": "{a}", "b": "{b}"})]
|
||||
|
||||
def test_i_correctly_create_format_instructions_with_filtering(self):
|
||||
sheerka, context, evaluator, execution_results = self.init_evaluator_with_list([])
|
||||
|
||||
ret_val = get_return_value(UnionNode(
|
||||
[
|
||||
FilterNode(TrueNode()),
|
||||
FilterNode(PropertyEqualsNode("id", "1"), [RecurseDefNode(2), FormatLNode("abc")]),
|
||||
]))
|
||||
res = evaluator.eval(context, ret_val)
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.EXPLANATION)
|
||||
|
||||
instructions = res.body.instructions
|
||||
assert instructions.format_l == {'core.sheerka.ExecutionContext.ExecutionContext': 'abc'}
|
||||
assert instructions.recursive_props == {"children": 2}
|
||||
|
||||
def test_i_can_have_different_instructions_for_different_filtering(self):
|
||||
sheerka, context, evaluator, execution_results = self.init_evaluator_with_list([])
|
||||
ret_val = get_return_value(UnionNode(
|
||||
[
|
||||
FilterNode(TrueNode()),
|
||||
FilterNode(PropertyEqualsNode("id", "1"), [RecurseDefNode(2)]),
|
||||
FilterNode(PropertyEqualsNode("desc", "good sub"), [FormatLNode("abc")]),
|
||||
]))
|
||||
|
||||
res = evaluator.eval(context, ret_val)
|
||||
assert res.status
|
||||
assert len(res.body) == 2
|
||||
|
||||
assert res.body[0].instructions.recursive_props == {"children": 2}
|
||||
assert res.body[1].instructions.format_l == {'core.sheerka.ExecutionContext.ExecutionContext': 'abc'}
|
||||
|
||||
def test_filtering_instructions_inherit_from_the_first_filtering_node(self):
|
||||
sheerka, context, evaluator, execution_results = self.init_evaluator_with_list([])
|
||||
ret_val = get_return_value(UnionNode(
|
||||
[
|
||||
FilterNode(TrueNode(), [RecurseDefNode(2)]),
|
||||
FilterNode(PropertyEqualsNode("id", "1"), [RecurseDefNode(1)]),
|
||||
FilterNode(PropertyEqualsNode("desc", "good sub"), [FormatLNode("abc")]),
|
||||
]))
|
||||
|
||||
res = evaluator.eval(context, ret_val)
|
||||
assert res.status
|
||||
assert len(res.body) == 2
|
||||
|
||||
assert res.body[0].instructions.recursive_props == {"children": 1} # overridden
|
||||
|
||||
assert res.body[1].instructions.format_l == {'core.sheerka.ExecutionContext.ExecutionContext': 'abc'}
|
||||
assert res.body[1].instructions.recursive_props == {"children": 2}
|
||||
|
||||
def test_i_can_reuse_a_recorded_digest(self):
|
||||
sheerka, context, evaluator, execution_results = self.init_evaluator_with_list([])
|
||||
expr = UnionNode([FilterNode(TrueNode(), [RecurseDefNode(2)])])
|
||||
|
||||
# need a valid result to test this feature
|
||||
event = Event("fake message")
|
||||
execution_context = ExecutionContext("TestExplainEvaluator", event, sheerka)
|
||||
sheerka.sdp.save_result(execution_context)
|
||||
|
||||
# save another result
|
||||
event2 = Event("fake message")
|
||||
execution_context = ExecutionContext("TestExplainEvaluator", event2, sheerka)
|
||||
sheerka.sdp.save_result(execution_context)
|
||||
|
||||
# digest is recorded during the first call
|
||||
explanation_node = ExplanationNode(event.get_digest(), "", expr=expr, record_digest=True)
|
||||
ret_val = get_return_value(explanation_node)
|
||||
evaluator.eval(context, ret_val)
|
||||
|
||||
# the next call to get_event_digest will load the recorded digest
|
||||
explanation_node = ExplanationNode("", "", expr=expr, record_digest=False) # digest is not provided
|
||||
digest = evaluator.get_event_digest(sheerka, explanation_node)
|
||||
assert digest == event.get_digest()
|
||||
|
||||
# test I can record another digest
|
||||
explanation_node = ExplanationNode(event2.get_digest(), "", expr=expr, record_digest=True)
|
||||
ret_val = get_return_value(explanation_node)
|
||||
evaluator.eval(context, ret_val)
|
||||
|
||||
explanation_node = ExplanationNode("", "", expr=expr, record_digest=False) # digest is not provided
|
||||
digest = evaluator.get_event_digest(sheerka, explanation_node)
|
||||
assert digest == event2.get_digest()
|
||||
|
||||
# test can now reset the recorded digest
|
||||
# (a digest is provided, but record_digest is set to False)
|
||||
explanation_node = ExplanationNode(event.get_digest(), "", expr=expr, record_digest=False)
|
||||
ret_val = get_return_value(explanation_node)
|
||||
evaluator.eval(context, ret_val)
|
||||
|
||||
explanation_node = ExplanationNode("", "", expr=expr, record_digest=False) # digest is not provided
|
||||
digest = evaluator.get_event_digest(sheerka, explanation_node)
|
||||
assert digest is None
|
||||
Reference in New Issue
Block a user