summaryrefslogtreecommitdiff
path: root/salut/tests/twisted/avahi/file-transfer/ft-client-caps.py
diff options
context:
space:
mode:
Diffstat (limited to 'salut/tests/twisted/avahi/file-transfer/ft-client-caps.py')
-rw-r--r--salut/tests/twisted/avahi/file-transfer/ft-client-caps.py423
1 files changed, 423 insertions, 0 deletions
diff --git a/salut/tests/twisted/avahi/file-transfer/ft-client-caps.py b/salut/tests/twisted/avahi/file-transfer/ft-client-caps.py
new file mode 100644
index 000000000..40598936c
--- /dev/null
+++ b/salut/tests/twisted/avahi/file-transfer/ft-client-caps.py
@@ -0,0 +1,423 @@
+
+"""
+Test FT capabilities with Connection.Interface.ContactCapabilities
+
+1. Receive presence and caps from contacts and check that
+GetContactCapabilities works correctly and that ContactCapabilitiesChanged is
+correctly received. Also check that GetContactAttributes gives the same
+results.
+
+- no FT cap at all
+- FT caps without metadata extension
+- FT caps with metadata extension
+- 1 FT cap with a service name
+- 2 FT caps with service names
+- 1 FT cap again, to test whether the caps cache works with FT services
+
+2. Test UpdateCapabilities and test that a presence stanza is sent to the
+contacts, test that the D-Bus signal ContactCapabilitiesChanged is fired for
+the self handle, ask Salut for its caps with an iq request, check the reply
+is correct, and ask Salut for its caps using D-Bus method
+GetContactCapabilities. Also check that GetContactAttributes gives the same
+results.
+
+Ensure that just a Requested=True channel class in a client filter doesn't
+make a FT service advertised as a cap.
+
+- no FT cap at all
+- 1 FT cap with no service name
+- 1 Requested=True FT cap with service name
+- 1 FT cap with service name
+- 1 FT cap with service name + 1 FT cap with no service name
+- 2 FT caps with service names
+- 1 FT cap with service name again, just for fun
+
+"""
+
+import dbus
+
+from avahitest import AvahiAnnouncer, AvahiListener
+from avahitest import get_host_name
+from avahitest import txt_get_key
+import avahi
+
+from xmppstream import connect_to_stream, setup_stream_listener
+
+from twisted.words.xish import xpath
+
+from servicetest import assertEquals, assertLength, assertContains,\
+ assertDoesNotContain, sync_dbus, EventPattern
+from saluttest import exec_test, make_result_iq, sync_stream, make_presence, \
+ fixed_features
+import constants as cs
+
+from caps_helper import check_caps, compute_caps_hash, text_fixed_properties, \
+ text_allowed_properties, caps_contain, disco_caps, \
+ ft_fixed_properties, ft_allowed_properties, ft_allowed_properties_with_metadata
+import ns
+from config import PACKAGE_STRING
+
+def dict_union(a, b):
+ return dbus.Dictionary(a.items() + b.items(), signature='sv')
+
+no_service_fixed_properties = {
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER,
+ }
+bidir_daap_fixed_properties = dict_union(no_service_fixed_properties, {
+ cs.FT_SERVICE_NAME: 'daap'
+ })
+outgoing_daap_fixed_properties = dict_union(bidir_daap_fixed_properties, {
+ cs.REQUESTED : True,
+ })
+incoming_daap_fixed_properties = dict_union(bidir_daap_fixed_properties, {
+ cs.REQUESTED : False,
+ })
+http_fixed_properties = dict_union(no_service_fixed_properties, {
+ cs.FT_SERVICE_NAME: 'http',
+ })
+xiangqi_fixed_properties = dict_union(no_service_fixed_properties, {
+ cs.FT_SERVICE_NAME: 'com.example.Xiangqi',
+ })
+go_fixed_properties = dict_union(no_service_fixed_properties, {
+ cs.FT_SERVICE_NAME: 'com.example.Go',
+ })
+
+client = 'http://telepathy.freedesktop.org/fake-client'
+
+def assertSameElements(a, b):
+ assertEquals(sorted(a), sorted(b))
+
+def receive_caps(q, bus, conn, service, contact, contact_handle, features,
+ expected_caps, expect_disco=True, expect_ccc=True):
+
+ ver = compute_caps_hash([], features, {})
+ txt_record = { "txtvers": "1", "status": "avail",
+ "node": client, "ver": ver, "hash": "sha-1"}
+
+ listener, port = setup_stream_listener(q, contact)
+ AvahiAnnouncer(contact, "_presence._tcp", port, txt_record)
+
+ if expect_disco:
+ # Salut looks up our capabilities
+ e = q.expect('incoming-connection', listener=listener)
+ stream = e.connection
+
+ event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO,
+ connection=stream)
+ query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
+ assert query_node.attributes['node'] == \
+ client + '#' + ver
+
+ # send good reply
+ result = make_result_iq(event.stanza)
+ query = result.firstChildElement()
+ query['node'] = client + '#' + ver
+
+ for f in features:
+ feature = query.addElement('feature')
+ feature['var'] = f
+
+ stream.send(result)
+
+ if expect_ccc:
+ event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
+ announced_ccs, = event.args
+ assertSameElements(expected_caps, announced_ccs[contact_handle])
+ else:
+ if expect_disco:
+ # Make sure Salut's got the caps
+ sync_stream(q, stream)
+
+ caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle])
+ assertSameElements(expected_caps, caps[contact_handle])
+
+ # test again, to check GetContactCapabilities does not have side effect
+ caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle])
+ assertSameElements(expected_caps, caps[contact_handle])
+
+ # check the Contacts interface give the same caps
+ caps_via_contacts_iface = conn.Contacts.GetContactAttributes(
+ [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
+ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES]
+ assertSameElements(caps[contact_handle], caps_via_contacts_iface)
+
+ # close the connection and expect a new one to be opened by Salut
+ # the next time we need some discoing doing
+ if expect_disco:
+ stream.send('</stream:stream>')
+ stream.transport.loseConnection()
+ # pass some time so Salut knows the connection is lost and
+ # won't try and send stuff down a closed connection on the
+ # next test.
+ sync_dbus(bus, q, conn)
+
+def test_ft_caps_from_contact(q, bus, conn, service, contact):
+ contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact])[0]
+
+ # Check that we don't crash if we haven't seen any caps/presence for this
+ # contact yet.
+ caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle])
+
+ basic_caps = [(text_fixed_properties, text_allowed_properties)]
+
+ # Since we don't know their caps, they should be omitted from the dict,
+ # rather than present with no caps, but all contacts have text chat caps.
+ assertEquals([], caps[contact_handle])
+
+ # send presence with no FT cap
+ # We don't expect ContactCapabilitiesChanged to be emitted here: we always
+ # assume people can do text channels.
+ receive_caps(q, bus, conn, service, contact, contact_handle, [], basic_caps,
+ expect_ccc=False)
+
+ # send presence with no mention of metadata
+ no_metadata_ft_caps = [
+ (text_fixed_properties, text_allowed_properties),
+ (ft_fixed_properties, ft_allowed_properties)
+ ]
+ receive_caps(q, bus, conn, service, contact, contact_handle,
+ [ns.IQ_OOB], no_metadata_ft_caps)
+
+ # send presence with generic FT caps including metadata from now on
+ generic_ft_caps = [
+ (text_fixed_properties, text_allowed_properties),
+ (ft_fixed_properties, ft_allowed_properties_with_metadata)
+ ]
+ generic_ft_features = [ns.IQ_OOB, ns.TP_FT_METADATA]
+ receive_caps(q, bus, conn, service, contact, contact_handle,
+ generic_ft_features, generic_ft_caps)
+
+ # send presence with 1 FT cap with a service
+ daap_caps = generic_ft_caps + [
+ (bidir_daap_fixed_properties, ft_allowed_properties + [cs.FT_METADATA])]
+ receive_caps(q, bus, conn, service, contact, contact_handle,
+ generic_ft_features + [ns.TP_FT_METADATA + '#daap'], daap_caps)
+
+ # send presence with 2 FT caps
+ daap_xiangqi_caps = daap_caps + [
+ (xiangqi_fixed_properties, ft_allowed_properties + [cs.FT_METADATA])]
+ receive_caps(q, bus, conn, service, contact, contact_handle,
+ generic_ft_features + [ns.TP_FT_METADATA + '#com.example.Xiangqi',
+ ns.TP_FT_METADATA + '#daap',
+ ], daap_xiangqi_caps)
+
+ # send presence with 1 FT cap again
+ # Salut does not look up our capabilities because of the cache
+ receive_caps(q, bus, conn, service, contact, contact_handle,
+ generic_ft_features + [ns.TP_FT_METADATA + '#daap'], daap_caps,
+ expect_disco=False)
+
+def advertise_caps(q, bus, conn, service, filters, expected_features, unexpected_features,
+ expected_caps):
+ # make sure nothing from a previous update is still running
+ sync_dbus(bus, q, conn)
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(1, [self_handle])[0]
+ ret_caps = conn.ContactCapabilities.UpdateCapabilities(
+ [(cs.CLIENT + '.Foo', filters, [])])
+
+ presence, event_dbus = q.expect_many(
+ EventPattern('service-resolved', service=service),
+ EventPattern('dbus-signal', signal='ContactCapabilitiesChanged')
+ )
+ assertLength(1, event_dbus.args)
+ signaled_caps = event_dbus.args[0]
+
+ outbound = connect_to_stream(q, 'test@foobar',
+ self_handle_name, str(presence.pt), presence.port)
+
+ e = q.expect('connection-result')
+ assert e.succeeded, e.reason
+
+ e = q.expect('stream-opened', connection=outbound)
+
+ # Expect Salut to reply with the correct caps
+ event, namespaces = disco_caps(q, outbound, presence.txt)
+
+ assertSameElements(expected_caps, signaled_caps[self_handle])
+
+ assertContains(ns.TP_FT_METADATA, namespaces)
+
+ for var in expected_features:
+ assertContains(var, namespaces)
+
+ for var in unexpected_features:
+ assertDoesNotContain(var, namespaces)
+
+ # Check our own caps
+ caps = conn.ContactCapabilities.GetContactCapabilities([self_handle])
+ assertSameElements(expected_caps, caps[self_handle])
+
+ # check the Contacts interface give the same caps
+ caps_via_contacts_iface = conn.Contacts.GetContactAttributes(
+ [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
+ [self_handle][cs.ATTR_CONTACT_CAPABILITIES]
+ assertSameElements(caps[self_handle], caps_via_contacts_iface)
+
+ # close things...
+ outbound.send('</stream:stream>')
+ sync_dbus(bus, q, conn)
+ outbound.transport.loseConnection()
+
+def test_ft_caps_to_contact(q, bus, conn, service):
+ self_handle = conn.GetSelfHandle()
+
+ basic_caps = [
+ (text_fixed_properties, text_allowed_properties),
+ (ft_fixed_properties, ft_allowed_properties_with_metadata),
+ ]
+ daap_caps = basic_caps + [
+ (bidir_daap_fixed_properties, ft_allowed_properties + [cs.FT_METADATA]),
+ ]
+ xiangqi_caps = basic_caps + [
+ (xiangqi_fixed_properties, ft_allowed_properties + [cs.FT_METADATA]),
+ ]
+ xiangqi_go_caps = xiangqi_caps + [
+ (go_fixed_properties, ft_allowed_properties + [cs.FT_METADATA]),
+ ]
+ go_caps = basic_caps + [
+ (go_fixed_properties, ft_allowed_properties + [cs.FT_METADATA]),
+ ]
+
+ #
+ # Check our own caps
+ #
+ caps = conn.ContactCapabilities.GetContactCapabilities([self_handle])
+ assertEquals(basic_caps, caps[self_handle])
+
+ # check the Contacts interface give the same caps
+ caps_via_contacts_iface = conn.Contacts.GetContactAttributes(
+ [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
+ [self_handle][cs.ATTR_CONTACT_CAPABILITIES]
+ assertEquals(caps[self_handle], caps_via_contacts_iface)
+
+ #
+ # Advertise nothing
+ #
+ conn.ContactCapabilities.UpdateCapabilities(
+ [(cs.CLIENT + '.Foo', {}, [])])
+
+ # Check our own caps
+ caps = conn.ContactCapabilities.GetContactCapabilities([self_handle])
+ assertLength(1, caps)
+ assertEquals(basic_caps, caps[self_handle])
+
+ # check the Contacts interface give the same caps
+ caps_via_contacts_iface = conn.Contacts.GetContactAttributes(
+ [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
+ [self_handle][cs.ATTR_CONTACT_CAPABILITIES]
+ assertEquals(caps[self_handle], caps_via_contacts_iface)
+
+ sync_dbus(bus, q, conn)
+
+ #
+ # Advertise FT but with no service name
+ #
+ conn.ContactCapabilities.UpdateCapabilities(
+ [(cs.CLIENT + '.Foo', [no_service_fixed_properties], [])])
+
+ # Check our own caps
+ caps = conn.ContactCapabilities.GetContactCapabilities([self_handle])
+ assertLength(1, caps)
+ assertEquals(basic_caps, caps[self_handle])
+
+ # check the Contacts interface give the same caps
+ caps_via_contacts_iface = conn.Contacts.GetContactAttributes(
+ [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
+ [self_handle][cs.ATTR_CONTACT_CAPABILITIES]
+ assertEquals(caps[self_handle], caps_via_contacts_iface)
+
+ sync_dbus(bus, q, conn)
+
+ #
+ # Advertise a Requested=True FT cap
+ #
+ conn.ContactCapabilities.UpdateCapabilities(
+ [(cs.CLIENT + '.Foo', [outgoing_daap_fixed_properties], [])])
+
+ # Check our own caps
+ caps = conn.ContactCapabilities.GetContactCapabilities([self_handle])
+ assertLength(1, caps)
+ assertEquals(basic_caps, caps[self_handle])
+
+ # check the Contacts interface give the same caps
+ caps_via_contacts_iface = conn.Contacts.GetContactAttributes(
+ [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
+ [self_handle][cs.ATTR_CONTACT_CAPABILITIES]
+ assertEquals(caps[self_handle], caps_via_contacts_iface)
+
+ advertise_caps(q, bus, conn, service,
+ [bidir_daap_fixed_properties],
+ [ns.TP_FT_METADATA + '#daap'],
+ [ns.TP_FT_METADATA + '#http',
+ ns.TP_FT_METADATA + '#com.example.Go',
+ ns.TP_FT_METADATA + '#com.example.Xiangqi',
+ ],
+ daap_caps)
+
+ advertise_caps(q, bus, conn, service,
+ [xiangqi_fixed_properties, no_service_fixed_properties],
+ [ns.TP_FT_METADATA + '#com.example.Xiangqi'],
+ [ns.TP_FT_METADATA + '#daap',
+ ns.TP_FT_METADATA + '#http',
+ ns.TP_FT_METADATA + '#com.example.Go',
+ ],
+ xiangqi_caps)
+
+ advertise_caps(q, bus, conn, service,
+ [xiangqi_fixed_properties, go_fixed_properties],
+ [ns.TP_FT_METADATA + '#com.example.Xiangqi',
+ ns.TP_FT_METADATA + '#com.example.Go',
+ ],
+ [ns.TP_FT_METADATA + '#http',
+ ns.TP_FT_METADATA + '#daap',
+ ],
+ xiangqi_go_caps)
+
+ advertise_caps(q, bus, conn, service,
+ [go_fixed_properties],
+ [ns.TP_FT_METADATA + '#com.example.Go',
+ ],
+ [ns.TP_FT_METADATA + '#http',
+ ns.TP_FT_METADATA + '#daap',
+ ns.TP_FT_METADATA + '#com.example.Xiangqi',
+ ],
+ go_caps)
+
+def test(q, bus, conn):
+ # last value of the "ver" key we resolved. We use it to be sure that the
+ # modified caps has already be announced.
+ old_ver = None
+
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0, 0])
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(1, [self_handle])[0]
+
+ AvahiListener(q).listen_for_service("_presence._tcp")
+ e = q.expect('service-added', name = self_handle_name,
+ protocol = avahi.PROTO_INET)
+ service = e.service
+ service.resolve()
+
+ e = q.expect('service-resolved', service = service)
+ ver = txt_get_key(e.txt, "ver")
+ while ver == old_ver:
+ # be sure that the announced caps actually changes
+ e = q.expect('service-resolved', service=service)
+ ver = txt_get_key(e.txt, "ver")
+ old_ver = ver
+
+ caps = compute_caps_hash(['client/pc//%s' % PACKAGE_STRING],
+ fixed_features, {})
+ assertEquals(caps, ver)
+
+ test_ft_caps_from_contact(q, bus, conn, service, 'yo@momma')
+
+ test_ft_caps_to_contact(q, bus, conn, service)
+
+if __name__ == '__main__':
+ exec_test(test)