From f68f7787f9b1ccb575d7584bce3d95f1362d490d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 23 Sep 2013 15:01:18 +0100 Subject: [PATCH 11/16] roster/groups.py: convert to ContactGroups As a side-effect, this adds proper test coverage for SetGroupMembers and SetContactGroups. --- tests/twisted/roster/groups.py | 191 ++++++++++++++++++++--------------------- 1 file changed, 93 insertions(+), 98 deletions(-) diff --git a/tests/twisted/roster/groups.py b/tests/twisted/roster/groups.py index 6d6fa4c..3e2dfee 100644 --- a/tests/twisted/roster/groups.py +++ b/tests/twisted/roster/groups.py @@ -8,7 +8,7 @@ from twisted.words.protocols.jabber.client import IQ from twisted.words.xish import domish, xpath from servicetest import (EventPattern, wrap_channel, assertLength, - assertEquals, call_async, sync_dbus, assertContains) + assertEquals, call_async, sync_dbus, assertContains, assertSameSets) from hazetest import acknowledge_iq, exec_test, sync_stream, close_all_groups import constants as cs import ns @@ -16,19 +16,6 @@ import ns def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") - # Close all Group channels to get a clean slate, so we can rely on - # the NewChannels signal for the default group later - close_all_groups(q, bus, conn, stream) - - call_async(q, conn.Requests, 'EnsureChannel',{ - cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, - cs.TARGET_HANDLE_TYPE: cs.HT_LIST, - cs.TARGET_ID: 'subscribe', - }) - e = q.expect('dbus-return', method='EnsureChannel') - subscribe = wrap_channel(bus.get_object(conn.bus_name, e.value[1]), - cs.CHANNEL_TYPE_CONTACT_LIST) - romeo, juliet, duncan = conn.get_contact_handles_sync( ['romeo@montague.lit', 'juliet@capulet.lit', 'duncan@scotland.lit']) @@ -64,94 +51,74 @@ def test(q, bus, conn, stream): sync_dbus(bus, q, conn) sync_stream(q, stream) - call_async(q, conn.Requests, 'EnsureChannel',{ - cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, - cs.TARGET_HANDLE_TYPE: cs.HT_GROUP, - cs.TARGET_ID: 'Still alive', - }) - e = q.expect('dbus-return', method='EnsureChannel') - still_alive = wrap_channel(bus.get_object(conn.bus_name, e.value[1]), - cs.CHANNEL_TYPE_CONTACT_LIST) - - call_async(q, conn.Requests, 'EnsureChannel',{ - cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, - cs.TARGET_HANDLE_TYPE: cs.HT_GROUP, - cs.TARGET_ID: 'Capulets', - }) - e = q.expect('dbus-return', method='EnsureChannel') - capulets = wrap_channel(bus.get_object(conn.bus_name, e.value[1]), - cs.CHANNEL_TYPE_CONTACT_LIST) - # the XMPP prpl puts people into some sort of group, probably called # Buddies - channels = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'Channels') + groups = conn.Properties.Get(cs.CONN_IFACE_CONTACT_GROUPS, 'Groups') default_group = None - default_props = None - - for path, props in channels: - if props.get(cs.CHANNEL_TYPE) != cs.CHANNEL_TYPE_CONTACT_LIST: - continue - - if props.get(cs.TARGET_HANDLE_TYPE) != cs.HT_GROUP: - continue - if props.get(cs.TARGET_ID) in ('Capulets', 'Still alive'): + for group in groups: + if group in ('Capulets', 'Still alive'): continue if default_group is not None: raise AssertionError('Two unexplained groups: %s, %s' % - (path, default_group.object_path)) + (group, default_group)) + + default_group = group - default_group = wrap_channel(bus.get_object(conn.bus_name, path), - cs.CHANNEL_TYPE_CONTACT_LIST) - default_group_name = props.get(cs.TARGET_ID) + call_async(q, conn.ContactList, 'GetContactListAttributes', + [cs.CONN_IFACE_CONTACT_GROUPS], False) + r = q.expect('dbus-return', method='GetContactListAttributes') - assertEquals(set([romeo, juliet]), set(still_alive.Group.GetMembers())) - assertEquals(set([juliet]), set(capulets.Group.GetMembers())) - assertEquals(set([duncan]), set(default_group.Group.GetMembers())) + assertSameSets(['Still alive'], r.value[0][romeo][cs.ATTR_GROUPS]) + assertSameSets(['Still alive', 'Capulets'], + r.value[0][juliet][cs.ATTR_GROUPS]) + assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS]) # We can't remove Duncan from the default group, because it's his only # group - call_async(q, default_group.Group, 'RemoveMembers', [duncan], '') - q.expect('dbus-error', method='RemoveMembers', + call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group, + [duncan]) + q.expect('dbus-error', method='RemoveFromGroup', name=cs.NOT_AVAILABLE) + call_async(q, conn.ContactGroups, 'SetGroupMembers', default_group, + []) + q.expect('dbus-error', method='SetGroupMembers', + name=cs.NOT_AVAILABLE) + # SetContactGroups just doesn't do anything in this situation + call_async(q, conn.ContactGroups, 'SetContactGroups', duncan, []) + q.expect('dbus-return', method='SetContactGroups') + + call_async(q, conn.ContactList, 'GetContactListAttributes', + [cs.CONN_IFACE_CONTACT_GROUPS], False) + r = q.expect('dbus-return', method='GetContactListAttributes') + assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS]) # Make a new group and add Duncan to it - call_async(q, conn.Requests, 'CreateChannel',{ - cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, - cs.TARGET_HANDLE_TYPE: cs.HT_GROUP, - cs.TARGET_ID: 'Scots', - }) - e = q.expect('dbus-return', method='CreateChannel') - scots = wrap_channel(bus.get_object(conn.bus_name, e.value[0]), - cs.CHANNEL_TYPE_CONTACT_LIST) - assertEquals(set(), set(scots.Group.GetMembers())) - - call_async(q, scots.Group, 'AddMembers', [duncan], '') + call_async(q, conn.ContactGroups, 'AddToGroup', 'Scots', [duncan]) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), - EventPattern('dbus-signal', signal='MembersChanged', - path=scots.object_path, - args=['', [duncan], [], [], [], self_handle, cs.GC_REASON_NONE]), - EventPattern('dbus-return', method='AddMembers'), + EventPattern('dbus-signal', signal='GroupsChanged', + args=[[duncan], ['Scots'], []]), + EventPattern('dbus-return', method='AddToGroup'), ) assertEquals('duncan@scotland.lit', iq.stanza.query.item['jid']) groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza)]) assertLength(2, groups) - assertContains(default_group_name, groups) + assertContains(default_group, groups) assertContains('Scots', groups) # Now we can remove him from the default group. Much rejoicing. - call_async(q, default_group.Group, 'RemoveMembers', [duncan], '') + call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group, + [duncan]) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), - EventPattern('dbus-signal', signal='MembersChanged', - path=default_group.object_path, - args=['', [], [duncan], [], [], self_handle, cs.GC_REASON_NONE]), - EventPattern('dbus-return', method='RemoveMembers'), + EventPattern('dbus-signal', signal='GroupsChanged', + args=[[duncan], [], [default_group]]), + EventPattern('dbus-return', method='RemoveFromGroup'), ) assertEquals('duncan@scotland.lit', iq.stanza.query.item['jid']) groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', @@ -159,22 +126,49 @@ def test(q, bus, conn, stream): assertLength(1, groups) assertContains('Scots', groups) + # Test SetContactGroups, which didn't previously have proper coverage + call_async(q, conn.ContactGroups, 'SetContactGroups', duncan, + ['Scottish former kings']) + iq, _, _, _ = q.expect_many( + EventPattern('stream-iq', iq_type='set', query_name='query', + query_ns=ns.ROSTER), + EventPattern('dbus-signal', signal='GroupsChanged', + args=[[duncan], ['Scottish former kings'], []]), + EventPattern('dbus-signal', signal='GroupsChanged', + args=[[duncan], [], ['Scots']]), + EventPattern('dbus-return', method='SetContactGroups'), + ) + assertEquals('duncan@scotland.lit', iq.stanza.query.item['jid']) + groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', + iq.stanza)]) + assertLength(2, groups) + assertContains('Scots', groups) + assertContains('Scottish former kings', groups) + iq, = q.expect_many( + EventPattern('stream-iq', iq_type='set', query_name='query', + query_ns=ns.ROSTER), + ) + assertEquals('duncan@scotland.lit', iq.stanza.query.item['jid']) + groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', + iq.stanza)]) + assertLength(1, groups) + assertContains('Scottish former kings', groups) + # Romeo dies. If he drops off the roster as a result, that would be # fd.o #21294. However, to fix that bug, Haze now puts him in the # default group. - call_async(q, still_alive.Group, 'RemoveMembers', [romeo], '') + call_async(q, conn.ContactGroups, 'RemoveFromGroup', 'Still alive', + [romeo]) iq1, iq2, _, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), - EventPattern('dbus-signal', signal='MembersChanged', - path=still_alive.object_path, - args=['', [], [romeo], [], [], self_handle, cs.GC_REASON_NONE]), - EventPattern('dbus-signal', signal='MembersChanged', - path=default_group.object_path, - args=['', [romeo], [], [], [], self_handle, cs.GC_REASON_NONE]), - EventPattern('dbus-return', method='RemoveMembers'), + EventPattern('dbus-signal', signal='GroupsChanged', + args=[[romeo], [default_group], []]), + EventPattern('dbus-signal', signal='GroupsChanged', + args=[[romeo], [], ['Still alive']]), + EventPattern('dbus-return', method='RemoveFromGroup'), ) assertEquals('romeo@montague.lit', iq1.stanza.query.item['jid']) @@ -182,24 +176,23 @@ def test(q, bus, conn, stream): iq1.stanza)]) assertLength(2, groups) assertContains('Still alive', groups) - assertContains(default_group_name, groups) + assertContains(default_group, groups) assertEquals('romeo@montague.lit', iq2.stanza.query.item['jid']) groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq2.stanza)]) assertLength(1, groups) - assertContains(default_group_name, groups) + assertContains(default_group, groups) # Juliet dies. She's in another group already, so the workaround for # fd.o #21294 is not active. - call_async(q, still_alive.Group, 'RemoveMembers', [juliet], '') + call_async(q, conn.ContactGroups, 'SetGroupMembers', 'Still alive', []) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), - EventPattern('dbus-signal', signal='MembersChanged', - path=still_alive.object_path, - args=['', [], [juliet], [], [], self_handle, cs.GC_REASON_NONE]), - EventPattern('dbus-return', method='RemoveMembers'), + EventPattern('dbus-signal', signal='GroupsChanged', + args=[[juliet], [], ['Still alive']]), + EventPattern('dbus-return', method='SetGroupMembers'), ) assertEquals('juliet@capulet.lit', iq.stanza.query.item['jid']) groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', @@ -209,17 +202,19 @@ def test(q, bus, conn, stream): # At the end of a tragedy, everyone dies, so there's no need for this # group. - call_async(q, still_alive, 'Close') - q.expect('dbus-signal', signal='Closed', path=still_alive.object_path) - q.expect('dbus-return', method='Close') - - # Deleting a non-empty group is not allowed. - call_async(q, capulets, 'Close') - q.expect('dbus-error', method='Close', name=cs.NOT_AVAILABLE) - - # Neither is deleting a List channel. - call_async(q, subscribe, 'Close') - q.expect('dbus-error', method='Close', name=cs.NOT_IMPLEMENTED) + call_async(q, conn.ContactGroups, 'RemoveGroup', 'Still alive') + q.expect('dbus-signal', signal='GroupsRemoved', args=[['Still alive']]) + + # Deleting a non-empty group is allowed. (It removes everyone.) + call_async(q, conn.ContactGroups, 'RemoveGroup', 'Capulets') + q.expect_many( + EventPattern('dbus-signal', signal='GroupsRemoved', + args=[['Capulets']]), + EventPattern('dbus-signal', signal='GroupsChanged', + args=[[juliet], [default_group], []]), + EventPattern('dbus-signal', signal='GroupsChanged', + args=[[juliet], [], ['Capulets']]), + ) if __name__ == '__main__': exec_test(test) -- 1.8.4.rc3