Philipp Hörist pushed to branch mainwindow at gajim / gajim
Commits:
-
540d2996
by lovetox at 2021-12-04T15:34:24+01:00
17 changed files:
- − test/broken/integration/test_gui_event_integration.py
- − test/broken/integration/test_roster.py
- − test/broken/test_pluginmanager.py
- − test/broken/unit/test_jingle.py
- − test/broken/unit/test_sessions.py
- − test/broken/unit/test_socks5.py
- − test/lib/__init__.py
- − test/lib/data.py
- − test/lib/gajim_mocks.py
- − test/lib/mock.py
- − test/lib/notify.py
- test/no_gui/__init__.py
- test/no_gui/unit/test_nick_completion.py → test/no_gui/test_nick_completion.py
- test/no_gui/test_styling.py
- − test/no_gui/unit/__init__.py
- − test/unit/__init__.py
- − test/unit/test_gui_interface.py
Changes:
| 1 |
-'''
|
|
| 2 |
-Tests for the miscellaneous functions scattered throughout gajim/gajim.py
|
|
| 3 |
-'''
|
|
| 4 |
-import unittest
|
|
| 5 |
- |
|
| 6 |
-import lib
|
|
| 7 |
-lib.setup_env()
|
|
| 8 |
- |
|
| 9 |
-import nbxmpp
|
|
| 10 |
- |
|
| 11 |
-from gajim.common import gajim
|
|
| 12 |
-from gajim.common import contacts as contacts_module
|
|
| 13 |
-from gajim.common import caps_cache
|
|
| 14 |
-from gajim.gajim import Interface
|
|
| 15 |
- |
|
| 16 |
-from gajim_mocks import *
|
|
| 17 |
-gajim.logger = MockLogger()
|
|
| 18 |
- |
|
| 19 |
-Interface()
|
|
| 20 |
- |
|
| 21 |
-import time
|
|
| 22 |
-from data import *
|
|
| 23 |
- |
|
| 24 |
-from gajim import roster_window
|
|
| 25 |
-from gajim import plugins
|
|
| 26 |
- |
|
| 27 |
-class TestStatusChange(unittest.TestCase):
|
|
| 28 |
- '''tests gajim.py's incredibly complex presence handling'''
|
|
| 29 |
- |
|
| 30 |
- def setUp(self):
|
|
| 31 |
- |
|
| 32 |
- gajim.connections = {}
|
|
| 33 |
- gajim.contacts = contacts_module.LegacyContactsAPI()
|
|
| 34 |
- gajim.interface.roster = roster_window.RosterWindow()
|
|
| 35 |
- gajim.plugin_manager = plugins.PluginManager()
|
|
| 36 |
- gajim.logger = MockLogger()
|
|
| 37 |
- caps_cache.initialize(gajim.logger)
|
|
| 38 |
- |
|
| 39 |
- for acc in contacts:
|
|
| 40 |
- gajim.connections[acc] = MockConnection(acc)
|
|
| 41 |
- |
|
| 42 |
- gajim.interface.roster.fill_contacts_and_groups_dicts(contacts[acc],
|
|
| 43 |
- acc)
|
|
| 44 |
- gajim.interface.roster.add_account(acc)
|
|
| 45 |
- gajim.interface.roster.add_account_contacts(acc)
|
|
| 46 |
- |
|
| 47 |
- self.assertEqual(0, len(notify.notifications))
|
|
| 48 |
- |
|
| 49 |
- def tearDown(self):
|
|
| 50 |
- notify.notifications = []
|
|
| 51 |
- for acc in contacts:
|
|
| 52 |
- gajim.connections[acc].cleanup()
|
|
| 53 |
- |
|
| 54 |
- def contact_comes_online(self, account, jid, resource, prio,
|
|
| 55 |
- should_popup=True):
|
|
| 56 |
- '''a remote contact comes online'''
|
|
| 57 |
- xml = """<presence from='%s/%s' id='123'><priority>%s</priority>
|
|
| 58 |
- <c node='http://gajim.org' ver='pRCD6cgQ4SDqNMCjdhRV6TECx5o='
|
|
| 59 |
- hash='sha-1' xmlns='http://jabber.org/protocol/caps'/>
|
|
| 60 |
- <status>I'm back!</status>
|
|
| 61 |
- </presence>
|
|
| 62 |
- """ % (jid, resource, prio)
|
|
| 63 |
- msg = nbxmpp.protocol.Presence(node=nbxmpp.simplexml.XML2Node(xml))
|
|
| 64 |
- gajim.connections[account]._presenceCB(None, msg)
|
|
| 65 |
- |
|
| 66 |
- contact = None
|
|
| 67 |
- for c in gajim.contacts.get_contacts(account, jid):
|
|
| 68 |
- if c.resource == resource:
|
|
| 69 |
- contact = c
|
|
| 70 |
- break
|
|
| 71 |
- |
|
| 72 |
- self.assertEqual('online', contact.show)
|
|
| 73 |
- self.assertEqual("I'm back!", contact.status)
|
|
| 74 |
- self.assertEqual(prio, contact.priority)
|
|
| 75 |
- |
|
| 76 |
- # the most recent notification is that the contact connected
|
|
| 77 |
- if should_popup:
|
|
| 78 |
- self.assertEqual('Contact Signed In',
|
|
| 79 |
- notify.notifications[-1].popup_event_type)
|
|
| 80 |
- else:
|
|
| 81 |
- self.assertEqual('', notify.notifications[-1].popup_event_type)
|
|
| 82 |
- |
|
| 83 |
- def contact_goes_offline(self, account, jid, resource, prio,
|
|
| 84 |
- still_exists = True):
|
|
| 85 |
- '''a remote contact goes offline.'''
|
|
| 86 |
- xml = """<presence type='unavailable' from='%s/%s' id='123'>
|
|
| 87 |
- <priority>%s</priority>
|
|
| 88 |
- <c node='http://gajim.org' ver='pRCD6cgQ4SDqNMCjdhRV6TECx5o='
|
|
| 89 |
- hash='sha-1' xmlns='http://jabber.org/protocol/caps'/>
|
|
| 90 |
- <status>Goodbye!</status>
|
|
| 91 |
- </presence>
|
|
| 92 |
- """ % (jid, resource, prio)
|
|
| 93 |
- msg = nbxmpp.protocol.Presence(node=nbxmpp.simplexml.XML2Node(xml))
|
|
| 94 |
- gajim.connections[account]._presenceCB(None, msg)
|
|
| 95 |
- |
|
| 96 |
- contact = None
|
|
| 97 |
- for c in gajim.contacts.get_contacts(account, jid):
|
|
| 98 |
- if c.resource == resource:
|
|
| 99 |
- contact = c
|
|
| 100 |
- break
|
|
| 101 |
- |
|
| 102 |
- if not still_exists:
|
|
| 103 |
- self.assertTrue(contact is None)
|
|
| 104 |
- return
|
|
| 105 |
- |
|
| 106 |
- self.assertEqual('offline', contact.show)
|
|
| 107 |
- self.assertEqual('Goodbye!', contact.status)
|
|
| 108 |
- self.assertEqual(prio, contact.priority)
|
|
| 109 |
- |
|
| 110 |
- self.assertEqual('Contact Signed Out',
|
|
| 111 |
- notify.notifications[-1].popup_event_type)
|
|
| 112 |
- |
|
| 113 |
- def user_starts_chatting(self, jid, account, resource=None):
|
|
| 114 |
- '''the user opens a chat window and starts talking'''
|
|
| 115 |
- ctrl = MockChatControl(jid, account)
|
|
| 116 |
- win = MockWindow()
|
|
| 117 |
- win.new_tab(ctrl)
|
|
| 118 |
- gajim.interface.msg_win_mgr._windows['test'] = win
|
|
| 119 |
- |
|
| 120 |
- if resource:
|
|
| 121 |
- jid = jid + '/' + resource
|
|
| 122 |
- |
|
| 123 |
- # a basic session is started
|
|
| 124 |
- session = gajim.connections[account1].make_new_session(jid,
|
|
| 125 |
- '01234567890abcdef', cls=MockSession)
|
|
| 126 |
- ctrl.set_session(session)
|
|
| 127 |
- |
|
| 128 |
- return ctrl
|
|
| 129 |
- |
|
| 130 |
- def user_starts_esession(self, jid, resource, account):
|
|
| 131 |
- '''the user opens a chat window and starts an encrypted session'''
|
|
| 132 |
- ctrl = self.user_starts_chatting(jid, account, resource)
|
|
| 133 |
- ctrl.session.status = 'active'
|
|
| 134 |
- ctrl.session.enable_encryption = True
|
|
| 135 |
- |
|
| 136 |
- return ctrl
|
|
| 137 |
- |
|
| 138 |
- def test_contact_comes_online(self):
|
|
| 139 |
- jid = 'default1@gajim.org'
|
|
| 140 |
- |
|
| 141 |
- # contact is offline initially
|
|
| 142 |
- contacts = gajim.contacts.get_contacts(account1, jid)
|
|
| 143 |
- self.assertEqual(1, len(contacts))
|
|
| 144 |
- self.assertEqual('offline', contacts[0].show)
|
|
| 145 |
- self.assertEqual('', contacts[0].status)
|
|
| 146 |
- |
|
| 147 |
- self.contact_comes_online(account1, jid, 'lowprio', 1)
|
|
| 148 |
- |
|
| 149 |
- def test_contact_goes_offline(self):
|
|
| 150 |
- jid = 'default1@gajim.org'
|
|
| 151 |
- |
|
| 152 |
- self.contact_comes_online(account1, jid, 'lowprio', 1)
|
|
| 153 |
- |
|
| 154 |
- ctrl = self.user_starts_chatting(jid, account1)
|
|
| 155 |
- orig_sess = ctrl.session
|
|
| 156 |
- |
|
| 157 |
- self.contact_goes_offline(account1, jid, 'lowprio', 1)
|
|
| 158 |
- |
|
| 159 |
- # session hasn't changed since we were talking to the bare jid
|
|
| 160 |
- self.assertEqual(orig_sess, ctrl.session)
|
|
| 161 |
- |
|
| 162 |
- def test_two_resources_higher_comes_online(self):
|
|
| 163 |
- jid = 'default1@gajim.org'
|
|
| 164 |
- |
|
| 165 |
- self.contact_comes_online(account1, jid, 'lowprio', 1)
|
|
| 166 |
- |
|
| 167 |
- ctrl = self.user_starts_chatting(jid, account1)
|
|
| 168 |
- |
|
| 169 |
- self.contact_comes_online(account1, jid, 'highprio', 50,
|
|
| 170 |
- should_popup=False)
|
|
| 171 |
- |
|
| 172 |
- # old session was dropped
|
|
| 173 |
- self.assertEqual(None, ctrl.session)
|
|
| 174 |
- |
|
| 175 |
- def test_two_resources_higher_goes_offline(self):
|
|
| 176 |
- jid = 'default1@gajim.org'
|
|
| 177 |
- |
|
| 178 |
- self.contact_comes_online(account1, jid, 'lowprio', 1)
|
|
| 179 |
- self.contact_comes_online(account1, jid, 'highprio', 50,
|
|
| 180 |
- should_popup=False)
|
|
| 181 |
- |
|
| 182 |
- ctrl = self.user_starts_chatting(jid, account1)
|
|
| 183 |
- |
|
| 184 |
- self.contact_goes_offline(account1, jid, 'highprio', 50,
|
|
| 185 |
- still_exists=False)
|
|
| 186 |
- |
|
| 187 |
- # old session was dropped
|
|
| 188 |
- self.assertEqual(None, ctrl.session)
|
|
| 189 |
- |
|
| 190 |
- def test_two_resources_higher_comes_online_with_esession(self):
|
|
| 191 |
- jid = 'default1@gajim.org'
|
|
| 192 |
- |
|
| 193 |
- self.contact_comes_online(account1, jid, 'lowprio', 1)
|
|
| 194 |
- |
|
| 195 |
- ctrl = self.user_starts_esession(jid, 'lowprio', account1)
|
|
| 196 |
- |
|
| 197 |
- self.contact_comes_online(account1, jid, 'highprio', 50,
|
|
| 198 |
- should_popup=False)
|
|
| 199 |
- |
|
| 200 |
- # session was associated with the low priority full jid, so it should
|
|
| 201 |
- # have been removed from the control
|
|
| 202 |
- self.assertEqual(None, ctrl.session)
|
|
| 203 |
- |
|
| 204 |
- def test_two_resources_higher_goes_offline_with_esession(self):
|
|
| 205 |
- jid = 'default1@gajim.org'
|
|
| 206 |
- |
|
| 207 |
- self.contact_comes_online(account1, jid, 'lowprio', 1)
|
|
| 208 |
- self.contact_comes_online(account1, jid, 'highprio', 50)
|
|
| 209 |
- |
|
| 210 |
- ctrl = self.user_starts_esession(jid, 'highprio', account1)
|
|
| 211 |
- |
|
| 212 |
- self.contact_goes_offline(account1, jid, 'highprio', 50,
|
|
| 213 |
- still_exists=False)
|
|
| 214 |
- |
|
| 215 |
- # session was associated with the high priority full jid, so it should
|
|
| 216 |
- # have been removed from the control
|
|
| 217 |
- self.assertEqual(None, ctrl.session)
|
|
| 218 |
- |
|
| 219 |
-if __name__ == '__main__':
|
|
| 220 |
- unittest.main()
|
| 1 |
-import unittest
|
|
| 2 |
- |
|
| 3 |
-import lib
|
|
| 4 |
-lib.setup_env()
|
|
| 5 |
- |
|
| 6 |
-from data import *
|
|
| 7 |
- |
|
| 8 |
-from gajim_mocks import *
|
|
| 9 |
- |
|
| 10 |
-from gajim.common import app
|
|
| 11 |
-from gajim.common import contacts as contacts_module
|
|
| 12 |
-from gajim import roster_window
|
|
| 13 |
- |
|
| 14 |
-app.get_jid_from_account = lambda acc: 'myjid@' + acc
|
|
| 15 |
- |
|
| 16 |
- |
|
| 17 |
-class TestRosterWindow(unittest.TestCase):
|
|
| 18 |
- |
|
| 19 |
- def setUp(self):
|
|
| 20 |
- app.interface = MockInterface()
|
|
| 21 |
- |
|
| 22 |
- self.C_NAME = roster_window.Column.NAME
|
|
| 23 |
- self.C_TYPE = roster_window.Column.TYPE
|
|
| 24 |
- self.C_JID = roster_window.Column.JID
|
|
| 25 |
- self.C_ACCOUNT = roster_window.Column.ACCOUNT
|
|
| 26 |
- |
|
| 27 |
- # Add after creating RosterWindow
|
|
| 28 |
- # We want to test the filling explicitly
|
|
| 29 |
- app.contacts = contacts_module.LegacyContactsAPI()
|
|
| 30 |
- app.connections = {}
|
|
| 31 |
- self.roster = roster_window.RosterWindow(app.app)
|
|
| 32 |
- |
|
| 33 |
- for acc in contacts:
|
|
| 34 |
- app.connections[acc] = MockConnection(acc)
|
|
| 35 |
- app.contacts.add_account(acc)
|
|
| 36 |
- |
|
| 37 |
- def tearDown(self):
|
|
| 38 |
- self.roster.window.destroy()
|
|
| 39 |
- # Clean main loop
|
|
| 40 |
- from gi.repository import GLib
|
|
| 41 |
- mc = GLib.main_context_default()
|
|
| 42 |
- while mc.pending():
|
|
| 43 |
- mc.iteration()
|
|
| 44 |
- |
|
| 45 |
- ### Custom assertions
|
|
| 46 |
- def assert_all_contacts_are_in_roster(self, acc):
|
|
| 47 |
- for jid in contacts[acc]:
|
|
| 48 |
- self.assert_contact_is_in_roster(jid, acc)
|
|
| 49 |
- |
|
| 50 |
- def assert_contact_is_in_roster(self, jid, account):
|
|
| 51 |
- contacts = app.contacts.get_contacts(account, jid)
|
|
| 52 |
- # check for all resources
|
|
| 53 |
- for contact in contacts:
|
|
| 54 |
- iters = self.roster._get_contact_iter(jid, account,
|
|
| 55 |
- model=self.roster.model)
|
|
| 56 |
- |
|
| 57 |
- if jid != app.get_jid_from_account(account):
|
|
| 58 |
- # We don't care for groups of SelfContact
|
|
| 59 |
- self.assertTrue(len(iters) == len(contact.get_shown_groups()),
|
|
| 60 |
- msg='Contact is not in all his groups')
|
|
| 61 |
- |
|
| 62 |
- # Are we big brother?
|
|
| 63 |
- bb_jid = None
|
|
| 64 |
- bb_account = None
|
|
| 65 |
- family = app.contacts.get_metacontacts_family(account, jid)
|
|
| 66 |
- if family:
|
|
| 67 |
- nearby_family, bb_jid, bb_account = \
|
|
| 68 |
- self.roster._get_nearby_family_and_big_brother(family, account)
|
|
| 69 |
- |
|
| 70 |
- is_in_nearby_family = (jid, account) in (
|
|
| 71 |
- (data['jid'], data['account']) for data in nearby_family)
|
|
| 72 |
- self.assertTrue(is_in_nearby_family,
|
|
| 73 |
- msg='Contact not in his own nearby family')
|
|
| 74 |
- |
|
| 75 |
- is_big_brother = (bb_jid, bb_account) == (jid, account)
|
|
| 76 |
- |
|
| 77 |
- # check for each group tag
|
|
| 78 |
- for titerC in iters:
|
|
| 79 |
- self.assertTrue(self.roster.model.iter_is_valid(titerC),
|
|
| 80 |
- msg='Contact iter invalid')
|
|
| 81 |
- |
|
| 82 |
- c_model = self.roster.model[titerC]
|
|
| 83 |
- # name can be stricked if contact or group is blocked
|
|
| 84 |
-# self.assertEqual(contact.get_shown_name(), c_model[self.C_NAME],
|
|
| 85 |
-# msg='Contact name missmatch')
|
|
| 86 |
- self.assertEqual(contact.jid, c_model[self.C_JID],
|
|
| 87 |
- msg='Jid missmatch')
|
|
| 88 |
- |
|
| 89 |
- if not self.roster.regroup:
|
|
| 90 |
- self.assertEqual(account, c_model[self.C_ACCOUNT],
|
|
| 91 |
- msg='Account missmatch')
|
|
| 92 |
- |
|
| 93 |
- # Check for correct nesting
|
|
| 94 |
- parent_iter = self.roster.model.iter_parent(titerC)
|
|
| 95 |
- p_model = self.roster.model[parent_iter]
|
|
| 96 |
- if family:
|
|
| 97 |
- if is_big_brother:
|
|
| 98 |
- self.assertTrue(p_model[self.C_TYPE] == 'group',
|
|
| 99 |
- msg='Big Brother is not on top')
|
|
| 100 |
- else:
|
|
| 101 |
- self.assertTrue(p_model[self.C_TYPE] == 'contact',
|
|
| 102 |
- msg='Little Brother brother has no BigB')
|
|
| 103 |
- else:
|
|
| 104 |
- if jid == app.get_jid_from_account(account):
|
|
| 105 |
- self.assertTrue(p_model[self.C_TYPE] == 'account',
|
|
| 106 |
- msg='SelfContact is not on top')
|
|
| 107 |
- else:
|
|
| 108 |
- self.assertTrue(p_model[self.C_TYPE] == 'group',
|
|
| 109 |
- msg='Contact not found in a group')
|
|
| 110 |
- |
|
| 111 |
- def assert_group_is_in_roster(self, group, account):
|
|
| 112 |
- #TODO
|
|
| 113 |
- pass
|
|
| 114 |
- |
|
| 115 |
- def assert_account_is_in_roster(self, acc):
|
|
| 116 |
- titerA = self.roster._get_account_iter(acc, model=self.roster.model)
|
|
| 117 |
- self.assertTrue(self.roster.model.iter_is_valid(titerA),
|
|
| 118 |
- msg='Account iter is invalid')
|
|
| 119 |
- |
|
| 120 |
- acc_model = self.roster.model[titerA]
|
|
| 121 |
- self.assertEqual(acc_model[self.C_TYPE], 'account',
|
|
| 122 |
- msg='No account found')
|
|
| 123 |
- |
|
| 124 |
- if not self.roster.regroup:
|
|
| 125 |
- self.assertEqual(acc_model[self.C_ACCOUNT], acc,
|
|
| 126 |
- msg='Account not found')
|
|
| 127 |
- |
|
| 128 |
- self_jid = app.get_jid_from_account(acc)
|
|
| 129 |
- self.assertEqual(acc_model[self.C_JID], self_jid,
|
|
| 130 |
- msg='Account JID not found in account row')
|
|
| 131 |
- |
|
| 132 |
- def assert_model_is_in_sync(self):
|
|
| 133 |
- #TODO: check that iter_n_children returns the correct numbers
|
|
| 134 |
- pass
|
|
| 135 |
- |
|
| 136 |
- # tests
|
|
| 137 |
- def test_fill_contacts_and_groups_dicts(self):
|
|
| 138 |
- for acc in contacts:
|
|
| 139 |
- self.roster.fill_contacts_and_groups_dicts(contacts[acc], acc)
|
|
| 140 |
- |
|
| 141 |
- for jid in contacts[acc]:
|
|
| 142 |
- instances = app.contacts.get_contacts(acc, jid)
|
|
| 143 |
- |
|
| 144 |
- # Created a contact for each single jid?
|
|
| 145 |
- self.assertTrue(len(instances) == 1)
|
|
| 146 |
- |
|
| 147 |
- # Contacts kept their info
|
|
| 148 |
- contact = instances[0]
|
|
| 149 |
- self.assertEqual(sorted(contact.groups), sorted(contacts[acc][jid]['groups']),
|
|
| 150 |
- msg='Group Missmatch')
|
|
| 151 |
- |
|
| 152 |
- groups = contacts[acc][jid]['groups'] or ['General',]
|
|
| 153 |
- |
|
| 154 |
- def test_fill_roster_model(self):
|
|
| 155 |
- for acc in contacts:
|
|
| 156 |
- self.roster.fill_contacts_and_groups_dicts(contacts[acc], acc)
|
|
| 157 |
- |
|
| 158 |
- self.roster.add_account(acc)
|
|
| 159 |
- self.assert_account_is_in_roster(acc)
|
|
| 160 |
- |
|
| 161 |
- self.roster.add_account_contacts(acc)
|
|
| 162 |
- self.assert_all_contacts_are_in_roster(acc)
|
|
| 163 |
- |
|
| 164 |
- self.assert_model_is_in_sync()
|
|
| 165 |
- |
|
| 166 |
- |
|
| 167 |
-class TestRosterWindowRegrouped(TestRosterWindow):
|
|
| 168 |
- |
|
| 169 |
- def setUp(self):
|
|
| 170 |
- app.settings.set('mergeaccounts', True)
|
|
| 171 |
- TestRosterWindow.setUp(self)
|
|
| 172 |
- |
|
| 173 |
- def test_toggle_regroup(self):
|
|
| 174 |
- self.roster.regroup = not self.roster.regroup
|
|
| 175 |
- self.roster.setup_and_draw_roster()
|
|
| 176 |
- self.roster.regroup = not self.roster.regroup
|
|
| 177 |
- self.roster.setup_and_draw_roster()
|
|
| 178 |
- |
|
| 179 |
- |
|
| 180 |
-class TestRosterWindowMetaContacts(TestRosterWindowRegrouped):
|
|
| 181 |
- |
|
| 182 |
- def test_receive_metacontact_data(self):
|
|
| 183 |
- for complete_data in metacontact_data:
|
|
| 184 |
- t_acc = complete_data[0]['account']
|
|
| 185 |
- t_jid = complete_data[0]['jid']
|
|
| 186 |
- data = complete_data[1:]
|
|
| 187 |
- for brother in data:
|
|
| 188 |
- acc = brother['account']
|
|
| 189 |
- jid = brother['jid']
|
|
| 190 |
- app.contacts.add_metacontact(t_acc, t_jid, acc, jid)
|
|
| 191 |
- self.roster.setup_and_draw_roster()
|
|
| 192 |
- |
|
| 193 |
- def test_connect_new_metacontact(self):
|
|
| 194 |
- self.test_fill_roster_model()
|
|
| 195 |
- |
|
| 196 |
- jid = 'coolstuff@gajim.org'
|
|
| 197 |
- contact = app.contacts.create_contact(jid, account1)
|
|
| 198 |
- app.contacts.add_contact(account1, contact)
|
|
| 199 |
- self.roster.add_contact(jid, account1)
|
|
| 200 |
- self.roster.chg_contact_status(contact, 'offline', '', account1)
|
|
| 201 |
- |
|
| 202 |
- app.contacts.add_metacontact(account1, 'samejid@gajim.org',
|
|
| 203 |
- account1, jid)
|
|
| 204 |
- self.roster.chg_contact_status(contact, 'online', '', account1)
|
|
| 205 |
- |
|
| 206 |
- self.assert_model_is_in_sync()
|
|
| 207 |
- |
|
| 208 |
- |
|
| 209 |
- |
|
| 210 |
-if __name__ == '__main__':
|
|
| 211 |
- unittest.main()
|
| 1 |
-#!/usr/bin/env python
|
|
| 2 |
-# -*- coding: utf-8 -*-
|
|
| 3 |
- |
|
| 4 |
-## This file is part of Gajim.
|
|
| 5 |
-##
|
|
| 6 |
-## Gajim is free software; you can redistribute it and/or modify
|
|
| 7 |
-## it under the terms of the GNU General Public License as published
|
|
| 8 |
-## by the Free Software Foundation; version 3 only.
|
|
| 9 |
-##
|
|
| 10 |
-## Gajim is distributed in the hope that it will be useful,
|
|
| 11 |
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
| 12 |
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
| 13 |
-## GNU General Public License for more details.
|
|
| 14 |
-##
|
|
| 15 |
-## You should have received a copy of the GNU General Public License
|
|
| 16 |
-## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
|
| 17 |
-##
|
|
| 18 |
- |
|
| 19 |
-'''
|
|
| 20 |
-Testing PluginManager class.
|
|
| 21 |
- |
|
| 22 |
-:author: Mateusz Biliński <mateusz@bilinski.it>
|
|
| 23 |
-:since: 05/30/2008
|
|
| 24 |
-:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
|
| 25 |
-:license: GPL
|
|
| 26 |
-'''
|
|
| 27 |
- |
|
| 28 |
-import sys
|
|
| 29 |
-import os
|
|
| 30 |
-import unittest
|
|
| 31 |
- |
|
| 32 |
-gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
|
|
| 33 |
-sys.path.append(gajim_root + '/gajim')
|
|
| 34 |
- |
|
| 35 |
-# a temporary version of ~/.gajim for testing
|
|
| 36 |
-configdir = gajim_root + '/test/tmp'
|
|
| 37 |
- |
|
| 38 |
-import time
|
|
| 39 |
- |
|
| 40 |
-# define _ for i18n
|
|
| 41 |
-import builtins
|
|
| 42 |
-builtins._ = lambda x: x
|
|
| 43 |
- |
|
| 44 |
-# wipe config directory
|
|
| 45 |
-import os
|
|
| 46 |
-if os.path.isdir(configdir):
|
|
| 47 |
- import shutil
|
|
| 48 |
- shutil.rmtree(configdir)
|
|
| 49 |
- |
|
| 50 |
-os.mkdir(configdir)
|
|
| 51 |
- |
|
| 52 |
-from gajim.common import configpaths
|
|
| 53 |
-configpaths.set_config_root(configdir)
|
|
| 54 |
-configpaths.init()
|
|
| 55 |
- |
|
| 56 |
-# for some reason common.app needs to be imported before xmpppy?
|
|
| 57 |
- |
|
| 58 |
-configpaths.override_path('DATA', gajim_root + '/gajim/data')
|
|
| 59 |
- |
|
| 60 |
-# name to use for the test account
|
|
| 61 |
-account_name = 'test'
|
|
| 62 |
- |
|
| 63 |
-from plugins import PluginManager
|
|
| 64 |
- |
|
| 65 |
-class PluginManagerTestCase(unittest.TestCase):
|
|
| 66 |
- def setUp(self):
|
|
| 67 |
- self.pluginmanager = PluginManager()
|
|
| 68 |
- |
|
| 69 |
- def tearDown(self):
|
|
| 70 |
- pass
|
|
| 71 |
- |
|
| 72 |
- def test_01_Singleton(self):
|
|
| 73 |
- """ 1. Checking whether PluginManger class is singleton. """
|
|
| 74 |
- self.pluginmanager.test_arg = 1
|
|
| 75 |
- secondPluginManager = PluginManager()
|
|
| 76 |
- |
|
| 77 |
- self.assertEqual(id(secondPluginManager), id(self.pluginmanager),
|
|
| 78 |
- 'Different IDs in references to PluginManager objects (not a singleton)')
|
|
| 79 |
- self.assertEqual(secondPluginManager.test_arg, 1,
|
|
| 80 |
- 'References point to different PluginManager objects (not a singleton')
|
|
| 81 |
- |
|
| 82 |
-def suite():
|
|
| 83 |
- suite = unittest.TestLoader().loadTestsFromTestCase(PluginManagerTestCase)
|
|
| 84 |
- return suite
|
|
| 85 |
- |
|
| 86 |
-if __name__=='__main__':
|
|
| 87 |
- runner = unittest.TextTestRunner()
|
|
| 88 |
- test_suite = suite()
|
|
| 89 |
- runner.run(test_suite)
|
| 1 |
-'''
|
|
| 2 |
-Tests for dispatcher.py
|
|
| 3 |
-'''
|
|
| 4 |
-import unittest
|
|
| 5 |
- |
|
| 6 |
-import lib
|
|
| 7 |
-lib.setup_env()
|
|
| 8 |
- |
|
| 9 |
-from mock import Mock
|
|
| 10 |
- |
|
| 11 |
- |
|
| 12 |
-from nbxmpp import dispatcher
|
|
| 13 |
-from nbxmpp.namespaces import Namespace
|
|
| 14 |
- |
|
| 15 |
-from gajim.common.protocol.bytestream import ConnectionIBBytestream
|
|
| 16 |
-from gajim.common.protocol.bytestream import ConnectionSocks5Bytestream
|
|
| 17 |
-from gajim.common.jingle import ConnectionJingle
|
|
| 18 |
-from gajim.common import app
|
|
| 19 |
-from gajim.common.socks5 import SocksQueue
|
|
| 20 |
- |
|
| 21 |
- |
|
| 22 |
-session_init = '''
|
|
| 23 |
-<iq xmlns="jabber:client" to="jingleft@thiessen.im/Gajim" type="set" id="43">
|
|
| 24 |
-<jingle xmlns="urn:xmpp:jingle:1" action="session-initiate" initiator="jtest@thiessen.im/Gajim" sid="38">
|
|
| 25 |
-<content name="fileWL1Y2JIPTM5RAD68" creator="initiator">
|
|
| 26 |
-<security xmlns="urn:xmpp:jingle:security:xtls:0">
|
|
| 27 |
-<method name="x509" />
|
|
| 28 |
-</security>
|
|
| 29 |
-<description xmlns="urn:xmpp:jingle:apps:file-transfer:1">
|
|
| 30 |
-<offer>
|
|
| 31 |
-<file xmlns="http://jabber.org/protocol/si/profile/file-transfer" name="to" size="2273">
|
|
| 32 |
-<desc />
|
|
| 33 |
-</file>
|
|
| 34 |
-</offer>
|
|
| 35 |
-</description>
|
|
| 36 |
-<transport xmlns="urn:xmpp:jingle:transports:s5b:1" sid="39">
|
|
| 37 |
-<candidate jid="jtest@thiessen.im/Gajim" cid="40" priority="8257536" host="192.168.2.100" type="direct" port="28011" />
|
|
| 38 |
-<candidate jid="proxy.thiessen.im" cid="41" priority="655360" host="192.168.2.100" type="proxy" port="5000" />
|
|
| 39 |
-<candidate jid="proxy.jabbim.cz" cid="42" priority="655360" host="192.168.2.100" type="proxy" port="7777" />
|
|
| 40 |
-</transport>
|
|
| 41 |
-</content>
|
|
| 42 |
-</jingle>
|
|
| 43 |
-</iq>
|
|
| 44 |
- '''
|
|
| 45 |
- |
|
| 46 |
- |
|
| 47 |
-transport_info = '''
|
|
| 48 |
-<iq from='jtest@thiessen.im/Gajim'
|
|
| 49 |
- id='hjdi8'
|
|
| 50 |
- to='jingleft@thiessen.im/Gajim'
|
|
| 51 |
- type='set'>
|
|
| 52 |
- <jingle xmlns='urn:xmpp:jingle:1'
|
|
| 53 |
- action='transport-info'
|
|
| 54 |
- initiator='jtest@thiessen.im/Gajim'
|
|
| 55 |
- sid='38'>
|
|
| 56 |
- <content creator='initiator' name='fileWL1Y2JIPTM5RAD68'>
|
|
| 57 |
- <transport xmlns='urn:xmpp:jingle:transports:s5b:1'
|
|
| 58 |
- sid='vj3hs98y'>
|
|
| 59 |
- <candidate-used cid='hr65dqyd'/>
|
|
| 60 |
- </transport>
|
|
| 61 |
- </content>
|
|
| 62 |
- </jingle>
|
|
| 63 |
-</iq>
|
|
| 64 |
- |
|
| 65 |
-'''
|
|
| 66 |
- |
|
| 67 |
-class Connection(Mock, ConnectionJingle, ConnectionSocks5Bytestream,
|
|
| 68 |
- ConnectionIBBytestream):
|
|
| 69 |
- |
|
| 70 |
- def __init__(self):
|
|
| 71 |
- Mock.__init__(self)
|
|
| 72 |
- ConnectionJingle.__init__(self)
|
|
| 73 |
- ConnectionSocks5Bytestream.__init__(self)
|
|
| 74 |
- ConnectionIBBytestream.__init__(self)
|
|
| 75 |
- self.connected = 2 # This tells gajim we are connected
|
|
| 76 |
- |
|
| 77 |
- def send(self, stanza=None, when=None):
|
|
| 78 |
- # Called when gajim wants to send something
|
|
| 79 |
- print(str(stanza))
|
|
| 80 |
- |
|
| 81 |
- |
|
| 82 |
-class TestJingle(unittest.TestCase):
|
|
| 83 |
- |
|
| 84 |
- def setUp(self):
|
|
| 85 |
- self.dispatcher = dispatcher.XMPPDispatcher()
|
|
| 86 |
- app.nec = Mock()
|
|
| 87 |
- app.socks5queue = SocksQueue(Mock())
|
|
| 88 |
- # Setup mock client
|
|
| 89 |
- self.client = Connection()
|
|
| 90 |
- self.client.__str__ = lambda: 'Mock' # FIXME: why do I need this one?
|
|
| 91 |
- self.client._caller = Connection()
|
|
| 92 |
- self.client.defaultNamespace = Namespace.CLIENT
|
|
| 93 |
- self.client.Connection = Connection() # mock transport
|
|
| 94 |
- self.con = self.client.Connection
|
|
| 95 |
- self.con.server_resource = None
|
|
| 96 |
- self.con.connection = Connection()
|
|
| 97 |
- |
|
| 98 |
- '''
|
|
| 99 |
- Fake file_props when we receive a file. Gajim creates a file_props
|
|
| 100 |
- out of a FileRequestReceive event and from then on it changes in
|
|
| 101 |
- a lot of places. It is easier to just copy it in here.
|
|
| 102 |
- If the session_initiate stanza changes, this also must change.
|
|
| 103 |
- '''
|
|
| 104 |
- self.receive_file = {'stream-methods':
|
|
| 105 |
- 'http://jabber.org/protocol/bytestreams',
|
|
| 106 |
- 'sender': 'jtest@thiessen.im/Gajim',
|
|
| 107 |
- 'file-name': 'test_received_file',
|
|
| 108 |
- 'request-id': '43', 'sid': '39',
|
|
| 109 |
- 'session-sid': '38', 'session-type': 'jingle',
|
|
| 110 |
- 'transfered_size': [], 'receiver':
|
|
| 111 |
- 'jingleft@thiessen.im/Gajim', 'desc': '',
|
|
| 112 |
- 'size': '2273', 'type': 'r',
|
|
| 113 |
- 'streamhosts': [{'initiator':
|
|
| 114 |
- 'jtest@thiessen.im/Gajim',
|
|
| 115 |
- 'target': 'jingleft@thiessen.im/Gajim',
|
|
| 116 |
- 'cid': '41', 'state': 0, 'host': '192.168.2.100',
|
|
| 117 |
- 'type': 'direct', 'port': '28011'},
|
|
| 118 |
- {'initiator': 'jtest@thiessen.im/Gajim',
|
|
| 119 |
- 'target': 'jingleft@thiessen.im/Gajim',
|
|
| 120 |
- 'cid': '42', 'state': 0, 'host': '192.168.2.100',
|
|
| 121 |
- 'type': 'proxy', 'port': '5000'}],
|
|
| 122 |
- 'name': 'to'}
|
|
| 123 |
- |
|
| 124 |
- def tearDown(self):
|
|
| 125 |
- # Unplug if needed
|
|
| 126 |
- if hasattr(self.dispatcher, '_owner'):
|
|
| 127 |
- self.dispatcher.PlugOut()
|
|
| 128 |
- |
|
| 129 |
- def _simulate_connect(self):
|
|
| 130 |
- self.dispatcher.PlugIn(self.client) # client is owner
|
|
| 131 |
- # Simulate that we have established a connection
|
|
| 132 |
- self.dispatcher.StreamInit()
|
|
| 133 |
- self.dispatcher.ProcessNonBlocking("<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client'>")
|
|
| 134 |
- |
|
| 135 |
- def _simulate_jingle_session(self):
|
|
| 136 |
- |
|
| 137 |
- self.dispatcher.RegisterHandler('iq', self.con._JingleCB, 'set',
|
|
| 138 |
- Namespace.JINGLE)
|
|
| 139 |
- self.dispatcher.ProcessNonBlocking(session_init)
|
|
| 140 |
- session = list(self.con._sessions.values())[0] # The only session we have
|
|
| 141 |
- jft = list(session.contents.values())[0] # jingleFT object
|
|
| 142 |
- jft.file_props = self.receive_file # We plug file_props manually
|
|
| 143 |
- # The user accepts to receive the file
|
|
| 144 |
- # we have to manually simulate this behavior
|
|
| 145 |
- session.approve_session()
|
|
| 146 |
- self.con.send_file_approval(self.receive_file)
|
|
| 147 |
- |
|
| 148 |
- self.dispatcher.ProcessNonBlocking(transport_info)
|
|
| 149 |
- |
|
| 150 |
- def test_jingle_session(self):
|
|
| 151 |
- self._simulate_connect()
|
|
| 152 |
- self._simulate_jingle_session()
|
|
| 153 |
- |
|
| 154 |
- |
|
| 155 |
-if __name__ == '__main__':
|
|
| 156 |
- unittest.main()
|
| 1 |
-import unittest
|
|
| 2 |
- |
|
| 3 |
-import lib
|
|
| 4 |
-lib.setup_env()
|
|
| 5 |
- |
|
| 6 |
-import notify
|
|
| 7 |
-import nbxmpp
|
|
| 8 |
- |
|
| 9 |
-from gajim.common import app
|
|
| 10 |
-from gajim.common import nec
|
|
| 11 |
-from gajim.common import ged
|
|
| 12 |
-from gajim.common.nec import NetworkEvent
|
|
| 13 |
- |
|
| 14 |
-from gajim.session import ChatControlSession
|
|
| 15 |
-from gajim.roster_window import RosterWindow
|
|
| 16 |
- |
|
| 17 |
-from gajim_mocks import *
|
|
| 18 |
-from data import account1
|
|
| 19 |
- |
|
| 20 |
-app.interface = MockInterface()
|
|
| 21 |
- |
|
| 22 |
- |
|
| 23 |
-# name to use for the test account
|
|
| 24 |
-account_name = account1
|
|
| 25 |
- |
|
| 26 |
- |
|
| 27 |
-class TestChatControlSession(unittest.TestCase):
|
|
| 28 |
- ''' Testclass for session.py '''
|
|
| 29 |
- |
|
| 30 |
- @classmethod
|
|
| 31 |
- def setUpClass(cls):
|
|
| 32 |
- app.nec = nec.NetworkEventsController()
|
|
| 33 |
- cls.conn = MockConnection(account_name, {'send_stanza': None})
|
|
| 34 |
- app.logger = MockLogger()
|
|
| 35 |
- app.default_session_type = ChatControlSession
|
|
| 36 |
- |
|
| 37 |
- def setUp(self):
|
|
| 38 |
- app.notification = notify.Notification()
|
|
| 39 |
- |
|
| 40 |
- # no notifications have been sent
|
|
| 41 |
- self.assertEqual(0, len(notify.notifications))
|
|
| 42 |
- |
|
| 43 |
- def tearDown(self):
|
|
| 44 |
- app.notification.clean()
|
|
| 45 |
- |
|
| 46 |
- def receive_chat_msg(self, jid, msgtxt):
|
|
| 47 |
- '''simulate receiving a chat message from jid'''
|
|
| 48 |
- msg = nbxmpp.Message()
|
|
| 49 |
- msg.setBody(msgtxt)
|
|
| 50 |
- msg.setType('chat')
|
|
| 51 |
- |
|
| 52 |
- xml = """<message from='%s' id='1' type='chat'><body>%s</body>
|
|
| 53 |
- <thread>123</thread></message>""" % (jid, msgtxt)
|
|
| 54 |
- stanza = nbxmpp.protocol.Message(node=nbxmpp.simplexml.XML2Node(xml))
|
|
| 55 |
- self.conn._messageCB(None, stanza)
|
|
| 56 |
- |
|
| 57 |
- # ----- custom assertions -----
|
|
| 58 |
- def assert_new_message_notification(self):
|
|
| 59 |
- '''a new_message notification has been sent'''
|
|
| 60 |
- self.assertEqual(1, len(notify.notifications))
|
|
| 61 |
- notif = notify.notifications[-1]
|
|
| 62 |
- self.assertEqual('New Message', notif.popup_event_type)
|
|
| 63 |
- |
|
| 64 |
- def assert_first_message_notification(self):
|
|
| 65 |
- '''this message was treated as a first message'''
|
|
| 66 |
- self.assert_new_message_notification()
|
|
| 67 |
- notif = notify.notifications[-1]
|
|
| 68 |
- first = notif.first_unread
|
|
| 69 |
- self.assertTrue(first,
|
|
| 70 |
- 'message should have been treated as a first message')
|
|
| 71 |
- |
|
| 72 |
- def assert_not_first_message_notification(self):
|
|
| 73 |
- '''this message was not treated as a first message'''
|
|
| 74 |
- self.assert_new_message_notification()
|
|
| 75 |
- notif = notify.notifications[-1]
|
|
| 76 |
- first = notif.first_unread
|
|
| 77 |
- self.assertTrue(not first,
|
|
| 78 |
- 'message was unexpectedly treated as a first message')
|
|
| 79 |
- |
|
| 80 |
- # ----- tests -----
|
|
| 81 |
- def test_receive_1nocontrol(self):
|
|
| 82 |
- '''test receiving a message in a blank state'''
|
|
| 83 |
- jid = 'bct@necronomicorp.com'
|
|
| 84 |
- fjid = 'bct@necronomicorp.com/Gajim'
|
|
| 85 |
- msgtxt = 'testing one'
|
|
| 86 |
- |
|
| 87 |
- self.receive_chat_msg(fjid, msgtxt)
|
|
| 88 |
- |
|
| 89 |
- # session is created
|
|
| 90 |
- self.assertTrue((jid in self.conn.sessions) and (
|
|
| 91 |
- '123' in self.conn.sessions[jid]), 'session is not created')
|
|
| 92 |
- sess = self.conn.sessions[jid]['123']
|
|
| 93 |
- |
|
| 94 |
- # message was logged
|
|
| 95 |
- calls = app.storage.archive.mockGetNamedCalls('insert_into_logs')
|
|
| 96 |
- self.assertEqual(1, len(calls))
|
|
| 97 |
- |
|
| 98 |
- # no ChatControl was open and autopopup was off
|
|
| 99 |
- # so the message goes into the event queue
|
|
| 100 |
- self.assertEqual(1, len(app.events.get_events(account_name)))
|
|
| 101 |
- |
|
| 102 |
- self.assert_first_message_notification()
|
|
| 103 |
- |
|
| 104 |
- # no control is attached to the session
|
|
| 105 |
- self.assertEqual(None, sess.control)
|
|
| 106 |
- |
|
| 107 |
- def test_receive_2already_has_control(self):
|
|
| 108 |
- '''test receiving a message with a session already attached to a
|
|
| 109 |
- control'''
|
|
| 110 |
- jid = 'bct@necronomicorp.com'
|
|
| 111 |
- fjid = 'bct@necronomicorp.com/Gajim'
|
|
| 112 |
- msgtxt = 'testing two'
|
|
| 113 |
- app.interface.roster = RosterWindow(app.app)
|
|
| 114 |
- |
|
| 115 |
- sess = self.conn.sessions[jid]['123']
|
|
| 116 |
- sess.control = MockChatControl(fjid, account_name)
|
|
| 117 |
- |
|
| 118 |
- self.receive_chat_msg(fjid, msgtxt)
|
|
| 119 |
- |
|
| 120 |
- # message was logged
|
|
| 121 |
- calls = app.storage.archive.mockGetNamedCalls('insert_into_logs')
|
|
| 122 |
- self.assertEqual(2, len(calls))
|
|
| 123 |
- |
|
| 124 |
- # the message does not go into the event queue
|
|
| 125 |
- self.assertEqual(1, len(app.events.get_events(account_name)))
|
|
| 126 |
- |
|
| 127 |
- self.assert_not_first_message_notification()
|
|
| 128 |
- |
|
| 129 |
- # message was printed to the control
|
|
| 130 |
- calls = sess.control.mockGetNamedCalls('print_conversation')
|
|
| 131 |
- self.assertEqual(1, len(calls))
|
|
| 132 |
- app.interface.roster.window.destroy()
|
|
| 133 |
- |
|
| 134 |
- #def test_received_3orphaned_control(self):
|
|
| 135 |
- #'''test receiving a message when a control that doesn't have a session
|
|
| 136 |
- #attached exists'''
|
|
| 137 |
- |
|
| 138 |
- #jid = 'bct@necronomicorp.com'
|
|
| 139 |
- #fjid = jid + '/Gajim'
|
|
| 140 |
- #msgtxt = 'testing three'
|
|
| 141 |
- |
|
| 142 |
- #ctrl = MockChatControl(jid, account_name)
|
|
| 143 |
- #gajim.interface.msg_win_mgr = Mock({'get_control': ctrl})
|
|
| 144 |
- #gajim.interface.msg_win_mgr.mockSetExpectation('get_control',
|
|
| 145 |
- #expectParams(jid, account_name))
|
|
| 146 |
- |
|
| 147 |
- #self.receive_chat_msg(fjid, msgtxt)
|
|
| 148 |
- |
|
| 149 |
- ## message was logged
|
|
| 150 |
- #calls = gajim.logger.mockGetNamedCalls('insert_into_logs')
|
|
| 151 |
- #self.assertEqual(1, len(calls))
|
|
| 152 |
- |
|
| 153 |
- ## the message does not go into the event queue
|
|
| 154 |
- #self.assertEqual(0, len(gajim.events.get_events(account_name)))
|
|
| 155 |
- |
|
| 156 |
- #self.assert_not_first_message_notification()
|
|
| 157 |
- |
|
| 158 |
- ## this session is now attached to that control
|
|
| 159 |
- #self.assertEqual(self.sess, ctrl.session)
|
|
| 160 |
- #self.assertEqual(ctrl, self.sess.control, 'foo')
|
|
| 161 |
- |
|
| 162 |
- ## message was printed to the control
|
|
| 163 |
- #calls = ctrl.mockGetNamedCalls('print_conversation')
|
|
| 164 |
- #self.assertEqual(1, len(calls))
|
|
| 165 |
- |
|
| 166 |
-if __name__ == '__main__':
|
|
| 167 |
- unittest.main()
|
| 1 |
-'''
|
|
| 2 |
-Tests for dispatcher.py
|
|
| 3 |
-'''
|
|
| 4 |
-import unittest
|
|
| 5 |
- |
|
| 6 |
-import lib
|
|
| 7 |
-lib.setup_env()
|
|
| 8 |
- |
|
| 9 |
-from mock import Mock
|
|
| 10 |
-import sys
|
|
| 11 |
-import socket
|
|
| 12 |
- |
|
| 13 |
-from gajim.common.socks5 import *
|
|
| 14 |
-from gajim.common import jingle_xtls
|
|
| 15 |
- |
|
| 16 |
-class fake_sock(Mock):
|
|
| 17 |
- def __init__(self, sockobj):
|
|
| 18 |
- Mock.__init__(self)
|
|
| 19 |
- |
|
| 20 |
- self.sockobj = sockobj
|
|
| 21 |
- |
|
| 22 |
- |
|
| 23 |
- def setup_stream(self):
|
|
| 24 |
- sha1 = self.sockobj._get_sha1_auth()
|
|
| 25 |
- |
|
| 26 |
- self.incoming = []
|
|
| 27 |
- self.incoming.append(self.sockobj._get_auth_response())
|
|
| 28 |
- self.incoming.append(self.sockobj._get_request_buff(sha1, 0x00))
|
|
| 29 |
- self.outgoing = []
|
|
| 30 |
- self.outgoing.append(self.sockobj._get_auth_buff())
|
|
| 31 |
- self.outgoing.append(self.sockobj._get_request_buff(sha1))
|
|
| 32 |
- |
|
| 33 |
- def switch_stream(self):
|
|
| 34 |
- # Roles are reversed, client will be expecting server stream
|
|
| 35 |
- # and server will be expecting client stream
|
|
| 36 |
- |
|
| 37 |
- temp = self.incoming
|
|
| 38 |
- self.incoming = self.outgoing
|
|
| 39 |
- self.outgoing = temp
|
|
| 40 |
- |
|
| 41 |
- def _recv(self, foo):
|
|
| 42 |
- return self.incoming.pop(0)
|
|
| 43 |
- |
|
| 44 |
- def _send(self, data):
|
|
| 45 |
- # This method is surrounded by a try block,
|
|
| 46 |
- # we can't use assert here
|
|
| 47 |
- |
|
| 48 |
- if data != self.outgoing[0]:
|
|
| 49 |
- print('FAILED SENDING TEST')
|
|
| 50 |
- self.outgoing.pop(0)
|
|
| 51 |
- |
|
| 52 |
-class fake_idlequeue(Mock):
|
|
| 53 |
- |
|
| 54 |
- def __init__(self):
|
|
| 55 |
- Mock.__init__(self)
|
|
| 56 |
- |
|
| 57 |
- def plug_idle(self, obj, writable=True, readable=True):
|
|
| 58 |
- |
|
| 59 |
- if readable:
|
|
| 60 |
- obj.pollin()
|
|
| 61 |
- if writable:
|
|
| 62 |
- obj.pollout()
|
|
| 63 |
- |
|
| 64 |
-class TestSocks5(unittest.TestCase):
|
|
| 65 |
- '''
|
|
| 66 |
- Test class for Socks5
|
|
| 67 |
- '''
|
|
| 68 |
- def setUp(self):
|
|
| 69 |
- streamhost = { 'host': None,
|
|
| 70 |
- 'port': 1,
|
|
| 71 |
- 'initiator' : None,
|
|
| 72 |
- 'target' : None}
|
|
| 73 |
- queue = Mock()
|
|
| 74 |
- queue.file_props = {}
|
|
| 75 |
- #self.sockobj = Socks5Receiver(fake_idlequeue(), streamhost, None)
|
|
| 76 |
- self.sockobj = Socks5Sender(fake_idlequeue(), None, 'server', Mock() ,
|
|
| 77 |
- None, None, True, file_props={})
|
|
| 78 |
- sock = fake_sock(self.sockobj)
|
|
| 79 |
- self.sockobj._sock = sock
|
|
| 80 |
- self.sockobj._recv = sock._recv
|
|
| 81 |
- self.sockobj._send = sock._send
|
|
| 82 |
- self.sockobj.state = 1
|
|
| 83 |
- self.sockobj.connected = True
|
|
| 84 |
- self.sockobj.pollend = self._pollend
|
|
| 85 |
- |
|
| 86 |
- # Something that the receiver needs
|
|
| 87 |
- #self.sockobj.file_props['type'] = 'r'
|
|
| 88 |
- |
|
| 89 |
- # Something that the sender needs
|
|
| 90 |
- self.sockobj.file_props = {}
|
|
| 91 |
- self.sockobj.file_props['type'] = 'r'
|
|
| 92 |
- self.sockobj.file_props['paused'] = ''
|
|
| 93 |
- self.sockobj.queue = Mock()
|
|
| 94 |
- self.sockobj.queue.process_result = self._pollend
|
|
| 95 |
- |
|
| 96 |
- def _pollend(self, foo = None, duu = None):
|
|
| 97 |
- # This is a disconnect function
|
|
| 98 |
- sys.exit("end of the road")
|
|
| 99 |
- |
|
| 100 |
- def _check_inout(self):
|
|
| 101 |
- # Check if there isn't anything else to receive or send
|
|
| 102 |
- sock = self.sockobj._sock
|
|
| 103 |
- assert(sock.incoming == [])
|
|
| 104 |
- assert(sock.outgoing == [])
|
|
| 105 |
- |
|
| 106 |
- def test_connection_server(self):
|
|
| 107 |
- return
|
|
| 108 |
- mocksock = self.sockobj._sock
|
|
| 109 |
- mocksock.setup_stream()
|
|
| 110 |
- #self.sockobj._sock.switch_stream()
|
|
| 111 |
- s = socket.socket(2, 1, 6)
|
|
| 112 |
- server = ('127.0.0.1', 28000)
|
|
| 113 |
- |
|
| 114 |
- s.connect(server)
|
|
| 115 |
- |
|
| 116 |
- s.send(mocksock.outgoing.pop(0))
|
|
| 117 |
- self.assertEqual(s.recv(64), mocksock.incoming.pop(0))
|
|
| 118 |
- |
|
| 119 |
- s.send(mocksock.outgoing.pop(0))
|
|
| 120 |
- self.assertEqual(s.recv(64), mocksock.incoming.pop(0))
|
|
| 121 |
- |
|
| 122 |
- def test_connection_client(self):
|
|
| 123 |
- |
|
| 124 |
- |
|
| 125 |
- mocksock = self.sockobj._sock
|
|
| 126 |
- mocksock.setup_stream()
|
|
| 127 |
- mocksock.switch_stream()
|
|
| 128 |
- s = socket.socket(10, 1, 6)
|
|
| 129 |
- |
|
| 130 |
- |
|
| 131 |
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
| 132 |
- s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
| 133 |
- s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
| 134 |
- |
|
| 135 |
- netadd = ('::', 28000, 0, 0)
|
|
| 136 |
- s.bind(netadd)
|
|
| 137 |
- s.listen(socket.SOMAXCONN)
|
|
| 138 |
- (s, address) = s.accept()
|
|
| 139 |
- |
|
| 140 |
- |
|
| 141 |
- self.assertEqual(s.recv(64), mocksock.incoming.pop(0))
|
|
| 142 |
- s.send(mocksock.outgoing.pop(0))
|
|
| 143 |
- |
|
| 144 |
- buff = s.recv(64)
|
|
| 145 |
- inco = mocksock.incoming.pop(0)
|
|
| 146 |
- #self.assertEqual(s.recv(64), mocksock.incoming.pop(0))
|
|
| 147 |
- s.send(mocksock.outgoing.pop(0))
|
|
| 148 |
- |
|
| 149 |
- def test_client_negoc(self):
|
|
| 150 |
- return
|
|
| 151 |
- self.sockobj._sock.setup_stream()
|
|
| 152 |
- try:
|
|
| 153 |
- self.sockobj.pollout()
|
|
| 154 |
- except SystemExit:
|
|
| 155 |
- pass
|
|
| 156 |
- |
|
| 157 |
- self._check_inout()
|
|
| 158 |
- |
|
| 159 |
- def test_server_negoc(self):
|
|
| 160 |
- return
|
|
| 161 |
- self.sockobj._sock.setup_stream()
|
|
| 162 |
- self.sockobj._sock.switch_stream()
|
|
| 163 |
- try:
|
|
| 164 |
- self.sockobj.idlequeue.plug_idle(self.sockobj, False, True)
|
|
| 165 |
- except SystemExit:
|
|
| 166 |
- pass
|
|
| 167 |
- self._check_inout()
|
|
| 168 |
- |
|
| 169 |
- |
|
| 170 |
-if __name__ == '__main__':
|
|
| 171 |
- |
|
| 172 |
- unittest.main()
|
| 1 |
-import os
|
|
| 2 |
- |
|
| 3 |
-from tempfile import gettempdir
|
|
| 4 |
- |
|
| 5 |
-# a temporary version of ~/.gajim for testing
|
|
| 6 |
-configdir = os.path.join(gettempdir(), 'gajim')
|
|
| 7 |
-os.makedirs(configdir, exist_ok=True)
|
|
| 8 |
- |
|
| 9 |
-# plugins config dir
|
|
| 10 |
-pluginsconfigdir = configdir + '/pluginsconfig'
|
|
| 11 |
-# theme config directory
|
|
| 12 |
-themedir = configdir + '/theme'
|
|
| 13 |
- |
|
| 14 |
-# define _ for i18n
|
|
| 15 |
-import builtins
|
|
| 16 |
-builtins._ = lambda x: x
|
|
| 17 |
- |
|
| 18 |
-from gajim.common.contacts import LegacyContactsAPI
|
|
| 19 |
- |
|
| 20 |
-def setup_env(use_x=True):
|
|
| 21 |
- # wipe config directory
|
|
| 22 |
- if os.path.isdir(configdir):
|
|
| 23 |
- import shutil
|
|
| 24 |
- shutil.rmtree(configdir)
|
|
| 25 |
- |
|
| 26 |
- os.mkdir(configdir)
|
|
| 27 |
- os.mkdir(pluginsconfigdir)
|
|
| 28 |
- os.mkdir(themedir)
|
|
| 29 |
- |
|
| 30 |
- from gajim.common import configpaths
|
|
| 31 |
- configpaths.set_config_root(configdir)
|
|
| 32 |
- configpaths.init()
|
|
| 33 |
- |
|
| 34 |
- # for some reason gajim.common.app needs to be imported before xmpppy?
|
|
| 35 |
- from gajim.common import app
|
|
| 36 |
- |
|
| 37 |
- import logging
|
|
| 38 |
- logging.basicConfig()
|
|
| 39 |
- |
|
| 40 |
- app.use_x = use_x
|
|
| 41 |
- app.contacts = LegacyContactsAPI()
|
|
| 42 |
- app.connections = {}
|
|
| 43 |
- |
|
| 44 |
- if use_x:
|
|
| 45 |
- from gajim.application import GajimApplication
|
|
| 46 |
- app.app = GajimApplication()
|
| 1 |
-# -*- coding: utf-8 -*-
|
|
| 2 |
-account1 = 'acc1'
|
|
| 3 |
-account2 = 'Cool"chârßéµö'
|
|
| 4 |
-account3 = 'dingdong.org'
|
|
| 5 |
- |
|
| 6 |
-contacts = {}
|
|
| 7 |
-contacts[account1] = {
|
|
| 8 |
- 'myjid@'+account1: {
|
|
| 9 |
- 'ask': None, 'groups': [], 'name': None, 'resources': {},
|
|
| 10 |
- 'subscription': 'both'},
|
|
| 11 |
- 'default1@gajim.org': {
|
|
| 12 |
- 'ask': None, 'groups': [], 'name': None, 'resources': {},
|
|
| 13 |
- 'subscription': 'both'},
|
|
| 14 |
- 'default2@gajim.org': {
|
|
| 15 |
- 'ask': None, 'groups': ['GroupA',], 'name': None, 'resources': {},
|
|
| 16 |
- 'subscription': 'both'},
|
|
| 17 |
- 'Cool"chârßéµö@gajim.org': {
|
|
| 18 |
- 'ask': None, 'groups': ['<Cool"chârßéµö', 'GroupB'],
|
|
| 19 |
- 'name': None, 'resources': {}, 'subscription': 'both'},
|
|
| 20 |
- 'samejid@gajim.org': {
|
|
| 21 |
- 'ask': None, 'groups': ['GroupA',], 'name': None, 'resources': {},
|
|
| 22 |
- 'subscription': 'both'}
|
|
| 23 |
-}
|
|
| 24 |
-contacts[account2] = {
|
|
| 25 |
- 'myjid@'+account2: {
|
|
| 26 |
- 'ask': None, 'groups': [], 'name': None, 'resources': {},
|
|
| 27 |
- 'subscription': 'both'},
|
|
| 28 |
- 'default3@gajim.org': {
|
|
| 29 |
- 'ask': None, 'groups': ['GroupC',], 'name': None, 'resources': {},
|
|
| 30 |
- 'subscription': 'both'},
|
|
| 31 |
- 'asksubfrom@gajim.org': {
|
|
| 32 |
- 'ask': 'subscribe', 'groups': ['GroupA',], 'name': None,
|
|
| 33 |
- 'resources': {}, 'subscription': 'from'},
|
|
| 34 |
- 'subto@gajim.org': {
|
|
| 35 |
- 'ask': None, 'groups': ['GroupB'], 'name': None, 'resources': {},
|
|
| 36 |
- 'subscription': 'to'},
|
|
| 37 |
- 'samejid@gajim.org': {
|
|
| 38 |
- 'ask': None, 'groups': ['GroupA', 'GroupB'], 'name': None,
|
|
| 39 |
- 'resources': {}, 'subscription': 'both'}
|
|
| 40 |
-}
|
|
| 41 |
-contacts[account3] = {
|
|
| 42 |
- #'guypsych0\\40h.com@msn.dingdong.org': {
|
|
| 43 |
- # 'ask': None, 'groups': [], 'name': None, 'resources': {},
|
|
| 44 |
- # 'subscription': 'both'},
|
|
| 45 |
- 'guypsych0%h.com@msn.delx.cjb.net': {
|
|
| 46 |
- 'ask': 'subscribe', 'groups': [], 'name': None,
|
|
| 47 |
- 'resources': {}, 'subscription': 'from'},
|
|
| 48 |
- #'guypsych0%h.com@msn.jabber.wiretrip.org': {
|
|
| 49 |
- # 'ask': None, 'groups': [], 'name': None, 'resources': {},
|
|
| 50 |
- # 'subscription': 'to'},
|
|
| 51 |
- #'guypsycho\\40g.com@gtalk.dingdong.org': {
|
|
| 52 |
- # 'ask': None, 'groups': [], 'name': None,
|
|
| 53 |
- # 'resources': {}, 'subscription': 'both'}
|
|
| 54 |
-}
|
|
| 55 |
- |
|
| 56 |
-# We have contacts that are not in roster but only specified in the metadata
|
|
| 57 |
-metacontact_data = [
|
|
| 58 |
- [{'account': account3,
|
|
| 59 |
- 'jid': 'guypsych0\\40h.com@msn.dingdong.org',
|
|
| 60 |
- 'order': 0},
|
|
| 61 |
- {'account': account3,
|
|
| 62 |
- 'jid': 'guypsych0%h.com@msn.delx.cjb.net',
|
|
| 63 |
- 'order': 0},
|
|
| 64 |
- {'account': account3,
|
|
| 65 |
- 'jid': 'guypsych0%h.com@msn.jabber.wiretrip.org',
|
|
| 66 |
- 'order': 0},
|
|
| 67 |
- {'account': account3,
|
|
| 68 |
- 'jid': 'guypsycho\\40g.com@gtalk.dingdong.org',
|
|
| 69 |
- 'order': 0}],
|
|
| 70 |
- |
|
| 71 |
- [{'account': account1,
|
|
| 72 |
- 'jid': 'samejid@gajim.org',
|
|
| 73 |
- 'order': 0},
|
|
| 74 |
- {'account': account2,
|
|
| 75 |
- 'jid': 'samejid@gajim.org',
|
|
| 76 |
- 'order': 0}]
|
|
| 77 |
- ]
|
| 1 |
-'''
|
|
| 2 |
-Module with dummy classes for Gajim specific unit testing
|
|
| 3 |
-'''
|
|
| 4 |
- |
|
| 5 |
-from .mock import Mock
|
|
| 6 |
-from gajim.common import app
|
|
| 7 |
-from gajim.common import ged
|
|
| 8 |
- |
|
| 9 |
-from gajim.common.connection_handlers import ConnectionHandlers
|
|
| 10 |
- |
|
| 11 |
-class MockConnection(Mock, ConnectionHandlers):
|
|
| 12 |
- def __init__(self, account, *args):
|
|
| 13 |
- Mock.__init__(self, *args)
|
|
| 14 |
- |
|
| 15 |
- self.connection = Mock()
|
|
| 16 |
- |
|
| 17 |
- self.name = account
|
|
| 18 |
- |
|
| 19 |
- ConnectionHandlers.__init__(self)
|
|
| 20 |
- |
|
| 21 |
- self.connected = 2
|
|
| 22 |
- self.pep = {}
|
|
| 23 |
- self.sessions = {}
|
|
| 24 |
- self.server_resource = 'Gajim'
|
|
| 25 |
- |
|
| 26 |
- app.interface.instances[account] = {
|
|
| 27 |
- 'infos': {},
|
|
| 28 |
- 'disco': {}
|
|
| 29 |
- }
|
|
| 30 |
- app.interface.minimized_controls[account] = {}
|
|
| 31 |
- app.contacts.add_account(account)
|
|
| 32 |
- app.groups[account] = {}
|
|
| 33 |
- app.gc_connected[account] = {}
|
|
| 34 |
- app.automatic_rooms[account] = {}
|
|
| 35 |
- app.newly_added[account] = []
|
|
| 36 |
- app.to_be_removed[account] = []
|
|
| 37 |
- app.nicks[account] = app.settings.get_account_setting(account, 'name')
|
|
| 38 |
- app.block_signed_in_notifications[account] = True
|
|
| 39 |
- |
|
| 40 |
- app.connections[account] = self
|
|
| 41 |
- |
|
| 42 |
-class MockWindow(Mock):
|
|
| 43 |
- def __init__(self, *args):
|
|
| 44 |
- Mock.__init__(self, *args)
|
|
| 45 |
- self.window = Mock()
|
|
| 46 |
- self._controls = {}
|
|
| 47 |
- |
|
| 48 |
- def get_control(self, jid, account):
|
|
| 49 |
- try:
|
|
| 50 |
- return self._controls[account][jid]
|
|
| 51 |
- except KeyError:
|
|
| 52 |
- return None
|
|
| 53 |
- |
|
| 54 |
- def has_control(self, jid, acct):
|
|
| 55 |
- return self.get_control(jid, acct) is not None
|
|
| 56 |
- |
|
| 57 |
- def new_tab(self, ctrl):
|
|
| 58 |
- account = ctrl.account
|
|
| 59 |
- jid = ctrl.jid
|
|
| 60 |
- |
|
| 61 |
- if account not in self._controls:
|
|
| 62 |
- self._controls[account] = {}
|
|
| 63 |
- |
|
| 64 |
- if jid not in self._controls[account]:
|
|
| 65 |
- self._controls[account][jid] = {}
|
|
| 66 |
- |
|
| 67 |
- self._controls[account][jid] = ctrl
|
|
| 68 |
- |
|
| 69 |
- def __nonzero__(self):
|
|
| 70 |
- return True
|
|
| 71 |
- |
|
| 72 |
-class MockChatControl(Mock):
|
|
| 73 |
- def __init__(self, jid, account, *args):
|
|
| 74 |
- Mock.__init__(self, *args)
|
|
| 75 |
- |
|
| 76 |
- self.jid = jid
|
|
| 77 |
- self.account = account
|
|
| 78 |
- |
|
| 79 |
- self.parent_win = MockWindow({'get_active_control': self})
|
|
| 80 |
- self.session = None
|
|
| 81 |
- |
|
| 82 |
- def set_session(self, sess):
|
|
| 83 |
- self.session = sess
|
|
| 84 |
- |
|
| 85 |
- def __nonzero__(self):
|
|
| 86 |
- return True
|
|
| 87 |
- |
|
| 88 |
- def __eq__(self, other):
|
|
| 89 |
- return self is other
|
|
| 90 |
- |
|
| 91 |
- |
|
| 92 |
-class MockInterface(Mock):
|
|
| 93 |
- def __init__(self, *args):
|
|
| 94 |
- Mock.__init__(self, *args)
|
|
| 95 |
- app.interface = self
|
|
| 96 |
- self.msg_win_mgr = Mock()
|
|
| 97 |
- self.roster = Mock()
|
|
| 98 |
- app.ged = ged.GlobalEventsDispatcher()
|
|
| 99 |
- from gajim import plugins
|
|
| 100 |
- app.plugin_manager = plugins.PluginManager()
|
|
| 101 |
- |
|
| 102 |
- self.instances = {}
|
|
| 103 |
- self.minimized_controls = {}
|
|
| 104 |
- |
|
| 105 |
- |
|
| 106 |
-class MockLogger(Mock):
|
|
| 107 |
- def __init__(self):
|
|
| 108 |
- Mock.__init__(self, {'insert_into_logs': None,
|
|
| 109 |
- 'get_transports_type': {}})
|
|
| 110 |
- self.cur = Mock()
|
|
| 111 |
- |
|
| 112 |
- |
|
| 113 |
-class MockContact(Mock):
|
|
| 114 |
- def __nonzero__(self):
|
|
| 115 |
- return True
|
|
| 116 |
- |
|
| 117 |
- |
|
| 118 |
-import random
|
|
| 119 |
- |
|
| 120 |
-class MockSession(Mock):
|
|
| 121 |
- def __init__(self, conn, jid, thread_id, type_):
|
|
| 122 |
- Mock.__init__(self)
|
|
| 123 |
- |
|
| 124 |
- self.conn = conn
|
|
| 125 |
- self.jid = jid
|
|
| 126 |
- self.type_ = type_
|
|
| 127 |
- self.thread_id = thread_id
|
|
| 128 |
- self.resource = ''
|
|
| 129 |
- |
|
| 130 |
- if not self.thread_id:
|
|
| 131 |
- self.thread_id = '%0x' % random.randint(0, 10000)
|
|
| 132 |
- |
|
| 133 |
- def __repr__(self):
|
|
| 134 |
- return '<MockSession %s>' % self.thread_id
|
|
| 135 |
- |
|
| 136 |
- def __nonzero__(self):
|
|
| 137 |
- return True
|
|
| 138 |
- |
|
| 139 |
- def __eq__(self, other):
|
|
| 140 |
- return self is other
|
| 1 |
-#
|
|
| 2 |
-# (c) Dave Kirby 2001 - 2005
|
|
| 3 |
-# mock@thedeveloperscoach.com
|
|
| 4 |
-#
|
|
| 5 |
-# Original call interceptor and call assertion code by Phil Dawes (pdawes@users.sourceforge.net)
|
|
| 6 |
-# Call interceptor code enhanced by Bruce Cropley (cropleyb@yahoo.com.au)
|
|
| 7 |
-#
|
|
| 8 |
-# This Python module and associated files are released under the FreeBSD
|
|
| 9 |
-# license. Essentially, you can do what you like with it except pretend you wrote
|
|
| 10 |
-# it yourself.
|
|
| 11 |
-#
|
|
| 12 |
-#
|
|
| 13 |
-# Copyright (c) 2005, Dave Kirby
|
|
| 14 |
-# Copyright (c) 2009, Yann Leboulanger
|
|
| 15 |
-#
|
|
| 16 |
-# All rights reserved.
|
|
| 17 |
-#
|
|
| 18 |
-# Redistribution and use in source and binary forms, with or without
|
|
| 19 |
-# modification, are permitted provided that the following conditions are met:
|
|
| 20 |
-#
|
|
| 21 |
-# * Redistributions of source code must retain the above copyright
|
|
| 22 |
-# notice, this list of conditions and the following disclaimer.
|
|
| 23 |
-#
|
|
| 24 |
-# * Redistributions in binary form must reproduce the above copyright
|
|
| 25 |
-# notice, this list of conditions and the following disclaimer in the
|
|
| 26 |
-# documentation and/or other materials provided with the distribution.
|
|
| 27 |
-#
|
|
| 28 |
-# * Neither the name of this library nor the names of its
|
|
| 29 |
-# contributors may be used to endorse or promote products derived from
|
|
| 30 |
-# this software without specific prior written permission.
|
|
| 31 |
-#
|
|
| 32 |
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
| 33 |
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
| 34 |
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
| 35 |
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
| 36 |
-# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
| 37 |
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
| 38 |
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
| 39 |
-# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
| 40 |
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
| 41 |
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
| 42 |
-#
|
|
| 43 |
-# mock@thedeveloperscoach.com
|
|
| 44 |
- |
|
| 45 |
- |
|
| 46 |
-"""
|
|
| 47 |
-Mock object library for Python. Mock objects can be used when unit testing
|
|
| 48 |
-to remove a dependency on another production class. They are typically used
|
|
| 49 |
-when the dependency would either pull in lots of other classes, or
|
|
| 50 |
-significantly slow down the execution of the test.
|
|
| 51 |
-They are also used to create exceptional conditions that cannot otherwise
|
|
| 52 |
-be easily triggered in the class under test.
|
|
| 53 |
-"""
|
|
| 54 |
- |
|
| 55 |
-__version__ = "0.1.0"
|
|
| 56 |
- |
|
| 57 |
-# Added in Python 2.1
|
|
| 58 |
-import inspect
|
|
| 59 |
-import re
|
|
| 60 |
- |
|
| 61 |
-class MockInterfaceError(Exception):
|
|
| 62 |
- pass
|
|
| 63 |
- |
|
| 64 |
-class Mock(object):
|
|
| 65 |
- """
|
|
| 66 |
- The Mock class emulates any other class for testing purposes.
|
|
| 67 |
- All method calls are stored for later examination.
|
|
| 68 |
- """
|
|
| 69 |
- |
|
| 70 |
- def __init__(self, returnValues=None, realClass=None):
|
|
| 71 |
- """
|
|
| 72 |
- The Mock class constructor takes a dictionary of method names and
|
|
| 73 |
- the values they return. Methods that are not in the returnValues
|
|
| 74 |
- dictionary will return None.
|
|
| 75 |
- You may also supply a class whose interface is being mocked.
|
|
| 76 |
- All calls will be checked to see if they appear in the original
|
|
| 77 |
- interface. Any calls to methods not appearing in the real class
|
|
| 78 |
- will raise a MockInterfaceError. Any calls that would fail due to
|
|
| 79 |
- non-matching parameter lists will also raise a MockInterfaceError.
|
|
| 80 |
- Both of these help to prevent the Mock class getting out of sync
|
|
| 81 |
- with the class it is Mocking.
|
|
| 82 |
- """
|
|
| 83 |
- self.mockCalledMethods = {}
|
|
| 84 |
- self.mockAllCalledMethods = []
|
|
| 85 |
- self.mockReturnValues = returnValues or {}
|
|
| 86 |
- self.mockExpectations = {}
|
|
| 87 |
- self.realClass = realClass
|
|
| 88 |
- self.realClassMethods = None
|
|
| 89 |
- if realClass:
|
|
| 90 |
- self.realClassMethods = dict(inspect.getmembers(realClass, inspect.isroutine))
|
|
| 91 |
- for retMethod in self.mockReturnValues.keys():
|
|
| 92 |
- if retMethod not in self.realClassMethods:
|
|
| 93 |
- raise MockInterfaceError("Return value supplied for method '%s' that was not in the original class" % retMethod)
|
|
| 94 |
- self._setupSubclassMethodInterceptors()
|
|
| 95 |
- |
|
| 96 |
- def _setupSubclassMethodInterceptors(self):
|
|
| 97 |
- methods = inspect.getmembers(self.realClass, inspect.isroutine)
|
|
| 98 |
- baseMethods = dict(inspect.getmembers(Mock, inspect.ismethod))
|
|
| 99 |
- for m in methods:
|
|
| 100 |
- name = m[0]
|
|
| 101 |
- # Don't record calls to methods of Mock base class.
|
|
| 102 |
- if not name in baseMethods:
|
|
| 103 |
- self.__dict__[name] = MockCallable(name, self, handcrafted=True)
|
|
| 104 |
- |
|
| 105 |
- def get_module(self, name):
|
|
| 106 |
- return Mock()
|
|
| 107 |
- |
|
| 108 |
- def __getattr__(self, name):
|
|
| 109 |
- return MockCallable(name, self)
|
|
| 110 |
- |
|
| 111 |
- def mockAddReturnValues(self, **methodReturnValues ):
|
|
| 112 |
- self.mockReturnValues.update(methodReturnValues)
|
|
| 113 |
- |
|
| 114 |
- def mockSetExpectation(self, name, testFn, after=0, until=0):
|
|
| 115 |
- self.mockExpectations.setdefault(name, []).append((testFn, after, until))
|
|
| 116 |
- |
|
| 117 |
- def _checkInterfaceCall(self, name, callParams, callKwParams):
|
|
| 118 |
- """
|
|
| 119 |
- Check that a call to a method of the given name to the original
|
|
| 120 |
- class with the given parameters would not fail. If it would fail,
|
|
| 121 |
- raise a MockInterfaceError.
|
|
| 122 |
- Based on the Python 2.3.3 Reference Manual section 5.3.4: Calls.
|
|
| 123 |
- """
|
|
| 124 |
- if self.realClassMethods is None:
|
|
| 125 |
- return
|
|
| 126 |
- if name not in self.realClassMethods:
|
|
| 127 |
- return
|
|
| 128 |
- |
|
| 129 |
- func = self.realClassMethods[name]
|
|
| 130 |
- try:
|
|
| 131 |
- args, varargs, varkw, defaults = inspect.getargspec(func)
|
|
| 132 |
- except TypeError:
|
|
| 133 |
- # func is not a Python function. It is probably a builtin,
|
|
| 134 |
- # such as __repr__ or __coerce__. TODO: Checking?
|
|
| 135 |
- # For now assume params are OK.
|
|
| 136 |
- return
|
|
| 137 |
- |
|
| 138 |
- # callParams doesn't include self; args does include self.
|
|
| 139 |
- numPosCallParams = 1 + len(callParams)
|
|
| 140 |
- |
|
| 141 |
- if numPosCallParams > len(args) and not varargs:
|
|
| 142 |
- raise MockInterfaceError("Original %s() takes at most %s arguments (%s given)" %
|
|
| 143 |
- (name, len(args), numPosCallParams))
|
|
| 144 |
- |
|
| 145 |
- # Get the number of positional arguments that appear in the call,
|
|
| 146 |
- # also check for duplicate parameters and unknown parameters
|
|
| 147 |
- numPosSeen = _getNumPosSeenAndCheck(numPosCallParams, callKwParams, args, varkw)
|
|
| 148 |
- |
|
| 149 |
- lenArgsNoDefaults = len(args) - len(defaults or [])
|
|
| 150 |
- if numPosSeen < lenArgsNoDefaults:
|
|
| 151 |
- raise MockInterfaceError("Original %s() takes at least %s arguments (%s given)" % (name, lenArgsNoDefaults, numPosSeen))
|
|
| 152 |
- |
|
| 153 |
- def mockGetAllCalls(self):
|
|
| 154 |
- """
|
|
| 155 |
- Return a list of MockCall objects,
|
|
| 156 |
- representing all the methods in the order they were called.
|
|
| 157 |
- """
|
|
| 158 |
- return self.mockAllCalledMethods
|
|
| 159 |
- getAllCalls = mockGetAllCalls # deprecated - kept for backward compatibility
|
|
| 160 |
- |
|
| 161 |
- def mockGetNamedCalls(self, methodName):
|
|
| 162 |
- """
|
|
| 163 |
- Return a list of MockCall objects,
|
|
| 164 |
- representing all the calls to the named method in the order they were called.
|
|
| 165 |
- """
|
|
| 166 |
- return self.mockCalledMethods.get(methodName, [])
|
|
| 167 |
- getNamedCalls = mockGetNamedCalls # deprecated - kept for backward compatibility
|
|
| 168 |
- |
|
| 169 |
- def mockCheckCall(self, index, name, *args, **kwargs):
|
|
| 170 |
- '''test that the index-th call had the specified name and parameters'''
|
|
| 171 |
- call = self.mockAllCalledMethods[index]
|
|
| 172 |
- assert name == call.getName(), "%r != %r" % (name, call.getName())
|
|
| 173 |
- call.checkArgs(*args, **kwargs)
|
|
| 174 |
- |
|
| 175 |
- |
|
| 176 |
-def _getNumPosSeenAndCheck(numPosCallParams, callKwParams, args, varkw):
|
|
| 177 |
- """
|
|
| 178 |
- Positional arguments can appear as call parameters either named as
|
|
| 179 |
- a named (keyword) parameter, or just as a value to be matched by
|
|
| 180 |
- position. Count the positional arguments that are given by either
|
|
| 181 |
- keyword or position, and check for duplicate specifications.
|
|
| 182 |
- Also check for arguments specified by keyword that do not appear
|
|
| 183 |
- in the method's parameter list.
|
|
| 184 |
- """
|
|
| 185 |
- posSeen = {}
|
|
| 186 |
- for arg in args[:numPosCallParams]:
|
|
| 187 |
- posSeen[arg] = True
|
|
| 188 |
- for kwp in callKwParams:
|
|
| 189 |
- if kwp in posSeen:
|
|
| 190 |
- raise MockInterfaceError("%s appears as both a positional and named parameter." % kwp)
|
|
| 191 |
- if kwp in args:
|
|
| 192 |
- posSeen[kwp] = True
|
|
| 193 |
- elif not varkw:
|
|
| 194 |
- raise MockInterfaceError("Original method does not have a parameter '%s'" % kwp)
|
|
| 195 |
- return len(posSeen)
|
|
| 196 |
- |
|
| 197 |
-class MockCall:
|
|
| 198 |
- """
|
|
| 199 |
- MockCall records the name and parameters of a call to an instance
|
|
| 200 |
- of a Mock class. Instances of MockCall are created by the Mock class,
|
|
| 201 |
- but can be inspected later as part of the test.
|
|
| 202 |
- """
|
|
| 203 |
- def __init__(self, name, params, kwparams ):
|
|
| 204 |
- self.name = name
|
|
| 205 |
- self.params = params
|
|
| 206 |
- self.kwparams = kwparams
|
|
| 207 |
- |
|
| 208 |
- def checkArgs(self, *args, **kwargs):
|
|
| 209 |
- assert args == self.params, "%r != %r" % (args, self.params)
|
|
| 210 |
- assert kwargs == self.kwparams, "%r != %r" % (kwargs, self.kwparams)
|
|
| 211 |
- |
|
| 212 |
- def getParam( self, n ):
|
|
| 213 |
- if isinstance(n, int):
|
|
| 214 |
- return self.params[n]
|
|
| 215 |
- elif isinstance(n, str):
|
|
| 216 |
- return self.kwparams[n]
|
|
| 217 |
- else:
|
|
| 218 |
- raise IndexError('illegal index type for getParam')
|
|
| 219 |
- |
|
| 220 |
- def getNumParams(self):
|
|
| 221 |
- return len(self.params)
|
|
| 222 |
- |
|
| 223 |
- def getNumKwParams(self):
|
|
| 224 |
- return len(self.kwparams)
|
|
| 225 |
- |
|
| 226 |
- def getName(self):
|
|
| 227 |
- return self.name
|
|
| 228 |
- |
|
| 229 |
- #pretty-print the method call
|
|
| 230 |
- def __str__(self):
|
|
| 231 |
- s = self.name + "("
|
|
| 232 |
- sep = ''
|
|
| 233 |
- for p in self.params:
|
|
| 234 |
- s = s + sep + repr(p)
|
|
| 235 |
- sep = ', '
|
|
| 236 |
- items = sorted(self.kwparams.items())
|
|
| 237 |
- for k, v in items:
|
|
| 238 |
- s = s + sep + k + '=' + repr(v)
|
|
| 239 |
- sep = ', '
|
|
| 240 |
- s = s + ')'
|
|
| 241 |
- return s
|
|
| 242 |
- def __repr__(self):
|
|
| 243 |
- return self.__str__()
|
|
| 244 |
- |
|
| 245 |
-class MockCallable:
|
|
| 246 |
- """
|
|
| 247 |
- Intercepts the call and records it, then delegates to either the mock's
|
|
| 248 |
- dictionary of mock return values that was passed in to the constructor,
|
|
| 249 |
- or a handcrafted method of a Mock subclass.
|
|
| 250 |
- """
|
|
| 251 |
- def __init__(self, name, mock, handcrafted=False):
|
|
| 252 |
- self.name = name
|
|
| 253 |
- self.mock = mock
|
|
| 254 |
- self.handcrafted = handcrafted
|
|
| 255 |
- |
|
| 256 |
- def __call__(self, *params, **kwparams):
|
|
| 257 |
- self.mock._checkInterfaceCall(self.name, params, kwparams)
|
|
| 258 |
- thisCall = self.recordCall(params, kwparams)
|
|
| 259 |
- self.checkExpectations(thisCall, params, kwparams)
|
|
| 260 |
- return self.makeCall(params, kwparams)
|
|
| 261 |
- |
|
| 262 |
- def recordCall(self, params, kwparams):
|
|
| 263 |
- """
|
|
| 264 |
- Record the MockCall in an ordered list of all calls, and an ordered
|
|
| 265 |
- list of calls for that method name.
|
|
| 266 |
- """
|
|
| 267 |
- thisCall = MockCall(self.name, params, kwparams)
|
|
| 268 |
- calls = self.mock.mockCalledMethods.setdefault(self.name, [])
|
|
| 269 |
- calls.append(thisCall)
|
|
| 270 |
- self.mock.mockAllCalledMethods.append(thisCall)
|
|
| 271 |
- return thisCall
|
|
| 272 |
- |
|
| 273 |
- def makeCall(self, params, kwparams):
|
|
| 274 |
- if self.handcrafted:
|
|
| 275 |
- allPosParams = (self.mock,) + params
|
|
| 276 |
- func = _findFunc(self.mock.realClass, self.name)
|
|
| 277 |
- if not func:
|
|
| 278 |
- raise NotImplementedError
|
|
| 279 |
- return func(*allPosParams, **kwparams)
|
|
| 280 |
- else:
|
|
| 281 |
- returnVal = self.mock.mockReturnValues.get(self.name)
|
|
| 282 |
- if isinstance(returnVal, ReturnValuesBase):
|
|
| 283 |
- returnVal = returnVal.next()
|
|
| 284 |
- return returnVal
|
|
| 285 |
- |
|
| 286 |
- def checkExpectations(self, thisCall, params, kwparams):
|
|
| 287 |
- if self.name in self.mock.mockExpectations:
|
|
| 288 |
- callsMade = len(self.mock.mockCalledMethods[self.name])
|
|
| 289 |
- for (expectation, after, until) in self.mock.mockExpectations[self.name]:
|
|
| 290 |
- if callsMade > after and (until==0 or callsMade < until):
|
|
| 291 |
- assert expectation(self.mock, thisCall, len(self.mock.mockAllCalledMethods)-1), 'Expectation failed: '+str(thisCall)
|
|
| 292 |
- |
|
| 293 |
- |
|
| 294 |
-def _findFunc(cl, name):
|
|
| 295 |
- """ Depth first search for a method with a given name. """
|
|
| 296 |
- if name in cl.__dict__:
|
|
| 297 |
- return cl.__dict__[name]
|
|
| 298 |
- for base in cl.__bases__:
|
|
| 299 |
- func = _findFunc(base, name)
|
|
| 300 |
- if func:
|
|
| 301 |
- return func
|
|
| 302 |
- return None
|
|
| 303 |
- |
|
| 304 |
- |
|
| 305 |
- |
|
| 306 |
-class ReturnValuesBase:
|
|
| 307 |
- def next(self):
|
|
| 308 |
- try:
|
|
| 309 |
- return self.iter.next()
|
|
| 310 |
- except StopIteration:
|
|
| 311 |
- raise AssertionError("No more return values")
|
|
| 312 |
- def __iter__(self):
|
|
| 313 |
- return self
|
|
| 314 |
- |
|
| 315 |
-class ReturnValues(ReturnValuesBase):
|
|
| 316 |
- def __init__(self, *values):
|
|
| 317 |
- self.iter = iter(values)
|
|
| 318 |
- |
|
| 319 |
- |
|
| 320 |
-class ReturnIterator(ReturnValuesBase):
|
|
| 321 |
- def __init__(self, iterator):
|
|
| 322 |
- self.iter = iter(iterator)
|
|
| 323 |
- |
|
| 324 |
- |
|
| 325 |
-def expectParams(*params, **keywords):
|
|
| 326 |
- '''check that the callObj is called with specified params and keywords
|
|
| 327 |
- '''
|
|
| 328 |
- def fn(mockObj, callObj, idx):
|
|
| 329 |
- return callObj.params == params and callObj.kwparams == keywords
|
|
| 330 |
- return fn
|
|
| 331 |
- |
|
| 332 |
- |
|
| 333 |
-def expectAfter(*methods):
|
|
| 334 |
- '''check that the function is only called after all the functions in 'methods'
|
|
| 335 |
- '''
|
|
| 336 |
- def fn(mockObj, callObj, idx):
|
|
| 337 |
- calledMethods = [method.getName() for method in mockObj.mockGetAllCalls()]
|
|
| 338 |
- #skip last entry, since that is the current call
|
|
| 339 |
- calledMethods = calledMethods[:-1]
|
|
| 340 |
- for method in methods:
|
|
| 341 |
- if method not in calledMethods:
|
|
| 342 |
- return False
|
|
| 343 |
- return True
|
|
| 344 |
- return fn
|
|
| 345 |
- |
|
| 346 |
-def expectException(exception, *args, **kwargs):
|
|
| 347 |
- ''' raise an exception when the method is called
|
|
| 348 |
- '''
|
|
| 349 |
- def fn(mockObj, callObj, idx):
|
|
| 350 |
- raise exception(*args, **kwargs)
|
|
| 351 |
- return fn
|
|
| 352 |
- |
|
| 353 |
- |
|
| 354 |
-def expectParam(paramIdx, cond):
|
|
| 355 |
- '''check that the callObj is called with parameter specified by paramIdx (a position index or keyword)
|
|
| 356 |
- fulfills the condition specified by cond.
|
|
| 357 |
- cond is a function that takes a single argument, the value to test.
|
|
| 358 |
- '''
|
|
| 359 |
- def fn(mockObj, callObj, idx):
|
|
| 360 |
- param = callObj.getParam(paramIdx)
|
|
| 361 |
- return cond(param)
|
|
| 362 |
- return fn
|
|
| 363 |
- |
|
| 364 |
-def EQ(value):
|
|
| 365 |
- def testFn(param):
|
|
| 366 |
- return param == value
|
|
| 367 |
- return testFn
|
|
| 368 |
- |
|
| 369 |
-def NE(value):
|
|
| 370 |
- def testFn(param):
|
|
| 371 |
- return param != value
|
|
| 372 |
- return testFn
|
|
| 373 |
- |
|
| 374 |
-def GT(value):
|
|
| 375 |
- def testFn(param):
|
|
| 376 |
- return param > value
|
|
| 377 |
- return testFn
|
|
| 378 |
- |
|
| 379 |
-def LT(value):
|
|
| 380 |
- def testFn(param):
|
|
| 381 |
- return param < value
|
|
| 382 |
- return testFn
|
|
| 383 |
- |
|
| 384 |
-def GE(value):
|
|
| 385 |
- def testFn(param):
|
|
| 386 |
- return param >= value
|
|
| 387 |
- return testFn
|
|
| 388 |
- |
|
| 389 |
-def LE(value):
|
|
| 390 |
- def testFn(param):
|
|
| 391 |
- return param <= value
|
|
| 392 |
- return testFn
|
|
| 393 |
- |
|
| 394 |
-def AND(*condlist):
|
|
| 395 |
- def testFn(param):
|
|
| 396 |
- for cond in condlist:
|
|
| 397 |
- if not cond(param):
|
|
| 398 |
- return False
|
|
| 399 |
- return True
|
|
| 400 |
- return testFn
|
|
| 401 |
- |
|
| 402 |
-def OR(*condlist):
|
|
| 403 |
- def testFn(param):
|
|
| 404 |
- for cond in condlist:
|
|
| 405 |
- if cond(param):
|
|
| 406 |
- return True
|
|
| 407 |
- return False
|
|
| 408 |
- return testFn
|
|
| 409 |
- |
|
| 410 |
-def NOT(cond):
|
|
| 411 |
- def testFn(param):
|
|
| 412 |
- return not cond(param)
|
|
| 413 |
- return testFn
|
|
| 414 |
- |
|
| 415 |
-def MATCHES(regex, *args, **kwargs):
|
|
| 416 |
- compiled_regex = re.compile(regex, *args, **kwargs)
|
|
| 417 |
- def testFn(param):
|
|
| 418 |
- return compiled_regex.match(param) is not None
|
|
| 419 |
- return testFn
|
|
| 420 |
- |
|
| 421 |
-def SEQ(*sequence):
|
|
| 422 |
- iterator = iter(sequence)
|
|
| 423 |
- def testFn(param):
|
|
| 424 |
- try:
|
|
| 425 |
- cond = iterator.next()
|
|
| 426 |
- except StopIteration:
|
|
| 427 |
- raise AssertionError('SEQ exhausted')
|
|
| 428 |
- return cond(param)
|
|
| 429 |
- return testFn
|
|
| 430 |
- |
|
| 431 |
-def IS(instance):
|
|
| 432 |
- def testFn(param):
|
|
| 433 |
- return param is instance
|
|
| 434 |
- return testFn
|
|
| 435 |
- |
|
| 436 |
-def ISINSTANCE(class_):
|
|
| 437 |
- def testFn(param):
|
|
| 438 |
- return isinstance(param, class_)
|
|
| 439 |
- return testFn
|
|
| 440 |
- |
|
| 441 |
-def ISSUBCLASS(class_):
|
|
| 442 |
- def testFn(param):
|
|
| 443 |
- return issubclass(param, class_)
|
|
| 444 |
- return testFn
|
|
| 445 |
- |
|
| 446 |
-def CONTAINS(val):
|
|
| 447 |
- def testFn(param):
|
|
| 448 |
- return val in param
|
|
| 449 |
- return testFn
|
|
| 450 |
- |
|
| 451 |
-def IN(container):
|
|
| 452 |
- def testFn(param):
|
|
| 453 |
- return param in container
|
|
| 454 |
- return testFn
|
|
| 455 |
- |
|
| 456 |
-def HASATTR(attr):
|
|
| 457 |
- def testFn(param):
|
|
| 458 |
- return hasattr(param, attr)
|
|
| 459 |
- return testFn
|
|
| 460 |
- |
|
| 461 |
-def HASMETHOD(method):
|
|
| 462 |
- def testFn(param):
|
|
| 463 |
- return hasattr(param, method) and callable(getattr(param, method))
|
|
| 464 |
- return testFn
|
|
| 465 |
- |
|
| 466 |
-CALLABLE = callable
|
| 1 |
-# mock notify module
|
|
| 2 |
- |
|
| 3 |
-from gajim.common import app
|
|
| 4 |
-from gajim.common import ged
|
|
| 5 |
- |
|
| 6 |
-notifications = []
|
|
| 7 |
- |
|
| 8 |
-class Notification:
|
|
| 9 |
- def _nec_notification(self, obj):
|
|
| 10 |
- global notifications
|
|
| 11 |
- notifications.append(obj)
|
|
| 12 |
- |
|
| 13 |
- def clean(self):
|
|
| 14 |
- global notifications
|
|
| 15 |
- notifications = []
|
|
| 16 |
- app.ged.remove_event_handler('notification', ged.GUI2,
|
|
| 17 |
- self._nec_notification)
|
|
| 18 |
- |
|
| 19 |
- def __init__(self):
|
|
| 20 |
- app.ged.register_event_handler('notification', ged.GUI2,
|
|
| 21 |
- self._nec_notification)
|
|
| 22 |
- |
|
| 23 |
- |
|
| 24 |
-def notify(event, jid, account, parameters, advanced_notif_num = None):
|
|
| 25 |
- notifications.append((event, jid, account, parameters, advanced_notif_num))
|
|
| 26 |
- |
|
| 27 |
-def get_advanced_notification(event, account, contact):
|
|
| 28 |
- return None
|
| 1 |
+import gi
|
|
| 2 |
+ |
|
| 3 |
+def require_versions():
|
|
| 4 |
+ gi.require_versions({'Gdk': '3.0',
|
|
| 5 |
+ 'GLib': '2.0',
|
|
| 6 |
+ 'Gio': '2.0',
|
|
| 7 |
+ 'Gtk': '3.0',
|
|
| 8 |
+ 'GObject': '2.0',
|
|
| 9 |
+ 'Pango': '1.0'})
|
|
| 10 |
+ |
|
| 11 |
+require_versions()
|
| ... | ... | @@ -8,8 +8,8 @@ |
| 8 | 8 |
from gajim.common.styling import StrongSpan
|
| 9 | 9 |
from gajim.common.styling import EmphasisSpan
|
| 10 | 10 |
from gajim.common.styling import StrikeSpan
|
| 11 |
-from gajim.common.styling import Url
|
|
| 12 |
-from gajim.common.styling import URL_RX
|
|
| 11 |
+from gajim.common.styling import Uri
|
|
| 12 |
+from gajim.common.styling import URI_RX
|
|
| 13 | 13 |
from gajim.common.styling import ADDRESS_RX
|
| 14 | 14 |
|
| 15 | 15 |
|
| ... | ... | @@ -135,7 +135,7 @@ |
| 135 | 135 |
'pre block with closing': {
|
| 136 | 136 |
'input': '```\npre *fmt* ```\n```\nplain',
|
| 137 | 137 |
'tokens': [
|
| 138 |
- PreBlock(start=0, end=22, text='```\npre *fmt* ```\n```\n', spans=[]),
|
|
| 138 |
+ PreBlock(start=0, end=22, text='```\npre *fmt* ```\n```\n'),
|
|
| 139 | 139 |
PlainBlock(start=22, end=27, text='plain', spans=[])
|
| 140 | 140 |
]
|
| 141 | 141 |
},
|
| ... | ... | @@ -143,7 +143,7 @@ |
| 143 | 143 |
'pre block EOF': {
|
| 144 | 144 |
'input': '````\na\n```',
|
| 145 | 145 |
'tokens': [
|
| 146 |
- PreBlock(start=0, end=10, text='````\na\n```', spans=[])
|
|
| 146 |
+ PreBlock(start=0, end=10, text='````\na\n```')
|
|
| 147 | 147 |
]
|
| 148 | 148 |
},
|
| 149 | 149 |
|
| ... | ... | @@ -164,7 +164,7 @@ |
| 164 | 164 |
'single level block quote': {
|
| 165 | 165 |
'input': '> quoted\nnot quoted',
|
| 166 | 166 |
'tokens': [
|
| 167 |
- QuoteBlock(start=0, end=10, text='> quoted\n', spans=[], blocks=[
|
|
| 167 |
+ QuoteBlock(start=0, end=10, text='> quoted\n', blocks=[
|
|
| 168 | 168 |
PlainBlock(start=0, end=8, text=' quoted\n', spans=[])
|
| 169 | 169 |
]),
|
| 170 | 170 |
PlainBlock(start=10, end=20, text='not quoted', spans=[])
|
| ... | ... | @@ -174,9 +174,9 @@ |
| 174 | 174 |
'multi level block quote': {
|
| 175 | 175 |
'input': '> quoted\n>> quote > 2\n>quote 1\n\nnot quoted',
|
| 176 | 176 |
'tokens': [
|
| 177 |
- QuoteBlock(start=0, end=34, text='> quoted\n>> quote > 2\n>quote 1\n', spans=[], blocks=[
|
|
| 177 |
+ QuoteBlock(start=0, end=34, text='> quoted\n>> quote > 2\n>quote 1\n', blocks=[
|
|
| 178 | 178 |
PlainBlock(start=0, end=8, text=' quoted\n', spans=[]),
|
| 179 |
- QuoteBlock(start=8, end=22, text='> quote > 2\n', spans=[], blocks=[
|
|
| 179 |
+ QuoteBlock(start=8, end=22, text='> quote > 2\n', blocks=[
|
|
| 180 | 180 |
PlainBlock(start=0, end=12, text=' quote > 2\n', spans=[])
|
| 181 | 181 |
]),
|
| 182 | 182 |
PlainBlock(start=22, end=30, text='quote 1\n', spans=[])
|
| ... | ... | @@ -188,15 +188,15 @@ |
| 188 | 188 |
'quote start then EOF': {
|
| 189 | 189 |
'input': '> ',
|
| 190 | 190 |
'tokens': [
|
| 191 |
- QuoteBlock(start=0, end=2, text='> ', spans=[], blocks=[])
|
|
| 191 |
+ QuoteBlock(start=0, end=2, text='> ', blocks=[])
|
|
| 192 | 192 |
]
|
| 193 | 193 |
},
|
| 194 | 194 |
|
| 195 | 195 |
'quote with children': {
|
| 196 | 196 |
'input': '> ```\n> pre\n> ```\n> not pre',
|
| 197 | 197 |
'tokens': [
|
| 198 |
- QuoteBlock(start=0, end=27, text='> ```\n> pre\n> ```\n> not pre', spans=[], blocks=[
|
|
| 199 |
- PreBlock(start=0, end=12, text='```\npre\n```\n', spans=[]),
|
|
| 198 |
+ QuoteBlock(start=0, end=27, text='> ```\n> pre\n> ```\n> not pre', blocks=[
|
|
| 199 |
+ PreBlock(start=0, end=12, text='```\npre\n```\n'),
|
|
| 200 | 200 |
PlainBlock(start=12, end=19, text='not pre', spans=[])
|
| 201 | 201 |
])
|
| 202 | 202 |
]
|
| ... | ... | @@ -205,8 +205,8 @@ |
| 205 | 205 |
'pre end of parent': {
|
| 206 | 206 |
'input': '> ``` \n> pre\nplain',
|
| 207 | 207 |
'tokens': [
|
| 208 |
- QuoteBlock(start=0, end=13, text='> ``` \n> pre\n', spans=[], blocks=[
|
|
| 209 |
- PreBlock(start=0, end=9, text='``` \npre\n', spans=[])
|
|
| 208 |
+ QuoteBlock(start=0, end=13, text='> ``` \n> pre\n', blocks=[
|
|
| 209 |
+ PreBlock(start=0, end=9, text='``` \npre\n')
|
|
| 210 | 210 |
]),
|
| 211 | 211 |
PlainBlock(start=13, end=18, text='plain', spans=[])
|
| 212 | 212 |
]
|
| ... | ... | @@ -223,7 +223,7 @@ |
| 223 | 223 |
'input': 'some kind of link http://foo.com/blah_blah',
|
| 224 | 224 |
'tokens': [
|
| 225 | 225 |
PlainBlock(start=0, end=42, text='some kind of link http://foo.com/blah_blah', spans=[], uris=[
|
| 226 |
- Url(start=18, end=42, text='http://foo.com/blah_blah')
|
|
| 226 |
+ Uri(start=18, end=42, text='http://foo.com/blah_blah')
|
|
| 227 | 227 |
])
|
| 228 | 228 |
]
|
| 229 | 229 |
},
|
| ... | ... | @@ -232,7 +232,7 @@ |
| 232 | 232 |
'input': 'some kind of link http://foo.com/blah_blah,',
|
| 233 | 233 |
'tokens': [
|
| 234 | 234 |
PlainBlock(start=0, end=43, text='some kind of link http://foo.com/blah_blah,', spans=[], uris=[
|
| 235 |
- Url(start=18, end=42, text='http://foo.com/blah_blah')
|
|
| 235 |
+ Uri(start=18, end=42, text='http://foo.com/blah_blah')
|
|
| 236 | 236 |
])
|
| 237 | 237 |
]
|
| 238 | 238 |
},
|
| ... | ... | @@ -243,7 +243,7 @@ |
| 243 | 243 |
PlainBlock(start=0, end=44, text='some *kind* of link http://foo.com/blah_blah', spans=[
|
| 244 | 244 |
StrongSpan(start=5, end=11, text='*kind*')
|
| 245 | 245 |
], uris=[
|
| 246 |
- Url(start=20, end=44, text='http://foo.com/blah_blah')
|
|
| 246 |
+ Uri(start=20, end=44, text='http://foo.com/blah_blah')
|
|
| 247 | 247 |
])
|
| 248 | 248 |
]
|
| 249 | 249 |
},
|
| ... | ... | @@ -316,7 +316,7 @@ def test_styling(self): |
| 316 | 316 |
|
| 317 | 317 |
def test_urls(self):
|
| 318 | 318 |
for url in URLS:
|
| 319 |
- match = URL_RX.search(url)
|
|
| 319 |
+ match = URI_RX.search(url)
|
|
| 320 | 320 |
self.assertIsNotNone(match)
|
| 321 | 321 |
start = match.start()
|
| 322 | 322 |
end = match.end()
|
| ... | ... | @@ -332,7 +332,7 @@ def test_emails(self): |
| 332 | 332 |
|
| 333 | 333 |
def test_url_with_text(self):
|
| 334 | 334 |
for text, result in URL_WITH_TEXT:
|
| 335 |
- match = URL_RX.search(text)
|
|
| 335 |
+ match = URI_RX.search(text)
|
|
| 336 | 336 |
self.assertIsNotNone(match)
|
| 337 | 337 |
start = match.start()
|
| 338 | 338 |
end = match.end()
|
| 1 |
-'''
|
|
| 2 |
- |
|
| 3 |
-This package just contains plain unit tests
|
|
| 4 |
- |
|
| 5 |
-'''
|
| 1 |
-'''
|
|
| 2 |
- |
|
| 3 |
-This package just contains plain unit tests
|
|
| 4 |
- |
|
| 5 |
-'''
|
| 1 |
-'''
|
|
| 2 |
-Some diverse tests covering functionality in the GUI Interface class.
|
|
| 3 |
-'''
|
|
| 4 |
-import unittest
|
|
| 5 |
- |
|
| 6 |
-import gajim.gui
|
|
| 7 |
-gajim.gui.init('gtk')
|
|
| 8 |
- |
|
| 9 |
-from test import lib
|
|
| 10 |
-lib.setup_env()
|
|
| 11 |
- |
|
| 12 |
-from gajim.common import logging_helpers
|
|
| 13 |
-logging_helpers.set_quiet()
|
|
| 14 |
- |
|
| 15 |
-from gajim.common import app
|
|
| 16 |
- |
|
| 17 |
-from gajim.gui_interface import Interface
|
|
| 18 |
- |
|
| 19 |
-from gi.repository import GLib
|
|
| 20 |
- |
|
| 21 |
-class TestInterface(unittest.TestCase):
|
|
| 22 |
- |
|
| 23 |
- def test_instantiation(self):
|
|
| 24 |
- ''' Test that we can proper initialize and do not fail on globals '''
|
|
| 25 |
- def close_app():
|
|
| 26 |
- app.app.quit()
|
|
| 27 |
- GLib.idle_add(close_app)
|
|
| 28 |
- app.app.run()
|
|
| 29 |
- |
|
| 30 |
- |
|
| 31 |
-if __name__ == "__main__":
|
|
| 32 |
- unittest.main()
|