9578053 Jan 22 2022 distfiles.gentoo.org/distfiles/gajim-1.3.3-2.tar.gz
This commit is contained in:
parent
a5b3822651
commit
4c1b226bff
1045 changed files with 753037 additions and 18 deletions
0
test/__init__.py
Normal file
0
test/__init__.py
Normal file
220
test/broken/integration/test_gui_event_integration.py
Normal file
220
test/broken/integration/test_gui_event_integration.py
Normal file
|
@ -0,0 +1,220 @@
|
|||
'''
|
||||
Tests for the miscellaneous functions scattered throughout gajim/gajim.py
|
||||
'''
|
||||
import unittest
|
||||
|
||||
import lib
|
||||
lib.setup_env()
|
||||
|
||||
import nbxmpp
|
||||
|
||||
from gajim.common import gajim
|
||||
from gajim.common import contacts as contacts_module
|
||||
from gajim.common import caps_cache
|
||||
from gajim.gajim import Interface
|
||||
|
||||
from gajim_mocks import *
|
||||
gajim.logger = MockLogger()
|
||||
|
||||
Interface()
|
||||
|
||||
import time
|
||||
from data import *
|
||||
|
||||
from gajim import roster_window
|
||||
from gajim import plugins
|
||||
|
||||
class TestStatusChange(unittest.TestCase):
|
||||
'''tests gajim.py's incredibly complex presence handling'''
|
||||
|
||||
def setUp(self):
|
||||
|
||||
gajim.connections = {}
|
||||
gajim.contacts = contacts_module.LegacyContactsAPI()
|
||||
gajim.interface.roster = roster_window.RosterWindow()
|
||||
gajim.plugin_manager = plugins.PluginManager()
|
||||
gajim.logger = MockLogger()
|
||||
caps_cache.initialize(gajim.logger)
|
||||
|
||||
for acc in contacts:
|
||||
gajim.connections[acc] = MockConnection(acc)
|
||||
|
||||
gajim.interface.roster.fill_contacts_and_groups_dicts(contacts[acc],
|
||||
acc)
|
||||
gajim.interface.roster.add_account(acc)
|
||||
gajim.interface.roster.add_account_contacts(acc)
|
||||
|
||||
self.assertEqual(0, len(notify.notifications))
|
||||
|
||||
def tearDown(self):
|
||||
notify.notifications = []
|
||||
for acc in contacts:
|
||||
gajim.connections[acc].cleanup()
|
||||
|
||||
def contact_comes_online(self, account, jid, resource, prio,
|
||||
should_popup=True):
|
||||
'''a remote contact comes online'''
|
||||
xml = """<presence from='%s/%s' id='123'><priority>%s</priority>
|
||||
<c node='http://gajim.org' ver='pRCD6cgQ4SDqNMCjdhRV6TECx5o='
|
||||
hash='sha-1' xmlns='http://jabber.org/protocol/caps'/>
|
||||
<status>I'm back!</status>
|
||||
</presence>
|
||||
""" % (jid, resource, prio)
|
||||
msg = nbxmpp.protocol.Presence(node=nbxmpp.simplexml.XML2Node(xml))
|
||||
gajim.connections[account]._presenceCB(None, msg)
|
||||
|
||||
contact = None
|
||||
for c in gajim.contacts.get_contacts(account, jid):
|
||||
if c.resource == resource:
|
||||
contact = c
|
||||
break
|
||||
|
||||
self.assertEqual('online', contact.show)
|
||||
self.assertEqual("I'm back!", contact.status)
|
||||
self.assertEqual(prio, contact.priority)
|
||||
|
||||
# the most recent notification is that the contact connected
|
||||
if should_popup:
|
||||
self.assertEqual('Contact Signed In',
|
||||
notify.notifications[-1].popup_event_type)
|
||||
else:
|
||||
self.assertEqual('', notify.notifications[-1].popup_event_type)
|
||||
|
||||
def contact_goes_offline(self, account, jid, resource, prio,
|
||||
still_exists = True):
|
||||
'''a remote contact goes offline.'''
|
||||
xml = """<presence type='unavailable' from='%s/%s' id='123'>
|
||||
<priority>%s</priority>
|
||||
<c node='http://gajim.org' ver='pRCD6cgQ4SDqNMCjdhRV6TECx5o='
|
||||
hash='sha-1' xmlns='http://jabber.org/protocol/caps'/>
|
||||
<status>Goodbye!</status>
|
||||
</presence>
|
||||
""" % (jid, resource, prio)
|
||||
msg = nbxmpp.protocol.Presence(node=nbxmpp.simplexml.XML2Node(xml))
|
||||
gajim.connections[account]._presenceCB(None, msg)
|
||||
|
||||
contact = None
|
||||
for c in gajim.contacts.get_contacts(account, jid):
|
||||
if c.resource == resource:
|
||||
contact = c
|
||||
break
|
||||
|
||||
if not still_exists:
|
||||
self.assertTrue(contact is None)
|
||||
return
|
||||
|
||||
self.assertEqual('offline', contact.show)
|
||||
self.assertEqual('Goodbye!', contact.status)
|
||||
self.assertEqual(prio, contact.priority)
|
||||
|
||||
self.assertEqual('Contact Signed Out',
|
||||
notify.notifications[-1].popup_event_type)
|
||||
|
||||
def user_starts_chatting(self, jid, account, resource=None):
|
||||
'''the user opens a chat window and starts talking'''
|
||||
ctrl = MockChatControl(jid, account)
|
||||
win = MockWindow()
|
||||
win.new_tab(ctrl)
|
||||
gajim.interface.msg_win_mgr._windows['test'] = win
|
||||
|
||||
if resource:
|
||||
jid = jid + '/' + resource
|
||||
|
||||
# a basic session is started
|
||||
session = gajim.connections[account1].make_new_session(jid,
|
||||
'01234567890abcdef', cls=MockSession)
|
||||
ctrl.set_session(session)
|
||||
|
||||
return ctrl
|
||||
|
||||
def user_starts_esession(self, jid, resource, account):
|
||||
'''the user opens a chat window and starts an encrypted session'''
|
||||
ctrl = self.user_starts_chatting(jid, account, resource)
|
||||
ctrl.session.status = 'active'
|
||||
ctrl.session.enable_encryption = True
|
||||
|
||||
return ctrl
|
||||
|
||||
def test_contact_comes_online(self):
|
||||
jid = 'default1@gajim.org'
|
||||
|
||||
# contact is offline initially
|
||||
contacts = gajim.contacts.get_contacts(account1, jid)
|
||||
self.assertEqual(1, len(contacts))
|
||||
self.assertEqual('offline', contacts[0].show)
|
||||
self.assertEqual('', contacts[0].status)
|
||||
|
||||
self.contact_comes_online(account1, jid, 'lowprio', 1)
|
||||
|
||||
def test_contact_goes_offline(self):
|
||||
jid = 'default1@gajim.org'
|
||||
|
||||
self.contact_comes_online(account1, jid, 'lowprio', 1)
|
||||
|
||||
ctrl = self.user_starts_chatting(jid, account1)
|
||||
orig_sess = ctrl.session
|
||||
|
||||
self.contact_goes_offline(account1, jid, 'lowprio', 1)
|
||||
|
||||
# session hasn't changed since we were talking to the bare jid
|
||||
self.assertEqual(orig_sess, ctrl.session)
|
||||
|
||||
def test_two_resources_higher_comes_online(self):
|
||||
jid = 'default1@gajim.org'
|
||||
|
||||
self.contact_comes_online(account1, jid, 'lowprio', 1)
|
||||
|
||||
ctrl = self.user_starts_chatting(jid, account1)
|
||||
|
||||
self.contact_comes_online(account1, jid, 'highprio', 50,
|
||||
should_popup=False)
|
||||
|
||||
# old session was dropped
|
||||
self.assertEqual(None, ctrl.session)
|
||||
|
||||
def test_two_resources_higher_goes_offline(self):
|
||||
jid = 'default1@gajim.org'
|
||||
|
||||
self.contact_comes_online(account1, jid, 'lowprio', 1)
|
||||
self.contact_comes_online(account1, jid, 'highprio', 50,
|
||||
should_popup=False)
|
||||
|
||||
ctrl = self.user_starts_chatting(jid, account1)
|
||||
|
||||
self.contact_goes_offline(account1, jid, 'highprio', 50,
|
||||
still_exists=False)
|
||||
|
||||
# old session was dropped
|
||||
self.assertEqual(None, ctrl.session)
|
||||
|
||||
def test_two_resources_higher_comes_online_with_esession(self):
|
||||
jid = 'default1@gajim.org'
|
||||
|
||||
self.contact_comes_online(account1, jid, 'lowprio', 1)
|
||||
|
||||
ctrl = self.user_starts_esession(jid, 'lowprio', account1)
|
||||
|
||||
self.contact_comes_online(account1, jid, 'highprio', 50,
|
||||
should_popup=False)
|
||||
|
||||
# session was associated with the low priority full jid, so it should
|
||||
# have been removed from the control
|
||||
self.assertEqual(None, ctrl.session)
|
||||
|
||||
def test_two_resources_higher_goes_offline_with_esession(self):
|
||||
jid = 'default1@gajim.org'
|
||||
|
||||
self.contact_comes_online(account1, jid, 'lowprio', 1)
|
||||
self.contact_comes_online(account1, jid, 'highprio', 50)
|
||||
|
||||
ctrl = self.user_starts_esession(jid, 'highprio', account1)
|
||||
|
||||
self.contact_goes_offline(account1, jid, 'highprio', 50,
|
||||
still_exists=False)
|
||||
|
||||
# session was associated with the high priority full jid, so it should
|
||||
# have been removed from the control
|
||||
self.assertEqual(None, ctrl.session)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
211
test/broken/integration/test_roster.py
Normal file
211
test/broken/integration/test_roster.py
Normal file
|
@ -0,0 +1,211 @@
|
|||
import unittest
|
||||
|
||||
import lib
|
||||
lib.setup_env()
|
||||
|
||||
from data import *
|
||||
|
||||
from gajim_mocks import *
|
||||
|
||||
from gajim.common import app
|
||||
from gajim.common import contacts as contacts_module
|
||||
from gajim import roster_window
|
||||
|
||||
app.get_jid_from_account = lambda acc: 'myjid@' + acc
|
||||
|
||||
|
||||
class TestRosterWindow(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
app.interface = MockInterface()
|
||||
|
||||
self.C_NAME = roster_window.Column.NAME
|
||||
self.C_TYPE = roster_window.Column.TYPE
|
||||
self.C_JID = roster_window.Column.JID
|
||||
self.C_ACCOUNT = roster_window.Column.ACCOUNT
|
||||
|
||||
# Add after creating RosterWindow
|
||||
# We want to test the filling explicitly
|
||||
app.contacts = contacts_module.LegacyContactsAPI()
|
||||
app.connections = {}
|
||||
self.roster = roster_window.RosterWindow(app.app)
|
||||
|
||||
for acc in contacts:
|
||||
app.connections[acc] = MockConnection(acc)
|
||||
app.contacts.add_account(acc)
|
||||
|
||||
def tearDown(self):
|
||||
self.roster.window.destroy()
|
||||
# Clean main loop
|
||||
from gi.repository import GLib
|
||||
mc = GLib.main_context_default()
|
||||
while mc.pending():
|
||||
mc.iteration()
|
||||
|
||||
### Custom assertions
|
||||
def assert_all_contacts_are_in_roster(self, acc):
|
||||
for jid in contacts[acc]:
|
||||
self.assert_contact_is_in_roster(jid, acc)
|
||||
|
||||
def assert_contact_is_in_roster(self, jid, account):
|
||||
contacts = app.contacts.get_contacts(account, jid)
|
||||
# check for all resources
|
||||
for contact in contacts:
|
||||
iters = self.roster._get_contact_iter(jid, account,
|
||||
model=self.roster.model)
|
||||
|
||||
if jid != app.get_jid_from_account(account):
|
||||
# We don't care for groups of SelfContact
|
||||
self.assertTrue(len(iters) == len(contact.get_shown_groups()),
|
||||
msg='Contact is not in all his groups')
|
||||
|
||||
# Are we big brother?
|
||||
bb_jid = None
|
||||
bb_account = None
|
||||
family = app.contacts.get_metacontacts_family(account, jid)
|
||||
if family:
|
||||
nearby_family, bb_jid, bb_account = \
|
||||
self.roster._get_nearby_family_and_big_brother(family, account)
|
||||
|
||||
is_in_nearby_family = (jid, account) in (
|
||||
(data['jid'], data['account']) for data in nearby_family)
|
||||
self.assertTrue(is_in_nearby_family,
|
||||
msg='Contact not in his own nearby family')
|
||||
|
||||
is_big_brother = (bb_jid, bb_account) == (jid, account)
|
||||
|
||||
# check for each group tag
|
||||
for titerC in iters:
|
||||
self.assertTrue(self.roster.model.iter_is_valid(titerC),
|
||||
msg='Contact iter invalid')
|
||||
|
||||
c_model = self.roster.model[titerC]
|
||||
# name can be stricked if contact or group is blocked
|
||||
# self.assertEqual(contact.get_shown_name(), c_model[self.C_NAME],
|
||||
# msg='Contact name missmatch')
|
||||
self.assertEqual(contact.jid, c_model[self.C_JID],
|
||||
msg='Jid missmatch')
|
||||
|
||||
if not self.roster.regroup:
|
||||
self.assertEqual(account, c_model[self.C_ACCOUNT],
|
||||
msg='Account missmatch')
|
||||
|
||||
# Check for correct nesting
|
||||
parent_iter = self.roster.model.iter_parent(titerC)
|
||||
p_model = self.roster.model[parent_iter]
|
||||
if family:
|
||||
if is_big_brother:
|
||||
self.assertTrue(p_model[self.C_TYPE] == 'group',
|
||||
msg='Big Brother is not on top')
|
||||
else:
|
||||
self.assertTrue(p_model[self.C_TYPE] == 'contact',
|
||||
msg='Little Brother brother has no BigB')
|
||||
else:
|
||||
if jid == app.get_jid_from_account(account):
|
||||
self.assertTrue(p_model[self.C_TYPE] == 'account',
|
||||
msg='SelfContact is not on top')
|
||||
else:
|
||||
self.assertTrue(p_model[self.C_TYPE] == 'group',
|
||||
msg='Contact not found in a group')
|
||||
|
||||
def assert_group_is_in_roster(self, group, account):
|
||||
#TODO
|
||||
pass
|
||||
|
||||
def assert_account_is_in_roster(self, acc):
|
||||
titerA = self.roster._get_account_iter(acc, model=self.roster.model)
|
||||
self.assertTrue(self.roster.model.iter_is_valid(titerA),
|
||||
msg='Account iter is invalid')
|
||||
|
||||
acc_model = self.roster.model[titerA]
|
||||
self.assertEqual(acc_model[self.C_TYPE], 'account',
|
||||
msg='No account found')
|
||||
|
||||
if not self.roster.regroup:
|
||||
self.assertEqual(acc_model[self.C_ACCOUNT], acc,
|
||||
msg='Account not found')
|
||||
|
||||
self_jid = app.get_jid_from_account(acc)
|
||||
self.assertEqual(acc_model[self.C_JID], self_jid,
|
||||
msg='Account JID not found in account row')
|
||||
|
||||
def assert_model_is_in_sync(self):
|
||||
#TODO: check that iter_n_children returns the correct numbers
|
||||
pass
|
||||
|
||||
# tests
|
||||
def test_fill_contacts_and_groups_dicts(self):
|
||||
for acc in contacts:
|
||||
self.roster.fill_contacts_and_groups_dicts(contacts[acc], acc)
|
||||
|
||||
for jid in contacts[acc]:
|
||||
instances = app.contacts.get_contacts(acc, jid)
|
||||
|
||||
# Created a contact for each single jid?
|
||||
self.assertTrue(len(instances) == 1)
|
||||
|
||||
# Contacts kept their info
|
||||
contact = instances[0]
|
||||
self.assertEqual(sorted(contact.groups), sorted(contacts[acc][jid]['groups']),
|
||||
msg='Group Missmatch')
|
||||
|
||||
groups = contacts[acc][jid]['groups'] or ['General',]
|
||||
|
||||
def test_fill_roster_model(self):
|
||||
for acc in contacts:
|
||||
self.roster.fill_contacts_and_groups_dicts(contacts[acc], acc)
|
||||
|
||||
self.roster.add_account(acc)
|
||||
self.assert_account_is_in_roster(acc)
|
||||
|
||||
self.roster.add_account_contacts(acc)
|
||||
self.assert_all_contacts_are_in_roster(acc)
|
||||
|
||||
self.assert_model_is_in_sync()
|
||||
|
||||
|
||||
class TestRosterWindowRegrouped(TestRosterWindow):
|
||||
|
||||
def setUp(self):
|
||||
app.settings.set('mergeaccounts', True)
|
||||
TestRosterWindow.setUp(self)
|
||||
|
||||
def test_toggle_regroup(self):
|
||||
self.roster.regroup = not self.roster.regroup
|
||||
self.roster.setup_and_draw_roster()
|
||||
self.roster.regroup = not self.roster.regroup
|
||||
self.roster.setup_and_draw_roster()
|
||||
|
||||
|
||||
class TestRosterWindowMetaContacts(TestRosterWindowRegrouped):
|
||||
|
||||
def test_receive_metacontact_data(self):
|
||||
for complete_data in metacontact_data:
|
||||
t_acc = complete_data[0]['account']
|
||||
t_jid = complete_data[0]['jid']
|
||||
data = complete_data[1:]
|
||||
for brother in data:
|
||||
acc = brother['account']
|
||||
jid = brother['jid']
|
||||
app.contacts.add_metacontact(t_acc, t_jid, acc, jid)
|
||||
self.roster.setup_and_draw_roster()
|
||||
|
||||
def test_connect_new_metacontact(self):
|
||||
self.test_fill_roster_model()
|
||||
|
||||
jid = 'coolstuff@gajim.org'
|
||||
contact = app.contacts.create_contact(jid, account1)
|
||||
app.contacts.add_contact(account1, contact)
|
||||
self.roster.add_contact(jid, account1)
|
||||
self.roster.chg_contact_status(contact, 'offline', '', account1)
|
||||
|
||||
app.contacts.add_metacontact(account1, 'samejid@gajim.org',
|
||||
account1, jid)
|
||||
self.roster.chg_contact_status(contact, 'online', '', account1)
|
||||
|
||||
self.assert_model_is_in_sync()
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
89
test/broken/test_pluginmanager.py
Normal file
89
test/broken/test_pluginmanager.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
## Gajim 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; version 3 only.
|
||||
##
|
||||
## Gajim 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 Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
'''
|
||||
Testing PluginManager class.
|
||||
|
||||
:author: Mateusz Biliński <mateusz@bilinski.it>
|
||||
:since: 05/30/2008
|
||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
||||
:license: GPL
|
||||
'''
|
||||
|
||||
import sys
|
||||
import os
|
||||
import unittest
|
||||
|
||||
gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
|
||||
sys.path.append(gajim_root + '/gajim')
|
||||
|
||||
# a temporary version of ~/.gajim for testing
|
||||
configdir = gajim_root + '/test/tmp'
|
||||
|
||||
import time
|
||||
|
||||
# define _ for i18n
|
||||
import builtins
|
||||
builtins._ = lambda x: x
|
||||
|
||||
# wipe config directory
|
||||
import os
|
||||
if os.path.isdir(configdir):
|
||||
import shutil
|
||||
shutil.rmtree(configdir)
|
||||
|
||||
os.mkdir(configdir)
|
||||
|
||||
from gajim.common import configpaths
|
||||
configpaths.set_config_root(configdir)
|
||||
configpaths.init()
|
||||
|
||||
# for some reason common.app needs to be imported before xmpppy?
|
||||
|
||||
configpaths.override_path('DATA', gajim_root + '/gajim/data')
|
||||
|
||||
# name to use for the test account
|
||||
account_name = 'test'
|
||||
|
||||
from plugins import PluginManager
|
||||
|
||||
class PluginManagerTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.pluginmanager = PluginManager()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_01_Singleton(self):
|
||||
""" 1. Checking whether PluginManger class is singleton. """
|
||||
self.pluginmanager.test_arg = 1
|
||||
secondPluginManager = PluginManager()
|
||||
|
||||
self.assertEqual(id(secondPluginManager), id(self.pluginmanager),
|
||||
'Different IDs in references to PluginManager objects (not a singleton)')
|
||||
self.assertEqual(secondPluginManager.test_arg, 1,
|
||||
'References point to different PluginManager objects (not a singleton')
|
||||
|
||||
def suite():
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(PluginManagerTestCase)
|
||||
return suite
|
||||
|
||||
if __name__=='__main__':
|
||||
runner = unittest.TextTestRunner()
|
||||
test_suite = suite()
|
||||
runner.run(test_suite)
|
156
test/broken/unit/test_jingle.py
Normal file
156
test/broken/unit/test_jingle.py
Normal file
|
@ -0,0 +1,156 @@
|
|||
'''
|
||||
Tests for dispatcher.py
|
||||
'''
|
||||
import unittest
|
||||
|
||||
import lib
|
||||
lib.setup_env()
|
||||
|
||||
from mock import Mock
|
||||
|
||||
|
||||
from nbxmpp import dispatcher
|
||||
from nbxmpp.namespaces import Namespace
|
||||
|
||||
from gajim.common.protocol.bytestream import ConnectionIBBytestream
|
||||
from gajim.common.protocol.bytestream import ConnectionSocks5Bytestream
|
||||
from gajim.common.jingle import ConnectionJingle
|
||||
from gajim.common import app
|
||||
from gajim.common.socks5 import SocksQueue
|
||||
|
||||
|
||||
session_init = '''
|
||||
<iq xmlns="jabber:client" to="jingleft@thiessen.im/Gajim" type="set" id="43">
|
||||
<jingle xmlns="urn:xmpp:jingle:1" action="session-initiate" initiator="jtest@thiessen.im/Gajim" sid="38">
|
||||
<content name="fileWL1Y2JIPTM5RAD68" creator="initiator">
|
||||
<security xmlns="urn:xmpp:jingle:security:xtls:0">
|
||||
<method name="x509" />
|
||||
</security>
|
||||
<description xmlns="urn:xmpp:jingle:apps:file-transfer:1">
|
||||
<offer>
|
||||
<file xmlns="http://jabber.org/protocol/si/profile/file-transfer" name="to" size="2273">
|
||||
<desc />
|
||||
</file>
|
||||
</offer>
|
||||
</description>
|
||||
<transport xmlns="urn:xmpp:jingle:transports:s5b:1" sid="39">
|
||||
<candidate jid="jtest@thiessen.im/Gajim" cid="40" priority="8257536" host="192.168.2.100" type="direct" port="28011" />
|
||||
<candidate jid="proxy.thiessen.im" cid="41" priority="655360" host="192.168.2.100" type="proxy" port="5000" />
|
||||
<candidate jid="proxy.jabbim.cz" cid="42" priority="655360" host="192.168.2.100" type="proxy" port="7777" />
|
||||
</transport>
|
||||
</content>
|
||||
</jingle>
|
||||
</iq>
|
||||
'''
|
||||
|
||||
|
||||
transport_info = '''
|
||||
<iq from='jtest@thiessen.im/Gajim'
|
||||
id='hjdi8'
|
||||
to='jingleft@thiessen.im/Gajim'
|
||||
type='set'>
|
||||
<jingle xmlns='urn:xmpp:jingle:1'
|
||||
action='transport-info'
|
||||
initiator='jtest@thiessen.im/Gajim'
|
||||
sid='38'>
|
||||
<content creator='initiator' name='fileWL1Y2JIPTM5RAD68'>
|
||||
<transport xmlns='urn:xmpp:jingle:transports:s5b:1'
|
||||
sid='vj3hs98y'>
|
||||
<candidate-used cid='hr65dqyd'/>
|
||||
</transport>
|
||||
</content>
|
||||
</jingle>
|
||||
</iq>
|
||||
|
||||
'''
|
||||
|
||||
class Connection(Mock, ConnectionJingle, ConnectionSocks5Bytestream,
|
||||
ConnectionIBBytestream):
|
||||
|
||||
def __init__(self):
|
||||
Mock.__init__(self)
|
||||
ConnectionJingle.__init__(self)
|
||||
ConnectionSocks5Bytestream.__init__(self)
|
||||
ConnectionIBBytestream.__init__(self)
|
||||
self.connected = 2 # This tells gajim we are connected
|
||||
|
||||
def send(self, stanza=None, when=None):
|
||||
# Called when gajim wants to send something
|
||||
print(str(stanza))
|
||||
|
||||
|
||||
class TestJingle(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.dispatcher = dispatcher.XMPPDispatcher()
|
||||
app.nec = Mock()
|
||||
app.socks5queue = SocksQueue(Mock())
|
||||
# Setup mock client
|
||||
self.client = Connection()
|
||||
self.client.__str__ = lambda: 'Mock' # FIXME: why do I need this one?
|
||||
self.client._caller = Connection()
|
||||
self.client.defaultNamespace = Namespace.CLIENT
|
||||
self.client.Connection = Connection() # mock transport
|
||||
self.con = self.client.Connection
|
||||
self.con.server_resource = None
|
||||
self.con.connection = Connection()
|
||||
|
||||
'''
|
||||
Fake file_props when we receive a file. Gajim creates a file_props
|
||||
out of a FileRequestReceive event and from then on it changes in
|
||||
a lot of places. It is easier to just copy it in here.
|
||||
If the session_initiate stanza changes, this also must change.
|
||||
'''
|
||||
self.receive_file = {'stream-methods':
|
||||
'http://jabber.org/protocol/bytestreams',
|
||||
'sender': 'jtest@thiessen.im/Gajim',
|
||||
'file-name': 'test_received_file',
|
||||
'request-id': '43', 'sid': '39',
|
||||
'session-sid': '38', 'session-type': 'jingle',
|
||||
'transfered_size': [], 'receiver':
|
||||
'jingleft@thiessen.im/Gajim', 'desc': '',
|
||||
'size': '2273', 'type': 'r',
|
||||
'streamhosts': [{'initiator':
|
||||
'jtest@thiessen.im/Gajim',
|
||||
'target': 'jingleft@thiessen.im/Gajim',
|
||||
'cid': '41', 'state': 0, 'host': '192.168.2.100',
|
||||
'type': 'direct', 'port': '28011'},
|
||||
{'initiator': 'jtest@thiessen.im/Gajim',
|
||||
'target': 'jingleft@thiessen.im/Gajim',
|
||||
'cid': '42', 'state': 0, 'host': '192.168.2.100',
|
||||
'type': 'proxy', 'port': '5000'}],
|
||||
'name': 'to'}
|
||||
|
||||
def tearDown(self):
|
||||
# Unplug if needed
|
||||
if hasattr(self.dispatcher, '_owner'):
|
||||
self.dispatcher.PlugOut()
|
||||
|
||||
def _simulate_connect(self):
|
||||
self.dispatcher.PlugIn(self.client) # client is owner
|
||||
# Simulate that we have established a connection
|
||||
self.dispatcher.StreamInit()
|
||||
self.dispatcher.ProcessNonBlocking("<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client'>")
|
||||
|
||||
def _simulate_jingle_session(self):
|
||||
|
||||
self.dispatcher.RegisterHandler('iq', self.con._JingleCB, 'set',
|
||||
Namespace.JINGLE)
|
||||
self.dispatcher.ProcessNonBlocking(session_init)
|
||||
session = list(self.con._sessions.values())[0] # The only session we have
|
||||
jft = list(session.contents.values())[0] # jingleFT object
|
||||
jft.file_props = self.receive_file # We plug file_props manually
|
||||
# The user accepts to receive the file
|
||||
# we have to manually simulate this behavior
|
||||
session.approve_session()
|
||||
self.con.send_file_approval(self.receive_file)
|
||||
|
||||
self.dispatcher.ProcessNonBlocking(transport_info)
|
||||
|
||||
def test_jingle_session(self):
|
||||
self._simulate_connect()
|
||||
self._simulate_jingle_session()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
167
test/broken/unit/test_sessions.py
Normal file
167
test/broken/unit/test_sessions.py
Normal file
|
@ -0,0 +1,167 @@
|
|||
import unittest
|
||||
|
||||
import lib
|
||||
lib.setup_env()
|
||||
|
||||
import notify
|
||||
import nbxmpp
|
||||
|
||||
from gajim.common import app
|
||||
from gajim.common import nec
|
||||
from gajim.common import ged
|
||||
from gajim.common.nec import NetworkEvent
|
||||
|
||||
from gajim.session import ChatControlSession
|
||||
from gajim.roster_window import RosterWindow
|
||||
|
||||
from gajim_mocks import *
|
||||
from data import account1
|
||||
|
||||
app.interface = MockInterface()
|
||||
|
||||
|
||||
# name to use for the test account
|
||||
account_name = account1
|
||||
|
||||
|
||||
class TestChatControlSession(unittest.TestCase):
|
||||
''' Testclass for session.py '''
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
app.nec = nec.NetworkEventsController()
|
||||
cls.conn = MockConnection(account_name, {'send_stanza': None})
|
||||
app.logger = MockLogger()
|
||||
app.default_session_type = ChatControlSession
|
||||
|
||||
def setUp(self):
|
||||
app.notification = notify.Notification()
|
||||
|
||||
# no notifications have been sent
|
||||
self.assertEqual(0, len(notify.notifications))
|
||||
|
||||
def tearDown(self):
|
||||
app.notification.clean()
|
||||
|
||||
def receive_chat_msg(self, jid, msgtxt):
|
||||
'''simulate receiving a chat message from jid'''
|
||||
msg = nbxmpp.Message()
|
||||
msg.setBody(msgtxt)
|
||||
msg.setType('chat')
|
||||
|
||||
xml = """<message from='%s' id='1' type='chat'><body>%s</body>
|
||||
<thread>123</thread></message>""" % (jid, msgtxt)
|
||||
stanza = nbxmpp.protocol.Message(node=nbxmpp.simplexml.XML2Node(xml))
|
||||
self.conn._messageCB(None, stanza)
|
||||
|
||||
# ----- custom assertions -----
|
||||
def assert_new_message_notification(self):
|
||||
'''a new_message notification has been sent'''
|
||||
self.assertEqual(1, len(notify.notifications))
|
||||
notif = notify.notifications[-1]
|
||||
self.assertEqual('New Message', notif.popup_event_type)
|
||||
|
||||
def assert_first_message_notification(self):
|
||||
'''this message was treated as a first message'''
|
||||
self.assert_new_message_notification()
|
||||
notif = notify.notifications[-1]
|
||||
first = notif.first_unread
|
||||
self.assertTrue(first,
|
||||
'message should have been treated as a first message')
|
||||
|
||||
def assert_not_first_message_notification(self):
|
||||
'''this message was not treated as a first message'''
|
||||
self.assert_new_message_notification()
|
||||
notif = notify.notifications[-1]
|
||||
first = notif.first_unread
|
||||
self.assertTrue(not first,
|
||||
'message was unexpectedly treated as a first message')
|
||||
|
||||
# ----- tests -----
|
||||
def test_receive_1nocontrol(self):
|
||||
'''test receiving a message in a blank state'''
|
||||
jid = 'bct@necronomicorp.com'
|
||||
fjid = 'bct@necronomicorp.com/Gajim'
|
||||
msgtxt = 'testing one'
|
||||
|
||||
self.receive_chat_msg(fjid, msgtxt)
|
||||
|
||||
# session is created
|
||||
self.assertTrue((jid in self.conn.sessions) and (
|
||||
'123' in self.conn.sessions[jid]), 'session is not created')
|
||||
sess = self.conn.sessions[jid]['123']
|
||||
|
||||
# message was logged
|
||||
calls = app.storage.archive.mockGetNamedCalls('insert_into_logs')
|
||||
self.assertEqual(1, len(calls))
|
||||
|
||||
# no ChatControl was open and autopopup was off
|
||||
# so the message goes into the event queue
|
||||
self.assertEqual(1, len(app.events.get_events(account_name)))
|
||||
|
||||
self.assert_first_message_notification()
|
||||
|
||||
# no control is attached to the session
|
||||
self.assertEqual(None, sess.control)
|
||||
|
||||
def test_receive_2already_has_control(self):
|
||||
'''test receiving a message with a session already attached to a
|
||||
control'''
|
||||
jid = 'bct@necronomicorp.com'
|
||||
fjid = 'bct@necronomicorp.com/Gajim'
|
||||
msgtxt = 'testing two'
|
||||
app.interface.roster = RosterWindow(app.app)
|
||||
|
||||
sess = self.conn.sessions[jid]['123']
|
||||
sess.control = MockChatControl(fjid, account_name)
|
||||
|
||||
self.receive_chat_msg(fjid, msgtxt)
|
||||
|
||||
# message was logged
|
||||
calls = app.storage.archive.mockGetNamedCalls('insert_into_logs')
|
||||
self.assertEqual(2, len(calls))
|
||||
|
||||
# the message does not go into the event queue
|
||||
self.assertEqual(1, len(app.events.get_events(account_name)))
|
||||
|
||||
self.assert_not_first_message_notification()
|
||||
|
||||
# message was printed to the control
|
||||
calls = sess.control.mockGetNamedCalls('print_conversation')
|
||||
self.assertEqual(1, len(calls))
|
||||
app.interface.roster.window.destroy()
|
||||
|
||||
#def test_received_3orphaned_control(self):
|
||||
#'''test receiving a message when a control that doesn't have a session
|
||||
#attached exists'''
|
||||
|
||||
#jid = 'bct@necronomicorp.com'
|
||||
#fjid = jid + '/Gajim'
|
||||
#msgtxt = 'testing three'
|
||||
|
||||
#ctrl = MockChatControl(jid, account_name)
|
||||
#gajim.interface.msg_win_mgr = Mock({'get_control': ctrl})
|
||||
#gajim.interface.msg_win_mgr.mockSetExpectation('get_control',
|
||||
#expectParams(jid, account_name))
|
||||
|
||||
#self.receive_chat_msg(fjid, msgtxt)
|
||||
|
||||
## message was logged
|
||||
#calls = gajim.logger.mockGetNamedCalls('insert_into_logs')
|
||||
#self.assertEqual(1, len(calls))
|
||||
|
||||
## the message does not go into the event queue
|
||||
#self.assertEqual(0, len(gajim.events.get_events(account_name)))
|
||||
|
||||
#self.assert_not_first_message_notification()
|
||||
|
||||
## this session is now attached to that control
|
||||
#self.assertEqual(self.sess, ctrl.session)
|
||||
#self.assertEqual(ctrl, self.sess.control, 'foo')
|
||||
|
||||
## message was printed to the control
|
||||
#calls = ctrl.mockGetNamedCalls('print_conversation')
|
||||
#self.assertEqual(1, len(calls))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
172
test/broken/unit/test_socks5.py
Normal file
172
test/broken/unit/test_socks5.py
Normal file
|
@ -0,0 +1,172 @@
|
|||
'''
|
||||
Tests for dispatcher.py
|
||||
'''
|
||||
import unittest
|
||||
|
||||
import lib
|
||||
lib.setup_env()
|
||||
|
||||
from mock import Mock
|
||||
import sys
|
||||
import socket
|
||||
|
||||
from gajim.common.socks5 import *
|
||||
from gajim.common import jingle_xtls
|
||||
|
||||
class fake_sock(Mock):
|
||||
def __init__(self, sockobj):
|
||||
Mock.__init__(self)
|
||||
|
||||
self.sockobj = sockobj
|
||||
|
||||
|
||||
def setup_stream(self):
|
||||
sha1 = self.sockobj._get_sha1_auth()
|
||||
|
||||
self.incoming = []
|
||||
self.incoming.append(self.sockobj._get_auth_response())
|
||||
self.incoming.append(self.sockobj._get_request_buff(sha1, 0x00))
|
||||
self.outgoing = []
|
||||
self.outgoing.append(self.sockobj._get_auth_buff())
|
||||
self.outgoing.append(self.sockobj._get_request_buff(sha1))
|
||||
|
||||
def switch_stream(self):
|
||||
# Roles are reversed, client will be expecting server stream
|
||||
# and server will be expecting client stream
|
||||
|
||||
temp = self.incoming
|
||||
self.incoming = self.outgoing
|
||||
self.outgoing = temp
|
||||
|
||||
def _recv(self, foo):
|
||||
return self.incoming.pop(0)
|
||||
|
||||
def _send(self, data):
|
||||
# This method is surrounded by a try block,
|
||||
# we can't use assert here
|
||||
|
||||
if data != self.outgoing[0]:
|
||||
print('FAILED SENDING TEST')
|
||||
self.outgoing.pop(0)
|
||||
|
||||
class fake_idlequeue(Mock):
|
||||
|
||||
def __init__(self):
|
||||
Mock.__init__(self)
|
||||
|
||||
def plug_idle(self, obj, writable=True, readable=True):
|
||||
|
||||
if readable:
|
||||
obj.pollin()
|
||||
if writable:
|
||||
obj.pollout()
|
||||
|
||||
class TestSocks5(unittest.TestCase):
|
||||
'''
|
||||
Test class for Socks5
|
||||
'''
|
||||
def setUp(self):
|
||||
streamhost = { 'host': None,
|
||||
'port': 1,
|
||||
'initiator' : None,
|
||||
'target' : None}
|
||||
queue = Mock()
|
||||
queue.file_props = {}
|
||||
#self.sockobj = Socks5Receiver(fake_idlequeue(), streamhost, None)
|
||||
self.sockobj = Socks5Sender(fake_idlequeue(), None, 'server', Mock() ,
|
||||
None, None, True, file_props={})
|
||||
sock = fake_sock(self.sockobj)
|
||||
self.sockobj._sock = sock
|
||||
self.sockobj._recv = sock._recv
|
||||
self.sockobj._send = sock._send
|
||||
self.sockobj.state = 1
|
||||
self.sockobj.connected = True
|
||||
self.sockobj.pollend = self._pollend
|
||||
|
||||
# Something that the receiver needs
|
||||
#self.sockobj.file_props['type'] = 'r'
|
||||
|
||||
# Something that the sender needs
|
||||
self.sockobj.file_props = {}
|
||||
self.sockobj.file_props['type'] = 'r'
|
||||
self.sockobj.file_props['paused'] = ''
|
||||
self.sockobj.queue = Mock()
|
||||
self.sockobj.queue.process_result = self._pollend
|
||||
|
||||
def _pollend(self, foo = None, duu = None):
|
||||
# This is a disconnect function
|
||||
sys.exit("end of the road")
|
||||
|
||||
def _check_inout(self):
|
||||
# Check if there isn't anything else to receive or send
|
||||
sock = self.sockobj._sock
|
||||
assert(sock.incoming == [])
|
||||
assert(sock.outgoing == [])
|
||||
|
||||
def test_connection_server(self):
|
||||
return
|
||||
mocksock = self.sockobj._sock
|
||||
mocksock.setup_stream()
|
||||
#self.sockobj._sock.switch_stream()
|
||||
s = socket.socket(2, 1, 6)
|
||||
server = ('127.0.0.1', 28000)
|
||||
|
||||
s.connect(server)
|
||||
|
||||
s.send(mocksock.outgoing.pop(0))
|
||||
self.assertEqual(s.recv(64), mocksock.incoming.pop(0))
|
||||
|
||||
s.send(mocksock.outgoing.pop(0))
|
||||
self.assertEqual(s.recv(64), mocksock.incoming.pop(0))
|
||||
|
||||
def test_connection_client(self):
|
||||
|
||||
|
||||
mocksock = self.sockobj._sock
|
||||
mocksock.setup_stream()
|
||||
mocksock.switch_stream()
|
||||
s = socket.socket(10, 1, 6)
|
||||
|
||||
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
|
||||
netadd = ('::', 28000, 0, 0)
|
||||
s.bind(netadd)
|
||||
s.listen(socket.SOMAXCONN)
|
||||
(s, address) = s.accept()
|
||||
|
||||
|
||||
self.assertEqual(s.recv(64), mocksock.incoming.pop(0))
|
||||
s.send(mocksock.outgoing.pop(0))
|
||||
|
||||
buff = s.recv(64)
|
||||
inco = mocksock.incoming.pop(0)
|
||||
#self.assertEqual(s.recv(64), mocksock.incoming.pop(0))
|
||||
s.send(mocksock.outgoing.pop(0))
|
||||
|
||||
def test_client_negoc(self):
|
||||
return
|
||||
self.sockobj._sock.setup_stream()
|
||||
try:
|
||||
self.sockobj.pollout()
|
||||
except SystemExit:
|
||||
pass
|
||||
|
||||
self._check_inout()
|
||||
|
||||
def test_server_negoc(self):
|
||||
return
|
||||
self.sockobj._sock.setup_stream()
|
||||
self.sockobj._sock.switch_stream()
|
||||
try:
|
||||
self.sockobj.idlequeue.plug_idle(self.sockobj, False, True)
|
||||
except SystemExit:
|
||||
pass
|
||||
self._check_inout()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
unittest.main()
|
137
test/gtk/assistant.py
Normal file
137
test/gtk/assistant.py
Normal file
|
@ -0,0 +1,137 @@
|
|||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
from gajim.common.const import CSSPriority
|
||||
|
||||
from gajim import gui
|
||||
gui.init('gtk')
|
||||
|
||||
from gajim.gui.assistant import Assistant
|
||||
from gajim.gui.assistant import Page
|
||||
|
||||
from test.gtk import util
|
||||
util.load_style('gajim.css', CSSPriority.APPLICATION)
|
||||
|
||||
|
||||
class TestAssistant(Assistant):
|
||||
def __init__(self):
|
||||
Assistant.__init__(self)
|
||||
|
||||
self.add_pages({'start': Start()})
|
||||
|
||||
progress = self.add_default_page('progress')
|
||||
progress.set_title('Executing...')
|
||||
progress.set_text('Something is in progress...')
|
||||
|
||||
error = self.add_default_page('error')
|
||||
error.set_title('Error')
|
||||
error.set_heading('Error Heading')
|
||||
error.set_text('This is the error text')
|
||||
|
||||
success = self.add_default_page('success')
|
||||
success.set_title('Success')
|
||||
success.set_heading('Success Heading')
|
||||
success.set_text('This is the success text')
|
||||
|
||||
self.add_button('forward', 'Forward', 'suggested-action', complete=True)
|
||||
self.add_button('close', 'Close', 'destructive-action')
|
||||
self.add_button('back', 'Back')
|
||||
|
||||
self.set_button_visible_func(self._visible_func)
|
||||
|
||||
self.connect('button-clicked', self._on_button_clicked)
|
||||
self.connect('page-changed', self._on_page_changed)
|
||||
|
||||
self.show_all()
|
||||
|
||||
@staticmethod
|
||||
def _visible_func(_assistant, page_name):
|
||||
if page_name == 'start':
|
||||
return ['forward']
|
||||
|
||||
if page_name == 'progress':
|
||||
return ['forward', 'back']
|
||||
|
||||
if page_name == 'success':
|
||||
return ['forward', 'back']
|
||||
|
||||
if page_name == 'error':
|
||||
return ['back', 'close']
|
||||
raise ValueError('page %s unknown' % page_name)
|
||||
|
||||
def _on_button_clicked(self, _assistant, button_name):
|
||||
page = self.get_current_page()
|
||||
if button_name == 'forward':
|
||||
if page == 'start':
|
||||
self.show_page('progress', Gtk.StackTransitionType.SLIDE_LEFT)
|
||||
elif page == 'progress':
|
||||
self.show_page('success', Gtk.StackTransitionType.SLIDE_LEFT)
|
||||
elif page == 'success':
|
||||
self.show_page('error', Gtk.StackTransitionType.SLIDE_LEFT)
|
||||
return
|
||||
|
||||
if button_name == 'back':
|
||||
if page == 'progress':
|
||||
self.show_page('start')
|
||||
if page == 'success':
|
||||
self.show_page('progress')
|
||||
if page == 'error':
|
||||
self.show_page('success')
|
||||
return
|
||||
|
||||
if button_name == 'close':
|
||||
self.destroy()
|
||||
|
||||
def _on_page_changed(self, _assistant, page_name):
|
||||
if page_name == 'start':
|
||||
self.set_default_button('forward')
|
||||
|
||||
elif page_name == 'progress':
|
||||
self.set_default_button('forward')
|
||||
|
||||
elif page_name == 'success':
|
||||
self.set_default_button('forward')
|
||||
|
||||
elif page_name == 'error':
|
||||
self.set_default_button('back')
|
||||
|
||||
|
||||
class Start(Page):
|
||||
def __init__(self):
|
||||
Page.__init__(self)
|
||||
|
||||
self.title = 'Start'
|
||||
self.complete = False
|
||||
|
||||
heading = Gtk.Label(label='Test Assistant')
|
||||
heading.get_style_context().add_class('large-header')
|
||||
|
||||
label1 = Gtk.Label(label='This is label 1 with some text')
|
||||
label1.set_max_width_chars(50)
|
||||
label1.set_line_wrap(True)
|
||||
label1.set_halign(Gtk.Align.CENTER)
|
||||
label1.set_justify(Gtk.Justification.CENTER)
|
||||
label1.set_margin_bottom(24)
|
||||
|
||||
entry = Gtk.Entry(activates_default=True)
|
||||
entry.connect('changed', self._on_changed)
|
||||
|
||||
self._server = Gtk.CheckButton.new_with_mnemonic('A fancy checkbox')
|
||||
self._server.set_halign(Gtk.Align.CENTER)
|
||||
|
||||
self.pack_start(heading, False, True, 0)
|
||||
self.pack_start(label1, False, True, 0)
|
||||
self.pack_start(entry, False, True, 0)
|
||||
self.pack_start(self._server, False, True, 0)
|
||||
self.show_all()
|
||||
|
||||
def _on_changed(self, entry):
|
||||
self.complete = bool(entry.get_text())
|
||||
self.update_page_complete()
|
||||
|
||||
|
||||
win = TestAssistant()
|
||||
win.connect('destroy', Gtk.main_quit)
|
||||
win.show_all()
|
||||
Gtk.main()
|
54
test/gtk/certificate_dialog.py
Normal file
54
test/gtk/certificate_dialog.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
import OpenSSL
|
||||
|
||||
from gajim import gui
|
||||
gui.init('gtk')
|
||||
|
||||
from test.gtk import util
|
||||
from gajim.common.const import CSSPriority
|
||||
from gajim.gui.dialogs import CertificateDialog
|
||||
|
||||
util.load_style('gajim.css', CSSPriority.APPLICATION)
|
||||
|
||||
cert = '''
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFhDCCBGygAwIBAgISA4oUEifTr7Y+mcdiwu6KWpcVMA0GCSqGSIb3DQEBCwUA
|
||||
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
|
||||
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA0MDMxODE3NDVaFw0x
|
||||
OTA3MDIxODE3NDVaMBsxGTAXBgNVBAMMECoubGlnaHR3aXRjaC5vcmcwggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/3mcevikse7QwDYwPcGAD9zHw3UWE
|
||||
7J8SJR349/rFTF2tBFDvEa62OUKTCg5vPKVKaXHlzruk/A7blgqsEdugycORwPD1
|
||||
7YNJ27EldrRtotjclurzKL6D/MgcaQ4cTkPOD3cWbf/L+HClGrpFt7su6Z6cTutC
|
||||
wiAYAdlfmVgSSv15F1xOTyFyfGJKQnW628Xs8xUvZh5H/SsEEum4MwVVGW06Z/A/
|
||||
mwX2jmJUb2M25S1Ma025nZpGYyAAqecTmPb3fStnXm4sdytfZhm4+nj9mH9GQIU1
|
||||
t/jO/7X7IFpc9DvVRSumSVqvNaVgiWmTLP4VxlCVJO6mibOXXUUDA5RfAgMBAAGj
|
||||
ggKRMIICjTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
|
||||
AQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFC4/ZRurw2wVFsgJBTb9Fh/3
|
||||
0aV9MB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEB
|
||||
BGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0
|
||||
Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0
|
||||
Lm9yZy8wSQYDVR0RBEIwQIIQKi5saWdodHdpdGNoLm9yZ4IOKi5tZXRyb25vbWUu
|
||||
aW2CDmxpZ2h0d2l0Y2gub3JnggxtZXRyb25vbWUuaW0wTAYDVR0gBEUwQzAIBgZn
|
||||
gQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5s
|
||||
ZXRzZW5jcnlwdC5vcmcwggECBgorBgEEAdZ5AgQCBIHzBIHwAO4AdQB0ftqDMa0z
|
||||
EJEhnM4lT0Jwwr/9XkIgCMY3NXnmEHvMVgAAAWnkosBAAAAEAwBGMEQCICEfmTBk
|
||||
OxS95eiYsfTH5HdL7kfp68BSin5LqeGyyxk9AiA3qeDZNKklJTdWqYjto7kUqJNd
|
||||
YiL99SrqwzR6w+AqSwB1ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM9OVFR/R4
|
||||
AAABaeSiwEUAAAQDAEYwRAIgFxouOkJeqkQUe6zNI5w/6YBIQFrsrIZdPcX+r6JI
|
||||
is8CIEEETzlEyj9lWR/BSSruSp0FT5CuoNNeEG7HxrJ+gVhZMA0GCSqGSIb3DQEB
|
||||
CwUAA4IBAQAQtfs1NPNMmBQRcKsZyGLZsvpp2hIhdYi72RYnHnIl4MXbhyNj9xtI
|
||||
cJr9PQ+3FsSnxy7LDjZMpbmBuXhawOyPBPw2M0f0Tv6Eo6miwvP/X1kLE3VjTzCo
|
||||
6JPh6bEB5wa+kH/pUcGlV6uyT7IuXOiArx0VmIpTA3uwlVdfynOnR3CF20Ds4FLc
|
||||
JxbGMqRuw/sGiTLKlXc1xVil8WZjL3hokzrgI7K6np2skUjWuMZvhJgwi5QiE7/C
|
||||
ejsJoYkpvcaiaLAyVymTY/n/oM2oQpv5Mqjit+18RB9c2P+ifH5iDKC/jTKn4NNz
|
||||
8xSTlUlCBTCozjzscZVeVDIojmejWclT
|
||||
-----END CERTIFICATE-----'''
|
||||
|
||||
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
|
||||
win = CertificateDialog(None, 'testacc', cert)
|
||||
win.connect("destroy", Gtk.main_quit)
|
||||
win.show_all()
|
||||
Gtk.main()
|
40
test/gtk/change_password.py
Normal file
40
test/gtk/change_password.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
from functools import partial
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
from nbxmpp.modules.dataforms import create_field
|
||||
from nbxmpp.modules.dataforms import SimpleDataForm
|
||||
|
||||
from gajim.common.const import CSSPriority
|
||||
|
||||
from gajim import gui
|
||||
gui.init('gtk')
|
||||
|
||||
from gajim.gui.change_password import ChangePassword
|
||||
|
||||
from test.gtk import util
|
||||
util.load_style('gajim.css', CSSPriority.APPLICATION)
|
||||
|
||||
fields = [
|
||||
create_field(typ='text-single', label='Username', var='username'),
|
||||
create_field(typ='text-single', label='Old Password', var='old_password'),
|
||||
create_field(typ='text-single', label='Mothers name', var='mother', required=True),
|
||||
]
|
||||
|
||||
form = SimpleDataForm(type_='form', fields=fields)
|
||||
|
||||
def _apply(self, next_stage=False):
|
||||
if next_stage:
|
||||
print(self.get_page('next_stage').get_submit_form())
|
||||
else:
|
||||
self.get_page('next_stage').set_form(form)
|
||||
self.show_page('next_stage', Gtk.StackTransitionType.SLIDE_LEFT)
|
||||
|
||||
win = ChangePassword(None)
|
||||
win._on_apply = partial(_apply, win)
|
||||
|
||||
win.connect('destroy', Gtk.main_quit)
|
||||
win.show_all()
|
||||
Gtk.main()
|
133
test/gtk/dataform.py
Normal file
133
test/gtk/dataform.py
Normal file
|
@ -0,0 +1,133 @@
|
|||
from base64 import b64decode
|
||||
|
||||
from gi.repository import Gtk
|
||||
import nbxmpp
|
||||
from nbxmpp.modules.dataforms import extend_form
|
||||
|
||||
from gajim import gui
|
||||
gui.init('gtk')
|
||||
|
||||
from gajim.gui.dataform import DataFormWidget
|
||||
from gajim.common.const import CSSPriority
|
||||
from gajim.common import app
|
||||
|
||||
from test.gtk import util
|
||||
util.load_style('gajim.css', CSSPriority.APPLICATION)
|
||||
|
||||
image = '''iVBORw0KGgoAAAANSUhEUgAAAIwAAAA8CAAAAACRYQ2XAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfiCwQXMiypK
|
||||
zsIAAAM4ElEQVRo3u1ZeVxUR7b+moZmE1AWFVDQNoIiEBFFxUHEXaJGQX0TnWhi0DhjYlQwbviEbOKOE9eIOjFGR8e44C4qcYkIssmiIMiO7Es3vXffe94ftxsaaUh0Zt689/ul/ulb59a5/VXVWb5TxSP832lG+B3M/wMwxr9plFpjWlFtZm9n+p8Gwxrll2vuXXDyf
|
||||
F5erZw5yde1G+/fBYb3K97UoErf3U/8y/xRrNREaFqVnFDuPmKm/38AjIaflVF32mKWzYCxSp4ZwJIRT1GRefflkNnuFv+LYIiHUsWLWNPelWs8BD3a20/Dkx+q5v/J9N8OhshI09SgMOled6nW5orZ+y5CD9aQv52Py4hY3ckkdJhN/jkwz1NLNeVHrUcNKsnkBziqZ
|
||||
gyFdWdqqow9Rit921kym/pUxhbJl/pwvVtPhMNd3xyMbCtrIjwU5lNcZTTaVt7Lqks9pvBk9WJv8zZ0zx9W8potnMXx7vssATQ6fWotsQ8Y9XpoqLUF7X1B9NFhIiK2TfqSOmtHBeFMa+d2+NIrxURUVf6+cTMRZbqJM09ti1yXQK/R2sA02xIRhR1u91pxLyLmfme6z
|
||||
AbEcU8VMwKv6aTKFb4qovMTiaguYdfmVdd/Oxh+lG6JEhUhAC71Gaa/bvHzhqvLkkydDW+xf0h8el9bQLln/I63dFL+tJ/Uw/CT+TTAQuhrJi28iAG819wmZkxZh5VRxAeubWQT1m+o72wuqQs+KlaLAqOU+kKNjZhmn9B22KRd/73yREaxiDGkr2hKZwxsU/p4ehWMJ
|
||||
n5gtIKI6IIp0xka9Wbe6u3fERGRiEQVnHDCNerzgoNCJWmPPuo7dqyvh8PITTfK24EmWf3ZyWPTO4BRJY982AFM/FvbtU8uDZ1vtGyB1TdFRKTaE+T6eSYREZ2areITEYlF18PWjHP89Lu1c0P+69vvFnR3mbQps9W2xDX7/ec/+sPVV8Fori3Io1fAaB4HhTfpwJS2K
|
||||
rAallTiekmrQDQsIXx9qniL6fo7mVPncv9j2WDNUP6Vg2+P+jL6oqKphSj7+PgnZRWpcR+Fxb1QapTS5trLy51W5xFNv9QGxhgA6rfnrXV/1ZieTl0Z2cHCFKUCgUrDmFFlvZOVnRkA/PjZxL7HhvszN72s+QcGAwAE0rweP+/uVS8eeYcfCZgC8PSs3H3Bx9dtQvm+4
|
||||
3PGVD7PTPKeHWtsMOgpzHT9JSPDtOE4vM9Xdjqp630XAFCJzLsBGhjBCKRUSR34PKiHplgCrHCo0SC/t+1tc3mmNhX3o6ecGxRiP9T3A3fpsdURACtvqjp9YVBzBWPEMAqV2m/8iEBtdJ+8YZw+nxF9fMy8I3XYItjWFoNNuQQlcIBKoKVACpM6Jc+BD8UxSwBXxm7Jv
|
||||
hnWwOfHOBSns1ONcwLiTOyBF3PHfXqwV8SK3OuPCow1Qg9zSVn6rYmj1Olw131c4tPOtUu/1zPHJZzNMMuC9Q3/4xadxeib7pOvTqt1AxKJiORxYw9s3puSG+fzwQEiInK9+5gk2UHGth/+Le1gj3y1VNQslp8JmbRywZaXTMcPgqiFISJqriMiougfuFDvn/wbIqbG5
|
||||
zvFBO5RSkQkmnKPiNXQrFi3LCIiWhEYtfzDgYvObBKuyvnSNl+n11JYKu0kHdwtIKKCE1F60mehadxDdVqrTCpWdNBOnvPMmwsNRESSoG84sWWJmZx7Klw3adrGVJWkdn+A04QH+qqSB5HniOL36ct4hL2Jm4sCexjkJaya41DPe1vjZOouMCqpREZWfEY2SDtQeTkUQ
|
||||
JlfFQ+AlvtMDVrmJAUAaf61wpHdr7qst4Rcni8+Eu5qLeDzQIys/kUhuh1cEPJOgm37dHA7ZNwdooYcA+G1TlpFRET9y6hJuCrUwRgWfb2dHIVvBZ/htjxPLSGqS1z3I5HiciOntdOzpdiZiDT1RzyXPiOiA2YnGSIimUgpKc1Nunxk+5boDT2Jrvv5r2tnM8ZAYG8PA
|
||||
HVzngFArY0pSGnKA4DaS3JBwXYAYKoLH6t4Yxb2t+cnz19kIzZNmP/JV2TJw/WSsMFGtt39J6Nw2FfBAIDGockWVWMA5k604JA/ACxbdlTUA4A5qirLihpYEys7xx1bgSljeiUxaxwMJMo8d87kfqT6YdrAO9vrxY5wImJETgt8FnbPaVSxxL59iojy7wZ48VbmlFKCX
|
||||
eitPCKi6r885pRu3GOI1JeKs7aHJ7PPOjP9bB8VEf2wsyF82j9qOiZKLZiww3T8fc67Z3Y/xewIJ2JuBfNXPNeOVAk4l0/0rtos+ODsbeOdX+QSEb3gck72nIgWIqLGZkX73Pp1pX5PNf82EVEOQ0QamZrVTwev8FvOaJX7Lh7a59obN79UfZYT7qKLUcYCLiY39t4QG
|
||||
RMxo5fNagBN9yYLAbB3jk6d1Q3igkye0KWbpSXL8GpSHEcBgNdnf+e3/UW+33gAGAIAfPOuam0tKzZ3Xnp1l3Xzwo1Lr8wDAM7f+Fr0MkcIjCOLRxtlCNeWKfaZmANA3b3xoRbVj49/n1nUyLcqTXwGvvPMmxMBYIbDfj3ujo8BYGuj1iNvHr1dLGO6LPx5JubO3krPu
|
||||
FB7YwB4/ywAWPuqAQBDkgAA77ksOcgb4XWtf25euazSIWJeXtyq0OujE/Fkd2Smr4cJD/x1k2MAYP+xsrbpmpkBSCjQurVpwB8qVzl4SrustSW5CVXBE3XWPmfnHAD4VlsQaRf9ATB69gW51whfmxLrSZO7jeo26GthReMf5dVVxaeXAIDg8+DPjQB88tcdOizHRgPA5
|
||||
pO6PzJ3c1vIii1hwIBzbHYdPHE1rbzy8LjlWnfoFy4iNZetZNdqGEVx9FGtmpyI1cgZNvdUcNDW809FLGlkTc3diUiW65PNjbkZSkQk9lRpde7OICKq3trBx3ikJRD57+ZpKQSbdopnbMx/nmwVFaKFLo07v2SuzNoIAJKXl1Cfpct1k5LTz172AKrr+1NNUna112hPi
|
||||
+wnM+wBnEk8wHmEtQIAyrROQHmuFgAgtTREyCNLKxV6K0PESH4Os/WYt2nriWwdXWeJZVLkHeZSM5gy5hARtZSQSq5KTvilQi+D9ZZxvz0NhZqHmg4iYwDyfgunOKh0jqQSl2fEZwXfHKzJevyk+P6ftU7Hxs5NOnD01amkj0DUNgCY6828O9TMT8sHv/cdDgAV5f0AA
|
||||
DXtdA6PGwjg+MvRBstbBS/2iE/3xGl9vFytI9w8Smrh6ie04wN4ud9ulW7kjBUBQYExr6hHOQRsuAxgz7mlC/RcN97Wrbd+LcxZe6HQCMCTPz4DgEGXBnZaN92PGhL9J38XCxgNWByXKmpl222canq8RDb3Cx0zl64hIqKpyUG5RERZhqM+2yImlpoecyHWRkJE7Lw9R
|
||||
EQVFmwX5W3tvMrC4irRkrj27znKteuBkqaP3P0kKXJjloghVe2DaE8iInKscDaAQbWGLSrhirQz04+c/XASJ7ZuJqIacyIi2vStAb3WOONwGoCm3qL9Tl7MWwsAsR+mDJJY1C+yc796/O0+/RtqG5s8AUDSWBioN1pZVlUnkmt4txx5TjMHxwIwnRsSc+hdrakpTAGcn
|
||||
wAAOHm/qyMRUY2bgdexFTsAwHVnGv9cv6vIepTWtLikqJe796plkwD8lCaZNV5nKLn55XKlSsMQBk93gmytRXjPdt8q8JbwoR5w1RNA+bhCXhdgSqctdrG2d3ZoH5JPpu/QlirJ7MICABhy2hNAw5AiCwABf5vwlDvda8jIkUjh3t/JrpuA44ctsWZr2n3ri6yzwPOAG
|
||||
gDY1fB1V4W/YtvmfxR22MUSxxRdRakRqImItqxjiZTbNhIRka9SQERElRe2Ra4+XdBet2HtsHP6/aHniGjHAiIi8kvv+nxGYtAfEqJik7Tl7Z+5k5aeKSS9MqqZiGjxiZYeRFR+NmZ9TJIB5cfLF91urQi+8WsiUg9JISJiuxmsDtp2xdJgsgxS3mp+5AEA+HTJFAA4M
|
||||
uulhdcJGwA53+9ttkLFo0KR4wwPQ8q+tkl30we/A0AjPb9nT3egrnwEAJS7WrzJCTn/neAbN2orrAAMNK/tCWB6pS6URT4yr2APNYmcQwd2QkKEfZMuSQ4GvHfF4XLGlukAUrwBAEff+7Uzvc5bYpWGiOihTzvpjU9YEo3fuLegS1025eIm89W2X9xvISJatI2IiMb+8
|
||||
itner+hRS7W65wJLCJi6kp+XU1MORru7EvVkztO6ln3z4Nh123WLUNDjFUxvXaTcc6nMFcbfP1a9028z9gj2WIWqpqHu9QF/V7/CNxkeFYjA/llf+M3ulV5pdUduNZjoEtZtdLtL65vctPzc0SjmDE3uzL4XwEGaE59Wir0GODyhncVCiWBZ2GCfw2Y3y9MfwfzO5hO2
|
||||
v8AIg7mWYx8/rwAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTgtMTEtMDRUMjM6NTA6NDQrMDE6MDBAxMf7AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE4LTExLTA0VDIzOjUwOjQ0KzAxOjAwMZl/RwAAAABJRU5ErkJggolQTkcNChoKAAAADUlIRFIAAAAoAAAAPAEAAAAAP
|
||||
MLFTQAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAJiS0dEAAHdihOkAAAAB3RJTUUH4gsEFzIsqSs7CAAAABBJREFUGNNj+A8CDKMkjUkAKsYq5D2hXoMAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTgtM
|
||||
TEtMDRUMjM6NTA6NDQrMDE6MDBAxMf7AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE4LTExLTA0VDIzOjUwOjQ0KzAxOjAwMZl/RwAAAABJRU5ErkJggg=='''
|
||||
|
||||
app.bob_cache['sha1+8f35fef110ffc5df08d579a50083ff9308fb6242'] = b64decode(image)
|
||||
|
||||
FORM = '''
|
||||
<x xmlns='jabber:x:data' type='form'>
|
||||
<title>Bot Configuration</title>
|
||||
<instructions>Fill out this form to configure your new bot!</instructions>
|
||||
<field type='hidden'
|
||||
var='FORM_TYPE'>
|
||||
<value>jabber:bot</value>
|
||||
</field>
|
||||
<field type='fixed'><value>Section 1: Bot Info</value></field>
|
||||
<field type='text-single'
|
||||
label='The name of your bot'
|
||||
var='botname'>
|
||||
<required/>
|
||||
</field>
|
||||
<field type='text-multi'
|
||||
label='Helpful description of your bot'
|
||||
var='description'>
|
||||
<required/>
|
||||
</field>
|
||||
<field type='boolean'
|
||||
label='Public bot?'
|
||||
var='public'/>
|
||||
<field type='text-private'
|
||||
label='Password for special access'
|
||||
var='password'>
|
||||
<required/>
|
||||
</field>
|
||||
<field type='fixed'><value>Section 2: Features</value></field>
|
||||
<field type='list-multi'
|
||||
label='What features will the bot support?'
|
||||
var='features'>
|
||||
<option label='Contests'><value>contests</value></option>
|
||||
<option label='News'><value>news</value></option>
|
||||
<option label='Polls'><value>polls</value></option>
|
||||
<option label='Reminders'><value>reminders</value></option>
|
||||
<option label='Search'><value>search</value></option>
|
||||
<option label='Search1'><value>search1</value></option>
|
||||
<option label='Really long long long long long long long long entry'><value>longentry</value></option>
|
||||
<option label='Search3'><value>search3</value></option>
|
||||
<value>news</value>
|
||||
<value>search</value>
|
||||
</field>
|
||||
<field type='fixed'><value>Section 3: Subscriber List</value></field>
|
||||
<field type='list-single'
|
||||
label='Maximum number of subscribers'
|
||||
var='maxsubs'>
|
||||
<value>20</value>
|
||||
<option label='10'><value>10</value></option>
|
||||
<option label='20'><value>20</value></option>
|
||||
<option label='30'><value>30</value></option>
|
||||
<option label='50'><value>50</value></option>
|
||||
<option label='100'><value>100</value></option>
|
||||
<option label='None'><value>none</value></option>
|
||||
</field>
|
||||
<field type='fixed'><value>Section 4: Invitations</value></field>
|
||||
<field type='jid-multi'
|
||||
label='People to invite'
|
||||
var='invitelist'>
|
||||
<desc>Tell all your friends about your new bot!</desc>
|
||||
<required/>
|
||||
</field>
|
||||
<field var='ocr' type='text-single' label='Fill in what you see'>
|
||||
<media xmlns='urn:xmpp:media-element'>
|
||||
<uri type='image/png'>cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org</uri>
|
||||
</media>
|
||||
<required/>
|
||||
</field>
|
||||
</x>
|
||||
'''
|
||||
|
||||
|
||||
class DataFormWindow(Gtk.Window):
|
||||
def __init__(self):
|
||||
Gtk.Window.__init__(self, title="Data Form Test")
|
||||
self.set_default_size(600, 600)
|
||||
options = {
|
||||
'left-width': 100,
|
||||
'form-width': 435,
|
||||
}
|
||||
self._widget = DataFormWidget(
|
||||
extend_form(node=nbxmpp.Node(node=FORM)), options)
|
||||
self.add(self._widget)
|
||||
self.show()
|
||||
|
||||
win = DataFormWindow()
|
||||
win.connect("destroy", Gtk.main_quit)
|
||||
win.show_all()
|
||||
Gtk.main()
|
50
test/gtk/fake_dataform.py
Normal file
50
test/gtk/fake_dataform.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
from gi.repository import Gtk
|
||||
|
||||
from gajim import gui
|
||||
gui.init('gtk')
|
||||
|
||||
from gajim.gui.dataform import FakeDataFormWidget
|
||||
from gajim.common.const import CSSPriority
|
||||
|
||||
from test.gtk import util
|
||||
util.load_style('gajim.css', CSSPriority.APPLICATION)
|
||||
|
||||
|
||||
fake_form = {
|
||||
'instructions': 'This is the a long long long long long long test instruction',
|
||||
'username': '',
|
||||
'nick': '',
|
||||
'password': '',
|
||||
'name': '',
|
||||
'first': '',
|
||||
'last': '',
|
||||
'email': '',
|
||||
'address': '',
|
||||
'city': '',
|
||||
'state': '',
|
||||
'zip': '',
|
||||
'phone': '',
|
||||
'url': '',
|
||||
'date': '',
|
||||
'misc': '',
|
||||
'text': '',
|
||||
'key': '',
|
||||
}
|
||||
|
||||
fake_form2 = {
|
||||
'instructions': 'To register, visit https://jabber.at/account/register/',
|
||||
'redirect-url': 'https://jabber.at/account/register/'
|
||||
}
|
||||
|
||||
class DataFormWindow(Gtk.Window):
|
||||
def __init__(self):
|
||||
Gtk.Window.__init__(self, title="Data Form Test")
|
||||
self.set_default_size(600, 600)
|
||||
self._widget = FakeDataFormWidget(fake_form2)
|
||||
self.add(self._widget)
|
||||
self.show()
|
||||
|
||||
win = DataFormWindow()
|
||||
win.connect("destroy", Gtk.main_quit)
|
||||
win.show_all()
|
||||
Gtk.main()
|
120
test/gtk/groupchat_info.py
Normal file
120
test/gtk/groupchat_info.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
import time
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
from nbxmpp.protocol import Iq
|
||||
from nbxmpp.modules.discovery import parse_disco_info
|
||||
|
||||
from gajim.common.const import CSSPriority
|
||||
|
||||
from gajim import gui
|
||||
gui.init('gtk')
|
||||
|
||||
from test.gtk import util
|
||||
from gajim.gui.groupchat_info import GroupChatInfoScrolled
|
||||
|
||||
util.load_style('gajim.css', CSSPriority.APPLICATION)
|
||||
|
||||
stanza = Iq(node='''
|
||||
<iq xmlns="jabber:client" xml:lang="de-DE" to="user@user.us" from="asd@conference.temptatio.dev" type="result" id="67284933-e526-41f3-8309-9d9475cf9c74">
|
||||
<query xmlns="http://jabber.org/protocol/disco#info">
|
||||
<identity name="ipsum dolor sit amet, consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt" type="text" category="conference" />
|
||||
<feature var="vcard-temp" />
|
||||
<feature var="http://jabber.org/protocol/muc" />
|
||||
<feature var="http://jabber.org/protocol/disco#info" />
|
||||
<feature var="http://jabber.org/protocol/disco#items" />
|
||||
<feature var="muc_temporary" />
|
||||
<feature var="muc_moderated" />
|
||||
<feature var="muc_open" />
|
||||
<feature var="muc_hidden" />
|
||||
<feature var="muc_nonanonymous" />
|
||||
<feature var="muc_passwordprotected" />
|
||||
<feature var="urn:xmpp:mam:2" />
|
||||
<feature var="muc_public" />
|
||||
<feature var="muc_persistent" />
|
||||
<feature var="muc_membersonly" />
|
||||
<feature var="muc_semianonymous" />
|
||||
<feature var="muc_unmoderated" />
|
||||
<feature var="muc_unsecured" />
|
||||
<x type="result" xmlns="jabber:x:data">
|
||||
<field var="FORM_TYPE" type="hidden">
|
||||
<value>http://jabber.org/protocol/muc#roominfo</value>
|
||||
</field>
|
||||
<field var="muc#roominfo_occupants" type="text-single" label="Number of occupants">
|
||||
<value>1</value>
|
||||
</field>
|
||||
<field var="muc#roomconfig_roomname" type="text-single" label="Natural-Language Room Name">
|
||||
<value>ipsum dolor sit amet, consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt</value>
|
||||
</field>
|
||||
<field var="muc#roominfo_description" type="text-single" label="Raum Beschreibung">
|
||||
<value>Lorem ipsum dolor sit amet, consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut labore et dolore magna</value>
|
||||
</field>
|
||||
<field var="muc#roominfo_contactjid" type="jid-multi" label="Contact Addresses (normally, room owner or owners)">
|
||||
<value>userA@user.us</value>
|
||||
<value>userB@user.us</value>
|
||||
</field>
|
||||
<field var="muc#roominfo_changesubject" type="boolean" label="Occupants May Change the Subject">
|
||||
<value>1</value>
|
||||
</field>
|
||||
<field var="muc#roomconfig_allowinvites" type="boolean" label="Occupants are allowed to invite others">
|
||||
<value>1</value>
|
||||
</field>
|
||||
<field var="muc#roomconfig_allowpm" type="list-single" label="Roles that May Send Private Messages">
|
||||
<value>anyone</value>
|
||||
<option label="Anyone">
|
||||
<value>anyone</value>
|
||||
</option>
|
||||
<option label="Anyone with Voice">
|
||||
<value>participants</value>
|
||||
</option>
|
||||
<option label="Moderators Only">
|
||||
<value>moderators</value>
|
||||
</option>
|
||||
<option label="Nobody">
|
||||
<value>none</value>
|
||||
</option>
|
||||
</field>
|
||||
<field var="muc#roominfo_lang" type="text-single" label="Natural Language for Room Discussions">
|
||||
<value>de</value>
|
||||
</field>
|
||||
<field type="text-single" var="muc#roominfo_logs">
|
||||
<value>https://logs.xmpp.org/xsf/</value>
|
||||
</field>
|
||||
</x>
|
||||
</query>
|
||||
</iq>''')
|
||||
|
||||
disco_info = parse_disco_info(stanza)
|
||||
|
||||
class GroupchatInfo(Gtk.ApplicationWindow):
|
||||
def __init__(self):
|
||||
Gtk.ApplicationWindow.__init__(self)
|
||||
self.set_name('GroupchatJoin')
|
||||
self.set_position(Gtk.WindowPosition.CENTER)
|
||||
self.set_show_menubar(False)
|
||||
self.set_title('Test Group chat info')
|
||||
|
||||
self._main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
|
||||
spacing=18)
|
||||
self._main_box.set_valign(Gtk.Align.FILL)
|
||||
|
||||
self._muc_info_box = GroupChatInfoScrolled(None)
|
||||
self._muc_info_box.set_vexpand(True)
|
||||
|
||||
self._main_box.add(self._muc_info_box)
|
||||
|
||||
self.add(self._main_box)
|
||||
self._muc_info_box.set_from_disco_info(disco_info)
|
||||
self._muc_info_box.set_subject(
|
||||
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr sed '
|
||||
'diam nonumy eirmod tempor invidunt ut labore et dolore magna')
|
||||
self._muc_info_box.set_author('userX', None)
|
||||
self.show_all()
|
||||
|
||||
|
||||
win = GroupchatInfo()
|
||||
win.connect('destroy', Gtk.main_quit)
|
||||
win.show_all()
|
||||
Gtk.main()
|
214
test/gtk/htmltextview.py
Normal file
214
test/gtk/htmltextview.py
Normal file
|
@ -0,0 +1,214 @@
|
|||
from unittest.mock import MagicMock
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
from gajim.common import app
|
||||
from gajim.common import configpaths
|
||||
configpaths.init()
|
||||
|
||||
|
||||
from gajim import gui
|
||||
gui.init('gtk')
|
||||
|
||||
from gajim.common.helpers import AdditionalDataDict
|
||||
|
||||
from gajim.conversation_textview import ConversationTextview
|
||||
from gajim.gui_interface import Interface
|
||||
|
||||
|
||||
app.settings = MagicMock()
|
||||
app.plugin_manager = MagicMock()
|
||||
app.logger = MagicMock()
|
||||
app.cert_store = MagicMock()
|
||||
app.storage = MagicMock()
|
||||
app.interface = Interface()
|
||||
|
||||
|
||||
XHTML = [
|
||||
'''
|
||||
<div>
|
||||
<span style="color: red; text-decoration:underline">Hello</span>
|
||||
<br/>\n
|
||||
<img src="http://images.slashdot.org/topics/topicsoftware.gif"/>
|
||||
<br/>\n
|
||||
<span style="font-size: 500%; font-family: serif">World</span>\n
|
||||
</div>
|
||||
''',
|
||||
|
||||
'''
|
||||
<hr />
|
||||
''',
|
||||
|
||||
'''
|
||||
<body xmlns='http://www.w3.org/1999/xhtml'>
|
||||
<p xmlns='http://www.w3.org/1999/xhtml'>Look here
|
||||
<a href='http://google.com/'>Google</a>
|
||||
</p>
|
||||
<br/>
|
||||
</body>
|
||||
''',
|
||||
|
||||
'''
|
||||
<hr />
|
||||
''',
|
||||
|
||||
'''
|
||||
<body xmlns='http://www.w3.org/1999/xhtml'>
|
||||
<p style='font-size:large'>
|
||||
<span style='font-style: italic'>O
|
||||
<span style='font-size:larger'>M</span>G
|
||||
</span>, I'm <span style='color:green'>green</span> with
|
||||
<span style='font-weight: bold'>envy</span>!
|
||||
</p>
|
||||
</body>
|
||||
''',
|
||||
|
||||
'''
|
||||
<hr />
|
||||
''',
|
||||
|
||||
'''
|
||||
<body xmlns='http://www.w3.org/1999/xhtml'>
|
||||
<p>
|
||||
As Emerson said in his essay
|
||||
<span style='font-style: italic; background-color:cyan'>
|
||||
Self-Reliance</span>:
|
||||
</p>
|
||||
<p style='margin-left: 5px; margin-right: 2%'>
|
||||
"A foolish consistency is the hobgoblin of little minds."
|
||||
</p>
|
||||
</body>
|
||||
''',
|
||||
|
||||
'''
|
||||
<hr />
|
||||
''',
|
||||
|
||||
'''
|
||||
<body xmlns='http://www.w3.org/1999/xhtml'>
|
||||
<p style='text-align:center'>
|
||||
Hey, are you licensed to <a href='http://www.jabber.org/'>Jabber</a>?
|
||||
</p>
|
||||
<p style='text-align:right'>
|
||||
<img src='http://www.xmpp.org/images/psa-license.jpg'
|
||||
alt='A License to Jabber' width='50%' height='50%'/>
|
||||
</p>
|
||||
</body>
|
||||
''',
|
||||
|
||||
'''
|
||||
<hr />
|
||||
''',
|
||||
|
||||
'''
|
||||
<body xmlns='http://www.w3.org/1999/xhtml'>
|
||||
<ul style='background-color:rgb(120,140,100)'>
|
||||
<li> One </li>
|
||||
<li> Two </li>
|
||||
<li> Three </li>
|
||||
</ul>
|
||||
<hr />
|
||||
<pre style="background-color:rgb(120,120,120)">def fac(n):
|
||||
def faciter(n,acc):
|
||||
if n==0: return acc
|
||||
return faciter(n-1, acc*n)
|
||||
if n<0: raise ValueError('Must be non-negative')
|
||||
return faciter(n,1)</pre>
|
||||
</body>
|
||||
''',
|
||||
|
||||
'''
|
||||
<hr />
|
||||
''',
|
||||
|
||||
'''
|
||||
<body xmlns='http://www.w3.org/1999/xhtml'>
|
||||
<ol style='background-color:rgb(120,140,100)'>
|
||||
<li> One </li>
|
||||
<li>
|
||||
Two is nested:
|
||||
<ul style='background-color:rgb(200,200,100)'>
|
||||
<li> One </li>
|
||||
<li style='font-size:50%'> Two </li>
|
||||
<li style='font-size:200%'> Three </li>
|
||||
<li style='font-size:9999pt'> Four </li>
|
||||
</ul>
|
||||
</li>
|
||||
<li> Three </li>
|
||||
</ol>
|
||||
</body>
|
||||
''',
|
||||
|
||||
'''
|
||||
<hr />
|
||||
''',
|
||||
|
||||
'''
|
||||
<body xmlns='http://www.w3.org/1999/xhtml'>
|
||||
<p>
|
||||
<strong>
|
||||
<a href='xmpp:example@example.org'>xmpp link</a>
|
||||
</strong>:
|
||||
</p>
|
||||
<div xmlns='http://www.w3.org/1999/xhtml'>
|
||||
<cite style='margin: 7px;' title='xmpp:examples@example.org'>
|
||||
<p>
|
||||
<strong>examples@example.org wrote:</strong>
|
||||
</p>
|
||||
<p>this cite - bla bla bla, smile- :-) …</p>
|
||||
</cite>
|
||||
<div>
|
||||
<p>some text</p>
|
||||
</div>
|
||||
</div>
|
||||
<p/>
|
||||
<p>#232/1</p>
|
||||
</body>
|
||||
''',
|
||||
|
||||
'''
|
||||
<hr />
|
||||
''',
|
||||
|
||||
'''
|
||||
<body xmlns='http://www.w3.org/1999/xhtml'>
|
||||
<img src='data:image/png;base64,R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAw\
|
||||
AAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFz\
|
||||
ByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSp\
|
||||
a/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJl\
|
||||
ZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uis\
|
||||
F81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PH\
|
||||
hhx4dbgYKAAA7' alt='Larry'/>
|
||||
</body>
|
||||
''',
|
||||
|
||||
]
|
||||
|
||||
|
||||
class TextviewWindow(Gtk.Window):
|
||||
def __init__(self):
|
||||
Gtk.Window.__init__(self, title="Textview Test")
|
||||
self.set_default_size(600, 600)
|
||||
|
||||
self._textview = ConversationTextview(None)
|
||||
|
||||
scrolled = Gtk.ScrolledWindow()
|
||||
scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
scrolled.add(self._textview.tv)
|
||||
self.add(scrolled)
|
||||
self.show()
|
||||
self._print_xhtml()
|
||||
|
||||
def _print_xhtml(self):
|
||||
for xhtml in XHTML:
|
||||
additional_data = AdditionalDataDict()
|
||||
additional_data.set_value('gajim', 'xhtml', xhtml)
|
||||
self._textview.print_real_text(None, additional_data=additional_data)
|
||||
self._textview.print_real_text('\n')
|
||||
|
||||
win = TextviewWindow()
|
||||
win.connect("destroy", Gtk.main_quit)
|
||||
win.show_all()
|
||||
Gtk.main()
|
55
test/gtk/ssl_error_dialog.py
Normal file
55
test/gtk/ssl_error_dialog.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
import OpenSSL
|
||||
|
||||
from gajim import gui
|
||||
gui.init('gtk')
|
||||
|
||||
from test.gtk import util
|
||||
from gajim.common.const import CSSPriority
|
||||
from gajim.gui.ssl_error_dialog import SSLErrorDialog
|
||||
|
||||
util.load_style('gajim.css', CSSPriority.APPLICATION)
|
||||
|
||||
cert = '''
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFhDCCBGygAwIBAgISA4oUEifTr7Y+mcdiwu6KWpcVMA0GCSqGSIb3DQEBCwUA
|
||||
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
|
||||
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA0MDMxODE3NDVaFw0x
|
||||
OTA3MDIxODE3NDVaMBsxGTAXBgNVBAMMECoubGlnaHR3aXRjaC5vcmcwggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/3mcevikse7QwDYwPcGAD9zHw3UWE
|
||||
7J8SJR349/rFTF2tBFDvEa62OUKTCg5vPKVKaXHlzruk/A7blgqsEdugycORwPD1
|
||||
7YNJ27EldrRtotjclurzKL6D/MgcaQ4cTkPOD3cWbf/L+HClGrpFt7su6Z6cTutC
|
||||
wiAYAdlfmVgSSv15F1xOTyFyfGJKQnW628Xs8xUvZh5H/SsEEum4MwVVGW06Z/A/
|
||||
mwX2jmJUb2M25S1Ma025nZpGYyAAqecTmPb3fStnXm4sdytfZhm4+nj9mH9GQIU1
|
||||
t/jO/7X7IFpc9DvVRSumSVqvNaVgiWmTLP4VxlCVJO6mibOXXUUDA5RfAgMBAAGj
|
||||
ggKRMIICjTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
|
||||
AQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFC4/ZRurw2wVFsgJBTb9Fh/3
|
||||
0aV9MB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEB
|
||||
BGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0
|
||||
Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0
|
||||
Lm9yZy8wSQYDVR0RBEIwQIIQKi5saWdodHdpdGNoLm9yZ4IOKi5tZXRyb25vbWUu
|
||||
aW2CDmxpZ2h0d2l0Y2gub3JnggxtZXRyb25vbWUuaW0wTAYDVR0gBEUwQzAIBgZn
|
||||
gQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5s
|
||||
ZXRzZW5jcnlwdC5vcmcwggECBgorBgEEAdZ5AgQCBIHzBIHwAO4AdQB0ftqDMa0z
|
||||
EJEhnM4lT0Jwwr/9XkIgCMY3NXnmEHvMVgAAAWnkosBAAAAEAwBGMEQCICEfmTBk
|
||||
OxS95eiYsfTH5HdL7kfp68BSin5LqeGyyxk9AiA3qeDZNKklJTdWqYjto7kUqJNd
|
||||
YiL99SrqwzR6w+AqSwB1ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM9OVFR/R4
|
||||
AAABaeSiwEUAAAQDAEYwRAIgFxouOkJeqkQUe6zNI5w/6YBIQFrsrIZdPcX+r6JI
|
||||
is8CIEEETzlEyj9lWR/BSSruSp0FT5CuoNNeEG7HxrJ+gVhZMA0GCSqGSIb3DQEB
|
||||
CwUAA4IBAQAQtfs1NPNMmBQRcKsZyGLZsvpp2hIhdYi72RYnHnIl4MXbhyNj9xtI
|
||||
cJr9PQ+3FsSnxy7LDjZMpbmBuXhawOyPBPw2M0f0Tv6Eo6miwvP/X1kLE3VjTzCo
|
||||
6JPh6bEB5wa+kH/pUcGlV6uyT7IuXOiArx0VmIpTA3uwlVdfynOnR3CF20Ds4FLc
|
||||
JxbGMqRuw/sGiTLKlXc1xVil8WZjL3hokzrgI7K6np2skUjWuMZvhJgwi5QiE7/C
|
||||
ejsJoYkpvcaiaLAyVymTY/n/oM2oQpv5Mqjit+18RB9c2P+ifH5iDKC/jTKn4NNz
|
||||
8xSTlUlCBTCozjzscZVeVDIojmejWclT
|
||||
-----END CERTIFICATE-----'''
|
||||
|
||||
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
|
||||
ssl_error_num = 10
|
||||
win = SSLErrorDialog('testacc', None, cert, ssl_error_num)
|
||||
win.connect('destroy', Gtk.main_quit)
|
||||
win.show_all()
|
||||
Gtk.main()
|
23
test/gtk/util.py
Normal file
23
test/gtk/util.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
from pathlib import Path
|
||||
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
def get_gajim_dir():
|
||||
gajim_path = Path(__file__) / '..' / '..' / '..' / 'gajim'
|
||||
return gajim_path.resolve()
|
||||
|
||||
def load_style(filename, priority):
|
||||
path = get_gajim_dir() / 'data' / 'style' / filename
|
||||
try:
|
||||
with open(str(path), "r") as file:
|
||||
css = file.read()
|
||||
except Exception as exc:
|
||||
print(exc)
|
||||
return
|
||||
provider = Gtk.CssProvider()
|
||||
provider.load_from_data(bytes(css.encode('utf-8')))
|
||||
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
|
||||
provider,
|
||||
priority)
|
46
test/lib/__init__.py
Normal file
46
test/lib/__init__.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
import os
|
||||
|
||||
from tempfile import gettempdir
|
||||
|
||||
# a temporary version of ~/.gajim for testing
|
||||
configdir = os.path.join(gettempdir(), 'gajim')
|
||||
os.makedirs(configdir, exist_ok=True)
|
||||
|
||||
# plugins config dir
|
||||
pluginsconfigdir = configdir + '/pluginsconfig'
|
||||
# theme config directory
|
||||
themedir = configdir + '/theme'
|
||||
|
||||
# define _ for i18n
|
||||
import builtins
|
||||
builtins._ = lambda x: x
|
||||
|
||||
from gajim.common.contacts import LegacyContactsAPI
|
||||
|
||||
def setup_env(use_x=True):
|
||||
# wipe config directory
|
||||
if os.path.isdir(configdir):
|
||||
import shutil
|
||||
shutil.rmtree(configdir)
|
||||
|
||||
os.mkdir(configdir)
|
||||
os.mkdir(pluginsconfigdir)
|
||||
os.mkdir(themedir)
|
||||
|
||||
from gajim.common import configpaths
|
||||
configpaths.set_config_root(configdir)
|
||||
configpaths.init()
|
||||
|
||||
# for some reason gajim.common.app needs to be imported before xmpppy?
|
||||
from gajim.common import app
|
||||
|
||||
import logging
|
||||
logging.basicConfig()
|
||||
|
||||
app.use_x = use_x
|
||||
app.contacts = LegacyContactsAPI()
|
||||
app.connections = {}
|
||||
|
||||
if use_x:
|
||||
from gajim.application import GajimApplication
|
||||
app.app = GajimApplication()
|
77
test/lib/data.py
Executable file
77
test/lib/data.py
Executable file
|
@ -0,0 +1,77 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
account1 = 'acc1'
|
||||
account2 = 'Cool"chârßéµö'
|
||||
account3 = 'dingdong.org'
|
||||
|
||||
contacts = {}
|
||||
contacts[account1] = {
|
||||
'myjid@'+account1: {
|
||||
'ask': None, 'groups': [], 'name': None, 'resources': {},
|
||||
'subscription': 'both'},
|
||||
'default1@gajim.org': {
|
||||
'ask': None, 'groups': [], 'name': None, 'resources': {},
|
||||
'subscription': 'both'},
|
||||
'default2@gajim.org': {
|
||||
'ask': None, 'groups': ['GroupA',], 'name': None, 'resources': {},
|
||||
'subscription': 'both'},
|
||||
'Cool"chârßéµö@gajim.org': {
|
||||
'ask': None, 'groups': ['<Cool"chârßéµö', 'GroupB'],
|
||||
'name': None, 'resources': {}, 'subscription': 'both'},
|
||||
'samejid@gajim.org': {
|
||||
'ask': None, 'groups': ['GroupA',], 'name': None, 'resources': {},
|
||||
'subscription': 'both'}
|
||||
}
|
||||
contacts[account2] = {
|
||||
'myjid@'+account2: {
|
||||
'ask': None, 'groups': [], 'name': None, 'resources': {},
|
||||
'subscription': 'both'},
|
||||
'default3@gajim.org': {
|
||||
'ask': None, 'groups': ['GroupC',], 'name': None, 'resources': {},
|
||||
'subscription': 'both'},
|
||||
'asksubfrom@gajim.org': {
|
||||
'ask': 'subscribe', 'groups': ['GroupA',], 'name': None,
|
||||
'resources': {}, 'subscription': 'from'},
|
||||
'subto@gajim.org': {
|
||||
'ask': None, 'groups': ['GroupB'], 'name': None, 'resources': {},
|
||||
'subscription': 'to'},
|
||||
'samejid@gajim.org': {
|
||||
'ask': None, 'groups': ['GroupA', 'GroupB'], 'name': None,
|
||||
'resources': {}, 'subscription': 'both'}
|
||||
}
|
||||
contacts[account3] = {
|
||||
#'guypsych0\\40h.com@msn.dingdong.org': {
|
||||
# 'ask': None, 'groups': [], 'name': None, 'resources': {},
|
||||
# 'subscription': 'both'},
|
||||
'guypsych0%h.com@msn.delx.cjb.net': {
|
||||
'ask': 'subscribe', 'groups': [], 'name': None,
|
||||
'resources': {}, 'subscription': 'from'},
|
||||
#'guypsych0%h.com@msn.jabber.wiretrip.org': {
|
||||
# 'ask': None, 'groups': [], 'name': None, 'resources': {},
|
||||
# 'subscription': 'to'},
|
||||
#'guypsycho\\40g.com@gtalk.dingdong.org': {
|
||||
# 'ask': None, 'groups': [], 'name': None,
|
||||
# 'resources': {}, 'subscription': 'both'}
|
||||
}
|
||||
|
||||
# We have contacts that are not in roster but only specified in the metadata
|
||||
metacontact_data = [
|
||||
[{'account': account3,
|
||||
'jid': 'guypsych0\\40h.com@msn.dingdong.org',
|
||||
'order': 0},
|
||||
{'account': account3,
|
||||
'jid': 'guypsych0%h.com@msn.delx.cjb.net',
|
||||
'order': 0},
|
||||
{'account': account3,
|
||||
'jid': 'guypsych0%h.com@msn.jabber.wiretrip.org',
|
||||
'order': 0},
|
||||
{'account': account3,
|
||||
'jid': 'guypsycho\\40g.com@gtalk.dingdong.org',
|
||||
'order': 0}],
|
||||
|
||||
[{'account': account1,
|
||||
'jid': 'samejid@gajim.org',
|
||||
'order': 0},
|
||||
{'account': account2,
|
||||
'jid': 'samejid@gajim.org',
|
||||
'order': 0}]
|
||||
]
|
139
test/lib/gajim_mocks.py
Normal file
139
test/lib/gajim_mocks.py
Normal file
|
@ -0,0 +1,139 @@
|
|||
'''
|
||||
Module with dummy classes for Gajim specific unit testing
|
||||
'''
|
||||
|
||||
from .mock import Mock
|
||||
from gajim.common import app
|
||||
from gajim.common import ged
|
||||
|
||||
from gajim.common.connection_handlers import ConnectionHandlers
|
||||
|
||||
class MockConnection(Mock, ConnectionHandlers):
|
||||
def __init__(self, account, *args):
|
||||
Mock.__init__(self, *args)
|
||||
|
||||
self.connection = Mock()
|
||||
|
||||
self.name = account
|
||||
|
||||
ConnectionHandlers.__init__(self)
|
||||
|
||||
self.connected = 2
|
||||
self.pep = {}
|
||||
self.sessions = {}
|
||||
self.server_resource = 'Gajim'
|
||||
|
||||
app.interface.instances[account] = {'infos': {}, 'disco': {},
|
||||
'gc_config': {}, 'search': {}, 'sub_request': {}}
|
||||
app.interface.minimized_controls[account] = {}
|
||||
app.contacts.add_account(account)
|
||||
app.groups[account] = {}
|
||||
app.gc_connected[account] = {}
|
||||
app.automatic_rooms[account] = {}
|
||||
app.newly_added[account] = []
|
||||
app.to_be_removed[account] = []
|
||||
app.nicks[account] = app.settings.get_account_setting(account, 'name')
|
||||
app.block_signed_in_notifications[account] = True
|
||||
app.last_message_time[account] = {}
|
||||
|
||||
app.connections[account] = self
|
||||
|
||||
class MockWindow(Mock):
|
||||
def __init__(self, *args):
|
||||
Mock.__init__(self, *args)
|
||||
self.window = Mock()
|
||||
self._controls = {}
|
||||
|
||||
def get_control(self, jid, account):
|
||||
try:
|
||||
return self._controls[account][jid]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def has_control(self, jid, acct):
|
||||
return self.get_control(jid, acct) is not None
|
||||
|
||||
def new_tab(self, ctrl):
|
||||
account = ctrl.account
|
||||
jid = ctrl.jid
|
||||
|
||||
if account not in self._controls:
|
||||
self._controls[account] = {}
|
||||
|
||||
if jid not in self._controls[account]:
|
||||
self._controls[account][jid] = {}
|
||||
|
||||
self._controls[account][jid] = ctrl
|
||||
|
||||
def __nonzero__(self):
|
||||
return True
|
||||
|
||||
class MockChatControl(Mock):
|
||||
def __init__(self, jid, account, *args):
|
||||
Mock.__init__(self, *args)
|
||||
|
||||
self.jid = jid
|
||||
self.account = account
|
||||
|
||||
self.parent_win = MockWindow({'get_active_control': self})
|
||||
self.session = None
|
||||
|
||||
def set_session(self, sess):
|
||||
self.session = sess
|
||||
|
||||
def __nonzero__(self):
|
||||
return True
|
||||
|
||||
def __eq__(self, other):
|
||||
return self is other
|
||||
|
||||
|
||||
class MockInterface(Mock):
|
||||
def __init__(self, *args):
|
||||
Mock.__init__(self, *args)
|
||||
app.interface = self
|
||||
self.msg_win_mgr = Mock()
|
||||
self.roster = Mock()
|
||||
app.ged = ged.GlobalEventsDispatcher()
|
||||
from gajim import plugins
|
||||
app.plugin_manager = plugins.PluginManager()
|
||||
|
||||
self.instances = {}
|
||||
self.minimized_controls = {}
|
||||
|
||||
|
||||
class MockLogger(Mock):
|
||||
def __init__(self):
|
||||
Mock.__init__(self, {'insert_into_logs': None,
|
||||
'get_transports_type': {}})
|
||||
self.cur = Mock()
|
||||
|
||||
|
||||
class MockContact(Mock):
|
||||
def __nonzero__(self):
|
||||
return True
|
||||
|
||||
|
||||
import random
|
||||
|
||||
class MockSession(Mock):
|
||||
def __init__(self, conn, jid, thread_id, type_):
|
||||
Mock.__init__(self)
|
||||
|
||||
self.conn = conn
|
||||
self.jid = jid
|
||||
self.type_ = type_
|
||||
self.thread_id = thread_id
|
||||
self.resource = ''
|
||||
|
||||
if not self.thread_id:
|
||||
self.thread_id = '%0x' % random.randint(0, 10000)
|
||||
|
||||
def __repr__(self):
|
||||
return '<MockSession %s>' % self.thread_id
|
||||
|
||||
def __nonzero__(self):
|
||||
return True
|
||||
|
||||
def __eq__(self, other):
|
||||
return self is other
|
466
test/lib/mock.py
Normal file
466
test/lib/mock.py
Normal file
|
@ -0,0 +1,466 @@
|
|||
#
|
||||
# (c) Dave Kirby 2001 - 2005
|
||||
# mock@thedeveloperscoach.com
|
||||
#
|
||||
# Original call interceptor and call assertion code by Phil Dawes (pdawes@users.sourceforge.net)
|
||||
# Call interceptor code enhanced by Bruce Cropley (cropleyb@yahoo.com.au)
|
||||
#
|
||||
# This Python module and associated files are released under the FreeBSD
|
||||
# license. Essentially, you can do what you like with it except pretend you wrote
|
||||
# it yourself.
|
||||
#
|
||||
#
|
||||
# Copyright (c) 2005, Dave Kirby
|
||||
# Copyright (c) 2009, Yann Leboulanger
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# * Neither the name of this library nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# mock@thedeveloperscoach.com
|
||||
|
||||
|
||||
"""
|
||||
Mock object library for Python. Mock objects can be used when unit testing
|
||||
to remove a dependency on another production class. They are typically used
|
||||
when the dependency would either pull in lots of other classes, or
|
||||
significantly slow down the execution of the test.
|
||||
They are also used to create exceptional conditions that cannot otherwise
|
||||
be easily triggered in the class under test.
|
||||
"""
|
||||
|
||||
__version__ = "0.1.0"
|
||||
|
||||
# Added in Python 2.1
|
||||
import inspect
|
||||
import re
|
||||
|
||||
class MockInterfaceError(Exception):
|
||||
pass
|
||||
|
||||
class Mock(object):
|
||||
"""
|
||||
The Mock class emulates any other class for testing purposes.
|
||||
All method calls are stored for later examination.
|
||||
"""
|
||||
|
||||
def __init__(self, returnValues=None, realClass=None):
|
||||
"""
|
||||
The Mock class constructor takes a dictionary of method names and
|
||||
the values they return. Methods that are not in the returnValues
|
||||
dictionary will return None.
|
||||
You may also supply a class whose interface is being mocked.
|
||||
All calls will be checked to see if they appear in the original
|
||||
interface. Any calls to methods not appearing in the real class
|
||||
will raise a MockInterfaceError. Any calls that would fail due to
|
||||
non-matching parameter lists will also raise a MockInterfaceError.
|
||||
Both of these help to prevent the Mock class getting out of sync
|
||||
with the class it is Mocking.
|
||||
"""
|
||||
self.mockCalledMethods = {}
|
||||
self.mockAllCalledMethods = []
|
||||
self.mockReturnValues = returnValues or {}
|
||||
self.mockExpectations = {}
|
||||
self.realClass = realClass
|
||||
self.realClassMethods = None
|
||||
if realClass:
|
||||
self.realClassMethods = dict(inspect.getmembers(realClass, inspect.isroutine))
|
||||
for retMethod in self.mockReturnValues.keys():
|
||||
if retMethod not in self.realClassMethods:
|
||||
raise MockInterfaceError("Return value supplied for method '%s' that was not in the original class" % retMethod)
|
||||
self._setupSubclassMethodInterceptors()
|
||||
|
||||
def _setupSubclassMethodInterceptors(self):
|
||||
methods = inspect.getmembers(self.realClass, inspect.isroutine)
|
||||
baseMethods = dict(inspect.getmembers(Mock, inspect.ismethod))
|
||||
for m in methods:
|
||||
name = m[0]
|
||||
# Don't record calls to methods of Mock base class.
|
||||
if not name in baseMethods:
|
||||
self.__dict__[name] = MockCallable(name, self, handcrafted=True)
|
||||
|
||||
def get_module(self, name):
|
||||
return Mock()
|
||||
|
||||
def __getattr__(self, name):
|
||||
return MockCallable(name, self)
|
||||
|
||||
def mockAddReturnValues(self, **methodReturnValues ):
|
||||
self.mockReturnValues.update(methodReturnValues)
|
||||
|
||||
def mockSetExpectation(self, name, testFn, after=0, until=0):
|
||||
self.mockExpectations.setdefault(name, []).append((testFn, after, until))
|
||||
|
||||
def _checkInterfaceCall(self, name, callParams, callKwParams):
|
||||
"""
|
||||
Check that a call to a method of the given name to the original
|
||||
class with the given parameters would not fail. If it would fail,
|
||||
raise a MockInterfaceError.
|
||||
Based on the Python 2.3.3 Reference Manual section 5.3.4: Calls.
|
||||
"""
|
||||
if self.realClassMethods is None:
|
||||
return
|
||||
if name not in self.realClassMethods:
|
||||
return
|
||||
|
||||
func = self.realClassMethods[name]
|
||||
try:
|
||||
args, varargs, varkw, defaults = inspect.getargspec(func)
|
||||
except TypeError:
|
||||
# func is not a Python function. It is probably a builtin,
|
||||
# such as __repr__ or __coerce__. TODO: Checking?
|
||||
# For now assume params are OK.
|
||||
return
|
||||
|
||||
# callParams doesn't include self; args does include self.
|
||||
numPosCallParams = 1 + len(callParams)
|
||||
|
||||
if numPosCallParams > len(args) and not varargs:
|
||||
raise MockInterfaceError("Original %s() takes at most %s arguments (%s given)" %
|
||||
(name, len(args), numPosCallParams))
|
||||
|
||||
# Get the number of positional arguments that appear in the call,
|
||||
# also check for duplicate parameters and unknown parameters
|
||||
numPosSeen = _getNumPosSeenAndCheck(numPosCallParams, callKwParams, args, varkw)
|
||||
|
||||
lenArgsNoDefaults = len(args) - len(defaults or [])
|
||||
if numPosSeen < lenArgsNoDefaults:
|
||||
raise MockInterfaceError("Original %s() takes at least %s arguments (%s given)" % (name, lenArgsNoDefaults, numPosSeen))
|
||||
|
||||
def mockGetAllCalls(self):
|
||||
"""
|
||||
Return a list of MockCall objects,
|
||||
representing all the methods in the order they were called.
|
||||
"""
|
||||
return self.mockAllCalledMethods
|
||||
getAllCalls = mockGetAllCalls # deprecated - kept for backward compatibility
|
||||
|
||||
def mockGetNamedCalls(self, methodName):
|
||||
"""
|
||||
Return a list of MockCall objects,
|
||||
representing all the calls to the named method in the order they were called.
|
||||
"""
|
||||
return self.mockCalledMethods.get(methodName, [])
|
||||
getNamedCalls = mockGetNamedCalls # deprecated - kept for backward compatibility
|
||||
|
||||
def mockCheckCall(self, index, name, *args, **kwargs):
|
||||
'''test that the index-th call had the specified name and parameters'''
|
||||
call = self.mockAllCalledMethods[index]
|
||||
assert name == call.getName(), "%r != %r" % (name, call.getName())
|
||||
call.checkArgs(*args, **kwargs)
|
||||
|
||||
|
||||
def _getNumPosSeenAndCheck(numPosCallParams, callKwParams, args, varkw):
|
||||
"""
|
||||
Positional arguments can appear as call parameters either named as
|
||||
a named (keyword) parameter, or just as a value to be matched by
|
||||
position. Count the positional arguments that are given by either
|
||||
keyword or position, and check for duplicate specifications.
|
||||
Also check for arguments specified by keyword that do not appear
|
||||
in the method's parameter list.
|
||||
"""
|
||||
posSeen = {}
|
||||
for arg in args[:numPosCallParams]:
|
||||
posSeen[arg] = True
|
||||
for kwp in callKwParams:
|
||||
if kwp in posSeen:
|
||||
raise MockInterfaceError("%s appears as both a positional and named parameter." % kwp)
|
||||
if kwp in args:
|
||||
posSeen[kwp] = True
|
||||
elif not varkw:
|
||||
raise MockInterfaceError("Original method does not have a parameter '%s'" % kwp)
|
||||
return len(posSeen)
|
||||
|
||||
class MockCall:
|
||||
"""
|
||||
MockCall records the name and parameters of a call to an instance
|
||||
of a Mock class. Instances of MockCall are created by the Mock class,
|
||||
but can be inspected later as part of the test.
|
||||
"""
|
||||
def __init__(self, name, params, kwparams ):
|
||||
self.name = name
|
||||
self.params = params
|
||||
self.kwparams = kwparams
|
||||
|
||||
def checkArgs(self, *args, **kwargs):
|
||||
assert args == self.params, "%r != %r" % (args, self.params)
|
||||
assert kwargs == self.kwparams, "%r != %r" % (kwargs, self.kwparams)
|
||||
|
||||
def getParam( self, n ):
|
||||
if isinstance(n, int):
|
||||
return self.params[n]
|
||||
elif isinstance(n, str):
|
||||
return self.kwparams[n]
|
||||
else:
|
||||
raise IndexError('illegal index type for getParam')
|
||||
|
||||
def getNumParams(self):
|
||||
return len(self.params)
|
||||
|
||||
def getNumKwParams(self):
|
||||
return len(self.kwparams)
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
|
||||
#pretty-print the method call
|
||||
def __str__(self):
|
||||
s = self.name + "("
|
||||
sep = ''
|
||||
for p in self.params:
|
||||
s = s + sep + repr(p)
|
||||
sep = ', '
|
||||
items = sorted(self.kwparams.items())
|
||||
for k, v in items:
|
||||
s = s + sep + k + '=' + repr(v)
|
||||
sep = ', '
|
||||
s = s + ')'
|
||||
return s
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
class MockCallable:
|
||||
"""
|
||||
Intercepts the call and records it, then delegates to either the mock's
|
||||
dictionary of mock return values that was passed in to the constructor,
|
||||
or a handcrafted method of a Mock subclass.
|
||||
"""
|
||||
def __init__(self, name, mock, handcrafted=False):
|
||||
self.name = name
|
||||
self.mock = mock
|
||||
self.handcrafted = handcrafted
|
||||
|
||||
def __call__(self, *params, **kwparams):
|
||||
self.mock._checkInterfaceCall(self.name, params, kwparams)
|
||||
thisCall = self.recordCall(params, kwparams)
|
||||
self.checkExpectations(thisCall, params, kwparams)
|
||||
return self.makeCall(params, kwparams)
|
||||
|
||||
def recordCall(self, params, kwparams):
|
||||
"""
|
||||
Record the MockCall in an ordered list of all calls, and an ordered
|
||||
list of calls for that method name.
|
||||
"""
|
||||
thisCall = MockCall(self.name, params, kwparams)
|
||||
calls = self.mock.mockCalledMethods.setdefault(self.name, [])
|
||||
calls.append(thisCall)
|
||||
self.mock.mockAllCalledMethods.append(thisCall)
|
||||
return thisCall
|
||||
|
||||
def makeCall(self, params, kwparams):
|
||||
if self.handcrafted:
|
||||
allPosParams = (self.mock,) + params
|
||||
func = _findFunc(self.mock.realClass, self.name)
|
||||
if not func:
|
||||
raise NotImplementedError
|
||||
return func(*allPosParams, **kwparams)
|
||||
else:
|
||||
returnVal = self.mock.mockReturnValues.get(self.name)
|
||||
if isinstance(returnVal, ReturnValuesBase):
|
||||
returnVal = returnVal.next()
|
||||
return returnVal
|
||||
|
||||
def checkExpectations(self, thisCall, params, kwparams):
|
||||
if self.name in self.mock.mockExpectations:
|
||||
callsMade = len(self.mock.mockCalledMethods[self.name])
|
||||
for (expectation, after, until) in self.mock.mockExpectations[self.name]:
|
||||
if callsMade > after and (until==0 or callsMade < until):
|
||||
assert expectation(self.mock, thisCall, len(self.mock.mockAllCalledMethods)-1), 'Expectation failed: '+str(thisCall)
|
||||
|
||||
|
||||
def _findFunc(cl, name):
|
||||
""" Depth first search for a method with a given name. """
|
||||
if name in cl.__dict__:
|
||||
return cl.__dict__[name]
|
||||
for base in cl.__bases__:
|
||||
func = _findFunc(base, name)
|
||||
if func:
|
||||
return func
|
||||
return None
|
||||
|
||||
|
||||
|
||||
class ReturnValuesBase:
|
||||
def next(self):
|
||||
try:
|
||||
return self.iter.next()
|
||||
except StopIteration:
|
||||
raise AssertionError("No more return values")
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
class ReturnValues(ReturnValuesBase):
|
||||
def __init__(self, *values):
|
||||
self.iter = iter(values)
|
||||
|
||||
|
||||
class ReturnIterator(ReturnValuesBase):
|
||||
def __init__(self, iterator):
|
||||
self.iter = iter(iterator)
|
||||
|
||||
|
||||
def expectParams(*params, **keywords):
|
||||
'''check that the callObj is called with specified params and keywords
|
||||
'''
|
||||
def fn(mockObj, callObj, idx):
|
||||
return callObj.params == params and callObj.kwparams == keywords
|
||||
return fn
|
||||
|
||||
|
||||
def expectAfter(*methods):
|
||||
'''check that the function is only called after all the functions in 'methods'
|
||||
'''
|
||||
def fn(mockObj, callObj, idx):
|
||||
calledMethods = [method.getName() for method in mockObj.mockGetAllCalls()]
|
||||
#skip last entry, since that is the current call
|
||||
calledMethods = calledMethods[:-1]
|
||||
for method in methods:
|
||||
if method not in calledMethods:
|
||||
return False
|
||||
return True
|
||||
return fn
|
||||
|
||||
def expectException(exception, *args, **kwargs):
|
||||
''' raise an exception when the method is called
|
||||
'''
|
||||
def fn(mockObj, callObj, idx):
|
||||
raise exception(*args, **kwargs)
|
||||
return fn
|
||||
|
||||
|
||||
def expectParam(paramIdx, cond):
|
||||
'''check that the callObj is called with parameter specified by paramIdx (a position index or keyword)
|
||||
fulfills the condition specified by cond.
|
||||
cond is a function that takes a single argument, the value to test.
|
||||
'''
|
||||
def fn(mockObj, callObj, idx):
|
||||
param = callObj.getParam(paramIdx)
|
||||
return cond(param)
|
||||
return fn
|
||||
|
||||
def EQ(value):
|
||||
def testFn(param):
|
||||
return param == value
|
||||
return testFn
|
||||
|
||||
def NE(value):
|
||||
def testFn(param):
|
||||
return param != value
|
||||
return testFn
|
||||
|
||||
def GT(value):
|
||||
def testFn(param):
|
||||
return param > value
|
||||
return testFn
|
||||
|
||||
def LT(value):
|
||||
def testFn(param):
|
||||
return param < value
|
||||
return testFn
|
||||
|
||||
def GE(value):
|
||||
def testFn(param):
|
||||
return param >= value
|
||||
return testFn
|
||||
|
||||
def LE(value):
|
||||
def testFn(param):
|
||||
return param <= value
|
||||
return testFn
|
||||
|
||||
def AND(*condlist):
|
||||
def testFn(param):
|
||||
for cond in condlist:
|
||||
if not cond(param):
|
||||
return False
|
||||
return True
|
||||
return testFn
|
||||
|
||||
def OR(*condlist):
|
||||
def testFn(param):
|
||||
for cond in condlist:
|
||||
if cond(param):
|
||||
return True
|
||||
return False
|
||||
return testFn
|
||||
|
||||
def NOT(cond):
|
||||
def testFn(param):
|
||||
return not cond(param)
|
||||
return testFn
|
||||
|
||||
def MATCHES(regex, *args, **kwargs):
|
||||
compiled_regex = re.compile(regex, *args, **kwargs)
|
||||
def testFn(param):
|
||||
return compiled_regex.match(param) is not None
|
||||
return testFn
|
||||
|
||||
def SEQ(*sequence):
|
||||
iterator = iter(sequence)
|
||||
def testFn(param):
|
||||
try:
|
||||
cond = iterator.next()
|
||||
except StopIteration:
|
||||
raise AssertionError('SEQ exhausted')
|
||||
return cond(param)
|
||||
return testFn
|
||||
|
||||
def IS(instance):
|
||||
def testFn(param):
|
||||
return param is instance
|
||||
return testFn
|
||||
|
||||
def ISINSTANCE(class_):
|
||||
def testFn(param):
|
||||
return isinstance(param, class_)
|
||||
return testFn
|
||||
|
||||
def ISSUBCLASS(class_):
|
||||
def testFn(param):
|
||||
return issubclass(param, class_)
|
||||
return testFn
|
||||
|
||||
def CONTAINS(val):
|
||||
def testFn(param):
|
||||
return val in param
|
||||
return testFn
|
||||
|
||||
def IN(container):
|
||||
def testFn(param):
|
||||
return param in container
|
||||
return testFn
|
||||
|
||||
def HASATTR(attr):
|
||||
def testFn(param):
|
||||
return hasattr(param, attr)
|
||||
return testFn
|
||||
|
||||
def HASMETHOD(method):
|
||||
def testFn(param):
|
||||
return hasattr(param, method) and callable(getattr(param, method))
|
||||
return testFn
|
||||
|
||||
CALLABLE = callable
|
28
test/lib/notify.py
Normal file
28
test/lib/notify.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# mock notify module
|
||||
|
||||
from gajim.common import app
|
||||
from gajim.common import ged
|
||||
|
||||
notifications = []
|
||||
|
||||
class Notification:
|
||||
def _nec_notification(self, obj):
|
||||
global notifications
|
||||
notifications.append(obj)
|
||||
|
||||
def clean(self):
|
||||
global notifications
|
||||
notifications = []
|
||||
app.ged.remove_event_handler('notification', ged.GUI2,
|
||||
self._nec_notification)
|
||||
|
||||
def __init__(self):
|
||||
app.ged.register_event_handler('notification', ged.GUI2,
|
||||
self._nec_notification)
|
||||
|
||||
|
||||
def notify(event, jid, account, parameters, advanced_notif_num = None):
|
||||
notifications.append((event, jid, account, parameters, advanced_notif_num))
|
||||
|
||||
def get_advanced_notification(event, account, contact):
|
||||
return None
|
0
test/no_gui/__init__.py
Normal file
0
test/no_gui/__init__.py
Normal file
36
test/no_gui/test_regex.py
Normal file
36
test/no_gui/test_regex.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
import unittest
|
||||
|
||||
from gajim import gui
|
||||
gui.init('gtk')
|
||||
import gajim.common.regex as regex
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
def test_links_regexp_entire(self):
|
||||
def assert_matches_all(str_):
|
||||
m = regex.BASIC_REGEX.match(str_)
|
||||
|
||||
# the match should equal the string
|
||||
str_span = (0, len(str_))
|
||||
self.assertEqual(m.span(), str_span)
|
||||
|
||||
# these entire strings should be parsed as links
|
||||
assert_matches_all('http://google.com/')
|
||||
assert_matches_all('http://google.com')
|
||||
assert_matches_all('http://www.google.ca/search?q=xmpp')
|
||||
|
||||
assert_matches_all('http://tools.ietf.org/html/draft-saintandre-rfc3920bis-05#section-12.3')
|
||||
|
||||
assert_matches_all('http://en.wikipedia.org/wiki/Protocol_(computing)')
|
||||
assert_matches_all(
|
||||
'http://en.wikipedia.org/wiki/Protocol_%28computing%29')
|
||||
|
||||
assert_matches_all('mailto:test@example.org')
|
||||
|
||||
assert_matches_all('xmpp:example-node@example.com')
|
||||
assert_matches_all('xmpp:example-node@example.com/some-resource')
|
||||
assert_matches_all('xmpp:example-node@example.com?message')
|
||||
assert_matches_all('xmpp://guest@example.com/support@example.com?message')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
5
test/no_gui/unit/__init__.py
Normal file
5
test/no_gui/unit/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
'''
|
||||
|
||||
This package just contains plain unit tests
|
||||
|
||||
'''
|
50
test/no_gui/unit/test_nick_completion.py
Normal file
50
test/no_gui/unit/test_nick_completion.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
import unittest
|
||||
|
||||
from gajim import gui
|
||||
gui.init('gtk')
|
||||
from gajim.gui.util import NickCompletionGenerator
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
def test_generate_suggestions(self):
|
||||
gen = NickCompletionGenerator('meeeee')
|
||||
|
||||
l = ['aaaa', 'meeeee', 'fooo', 'xxxxz', 'xaaaz']
|
||||
for n in l:
|
||||
gen.record_message(n, False)
|
||||
l2 = ['xxx'] + l
|
||||
r = gen.generate_suggestions(nicks=l2, beginning='x')
|
||||
self.assertEqual(r, ['xaaaz', 'xxxxz', 'xxx'])
|
||||
|
||||
r = gen.generate_suggestions(
|
||||
nicks=l2,
|
||||
beginning='m'
|
||||
)
|
||||
self.assertEqual(r, [])
|
||||
|
||||
for n in ['xaaaz', 'xxxxz']:
|
||||
gen.record_message(n, True)
|
||||
|
||||
r = gen.generate_suggestions(
|
||||
nicks=l2,
|
||||
beginning='x'
|
||||
)
|
||||
self.assertEqual(r, ['xxxxz', 'xaaaz', 'xxx'])
|
||||
r = gen.generate_suggestions(
|
||||
nicks=l2,
|
||||
beginning=''
|
||||
)
|
||||
self.assertEqual(r, ['xxxxz', 'xaaaz', 'aaaa', 'fooo', 'xxx'])
|
||||
|
||||
l2[1] = 'bbbb'
|
||||
gen.contact_renamed('aaaa', 'bbbb')
|
||||
r = gen.generate_suggestions(
|
||||
nicks=l2,
|
||||
beginning=''
|
||||
)
|
||||
self.assertEqual(r, ['xxxxz', 'xaaaz', 'bbbb', 'fooo', 'xxx'])
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
5
test/unit/__init__.py
Normal file
5
test/unit/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
'''
|
||||
|
||||
This package just contains plain unit tests
|
||||
|
||||
'''
|
32
test/unit/test_gui_interface.py
Normal file
32
test/unit/test_gui_interface.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
'''
|
||||
Some diverse tests covering functionality in the GUI Interface class.
|
||||
'''
|
||||
import unittest
|
||||
|
||||
import gajim.gui
|
||||
gajim.gui.init('gtk')
|
||||
|
||||
from test import lib
|
||||
lib.setup_env()
|
||||
|
||||
from gajim.common import logging_helpers
|
||||
logging_helpers.set_quiet()
|
||||
|
||||
from gajim.common import app
|
||||
|
||||
from gajim.gui_interface import Interface
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
class TestInterface(unittest.TestCase):
|
||||
|
||||
def test_instantiation(self):
|
||||
''' Test that we can proper initialize and do not fail on globals '''
|
||||
def close_app():
|
||||
app.app.quit()
|
||||
GLib.idle_add(close_app)
|
||||
app.app.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
Loading…
Add table
Add a link
Reference in a new issue