From 6584510d390a37153c20974da6704a907058fea0 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 19 Oct 2009 04:38:32 +0000 Subject: Merge gitorious@git.sugarlabs.org:tutorius/michaeljm-dev into merge_michaeljm-dev --- (limited to 'tests/coretests.py') diff --git a/tests/coretests.py b/tests/coretests.py index eadea01..4f564c8 100644 --- a/tests/coretests.py +++ b/tests/coretests.py @@ -28,13 +28,14 @@ and event filters. Those are in their separate test module import unittest +import copy import logging -from sugar.tutorius.actions import Action, OnceWrapper, ClickAction, TypeTextAction +from sugar.tutorius.actions import * +from sugar.tutorius.addon import * from sugar.tutorius.core import * from sugar.tutorius.filters import * - -from actiontests import CountAction +from actiontests import CountAction, FakeEventFilter # Helper classes to help testing class SimpleTutorial(Tutorial): @@ -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. @@ -73,173 +96,6 @@ class TrueWhileActiveAction(Action): def undo(self): self.active = False - -class ClickableWidget(): - """ - This class fakes a widget with a clicked() method - """ - def __init__(self): - self.click_count = 0 - - def clicked(self): - self.click_count += 1 - -class FakeTextEntry(): - """ - This class fakes a widget with an insert_text() method - """ - def __init__(self): - self.text_lines = [] - self.last_entered_line = "" - self.displayed_text = "" - - def insert_text(self, text, index): - self.last_entered_line = text - self.text_lines.append(text) - self.displayed_text = self.displayed_text[0:index] + text + self.displayed_text[index+1:] - -class FakeParentWidget(): - """ - This class fakes a widet container, it implements the get_children() method - """ - def __init__(self): - self._children = [] - - def add_child(self, child): - self._children.append(child) - - def get_children(self): - return self._children - - - - -class TriggerEventFilter(EventFilter): - """ - This event filter can be triggered by simply calling its do_callback function. - - Used to fake events and see the effect on the FSM. - """ - def __init__(self, next_state): - EventFilter.__init__(self, next_state) - self.toggle_on_callback = False - - def install_handlers(self, callback, **kwargs): - """ - Forsakes the incoming callback function and just set the inner one. - """ - self._callback = self._inner_cb - - def _inner_cb(self, event_filter): - self.toggle_on_callback = not self.toggle_on_callback - -class FakeEventFilter(TriggerEventFilter): - """ - This is a fake event that is connected to the tutorial. - - The difference between this one and the TriggerEventFilter is that the - tutorial's set_state will be called on the callback. - - Do not forget to add the do_callback() after creating the object. - """ - def set_tutorial(self, tutorial): - self.tutorial = tutorial - - def _inner_cb(self, event_filter): - self.toggle_on_callback = not self.toggle_on_callback - self.tutorial.set_state(event_filter.get_next_state()) - - -class ClickActionTests(unittest.TestCase): - """ - Test class for click action - """ - def test_do_action(self): - activity = FakeParentWidget() - widget = ClickableWidget() - activity.add_child(widget) - ObjectStore().activity = activity - - action = ClickAction("0.0") - - assert widget == ObjectStore().activity.get_children()[0],\ - "The clickable widget isn't reachable from the object store \ - the test cannot pass" - - action.do() - - assert widget.click_count == 1, "clicked() should have been called by do()" - - action.do() - - assert widget.click_count == 2, "clicked() should have been called by do()" - - def test_undo(self): - activity = FakeParentWidget() - widget = ClickableWidget() - activity.add_child(widget) - ObjectStore().activity = activity - - action = ClickAction("0.0") - - assert widget == ObjectStore().activity.get_children()[0],\ - "The clickable widget isn't reachable from the object store \ - the test cannot pass" - - action.undo() - - #There is no undo for this action so the test should not fail - assert True - - - -class TypeTextActionTests(unittest.TestCase): - """ - Test class for type text action - """ - def test_do_action(self): - activity = FakeParentWidget() - widget = FakeTextEntry() - activity.add_child(widget) - ObjectStore().activity = activity - - test_text = "This is text" - - - action = TypeTextAction("0.0", test_text) - - assert widget == ObjectStore().activity.get_children()[0],\ - "The clickable widget isn't reachable from the object store \ - the test cannot pass" - - action.do() - - assert widget.last_entered_line == test_text, "insert_text() should have been called by do()" - - action.do() - - assert widget.last_entered_line == test_text, "insert_text() should have been called by do()" - assert len(widget.text_lines) == 2, "insert_text() should have been called twice" - - def test_undo(self): - activity = FakeParentWidget() - widget = FakeTextEntry() - activity.add_child(widget) - ObjectStore().activity = activity - - test_text = "This is text" - - - action = TypeTextAction("0.0", test_text) - - assert widget == ObjectStore().activity.get_children()[0],\ - "The clickable widget isn't reachable from the object store \ - the test cannot pass" - - action.undo() - - #There is no undo for this action so the test should not fail - assert True # State testing class class StateTest(unittest.TestCase): @@ -274,7 +130,7 @@ class StateTest(unittest.TestCase): Tests the fact that the event filters are correctly installed on setup and uninstalled on teardown. """ - event_filter = TriggerEventFilter("second_state") + event_filter = addon.create('TriggerEventFilter', "second_state") state = State("event_test", event_filter_list=[event_filter]) state.set_tutorial(SimpleTutorial()) @@ -326,14 +182,11 @@ class StateTest(unittest.TestCase): assert state.add_action(act2), "Could not add the second action" assert state.add_action(act3), "Could not add the third action" - # Try to add a second time an action that was already inserted - assert state.add_action(act1) == False, "Not supposed to insert an action twice" - # Fetch the associated actions actions = state.get_action_list() # Make sure all the actions are present in the state - assert act1 in actions and act2 in actions and act3 in actions,\ + assert act1 in actions and act2 in actions and act3 in actions, \ "The actions were not properly inserted in the state" # Clear the list @@ -345,9 +198,9 @@ class StateTest(unittest.TestCase): def test_add_event_filter(self): state = State("INIT") - event1 = TriggerEventFilter("s") - event2 = TriggerEventFilter("t") - event3 = TriggerEventFilter("r") + event1 = addon.create('TriggerEventFilter', "s") + event2 = addon.create('TriggerEventFilter', "t") + event3 = addon.create('TriggerEventFilter', "r") # Insert the event filters assert state.add_event_filter(event1), "Could not add event filter 1" @@ -368,7 +221,80 @@ class StateTest(unittest.TestCase): assert len(state.get_event_filter_list()) == 0, \ "Could not clear the event filter list properly" + + def test_eq_simple(self): + """ + Two empty states with the same name must be identical + """ + st1 = State("Identical") + st2 = State("Identical") + + assert st1 == st2, "Empty states with the same name should be identical" + + def test_eq(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 == st2, "States should be considered as identical" + assert st2 == 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 not (st1 == st2), "Action was changed and states should be different" + assert not (st2 == st1), "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 not (st1 == non_state), "Passing a non-State object should fail for identity" + + st2.name = "Not identical anymore" + assert not(st1 == st2), "Different state names should give different states" + st2.name = "Identical" + + st3 = copy.deepcopy(st1) + st3.add_action(addon.create("BubbleMessage", "Hi!", [128,264])) + + assert not (st1 == st3), "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 not (st1 == st4), "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 not (st1 == st5), "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 @@ -413,6 +339,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. @@ -472,9 +399,9 @@ class FSMTest(unittest.TestCase): This test removes a state from the FSM. It also verifies that the links from other states going into the removed state are gone. """ - st1 = State("INIT", event_filter_list=[TriggerEventFilter("second")]) - st2 = State("second", event_filter_list=[TriggerEventFilter("third")]) - st3 = State("third", event_filter_list=[TriggerEventFilter("second")]) + st1 = State("INIT", event_filter_list=[addon.create('TriggerEventFilter', "second")]) + st2 = State("second", event_filter_list=[addon.create('TriggerEventFilter', "third")]) + st3 = State("third", event_filter_list=[addon.create('TriggerEventFilter', "second")]) fsm = FiniteStateMachine("StateRemovalTest") @@ -504,10 +431,10 @@ class FSMTest(unittest.TestCase): # Make sure that there is no link to the removed state in the rest # of the FSM - assert "second" not in fsm.get_following_states("INIT"),\ + assert "second" not in fsm.get_following_states("INIT"), \ "The link to second from INIT still exists after removal" - assert "second" not in fsm.get_following_states("third"),\ + assert "second" not in fsm.get_following_states("third"), \ "The link to second from third still exists after removal" def test_set_same_state(self): @@ -534,8 +461,116 @@ class FSMTest(unittest.TestCase): "The action was triggered a second time, do_count = %d"%do_count 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,\ + 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_eq_(self): + fsm = FiniteStateMachine("Identity test") + + non_fsm_object = object() + + assert not (fsm == non_fsm_object), "Testing with non FSM object should not give identity" + + # Compare FSMs + act1 = CountAction() + + fsm.add_action(act1) + + fsm2 = copy.deepcopy(fsm) + + assert fsm == fsm2 + + act2 = CountAction() + fsm2.add_action(act2) + + assert not(fsm == fsm2), \ + "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 not(fsm3 == fsm), \ + "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 == fsm4 + + st3 = State("Last State") + + fsm4.add_state(st3) + + assert not (fsm == fsm4), "FSMs having a different number of states should not be identical" + + fsm4.remove_state("OtherState") + + assert not (fsm == fsm4), "FSMs having different states should be different" + + fsm4.remove_state("Last State") + + st5 = State("OtherState") + st5.add_action(CountAction()) + + fsm4.add_state(st5) + + assert not(fsm == fsm4), "FSMs having states with same name but different content should be different" class FSMExplorationTests(unittest.TestCase): def setUp(self): @@ -547,13 +582,13 @@ class FSMExplorationTests(unittest.TestCase): """ st1 = State("INIT") st1.add_action(CountAction()) - st1.add_event_filter(TriggerEventFilter("Second")) - st1.add_event_filter(TriggerEventFilter("Third")) + st1.add_event_filter(addon.create('TriggerEventFilter', "Second")) + st1.add_event_filter(addon.create('TriggerEventFilter', "Third")) st2 = State("Second") st2.add_action(TrueWhileActiveAction()) - st2.add_event_filter(TriggerEventFilter("Third")) - st2.add_event_filter(TriggerEventFilter("Fourth")) + st2.add_event_filter(addon.create('TriggerEventFilter', "Third")) + st2.add_event_filter(addon.create('TriggerEventFilter', "Fourth")) st3 = State("Third") st3.add_action(CountAction()) @@ -592,6 +627,5 @@ class FSMExplorationTests(unittest.TestCase): self.validate_previous_states("Fourth", ("Second")) - if __name__ == "__main__": unittest.main() -- cgit v0.9.1