From c9f5b6ad86fa1e5e0d7cd5a3418188092141f73b Mon Sep 17 00:00:00 2001 From: mike Date: Fri, 20 Mar 2009 04:50:48 +0000 Subject: TutoriusV2 : Adding linear tutorial creator wih basic tests --- (limited to 'src/sugar/tutorius') diff --git a/src/sugar/tutorius/Makefile.am b/src/sugar/tutorius/Makefile.am index 1fb11e1..9ff425e 100644 --- a/src/sugar/tutorius/Makefile.am +++ b/src/sugar/tutorius/Makefile.am @@ -8,4 +8,5 @@ sugar_PYTHON = \ filters.py \ services.py \ overlayer.py \ - editor.py + editor.py \ + linear_creator.py diff --git a/src/sugar/tutorius/core.py b/src/sugar/tutorius/core.py index 2bdacee..901820f 100644 --- a/src/sugar/tutorius/core.py +++ b/src/sugar/tutorius/core.py @@ -239,7 +239,7 @@ class State(object): was just cleared will become a sink and will be the end of the tutorial. """ - self._event_filters.clear() + self._event_filters = [] class FiniteStateMachine(State): """ diff --git a/src/sugar/tutorius/filters.py b/src/sugar/tutorius/filters.py index 4c04cf6..3acbb36 100644 --- a/src/sugar/tutorius/filters.py +++ b/src/sugar/tutorius/filters.py @@ -37,6 +37,13 @@ class EventFilter(object): """ return self._next_state + def set_next_state(self, new_next_name): + """ + Setter for the next state. Should only be used during construction of + the event_fitler, not while the tutorial is running. + """ + self._next_state = new_next_name + def install_handlers(self, callback, **kwargs): """ install_handlers is called for eventfilters to setup all diff --git a/src/sugar/tutorius/linear_creator.py b/src/sugar/tutorius/linear_creator.py new file mode 100644 index 0000000..02bb497 --- /dev/null +++ b/src/sugar/tutorius/linear_creator.py @@ -0,0 +1,97 @@ +# Copyright (C) 2009, Tutorius.org +# Greatly influenced by sugar/activity/namingalert.py +# +# 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 +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from sugar.tutorius.core import * +from sugar.tutorius.actions import * +from sugar.tutorius.filters import * + +from copy import deepcopy + +class LinearCreator(object): + """ + This class is used to create a FSM from a linear sequence of orders. The + orders themselves are meant to be either an action or a transition. + """ + + def __init__(self): + self.fsm = FiniteStateMachine("Sample Tutorial") + self.current_actions = [] + self.nb_state = 0 + + def set_name(self, name): + """ + Sets the name of the generated FSM. + """ + self.fsm.name = name + + def action(self, action): + """ + Adds an action to execute in the current state. + """ + self.current_actions.append(action) + + def event(self, event_filter): + """ + Adds a transition to another state. When executing this, all the actions + previously called will be bundled in a single state, with the exit + condition of this state being the transition just added. + + Whatever the name of the next state you inserted in the event, it will + be replaced to point to the next event in the line. + """ + if len(self.current_actions) != 0: + state_name = "" + if self.nb_state == 0: + state_name = "INIT" + else: + state_name = "State" + str(self.nb_state) + # Set the next state name - there is no way the caller should have + # to deal with that. + next_state_name = "State" + str(self.nb_state+1) + event_filter.set_next_state(next_state_name) + + state = State(state_name, action_list=self.current_actions, event_filter_list=[event_filter]) + self.nb_state += 1 + self.fsm.add_state(state) + + # Clear the actions from the list + self.current_actions = [] + + def generate_fsm(self): + """ + Returns a finite state machine corresponding to the sequence of calls + that were made from this point on. + """ + # Copy the whole FSM that was generated yet + new_fsm = deepcopy(self.fsm) + + # Generate the final state + state = None + if len(self.current_actions) != 0: + state = State("State" + str(self.nb_state), action_list=self.current_actions) + # Don't increment the nb_state here - we would break the linearity + # because we might generate more stuff with this creator later. + # Since we rely on linearity for continuity when generating the + # next state's name on an event filter, we cannot increment here. + else: + state = State("State" + str(self.nb_state)) + + # Insert the state in the copy of the FSM + new_fsm.add_state(state) + + return new_fsm + \ No newline at end of file diff --git a/src/sugar/tutorius/tests/linear_creatortests.py b/src/sugar/tutorius/tests/linear_creatortests.py new file mode 100644 index 0000000..b328c45 --- /dev/null +++ b/src/sugar/tutorius/tests/linear_creatortests.py @@ -0,0 +1,71 @@ +# Copyright (C) 2009, Tutorius.org +# Greatly influenced by sugar/activity/namingalert.py +# +# 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 +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from sugar.tutorius.core import * +from sugar.tutorius.actions import * +from sugar.tutorius.filters import * +from sugar.tutorius.linear_creator import * +from coretests import TriggerEventFilter, CountAction + +import unittest + +class CreatorTests(unittest.TestCase): + + def test_simple_usage(self): + creator = LinearCreator() + fsm_name = "SimpleUsageTest" + + creator.set_name(fsm_name) + + # Generate an FSM using the steps + creator.action(CountAction()) + creator.action(CountAction()) + + creator.event(TriggerEventFilter("Not important")) + + creator.action(CountAction()) + + creator.event(TriggerEventFilter("Not good either...")) + + fsm = creator.generate_fsm() + print "------------- Generated FSM -------------------" + print str(fsm) + print "-----------------------------------------------" + # Make sure everything worked! + assert fsm.name == fsm_name, "Name was not set properly" + + init_state = fsm.get_state_by_name("INIT") + + assert len(init_state.get_action_list()) == 2, "Creator did not insert all the actions" + + assert init_state.get_event_filter_list()[0].get_next_state() == "State1" + + state1 = fsm.get_state_by_name("State1") + + assert len(state1.get_action_list()) == 1, "Creator did not insert all the actions" + + assert state1.get_event_filter_list()[0].get_next_state() == "State2" + + # Make sure we have the final state and that it's empty + state2 = fsm.get_state_by_name("State2") + + assert len(state2.get_action_list()) == 0, "Creator inserted extra actions on wrong state" + + assert len(state2.get_event_filter_list()) == 0, "Creator assigner events to the final state" + +if __name__ == '__main__': + unittest.main() \ No newline at end of file -- cgit v0.9.1