From 3b9bff2ef1826987d95815ff03c235052cea9aae Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 17 Oct 2009 17:47:58 +0000 Subject: LP 439980 : Code review changes : renamed is_identical to __eq__, relaxed action insertion constraints, added fixed meta-props for addons --- diff --git a/addons/clickaction.py b/addons/clickaction.py index 0018c1c..828dd75 100644 --- a/addons/clickaction.py +++ b/addons/clickaction.py @@ -48,5 +48,5 @@ __action__ = { 'display_name' : 'Click', 'icon' : 'format-justify-center', 'class' : ClickAction, - 'mandatoryprops' : ['widget'] + 'mandatory_props' : ['widget'] } diff --git a/addons/disablewidget.py b/addons/disablewidget.py index 210d1eb..ce3f235 100644 --- a/addons/disablewidget.py +++ b/addons/disablewidget.py @@ -36,12 +36,19 @@ class DisableWidgetAction(Action): if os.activity: self._widget = gtkutils.find_widget(os.activity, self.target) if self._widget: + # If we have an object whose sensitivity we can query, we will + # keep it to reset it in the undo() method + if hasattr(self._widget, 'get_sensitive') and callable(self._widget.get_sensitive): + self._previous_sensitivity = self._widget.get_sensitive() self._widget.set_sensitive(False) def undo(self): """Action undo""" if self._widget: - self._widget.set_sensitive(True) + if hasattr(self, '_previous_sensitivity'): + self._widget.set_sensitive(self._previous_sensitivity) + else: + self._widget.set_sensitive(True) __action__ = { 'name' : 'DisableWidgetAction', diff --git a/addons/oncewrapper.py b/addons/oncewrapper.py index fb0196f..97f4752 100644 --- a/addons/oncewrapper.py +++ b/addons/oncewrapper.py @@ -55,5 +55,5 @@ __action__ = { 'display_name' : 'Execute an action only once', 'icon' : 'once_wrapper', 'class' : OnceWrapper, - 'mandatoryprops' : ['action'] + 'mandatory_props' : ['action'] } diff --git a/addons/triggereventfilter.py b/addons/triggereventfilter.py index ea2107b..06c0995 100644 --- a/addons/triggereventfilter.py +++ b/addons/triggereventfilter.py @@ -41,5 +41,6 @@ __event__ = { 'display_name' : 'Triggerable event filter (test only)', 'icon' : '', 'class' : TriggerEventFilter, - 'mandatory_props' : ['next_state'] + 'mandatory_props' : ['next_state'], + 'test' : True } diff --git a/addons/typetextaction.py b/addons/typetextaction.py index 251dea2..fee66e5 100644 --- a/addons/typetextaction.py +++ b/addons/typetextaction.py @@ -15,7 +15,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from sugar.tutorius.actions import * - +from sugar.tutorius import gtkutils class TypeTextAction(Action): """ @@ -53,5 +53,5 @@ __action__ = { 'display_name' : 'Type text', 'icon' : 'format-justify-center', 'class' : TypeTextAction, - 'mandatoryprops' : ['widgetUAM', 'text'] + 'mandatory_props' : ['widgetUAM', 'text'] } diff --git a/addons/widgetidentifyaction.py b/addons/widgetidentifyaction.py index 65edf62..3c66211 100644 --- a/addons/widgetidentifyaction.py +++ b/addons/widgetidentifyaction.py @@ -13,8 +13,11 @@ # 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.actions import * +from sugar.tutorius.editor import WidgetIdentifier + class WidgetIdentifyAction(Action): def __init__(self): Action.__init__(self) @@ -39,5 +42,6 @@ __action__ = { "display_name" : 'Widget Identifier', "icon" : 'viewmag1', "class" : WidgetIdentifyAction, - "mandatory_props" : [] + "mandatory_props" : [], + 'test' : True } diff --git a/tests/coretests.py b/tests/coretests.py index 10cc716..4f564c8 100644 --- a/tests/coretests.py +++ b/tests/coretests.py @@ -182,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 @@ -225,16 +222,16 @@ 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): + def test_eq_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" + assert st1 == st2, "Empty states with the same name should be identical" - def test_is_identical(self): + def test_eq(self): """ Test whether two states share the same set of actions and event filters. """ @@ -261,33 +258,33 @@ class StateTest(unittest.TestCase): 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" + 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 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" + 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 st1.is_identical(non_state) == False, "Passing a non-State object should fail for identity" + assert not (st1 == non_state), "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" + 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 st1.is_identical(st3) == False, "States having a different number of actions should be different" + 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 st1.is_identical(st4) == False, "States having a different number of events should be different" + assert not (st1 == st4), "States having a different number of events should be different" st5 = copy.deepcopy(st1) st5._event_filters = [] @@ -295,7 +292,8 @@ class StateTest(unittest.TestCase): 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" + assert not (st1 == st5), "States having the same number of event filters" \ + + " but those being different should be different" class FSMTest(unittest.TestCase): """ @@ -433,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): @@ -463,7 +461,7 @@ 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): @@ -514,12 +512,12 @@ class FSMTest(unittest.TestCase): assert str(fsm) == "INIT, Final State, Other State, " - def test_is_identical(self): + def test_eq_(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" + assert not (fsm == non_fsm_object), "Testing with non FSM object should not give identity" # Compare FSMs act1 = CountAction() @@ -528,19 +526,21 @@ class FSMTest(unittest.TestCase): fsm2 = copy.deepcopy(fsm) - assert fsm.is_identical(fsm2) + assert fsm == fsm2 act2 = CountAction() fsm2.add_action(act2) - assert fsm.is_identical(fsm2) == False, "FSMs having a different number of actions should be different" + 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 fsm3.is_identical(fsm) == False, "Actions having the same number of actions but different ones should be different" + assert not(fsm3 == fsm), \ + "Actions having the same number of actions but different ones should be different" st1 = State("INIT") @@ -551,17 +551,17 @@ class FSMTest(unittest.TestCase): fsm4 = copy.deepcopy(fsm) - assert fsm.is_identical(fsm4) + assert fsm == 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" + assert not (fsm == fsm4), "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" + assert not (fsm == fsm4), "FSMs having different states should be different" fsm4.remove_state("Last State") @@ -570,7 +570,7 @@ class FSMTest(unittest.TestCase): fsm4.add_state(st5) - assert fsm.is_identical(fsm4) == False, "FSMs having states with same name but different content should be different" + assert not(fsm == fsm4), "FSMs having states with same name but different content should be different" class FSMExplorationTests(unittest.TestCase): def setUp(self): @@ -627,34 +627,5 @@ 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 03e7814..0b8251a 100644 --- a/tests/propertiestests.py +++ b/tests/propertiestests.py @@ -17,6 +17,7 @@ import unittest import uuid import os +import copy from sugar.tutorius.constraints import * from sugar.tutorius.properties import * @@ -84,14 +85,14 @@ class BasePropertyTest(unittest.TestCase): assert obj.prop == 2, "Unable to set a value on base class" - def test_is_identical(self): + def test_eq_(self): class klass(TPropContainer): prop = TutoriusProperty() obj = klass() obj2 = klass() - assert obj.is_identical(obj2), "Base property containers should be identical" + assert obj == obj2, "Base property containers should be identical" class AdvancedPropertyTest(unittest.TestCase): def test_properties_groups(self): @@ -104,11 +105,6 @@ class AdvancedPropertyTest(unittest.TestCase): property = TutoriusProperty() data = TutoriusProperty() - class klass2(TPropContainer): - property = TutoriusProperty() - message = TutoriusProperty() - data = TutoriusProperty() - class klass3(TPropContainer): property = TutoriusProperty() message = TutoriusProperty() @@ -125,7 +121,7 @@ class AdvancedPropertyTest(unittest.TestCase): obj1.message = "Initial message" obj1.data = [132, 208, 193, 142] - obj2 = klass2() + obj2 = klass1() obj2.property = 12 obj2.message = "Initial message" obj2.data = [132, 208, 193, 142] @@ -143,16 +139,20 @@ class AdvancedPropertyTest(unittest.TestCase): # 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" + assert obj1 == obj1, "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" + assert not (obj1 == obj3), "Objects should not be identical since obj3 has more props" + assert not (obj3 == obj1), "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" + assert not (obj1 == obj4), "Properties of different type should not be equal" def test_addon_properties(self): + """Test an addon property. + + This tests creates a class with a single addon property (klass1) and + assigns a new addon to it (inner1).""" class klass1(TPropContainer): addon = TAddonProperty() @@ -168,12 +168,12 @@ class AdvancedPropertyTest(unittest.TestCase): obj2 = klass1() obj2.addon = inner1("Hi!") - assert obj1.is_identical(obj2), "Identical objects with addon proeprties were treated as different" + assert obj1 == obj2, "Identical objects with addon properties 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" + assert not (obj1 == obj3), "Objects with addon property having a different value should be considered different" def test_addonlist_properties(self): class klass1(TPropContainer): @@ -200,11 +200,11 @@ class AdvancedPropertyTest(unittest.TestCase): 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" + assert obj1 == 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" + assert not (obj1 == obj3), "Differently named properties should be considered different in the addon list tests" class TIntPropertyTest(unittest.TestCase): def test_int_property(self): diff --git a/tests/run-tests.py b/tests/run-tests.py deleted file mode 100755 index beb05ab..0000000 --- a/tests/run-tests.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/python -# This is a dumb script to run tests on the sugar-jhbuild installed files -# The path added is the default path for the jhbuild build - -INSTALL_PATH="../../../../../../install/lib/python2.5/site-packages/" - -import os, sys -sys.path.insert(0, - os.path.abspath(INSTALL_PATH) -) - -FULL_PATH = os.path.join(INSTALL_PATH,"sugar/tutorius") -SUBDIRS = ["uam"] -GLOB_PATH = os.path.join(FULL_PATH,"*.py") -import unittest -from glob import glob -def report_files(): - ret = glob(GLOB_PATH) - for dir in SUBDIRS: - ret += glob(os.path.join(FULL_PATH,dir,"*.py")) - return ret - -import sys -if __name__=='__main__': - if "--coverage" in sys.argv: - sys.argv=[arg for arg in sys.argv if arg != "--coverage"] - import coverage - coverage.erase() - #coverage.exclude('raise NotImplementedError') - coverage.start() - - import coretests - import servicestests - import gtkutilstests - #import overlaytests # broken - import linear_creatortests - import actiontests - import uamtests - import filterstests - import constraintstests - import propertiestests - import serializertests - import addontests - import storetests - suite = unittest.TestSuite() - suite.addTests(unittest.findTestCases(coretests)) - suite.addTests(unittest.findTestCases(servicestests)) - suite.addTests(unittest.findTestCases(gtkutilstests)) - #suite.addTests(unittest.findTestCases(overlaytests)) # broken - suite.addTests(unittest.findTestCases(linear_creatortests)) - suite.addTests(unittest.findTestCases(actiontests)) - suite.addTests(unittest.findTestCases(uamtests)) - suite.addTests(unittest.findTestCases(filterstests)) - suite.addTests(unittest.findTestCases(constraintstests)) - suite.addTests(unittest.findTestCases(propertiestests)) - suite.addTests(unittest.findTestCases(serializertests)) - runner = unittest.TextTestRunner() - runner.run(suite) - coverage.stop() - coverage.report(report_files()) - coverage.erase() - else: - from coretests import * - from servicestests import * - from gtkutilstests import * - #from overlaytests import * # broken - from actiontests import * - from linear_creatortests import * - from uamtests import * - from filterstests import * - from constraintstests import * - from propertiestests import * - from actiontests import * - from serializertests import * - from addontests import * - from storetests import * - unittest.main() diff --git a/tests/serializertests.py b/tests/serializertests.py index c939b7a..2f2e287 100644 --- a/tests/serializertests.py +++ b/tests/serializertests.py @@ -164,7 +164,7 @@ class XMLSerializerTest(unittest.TestCase): self.test_save() reloaded_fsm = xml_ser.load_fsm(str(self.uuid)) - assert self.fsm.is_identical(reloaded_fsm), "Expected equivalence before saving vs after loading." + assert self.fsm == reloaded_fsm, "Expected equivalence before saving vs after loading." def test_all_filters(self): """ @@ -190,7 +190,7 @@ class XMLSerializerTest(unittest.TestCase): reloaded_fsm = xml_ser.load_fsm(str(self.uuid)) - assert self.fsm.is_identical(reloaded_fsm), "Expected equivalence before saving vs after loading." + assert self.fsm == reloaded_fsm, "Expected equivalence before saving vs after loading." if __name__ == "__main__": unittest.main() diff --git a/tutorius/actions.py b/tutorius/actions.py index 89e71ae..cd34976 100644 --- a/tutorius/actions.py +++ b/tutorius/actions.py @@ -18,14 +18,10 @@ This module defines Actions that can be done and undone on a state """ from gettext import gettext as _ -from sugar.tutorius import gtkutils, addon -from dialog import TutoriusDialog -import overlayer -from sugar.tutorius.editor import WidgetIdentifier +from sugar.tutorius import addon from sugar.tutorius.services import ObjectStore from sugar.tutorius.properties import * from sugar.graphics import icon -import gtk.gdk class DragWrapper(object): """Wrapper to allow gtk widgets to be dragged around""" diff --git a/tutorius/core.py b/tutorius/core.py index d034c30..4376315 100644 --- a/tutorius/core.py +++ b/tutorius/core.py @@ -89,18 +89,6 @@ 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()) - def _prepare_activity(self): """ Prepare the activity for the tutorial by loading the saved state and @@ -202,15 +190,13 @@ class State(object): # These functions are used to simplify the creation of states def add_action(self, new_action): """ - Adds an action to the state (only if it wasn't added before) + Adds an action to the state @param new_action The new action to execute when in this state @return True if added, False otherwise """ - if new_action not in self._actions: - self._actions.append(new_action) - return True - return False + self._actions.append(new_action) + return True # remove_action - We did not define names for the action, hence they're # pretty hard to remove on a precise basis @@ -256,7 +242,7 @@ class State(object): """ self._event_filters = [] - def is_identical(self, otherState): + def __eq__(self, otherState): """ Compares two states and tells whether they contain the same states with the same actions and event filters. @@ -284,7 +270,7 @@ class State(object): found = False # For each action in the other state, try to match it with this one. for otherAct in otherState._actions: - if act.is_identical(otherAct): + if act == otherAct: found = True break if found == False: @@ -299,7 +285,7 @@ class State(object): # the current filter. We just need to find one with the right # properties and values. for otherEvent in otherState._event_filters: - if event.is_identical(otherEvent): + if event == otherEvent: found = True break if found == False: @@ -578,7 +564,7 @@ class FiniteStateMachine(State): out_string += st.name + ", " return out_string - def is_identical(self, otherFSM): + def __eq__(self, otherFSM): """ Compares the elements of two FSM to ensure and returns true if they have the same set of states, containing the same actions and the same event filters. @@ -603,7 +589,7 @@ class FiniteStateMachine(State): # For every action in the other FSM, try to match it with the # current one. for otherAct in otherFSM._actions: - if act.is_identical(otherAct): + if act == otherAct: found = True break if found == False: @@ -626,7 +612,7 @@ class FiniteStateMachine(State): return False # If two states with the same name exist, then we want to make sure # they are also identical - if not state.is_identical(other_state): + if not state == other_state: return False # If we made it here, then all the states in this FSM could be matched to an diff --git a/tutorius/properties.py b/tutorius/properties.py index 2afe119..4c34511 100644 --- a/tutorius/properties.py +++ b/tutorius/properties.py @@ -95,7 +95,7 @@ class TPropContainer(object): """ return object.__getattribute__(self, "_props").keys() - def is_identical(self, otherContainer): + def __eq__(self, otherContainer): """ Compare this property container to the other one and returns True only if the every property of the first one can be found in the other container, with @@ -108,6 +108,10 @@ class TPropContainer(object): # Make sure both have the same number of properties if len(self._props) != len(otherContainer._props): return False + + if not(type(self) == type(otherContainer)): + return False + # For every property in this container for prop in self._props.keys(): found = False @@ -131,12 +135,12 @@ class TPropContainer(object): # If this is just an embedded / decorated container, then we want to # make sure the sub component are identical. elif this_type == "addon": - if not self._props[prop].is_identical(otherContainer._props[prop]): + if not (self._props[prop] == otherContainer._props[prop]): return False found = True break else: - if self._props[prop]== otherContainer._props[prop]: + if self._props[prop] == otherContainer._props[prop]: found = True break # If we arrive here, then we couldn't find any property in the second @@ -161,7 +165,7 @@ class TPropContainer(object): # Attempt to match it with every property in the other list for other_container in otherList: # If the containers are identical, - if container.is_identical(other_container): + if container == other_container: # We found a matching container. We don't need to search in the # second list anymore, so we break found = True diff --git a/tutorius/store.py b/tutorius/store.py index d66bb81..480c81b 100644 --- a/tutorius/store.py +++ b/tutorius/store.py @@ -91,8 +91,6 @@ class StoreProxy(object): state. After a successful logon, the operation requiring a login will be successful. - @param username - @param password @return True if the login was successful, False otherwise """ raise NotImplementedError("login() not implemented yet") -- cgit v0.9.1