Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormike <michael.jmontcalm@gmail.com>2009-10-04 21:36:50 (GMT)
committer mike <michael.jmontcalm@gmail.com>2009-10-04 21:36:50 (GMT)
commit2c29c0111ee94d69d038c22294dfdd1549fdbed3 (patch)
tree67ab3f9cf9777212dafacc628e8263100dc3ac20
parentc5a4e544e1abf11ffa8378a4a03df92e40137cff (diff)
LP 439980 : Adding tests for the is_identical function in the Core, on the FSM and the State
-rw-r--r--tests/coretests.py230
-rw-r--r--tests/propertiestests.py161
-rw-r--r--tutorius/core.py27
-rw-r--r--tutorius/properties.py19
4 files changed, 406 insertions, 31 deletions
diff --git a/tests/coretests.py b/tests/coretests.py
index f90374f..10cc716 100644
--- a/tests/coretests.py
+++ b/tests/coretests.py
@@ -28,6 +28,7 @@ and event filters. Those are in their separate test module
import unittest
+import copy
import logging
from sugar.tutorius.actions import *
from sugar.tutorius.addon import *
@@ -49,6 +50,28 @@ class SimpleTutorial(Tutorial):
def set_state(self, name):
self.current_state_name = name
+class TutorialTest(unittest.TestCase):
+ """Tests the tutorial functions that are not covered elsewhere."""
+ def test_detach(self):
+ class Activity(object):
+ name = "this"
+
+ activity1 = Activity()
+ activity2 = Activity()
+
+ fsm = FiniteStateMachine("Sample example")
+
+ tutorial = Tutorial("Test tutorial", fsm)
+
+ assert tutorial.activity == None, "There is a default activity in the tutorial"
+
+ tutorial.attach(activity1)
+
+ assert tutorial.activity == activity1, "Activity should have been associated to this tutorial"
+
+ tutorial.attach(activity2)
+ assert tutorial.activity == activity2, "Activity should have been changed to activity2"
+
class TutorialWithFSM(Tutorial):
"""
Fake tutorial, but associated with a FSM.
@@ -201,7 +224,79 @@ class StateTest(unittest.TestCase):
assert len(state.get_event_filter_list()) == 0, \
"Could not clear the event filter list properly"
+
+ def test_is_identical_simple(self):
+ """
+ Two empty states with the same name must be identical
+ """
+ st1 = State("Identical")
+ st2 = State("Identical")
+
+ assert st1.is_identical(st2), "Empty states with the same name should be identical"
+
+ def test_is_identical(self):
+ """
+ Test whether two states share the same set of actions and event filters.
+ """
+ st1 = State("Identical")
+ st2 = State("Identical")
+
+ non_state = object()
+
+ act1 = addon.create("BubbleMessage", message="Hi", position=[132,450])
+ act2 = addon.create("BubbleMessage", message="Hi", position=[132,450])
+
+ event1 = addon.create("GtkWidgetEventFilter", "nextState", "0.0.0.1.1.2.3.1", "clicked")
+
+ act3 = addon.create("DialogMessage", message="Hello again.", position=[200, 400])
+
+ # Build the first state
+ st1.add_action(act1)
+ st1.add_action(act3)
+ st1.add_event_filter(event1)
+
+ # Build the second state
+ st2.add_action(act2)
+ st2.add_action(act3)
+ st2.add_event_filter(event1)
+
+ # Make sure that they are identical for now
+ assert st1.is_identical(st2), "States should be considered as identical"
+ assert st2.is_identical(st1), "States should be considered as identical"
+
+ # Modify the second bubble message action
+ act2.message = "New message"
+
+ # Since one action changed in the second state, this should indicate that the states
+ # are not identical anymore
+ assert st1.is_identical(st2) == False, "Action was changed and states should be different"
+ assert st2.is_identical(st1) == False, "Action was changed and states should be different"
+
+ # Make sure that trying to find identity with something else than a State object fails properly
+ assert st1.is_identical(non_state) == False, "Passing a non-State object should fail for identity"
+
+ st2.name = "Not identical anymore"
+ assert st1.is_identical(st2) == False, "Different state names should give different states"
+ st2.name = "Identical"
+
+ st3 = copy.deepcopy(st1)
+ st3.add_action(addon.create("BubbleMessage", "Hi!", [128,264]))
+
+ assert st1.is_identical(st3) == False, "States having a different number of actions should be different"
+
+ st4 = copy.deepcopy(st1)
+ st4.add_event_filter(addon.create("GtkWidgetEventFilter", "next_state", "0.0.1.1.2.2.3", "clicked"))
+
+ assert st1.is_identical(st4) == False, "States having a different number of events should be different"
+
+ st5 = copy.deepcopy(st1)
+ st5._event_filters = []
+ st5.add_event_filter(addon.create("GtkWidgetEventFilter", "other_state", "0.1.2.3.4.1.2", "pressed"))
+
+ #import rpdb2; rpdb2.start_embedded_debugger('pass')
+ assert st1.is_identical(st5) == False, "States having the same number of event filters but those being different should be different"
+
class FSMTest(unittest.TestCase):
"""
This class needs to text the interface and functionality of the Finite
@@ -246,6 +341,7 @@ class FSMTest(unittest.TestCase):
assert act_second.active == False, "FSM did not teardown SECOND properly"
+
def test_state_insert(self):
"""
This is a simple test to insert, then find a state.
@@ -369,6 +465,112 @@ class FSMTest(unittest.TestCase):
undo_count = fsm.get_state_by_name("INIT").get_action_list()[0].undo_count
assert fsm.get_state_by_name("INIT").get_action_list()[0].undo_count == 0,\
"The action has been undone unappropriately, undo_count = %d"%undo_count
+
+ def test_setup(self):
+ fsm = FiniteStateMachine("New state machine")
+
+ try:
+ fsm.setup()
+ assert False, "fsm should throw an exception when trying to setup and not bound to a tutorial"
+ except UnboundLocalError:
+ pass
+
+ def test_setup_actions(self):
+ tut = SimpleTutorial()
+
+ states_dict = {"INIT": State("INIT")}
+ fsm = FiniteStateMachine("New FSM", state_dict=states_dict)
+
+ act = CountAction()
+ fsm.add_action(act)
+
+ fsm.set_tutorial(tut)
+
+ fsm.setup()
+
+ # Let's also test the current state name
+ assert fsm.get_current_state_name() == "INIT", "Initial state should be INIT"
+
+ assert act.do_count == 1, "Action should have been called during setup"
+
+ fsm._fsm_has_finished = True
+
+ fsm.teardown()
+
+ assert act.undo_count == 1, "Action should have been undone"
+
+ def test_string_rep(self):
+ fsm = FiniteStateMachine("Testing machine")
+
+ st1 = State("INIT")
+ st2 = State("Other State")
+ st3 = State("Final State")
+
+ st1.add_action(addon.create("BubbleMessage", "Hi!", [132,312]))
+
+ fsm.add_state(st1)
+ fsm.add_state(st2)
+ fsm.add_state(st3)
+
+ assert str(fsm) == "INIT, Final State, Other State, "
+
+ def test_is_identical(self):
+ fsm = FiniteStateMachine("Identity test")
+
+ non_fsm_object = object()
+
+ assert fsm.is_identical(non_fsm_object) == False, "Testing with non FSM object should not give identity"
+
+ # Compare FSMs
+ act1 = CountAction()
+
+ fsm.add_action(act1)
+
+ fsm2 = copy.deepcopy(fsm)
+
+ assert fsm.is_identical(fsm2)
+
+ act2 = CountAction()
+ fsm2.add_action(act2)
+
+ assert fsm.is_identical(fsm2) == False, "FSMs having a different number of actions should be different"
+
+ fsm3 = FiniteStateMachine("Identity test")
+
+ act3 = addon.create("BubbleMessage", "Hi!", [123,312])
+ fsm3.add_action(act3)
+
+ assert fsm3.is_identical(fsm) == False, "Actions having the same number of actions but different ones should be different"
+
+ st1 = State("INIT")
+
+ st2 = State("OtherState")
+
+ fsm.add_state(st1)
+ fsm.add_state(st2)
+
+ fsm4 = copy.deepcopy(fsm)
+
+ assert fsm.is_identical(fsm4)
+
+ st3 = State("Last State")
+
+ fsm4.add_state(st3)
+
+ assert fsm.is_identical(fsm4) == False, "FSMs having a different number of states should not be identical"
+
+ fsm4.remove_state("OtherState")
+
+ assert fsm.is_identical(fsm4) == False, "FSMs having different states should be different"
+
+ fsm4.remove_state("Last State")
+
+ st5 = State("OtherState")
+ st5.add_action(CountAction())
+
+ fsm4.add_state(st5)
+
+ assert fsm.is_identical(fsm4) == False, "FSMs having states with same name but different content should be different"
class FSMExplorationTests(unittest.TestCase):
def setUp(self):
@@ -425,6 +627,34 @@ class FSMExplorationTests(unittest.TestCase):
self.validate_previous_states("Fourth", ("Second"))
+ def test_is_identical(self):
+ otherFSM = copy.deepcopy(self.fsm)
+
+ assert self.fsm.is_identical(otherFSM), "Copied FSM was different"
+
+ # Change the name of the second FSM
+ otherFSM.name = "OtherName"
+
+ assert self.fsm.is_identical(otherFSM) == False, "Name change should make the FSMs different"
+
+ otherFSM.name = self.fsm.name
+
+ # Add an extra state to the second FSM
+ new_state = State("New State!")
+
+ act1 = addon.create("BubbleMessage", message="This will make the second FSM different", position=[100, 0])
+ new_state.add_action(act1)
+
+ otherFSM.add_state(new_state)
+
+ assert self.fsm.is_identical(otherFSM) == False, "The second FSM has an extra state and should not be identical"
+
+ otherFSM.remove_state("New State!")
+
+ # Test difference with one FSM having an FSM-level action
+ otherFSM.add_action(act1)
+
+ assert self.fsm.is_identical(otherFSM) == False, "The second FSM has an FSM-level action and should be different"
if __name__ == "__main__":
unittest.main()
diff --git a/tests/propertiestests.py b/tests/propertiestests.py
index e1f6f4b..389671d 100644
--- a/tests/propertiestests.py
+++ b/tests/propertiestests.py
@@ -83,7 +83,129 @@ class BasePropertyTest(unittest.TestCase):
obj.prop = 2
assert obj.prop == 2, "Unable to set a value on base class"
+
+ def test_is_identical(self):
+ class klass(TPropContainer):
+ prop = TutoriusProperty()
+ obj = klass()
+
+ obj2 = klass()
+
+ assert obj.is_identical(obj2), "Base property containers should be identical"
+class AdvancedPropertyTest(unittest.TestCase):
+ def test_properties_groups(self):
+ """
+ Tests complex properties containers for identity.
+ """
+
+ class klass1(TPropContainer):
+ message = TutoriusProperty()
+ property = TutoriusProperty()
+ data = TutoriusProperty()
+
+ class klass2(TPropContainer):
+ property = TutoriusProperty()
+ message = TutoriusProperty()
+ data = TutoriusProperty()
+
+ class klass3(TPropContainer):
+ property = TutoriusProperty()
+ message = TutoriusProperty()
+ data = TutoriusProperty()
+ extra_prop = TutoriusProperty()
+
+ class klass4(TPropContainer):
+ property = TutoriusProperty()
+ message = TutoriusProperty()
+ data = TFloatProperty(13.0)
+
+ obj1 = klass1()
+ obj1.property = 12
+ obj1.message = "Initial message"
+ obj1.data = [132, 208, 193, 142]
+
+ obj2 = klass2()
+ obj2.property = 12
+ obj2.message = "Initial message"
+ obj2.data = [132, 208, 193, 142]
+
+ obj3 = klass3()
+ obj3.property = 12
+ obj3.message = "Initial message"
+ obj3.data = [132, 208, 193, 142]
+ obj3.extra_prop = "Suprprise!"
+
+ obj4 = klass4()
+ obj4.property = 12
+ obj4.message = "Initial message"
+ obj4.data = 13.4
+
+ # Ensure that both obj1 and obj2 are identical (they have the same list of
+ # properties and they have the same values
+ assert obj1.is_identical(obj2), "Identical objects were considered as different"
+
+ # Ensure that obj1 is different from obj3, since obj3 has an extra property
+ assert obj1.is_identical(obj3) == False, "Objects should not be identical since obj3 has more props"
+ assert obj3.is_identical(obj1) == False, "Objects should not be identical since obj3 has more properties"
+
+ # Ensure that properties of different type are considered as different
+ assert obj1.is_identical(obj4) == False, "Properties of different type should not be equal"
+
+ def test_addon_properties(self):
+ class klass1(TPropContainer):
+ addon = TAddonProperty()
+
+ class inner1(TPropContainer):
+ internal = TutoriusProperty()
+ def __init__(self, value):
+ TPropContainer.__init__(self)
+ self.internal = value
+
+ obj1 = klass1()
+ obj1.addon = inner1("Hi!")
+
+ obj2 = klass1()
+ obj2.addon = inner1("Hi!")
+
+ assert obj1.is_identical(obj2), "Identical objects with addon proeprties were treated as different"
+
+ obj3 = klass1()
+ obj3.addon = inner1("Hello!")
+
+ assert obj1.is_identical(obj3) == False, "Objects with addon property having a different value should be considered different"
+
+ def test_addonlist_properties(self):
+ class klass1(TPropContainer):
+ addon_list = TAddonListProperty()
+
+ class inner1(TPropContainer):
+ message = TutoriusProperty()
+ data = TutoriusProperty()
+ def __init__(self, message, data):
+ TPropContainer.__init__(self)
+ self.message = message
+ self.data = data
+
+ class inner2(TPropContainer):
+ message = TutoriusProperty()
+ other_data = TutoriusProperty()
+ def __init__(self, message, data):
+ TPropContainer.__init__(self)
+ self.message = message
+ self.other_data = data
+
+ obj1 = klass1()
+ obj1.addon_list = [inner1('Hi!', 12), inner1('Hello.', [1,2])]
+ obj2 = klass1()
+ obj2.addon_list = [inner1('Hi!', 12), inner1('Hello.', [1,2])]
+
+ assert obj1.is_identical(obj2), "Addon lists with the same containers were considered different"
+
+ obj3 = klass1()
+ obj3.addon_list = [inner1('Hi!', 12), inner2('Hello.', [1,2])]
+ assert obj1.is_identical(obj3) == False, "Differently named proeprties should be considered different in the addon list tests"
+
class TIntPropertyTest(unittest.TestCase):
def test_int_property(self):
class klass(TPropContainer):
@@ -412,6 +534,45 @@ class TFilePropertyTest(unittest.TestCase):
except FileConstraintError:
pass
+class TAddonPropertyTest(unittest.TestCase):
+ def test_wrong_value(self):
+ class klass1(TPropContainer):
+ addon = TAddonProperty()
+
+ class wrongAddon(object):
+ pass
+
+ obj1 = klass1()
+ obj1.addon = klass1()
+
+ try:
+ obj1.addon = wrongAddon()
+ assert False, "Addon Property should not accept non-TPropContainer values"
+ except ValueError:
+ pass
+
+class TAddonPropertyList(unittest.TestCase):
+ def test_wrong_value(self):
+ class klass1(TPropContainer):
+ addonlist = TAddonListProperty()
+
+ class wrongAddon(object):
+ pass
+
+ obj1 = klass1()
+
+ obj1.addonlist = [klass1(), klass1()]
+
+ try:
+ obj1.addonlist = klass1()
+ assert False, "TAddonPropeprty shouldn't accept anything else than a list"
+ except ValueError:
+ pass
+
+ try:
+ obj1.addonlist = [klass1(), klass1(), wrongAddon(), klass1()]
+ except ValueError:
+ pass
if __name__ == "__main__":
unittest.main()
diff --git a/tutorius/core.py b/tutorius/core.py
index ff592ad..d034c30 100644
--- a/tutorius/core.py
+++ b/tutorius/core.py
@@ -90,16 +90,16 @@ class Tutorial (object):
self.state_machine.set_state(name)
- # Currently unused -- equivalent function is in each state
- def _eventfilter_state_done(self, eventfilter):
- """
- Callback handler for eventfilter to notify
- when we must go to the next state.
- """
- #XXX Tests should be run here normally
-
- #Swith to the next state pointed by the eventfilter
- self.set_state(eventfilter.get_next_state())
+## # Currently unused -- equivalent function is in each state
+## def _eventfilter_state_done(self, eventfilter):
+## """
+## Callback handler for eventfilter to notify
+## when we must go to the next state.
+## """
+## #XXX Tests should be run here normally
+##
+## #Swith to the next state pointed by the eventfilter
+## self.set_state(eventfilter.get_next_state())
def _prepare_activity(self):
"""
@@ -141,9 +141,6 @@ class State(object):
self._actions = action_list or []
- # Unused for now
- #self.tests = []
-
self._event_filters = event_filter_list or []
self.tutorial = tutorial
@@ -402,7 +399,7 @@ class FiniteStateMachine(State):
# Flag the FSM level setup as done
self._fsm_setup_done = True
# Execute all the FSM level actions
- for action in self.actions:
+ for action in self._actions:
action.do()
# Then, we need to run the setup of the current state
@@ -467,7 +464,7 @@ class FiniteStateMachine(State):
# Flag the FSM teardown as not needed anymore
self._fsm_teardown_done = True
# Undo all the FSM level actions here
- for action in self.actions:
+ for action in self._actions:
action.undo()
# TODO : It might be nice to have a start() and stop() method for the
diff --git a/tutorius/properties.py b/tutorius/properties.py
index 7bfbad0..2afe119 100644
--- a/tutorius/properties.py
+++ b/tutorius/properties.py
@@ -101,13 +101,13 @@ class TPropContainer(object):
the every property of the first one can be found in the other container, with
the same name and the same value.
- This is an approximation of identity because we are really looking to see
- if this container is at least a subset of the other.
-
@param otherContainer The other container that we wish to test for equality.
@returns True if every property in the first container can be found with the same
value and the same name in the second container.
"""
+ # Make sure both have the same number of properties
+ if len(self._props) != len(otherContainer._props):
+ return False
# For every property in this container
for prop in self._props.keys():
found = False
@@ -224,19 +224,6 @@ class TAddonListProperty(TutoriusProperty):
"""
pass
-
- def get_constraints(self):
- """
- Returns the list of constraints associated to this property.
- """
- if self._constraints is None:
- self._constraints = []
- for i in dir(self):
- typ = getattr(self, i)
- if isinstance(typ, Constraint):
- self._constraints.append(i)
- return self._constraints
-
class TIntProperty(TutoriusProperty):
"""
Represents an integer. Can have an upper value limit and/or a lower value