diff --git a/experta/strategies.py b/experta/strategies.py index 1b3d473..65c3a55 100644 --- a/experta/strategies.py +++ b/experta/strategies.py @@ -15,12 +15,17 @@ def get_key(self, activation): def _update_agenda(self, agenda, added, removed): for act in removed: act.key = self.get_key(act) - try: - idx = bisect.bisect_left(agenda.activations, act) - if agenda.activations[idx] == act: - del agenda.activations[idx] - except IndexError: - pass + idx = bisect.bisect_left(agenda.activations, act) + for o in (0, 1, -1): + try: + if agenda.activations[idx+o] == act: + del agenda.activations[idx+o] + else: + continue + except IndexError: + pass + else: + break for act in added: act.key = self.get_key(act) diff --git a/tests/unit/test_strategy_statemachine.py b/tests/unit/test_strategy_statemachine.py new file mode 100644 index 0000000..6f6f289 --- /dev/null +++ b/tests/unit/test_strategy_statemachine.py @@ -0,0 +1,61 @@ +import pytest + +from hypothesis import strategies as st +from hypothesis import assume, settings +from hypothesis.database import DirectoryBasedExampleDatabase +from hypothesis.stateful import Bundle, RuleBasedStateMachine, rule, invariant, consumes + +from experta.activation import Activation +from experta.agenda import Agenda +from experta.engine import KnowledgeEngine +from experta.fact import Fact +from experta.factlist import FactList +from experta.rule import Rule +from experta.strategies import DepthStrategy + + +def get_rule_stm(strategy): + class StrategyStateMachine(RuleBasedStateMachine): + def __init__(self): + super(StrategyStateMachine, self).__init__() + self.model = set() + self.agenda = Agenda() + self.strategy = strategy() + self.fss = set() + + activations = Bundle("activations") + + @rule(target=activations, + r=st.integers(min_value=0), + fs=st.sets(st.integers(min_value=0), min_size=1)) + def declare(self, r, fs): + assume((r, frozenset(fs)) not in self.fss) + self.fss.add((r, frozenset(fs))) + + fs = [Fact(i, __factid__=i) for i in fs] + act = Activation(Rule(Fact(r)), facts=tuple(fs)) + + # Update agenda + self.strategy.update_agenda(self.agenda, [act], []) + + # Update model + self.model |= set([act]) + + return act + + @rule(act=consumes(activations)) + def retract(self, act): + # Update agenda + self.strategy.update_agenda(self.agenda, [], [act]) + + # Update model + self.model -= set([act]) + + @invariant() + def values_agree(self): + assert set(self.agenda.activations) == self.model + + return StrategyStateMachine + + +test_depthstrategy_state_machine = get_rule_stm(DepthStrategy).TestCase