Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/actiontests.py206
-rw-r--r--tests/bundlertests.py65
-rw-r--r--tests/constraintstests.py233
-rw-r--r--tests/coretests.py597
-rw-r--r--tests/filterstests.py201
-rw-r--r--tests/graphics/common.py55
-rw-r--r--tests/graphics/hipposcalability.py50
-rw-r--r--tests/graphics/iconcache.py69
-rw-r--r--tests/graphics/iconwidget.py87
-rw-r--r--tests/graphics/ticket2855.py59
-rw-r--r--tests/graphics/ticket2999.py38
-rw-r--r--tests/graphics/ticket3000.py48
-rw-r--r--tests/graphics/toolbarpalettes.py65
-rw-r--r--tests/graphics/tray.py82
-rw-r--r--tests/gtkutilstests.py211
-rw-r--r--tests/lib/runall.py28
-rw-r--r--tests/lib/test_mime.py81
-rw-r--r--tests/linear_creatortests.py79
-rw-r--r--tests/overlaytests.py119
-rw-r--r--tests/propertiestests.py402
-rwxr-xr-xtests/run-tests.py74
-rw-r--r--tests/serializertests.py197
-rw-r--r--tests/servicestests.py53
-rw-r--r--tests/uamtests.py61
24 files changed, 2498 insertions, 662 deletions
diff --git a/tests/actiontests.py b/tests/actiontests.py
new file mode 100644
index 0000000..4e126b3
--- /dev/null
+++ b/tests/actiontests.py
@@ -0,0 +1,206 @@
+# Copyright (C) 2009, Tutorius.org
+# Copyright (C) 2009, Michael Janelle-Montcalm <michael.jmontcalm@gmail.com>
+# Copyright (C) 2009, Vincent Vinet <vince.vinet@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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
+"""
+Action tests
+
+The behavior of the actions must be tested here.
+"""
+
+import unittest
+import gtk
+
+from sugar.tutorius import addon
+from sugar.tutorius.actions import *
+from sugar.tutorius.services import ObjectStore
+
+test_props = {"prop_a":8, "prop_b":3, "prop_c":"Hi"}
+
+class PropertyAction(Action):
+ prop_a = TIntProperty(test_props["prop_a"])
+ prop_b = TIntProperty(test_props["prop_b"])
+ prop_c = TStringProperty(test_props["prop_c"])
+ def __init__(self, na):
+ Action.__init__(self)
+
+def has_function(obj, function_name):
+ """
+ Checks whether the object has a function by that name.
+ """
+ if hasattr(obj, function_name) and hasattr(obj.__getattribute__(function_name), "__call__"):
+ return True
+ return False
+
+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'"
+
+ for prop_name in act.get_properties():
+ 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])
+
+ def test_properties(self):
+ assert self.dial.message == "Message text", "Wrong start value for the message"
+
+ assert self.dial.position == [200, 300], "Wrong start value for the position"
+
+class BubbleMessageTest(unittest.TestCase):
+ def setUp(self):
+ self.bubble = addon.create('BubbleMessage', message="Message text", pos=[200, 300], tailpos=[-15, -25])
+
+ def test_properties(self):
+ props = self.bubble.get_properties()
+
+ assert "message" in props, 'No message property of BubbleMessage'
+
+ assert "position" in props, 'No position property in BubbleMessage'
+
+ assert "tail_pos" in props, 'No tail position property in BubbleMessage'
+
+
+class CountAction(Action):
+ """
+ This action counts how many times it's do and undo methods get called
+ """
+ def __init__(self):
+ Action.__init__(self)
+ self.do_count = 0
+ self.undo_count = 0
+
+ def do(self):
+ self.do_count += 1
+
+ def undo(self):
+ self.undo_count += 1
+
+
+class BaseActionTests(unittest.TestCase):
+ def test_do_unimplemented(self):
+ act = Action()
+ try:
+ act.do()
+ assert False, "do() should trigger a NotImplemented"
+ except NotImplementedError:
+ assert True, "do() should trigger a NotImplemented"
+
+ def test_undo(self):
+ act = Action()
+ act.undo()
+ assert True, "undo() should never fail on the base action"
+
+
+class OnceWrapperTests(unittest.TestCase):
+ def test_onceaction_toggle(self):
+ """
+ Validate that the OnceWrapper wrapper works properly using the
+ CountAction
+ """
+ act = CountAction()
+ wrap = OnceWrapper(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__()"
+
+ wrap.undo()
+
+ assert act.undo_count == 0, "undo() should not be called if do() has not been called"
+
+ wrap.do()
+ assert act.do_count == 1, "do() should have been called once"
+
+ wrap.do()
+ assert act.do_count == 1, "do() should have been called only once"
+
+ wrap.undo()
+ assert act.undo_count == 1, "undo() should have been called once"
+
+ wrap.undo()
+ assert act.undo_count == 1, "undo() should have been called only once"
+
+class ChainTester(Action):
+ def __init__(self, witness):
+ Action.__init__(self)
+ self._witness = witness
+
+ def do(self, **kwargs):
+ self._witness.append([self,"do"])
+
+ def undo(self):
+ self._witness.append([self,"undo"])
+
+class ChainActionTest(unittest.TestCase):
+ """Tester for ChainAction"""
+ def test_empty(self):
+ """If the expected empty behavior (do nothing) changes
+ and starts throwing exceptions, this will flag it"""
+ a = ChainAction()
+ a.do()
+ a.undo()
+
+ def test_order(self):
+ witness = []
+ first = ChainTester(witness)
+ second = ChainTester(witness)
+
+ c = ChainAction(first, second)
+ assert witness == [], "Actions should not be triggered on init"""
+ c.do()
+
+ assert witness[0][0] is first, "First triggered action must be 'first'"
+ assert witness[0][1] is "do", "Action do() should be triggered"
+
+ assert witness[1][0] is second, "second triggered action must be 'second'"
+ assert witness[1][1] is "do", "Action do() should be triggered"
+
+ assert len(witness) is 2, "Two actions should give 2 do's"
+
+ #empty the witness list
+ while len(witness):
+ rm = witness.pop()
+
+ c.undo()
+ assert witness[1][0] is first, "second triggered action must be 'first'"
+ assert witness[1][1] is "undo", "Action undo() should be triggered"
+
+ assert witness[0][0] is second, "first triggered action must be 'second'"
+ assert witness[0][1] is "undo", "Action undo() should be triggered"
+
+ assert len(witness) is 2, "Two actions should give 2 undo's"
+
+class DisableWidgetActionTests(unittest.TestCase):
+ def test_disable(self):
+ btn = gtk.Button()
+ ObjectStore().activity = btn
+ btn.set_sensitive(True)
+
+ assert btn.props.sensitive is True, "Callback should have been called"
+
+ act = DisableWidgetAction("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"
+ act.undo()
+ assert btn.props.sensitive is True, "Callback should have been called again"
+
+if __name__ == "__main__":
+ unittest.main()
+
diff --git a/tests/bundlertests.py b/tests/bundlertests.py
new file mode 100644
index 0000000..8da2310
--- /dev/null
+++ b/tests/bundlertests.py
@@ -0,0 +1,65 @@
+# Copyright (C) 2009, Tutorius.org
+# Copyright (C) 2009, Charles-Etienne Carriere <iso.swiffer@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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
+"""
+Bundler tests
+
+This module contains all the tests for the storage mecanisms for tutorials
+This mean testing savins and loading tutorial, .ini file management and
+adding ressources to tutorial
+"""
+
+import unittest
+import os
+import uuid
+
+from sugar.tutorius import bundler
+
+class TutorialBundlerTests(unittest.TestCase):
+
+ def setUp(self):
+
+ #generate a test GUID
+ self.test_guid = uuid.uuid1()
+ self.guid_path = os.path.join(bundler._get_store_root(),str(self.test_guid))
+ os.mkdir(self.guid_path)
+
+ self.ini_file = os.path.join(self.guid_path, "meta.ini")
+
+ f = open(self.ini_file,'w')
+ f.write("[GENERAL_METADATA]")
+ f.write(os.linesep)
+ f.write("GUID:")
+ f.write(str(self.test_guid))
+ f.close()
+
+ def tearDown(self):
+ os.remove(self.ini_file)
+ os.rmdir(self.guid_path)
+
+ def test_add_ressource(self):
+ bund = bundler.TutorialBundler(self.test_guid)
+
+ temp_file = open("test.txt",'w')
+ temp_file.write('test')
+ temp_file.close()
+
+ bund.add_resource("test.txt")
+
+ assert os.path.exists(os.path.join(self.guid_path,"test.txt")), "add_ressource did not create the file"
+
+if __name__ == "__main__":
+ unittest.main() \ No newline at end of file
diff --git a/tests/constraintstests.py b/tests/constraintstests.py
new file mode 100644
index 0000000..b7b0a47
--- /dev/null
+++ b/tests/constraintstests.py
@@ -0,0 +1,233 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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
+
+import unittest
+
+from sugar.tutorius.constraints import *
+
+class ConstraintTest(unittest.TestCase):
+ def test_base_class(self):
+ cons = Constraint()
+ try:
+ cons.validate(1)
+ assert False, "Base class should throw an assertion"
+ except NotImplementedError:
+ pass
+
+class ValueConstraintTest(unittest.TestCase):
+ def test_limit_set(self):
+ cons = ValueConstraint(12)
+
+ assert cons.limit == 12
+
+class UpperLimitConstraintTest(unittest.TestCase):
+ def test_empty_constraint(self):
+ cons = UpperLimitConstraint(None)
+ try:
+ cons.validate(20)
+ except UpperLimitConstraintError:
+ assert False, "Empty contraint should not raise an exception"
+
+ def test_validate(self):
+ cons = UpperLimitConstraint(10)
+
+ try:
+ cons.validate(20)
+ assert False, "Validation of UpperLimit(10) on 20 should raise an exception"
+ except UpperLimitConstraintError:
+ pass
+
+ try:
+ cons.validate(5)
+ except UpperLimitConstraintError:
+ assert True, "Validation of UpperLimit(10) on 5 should not raise an exception"
+
+class LowerLimitConstraintTest(unittest.TestCase):
+ def test_empty_constraint(self):
+ cons = LowerLimitConstraint(None)
+ try:
+ cons.validate(20)
+ except LowerLimitConstraintError:
+ assert False, "Empty contraint should not raise an exception"
+
+ def test_validate(self):
+ cons = LowerLimitConstraint(10)
+
+ try:
+ cons.validate(5)
+ assert False, "Validation of LowerLimit(10) on 5 should raise an exception"
+ except LowerLimitConstraintError:
+ pass
+
+ try:
+ cons.validate(20)
+ except LowerLimitConstraintError:
+ assert True, "Validation of LowerLimit(10) on 20 should not raise an exception"
+
+class MaxSizeConstraintTest(unittest.TestCase):
+ def test_empty_constraint(self):
+ cons = MaxSizeConstraint(None)
+ try:
+ cons.validate(20)
+ except MaxSizeConstraintError:
+ assert False, "Empty contraint should not raise an exception"
+
+ def test_validate(self):
+ cons = MaxSizeConstraint(10)
+
+ try:
+ cons.validate(range(0, 20))
+ assert False, "Validation of MaxSizeConstraint(10) on list of length 20 should raise an exception"
+ except MaxSizeConstraintError:
+ pass
+
+ try:
+ cons.validate(range(0,5))
+ except MaxSizeConstraintError:
+ assert True, "Validation of MaxSizeConstraint(10) on list of length 5 should not raise an exception"
+
+class MinSizeConstraintTest(unittest.TestCase):
+ def test_empty_constraint(self):
+ cons = MinSizeConstraint(None)
+ try:
+ cons.validate(20)
+ except MinSizeConstraintError:
+ assert False, "Empty contraint should not raise an exception"
+
+ def test_validate(self):
+ cons = MinSizeConstraint(10)
+
+ try:
+ cons.validate(range(0, 5))
+ assert False, "Validation of MinSizeConstraint(10) on list of length 20 should raise an exception"
+ except MinSizeConstraintError:
+ pass
+
+ try:
+ cons.validate(range(0,20))
+ except MinSizeConstraintError:
+ assert True, "Validation of MinSizeConstraint(10) on list of length 5 should not raise an exception"
+
+class ColorConstraintTest(unittest.TestCase):
+ def test_validate(self):
+ cons = ColorConstraint()
+
+ try:
+ cons.validate([0, 0])
+ assert False, "ColorConstraint on list of length 2 should raise an exception"
+ except ColorArraySizeError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([0, 0, "str"])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorTypeError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([0, "str", 0])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorTypeError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate(["str", 0, 0])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorTypeError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([1, 2, 300])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorValueError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([1, -100, 30])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorValueError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([999999, 2, 300])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorValueError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([12, 23, 34])
+ except LowerLimitConstraintError:
+ assert True, "ColorConstraint on expected input should not raise an exception"
+
+class BooleanConstraintTest(unittest.TestCase):
+ def test_validate(self):
+ cons = BooleanConstraint()
+
+ cons.validate(True)
+ cons.validate(False)
+
+ try:
+ cons.validate(18)
+ assert False, "Setting integer on constraint should raise an error"
+ except BooleanConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type raised when setting wrong type"
+
+class EnumConstraintTest(unittest.TestCase):
+ def test_validate(self):
+ cons = EnumConstraint([1,2,3,7,8,9, "ex"])
+
+ cons.validate(8)
+
+ cons.validate("ex")
+
+ try:
+ cons.validate(4)
+ assert False, "There should be an exception on setting a value out of the enum"
+ except EnumConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type thrown"
+
+class FileConstraintTest(unittest.TestCase):
+ def test_validate(self):
+ cons = FileConstraint()
+
+ cons.validate("run-tests.py")
+
+ try:
+ cons.validate("unknown/file.py")
+ assert False, "Non-existing file check should throw an exception"
+ except FileConstraintError:
+ pass
+
+if __name__ == "__main__":
+ unittest.main() \ No newline at end of file
diff --git a/tests/coretests.py b/tests/coretests.py
new file mode 100644
index 0000000..eadea01
--- /dev/null
+++ b/tests/coretests.py
@@ -0,0 +1,597 @@
+# Copyright (C) 2009, Tutorius.org
+# Copyright (C) 2009, Michael Janelle-Montcalm <michael.jmontcalm@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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
+"""
+Core Tests
+
+This module contains all the tests that pertain to the usage of the Tutorius
+Core. This means that the Event Filters, the Finite State Machine and all the
+related elements and interfaces are tested here.
+
+Usage of actions and event filters is tested, but not the concrete actions
+and event filters. Those are in their separate test module
+
+"""
+
+import unittest
+
+import logging
+from sugar.tutorius.actions import Action, OnceWrapper, ClickAction, TypeTextAction
+from sugar.tutorius.core import *
+from sugar.tutorius.filters import *
+
+
+from actiontests import CountAction
+
+# Helper classes to help testing
+class SimpleTutorial(Tutorial):
+ """
+ Fake tutorial
+ """
+ def __init__(self, start_name="INIT"):
+ #Tutorial.__init__(self, "Simple Tutorial", None)
+ self.current_state_name = start_name
+ self.activity = "TODO : This should be an activity"
+
+ def set_state(self, name):
+ self.current_state_name = name
+
+class TutorialWithFSM(Tutorial):
+ """
+ Fake tutorial, but associated with a FSM.
+ """
+ def __init__(self, start_name="INIT", fsm=None):
+ Tutorial.__init__(self, start_name, fsm)
+ self.activity = activity.Activity()
+
+class TrueWhileActiveAction(Action):
+ """
+ This action's active member is set to True after a do and to False after
+ an undo.
+
+ Used to verify that a State correctly triggers the do and undo actions.
+ """
+ def __init__(self):
+ Action.__init__(self)
+ self.active = False
+
+ def do(self):
+ self.active = True
+
+ 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):
+ """
+ This class has to test the State interface as well as the expected
+ functionality.
+ """
+
+ def test_action_toggle(self):
+ """
+ Validate that the actions are properly done on setup and undone on
+ teardown.
+
+ Pretty awesome.
+ """
+ act = TrueWhileActiveAction()
+
+ state = State("action_test", action_list=[act])
+
+ assert act.active == False, "Action is not initialized properly"
+
+ state.setup()
+
+ assert act.active == True, "Action was not triggered properly"
+
+ state.teardown()
+
+ assert act.active == False, "Action was not undone properly"
+
+ def test_event_filter(self):
+ """
+ Tests the fact that the event filters are correctly installed on setup
+ and uninstalled on teardown.
+ """
+ event_filter = TriggerEventFilter("second_state")
+
+ state = State("event_test", event_filter_list=[event_filter])
+ state.set_tutorial(SimpleTutorial())
+
+ assert event_filter.toggle_on_callback == False, "Wrong init of event_filter"
+ assert event_filter._callback == None, "Event filter has a registered callback before installing handlers"
+
+ state.setup()
+
+ assert event_filter._callback != None, "Event filter did not register callback!"
+
+ # 'Trigger' the event - This is more like a EventFilter test.
+ event_filter.do_callback()
+
+ assert event_filter.toggle_on_callback == True, "Event filter did not execute callback"
+
+ state.teardown()
+
+ assert event_filter._callback == None, "Event filter did not remove callback properly"
+
+ def test_warning_set_tutorial_twice(self):
+ """
+ Calls set_tutorial twice and expects a warning on the second.
+ """
+ state = State("start_state")
+ tut = SimpleTutorial("First")
+ tut2 = SimpleTutorial("Second")
+
+ state.set_tutorial(tut)
+
+ try:
+ state.set_tutorial(tut2)
+ assert False, "No RuntimeWarning was raised on second set_tutorial"
+ except :
+ pass
+
+ def test_add_action(self):
+ """
+ Tests on manipulating the actions inside a state.
+ """
+ state = State("INIT")
+
+ act1 = CountAction()
+ act2 = CountAction()
+ act3 = CountAction()
+
+ # Try to add the actions
+ assert state.add_action(act1), "Could not add the first action"
+ 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,\
+ "The actions were not properly inserted in the state"
+
+ # Clear the list
+ state.clear_actions()
+
+ # Make sure the list of actions is empty now
+ assert len(state.get_action_list()) == 0, "Clearing of actions failed"
+
+ def test_add_event_filter(self):
+ state = State("INIT")
+
+ event1 = TriggerEventFilter("s")
+ event2 = TriggerEventFilter("t")
+ event3 = TriggerEventFilter("r")
+
+ # Insert the event filters
+ assert state.add_event_filter(event1), "Could not add event filter 1"
+ assert state.add_event_filter(event2), "Could not add event filter 2"
+ assert state.add_event_filter(event3), "Could not add event filter 3"
+
+ # Make sure we cannot insert an event twice
+ assert state.add_event_filter(event1) == False, "Could add twice the event filter"
+
+ # Get the list of event filters
+ event_filters = state.get_event_filter_list()
+
+ assert event1 in event_filters and event2 in event_filters and event3 in event_filters, \
+ "The event filters were not all added inside the state"
+
+ # Clear the list
+ state.clear_event_filters()
+
+ assert len(state.get_event_filter_list()) == 0, \
+ "Could not clear the event filter list properly"
+
+class FSMTest(unittest.TestCase):
+ """
+ This class needs to text the interface and functionality of the Finite
+ State Machine.
+ """
+
+ def test_sample_usage(self):
+ act_init = TrueWhileActiveAction()
+ act_second = TrueWhileActiveAction()
+
+ event_init = FakeEventFilter("SECOND")
+
+ content = {
+ "INIT": State("INIT", action_list=[act_init],event_filter_list=[event_init]),
+ "SECOND": State("SECOND", action_list=[act_second])
+ }
+
+ fsm = FiniteStateMachine("SampleUsage", state_dict=content)
+
+ assert fsm is not None, "Unable to create FSM"
+
+ tut = Tutorial("SampleUsageTutorial", fsm)
+
+ tut.attach(None)
+ event_init.set_tutorial(tut)
+
+ assert fsm.current_state.name == "INIT", "Unable to set state to initial state"
+
+ assert act_init.active, "FSM did not call the state's action DO properly"
+
+ # Trigger the event of the INIT state
+ event_init.do_callback()
+
+ assert act_init.active == False, "FSM did not teardown INIT properly"
+
+ assert fsm.current_state.name == "SECOND", "FSM did not switch to SECOND state"
+
+ assert act_second.active == True, "FSM did not setup SECOND properly"
+
+ tut.detach()
+
+ 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.
+ """
+ st1 = State("FakeState")
+
+ fsm = FiniteStateMachine("StateInsertTest")
+
+ fsm.add_state(st1)
+
+ inserted_state = fsm.get_state_by_name(st1.name)
+
+ assert inserted_state is st1, "Inserting, then fetching a state did not work"
+
+ # Make sure we cannot insert it twice
+ try :
+ fsm.add_state(st1)
+ assert False, "No error raised on addition of an already present state"
+ except KeyError:
+ pass
+
+ def test_state_find_by_name(self):
+ """
+ Tests the interface for fetching a state by name.
+ - Basic functionnality
+ - Non-existent state
+ """
+
+ st1 = State("INIT")
+
+ st2 = State("second")
+
+ fsm = FiniteStateMachine("StateFindTest")
+
+ fsm.add_state(st1)
+ fsm.add_state(st2)
+
+ # Test the fetch by name
+ fetched_st1 = fsm.get_state_by_name(st1.name)
+
+ assert fetched_st1 is st1, "Fetched state is not the same as the inserted one"
+
+ fetched_st2 = fsm.get_state_by_name(st2.name)
+
+ assert fetched_st2 is st2, "Fetched state is not the same as the inserted one"
+
+ try:
+ fsm.get_state_by_name("no such state")
+ assert False, "Did not get a KeyError on non-existing key search"
+ except KeyError:
+ pass
+ except Exception:
+ assert False, "Did not get the right error on non-existing key search"
+
+ def test_state_removal(self):
+ """
+ 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")])
+
+ fsm = FiniteStateMachine("StateRemovalTest")
+
+ fsm.add_state(st1)
+ fsm.add_state(st2)
+ fsm.add_state(st3)
+
+ # First tests - Removing a non-existing state and make sure we get a
+ # KeyError
+ try:
+ fsm.remove_state("Non-existing")
+ assert False, "Removing a non-existing state did not throw a KeyError"
+ except KeyError:
+ pass
+ except Exception:
+ assert False, "Removing a non-existing state dit not throw the right kind of exception"
+
+ # Now try removing the second state
+ fsm.remove_state("second")
+
+ # Make sure it cannot be fetched
+ try :
+ fetched_state = fsm.get_state_by_name("second")
+ assert False, "The supposedly removed state is still present in the FSM"
+ except KeyError:
+ pass
+
+ # 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"),\
+ "The link to second from INIT still exists after removal"
+
+ 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):
+ fsm = FiniteStateMachine("Set same state")
+
+ st1 = State("INIT")
+ st1.add_action(CountAction())
+
+ fsm.add_state(st1)
+
+ tut = SimpleTutorial()
+
+ fsm.set_tutorial(tut)
+
+ fsm.set_state("INIT")
+
+ assert fsm.get_state_by_name("INIT").get_action_list()[0].do_count == 1, \
+ "The action was not triggered on 'INIT'"
+
+ fsm.set_state("INIT")
+
+ do_count = fsm.get_state_by_name("INIT").get_action_list()[0].do_count
+ assert fsm.get_state_by_name("INIT").get_action_list()[0].do_count == 1, \
+ "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,\
+ "The action has been undone unappropriately, undo_count = %d"%undo_count
+
+class FSMExplorationTests(unittest.TestCase):
+ def setUp(self):
+ self.buildFSM()
+
+ def buildFSM(self):
+ """
+ Create a sample FSM to play with in the rest of the tests.
+ """
+ st1 = State("INIT")
+ st1.add_action(CountAction())
+ st1.add_event_filter(TriggerEventFilter("Second"))
+ st1.add_event_filter(TriggerEventFilter("Third"))
+
+ st2 = State("Second")
+ st2.add_action(TrueWhileActiveAction())
+ st2.add_event_filter(TriggerEventFilter("Third"))
+ st2.add_event_filter(TriggerEventFilter("Fourth"))
+
+ st3 = State("Third")
+ st3.add_action(CountAction())
+ st3.add_action(TrueWhileActiveAction())
+
+ self.fsm = FiniteStateMachine("ExplorationTestingMachine")
+ self.fsm.add_state(st1)
+ self.fsm.add_state(st2)
+ self.fsm.add_state(st3)
+
+ def validate_following_states(self, in_name, out_name_list):
+ nextStates = self.fsm.get_following_states(in_name)
+ assert list(nextStates).sort() == list(out_name_list).sort(), \
+ "The following states for %s are wrong : got %s"%\
+ (in_name, str(nextStates))
+
+ def validate_previous_states(self, in_name, out_name_list):
+ prevStates = self.fsm.get_previous_states(in_name)
+ assert list(prevStates).sort() == list(out_name_list).sort(), \
+ "The following states for %s are wrong : got %s"%\
+ (in_name, str(prevStates))
+
+ def test_get_following_states(self):
+ self.validate_following_states("INIT", ('Second', 'Third'))
+
+ self.validate_following_states("Second", ("Third", "Fourth"))
+
+ self.validate_following_states("Third", ())
+
+ def test_get_previous_states(self):
+ self.validate_previous_states("INIT", ())
+
+ self.validate_previous_states("Second", ("INIT"))
+
+ self.validate_previous_states("Third", ("INIT", "Second"))
+
+ self.validate_previous_states("Fourth", ("Second"))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/filterstests.py b/tests/filterstests.py
new file mode 100644
index 0000000..3e79bcc
--- /dev/null
+++ b/tests/filterstests.py
@@ -0,0 +1,201 @@
+# Copyright (C) 2009, Tutorius.org
+# Copyright (C) 2009, Vincent Vinet <vince.vinet@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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
+"""
+Filters Tests
+
+This module contains all the tests that pertain to the usage of the Tutorius
+Event Filters
+"""
+
+import unittest
+import time
+import gobject
+import gtk
+
+from sugar.tutorius.filters import EventFilter, TimerEvent, GtkWidgetTypeFilter
+from sugar.tutorius import addon
+from gtkutilstests import SignalCatcher
+
+class BaseEventFilterTests(unittest.TestCase):
+ """Test the behavior of the Base EventFilter class"""
+ def test_properties(self):
+ """Test EventFilter properties"""
+ e = EventFilter("NEXTSTATE")
+
+ assert e.next_state == "NEXTSTATE", "next_state should have value used in constructor"
+
+ e.next_state = "NEWSTATE"
+
+ assert e.next_state == "NEWSTATE", "next_state should have been changed by setter"
+
+
+ def test_callback(self):
+ """Test the callback mechanism"""
+ e = EventFilter("Next")
+ s = SignalCatcher()
+
+ #Trigger the do_callback, shouldn't do anything
+ e.do_callback()
+
+ #Install the handler
+ e.install_handlers(s.callback)
+
+ #Trigger the do_callback, s should receive e
+ e.do_callback()
+ assert s.data[0] is e
+
+ s.data = None
+
+ e.remove_handlers()
+
+ #Trigger callback, nothing should happen again
+ e.do_callback()
+
+ assert s.data is None
+
+
+
+
+
+class TestTimerEvent(unittest.TestCase):
+ """Tests for timer"""
+ def test_timer(self):
+ """Make sure timer gets called once, and only once"""
+ gobject.threads_init()
+ ctx = gobject.MainContext()
+ main = gobject.MainLoop(ctx)
+
+ e = TimerEvent("Next",1) #1 second should be enough :s
+ s = SignalCatcher()
+
+ e.install_handlers(s.callback)
+
+ assert s.data is None, "Callback should not have been called yet"
+
+ #process events
+ while gtk.events_pending():
+ gtk.main_iteration(block=False)
+ while ctx.pending():
+ ctx.iteration(may_block=False)
+
+ #Wait 1.4 sec
+ time.sleep(1.4)
+
+ #process events
+ while gtk.events_pending():
+ gtk.main_iteration(block=False)
+ while ctx.pending():
+ ctx.iteration(may_block=False)
+
+ assert not s.data is None, "Callback should have been called"
+
+ s.data = None
+
+ #Wait 1.4 sec
+ time.sleep(1.4)
+
+ #process events
+ while gtk.events_pending():
+ gtk.main_iteration(block=False)
+ while ctx.pending():
+ ctx.iteration(may_block=False)
+
+ assert s.data is None, "Callback should not have been called again"
+
+ def test_timer_stop(self):
+ """Make sure timer can be stopped"""
+ gobject.threads_init()
+ ctx = gobject.MainContext()
+ main = gobject.MainLoop(ctx)
+
+ e = TimerEvent("Next",1) #1 second should be enough :s
+ s = SignalCatcher()
+
+ e.install_handlers(s.callback)
+
+ assert s.data is None, "Callback should not have been called yet"
+
+ #process events
+ while gtk.events_pending():
+ gtk.main_iteration(block=False)
+ while ctx.pending():
+ ctx.iteration(may_block=False)
+
+ assert s.data is None, "Callback should not have been called yet"
+
+ #Wait 0.5 sec
+ time.sleep(0.5)
+
+ e.remove_handlers()
+
+ #Wait 0.5 sec
+ time.sleep(0.7)
+
+ #process events
+ while gtk.events_pending():
+ gtk.main_iteration(block=False)
+ while ctx.pending():
+ ctx.iteration(may_block=False)
+
+ assert s.data is None, "Callback should not have been called"
+
+ s.data = None
+
+
+class TestGtkWidgetEventFilter(unittest.TestCase):
+ """Tests for GtkWidgetEventFilter"""
+ def __init__(self,*args):
+ unittest.TestCase.__init__(self,*args)
+ self.top=None
+ self.btn1=None
+
+ def setUp(self):
+ self.top = gtk.Window()
+ self.btn1 = gtk.Button()
+ self.top.add(self.btn1)
+
+ def test_install(self):
+ h = addon.create('GtkWidgetEventFilter', "Next","0","whatever")
+ try:
+ h.install_handlers(None)
+
+ assert False, "Install handlers should have failed"
+ except TypeError:
+ assert True, "Install should have failed"
+
+ def test_button_clicks(self):
+ h = addon.create('GtkWidgetEventFilter', "Next","0.0","clicked")
+ s = SignalCatcher()
+
+ h.install_handlers(s.callback, activity=self.top)
+
+ assert s.data is None, "no callback to call yet"
+
+ self.btn1.clicked()
+ assert not s.data is None, "callback should have been called"
+ s.data = None
+
+ h.remove_handlers()
+
+ assert s.data is None, "callback must not be called again"
+
+ self.btn1.clicked()
+
+ assert s.data is None, "callback must not be called again"
+
+
+
diff --git a/tests/graphics/common.py b/tests/graphics/common.py
deleted file mode 100644
index 2f00099..0000000
--- a/tests/graphics/common.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright (C) 2007, Red Hat, Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-import gtk
-
-from sugar.graphics.toolbutton import ToolButton
-
-class Test(gtk.VBox):
- def __init__(self):
- gtk.VBox.__init__(self)
-
-class TestPalette(Test):
- def __init__(self):
- Test.__init__(self)
-
- toolbar = gtk.Toolbar()
-
- self._invoker = ToolButton('go-previous')
- toolbar.insert(self._invoker, -1)
- self._invoker.show()
-
- self.pack_start(toolbar, False)
- toolbar.show()
-
- def set_palette(self, palette):
- self._invoker.set_palette(palette)
-
-class TestRunner(object):
- def run(self, test):
- window = gtk.Window()
- window.connect("destroy", lambda w: gtk.main_quit())
- window.add(test)
- test.show()
-
- window.show()
-
-def main(test):
- runner = TestRunner()
- runner.run(test)
-
- gtk.main()
diff --git a/tests/graphics/hipposcalability.py b/tests/graphics/hipposcalability.py
deleted file mode 100644
index a5cebcc..0000000
--- a/tests/graphics/hipposcalability.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import hippo
-import gtk
-import gobject
-
-from sugar.graphics.icon import CanvasIcon
-from sugar.graphics.roundbox import CanvasRoundBox
-
-import common
-
-test = common.Test()
-
-canvas = hippo.Canvas()
-test.pack_start(canvas)
-canvas.show()
-
-scrollbars = hippo.CanvasScrollbars()
-canvas.set_root(scrollbars)
-
-box = hippo.CanvasBox(padding=10, spacing=10)
-scrollbars.set_root(box)
-
-def idle_cb():
- global countdown
-
- for i in range(0, 100):
- entry = hippo.CanvasBox(border=2, border_color=0x000000ff,
- orientation=hippo.ORIENTATION_HORIZONTAL,
- padding=10, spacing=10)
-
- for j in range(0, 3):
- icon = CanvasIcon(icon_name='go-left')
- entry.append(icon)
-
- for j in range(0, 2):
- text = hippo.CanvasText(text='Text %s %s' % (countdown, j))
- entry.append(text)
-
- box.append(entry)
-
- countdown -= 1
-
- return countdown > 0
-
-countdown = 1000
-gobject.idle_add(idle_cb)
-
-test.show()
-
-if __name__ == "__main__":
- common.main(test)
diff --git a/tests/graphics/iconcache.py b/tests/graphics/iconcache.py
deleted file mode 100644
index b03ecb6..0000000
--- a/tests/graphics/iconcache.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright (C) 2007, Red Hat, Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-"""
-Test the sugar.graphics.icon.* cache.
-"""
-
-import gtk
-
-from sugar.graphics.icon import Icon
-from sugar.graphics.xocolor import XoColor
-
-import common
-
-test = common.Test()
-
-data = [
- ['battery-000', '#FF8F00,#FF2B34'],
- ['battery-010', '#D1A3FF,#00A0FF'],
- ['battery-020', '#FF8F00,#FF2B34'],
- ['battery-030', '#00A0FF,#D1A3FF'],
- ['battery-040', '#AC32FF,#FF2B34'],
- ['battery-050', '#D1A3FF,#00A0FF'],
- ['battery-060', '#AC32FF,#FF2B34'],
- ['battery-070', '#00A0FF,#D1A3FF'],
- ['battery-080', '#FF8F00,#FF2B34'],
- ['battery-090', '#D1A3FF,#00A0FF'],
- ['battery-100', '#AC32FF,#FF2B34']]
-
-def _button_activated_cb(button):
- import random
-
- global data
- random.shuffle(data)
-
- for i in range(0, len(test.get_children()) - 1):
- test.get_children()[i].props.icon_name = data[i][0]
- test.get_children()[i].props.xo_color = XoColor(data[i][1])
-
-for d in data:
- icon = Icon(icon_name=d[0],
- icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR,
- xo_color=XoColor(d[1]))
- test.pack_start(icon)
- icon.show()
-
-button = gtk.Button("mec mac")
-test.pack_start(button)
-button.connect('activate', _button_activated_cb)
-button.show()
-
-test.show()
-
-if __name__ == "__main__":
- common.main(test)
diff --git a/tests/graphics/iconwidget.py b/tests/graphics/iconwidget.py
deleted file mode 100644
index cacf501..0000000
--- a/tests/graphics/iconwidget.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# Copyright (C) 2007, Red Hat, Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-"""
-Test the sugar.graphics.icon.Icon widget.
-"""
-
-import gtk
-
-from sugar.graphics.icon import Icon
-from sugar.graphics.xocolor import XoColor
-
-import common
-
-test = common.Test()
-
-hbox = gtk.HBox()
-test.pack_start(hbox)
-sensitive_box = gtk.VBox()
-insensitive_box = gtk.VBox()
-
-hbox.pack_start(sensitive_box)
-hbox.pack_start(insensitive_box)
-hbox.show_all()
-
-
-def create_icon_widgets(box, sensitive=True):
- icon = Icon(icon_name='go-previous')
- icon.props.icon_size = gtk.ICON_SIZE_LARGE_TOOLBAR
- box.pack_start(icon)
- icon.set_sensitive(sensitive)
- icon.show()
-
- icon = Icon(icon_name='computer-xo',
- icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR,
- xo_color=XoColor())
- box.pack_start(icon)
- icon.set_sensitive(sensitive)
- icon.show()
-
- icon = Icon(icon_name='battery-000',
- icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR,
- badge_name='emblem-busy')
- box.pack_start(icon)
- icon.set_sensitive(sensitive)
- icon.show()
-
- icon = Icon(icon_name='gtk-new',
- icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR,
- badge_name='gtk-cancel')
- box.pack_start(icon)
- icon.set_sensitive(sensitive)
- icon.show()
-
-
-create_icon_widgets(sensitive_box, True)
-create_icon_widgets(insensitive_box, False)
-
-test.show()
-
-# This can be used to test for leaks by setting the LRU cache size
-# in icon.py to 1.
-#def idle_cb():
-# import gc
-# gc.collect()
-# test.queue_draw()
-# return True
-#
-#import gobject
-#gobject.idle_add(idle_cb)
-
-if __name__ == "__main__":
- common.main(test)
diff --git a/tests/graphics/ticket2855.py b/tests/graphics/ticket2855.py
deleted file mode 100644
index cc4b3c0..0000000
--- a/tests/graphics/ticket2855.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Copyright (C) 2007, Red Hat, Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-"""
-Test the style of toggle and radio buttons inside a palette. The buttons
-contains only an icon and should be rendered similarly to the toolbar
-controls. Ticket #2855.
-"""
-
-import gtk
-
-from sugar.graphics.palette import Palette
-from sugar.graphics.icon import Icon
-
-import common
-
-test = common.TestPalette()
-
-palette = Palette('Test radio and toggle')
-test.set_palette(palette)
-
-box = gtk.HBox()
-
-toggle = gtk.ToggleButton()
-
-icon = Icon(icon_name='go-previous', icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
-toggle.set_image(icon)
-
-box.pack_start(toggle, False)
-toggle.show()
-
-radio = gtk.RadioButton()
-
-icon = Icon(icon_name='go-next', icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
-radio.set_image(icon)
-
-radio.set_mode(False)
-box.pack_start(radio, False)
-radio.show()
-
-palette.set_content(box)
-box.show()
-
-if __name__ == "__main__":
- common.main(test)
diff --git a/tests/graphics/ticket2999.py b/tests/graphics/ticket2999.py
deleted file mode 100644
index a7b92d5..0000000
--- a/tests/graphics/ticket2999.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (C) 2007, One Laptop Per Child
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-"""
-Spec in ticket #2999.
-"""
-
-import gtk
-
-from sugar.graphics.palette import Palette
-from sugar.graphics.icon import Icon
-
-import common
-
-test = common.Test()
-test.set_border_width(60)
-
-text_view = gtk.TextView()
-text_view.props.buffer.props.text = 'Blah blah blah, blah blah blah.'
-test.pack_start(text_view)
-text_view.show()
-
-if __name__ == "__main__":
- common.main(test)
diff --git a/tests/graphics/ticket3000.py b/tests/graphics/ticket3000.py
deleted file mode 100644
index c28b2cb..0000000
--- a/tests/graphics/ticket3000.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2007, One Laptop Per Child
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-"""
-Spec in ticket #3000.
-"""
-
-import gtk
-
-from sugar.graphics.toolbutton import ToolButton
-
-import common
-
-test = common.Test()
-
-toolbar = gtk.Toolbar()
-test.pack_start(toolbar, False)
-toolbar.show()
-
-button = ToolButton('go-previous')
-toolbar.insert(button, -1)
-button.show()
-
-separator = gtk.SeparatorToolItem()
-toolbar.add(separator)
-separator.show()
-
-button = ToolButton('go-next')
-toolbar.insert(button, -1)
-button.show()
-
-
-if __name__ == "__main__":
- common.main(test)
diff --git a/tests/graphics/toolbarpalettes.py b/tests/graphics/toolbarpalettes.py
deleted file mode 100644
index 608ef57..0000000
--- a/tests/graphics/toolbarpalettes.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright (C) 2007, Red Hat, Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-"""
-Test palette positioning for toolbar and tray.
-"""
-
-import gtk
-
-from sugar.graphics.tray import HTray, TrayButton
-from sugar.graphics.toolbutton import ToolButton
-
-import common
-
-test = common.Test()
-
-vbox = gtk.VBox()
-
-theme_icons = gtk.icon_theme_get_default().list_icons()
-
-toolbar = gtk.Toolbar()
-vbox.pack_start(toolbar, False)
-toolbar.show()
-
-for i in range(0, 5):
- button = ToolButton(icon_name=theme_icons[i])
- button.set_tooltip('Icon %d' % i)
- toolbar.insert(button, -1)
- button.show()
-
-content = gtk.Label()
-vbox.pack_start(content)
-content.show()
-
-tray = HTray()
-vbox.pack_start(tray, False)
-tray.show()
-
-for i in range(0, 30):
- button = TrayButton(icon_name=theme_icons[i])
- button.set_tooltip('Icon %d' % i)
- tray.add_item(button)
- button.show()
-
-test.pack_start(vbox)
-vbox.show()
-
-test.show()
-
-if __name__ == "__main__":
- common.main(test)
diff --git a/tests/graphics/tray.py b/tests/graphics/tray.py
deleted file mode 100644
index f589f4e..0000000
--- a/tests/graphics/tray.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# Copyright (C) 2007, Red Hat, Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-"""
-Test the sugar.graphics.icon.Icon widget.
-"""
-
-import gtk
-
-from sugar.graphics.tray import HTray, VTray
-from sugar.graphics.tray import TrayButton, TrayIcon
-
-import common
-
-test = common.Test()
-
-vbox = gtk.VBox()
-
-tray = HTray()
-vbox.pack_start(tray, False)
-tray.show()
-
-theme_icons = gtk.icon_theme_get_default().list_icons()
-
-for i in range(0, 100):
- button = TrayButton(icon_name=theme_icons[i])
- tray.add_item(button)
- button.show()
-
-tray = HTray()
-vbox.pack_start(tray, False)
-tray.show()
-
-for i in range(0, 10):
- icon = TrayIcon(icon_name=theme_icons[i])
- tray.add_item(icon)
- icon.show()
-
-hbox = gtk.HBox()
-
-tray = VTray()
-hbox.pack_start(tray, False)
-tray.show()
-
-for i in range(0, 100):
- button = TrayButton(icon_name=theme_icons[i])
- tray.add_item(button)
- button.show()
-
-tray = VTray()
-hbox.pack_start(tray, False)
-tray.show()
-
-for i in range(0, 4):
- button = TrayButton(icon_name=theme_icons[i])
- tray.add_item(button)
- button.show()
-
-vbox.pack_start(hbox)
-hbox.show()
-
-test.pack_start(vbox)
-vbox.show()
-
-test.show()
-
-if __name__ == "__main__":
- common.main(test)
diff --git a/tests/gtkutilstests.py b/tests/gtkutilstests.py
new file mode 100644
index 0000000..41634ae
--- /dev/null
+++ b/tests/gtkutilstests.py
@@ -0,0 +1,211 @@
+# Copyright (C) 2009, Tutorius.org
+# Copyright (C) 2009, Simon Poirier <simpoir@gmail.com>
+# Copyright (C) 2009, Vincent Vinet <vince.vinet@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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
+"""
+Gtk Utils Tests
+
+This module contains all the tests that pertain to the usage of the Tutorius
+gtkutils
+"""
+
+import unittest
+
+import logging
+import gtk, gobject
+from sugar.tutorius.gtkutils import find_widget, register_signals_numbered, register_signals, get_children
+
+class SignalCatcher(object):
+ """Test class that store arguments received on it's callback method.
+ Useful for testing callbacks"""
+ def __init__(self):
+ """Constructor"""
+ self.data = None
+
+ def callback(self, *args):
+ """Callback function, stores argument list in self.data"""
+ self.data = args
+
+def disconnect_handlers(hlist):
+ """Disconnect handles in handler list. hlist must be a list of
+ two-tuples (widget, handler_id)"""
+ for widget, handler in hlist:
+ try:
+ widget.handler_disconnect(handler)
+ except:
+ pass
+
+class GtkUtilsTests(unittest.TestCase):
+ def __init__(self,*args):
+ unittest.TestCase.__init__(self,*args)
+ self.widgets = {}
+ self.top = None
+
+ def setUp(self):
+ #create hierarchy
+ self.top = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
+ self.top.set_name("Top")
+ self.widgets["top"] = {"named":"Top","numbered":"0","widget":self.top}
+
+ hbox = gtk.HBox()
+ self.top.add(hbox)
+ hbox.show()
+ self.widgets["hbox0"] = {"name":"Top.GtkHBox","numbered":"0.0","widget":hbox}
+
+ btn1 = gtk.Button()
+ btn1.set_name("Button1")
+ hbox.pack_start(btn1)
+ btn1.show()
+ self.widgets["btn1"] = {"name":"Top.GtkHBox.Button1","numbered":"0.0.0","widget":btn1}
+
+ btn2 = gtk.Button()
+ btn2.set_name("Button2")
+ hbox.pack_start(btn2)
+ btn2.show()
+ self.widgets["btn2"] = {"name":"Top.GtkHBox.Button2","numbered":"0.0.1","widget":btn2}
+
+ vbox = gtk.VBox()
+ vbox.set_name("VBox1")
+ hbox.pack_start(vbox)
+ hbox.show()
+ self.widgets["vbox0"] = {"name":"Top.GtkHBox.VBox1","numbered":"0.0.2","widget":vbox}
+
+ btn3 = gtk.Button()
+ btn3.set_name("Button3")
+ vbox.pack_start(btn3)
+ btn3.show()
+ self.widgets["btn3"] = {"name":"Top.GtkHBox.VBox1.Button3","numbered":"0.0.2.0","widget":btn3}
+
+ btn4 = gtk.Button()
+ vbox.pack_start(btn4)
+ btn4.show()
+ self.widgets["btn4"] = {"name":"Top.GtkHBox.VBox1.GtkButton","numbered":"0.0.2.1","widget":btn4}
+
+ def tearDown(self):
+ #destroy hierarchy
+ self.top.destroy()
+ self.top = None
+ self.widgets = {}
+
+ def test_named(self):
+ #def register_signals(target, handler, prefix=None, max_depth=None):
+ s=SignalCatcher()
+
+ #Test 0 depth
+ handler_list = register_signals(self.top, s.callback, max_depth=0)
+
+ #remove duplicates in widget list
+ widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
+
+ assert len(widget_list) == 1, "register_signals should not have recursed (%d objects registered)" % len(widget_list)
+
+ assert widget_list[0] == self.top, "register_signals should have gotten only the top"
+
+ disconnect_handlers(handler_list)
+
+ #Test 2 depth
+ handler_list = register_signals(self.top, s.callback, max_depth=2)
+
+ #remove duplicates in widget list
+ widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
+
+ assert len(widget_list) == 5, "expected %d objects (got %d)" % (len(widget_list), 5)
+
+ disconnect_handlers(handler_list)
+
+ #Test Infinite depth
+ handler_list = register_signals(self.top, s.callback, max_depth=None)
+
+ #remove duplicates in widget list
+ widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
+
+ assert len(widget_list) == 7, "expected %d objects (got %d)" % (len(widget_list), 7)
+
+ disconnect_handlers(handler_list)
+
+
+ def test_numbered(self):
+ s=SignalCatcher()
+ #def register_signals_numbered(target, handler, prefix="0", max_depth=None):
+
+ #Test 0 depth
+ handler_list = register_signals_numbered(self.top, s.callback, max_depth=0)
+
+ #remove duplicates in widget list
+ widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
+
+ assert len(widget_list) == 1, "register_signals should not have recursed (%d objects registered)" % len(widget_list)
+
+ assert widget_list[0] == self.top, "register_signals should have gotten only the top"
+
+ disconnect_handlers(handler_list)
+
+ #Test 1 depth
+ handler_list = register_signals_numbered(self.top, s.callback, max_depth=1)
+
+ #remove duplicates in widget list
+ widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
+
+ assert len(widget_list) == 2, "expected %d objects (got %d)" % (len(widget_list), 2)
+
+ disconnect_handlers(handler_list)
+
+ #Test Infinite depth
+ handler_list = register_signals_numbered(self.top, s.callback, max_depth=None)
+
+ #remove duplicates in widget list
+ widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
+
+ assert len(widget_list) == 7, "expected %d objects (got %d)" % (len(widget_list), 7)
+
+ disconnect_handlers(handler_list)
+
+
+ def test_find_widget(self):
+ #Test individual values in the defined widgets
+ for widget in self.widgets.values():
+ f = find_widget(self.top, widget["numbered"])
+ assert f is widget["widget"], "Widget %s found with path %s, expected %s" % (f, widget["numbered"], widget["widget"])
+
+ #Test out of index
+ f = find_widget(self.top, "0.99.1.2")
+ assert f is self.top, "Should have returned top widget"
+
+ def test_register_args_numbered(self):
+ #Need to check the signal catcher and stuff... grreat
+ while gtk.events_pending():
+ gtk.main_iteration(block=False)
+
+
+ def test_register_args_normal(self):
+ #Need to check the signal catcher and stuff... grreat
+ while gtk.events_pending():
+ gtk.main_iteration(block=False)
+
+ def test_notwidget(self):
+ """Test the get_children function"""
+ o = object()
+ res = get_children(o)
+
+ assert len(res) == 0, "object has no children"
+
+ top_children = get_children(self.top)
+ expected = [self.widgets["hbox0"]["widget"],]
+ assert top_children == expected, "expected %s for top's children, got %s" % (str(expected),str(top_children))
+
+if __name__ == "__main__":
+ unittest.main()
+
diff --git a/tests/lib/runall.py b/tests/lib/runall.py
deleted file mode 100644
index ae1bb3a..0000000
--- a/tests/lib/runall.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (C) 2007, Red Hat, Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-import unittest
-
-import test_mime
-
-runner = unittest.TextTestRunner()
-loader = unittest.TestLoader()
-
-suite = unittest.TestSuite()
-suite.addTest(loader.loadTestsFromModule(test_mime))
-
-runner.run(suite)
diff --git a/tests/lib/test_mime.py b/tests/lib/test_mime.py
deleted file mode 100644
index 3df0ce6..0000000
--- a/tests/lib/test_mime.py
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright (C) 2006, Red Hat, Inc.
-# Copyright (C) 2007, One Laptop Per Child
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# 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
-
-import sys
-import unittest
-
-from sugar import mime
-
-class TestMime(unittest.TestCase):
- def test_from_file_name(self):
- self.assertEqual(mime.get_from_file_name('test.pdf'),
- 'application/pdf')
-
- def test_choose_most_significant(self):
- # Mozilla's text in dnd
- mime_type = mime.choose_most_significant(
- ['text/plain', 'text/_moz_htmlcontext', 'text/unicode',
- 'text/html', 'text/_moz_htmlinfo'])
- self.assertEqual(mime_type, 'text/html')
-
- # Mozilla's text in c&v
- mime_type = mime.choose_most_significant(
- ['text/_moz_htmlcontext', 'STRING', 'text/html', 'text/_moz_htmlinfo',
- 'text/x-moz-url-priv', 'UTF8_STRING', 'COMPOUND_TEXT'])
- self.assertEqual(mime_type, 'text/html')
-
- # Mozilla gif in dnd
- mime_type = mime.choose_most_significant(
- ['application/x-moz-file-promise-url',
- 'application/x-moz-file-promise-dest-filename', 'text/_moz_htmlinfo',
- 'text/x-moz-url-desc', 'text/_moz_htmlcontext', 'text/x-moz-url-data',
- 'text/uri-list'])
- self.assertEqual(mime_type, 'text/uri-list')
-
- # Mozilla url in dnd
- mime_type = mime.choose_most_significant(
- ['text/_moz_htmlcontext', 'text/html', 'text/_moz_htmlinfo',
- '_NETSCAPE_URL', 'text/x-moz-url', 'text/x-moz-url-desc',
- 'text/x-moz-url-data', 'text/plain', 'text/unicode'])
- self.assertEqual(mime_type, 'text/x-moz-url')
-
- # Abiword text in dnd
- mime_type = mime.choose_most_significant(
- ['text/rtf', 'text/uri-list'])
- self.assertEqual(mime_type, 'text/uri-list')
-
- # Abiword text in c&v
- mime_type = mime.choose_most_significant(
- ['UTF8_STRING', 'STRING', 'text/html', 'TEXT', 'text/rtf',
- 'COMPOUND_TEXT', 'application/rtf', 'text/plain',
- 'application/xhtml+xml'])
- self.assertEqual(mime_type, 'application/rtf')
-
- # Abiword text in c&v
- mime_type = mime.choose_most_significant(
- ['GTK_TEXT_BUFFER_CONTENTS',
- 'application/x-gtk-text-buffer-rich-text',
- 'UTF8_STRING', 'COMPOUND_TEXT', 'TEXT', 'STRING',
- 'text/plain;charset=utf-8', 'text/plain;charset=UTF-8',
- 'text/plain'])
- self.assertEqual(mime_type, 'text/plain')
-
-if __name__ == "__main__":
- unittest.main()
-
diff --git a/tests/linear_creatortests.py b/tests/linear_creatortests.py
new file mode 100644
index 0000000..dcded57
--- /dev/null
+++ b/tests/linear_creatortests.py
@@ -0,0 +1,79 @@
+# Copyright (C) 2009, Tutorius.org
+# Greatly influenced by sugar/activity/namingalert.py
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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.core import *
+from sugar.tutorius.actions import *
+from sugar.tutorius.filters import *
+from sugar.tutorius.linear_creator import *
+from coretests import TriggerEventFilter
+from actiontests import CountAction
+import unittest
+
+class CreatorTests(unittest.TestCase):
+
+ def test_simple_usage(self):
+ creator = LinearCreator()
+ fsm_name = "SimpleUsageTest"
+
+ creator.set_name(fsm_name)
+
+ # Generate an FSM using the steps
+ creator.action(CountAction())
+ creator.action(CountAction())
+
+ creator.event(TriggerEventFilter("Not important"))
+
+ creator.action(CountAction())
+
+ creator.event(TriggerEventFilter("Not good either..."))
+
+ fsm = creator.generate_fsm()
+
+ # Make sure everything worked!
+ assert fsm.name == fsm_name, "Name was not set properly"
+
+ init_state = fsm.get_state_by_name("INIT")
+
+ assert len(init_state.get_action_list()) == 2, "Creator did not insert all the actions"
+
+ assert init_state.get_event_filter_list()[0].get_next_state() == "State 1" , "expected next state to be 'State 1' but got %s" % init_state.get_event_filter_list()[0].get_next_state()
+
+ state1 = fsm.get_state_by_name("State 1")
+
+ assert len(state1.get_action_list()) == 1, "Creator did not insert all the actions"
+
+ assert state1.get_event_filter_list()[0].get_next_state() == "State 2"
+
+ # Make sure we have the final state and that it's empty
+ state2 = fsm.get_state_by_name("State2")
+
+ assert len(state2.get_action_list()) == 0, "Creator inserted extra actions on wrong state"
+
+ assert len(state2.get_event_filter_list()) == 0, "Creator assigner events to the final state"
+
+ creator.action(CountAction())
+
+ fsm = creator.generate_fsm()
+
+ state2 = fsm.get_state_by_name("State2")
+
+ assert len(state2.get_action_list()) == 1, "Creator did not add the action"
+
+ assert len(state2.get_event_filter_list()) == 0, "Creator assigner events to the final state"
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/overlaytests.py b/tests/overlaytests.py
new file mode 100644
index 0000000..783377c
--- /dev/null
+++ b/tests/overlaytests.py
@@ -0,0 +1,119 @@
+# Copyright (C) 2009, Tutorius.org
+# Copyright (C) 2009, Simon Poirier <simpoir@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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
+"""
+GUI Tests
+
+This module contains all the tests that pertain to the usage of the Tutorius
+overlay mechanism used to display objects on top of the application.
+"""
+
+import unittest
+
+import logging
+import gtk, gobject
+from sugar.tutorius.actions import Action
+import sugar.tutorius.overlayer as overlayer
+
+class CanvasDrawable(object):
+ def __init__(self):
+ self._no_expose = False
+ self.exposition_count = 0
+ def _set_no_expose(self, value):
+ self._no_expose = value
+ def draw_with_context(self, context):
+ self.exposition_count += 1
+ no_expose = property(fset=_set_no_expose)
+
+
+class OverlayerTest(unittest.TestCase):
+ def test_cairodrawable_iface(self):
+ """
+ Quickly validates that all our cairo widgets have a minimal interface
+ implemented.
+ """
+ drawables = [overlayer.TextBubble]
+ for widget in drawables:
+ for attr in filter(lambda s:s[0]!='_', dir(CanvasDrawable)):
+ assert hasattr(widget, attr), \
+ "%s not implementing CanvasDrawable iface"%widget.__name__
+
+
+ def test_drawn(self):
+ """
+ Ensures a cairo widget draw method is called at least once in
+ a real gui app.
+ """
+ win = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
+
+ btn = gtk.Button()
+ btn.show()
+ overlay = overlayer.Overlayer(btn)
+ win.add(overlay)
+ # let's also try to draw substitute button label
+ lbl = overlayer.TextBubble("test!")
+ assert lbl.label == 'test!', \
+ "label property mismatch"
+ btn.show()
+ lbl.show()
+ btn.add(lbl)
+
+ lbl.no_expose = True
+ assert lbl.no_expose, "wrong no_expose evaluation"
+ lbl.no_expose = False
+ assert not lbl.no_expose, "wrong no_expose evaluation"
+
+
+ widg = gtk.Button('bo')
+ widg.show()
+ overlay.put(widg, 50,50)
+ #widget = overlayer.TextBubble("testing msg!", tailpos=(10,-20))
+ #widget.exposition_count = 0
+ ## override draw method
+ #def counter(ctx, self=widget):
+ # self.exposition_count += 1
+ # self.real_exposer(ctx)
+ #widget.real_exposer = widget.draw_with_context
+ #widget.draw_with_context = counter
+ ## centering allows to test the blending with the label
+ #overlay.put(widget, 50, 50)
+ #widget.show()
+ #assert widget.no_expose, \
+ # "Overlay should overide exposition handling of widget"
+ #assert not lbl.no_expose, \
+ # "Non-overlayed cairo should expose as usual"
+
+ # force widget realization
+ # the child is flagged to be redrawn, the overlay should redraw too.
+ win.set_default_size(100, 100)
+ win.show()
+
+ while gtk.events_pending():
+ gtk.main_iteration(block=False)
+ # visual validation: there should be 2 visible bubbles, one as label,
+ # one as overlay
+ import time
+ time.sleep(1)
+ # as x11 events are asynchronous, wait a bit before assuming it went
+ # wrong.
+ while gtk.events_pending():
+ gtk.main_iteration(block=False)
+ time.sleep(10)
+ assert widget.exposition_count>0, "overlay widget should expose"
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/propertiestests.py b/tests/propertiestests.py
new file mode 100644
index 0000000..46346c4
--- /dev/null
+++ b/tests/propertiestests.py
@@ -0,0 +1,402 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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
+
+import unittest
+
+from sugar.tutorius.constraints import *
+from sugar.tutorius.properties import *
+
+# Helper function to test the wrong types on a property, given its type
+def try_wrong_values(obj):
+ typ = type(obj).prop.type
+ if typ != "int":
+ try:
+ obj.prop = 3
+ assert False, "Able to insert int value in property of type %s"%typ
+ except:
+ pass
+
+ if typ != "float":
+ try:
+ obj.prop = 1.1
+ assert False, "Able to insert float value in property of type %s"%typ
+ except:
+ pass
+
+ if typ != "string":
+ try:
+ obj.prop = "Fake string"
+ assert False, "Able to insert string value in property of type %s"%typ
+ except:
+ pass
+
+ if typ != "array":
+ try:
+ obj.prop = [1, 2000, 3, 400]
+ assert False, "Able to insert array value in property of type %s"%typ
+ except:
+ pass
+
+ if typ != "color":
+ try:
+ obj.prop = [1,2,3]
+ if typ != "array":
+ assert False, "Able to insert color value in property of type %s"%typ
+ except:
+ pass
+
+ if typ != "boolean":
+ try:
+ obj.prop = True
+ if typ != "boolean":
+ assert False, "Able to set boolean value in property of type %s"%typ
+ except:
+ pass
+
+class BasePropertyTest(unittest.TestCase):
+ def test_base_class(self):
+ class klass(TPropContainer):
+ prop = TutoriusProperty()
+ obj = klass()
+
+ assert klass.prop.default == None, "There should not be an initial value in the base property"
+
+ assert klass.prop.type == None, "There should be no type associated with the base property"
+
+ assert klass.prop.get_constraints() == [], "There should be no constraints on the base property"
+
+ obj.prop = 2
+
+ assert obj.prop == 2, "Unable to set a value on base class"
+
+class TIntPropertyTest(unittest.TestCase):
+ def test_int_property(self):
+ class klass(TPropContainer):
+ prop = TIntProperty(22)
+ obj = klass()
+
+ assert obj.prop == 22, "Could not set value on property via constructor"
+
+ assert klass.prop.type == "int", "Wrong type on int property : %s" % prop.type
+ cons = klass.prop.get_constraints()
+ assert len(cons) == 2, "Not enough constraints on the int property"
+
+ obj.prop = 12
+
+ assert obj.prop == 12, "Could not set value"
+
+ def test_wrong_values(self):
+ class klass(TPropContainer):
+ prop = TIntProperty(33)
+ obj = klass()
+
+ # Try setting values of other types
+ try_wrong_values(obj)
+
+ def test_limit_constructor(self):
+ class klass(TPropContainer):
+ prop = TIntProperty(22, 0, 30)
+ obj = klass()
+
+ try:
+ obj.prop = -22
+ assert False, "Assigning an out-of-range value should trigger LowerLimitConstraint"
+ except LowerLimitConstraintError:
+ pass
+ except Exception:
+ assert False, "Wrong exception triggered by assignation"
+
+ try:
+ obj.prop = 222
+ assert False, "Assigning an out-of-range value should trigger UpperLimitConstraint"
+ except UpperLimitConstraintError:
+ pass
+ except Exception:
+ assert False, "Wrong exception triggered by assignation"
+
+ def test_failing_constructor(self):
+ try:
+ prop = TIntProperty(100, 0, 20)
+ assert False, "Creation of the property should fail."
+ except UpperLimitConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+ try:
+ prop = TIntProperty(-100, 0, 20)
+ assert False, "Creation of the property should fail."
+ except LowerLimitConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+class TFloatPropertyTest(unittest.TestCase):
+ def test_float_property(self):
+ class klass(TPropContainer):
+ prop = TFloatProperty(22)
+ obj = klass()
+
+ assert obj.prop == 22, "Could not set value on property via constructor"
+
+ assert klass.prop.type == "float", "Wrong type on float property : %s" % klass.prop.type
+ cons = klass.prop.get_constraints()
+ assert len(cons) == 2, "Not enough constraints on the float property"
+
+ obj.prop = 12
+
+ assert obj.prop == 12, "Could not set value"
+
+ def test_wrong_values(self):
+ class klass(TPropContainer):
+ prop = TFloatProperty(33)
+ obj = klass()
+
+ # Try setting values of other types
+ try_wrong_values(obj)
+
+ def test_limit_constructor(self):
+ class klass(TPropContainer):
+ prop = TFloatProperty(22.4, 0.1, 30.5223)
+ obj = klass()
+
+ try:
+ obj.prop = -22.8
+ assert False, "Assigning an out-of-range value should trigger LowerLimitConstraint"
+ except LowerLimitConstraintError:
+ pass
+ except Exception:
+ assert False, "Wrong exception triggered by assignation"
+
+ try:
+ obj.prop = 222.2
+ assert False, "Assigning an out-of-range value should trigger UpperLimitConstraint"
+ except UpperLimitConstraintError:
+ pass
+ except Exception:
+ assert False, "Wrong exception triggered by assignation"
+
+ def test_failing_constructor(self):
+ try:
+ prop = TFloatProperty(100, 0, 20)
+ assert False, "Creation of the property should fail."
+ except UpperLimitConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+ try:
+ prop = TFloatProperty(-100, 0, 20)
+ assert False, "Creation of the property should fail."
+ except LowerLimitConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+class TStringPropertyTest(unittest.TestCase):
+ def test_basic_string(self):
+ class klass(TPropContainer):
+ prop = TStringProperty("Starter string")
+ obj = klass()
+
+ assert obj.prop == "Starter string", "Could not set string value via constructor"
+
+ assert klass.prop.type == "string", "Wrong type for string property : %s" % klass.prop.type
+
+ def test_size_limit(self):
+ class klass(TPropContainer):
+ prop = TStringProperty("Small", 10)
+ obj = klass()
+
+ try:
+ obj.prop = "My string is too big!"
+ assert False, "String should not set to longer than max size"
+ except MaxSizeConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type thrown"
+
+ def test_wrong_values(self):
+ class klass(TPropContainer):
+ prop = TStringProperty("Beginning")
+ obj = klass()
+
+ try_wrong_values(obj)
+
+ def test_failing_constructor(self):
+ try:
+ prop = TStringProperty("This is normal", 5)
+ assert False, "Creation of the property should fail."
+ except MaxSizeConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+class TArrayPropertyTest(unittest.TestCase):
+ def test_basic_array(self):
+ class klass(TPropContainer):
+ prop = TArrayProperty([1, 2, 3, 4])
+ obj = klass()
+
+ assert obj.prop == [1,2,3,4], "Unable to set initial value via constructor"
+
+ assert klass.prop.type == "array", "Wrong type for array : %s"%klass.prop.type
+
+ def test_wrong_values(self):
+ class klass(TPropContainer):
+ prop = TArrayProperty([1,2,3,4,5])
+ obj = klass()
+
+ try_wrong_values(obj)
+
+ def test_size_limits(self):
+ class klass(TPropContainer):
+ prop = TArrayProperty([1,2], None, 4)
+ obj = klass()
+
+ try:
+ obj.prop = [1,2,4,5,6,7]
+ assert False, "Maximum size limit constraint was not properly applied"
+ except MaxSizeConstraintError:
+ pass
+
+ class klass(TPropContainer):
+ prop = TArrayProperty([1,2,3,4], 2)
+ obj = klass()
+
+ try:
+ obj.prop = [1]
+ assert False, "Minimum size limit constraint was not properly applied"
+ except MinSizeConstraintError:
+ pass
+
+ def test_failing_constructor(self):
+ try:
+ prop = TArrayProperty([100, 0, 20], None, 2)
+ assert False, "Creation of the property should fail."
+ except MaxSizeConstraintError:
+ pass
+ try:
+ prop = TArrayProperty([100, 0, 20], 4, None)
+ assert False, "Creation of the property should fail."
+ except MinSizeConstraintError:
+ pass
+
+class TColorPropertyTest(unittest.TestCase):
+ def test_basic_color(self):
+ class klass(TPropContainer):
+ prop = TColorProperty(20, 40, 60)
+ obj = klass()
+
+ assert obj.prop == [20, 40, 60], "Could not set initial value with constructor"
+
+ assert klass.prop.type == "color", "Wrong type on color : %s"%klass.prop.type
+
+ def test_wrong_values(self):
+ class klass(TPropContainer):
+ prop = TColorProperty(250, 250, 250)
+ obj = klass()
+
+ try_wrong_values(obj)
+
+ def test_failing_constructor(self):
+ try:
+ prop = TColorProperty(0, "str", 0)
+ assert False, "Creation of the property should fail."
+ except ColorTypeError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+class TBooleanPropertyTest(unittest.TestCase):
+ def setUp(self):
+ class klass(TPropContainer):
+ prop = TBooleanProperty(False)
+ self.obj = klass()
+
+ def test_basic_boolean(self):
+ assert self.obj.prop == False, "Could not set initial value via constructor"
+
+ assert self.obj.__class__.prop.type == "boolean", "Wrong type for TBooleanProperty : %s"%self.obj.__class__.prop.type
+
+ self.obj.prop = True
+
+ assert self.obj.prop == True, "Could not change the value via set"
+
+ self.obj.prop = False
+
+ assert self.obj.prop == False, "Could not change the value via set"
+
+ def test_wrong_types(self):
+ try_wrong_values(self.obj)
+
+ def test_failing_constructor(self):
+ try:
+ prop = TBooleanProperty(64)
+ assert False, "Creation of the property should fail with non-boolean value"
+ except BooleanConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+class TEnumPropertyTest(unittest.TestCase):
+ def setUp(self):
+ class klass(TPropContainer):
+ prop = TEnumProperty("hello", [1, 2, "hello", "world", True, None])
+ self.obj = klass()
+
+ def test_basic_enum(self):
+ assert self.obj.prop == "hello", "Could not set initial value on property"
+
+ assert type(self.obj).prop.type == "enum", "Wrong type for TEnumProperty : %s"%type(self.obj).prop.type
+
+ self.obj.prop = True
+
+ assert self.obj.prop, "Could not change the value via set"
+
+ try:
+ self.obj.prop = 4
+ assert False, "Switched to a value outside the enum"
+ except EnumConstraintError:
+ pass
+
+ def test_wrong_type(self):
+ try_wrong_values(self.obj)
+
+class TFilePropertyTest(unittest.TestCase):
+ def setUp(self):
+ class klass(TPropContainer):
+ prop = TFileProperty("propertiestests.py")
+ self.obj = klass()
+
+ def test_basic_file(self):
+ assert self.obj.prop == "propertiestests.py", "Could not set initial value"
+
+ assert type(self.obj).prop.type == "file", "Wrong type for TFileProperty : %s"%type(self.obj).prop.type
+
+ self.obj.prop = "run-tests.py"
+
+ assert self.obj.prop == "run-tests.py", "Could not change value"
+
+ try:
+ self.obj.prop = "unknown/file/on/disk.gif"
+ assert False, "An exception should be thrown on unknown file"
+ except FileConstraintError:
+ pass
+
+if __name__ == "__main__":
+ unittest.main()
+
diff --git a/tests/run-tests.py b/tests/run-tests.py
new file mode 100755
index 0000000..d41aa0a
--- /dev/null
+++ b/tests/run-tests.py
@@ -0,0 +1,74 @@
+#!/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
+ 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 *
+
+ unittest.main()
diff --git a/tests/serializertests.py b/tests/serializertests.py
new file mode 100644
index 0000000..6c25bae
--- /dev/null
+++ b/tests/serializertests.py
@@ -0,0 +1,197 @@
+# Copyright (C) 2009, Tutorius.org
+# Copyright (C) 2009, Jean-Christophe Savard <savard.jean.christophe@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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
+"""
+Serialization Tests
+
+This module contains all the tests that pertain to the usage of the Tutorius
+Serializer object. This means testing saving a tutorial dictionary to a .tml
+file, loading the list of tutorials for this activity and building chosen
+tutorial.
+"""
+
+import unittest
+
+import os
+import shutil
+
+from sugar.tutorius import bundler, addon
+from sugar.tutorius.core import State, FiniteStateMachine
+from sugar.tutorius.actions import *
+from sugar.tutorius.filters import *
+from sugar.tutorius.bundler import XMLSerializer, Serializer
+import sugar
+from uuid import uuid1
+
+class SerializerInterfaceTest(unittest.TestCase):
+ """
+ For completeness' sake.
+ """
+ def test_save(self):
+ ser = Serializer()
+
+ try:
+ ser.save_fsm(None)
+ assert False, "save_fsm() should throw an unimplemented error"
+ except:
+ pass
+
+ def test_load(self):
+ ser = Serializer()
+
+ try:
+ ser.load_fsm(str(uuid.uuid1()))
+ assert False, "load_fsm() should throw an unimplemented error"
+ except:
+ pass
+
+class XMLSerializerTest(unittest.TestCase):
+ """
+ Tests the transformation of XML to FSM, then back.
+ """
+ def setUp(self):
+ # Make the serializer believe the test is in a activity path
+ self.testpath = "/tmp/testdata/"
+ os.environ["SUGAR_BUNDLE_PATH"] = self.testpath
+ os.environ["SUGAR_PREFIX"] = self.testpath
+ os.environ["SUGAR_PROFILE"] = 'test'
+## os.mkdir(sugar.tutorius.bundler._get_store_root())
+
+ # Create the sample FSM
+ self.fsm = FiniteStateMachine("testingMachine")
+
+ # Add a few states
+ act1 = addon.create('BubbleMessage', message="Hi", pos=[300, 450])
+ ev1 = addon.create('GtkWidgetEventFilter', "0.12.31.2.2", "clicked", "Second")
+ act2 = addon.create('BubbleMessage', message="Second message", pos=[250, 150], tailpos=[1,2])
+
+ st1 = State("INIT")
+ st1.add_action(act1)
+ st1.add_event_filter(ev1)
+
+ st2 = State("Second")
+
+ st2.add_action(act2)
+
+ self.fsm.add_state(st1)
+ self.fsm.add_state(st2)
+
+ self.uuid = uuid1()
+
+ # Flag to set to True if the output can be deleted after execution of
+ # the test
+ self.remove = True
+
+ def tearDown(self):
+ """
+ Removes the created files, if need be.
+ """
+ if self.remove == True:
+ shutil.rmtree(os.path.join(os.getenv("HOME"),".sugar",os.getenv("SUGAR_PROFILE")))
+ if os.path.isdir(self.testpath):
+ shutil.rmtree(self.testpath)
+
+ def test_save(self):
+ """
+ Writes an FSM to disk, then compares the file to the expected results.
+ "Remove" boolean argument specify if the test data must be removed or not
+ """
+ xml_ser = XMLSerializer()
+ os.makedirs(os.path.join(sugar.tutorius.bundler._get_store_root(), str(self.uuid)))
+ #rpdb2.start_embedded_debugger('flakyPass')
+ xml_ser.save_fsm(self.fsm, bundler.TUTORIAL_FILENAME, os.path.join(sugar.tutorius.bundler._get_store_root(), str(self.uuid)))
+
+ def test_save_and_load(self):
+ """
+ Load up the written FSM and compare it with the object representation.
+ """
+ self.test_save()
+ testpath = "/tmp/testdata/"
+ #rpdb2.start_embedded_debugger('flakyPass')
+ xml_ser = XMLSerializer()
+
+ # This interface needs to be redone... It's not clean because there is
+ # a responsibility mixup between the XML reader and the bundler.
+ loaded_fsm = xml_ser.load_fsm(str(self.uuid))
+
+ # Compare the two FSMs
+ assert loaded_fsm._states.get("INIT").name == self.fsm._states.get("INIT").name, \
+ 'FSM underlying dictionary differ from original to pickled/reformed one'
+ assert loaded_fsm._states.get("Second").name == self.fsm._states.get("Second").name, \
+ 'FSM underlying dictionary differ from original to pickled/reformed one'
+ assert loaded_fsm._states.get("INIT").get_action_list()[0].message == \
+ self.fsm._states.get("INIT").get_action_list()[0].message, \
+ 'FSM underlying State underlying Action differ from original to reformed one'
+ assert len(loaded_fsm.get_action_list()) == 0, "FSM should not have any actions on itself"
+
+ def test_all_actions(self):
+ """
+ Inserts all the known action types in a FSM, then attempt to load it.
+ """
+ st = State("INIT")
+
+ act1 = addon.create('BubbleMessage', "Hi!", pos=[10,120], tailpos=[-12,30])
+ act2 = addon.create('DialogMessage', "Hello again.", pos=[120,10])
+ act3 = WidgetIdentifyAction()
+ act4 = DisableWidgetAction("0.0.0.1.0.0.0")
+ act5 = TypeTextAction("0.0.0.1.1.1.0.0", "New text")
+ act6 = ClickAction("0.0.1.0.1.1")
+ act7 = OnceWrapper(act1)
+ act8 = ChainAction([act1, act2, act3, act4])
+ actions = [act1, act2, act3, act4, act5, act6, act7, act8]
+
+ for action in actions:
+ st.add_action(action)
+
+ self.fsm.remove_state("Second")
+ self.fsm.remove_state("INIT")
+ self.fsm.add_state(st)
+
+ xml_ser = XMLSerializer()
+
+ self.test_save()
+
+ reloaded_fsm = xml_ser.load_fsm(str(self.uuid))
+ assert self.fsm == reloaded_fsm, "Expected equivalence before saving vs after loading."
+
+ def test_all_filters(self):
+ """
+ Inserts all the known action types in a FSM, then attempt to load it.
+ """
+ st = State("INIT")
+
+ ev1 = TimerEvent("Second", 1000)
+ ev2 = addon.create('GtkWidgetEventFilter', "Second", "0.0.1.1.0.0.1", "clicked")
+ ev3 = GtkWidgetTypeFilter("Second", "0.0.1.1.1.2.3", text="Typed stuff")
+ ev4 = GtkWidgetTypeFilter("Second", "0.0.1.1.1.2.3", strokes="acbd")
+ filters = [ev1, ev2, ev3, ev4]
+
+ for filter in filters:
+ st.add_event_filter(filter)
+
+ self.fsm.remove_state("INIT")
+ self.fsm.add_state(st)
+
+ xml_ser = XMLSerializer()
+
+ self.test_save()
+
+ reloaded_fsm = xml_ser.load_fsm(str(self.uuid))
+
+ assert self.fsm == reloaded_fsm, "Expected equivalence before saving vs after loading."
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/servicestests.py b/tests/servicestests.py
new file mode 100644
index 0000000..d669012
--- /dev/null
+++ b/tests/servicestests.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2009, Tutorius.org
+# Copyright (C) 2009, Michael Janelle-Montcalm <michael.jmontcalm@gmail.com>
+# Copyright (C) 2009, Vincent Vinet <vince.vinet@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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
+"""Services tests"""
+
+import unittest
+
+from sugar.tutorius.services import *
+
+
+class ObjectStoreTests(unittest.TestCase):
+ def setUp(self):
+ self.os1 = ObjectStore()
+ self.os2 = ObjectStore()
+
+ def tearDown(self):
+ del self.os1
+ del self.os2
+ ObjectStore.instance = None
+
+ def test_singleton(self):
+ """
+ Validate that the object store is a singleton
+ """
+ assert self.os1 is self.os2, "Both objectstore objects should be the same"
+
+ def test_activity(self):
+ """Validate the activity property"""
+ act = object()
+ self.os1.activity = act
+ assert self.os1.activity is self.os2.activity
+
+ def test_tutorial(self):
+ """Validate the tutorial property"""
+ tut = object()
+ self.os1.tutorial = tut
+ assert self.os1.tutorial is self.os2.tutorial
+
+
diff --git a/tests/uamtests.py b/tests/uamtests.py
new file mode 100644
index 0000000..b2a5901
--- /dev/null
+++ b/tests/uamtests.py
@@ -0,0 +1,61 @@
+# Copyright (C) 2009, Tutorius.org
+# Copyright (C) 2009, Michael Janelle-Montcalm <michael.jmontcalm@gmail.com>
+# Copyright (C) 2009, Vincent Vinet <vince.vinet@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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
+
+import unittest
+
+from sugar.tutorius.uam import parse_uri, SchemeError
+
+PARSE_SUITE={
+#URI SCHEME HOST PARAMS PATH QUERY FRAGMENT
+"tap://act.tut.org/": ["tap", "act.tut.org","", "/", "", ""],
+"tap.gtk://a.t.o/0/1": ["tap.gtk","a.t.o","","/0/1","","",""],
+"tap.gobject://a.t.o/Timer?timeout=5":["tap.gobject","a.t.o","","/Timer","timeout=5",""],
+}
+
+class ParseUriTests(unittest.TestCase):
+ """Tests the UAM parsers"""
+ def test_parse_uri(self):
+ """Test parsing results"""
+ for uri, test in PARSE_SUITE.items():
+ res = parse_uri(uri)
+
+ assert res.scheme == test[0], "%s : Expected scheme %s, got %s" % (uri, test[0], res.scheme)
+ assert res.netloc == test[1], "%s : Expected netloc %s, got %s" % (uri, test[1], res.netloc)
+ assert res.params == test[2], "%s : Expected params %s, got %s" % (uri, test[2], res.params)
+ assert res.path == test[3], "%s : Expected path %s, got %s" % (uri, test[3], res.path)
+ assert res.query == test[4], "%s : Expected query %s, got %s" % (uri, test[4], res.query)
+ assert res.fragment == test[5], "%s : Expected fragment %s, got %s" % (uri, test[5], res.fragment)
+
+ def test_errors(self):
+ """Test exceptions"""
+ try:
+ parse_uri("http://something.org/path")
+ assert False, "Parsing http should fail"
+ except SchemeError:
+ pass
+
+ try:
+ parse_uri("tap.notarealsubscheme://something.org/path")
+ assert False, "Invalid Subscheme should fail"
+ except SchemeError:
+ pass
+
+
+if __name__ == "__main__":
+ unittest.main()
+