2
Test adding to, and removing from, groups
7
from twisted.words.protocols.jabber.client import IQ
8
from twisted.words.xish import domish, xpath
10
from servicetest import (EventPattern, wrap_channel, assertLength,
11
assertEquals, call_async, sync_dbus, assertContains)
12
from hazetest import acknowledge_iq, exec_test, sync_stream
13
import constants as cs
16
def test(q, bus, conn, stream):
18
q.expect('dbus-signal', signal='StatusChanged',
19
args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
21
call_async(q, conn.Requests, 'EnsureChannel',{
22
cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
23
cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
24
cs.TARGET_ID: 'subscribe',
26
e = q.expect('dbus-return', method='EnsureChannel')
27
subscribe = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
28
cs.CHANNEL_TYPE_CONTACT_LIST)
30
romeo, juliet, duncan = conn.RequestHandles(cs.HT_CONTACT,
31
['romeo@montague.lit', 'juliet@capulet.lit',
32
'duncan@scotland.lit'])
34
# receive some roster pushes for the "initial" state
35
iq = IQ(stream, 'set')
36
iq['id'] = 'roster-push'
37
query = iq.addElement(('jabber:iq:roster', 'query'))
38
item = query.addElement('item')
39
item['jid'] = 'juliet@capulet.lit'
40
item['subscription'] = 'both'
41
group = item.addElement('group', content='Still alive')
42
group = item.addElement('group', content='Capulets')
45
iq = IQ(stream, 'set')
46
iq['id'] = 'roster-push'
47
query = iq.addElement(('jabber:iq:roster', 'query'))
48
item = query.addElement('item')
49
item['jid'] = 'romeo@montague.lit'
50
item['subscription'] = 'both'
51
group = item.addElement('group', content='Still alive')
54
iq = IQ(stream, 'set')
55
iq['id'] = 'roster-push'
56
query = iq.addElement(('jabber:iq:roster', 'query'))
57
item = query.addElement('item')
58
item['jid'] = 'duncan@scotland.lit'
59
item['subscription'] = 'both'
62
sync_dbus(bus, q, conn)
63
sync_stream(q, stream)
65
call_async(q, conn.Requests, 'EnsureChannel',{
66
cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
67
cs.TARGET_HANDLE_TYPE: cs.HT_GROUP,
68
cs.TARGET_ID: 'Still alive',
70
e = q.expect('dbus-return', method='EnsureChannel')
71
still_alive = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
72
cs.CHANNEL_TYPE_CONTACT_LIST)
74
call_async(q, conn.Requests, 'EnsureChannel',{
75
cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
76
cs.TARGET_HANDLE_TYPE: cs.HT_GROUP,
77
cs.TARGET_ID: 'Capulets',
79
e = q.expect('dbus-return', method='EnsureChannel')
80
capulets = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
81
cs.CHANNEL_TYPE_CONTACT_LIST)
83
# the XMPP prpl puts people into some sort of group, probably called
85
channels = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'Channels')
89
for path, props in channels:
90
if props.get(cs.CHANNEL_TYPE) != cs.CHANNEL_TYPE_CONTACT_LIST:
93
if props.get(cs.TARGET_HANDLE_TYPE) != cs.HT_GROUP:
96
if props.get(cs.TARGET_ID) in ('Capulets', 'Still alive'):
99
if default_group is not None:
100
raise AssertionError('Two unexplained groups: %s, %s' %
101
(path, default_group.object_path))
103
default_group = wrap_channel(bus.get_object(conn.bus_name, path),
104
cs.CHANNEL_TYPE_CONTACT_LIST)
105
default_group_name = props.get(cs.TARGET_ID)
107
assertEquals(set([romeo, juliet]), set(still_alive.Group.GetMembers()))
108
assertEquals(set([juliet]), set(capulets.Group.GetMembers()))
109
assertEquals(set([duncan]), set(default_group.Group.GetMembers()))
111
# We can't remove Duncan from the default group, because it's his only
113
call_async(q, default_group.Group, 'RemoveMembers', [duncan], '')
114
q.expect('dbus-error', method='RemoveMembers',
115
name=cs.NOT_AVAILABLE)
117
# Make a new group and add Duncan to it
118
call_async(q, conn.Requests, 'CreateChannel',{
119
cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
120
cs.TARGET_HANDLE_TYPE: cs.HT_GROUP,
121
cs.TARGET_ID: 'Scots',
123
e = q.expect('dbus-return', method='CreateChannel')
124
scots = wrap_channel(bus.get_object(conn.bus_name, e.value[0]),
125
cs.CHANNEL_TYPE_CONTACT_LIST)
126
assertEquals(set(), set(scots.Group.GetMembers()))
128
call_async(q, scots.Group, 'AddMembers', [duncan], '')
129
iq, _, _ = q.expect_many(
130
EventPattern('stream-iq', iq_type='set', query_name='query',
132
EventPattern('dbus-signal', signal='MembersChanged',
133
path=scots.object_path,
134
args=['', [duncan], [], [], [], 0, cs.GC_REASON_NONE]),
135
EventPattern('dbus-return', method='AddMembers'),
137
assertEquals('duncan@scotland.lit', iq.stanza.query.item['jid'])
138
groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group',
140
assertLength(2, groups)
141
assertContains(default_group_name, groups)
142
assertContains('Scots', groups)
144
# Now we can remove him from the default group. Much rejoicing.
145
call_async(q, default_group.Group, 'RemoveMembers', [duncan], '')
146
iq, _, _ = q.expect_many(
147
EventPattern('stream-iq', iq_type='set', query_name='query',
149
EventPattern('dbus-signal', signal='MembersChanged',
150
path=default_group.object_path,
151
args=['', [], [duncan], [], [], 0, cs.GC_REASON_NONE]),
152
EventPattern('dbus-return', method='RemoveMembers'),
154
assertEquals('duncan@scotland.lit', iq.stanza.query.item['jid'])
155
groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group',
157
assertLength(1, groups)
158
assertContains('Scots', groups)
160
# Romeo dies. If he drops off the roster as a result, that would be
161
# fd.o #21294. However, to fix that bug, Haze now puts him in the
163
call_async(q, still_alive.Group, 'RemoveMembers', [romeo], '')
164
iq1, iq2, _, _, _ = q.expect_many(
165
EventPattern('stream-iq', iq_type='set', query_name='query',
167
EventPattern('stream-iq', iq_type='set', query_name='query',
169
EventPattern('dbus-signal', signal='MembersChanged',
170
path=still_alive.object_path,
171
args=['', [], [romeo], [], [], 0, cs.GC_REASON_NONE]),
172
EventPattern('dbus-signal', signal='MembersChanged',
173
path=default_group.object_path,
174
args=['', [romeo], [], [], [], 0, cs.GC_REASON_NONE]),
175
EventPattern('dbus-return', method='RemoveMembers'),
178
assertEquals('romeo@montague.lit', iq1.stanza.query.item['jid'])
179
groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group',
181
assertLength(2, groups)
182
assertContains('Still alive', groups)
183
assertContains(default_group_name, groups)
185
assertEquals('romeo@montague.lit', iq2.stanza.query.item['jid'])
186
groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group',
188
assertLength(1, groups)
189
assertContains(default_group_name, groups)
191
# Juliet dies. She's in another group already, so the workaround for
192
# fd.o #21294 is not active.
193
call_async(q, still_alive.Group, 'RemoveMembers', [juliet], '')
194
iq, _, _ = q.expect_many(
195
EventPattern('stream-iq', iq_type='set', query_name='query',
197
EventPattern('dbus-signal', signal='MembersChanged',
198
path=still_alive.object_path,
199
args=['', [], [juliet], [], [], 0, cs.GC_REASON_NONE]),
200
EventPattern('dbus-return', method='RemoveMembers'),
202
assertEquals('juliet@capulet.lit', iq.stanza.query.item['jid'])
203
groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group',
205
assertLength(1, groups)
206
assertContains('Capulets', groups)
208
if __name__ == '__main__':