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-11-08 03:26:16 (GMT)
committer mike <michael.jmontcalm@gmail.com>2009-11-08 03:26:16 (GMT)
commit80ebf5d2fc91e0e9359becdd0b344332063951ab (patch)
tree1ebbe610ea58d9d88cf74624a418d7e5f8815abe
parent5491c551a2f88dbda852bc0e9d0c5406019e2be8 (diff)
parentb3039144e050555ad93c1ab18ae228b5b1970fb5 (diff)
Merge branch 'engine_tutorial_ADT_migration' of ../mainline
-rw-r--r--tests/probetests.py131
-rw-r--r--tutorius/TProbe.py140
-rw-r--r--tutorius/service.py63
3 files changed, 230 insertions, 104 deletions
diff --git a/tests/probetests.py b/tests/probetests.py
index e1a587b..977eda9 100644
--- a/tests/probetests.py
+++ b/tests/probetests.py
@@ -66,15 +66,20 @@ class MockAddon(Action):
fake_addon_cache["MockAddon"] = MockAddon
class MockActivity(object):
- pass
+ def get_bundle_id(self):
+ return "localhost.unittest.ProbeTest"
+
+ def get_id(self):
+ return "unique_id_1"
+
class MockProbeProxy(object):
_MockProxyCache = {}
- def __new__(cls, activityName):
+ def __new__(cls, activityName, unique_id):
#For testing, use only one instance per activityName
return cls._MockProxyCache.setdefault(activityName, super(MockProbeProxy, cls).__new__(cls))
- def __init__(self, activityName):
+ def __init__(self, activityName, unique_id):
"""
Constructor
@param activityName unique activity id. Must be a valid dbus bus name.
@@ -153,6 +158,12 @@ class MockSessionBus(object):
old_SessionBus = dbus.SessionBus
+class MockServiceProxy(object):
+ def register_probe(self, process_name, unique_id):
+ pass
+ def unregister_probe(self, unique_id):
+ pass
+
###########################################################################
# Begin Test Cases
###########################################################################
@@ -170,7 +181,7 @@ class ProbeTest(unittest.TestCase):
#Setup the activity and probe
self.activity = MockActivity()
- self.probe = TProbe("localhost.unittest.ProbeTest", self.activity)
+ self.probe = TProbe(self.activity, MockServiceProxy())
#Override the eventOccured on the Probe...
self.old_eO = self.probe.eventOccured
@@ -287,67 +298,49 @@ class ProbeManagerTest(unittest.TestCase):
MockProbeProxy._MockProxyCache = {}
self.probeManager = ProbeManager(proxy_class=MockProbeProxy)
- def test_attach(self):
- #ErrorCase: Set currentActivity to unattached activity
- #Attempt to set to a non existing activity
- try:
- self.probeManager.currentActivity = "act1"
- assert False, "Exception expected"
- except RuntimeError, e:
- pass
-
- #Attach an activity
- self.probeManager.attach("act1")
-
- #Should have been created
- assert "act1" in MockProbeProxy._MockProxyCache.keys(), "Proxy not created"
-
- #ErrorCase: Attach multiple times to same activity
- #Try to attach again
- self.assertRaises(RuntimeWarning, self.probeManager.attach, "act1")
-
- #Set current activity should work
- self.probeManager.currentActivity = "act1"
-
- #TODO Fill in the alive/notalive behavior at creation time once
- # it is fixed in the ProbeManager
-
- def test_detach(self):
- #attach an activity
- self.probeManager.attach("act1")
- self.probeManager.currentActivity = "act1"
- act1 = MockProbeProxy("act1")
-
- #Now we detach
- self.probeManager.detach("act1")
- assert act1.MockAlive == False, "ProbeProxy should have been detached"
- assert self.probeManager.currentActivity is None, "Current activity should be None"
-
- #Attempt to detach again, should do nothing
- #ErrorCase: detach already detached (currently silent fail)
- self.probeManager.detach("act1")
-
- #Now, attach 2 activities
- self.probeManager.attach("act2")
- self.probeManager.attach("act3")
- act2 = MockProbeProxy("act2")
- act3 = MockProbeProxy("act3")
-
- self.probeManager.currentActivity = "act2"
-
- assert act2.MockAlive and act3.MockAlive, "Both ProbeProxy instances should be alive"
+ def test_register_probe(self):
+ assert len(self.probeManager.get_registered_probes_list()) == 0
+
+ self.probeManager.register_probe("act1", "unique_id_1")
+ assert len(self.probeManager.get_registered_probes_list()) == 1
+ assert len(self.probeManager.get_registered_probes_list("act1")) == 1
+ assert self.probeManager.get_registered_probes_list()[0][0] == "unique_id_1"
+
+ self.probeManager.register_probe("act2","unique_id_2")
+ assert len(self.probeManager.get_registered_probes_list()) == 2
+ assert len(self.probeManager.get_registered_probes_list("act1")) == 1
+ assert self.probeManager.get_registered_probes_list("act1")[0][0] == "unique_id_1"
+ assert len(self.probeManager.get_registered_probes_list("act2")) == 1
+ assert self.probeManager.get_registered_probes_list("act2")[0][0] == "unique_id_2"
+
+ def test_register_multiple_probes(self):
+ assert len(self.probeManager.get_registered_probes_list()) == 0
+
+ self.probeManager.register_probe("act1", "unique_id_1")
+ self.probeManager.register_probe("act1","unique_id_2")
+ assert len(self.probeManager.get_registered_probes_list()) == 2
+ assert len(self.probeManager.get_registered_probes_list("act1")) == 2
+ assert self.probeManager.get_registered_probes_list("act1")[0][0] == "unique_id_1"
+ assert self.probeManager.get_registered_probes_list("act1")[1][0] == "unique_id_2"
+
+ def test_unregister_probe(self):
+ assert len(self.probeManager.get_registered_probes_list()) == 0
+ self.probeManager.register_probe("act1", "unique_id_1")
+ self.probeManager.register_probe("act1","unique_id_2")
+
+ self.probeManager.unregister_probe("unique_id_1")
+ assert len(self.probeManager.get_registered_probes_list("act1")) == 1
+ assert self.probeManager.get_registered_probes_list("act1")[0][0] == "unique_id_2"
- #Detach the not active activity
- self.probeManager.detach("act3")
- #Check the statuses
- assert act2.MockAlive and not act3.MockAlive, "Only act2 should be alive"
- assert self.probeManager.currentActivity == "act2", "act2 should not have failed"
+ self.probeManager.unregister_probe("unique_id_2")
+ assert len(self.probeManager.get_registered_probes_list("act1")) == 0
+ assert self.probeManager.get_registered_probes_list("act1") == []
def test_actions(self):
- self.probeManager.attach("act1")
- self.probeManager.attach("act2")
- act1 = MockProbeProxy("act1")
- act2 = MockProbeProxy("act2")
+ 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()
#ErrorCase: install, update, uninstall without currentActivity
@@ -376,10 +369,10 @@ class ProbeManagerTest(unittest.TestCase):
assert act1.MockAction is None, "Action should be uninstalled"
def test_events(self):
- self.probeManager.attach("act1")
- self.probeManager.attach("act2")
- act1 = MockProbeProxy("act1")
- act2 = MockProbeProxy("act2")
+ 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()
@@ -405,16 +398,18 @@ class ProbeManagerTest(unittest.TestCase):
assert act1.MockEventAddr == "SomeAddress", "Unsubscribe should have been called"
assert act2.MockEventAddr is None, "Unsubscribe should not have been called"
+
class ProbeProxyTest(unittest.TestCase):
def setUp(self):
dbus.SessionBus = MockSessionBus
- self.mockObj = MockProxyObject("unittest.TestCase", "/tutorius/Probe")
- self.probeProxy = ProbeProxy("unittest.TestCase")
+ self.mockObj = MockProxyObject("unittest.TestCase", "/tutorius/Probe/unique_id_1")
+ self.probeProxy = ProbeProxy("unittest.TestCase", "unique_id_1")
def tearDown(self):
dbus.SessionBus = old_SessionBus
MockProxyObject._MockProxyObjects = {}
+ # TODO: Clean-Up the dbus session bus ???
def test_Alive(self):
self.mockObj.MockRet["ping"] = "alive"
diff --git a/tutorius/TProbe.py b/tutorius/TProbe.py
index f55547c..08c9651 100644
--- a/tutorius/TProbe.py
+++ b/tutorius/TProbe.py
@@ -38,45 +38,65 @@ class TProbe(dbus.service.Object):
a DBUS Interface.
"""
- def __init__(self, activity_name, activity):
+ def __init__(self, activity, service_proxy=None):
"""
Create and register a TProbe for an activity.
- @param activity_name unique activity_id
@param activity activity reference, must be a gtk container
+ @param service_proxy
"""
- LOGGER.debug("TProbe :: Creating TProbe for %s (%d)", activity_name, os.getpid())
- LOGGER.debug("TProbe :: Current gobject context: %s", str(gobject.main_context_default()))
- LOGGER.debug("TProbe :: Current gobject depth: %s", str(gobject.main_depth()))
# Moving the ObjectStore assignment here, in the meantime
# the reference to the activity shouldn't be share as a
# global variable but passed by the Probe to the objects
# that requires it
self._activity = activity
+
+ if service_proxy == None:
+ from .service import ServiceProxy
+
+ self._service_proxy = service_proxy or ServiceProxy()
ObjectStore().activity = activity
- self._activity_name = activity_name
+ self._activity_name = activity.get_bundle_id()
+ self._unique_id = activity.get_id()
+
+ LOGGER.debug("TProbe :: Creating TProbe for %s (%d)", self._activity_name, os.getpid())
+ LOGGER.debug("TProbe :: Current gobject context: %s", str(gobject.main_context_default()))
+ LOGGER.debug("TProbe :: Current gobject depth: %s", str(gobject.main_depth()))
self._session_bus = dbus.SessionBus()
# Giving a new name because _name is already used by dbus
- self._name2 = dbus.service.BusName(activity_name, self._session_bus)
- dbus.service.Object.__init__(self, self._session_bus, "/tutorius/Probe")
+ self._name2 = dbus.service.BusName(self._activity_name, self._session_bus)
+ dbus.service.Object.__init__(self, self._session_bus, "/tutorius/Probe/"+str(self._unique_id))
# Add the dictionary we will use to store which actions and events
# are known
self._installedActions = {}
self._subscribedEvents = {}
+ LOGGER.debug("TProbe :: registering '%s' with unique_id '%s'", self._activity_name, activity.get_id())
+ self._service_proxy.register_probe(self._activity_name, self._unique_id)
+
+
+
def start(self):
"""
Optional method to call if the probe is not inserted into an
existing activity. Starts a gobject mainloop
"""
mainloop = gobject.MainLoop()
- print "Starting Probe for " + self._activity_name
mainloop.run()
+ def stop(self):
+ """
+ Clean up the probe when finished. Should be called just
+ before a process ends
+ """
+ from .service import ServiceProxy
+ LOGGER.debug("TProbe :: unregistering '%s' with unique_id '%s'", self._activity_name, self._unique_id)
+ ServiceProxy().unregister_probe(self._unique_id)
+
@dbus.service.method("org.tutorius.ProbeInterface",
in_signature='s', out_signature='')
def registered(self, service):
@@ -234,16 +254,17 @@ class ProbeProxy:
It provides an object interface to the TProbe, which requires pickled
strings, across a DBus communication.
"""
- def __init__(self, activityName):
+ def __init__(self, activityName, unique_id):
"""
Constructor
- @param activityName unique activity id. Must be a valid dbus bus name.
+ @param activityName generic activity name. Must be a valid dbus bus name.
+ @param unique_id unique id specific to an instance of an activity
"""
LOGGER.debug("ProbeProxy :: Creating ProbeProxy for %s (%d)", activityName, os.getpid())
LOGGER.debug("ProbeProxy :: Current gobject context: %s", str(gobject.main_context_default()))
LOGGER.debug("ProbeProxy :: Current gobject depth: %s", str(gobject.main_depth()))
bus = dbus.SessionBus()
- self._object = bus.get_object(activityName, "/tutorius/Probe")
+ self._object = bus.get_object(activityName, "/tutorius/Probe/"+str(unique_id))
self._probe = dbus.Interface(self._object, "org.tutorius.ProbeInterface")
self._actions = {}
@@ -397,7 +418,7 @@ class ProbeProxy:
return_cb=save_args(self.__clear_event, address),
block=block)
else:
- LOGGER.debug("ProbeProxy :: unsubsribe address %s failed : not registered", address)
+ LOGGER.debug("ProbeProxy :: unsubscribe address %s failed : not registered", address)
def detach(self, block=False):
"""
@@ -418,16 +439,22 @@ class ProbeManager(object):
For now, it only handles one at a time, though.
Actually it doesn't do much at all. But it keeps your encapsulation happy
"""
+ _LOGGER = logging.getLogger("sugar.tutorius.ProbeManager")
+
def __init__(self, proxy_class=ProbeProxy):
"""Constructor
@param proxy_class Class to use for creating Proxies to activities.
The class should support the same interface as ProbeProxy. Exists
to make this class unit-testable by replacing the Proxy with a mock
"""
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
self._ProxyClass = proxy_class
self._probes = {}
self._current_activity = None
+ ProbeManager._LOGGER.debug("__init__()")
+
def setCurrentActivity(self, activity_id):
if not activity_id in self._probes:
raise RuntimeError("Activity not attached")
@@ -437,23 +464,6 @@ class ProbeManager(object):
return self._current_activity
currentActivity = property(fget=getCurrentActivity, fset=setCurrentActivity)
- def attach(self, activity_id):
- if activity_id in self._probes:
- raise RuntimeWarning("Activity already attached")
-
- self._probes[activity_id] = self._ProxyClass(activity_id)
- #TODO what do we do with this? Raise something?
- if self._probes[activity_id].isAlive():
- print "Alive!"
- else:
- print "FAil!"
-
- def detach(self, activity_id):
- if activity_id in self._probes:
- probe = self._probes.pop(activity_id)
- probe.detach()
- if self._current_activity == activity_id:
- self._current_activity = None
def install(self, action, block=False):
"""
@@ -463,7 +473,7 @@ class ProbeManager(object):
@return None
"""
if self.currentActivity:
- return self._probes[self.currentActivity].install(action, block)
+ return self._first_proxy(self.currentActivity).install(action, block)
else:
raise RuntimeWarning("No activity attached")
@@ -476,7 +486,7 @@ class ProbeManager(object):
@return None
"""
if self.currentActivity:
- return self._probes[self.currentActivity].update(action, newaction, block)
+ return self._first_proxy(self.currentActivity).update(action, newaction, block)
else:
raise RuntimeWarning("No activity attached")
@@ -487,7 +497,7 @@ class ProbeManager(object):
@param block Force a synchroneous dbus call if True
"""
if self.currentActivity:
- return self._probes[self.currentActivity].uninstall(action, block)
+ return self._first_proxy(self.currentActivity).uninstall(action, block)
else:
raise RuntimeWarning("No activity attached")
@@ -499,7 +509,7 @@ class ProbeManager(object):
@return address identifier used for unsubscribing
"""
if self.currentActivity:
- return self._probes[self.currentActivity].subscribe(event, callback)
+ return self._first_proxy(self.currentActivity).subscribe(event, callback)
else:
raise RuntimeWarning("No activity attached")
@@ -510,7 +520,67 @@ class ProbeManager(object):
@return None
"""
if self.currentActivity:
- return self._probes[self.currentActivity].unsubscribe(address)
+ return self._first_proxy(self.currentActivity).unsubscribe(address)
else:
raise RuntimeWarning("No activity attached")
+ def register_probe(self, process_name, unique_id):
+ """ Adds a probe to the known probes, to be used by a tutorial.
+
+ A generic name for a process (like an Activity) is passed
+ so that the execution of a tutorial will use that generic
+ name. However, a unique id is also passed to differentiate
+ between many instances of the same process.
+
+ @param process_name The generic name of a process
+ @param unique_id The unique identification associated to this
+ process
+ """
+ ProbeManager._LOGGER.debug("register_probe(%s,%s)", process_name, unique_id)
+ if process_name not in self._probes:
+ self._probes[process_name] = [(unique_id,self._ProxyClass(process_name, unique_id))]
+ else:
+ self._probes[process_name].append((unique_id,self._ProxyClass(process_name, unique_id)))
+
+
+ def unregister_probe(self, unique_id):
+ """ Remove a probe from the known probes.
+
+ @param unique_id The unique identification associated to this
+ process
+ """
+ ProbeManager._LOGGER.debug("unregister_probe(%s)", unique_id)
+ for process_name, proxies in self._probes.items():
+ for id, proxy in proxies:
+ if unique_id == id:
+ proxy.detach()
+ proxies.remove((id,proxy))
+ if len(proxies) == 0:
+ self._probes.pop(process_name)
+
+ def get_registered_probes_list(self, process_name=None):
+ if process_name == None:
+ probe_list = []
+ for probes in self._probes.itervalues():
+ probe_list.extend(probes)
+ return probe_list
+ else:
+ if process_name in self._probes:
+ return self._probes[process_name]
+ else:
+ return []
+
+
+
+ def _first_proxy(self, process_name):
+ """
+ Returns the oldest probe connected under the process_name
+ @param process_name The generic process name under which the probe
+ is connected
+ """
+ if process_name in self._probes:
+ return self._probes[process_name][0][1]
+ else:
+ raise RuntimeWarning("No activity attached under '%s'", process_name)
+
+
diff --git a/tutorius/service.py b/tutorius/service.py
index eb246a1..8694cb5 100644
--- a/tutorius/service.py
+++ b/tutorius/service.py
@@ -2,6 +2,9 @@ import dbus
from .engine import Engine
from .dbustools import remote_call
+from .TProbe import ProbeManager
+import logging
+LOGGER = logging.getLogger("sugar.tutorius.service")
_DBUS_SERVICE = "org.tutorius.Service"
_DBUS_PATH = "/org/tutorius/Service"
@@ -19,11 +22,13 @@ class Service(dbus.service.Object):
self._engine = None
+ self._probeMgr = ProbeManager()
+
def start(self):
""" Start the service itself
"""
# For the moment there is nothing to do
- pass
+ LOGGER.debug("Service.start()")
@dbus.service.method(_DBUS_SERVICE_IFACE,
@@ -50,6 +55,35 @@ class Service(dbus.service.Object):
"""
self._engine.pause()
+ @dbus.service.method(_DBUS_SERVICE_IFACE,
+ in_signature="ss", out_signature="")
+ def register_probe(self, process_name, unique_id):
+ """ Adds a probe to the known probes, to be used by a tutorial.
+
+ A generic name for a process (like an Activity) is passed
+ so that the execution of a tutorial will use that generic
+ name. However, a unique id is also passed to differentiate
+ between many instances of the same process.
+
+ @param process_name The generic name of a process
+ @param unique_id The unique identification associated to this
+ process
+ """
+ LOGGER.debug("Service.register_probe(%s,%s)", process_name, unique_id)
+ self._probeMgr.register_probe(process_name, unique_id)
+
+ @dbus.service.method(_DBUS_SERVICE_IFACE,
+ in_signature="s", out_signature="")
+ def unregister_probe(self, unique_id):
+ """ Remove a probe from the known probes.
+
+ @param process_name The generic name of a process
+ @param unique_id The unique identification associated to this
+ process
+ """
+ LOGGER.debug("Service.unregister_probe(%s)", unique_id)
+ self._probeMgr.unregister_probe(unique_id)
+
class ServiceProxy:
""" Proxy to connect to the Service object, abstracting the DBus interface"""
@@ -74,6 +108,33 @@ class ServiceProxy:
"""
remote_call(self._service.pause, (), block=False)
+ def register_probe(self, process_name, unique_id):
+ """ Adds a probe to the known probes, to be used by a tutorial.
+
+ A generic name for a process (like an Activity) is passed
+ so that the execution of a tutorial will use that generic
+ name. However, a unique id is also passed to differentiate
+ between many instances of the same process.
+
+ @param process_name The generic name of a process
+ @param unique_id The unique identification associated to this
+ process
+ """
+ remote_call(self._service.register_probe, (process_name,unique_id), block=False)
+
+ def unregister_probe(self, unique_id):
+ """ Remove a probe from the known probes.
+
+ @param process_name The generic name of a process
+ @param unique_id The unique identification associated to this
+ process
+ """
+ # We make it synchronous because otherwise on closing,
+ # activities kill the dbus session bus too fast for the
+ # asynchronous call to be completed
+ self._service.unregister_probe(unique_id)
+
+
if __name__ == "__main__":
import dbus.mainloop.glib
import gobject