From 8af49b7f9132fda14476c07eb90bc95e86f1c330 Mon Sep 17 00:00:00 2001 From: erick Date: Sun, 15 Nov 2009 02:30:02 +0000 Subject: Added tests for the engine, added automatic transition support --- diff --git a/tests/enginetests.py b/tests/enginetests.py index 2cb2330..30d68de 100644 --- a/tests/enginetests.py +++ b/tests/enginetests.py @@ -1,5 +1,5 @@ # Copyright (C) 2009, Tutorius.org -# Copyright (C) 2009, Michael Janelle-Montcalm +# Copyright (C) 2009, Erick Lavoie # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,11 +15,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ -Core Tests +Engine Tests + -This module contains all the tests that pertain to the usage of the Tutorius -Core. This means that the Event Filters, the Finite State Machine and all the -related elements and interfaces are tested here. Usage of actions and event filters is tested, but not the concrete actions and event filters. Those are in their separate test module @@ -28,12 +26,11 @@ and event filters. Those are in their separate test module import unittest -from copy import deepcopy - from sugar.tutorius.tutorial import Tutorial from sugar.tutorius.engine import TutorialRunner +from sugar.tutorius.filters import EventFilter -from actiontests import CountAction, FakeEventFilter +from actiontests import CountAction class MockProbeMgr(object): def __init__(self): @@ -58,10 +55,16 @@ class MockProbeMgr(object): def subscribe(self, event, callback): self.event = event self.cB = callback + self.event.install_handlers(callback) return str(event) def unsubscribe(self, address): - self.event = address + self.event = None + +class MockEvent(EventFilter): + pass + + class TutorialRunnerTest(unittest.TestCase): """ @@ -69,12 +72,45 @@ class TutorialRunnerTest(unittest.TestCase): """ def setUp(self): self.pM = MockProbeMgr() - self.runner = TutorialRunner(self.pM) + def tearDown(self): - self.runner = None self.pM = None - + + # Basic interface cases + def testOneStateTutorial(self): + tutorial = Tutorial("TutorialRunner") + state_name = tutorial.add_state() + tutorial.update_transition(Tutorial.INITIAL_TRANSITION_NAME, + None, state_name) + event = MockEvent() + tutorial.add_transition(state_name, (event, Tutorial.END)) + + runner = TutorialRunner(tutorial, self.pM) + runner.start() + + assert runner._state == state_name, "Current state is: %s"%runner._state + assert self.pM.action == None + assert self.pM.event == event + + event.do_callback() + assert runner._state == Tutorial.END, "Current state is: %s"%runner._state + assert self.pM.action == None, "Current action is %s"%str(self.pM.action) + assert self.pM.event == None, "Current event is %s"%str(self.pM.event) + + + + # Limit cases + def testEmptyTutorial(self): + tutorial = Tutorial("TutorialRunner") + runner = TutorialRunner(tutorial, self.pM) + runner.start() + + assert runner._state == Tutorial.END, "Current state is: %s"%runner._state + assert self.pM.action == None + assert self.pM.event == None + + # Error cases if __name__ == "__main__": unittest.main() diff --git a/tutorius/engine.py b/tutorius/engine.py index b0a49a8..c945e49 100644 --- a/tutorius/engine.py +++ b/tutorius/engine.py @@ -6,6 +6,7 @@ from sugar.bundle.activitybundle import ActivityBundle from .vault import Vault from .TProbe import ProbeManager from .dbustools import save_args +from .tutorial import Tutorial, AutomaticTransitionEvent class TutorialRunner(object): @@ -36,17 +37,17 @@ class TutorialRunner(object): def start(self): self.setCurrentActivity() #Temp Hack until activity in events/actions - self.setState(self._tutorial.INIT) + self.enterState(self._tutorial.INIT) def stop(self): self.setCurrentActivity() #Temp Hack until activity in events/actions - self.setState(self._tutorial.END) + self.enterState(self._tutorial.END) self._teardownState() self._state = None def _handleEvent(self, next_state, event): - #FIXME sanity check - self.setState(next_state) + #FIXME sanity check, log event that was not installed and ignore + self.enterState(next_state) def _teardownState(self): if self._state is None: @@ -67,22 +68,51 @@ class TutorialRunner(object): if self._state is None: raise RuntimeError("Attempting to setupState without a state") + # Handle the automatic event + state_name = self._state + self._actions = self._tutorial.get_action_dict(self._state) transitions = self._tutorial.get_transition_dict(self._state) + for (event, next_state) in transitions.values(): + if isinstance(event, AutomaticTransitionEvent): + state_name = next_state + break + self._sEvents.add(self._pM.subscribe(event, save_args(self._handleEvent, next_state))) + for action in self._actions.values(): self._pM.install(action) - def setState(self, state_name): + return state_name + + def enterState(self, state_name): + """ + Starting from the state_name, the runner execute states until + no automatic transition are found and will wait for an external + event to occur. + + When entering the state, actions and events from the previous + state are respectively uninstalled and unsubscribed and actions + and events from the state_name will be installed and subscribed. + + @param state_name The name of the state to enter in + """ self.setCurrentActivity() #Temp Hack until activity in events/actions + + # Recursive base case if state_name == self._state: #Nothing to do return self._teardownState() self._state = state_name - self._setupState() + + # Recursively call the enterState in case there was an automatic + # transition in the state definition + self.enterState(self._setupState()) + + class Engine: -- cgit v0.9.1