1# vim: set fileencoding=utf-8 2# Tests publishing an avatar in MUCs, and getting tokens for ourselves and 3# others. Serves as a regression test for 4# <https://bugs.freedesktop.org/show_bug.cgi?id=32017>, where our MUC-specific 5# self handle would have an empty avatar token even though we're publishing our 6# avatar on the wire correctly. 7 8import hashlib 9from servicetest import ( 10 call_async, EventPattern, assertEquals, assertLength, sync_dbus, 11 wrap_channel, 12 ) 13from gabbletest import ( 14 exec_test, expect_and_handle_get_vcard, expect_and_handle_set_vcard, 15 make_muc_presence, elem, 16 ) 17from twisted.words.xish import xpath 18import ns 19import constants as cs 20from mucutil import try_to_join_muc 21 22AVATAR_1_DATA = 'nyan' 23AVATAR_1_SHA1 = hashlib.sha1(AVATAR_1_DATA).hexdigest() 24AVATAR_1_MIME_TYPE = 'image/x-pop-tart' 25 26AVATAR_2_DATA = 'NYAN' 27AVATAR_2_SHA1 = hashlib.sha1(AVATAR_2_DATA).hexdigest() 28AVATAR_2_MIME_TYPE = 'image/x-pop-tart' 29 30MUC = 'taco-dog@nyan.cat' 31 32def extract_hash_from_presence(stanza): 33 return xpath.queryForString( 34 '/presence/x[@xmlns="%s"]/photo' % ns.VCARD_TEMP_UPDATE, 35 stanza) 36 37def test(q, bus, conn, stream): 38 self_handle = conn.GetSelfHandle() 39 40 # When Gabble initially requests its avatar from the server, it discovers 41 # it has none. 42 expect_and_handle_get_vcard(q, stream) 43 handle, signalled_token = q.expect('dbus-signal', signal='AvatarUpdated').args 44 45 assertEquals(self_handle, handle) 46 assertEquals('', signalled_token) 47 48 # The user sets an avatar. 49 call_async(q, conn.Avatars, 'SetAvatar', AVATAR_1_DATA, AVATAR_1_MIME_TYPE) 50 expect_and_handle_get_vcard(q, stream) 51 expect_and_handle_set_vcard(q, stream) 52 53 # It's signalled on D-Bus … 54 set_ret, avatar_updated = q.expect_many( 55 EventPattern('dbus-return', method='SetAvatar'), 56 EventPattern('dbus-signal', signal='AvatarUpdated'), 57 ) 58 59 returned_token, = set_ret.value 60 handle, signalled_token = avatar_updated.args 61 62 assertEquals(self_handle, handle) 63 assertEquals(returned_token, signalled_token) 64 65 # … and also on XMPP. 66 broadcast = q.expect('stream-presence', to=None) 67 broadcast_hash = extract_hash_from_presence(broadcast.stanza) 68 assertEquals(AVATAR_1_SHA1, broadcast_hash) 69 70 # If applications ask Gabble for information about the user's own avatar, 71 # it should be able to answer. (Strictly speaking, expecting Gabble to know 72 # the avatar data is risky because Gabble discards cached vCards after a 73 # while, but we happen to know it takes 20 seconds or so for that to 74 # happen.) 75 known = conn.Avatars.GetKnownAvatarTokens([self_handle]) 76 assertEquals({self_handle: signalled_token}, known) 77 78 conn.Avatars.RequestAvatars([self_handle]) 79 retrieved = q.expect('dbus-signal', signal='AvatarRetrieved') 80 handle, token, data, mime_type = retrieved.args 81 assertEquals(self_handle, handle) 82 assertEquals(signalled_token, token) 83 assertEquals(AVATAR_1_DATA, data) 84 assertEquals(AVATAR_1_MIME_TYPE, mime_type) 85 86 # Well, that was quite easy. How about we join a MUC? XEP-0153 §4.1 says: 87 # If a client supports the protocol defined herein, it […] SHOULD 88 # also include the update child in directed presence stanzas (e.g., 89 # directed presence sent when joining Multi-User Chat [5] rooms). 90 # — http://xmpp.org/extensions/xep-0153.html#bizrules-presence 91 join_event = try_to_join_muc(q, bus, conn, stream, MUC) 92 directed_hash = extract_hash_from_presence(join_event.stanza) 93 assertEquals(AVATAR_1_SHA1, directed_hash) 94 95 # There are two others in the MUC: fredrik has no avatar, wendy has an 96 # avatar. We, of course, have our own avatar. 97 stream.send(make_muc_presence('none', 'participant', MUC, 'fredrik')) 98 stream.send(make_muc_presence('none', 'participant', MUC, 'wendy', 99 photo=AVATAR_2_SHA1)) 100 stream.send(make_muc_presence('owner', 'moderator', MUC, 'test', 101 photo=AVATAR_1_SHA1)) 102 103 path, _ = q.expect('dbus-return', method='CreateChannel').value 104 chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', 105 ['Messages']) 106 107 members = chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'Members') 108 assertLength(3, members) 109 110 fredrik, wendy, muc_self_handle = conn.RequestHandles(cs.HT_CONTACT, 111 ['%s/%s' % (MUC, x) for x in ["fredrik", "wendy", "test"]]) 112 113 known = conn.Avatars.GetKnownAvatarTokens(members) 114 # <https://bugs.freedesktop.org/show_bug.cgi?id=32017>: this assertion 115 # failed, the MUC self handle's token was the empty string. 116 assertEquals(AVATAR_1_SHA1, known[muc_self_handle]) 117 assertEquals(AVATAR_2_SHA1, known[wendy]) 118 assertEquals('', known[fredrik]) 119 120 # 'k, cool. Wendy loves our avatar and switches to it. 121 stream.send(make_muc_presence('none', 'participant', MUC, 'wendy', 122 photo=AVATAR_1_SHA1)) 123 # Okay this is technically assuming that we just expose the SHA1 sums 124 # directly which is not guaranteed … but we do. 125 q.expect('dbus-signal', signal='AvatarUpdated', 126 args=[wendy, AVATAR_1_SHA1]) 127 128 # Fredrik switches too. 129 stream.send(make_muc_presence('none', 'participant', MUC, 'fredrik', 130 photo=AVATAR_1_SHA1)) 131 q.expect('dbus-signal', signal='AvatarUpdated', 132 args=[fredrik, AVATAR_1_SHA1]) 133 134 # And we switch to some other avatar. Gabble should update its vCard, and 135 # then update its MUC presence (which the test, acting as the MUC server, 136 # must echo). 137 call_async(q, conn.Avatars, 'SetAvatar', AVATAR_2_DATA, AVATAR_2_MIME_TYPE) 138 expect_and_handle_get_vcard(q, stream) 139 expect_and_handle_set_vcard(q, stream) 140 141 muc_presence = q.expect('stream-presence', to=('%s/test' % MUC)) 142 directed_hash = extract_hash_from_presence(muc_presence.stanza) 143 stream.send(make_muc_presence('owner', 'moderator', MUC, 'test', 144 photo=directed_hash)) 145 146 # Gabble should signal an avatar update for both our global self-handle and 147 # our MUC self-handle. (The first of these of course does not need to wait 148 # for the MUC server to echo our presence.) 149 q.expect_many( 150 EventPattern('dbus-signal', signal='AvatarUpdated', 151 args=[self_handle, AVATAR_2_SHA1]), 152 EventPattern('dbus-signal', signal='AvatarUpdated', 153 args=[muc_self_handle, AVATAR_2_SHA1]), 154 ) 155 156if __name__ == '__main__': 157 exec_test(test) 158