Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--addons/bubblemessage.py27
-rw-r--r--addons/bubblemessagewimg.py14
-rw-r--r--addons/chainaction.py7
-rw-r--r--addons/changecolor.py9
-rw-r--r--addons/clickaction.py3
-rw-r--r--addons/dialogmessage.py9
-rw-r--r--addons/disablewidget.py7
-rw-r--r--addons/gtkwidgeteventfilter.py7
-rw-r--r--addons/gtkwidgettypefilter.py9
-rw-r--r--addons/messagebuttonnext.py13
-rw-r--r--addons/oncewrapper.py5
-rw-r--r--addons/readfile.py16
-rw-r--r--addons/timerevent.py8
-rw-r--r--addons/triggereventfilter.py4
-rw-r--r--addons/typetextaction.py6
-rw-r--r--addons/widgetidentifyaction.py4
-rw-r--r--tests/actiontests.py20
-rw-r--r--tests/coretests.py12
-rw-r--r--tests/filterstests.py8
-rw-r--r--tests/probetests.py129
-rw-r--r--tests/translatortests.py2
-rw-r--r--tests/vaulttests.py20
-rw-r--r--tutorius/TProbe.py78
-rw-r--r--tutorius/actions.py6
-rw-r--r--tutorius/engine.py11
-rw-r--r--tutorius/filters.py6
-rw-r--r--tutorius/properties.py18
27 files changed, 254 insertions, 204 deletions
diff --git a/addons/bubblemessage.py b/addons/bubblemessage.py
index 7e91d00..accc79f 100644
--- a/addons/bubblemessage.py
+++ b/addons/bubblemessage.py
@@ -25,42 +25,33 @@ class BubbleMessage(Action):
# Do the same for the tail position
tail_pos = TArrayProperty((0,0), 2, 2)
- def __init__(self, message=None, position=None, speaker=None, tail_pos=None):
+ def __init__(self, **kwargs):
"""
Shows a dialog with a given text, at the given position on the screen.
+ Accepted keyword args:
@param message A string to display to the user
@param position A list of the form [x, y]
@param speaker treeish representation of the speaking widget
@param tail_pos The position of the tail of the bubble; useful to point to
specific elements of the interface
"""
- Action.__init__(self)
-
- if position:
- self.position = position
- if tail_pos:
- self.tail_pos = tail_pos
- if message:
- self.message = message
+ super(BubbleMessage, self).__init__(**kwargs)
self.overlay = None
self._bubble = None
self._speaker = None
- def do(self, **kwargs):
+ def do(self, activity=None, **kwargs):
"""
Show the dialog
"""
- # get or inject overlayer
- self.overlay = ObjectStore().activity._overlayer
- # FIXME: subwindows, are left to overlap this. This behaviour is
- # undesirable. subwindows (i.e. child of top level windows) should be
- # handled either by rendering over them, or by finding different way to
- # draw the overlay.
+ if activity is None:
+ raise TypeError("Missing argument activity")
+
+ # get overlayer
+ self.overlay = activity._overlayer
- if not self.overlay:
- self.overlay = ObjectStore().activity._overlayer
if not self._bubble:
x, y = self.position
# TODO: tails are relative to tailpos. They should be relative to
diff --git a/addons/bubblemessagewimg.py b/addons/bubblemessagewimg.py
index 0ad444f..816a9ed 100644
--- a/addons/bubblemessagewimg.py
+++ b/addons/bubblemessagewimg.py
@@ -26,26 +26,18 @@ class BubbleMessageWImg(Action):
tail_pos = TArrayProperty((0,0), 2, 2)
imgpath = TResourceProperty("")
- def __init__(self, message=None, position=None, speaker=None, tail_pos=None, imgpath=None):
+ def __init__(self, **kwargs):
"""
Shows a dialog with a given text, at the given position on the screen.
+ Accepted keyword args:
@param message A string to display to the user
@param position A list of the form [x, y]
@param speaker treeish representation of the speaking widget
@param tail_pos The position of the tail of the bubble; useful to point to
specific elements of the interface
"""
- Action.__init__(self)
-
- if position:
- self.position = position
- if tail_pos:
- self.tail_pos = tail_pos
- if message:
- self.message = message
- if imgpath:
- self.imgpath = imgpath
+ super(BubbleMessageWImg, self).__init__(**kwargs)
self.overlay = None
self._bubble = None
diff --git a/addons/chainaction.py b/addons/chainaction.py
index 9e8b6c8..cc610d6 100644
--- a/addons/chainaction.py
+++ b/addons/chainaction.py
@@ -17,13 +17,8 @@
from ..actions import *
class ChainAction(Action):
- actions = TAddonListProperty()
-
"""Utility class to allow executing actions in a specific order"""
- def __init__(self, actions=[]):
- """ChainAction(action1, ... ) builds a chain of actions"""
- Action.__init__(self)
- self.actions = actions
+ actions = TAddonListProperty()
def do(self,**kwargs):
"""do() each action in the chain"""
diff --git a/addons/changecolor.py b/addons/changecolor.py
index 460da32..a510d31 100644
--- a/addons/changecolor.py
+++ b/addons/changecolor.py
@@ -14,8 +14,6 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-import time
-
import gobject
import gtk, gtk.gdk
@@ -40,13 +38,12 @@ class ChangeColor(Action):
# set timeout
timeout = 500
- def __init__(self, widaddr=None):
+ def __init__(self, **kwargs):
"""Constructor - Change a widget color
+ Accepted keyword arguments:
@param widaddr: the widget for which you want to change the color (UAM)
"""
- Action.__init__(self)
-
- if widaddr: self.widaddr = widaddr
+ super(ChangeColor, self).__init__(**kwargs)
self.init_style = None
self._new_color = None
diff --git a/addons/clickaction.py b/addons/clickaction.py
index 2821541..eae713e 100644
--- a/addons/clickaction.py
+++ b/addons/clickaction.py
@@ -25,9 +25,6 @@ class ClickAction(Action):
@param widget The threehish representation of the widget
"""
widget = TStringProperty("")
- def __init__(self, widget):
- Action.__init__(self)
- self.widget = widget
def do(self, **kwargs):
"""
diff --git a/addons/dialogmessage.py b/addons/dialogmessage.py
index fad6d2c..24646f8 100644
--- a/addons/dialogmessage.py
+++ b/addons/dialogmessage.py
@@ -22,20 +22,17 @@ class DialogMessage(Action):
message = TStringProperty("Message")
position = TArrayProperty((0, 0), 2, 2)
- def __init__(self, message=None, position=None):
+ def __init__(self, **kwargs):
"""
Shows a dialog with a given text, at the given position on the screen.
+ Accepted keyword args:
@param message A string to display to the user
@param position A list of the form [x, y]
"""
- super(DialogMessage, self).__init__()
+ super(DialogMessage, self).__init__(**kwargs)
self._dialog = None
- if message:
- self.message = message
- if position: self.position = position
-
def do(self, **kwargs):
"""
Show the dialog
diff --git a/addons/disablewidget.py b/addons/disablewidget.py
index 8b34254..15e07f2 100644
--- a/addons/disablewidget.py
+++ b/addons/disablewidget.py
@@ -21,13 +21,12 @@ from ..services import ObjectStore
class DisableWidgetAction(Action):
target = TStringProperty("0")
- def __init__(self, target):
+ def __init__(self, **kwargs):
"""Constructor
+ Accepted keyword args:
@param target target treeish
"""
- Action.__init__(self)
- if target is not None:
- self.target = target
+ super(DisableWidgetAction, self).__init__(**kwargs)
self._widget = None
def do(self, **kwargs):
diff --git a/addons/gtkwidgeteventfilter.py b/addons/gtkwidgeteventfilter.py
index ac14399..154c331 100644
--- a/addons/gtkwidgeteventfilter.py
+++ b/addons/gtkwidgeteventfilter.py
@@ -24,15 +24,14 @@ class GtkWidgetEventFilter(EventFilter):
object_id = TUAMProperty()
event_name = TEventType('clicked')
- def __init__(self, object_id=None, event_name=None):
+ def __init__(self, **kwargs):
"""Constructor
+ Accepted keyword args:
@param object_id object fqdn-style identifier
@param event_name event to attach to
"""
- super(GtkWidgetEventFilter,self).__init__()
+ super(GtkWidgetEventFilter,self).__init__(**kwargs)
self._callback = None
- self.object_id = object_id
- self.event_name = event_name
self._widget = None
self._handler_id = None
diff --git a/addons/gtkwidgettypefilter.py b/addons/gtkwidgettypefilter.py
index 0339e74..7b2e15e 100644
--- a/addons/gtkwidgettypefilter.py
+++ b/addons/gtkwidgettypefilter.py
@@ -30,20 +30,17 @@ class GtkWidgetTypeFilter(EventFilter):
text = TStringProperty("")
strokes = TSequenceProperty("")
- def __init__(self, object_id, text=None, strokes=None):
+ def __init__(self, **kwargs):
"""Constructor
- @param next_state default EventFilter param, passed on to EventFilter
+ Accepted keyword args:
@param object_id object tree-ish identifier
@param text resulting text expected
@param strokes list of strokes expected
At least one of text or strokes must be supplied
"""
- super(GtkWidgetTypeFilter, self).__init__()
- self.object_id = object_id
- self.text = text or ""
+ super(GtkWidgetTypeFilter, self).__init__(**kwargs)
self._captext = ""
- self.strokes = strokes or []
self._capstrokes = []
self._widget = None
self._handler_id = None
diff --git a/addons/messagebuttonnext.py b/addons/messagebuttonnext.py
index 74ce1bb..058ef41 100644
--- a/addons/messagebuttonnext.py
+++ b/addons/messagebuttonnext.py
@@ -38,23 +38,18 @@ class MessageButtonNext(EventFilter):
# set padding
padding = 40
- def __init__(self, message=None, position=None, center_pos=False):
+ def __init__(self, **kwargs):
"""Constructor.
-
+ Accepted keyword arguments:
@param message message to display
@param position message position
"""
- super(MessageButtonNext,self).__init__()
+ super(MessageButtonNext,self).__init__(**kwargs)
- if position:
- self.position = position
- else:
+ if not self.position:
# TODO: to be removed when creator supports editing properties on events
self.position = (300, 200)
- if message:
- self.message = message
-
self.overlay = None
self.msgnext = None
diff --git a/addons/oncewrapper.py b/addons/oncewrapper.py
index 3f6b2d0..f263dd7 100644
--- a/addons/oncewrapper.py
+++ b/addons/oncewrapper.py
@@ -26,11 +26,10 @@ class OnceWrapper(Action):
action = TAddonProperty()
- def __init__(self, action):
- Action.__init__(self)
+ def __init__(self, **kwargs):
+ super(OnceWrapper, self).__init__(**kwargs)
self._called = False
self._need_undo = False
- self.action = action
def do(self, **kwargs):
"""
diff --git a/addons/readfile.py b/addons/readfile.py
index 4a6c54d..81385ba 100644
--- a/addons/readfile.py
+++ b/addons/readfile.py
@@ -21,19 +21,13 @@ from ..properties import TFileProperty
from ..services import ObjectStore
class ReadFile(Action):
+ """
+ Calls activity.read_file to restore a specified state to an activity
+ like when restored from the journal.
+ @param filename Path to the file to read
+ """
filename = TFileProperty(None)
- def __init__(self, filename=None):
- """
- Calls activity.read_file to restore a specified state to an activity
- like when restored from the journal.
- @param filename Path to the file to read
- """
- Action.__init__(self)
-
- if filename:
- self.filename=filename
-
def do(self, **kwargs):
"""
Perform the action, call read_file on the activity
diff --git a/addons/timerevent.py b/addons/timerevent.py
index a986fa0..9896075 100644
--- a/addons/timerevent.py
+++ b/addons/timerevent.py
@@ -27,14 +27,12 @@ class TimerEvent(EventFilter):
"""
timeout = TIntProperty(15, 0)
- def __init__(self, timeout=None):
+ def __init__(self, **kwargs):
"""Constructor.
-
+ Accepted keyword args:
@param timeout timeout in seconds
"""
- super(TimerEvent,self).__init__()
- if timeout:
- self.timeout = timeout
+ super(TimerEvent,self).__init__(**kwargs)
self._handler_id = None
def install_handlers(self, callback, **kwargs):
diff --git a/addons/triggereventfilter.py b/addons/triggereventfilter.py
index 19544b0..3fc48b7 100644
--- a/addons/triggereventfilter.py
+++ b/addons/triggereventfilter.py
@@ -23,8 +23,8 @@ class TriggerEventFilter(EventFilter):
Used to fake events and see the effect on the FSM.
"""
- def __init__(self):
- EventFilter.__init__(self)
+ def __init__(self, **kwargs):
+ super(EventFilter, self).__init__(**kwargs)
self.toggle_on_callback = False
def install_handlers(self, callback, **kwargs):
diff --git a/addons/typetextaction.py b/addons/typetextaction.py
index 8c794d9..8a09ff9 100644
--- a/addons/typetextaction.py
+++ b/addons/typetextaction.py
@@ -27,12 +27,6 @@ class TypeTextAction(Action):
"""
widget = TStringProperty("")
text = TStringProperty("")
-
- def __init__(self, widget, text):
- Action.__init__(self)
-
- self.widget = widget
- self.text = text
def do(self, **kwargs):
"""
diff --git a/addons/widgetidentifyaction.py b/addons/widgetidentifyaction.py
index c44964b..b59c94a 100644
--- a/addons/widgetidentifyaction.py
+++ b/addons/widgetidentifyaction.py
@@ -19,8 +19,8 @@ from ..actions import *
from ..editor import WidgetIdentifier
class WidgetIdentifyAction(Action):
- def __init__(self):
- Action.__init__(self)
+ def __init__(self, **kwargs):
+ super(WidgetIdentifyAction, self).__init__(**kwargs)
self.activity = None
self._dialog = None
diff --git a/tests/actiontests.py b/tests/actiontests.py
index 7b8d1cb..80a10f8 100644
--- a/tests/actiontests.py
+++ b/tests/actiontests.py
@@ -50,14 +50,14 @@ class PropsTest(unittest.TestCase):
def test_get_properties(self):
act = PropertyAction(8)
- assert act.get_properties() == test_props.keys(), "Action does not contain property 'a'"
+ assert set(act.get_properties()).issuperset(test_props.keys()), "Action properties should contain at least those we specified"
- for prop_name in act.get_properties():
+ for prop_name in test_props.keys():
assert getattr(act, prop_name) == test_props[prop_name], "Wrong initial value for property %s : %s"%(prop_name,str(getattr(act, prop_name)))
class DialogMessageTest(unittest.TestCase):
def setUp(self):
- self.dial = addon.create('DialogMessage', "Message text", [200, 300])
+ self.dial = addon.create('DialogMessage', message="Message text", position=[200, 300])
def test_properties(self):
assert self.dial.message == "Message text", "Wrong start value for the message"
@@ -116,7 +116,7 @@ class OnceWrapperTests(unittest.TestCase):
CountAction
"""
act = CountAction()
- wrap = addon.create('OnceWrapper', act)
+ wrap = addon.create('OnceWrapper', action=act)
assert act.do_count == 0, "do() should not have been called in __init__()"
assert act.undo_count == 0, "undo() should not have been called in __init__()"
@@ -162,7 +162,7 @@ class ChainActionTest(unittest.TestCase):
first = ChainTester(witness)
second = ChainTester(witness)
- c = addon.create('ChainAction', [first, second])
+ c = addon.create('ChainAction', actions=[first, second])
assert witness == [], "Actions should not be triggered on init"""
c.do()
@@ -195,7 +195,7 @@ class DisableWidgetActionTests(unittest.TestCase):
assert btn.props.sensitive is True, "Callback should have been called"
- act = addon.create('DisableWidgetAction', "0")
+ act = addon.create('DisableWidgetAction', target="0")
assert btn.props.sensitive is True, "Callback should have been called again"
act.do()
assert btn.props.sensitive is False, "Callback should not have been called again"
@@ -285,7 +285,7 @@ class TypeTextActionTests(unittest.TestCase):
test_text = "This is text"
- action = addon.create('TypeTextAction', "0.0", test_text)
+ action = addon.create('TypeTextAction', widgetUAM="0.0", text=test_text)
assert widget == ObjectStore().activity.get_children()[0],\
"The clickable widget isn't reachable from the object store \
@@ -309,7 +309,7 @@ class TypeTextActionTests(unittest.TestCase):
test_text = "This is text"
- action = addon.create('TypeTextAction', "0.0", test_text)
+ action = addon.create('TypeTextAction', widgetUAM="0.0", text=test_text)
assert widget == ObjectStore().activity.get_children()[0],\
"The clickable widget isn't reachable from the object store \
@@ -330,7 +330,7 @@ class ClickActionTests(unittest.TestCase):
activity.add_child(widget)
ObjectStore().activity = activity
- action = addon.create('ClickAction', "0.0")
+ action = addon.create('ClickAction', widget="0.0")
assert widget == ObjectStore().activity.get_children()[0],\
"The clickable widget isn't reachable from the object store \
@@ -350,7 +350,7 @@ class ClickActionTests(unittest.TestCase):
activity.add_child(widget)
ObjectStore().activity = activity
- action = addon.create('ClickAction', "0.0")
+ action = addon.create('ClickAction', widget="0.0")
assert widget == ObjectStore().activity.get_children()[0],\
"The clickable widget isn't reachable from the object store \
diff --git a/tests/coretests.py b/tests/coretests.py
index b9e04e5..1cc5431 100644
--- a/tests/coretests.py
+++ b/tests/coretests.py
@@ -242,7 +242,7 @@ class StateTest(unittest.TestCase):
act1 = addon.create("BubbleMessage", message="Hi", position=[132,450])
act2 = addon.create("BubbleMessage", message="Hi", position=[132,450])
- event1 = addon.create("GtkWidgetEventFilter", "0.0.0.1.1.2.3.1", "clicked")
+ event1 = addon.create("GtkWidgetEventFilter", object_id="0.0.0.1.1.2.3.1", event_name="clicked")
act3 = addon.create("DialogMessage", message="Hello again.", position=[200, 400])
@@ -276,19 +276,19 @@ class StateTest(unittest.TestCase):
st2.name = "Identical"
st3 = deepcopy(st1)
- st3.add_action(addon.create("BubbleMessage", "Hi!", [128,264]))
+ st3.add_action(addon.create("BubbleMessage", message="Hi!", position=[128,264]))
assert not (st1 == st3), "States having a different number of actions should be different"
st4 = deepcopy(st1)
- st4.add_event_filter(addon.create("GtkWidgetEventFilter", "0.0.1.1.2.2.3", "clicked"), "next_state")
+ st4.add_event_filter(addon.create("GtkWidgetEventFilter", object_id="0.0.1.1.2.2.3", event_name="clicked"), "next_state")
assert not (st1 == st4), "States having a different number of events should be different"
st5 = deepcopy(st1)
st5._event_filters = []
- st5.add_event_filter(addon.create("GtkWidgetEventFilter", "0.1.2.3.4.1.2", "pressed"), "other_state")
+ st5.add_event_filter(addon.create("GtkWidgetEventFilter", object_id="0.1.2.3.4.1.2", event_name="pressed"), "other_state")
assert not (st1 == st5), "States having the same number of event filters" \
+ " but those being different should be different"
@@ -502,7 +502,7 @@ class FSMTest(unittest.TestCase):
st2 = State("Other State")
st3 = State("Final State")
- st1.add_action(addon.create("BubbleMessage", "Hi!", [132,312]))
+ st1.add_action(addon.create("BubbleMessage", message="Hi!", position=[132,312]))
fsm.add_state(st1)
fsm.add_state(st2)
@@ -534,7 +534,7 @@ class FSMTest(unittest.TestCase):
fsm3 = FiniteStateMachine("Identity test")
- act3 = addon.create("BubbleMessage", "Hi!", [123,312])
+ act3 = addon.create("BubbleMessage", message="Hi!", position=[123,312])
fsm3.add_action(act3)
assert not(fsm3 == fsm), \
diff --git a/tests/filterstests.py b/tests/filterstests.py
index ee6033b..5358605 100644
--- a/tests/filterstests.py
+++ b/tests/filterstests.py
@@ -69,7 +69,7 @@ class TestTimerEvent(unittest.TestCase):
ctx = gobject.MainContext()
main = gobject.MainLoop(ctx)
- e = addon.create('TimerEvent', 2) # 2 seconds should be enough :s
+ e = addon.create('TimerEvent', timeout=2) # 2 seconds should be enough :s
s = SignalCatcher()
e.install_handlers(s.callback)
@@ -112,7 +112,7 @@ class TestTimerEvent(unittest.TestCase):
ctx = gobject.MainContext()
main = gobject.MainLoop(ctx)
- e = addon.create('TimerEvent', 2) # 2 seconds should be enough :s
+ e = addon.create('TimerEvent', timeout=2) # 2 seconds should be enough :s
s = SignalCatcher()
e.install_handlers(s.callback)
@@ -159,7 +159,7 @@ class TestGtkWidgetEventFilter(unittest.TestCase):
self.top.add(self.btn1)
def test_install(self):
- h = addon.create('GtkWidgetEventFilter', "0","whatever")
+ h = addon.create('GtkWidgetEventFilter', object_id="0", event_name="whatever")
try:
h.install_handlers(None)
@@ -168,7 +168,7 @@ class TestGtkWidgetEventFilter(unittest.TestCase):
assert True, "Install should have failed"
def test_button_clicks(self):
- h = addon.create('GtkWidgetEventFilter', "0.0","clicked")
+ h = addon.create('GtkWidgetEventFilter', object_id="0.0", event_name="clicked")
s = SignalCatcher()
h.install_handlers(s.callback, activity=self.top)
diff --git a/tests/probetests.py b/tests/probetests.py
index 17c6afc..c93cf5c 100644
--- a/tests/probetests.py
+++ b/tests/probetests.py
@@ -71,7 +71,6 @@ class MockActivity(object):
def get_id(self):
return "unique_id_1"
-
class MockProbeProxy(object):
_MockProxyCache = {}
@@ -190,6 +189,8 @@ class ProbeTest(unittest.TestCase):
self.probe = TProbe(self.activity, MockServiceProxy())
#Override the eventOccured on the Probe...
+ # Stores events in a global "event box"
+ #WARNING: Depends on the implementation of TProbe!!
self.old_eO = self.probe.eventOccured
def newEo(event):
global event_box
@@ -342,51 +343,78 @@ class ProbeManagerTest(unittest.TestCase):
assert len(self.probeManager.get_registered_probes_list("act1")) == 0
assert self.probeManager.get_registered_probes_list("act1") == []
- def test_actions(self):
+ def test_action_uninstall(self):
self.probeManager.register_probe("act1", "unique_id_1")
self.probeManager.register_probe("act2", "unique_id_2")
act1 = self.probeManager.get_registered_probes_list("act1")[0][1]
act2 = self.probeManager.get_registered_probes_list("act2")[0][1]
- ad1 = MockAddon()
- ad1_address = "Address1"
- def callback(value):
- pass
- def error_cb():
- pass
- #ErrorCase: install, update, uninstall without currentActivity
- #Action functions should do a warning if there is no activity
- self.assertRaises(RuntimeWarning, self.probeManager.install, ad1_address, ad1, callback)
- self.assertRaises(RuntimeWarning, self.probeManager.update, ad1_address, ad1)
- self.assertRaises(RuntimeWarning, self.probeManager.uninstall, ad1_address)
- assert act1.MockAction is None, "Action should not be installed on inactive proxy"
- assert act2.MockAction is None, "Action should not be installed on inactive proxy"
-
- self.probeManager.currentActivity = "act1"
- self.probeManager.install(ad1, callback, error_cb)
- assert act1.MockAction == ad1, "Action should have been installed"
- assert act2.MockAction is None, "Action should not be installed on inactive proxy"
+ ad1 = MockAddon(source="act1")
- self.probeManager.update(ad1_address, ad1)
- assert act1.MockActionUpdate == ad1, "Action should have been updated"
- assert act2.MockActionUpdate is None, "Should not update on inactive"
+ #ErrorCase: update should fail without a source
+ self.assertRaises(RuntimeWarning, self.probeManager.update, "SomeAddress", ad1)
+ #ErrorCase: update should fail if the source does not exist
+ self.assertRaises(RuntimeWarning, self.probeManager.update, "unique:SomeAddress", ad1)
- self.probeManager.currentActivity = "act2"
- self.probeManager.uninstall(ad1_address)
- assert act1.MockActionAddress == ad1_address, "Action should still be installed"
+ self.probeManager.install(ad1, None, None)
+
+ assert act1.MockAction == ad1, "Action should have been installed"
+
+ self.probeManager.uninstall("unique_id_2:SomeAddress")
+ assert act1.MockAction == ad1, "Action should still be installed"
- self.probeManager.currentActivity = "act1"
- self.probeManager.uninstall(ad1_address)
+ self.probeManager.uninstall("unique_id_1:SomeAddress")
assert act1.MockAction is None, "Action should be uninstalled"
- def test_events(self):
+ def test_action_install(self):
self.probeManager.register_probe("act1", "unique_id_1")
self.probeManager.register_probe("act2", "unique_id_2")
act1 = self.probeManager.get_registered_probes_list("act1")[0][1]
act2 = self.probeManager.get_registered_probes_list("act2")[0][1]
+ ad1 = MockAddon(source="act1")
+ ad2 = MockAddon(source="act2")
+ ad3 = MockAddon(source="act3")
+ ad4 = MockAddon()
+
+ #ErrorCase: install should fail without a source
+ self.assertRaises(RuntimeWarning, self.probeManager.install, ad4, None, None)
+ #ErrorCase: install should fail if the source does not exist
+ self.assertRaises(RuntimeWarning, self.probeManager.install, ad3, None, None)
+
+ #install should put the action in the right probes
+ self.probeManager.install(ad1, None, None)
+ assert act1.MockAction is ad1, "Action should be on act1"
+ assert act2.MockAction is not ad1, "Action should not be on act2"
+
+ self.probeManager.install(ad2, None, None)
+ assert act1.MockAction is not ad2, "Action should not be on act1"
+ assert act2.MockAction is ad2, "Action should be on act2"
+
+ def test_action_update(self):
+ self.probeManager.register_probe("act1", "unique_id_1")
+ self.probeManager.register_probe("act2", "unique_id_2")
+ act1 = self.probeManager.get_registered_probes_list("act1")[0][1]
+ act2 = self.probeManager.get_registered_probes_list("act2")[0][1]
ad1 = MockAddon()
- ad2 = MockAddon()
+
+ #ErrorCase: update should fail without a source
+ self.assertRaises(RuntimeWarning, self.probeManager.update, "SomeAddress", ad1)
+ #ErrorCase: update should fail if the source does not exist
+ self.assertRaises(RuntimeWarning, self.probeManager.update, "unique:SomeAddress", ad1)
+
+ #update should update the right probe
+ self.probeManager.update("unique_id_1:Address", ad1)
+ assert act1.MockActionUpdate is ad1, "Action should be on act1"
+ assert act2.MockActionUpdate is not ad1, "Action should not be on act1"
+
+ def test_event_unsubscribe(self):
+ self.probeManager.register_probe("act1", "unique_id_1")
+ self.probeManager.register_probe("act2", "unique_id_2")
+ act1 = self.probeManager.get_registered_probes_list("act1")[0][1]
+
+ ad1 = MockAddon(source="act1")
+ ad2 = MockAddon(source="act2")
ad2.i, ad2.s = (2, "test2")
cb1 = lambda *args: None
@@ -394,22 +422,43 @@ class ProbeManagerTest(unittest.TestCase):
error_cb1 = lambda *args:None
cb2 = lambda *args: None
- #ErrorCase: unsubscribe and subscribe without current activity
- #Event functions should do a warning if there is no activity
- self.assertRaises(RuntimeWarning, self.probeManager.subscribe, ad1, cb1, install_cb1, error_cb1)
- self.assertRaises(RuntimeWarning, self.probeManager.unsubscribe, None)
- assert act1.MockEvent is None, "No event should be on act1"
- assert act2.MockEvent is None, "No event should be on act2"
+ #ErrorCase: unsubscribe should fail without a source
+ self.assertRaises(RuntimeWarning, self.probeManager.unsubscribe, "SomeAddress")
+ #ErrorCase: unsubscribe should fail silently if the source does not exist
+ self.probeManager.unsubscribe("unique:SomeAddress")
+ assert act1.MockEventAddr is None, "No unsubcribe should have been called"
- self.probeManager.currentActivity = "act1"
self.probeManager.subscribe(ad1, cb1, install_cb1, error_cb1)
assert act1.MockEvent == ad1, "Event should have been installed"
assert act1.MockCB == cb1, "Callback should have been set"
- assert act2.MockEvent is None, "No event should be on act2"
- self.probeManager.unsubscribe("SomeAddress")
+ self.probeManager.unsubscribe("unique_id_1:SomeAddress")
assert act1.MockEventAddr == "SomeAddress", "Unsubscribe should have been called"
- assert act2.MockEventAddr is None, "Unsubscribe should not have been called"
+
+ def test_event_subscribe(self):
+ self.probeManager.register_probe("act1", "unique_id_1")
+ self.probeManager.register_probe("act2", "unique_id_2")
+ act1 = self.probeManager.get_registered_probes_list("act1")[0][1]
+ act2 = self.probeManager.get_registered_probes_list("act2")[0][1]
+
+ ad1 = MockAddon(source="act1")
+ ad2 = MockAddon(source="act2")
+ ad3 = MockAddon(source="act3")
+ ad4 = MockAddon()
+
+ #ErrorCase: subscribe should fail without a source
+ self.assertRaises(RuntimeWarning, self.probeManager.subscribe, ad4, None, None, None)
+ #ErrorCase: subscribe should fail if the source does not exist
+ self.assertRaises(RuntimeWarning, self.probeManager.subscribe, ad3, None, None, None)
+ #subscribe should put the action in the right probes
+ self.probeManager.subscribe(ad1, None, None, None)
+ assert act1.MockEvent is ad1, "Action should be on act1"
+ assert act2.MockEvent is not ad1, "Action should not be on act2"
+
+ self.probeManager.subscribe(ad2, None, None, None)
+ assert act1.MockEvent is not ad2, "Action should not be on act1"
+ assert act2.MockEvent is ad2, "Action should be on act2"
+
class ProbeProxyTest(unittest.TestCase):
def setUp(self):
diff --git a/tests/translatortests.py b/tests/translatortests.py
index 3b5ca6f..05a7831 100644
--- a/tests/translatortests.py
+++ b/tests/translatortests.py
@@ -61,7 +61,7 @@ class ResourceTranslatorTests(unittest.TestCase):
self.fsm = Tutorial("TestTutorial1")
# Add a few states
act1 = addon.create('BubbleMessage', message="Hi", position=[300, 450])
- ev1 = addon.create('GtkWidgetEventFilter', "0.12.31.2.2", "clicked")
+ ev1 = addon.create('GtkWidgetEventFilter', object_id="0.12.31.2.2", event_name="clicked")
act2 = addon.create('BubbleMessage', message="Second message", position=[250, 150], tail_pos=[1,2])
self.fsm.add_action("INIT", act1)
st2 = self.fsm.add_state((act2,))
diff --git a/tests/vaulttests.py b/tests/vaulttests.py
index 729d36d..1e39d8c 100644
--- a/tests/vaulttests.py
+++ b/tests/vaulttests.py
@@ -106,7 +106,7 @@ class VaultInterfaceTest(unittest.TestCase):
self.fsm = Tutorial("TestTutorial1")
# Add a few states
act1 = addon.create('BubbleMessage', message="Hi", position=[300, 450])
- ev1 = addon.create('GtkWidgetEventFilter', "0.12.31.2.2", "clicked")
+ ev1 = addon.create('GtkWidgetEventFilter', object_id="0.12.31.2.2", event_name="clicked")
act2 = addon.create('BubbleMessage', message="Second message", position=[250, 150], tail_pos=[1,2])
self.fsm.add_action("INIT", act1)
st2 = self.fsm.add_state((act2,))
@@ -487,7 +487,7 @@ class XMLSerializerTest(unittest.TestCase):
# Add a few states
act1 = addon.create('BubbleMessage', message="Hi", position=[300, 450])
- ev1 = addon.create('GtkWidgetEventFilter', "0.12.31.2.2", "clicked")
+ ev1 = addon.create('GtkWidgetEventFilter', object_id="0.12.31.2.2", event_name="clicked")
act2 = addon.create('BubbleMessage', message="Second message", position=[250, 150], tail_pos=[1,2])
self.fsm.add_action("INIT",act1)
@@ -535,12 +535,12 @@ class XMLSerializerTest(unittest.TestCase):
"""
fsm = Tutorial("TestActions")
tuto_file = cStringIO.StringIO()
- act1 = addon.create('BubbleMessage', "Hi!", position=[10,120], tail_pos=[-12,30])
- act2 = addon.create('DialogMessage', "Hello again.", position=[120,10])
+ act1 = addon.create('BubbleMessage', message="Hi!", position=[10,120], tail_pos=[-12,30])
+ act2 = addon.create('DialogMessage', message="Hello again.", position=[120,10])
act3 = addon.create('WidgetIdentifyAction')
- act4 = addon.create('DisableWidgetAction', "0.0.0.1.0.0.0")
- act5 = addon.create('TypeTextAction', "0.0.0.1.1.1.0.0", "New text")
- act6 = addon.create('ClickAction', "0.0.1.0.1.1")
+ act4 = addon.create('DisableWidgetAction', target="0.0.0.1.0.0.0")
+ act5 = addon.create('TypeTextAction', widget="0.0.0.1.1.1.0.0", text="New text")
+ act6 = addon.create('ClickAction', widget="0.0.1.0.1.1")
act7 = addon.create('OnceWrapper', action=act1)
act8 = addon.create('ChainAction', actions=[act1, act2, act3, act4])
actions = [act1, act2, act3, act4, act5, act6, act7, act8]
@@ -565,10 +565,10 @@ class XMLSerializerTest(unittest.TestCase):
fsm = Tutorial("TestFilters")
tuto_file = cStringIO.StringIO()
- ev1 = addon.create('TimerEvent', 1000)
+ ev1 = addon.create('TimerEvent', timeout=1000)
ev2 = addon.create('GtkWidgetEventFilter', object_id="0.0.1.1.0.0.1", event_name="clicked")
- ev3 = addon.create('GtkWidgetTypeFilter', "0.0.1.1.1.2.3", text="Typed stuff")
- ev4 = addon.create('GtkWidgetTypeFilter', "0.0.1.1.1.2.3", strokes="acbd")
+ ev3 = addon.create('GtkWidgetTypeFilter', object_id="0.0.1.1.1.2.3", text="Typed stuff")
+ ev4 = addon.create('GtkWidgetTypeFilter', object_id="0.0.1.1.1.2.3", strokes="acbd")
filters = [ev1, ev2, ev3, ev4]
for efilter in filters:
diff --git a/tutorius/TProbe.py b/tutorius/TProbe.py
index 2834f0c..7f4126e 100644
--- a/tutorius/TProbe.py
+++ b/tutorius/TProbe.py
@@ -46,6 +46,10 @@ import copy
-------------------- ----------
"""
+
+#Prefix separator for action/event addresses
+PSEP=":"
+
#TODO Add stub error handling for remote calls in the classes so that it will
# be clearer how errors can be handled in the future.
@@ -94,7 +98,7 @@ class TProbe(dbus.service.Object):
self._installedActions = {}
self._subscribedEvents = {}
- LOGGER.debug("TProbe :: registering '%s' with unique_id '%s'", self._activity_name, activity.get_id())
+ LOGGER.debug("TProbe :: registering '%s' with unique_id '%s'", self._activity_name, self._unique_id)
self._service_proxy.register_probe(self._activity_name, self._unique_id)
def start(self):
@@ -324,6 +328,7 @@ class ProbeProxy:
bus = dbus.SessionBus()
self._object = bus.get_object(activityName, "/tutorius/Probe/"+str(unique_id))
self._probe = dbus.Interface(self._object, "org.tutorius.ProbeInterface")
+ self.prefix = str(unique_id)+PSEP
self._actions = {}
self._edition_callbacks = {}
@@ -375,7 +380,7 @@ class ProbeProxy:
if editing_cb:
self._edition_callbacks[address] = editing_cb
# Propagate the action installed callback upwards in the stack
- callback(address)
+ callback(self.prefix + address)
def __clear_action(self, address):
# Remove the action installed at this address
@@ -458,7 +463,7 @@ class ProbeProxy:
# our dictionary (python pass arguments by reference)
self._subscribedEvents[address] = copy.copy(event)
- event_subscribed_cb(address)
+ event_subscribed_cb(self.prefix + address)
return address
def __clear_event(self, address):
@@ -574,6 +579,12 @@ class ProbeManager(object):
currentActivity = property(fget=getCurrentActivity, fset=setCurrentActivity)
+ def get_source_activity(self, propc):
+ if hasattr(propc, "source"):
+ return propc.source
+ else:
+ return None
+
def install(self, action, action_installed_cb, error_cb, is_editing=False, editing_cb=None):
"""
Install an action on the current activity
@@ -586,8 +597,13 @@ class ProbeManager(object):
this action (only used when is_editing is true)
@return None
"""
- if self.currentActivity:
- return self._first_proxy(self.currentActivity).install(
+ activity = self.get_source_activity(action)
+ #Allow the creator to look for the current activity
+ if activity is None and is_editing:
+ activity = self.currentActivity
+
+ if activity:
+ return self._first_proxy(activity).install(
action=action,
is_editing=is_editing,
action_installed_cb=action_installed_cb,
@@ -605,8 +621,15 @@ class ProbeManager(object):
@param is_editing whether this action comes from the editor
@return None
"""
- if self.currentActivity:
- return self._first_proxy(self.currentActivity).update(action_address, newaction, is_editing)
+ probe_id, sep, address = action_address.rpartition(PSEP)
+ if probe_id:
+ probe = self._get_proxy_by_unique_id(probe_id)
+ if probe is None:
+ #TODO What happens if the Probe is gone??
+ raise RuntimeWarning("ProbeProxy containing action address is gone")
+ else:
+ return probe.update(address, newaction, is_editing)
+
else:
raise RuntimeWarning("No activity attached")
@@ -617,8 +640,16 @@ class ProbeManager(object):
@param block Force a synchroneous dbus call if True
@param is_editing whether this action comes from the editor
"""
- if self.currentActivity:
- return self._first_proxy(self.currentActivity).uninstall(action_address, is_editing)
+ probe_id, sep, address = action_address.rpartition(PSEP)
+ if probe_id:
+ probe = self._get_proxy_by_unique_id(probe_id)
+ if probe is None:
+ logging.warning(
+ "ProbeProxy for address %s is gone, assuming uninstall not necessary" % \
+ action_address)
+ else:
+ return probe.uninstall(address, is_editing)
+
else:
raise RuntimeWarning("No activity attached")
@@ -646,8 +677,10 @@ class ProbeManager(object):
installation
@return address identifier used for unsubscribing
"""
- if self.currentActivity:
- return self._first_proxy(self.currentActivity).subscribe(event, notification_cb,\
+ activity = self.get_source_activity(event)
+
+ if activity:
+ return self._first_proxy(activity).subscribe(event, notification_cb,\
event_subscribed_cb, error_cb)
else:
raise RuntimeWarning("No activity attached")
@@ -658,8 +691,15 @@ class ProbeManager(object):
@param address identifier given by subscribe()
@return None
"""
- if self.currentActivity:
- return self._first_proxy(self.currentActivity).unsubscribe(address)
+ probe_id, sep, address = address.rpartition(PSEP)
+ if probe_id:
+ probe = self._get_proxy_by_unique_id(probe_id)
+ if probe is None:
+ logging.warning(
+ "ProbeProxy for address %s is gone, assuming unsubscribe not necessary" % \
+ address)
+ else:
+ return probe.unsubscribe(address)
else:
raise RuntimeWarning("No activity attached")
@@ -720,4 +760,14 @@ class ProbeManager(object):
else:
raise RuntimeWarning("No activity attached under '%s'", process_name)
-
+ def _get_proxy_by_unique_id(self, unique_id):
+ """
+ Get a probe proxy by it's unique id.
+ @param unique_id The unique id of the probe
+ @return the probe proxy or None if not found
+ """
+ for probes in self._probes.values():
+ for id, probe in probes:
+ if id == unique_id:
+ return probe
+ return None
diff --git a/tutorius/actions.py b/tutorius/actions.py
index cf586f2..6d1f58e 100644
--- a/tutorius/actions.py
+++ b/tutorius/actions.py
@@ -165,9 +165,9 @@ class DragWrapper(object):
class Action(TPropContainer):
"""Base class for Actions"""
- def __init__(self):
- TPropContainer.__init__(self)
- self.position = (0,0)
+ source = TStringProperty(None, null=True)
+ def __init__(self, **kwargs):
+ super(Action, self).__init__(**kwargs)
self._drag = None
# The callback that will be triggered when the action is requested
# to notify all its changes
diff --git a/tutorius/engine.py b/tutorius/engine.py
index 198fa11..c0769b5 100644
--- a/tutorius/engine.py
+++ b/tutorius/engine.py
@@ -77,14 +77,9 @@ class TutorialRunner(object):
#Temp FIX until event/actions have an activity id
self._activity_id = None
- #Temp FIX until event, actions have an activity id
- def setCurrentActivity(self):
- self._pM.currentActivity = self._activity_id
-
###########################################################################
# Incoming messages
def start(self):
- self.setCurrentActivity() #Temp Hack until activity in events/actions
self.enterState(self._tutorial.INIT)
def stop(self):
@@ -141,6 +136,8 @@ class TutorialRunner(object):
# Send all the event registration
for (event_name, (event, next_state)) in transitions.items():
+ if hasattr(event, "source") and not event.source:
+ event.source = self._activity_id
self._pM.subscribe(event,
save_args(self._handleEvent, next_state),
save_args(self.event_subscribed, event_name),
@@ -153,7 +150,6 @@ class TutorialRunner(object):
###########################################################################
# Helper functions
def _execute_stop(self):
- self.setCurrentActivity() #Temp Hack until activity in events/actions
self._teardownState()
self._state = None
self._runner_state = RUNNER_STATE_IDLE
@@ -253,6 +249,8 @@ class TutorialRunner(object):
for (action_name, action) in actions.items():
LOGGER.debug("TutorialRunner :: Installed action %s"%(action_name))
+ if hasattr(action, "source") and not action.source:
+ action.source = self._activity_id
self._pM.install(action,
save_args(self.action_installed, action_name),
save_args(self.install_error, action_name))
@@ -269,7 +267,6 @@ class TutorialRunner(object):
@param state_name The name of the state to enter in
"""
- self.setCurrentActivity() #Temp Hack until activity in events/actions
# Set the runner state to actions setup
self._runner_state = RUNNER_STATE_SETUP_ACTIONS
diff --git a/tutorius/filters.py b/tutorius/filters.py
index 38cf86b..6ef8867 100644
--- a/tutorius/filters.py
+++ b/tutorius/filters.py
@@ -25,12 +25,12 @@ class EventFilter(properties.TPropContainer):
"""
Base class for an event filter
"""
-
- def __init__(self):
+ source = properties.TStringProperty(None, null=True)
+ def __init__(self, **kwargs):
"""
Constructor.
"""
- super(EventFilter, self).__init__()
+ super(EventFilter, self).__init__(**kwargs)
self._callback = None
def install_handlers(self, callback, **kwargs):
diff --git a/tutorius/properties.py b/tutorius/properties.py
index bfdb32c..a0d63bb 100644
--- a/tutorius/properties.py
+++ b/tutorius/properties.py
@@ -49,12 +49,14 @@ class TPropContainer(object):
at the cost of needing a mapping between container instances, and
property values. This is what TPropContainer does.
"""
- def __init__(self):
+ def __init__(self, **kwargs):
"""
Prepares the instance for property value storage. This is done at
object initialization, thus allowing initial mapping of properties
declared on the class. Properties won't work correctly without
this call.
+
+ Keyword arguments will be evaluated as properties
"""
# create property value storage
object.__setattr__(self, "_props", {})
@@ -74,6 +76,10 @@ class TPropContainer(object):
# to the creator to update its action edition dialog.
self._diff_dict = {}
+ #Set attribute values that were supplied
+ for key, value in kwargs.items():
+ setattr(self, key, value)
+
def __getattribute__(self, name):
"""
Process the 'fake' read of properties in the appropriate instance
@@ -254,11 +260,15 @@ class TStringProperty(TutoriusProperty):
Represents a string. Can have a maximum size limit.
"""
widget_class = StringPropWidget
- def __init__(self, value, size_limit=None):
+ def __init__(self, value, size_limit=None, null=False):
TutoriusProperty.__init__(self)
self.type = "string"
- self.size_limit = MaxSizeConstraint(size_limit)
- self.string_type = StringTypeConstraint()
+ if size_limit:
+ self.size_limit = MaxSizeConstraint(size_limit)
+ if null:
+ self.string_type = TypeConstraint((str, type(None)))
+ else:
+ self.string_type = StringTypeConstraint()
self.default = self.validate(value)