diff --git a/.hgignore b/.hgignore
index 458f69c4..9cb58938 100644
--- a/.hgignore
+++ b/.hgignore
@@ -7,6 +7,7 @@ tags
twistd.log
twistd.pid
bridge_constructor/generated
+_trial_temp/
sat.egg-info
*.un~
dist
diff --git a/setup.py b/setup.py
index 254589a4..69b1c8cf 100644
--- a/setup.py
+++ b/setup.py
@@ -191,7 +191,7 @@ setup(name=NAME,
'Operating System :: POSIX :: Linux',
'Topic :: Communications :: Chat'],
package_dir = {'sat':'src', 'sat_frontends':'frontends/src'},
- packages=['sat','sat.tools','sat.bridge', 'sat.plugins',
+ packages=['sat','sat.tools','sat.bridge', 'sat.plugins', 'sat.test', 'sat.core',
'sat_frontends', 'sat_frontends.bridge', 'sat_frontends.quick_frontend',
'sat_frontends.primitivus', 'sat_frontends.wix'],
package_data = {'sat': ['sat.tac','sat.sh'],
diff --git a/src/test/__init__.py b/src/test/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/test/helpers.py b/src/test/helpers.py
new file mode 100644
index 00000000..c5355649
--- /dev/null
+++ b/src/test/helpers.py
@@ -0,0 +1,95 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+SAT: a jabber client
+Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.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 3 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, see .
+"""
+
+import __builtin__
+from twisted.words.protocols.jabber.jid import JID
+
+TEST_JID_STR = u"test@example.org/SàT"
+TEST_JID = JID(u"test@example.org/SàT")
+TEST_PROFILE = 'test_profile'
+
+class DifferentArgsException(Exception):
+ pass
+
+class FakeSAT(object):
+ """Class to simulate a SAT instance"""
+
+ def __init__(self):
+ self.bridge = FakeBridge()
+ self.memory = FakeMemory()
+ self.trigger = FakeTriggerManager()
+
+
+class FakeBridge(object):
+ """Class to simulate and test bridge calls"""
+
+ def expectCall(self, name, *check_args, **check_kwargs):
+ def checkCall(*args, **kwargs):
+ if args != check_args or kwargs != check_kwargs:
+ print "\n\n--------------------"
+ print "Args are not equals:"
+ print "args\n----\n%s (sent)\n%s (wanted)" % (args, check_args)
+ print "kwargs\n------\n%s (sent)\n%s (wanted)" % (kwargs, check_kwargs)
+ print "--------------------\n\n"
+ raise DifferentArgsException
+
+ setattr(self, name, checkCall)
+
+
+class FakeMemory(object):
+ """Class to simulate and test memory object"""
+
+ def getProfileName(self, profile_key):
+ return profile_key
+
+ def addToHistory(self, me_jid, from_jid, to_jid, type, message):
+ pass
+
+ def addContact(self, contact_jid, attributes, groups, profile_key='@DEFAULT@'):
+ pass
+
+ def addPresenceStatus(self, contact_jid, show, priority, statuses, profile_key='@DEFAULT@'):
+ pass
+
+ def addWaitingSub(self, type, contact_jid, profile_key):
+ pass
+
+ def delWaitingSub(self, contact_jid, profile_key):
+ pass
+
+class FakeTriggerManager(object):
+
+ def add(self, point_name, callback):
+ pass
+
+ def point(self, point_name, *args, **kwargs):
+ """We always return true to continue the action"""
+ return True
+
+class FakeParent(object):
+ def __init__(self):
+ self.profile = 'test_profile'
+ self.jid = TEST_JID
+
+def _(text):
+ return text
+
+__builtin__.__dict__['_'] = _
diff --git a/src/test/test_core_xmpp.py b/src/test/test_core_xmpp.py
new file mode 100644
index 00000000..a5ccdcad
--- /dev/null
+++ b/src/test/test_core_xmpp.py
@@ -0,0 +1,114 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+SAT: a jabber client
+Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.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 3 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, see .
+"""
+
+from sat.test import helpers
+from twisted.trial import unittest
+from sat.core.sat_main import SAT
+from sat.core import xmpp
+from twisted.internet import defer
+from twisted.words.protocols.jabber.jid import JID
+from wokkel.generic import parseXml
+from wokkel.xmppim import RosterItem
+
+
+
+class SatXMPPClientTest(unittest.TestCase):
+
+ def setUp(self):
+ self.host = helpers.FakeSAT()
+ self.client = xmpp.SatXMPPClient(self.host, "test_profile", JID("test@example.org"), "test")
+
+ def test_init(self):
+ """Check that init values are correctly initialised"""
+ self.assertEqual(self.client.profile, "test_profile")
+ print self.client.host
+ self.assertEqual(self.client.host_app, self.host)
+ self.assertTrue(isinstance(self.client.client_initialized, defer.Deferred))
+
+class SatMessageProtocolTest(unittest.TestCase):
+
+ def setUp(self):
+ self.host = helpers.FakeSAT()
+ self.message = xmpp.SatMessageProtocol(self.host)
+ self.message.parent = helpers.FakeParent()
+
+ def test_onMessage(self):
+ xml = """
+
+ test
+
+ """
+ stanza = parseXml(xml)
+ self.host.bridge.expectCall("newMessage", "sender@example.net/house", "test", "chat", u"test@example.org/SàT", profile="test_profile")
+ self.message.onMessage(stanza)
+
+class SatRosterProtocolTest(unittest.TestCase):
+
+ def setUp(self):
+ self.host = helpers.FakeSAT()
+ self.roster = xmpp.SatRosterProtocol(self.host)
+ self.roster.parent = helpers.FakeParent()
+
+ def test_onRosterSet(self):
+ roster_item = RosterItem(helpers.TEST_JID)
+ roster_item.name = u"Test Man"
+ roster_item.subscriptionTo = True
+ roster_item.subscriptionFrom = True
+ roster_item.ask = False
+ roster_item.groups = set([u"Test Group 1", u"Test Group 2", u"Test Group 3"])
+ self.host.bridge.expectCall("newContact", helpers.TEST_JID_STR, {'to':'True', 'from': 'True', 'ask': 'False', 'name': u'Test Man'}, set([u"Test Group 1", u"Test Group 2", u"Test Group 3"]), "test_profile")
+ self.roster.onRosterSet(roster_item)
+
+class SatPresenceProtocolTest(unittest.TestCase):
+
+ def setUp(self):
+ self.host = helpers.FakeSAT()
+ self.presence = xmpp.SatPresenceProtocol(self.host)
+ self.presence.parent = helpers.FakeParent()
+
+ def test_availableReceived(self):
+ self.host.bridge.expectCall("presenceUpdate", helpers.TEST_JID_STR, "xa", 15, {'default': "test status", 'fr':'statut de test'}, helpers.TEST_PROFILE)
+ self.presence.availableReceived(helpers.TEST_JID, 'xa', {None: "test status", 'fr':'statut de test'}, 15)
+
+ def test_availableReceived_empty_statuses(self):
+ self.host.bridge.expectCall("presenceUpdate", helpers.TEST_JID_STR, "xa", 15, {}, helpers.TEST_PROFILE)
+ self.presence.availableReceived(helpers.TEST_JID, 'xa', None, 15)
+
+ def test_unavailableReceived(self):
+ self.host.bridge.expectCall("presenceUpdate", helpers.TEST_JID_STR, "unavailable", 0, {}, helpers.TEST_PROFILE)
+ self.presence.unavailableReceived(helpers.TEST_JID, None)
+
+ def test_subscribedReceived(self):
+ self.host.bridge.expectCall("subscribe", "subscribed", helpers.TEST_JID.userhost(), helpers.TEST_PROFILE)
+ self.presence.subscribedReceived(helpers.TEST_JID)
+
+ def test_unsubscribedReceived(self):
+ self.host.bridge.expectCall("subscribe", "unsubscribed", helpers.TEST_JID.userhost(), helpers.TEST_PROFILE)
+ self.presence.unsubscribedReceived(helpers.TEST_JID)
+
+ def test_subscribeReceived(self):
+ self.host.bridge.expectCall("subscribe", "subscribe", helpers.TEST_JID.userhost(), helpers.TEST_PROFILE)
+ self.presence.subscribeReceived(helpers.TEST_JID)
+
+ def test_unsubscribeReceived(self):
+ self.host.bridge.expectCall("subscribe", "unsubscribe", helpers.TEST_JID.userhost(), helpers.TEST_PROFILE)
+ self.presence.unsubscribeReceived(helpers.TEST_JID)
+