def make_caps_disco_reply(stream, req, identities, features, dataforms={}):
    iq = make_result_iq(stream, req)
    query = iq.firstChildElement()

    for identity in identities:
        category, type_, lang, name = identity.split('/')
        el = query.addElement('identity')
        el['category'] = category
        el['type'] = type_
        el['name'] = name

    for f in features:
        el = domish.Element((None, 'feature'))
        el['var'] = f
        query.addChild(el)

    for type, fields in dataforms.iteritems():
        x = query.addElement((ns.X_DATA, 'x'))
        x['type'] = 'result'

        field = x.addElement('field')
        field['var'] = 'FORM_TYPE'
        field['type'] = 'hidden'
        field.addElement('value', content=type)

        for var, values in fields.iteritems():
            field = x.addElement('field')
            field['var'] = var

            for value in values:
                field.addElement('value', content=value)

    return iq
def make_caps_disco_reply(stream, req, identities, features, dataforms={}):
    iq = make_result_iq(stream, req)
    query = iq.firstChildElement()

    for identity in identities:
        category, type_, lang, name = identity.split('/')
        el = query.addElement('identity')
        el['category'] = category
        el['type'] = type_
        el['name'] = name

    for f in features:
        el = domish.Element((None, 'feature'))
        el['var'] = f
        query.addChild(el)

    for type, fields in dataforms.iteritems():
        x = query.addElement((ns.X_DATA, 'x'))
        x['type'] = 'result'

        field = x.addElement('field')
        field['var'] = 'FORM_TYPE'
        field['type'] = 'hidden'
        field.addElement('value', content=type)

        for var, values in fields.iteritems():
            field = x.addElement('field')
            field['var'] = var

            for value in values:
                field.addElement('value', content=value)

    return iq
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)

    # 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(expected_caps, 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)
示例#4
0
def outgoing_reply(q, bus, conn):
    path, incoming, stanza = setup_outgoing_tests(q, bus, conn)

    # reply with nothing
    reply = make_result_iq(stanza)
    incoming.send(reply)

    e = q.expect('dbus-signal', signal='Replied', path=path)
    args, xml = e.args
    assertEquals({}, args)
    assertEquals('<?xml version="1.0" encoding="UTF-8"?>\n' \
                 + '<message xmlns="urn:ytstenut:message"/>\n', xml)
def outgoing_reply(q, bus, conn):
    path, incoming, stanza = setup_outgoing_tests(q, bus, conn)

    # reply with nothing
    reply = make_result_iq(stanza)
    incoming.send(reply)

    e = q.expect('dbus-signal', signal='Replied', path=path)
    args, xml = e.args
    assertEquals({}, args)
    assertEquals('<?xml version="1.0" encoding="UTF-8"?>\n' \
                 + '<message xmlns="urn:ytstenut:message"/>\n', xml)
def make_caps_disco_reply(stream, req, identities, features, dataforms={}):
    iq = make_result_iq(req)
    query = iq.firstChildElement()

    for identity in identities:
        category, type_, lang, name = identity.split('/')
        el = query.addElement('identity')
        el['category'] = category
        el['type'] = type_
        el['name'] = name

    for f in features:
        el = domish.Element((None, 'feature'))
        el['var'] = f
        query.addChild(el)

    add_dataforms(query, dataforms)

    return iq
def test(q, bus, conn):
    # we won't be using any data forms, so these two shouldn't ever be
    # fired.
    q.forbid_events([EventPattern('dbus-signal', signal='ServiceAdded'),
                     EventPattern('dbus-signal', signal='ServiceRemoved')])

    call_async(q, conn.Future, 'EnsureSidecar', ycs.STATUS_IFACE)

    conn.Connect()

    # Now we're connected, the call we made earlier should return.
    e = q.expect('dbus-return', method='EnsureSidecar')
    path, props = e.value
    assertEquals({}, props)

    status = ProxyWrapper(bus.get_object(conn.bus_name, path),
                          ycs.STATUS_IFACE, {})

    # bad capability argument
    call_async(q, status, 'AdvertiseStatus', '', 'service.name', '')
    q.expect('dbus-error', method='AdvertiseStatus')

    # bad service name
    call_async(q, status, 'AdvertiseStatus', CAP_NAME, '', '')
    q.expect('dbus-error', method='AdvertiseStatus')

    # we can't test that the message type="headline" stanza is
    # actually received because it's thrown into the loopback stream
    # immediately.

    # announce a contact with the right caps
    ver = compute_caps_hash([], [CAP_NAME + '+notify'], {})
    txt_record = { "txtvers": "1", "status": "avail",
        "node": CLIENT_NAME, "ver": ver, "hash": "sha-1"}
    contact_name = "test-status@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)

    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record)

    handle = wait_for_contact_in_publish(q, bus, conn, contact_name)

    # this is the first presence, Salut connects to the contact
    e = q.expect('incoming-connection', listener=listener)
    incoming = e.connection

    # Salut looks up its capabilities
    event = q.expect('stream-iq', connection=incoming,
        query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assertEquals(CLIENT_NAME + '#' + ver, query_node.attributes['node'])

    contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact_name])[0]

    # send good reply
    result = make_result_iq(event.stanza)
    query = result.firstChildElement()
    query['node'] = CLIENT_NAME + '#' + ver

    feature = query.addElement('feature')
    feature['var'] = CAP_NAME + '+notify'
    incoming.send(result)

    # this will be fired as text channel caps will be fired
    q.expect('dbus-signal', signal='ContactCapabilitiesChanged',
             predicate=lambda e: contact_handle in e.args[0])

    # okay now we know about the contact's caps, we can go ahead

    discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredStatuses',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({}, discovered)

    el = Element(('urn:ytstenut:status', 'status'))
    el['activity'] = 'messing-with-your-stuff'
    desc = el.addElement('ytstenut:description', content='Yeah sorry about that')
    desc['xml:lang'] = 'en-GB'

    call_async(q, status, 'AdvertiseStatus', CAP_NAME,
               'ants.in.their.pants', el.toXml())

    e, _, sig = q.expect_many(EventPattern('stream-message', connection=incoming),
                              EventPattern('dbus-return', method='AdvertiseStatus'),
                              EventPattern('dbus-signal', signal='StatusChanged',
                                           interface=ycs.STATUS_IFACE))

    # check message
    message = e.stanza
    event = message.children[0]
    items = event.children[0]
    item = items.children[0]
    status_el = item.children[0]

    assertEquals('status', status_el.name)
    assertEquals('messing-with-your-stuff', status_el['activity'])
    assertEquals('ants.in.their.pants', status_el['from-service'])
    assertEquals(CAP_NAME, status_el['capability'])

    # check signal
    contact_id, capability, service_name, status_str = sig.args
    assertEquals(CAP_NAME, capability)
    assertEquals('ants.in.their.pants', service_name)
    assertNotEquals('', status_str)

    # check property
    discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredStatuses',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({'testsuite@testsuite': {CAP_NAME: {'ants.in.their.pants': status_str}}},
                 discovered)

    # set another
    el = Element(('urn:ytstenut:status', 'status'))
    el['activity'] = 'rofling'
    desc = el.addElement('ytstenut:description', content='U MAD?')
    desc['xml:lang'] = 'en-GB'

    call_async(q, status, 'AdvertiseStatus', CAP_NAME,
               'bananaman.on.holiday', el.toXml())

    e, _, sig = q.expect_many(EventPattern('stream-message', connection=incoming),
                              EventPattern('dbus-return', method='AdvertiseStatus'),
                              EventPattern('dbus-signal', signal='StatusChanged',
                                           interface=ycs.STATUS_IFACE))

    # check message
    message = e.stanza
    event = message.children[0]
    items = event.children[0]
    item = items.children[0]
    status_el = item.children[0]

    assertEquals('status', status_el.name)
    assertEquals('rofling', status_el['activity'])
    assertEquals('bananaman.on.holiday', status_el['from-service'])
    assertEquals(CAP_NAME, status_el['capability'])

    # check signal
    contact_id, capability, service_name, bananaman_status_str = sig.args
    assertEquals(CAP_NAME, capability)
    assertEquals('bananaman.on.holiday', service_name)
    assertNotEquals('', bananaman_status_str)

    # check property
    discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredStatuses',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({'testsuite@testsuite': {CAP_NAME: {
                    'ants.in.their.pants': status_str,
                    'bananaman.on.holiday': bananaman_status_str}}},
                 discovered)

    # unset the status from one service
    call_async(q, status, 'AdvertiseStatus', CAP_NAME,
               'ants.in.their.pants', '')

    e, _, sig = q.expect_many(EventPattern('stream-message', connection=incoming),
                              EventPattern('dbus-return', method='AdvertiseStatus'),
                              EventPattern('dbus-signal', signal='StatusChanged',
                                           interface=ycs.STATUS_IFACE))

    # check message
    message = e.stanza
    event = message.children[0]
    items = event.children[0]
    item = items.children[0]
    status_el = item.children[0]

    assertEquals('status', status_el.name)
    assertEquals('ants.in.their.pants', status_el['from-service'])
    assertEquals(CAP_NAME, status_el['capability'])
    assert 'activity' not in status_el.attributes
    assertEquals([], status_el.children)

    # check signal
    contact_id, capability, service_name, status_str = sig.args
    assertEquals(CAP_NAME, capability)
    assertEquals('ants.in.their.pants', service_name)
    assertEquals('', status_str)

    # check property
    discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredStatuses',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({'testsuite@testsuite': {CAP_NAME: {
                    'bananaman.on.holiday': bananaman_status_str}}},
                 discovered)

    # unset the status from the other service
    call_async(q, status, 'AdvertiseStatus', CAP_NAME,
               'bananaman.on.holiday', '')

    e, _, sig = q.expect_many(EventPattern('stream-message', connection=incoming),
                              EventPattern('dbus-return', method='AdvertiseStatus'),
                              EventPattern('dbus-signal', signal='StatusChanged',
                                           interface=ycs.STATUS_IFACE))

    # check message
    message = e.stanza
    event = message.children[0]
    items = event.children[0]
    item = items.children[0]
    status_el = item.children[0]

    assertEquals('status', status_el.name)
    assertEquals('bananaman.on.holiday', status_el['from-service'])
    assertEquals(CAP_NAME, status_el['capability'])
    assert 'activity' not in status_el.attributes
    assertEquals([], status_el.children)

    # check signal
    contact_id, capability, service_name, status_str = sig.args
    assertEquals(CAP_NAME, capability)
    assertEquals('bananaman.on.holiday', service_name)
    assertEquals('', status_str)

    # check property
    discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredStatuses',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({}, discovered)
示例#8
0
def test(q, bus, conn):
    # we won't be using any data forms, so these two shouldn't ever be
    # fired.
    q.forbid_events([
        EventPattern('dbus-signal', signal='ServiceAdded'),
        EventPattern('dbus-signal', signal='ServiceRemoved')
    ])

    call_async(q, conn.Future, 'EnsureSidecar', ycs.STATUS_IFACE)

    conn.Connect()

    # Now we're connected, the call we made earlier should return.
    e = q.expect('dbus-return', method='EnsureSidecar')
    path, props = e.value
    assertEquals({}, props)

    status = ProxyWrapper(bus.get_object(conn.bus_name, path),
                          ycs.STATUS_IFACE, {})

    # bad capability argument
    call_async(q, status, 'AdvertiseStatus', '', 'service.name', '')
    q.expect('dbus-error', method='AdvertiseStatus')

    # bad service name
    call_async(q, status, 'AdvertiseStatus', CAP_NAME, '', '')
    q.expect('dbus-error', method='AdvertiseStatus')

    # we can't test that the message type="headline" stanza is
    # actually received because it's thrown into the loopback stream
    # immediately.

    # announce a contact with the right caps
    ver = compute_caps_hash([], [CAP_NAME + '+notify'], {})
    txt_record = {
        "txtvers": "1",
        "status": "avail",
        "node": CLIENT_NAME,
        "ver": ver,
        "hash": "sha-1"
    }
    contact_name = "test-status@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)

    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
                               txt_record)

    handle = wait_for_contact_in_publish(q, bus, conn, contact_name)

    # this is the first presence, Salut connects to the contact
    e = q.expect('incoming-connection', listener=listener)
    incoming = e.connection

    # Salut looks up its capabilities
    event = q.expect('stream-iq', connection=incoming, query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assertEquals(CLIENT_NAME + '#' + ver, query_node.attributes['node'])

    contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact_name])[0]

    # send good reply
    result = make_result_iq(event.stanza)
    query = result.firstChildElement()
    query['node'] = CLIENT_NAME + '#' + ver

    feature = query.addElement('feature')
    feature['var'] = CAP_NAME + '+notify'
    incoming.send(result)

    # this will be fired as text channel caps will be fired
    q.expect('dbus-signal',
             signal='ContactCapabilitiesChanged',
             predicate=lambda e: contact_handle in e.args[0])

    # okay now we know about the contact's caps, we can go ahead

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredStatuses',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({}, discovered)

    el = Element(('urn:ytstenut:status', 'status'))
    el['activity'] = 'messing-with-your-stuff'
    desc = el.addElement('ytstenut:description',
                         content='Yeah sorry about that')
    desc['xml:lang'] = 'en-GB'

    call_async(q, status, 'AdvertiseStatus', CAP_NAME, 'ants.in.their.pants',
               el.toXml())

    e, _, sig = q.expect_many(
        EventPattern('stream-message', connection=incoming),
        EventPattern('dbus-return', method='AdvertiseStatus'),
        EventPattern('dbus-signal',
                     signal='StatusChanged',
                     interface=ycs.STATUS_IFACE))

    # check message
    message = e.stanza
    event = message.children[0]
    items = event.children[0]
    item = items.children[0]
    status_el = item.children[0]

    assertEquals('status', status_el.name)
    assertEquals('messing-with-your-stuff', status_el['activity'])
    assertEquals('ants.in.their.pants', status_el['from-service'])
    assertEquals(CAP_NAME, status_el['capability'])

    # check signal
    contact_id, capability, service_name, status_str = sig.args
    assertEquals(CAP_NAME, capability)
    assertEquals('ants.in.their.pants', service_name)
    assertNotEquals('', status_str)

    # check property
    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredStatuses',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            'testsuite@testsuite': {
                CAP_NAME: {
                    'ants.in.their.pants': status_str
                }
            }
        }, discovered)

    # set another
    el = Element(('urn:ytstenut:status', 'status'))
    el['activity'] = 'rofling'
    desc = el.addElement('ytstenut:description', content='U MAD?')
    desc['xml:lang'] = 'en-GB'

    call_async(q, status, 'AdvertiseStatus', CAP_NAME, 'bananaman.on.holiday',
               el.toXml())

    e, _, sig = q.expect_many(
        EventPattern('stream-message', connection=incoming),
        EventPattern('dbus-return', method='AdvertiseStatus'),
        EventPattern('dbus-signal',
                     signal='StatusChanged',
                     interface=ycs.STATUS_IFACE))

    # check message
    message = e.stanza
    event = message.children[0]
    items = event.children[0]
    item = items.children[0]
    status_el = item.children[0]

    assertEquals('status', status_el.name)
    assertEquals('rofling', status_el['activity'])
    assertEquals('bananaman.on.holiday', status_el['from-service'])
    assertEquals(CAP_NAME, status_el['capability'])

    # check signal
    contact_id, capability, service_name, bananaman_status_str = sig.args
    assertEquals(CAP_NAME, capability)
    assertEquals('bananaman.on.holiday', service_name)
    assertNotEquals('', bananaman_status_str)

    # check property
    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredStatuses',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            'testsuite@testsuite': {
                CAP_NAME: {
                    'ants.in.their.pants': status_str,
                    'bananaman.on.holiday': bananaman_status_str
                }
            }
        }, discovered)

    # unset the status from one service
    call_async(q, status, 'AdvertiseStatus', CAP_NAME, 'ants.in.their.pants',
               '')

    e, _, sig = q.expect_many(
        EventPattern('stream-message', connection=incoming),
        EventPattern('dbus-return', method='AdvertiseStatus'),
        EventPattern('dbus-signal',
                     signal='StatusChanged',
                     interface=ycs.STATUS_IFACE))

    # check message
    message = e.stanza
    event = message.children[0]
    items = event.children[0]
    item = items.children[0]
    status_el = item.children[0]

    assertEquals('status', status_el.name)
    assertEquals('ants.in.their.pants', status_el['from-service'])
    assertEquals(CAP_NAME, status_el['capability'])
    assert 'activity' not in status_el.attributes
    assertEquals([], status_el.children)

    # check signal
    contact_id, capability, service_name, status_str = sig.args
    assertEquals(CAP_NAME, capability)
    assertEquals('ants.in.their.pants', service_name)
    assertEquals('', status_str)

    # check property
    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredStatuses',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            'testsuite@testsuite': {
                CAP_NAME: {
                    'bananaman.on.holiday': bananaman_status_str
                }
            }
        }, discovered)

    # unset the status from the other service
    call_async(q, status, 'AdvertiseStatus', CAP_NAME, 'bananaman.on.holiday',
               '')

    e, _, sig = q.expect_many(
        EventPattern('stream-message', connection=incoming),
        EventPattern('dbus-return', method='AdvertiseStatus'),
        EventPattern('dbus-signal',
                     signal='StatusChanged',
                     interface=ycs.STATUS_IFACE))

    # check message
    message = e.stanza
    event = message.children[0]
    items = event.children[0]
    item = items.children[0]
    status_el = item.children[0]

    assertEquals('status', status_el.name)
    assertEquals('bananaman.on.holiday', status_el['from-service'])
    assertEquals(CAP_NAME, status_el['capability'])
    assert 'activity' not in status_el.attributes
    assertEquals([], status_el.children)

    # check signal
    contact_id, capability, service_name, status_str = sig.args
    assertEquals(CAP_NAME, capability)
    assertEquals('bananaman.on.holiday', service_name)
    assertEquals('', status_str)

    # check property
    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredStatuses',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({}, discovered)
def test(q, bus, conn):
    forbidden = [EventPattern('dbus-signal', signal='ServiceAdded'),
                 EventPattern('dbus-signal', signal='ServiceRemoved')]
    q.forbid_events(forbidden)

    conn.Connect()

    q.expect('dbus-signal', signal='StatusChanged', args=[0, 0])

    # announce a contact with the right caps
    ver = compute_caps_hash([], [], banshee)
    txt_record = { "txtvers": "1", "status": "avail",
        "node": CLIENT_NAME, "ver": ver, "hash": "sha-1"}
    contact_name = "test-service@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)

    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record)

    handle = wait_for_contact_in_publish(q, bus, conn, contact_name)

    # this is the first presence, Salut connects to the contact
    e = q.expect('incoming-connection', listener=listener)
    incoming = e.connection

    # Salut looks up its capabilities
    event = q.expect('stream-iq', connection=incoming,
        query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assertEquals(CLIENT_NAME + '#' + ver, query_node.attributes['node'])

    contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact_name])[0]

    # send good reply
    result = make_result_iq(event.stanza)
    query = result.firstChildElement()
    query['node'] = CLIENT_NAME + '#' + ver
    x = query.addElement((ns.X_DATA, 'x'))
    x['type'] = 'result'

    # FORM_TYPE
    field = x.addElement((None, 'field'))
    field['var'] = 'FORM_TYPE'
    field['type'] = 'hidden'
    field.addElement((None, 'value'), content='urn:ytstenut:capabilities#org.gnome.Banshee')

    # type
    field = x.addElement((None, 'field'))
    field['var'] = 'type'
    field.addElement((None, 'value'), content='application')

    # name
    field = x.addElement((None, 'field'))
    field['var'] = 'name'
    field.addElement((None, 'value'), content='en_GB/Banshee Media Player')
    field.addElement((None, 'value'), content='fr/Banshee Lecteur de Musique')

    # capabilities
    field = x.addElement((None, 'field'))
    field['var'] = 'capabilities'
    field.addElement((None, 'value'), content='urn:ytstenut:capabilities:yts-caps-audio')
    field.addElement((None, 'value'), content='urn:ytstenut:data:jingle:rtp')

    incoming.send(result)

    # this will be fired as text channel caps will be fired
    q.expect('dbus-signal', signal='ContactCapabilitiesChanged',
             predicate=lambda e: contact_handle in e.args[0])

    # add evince
    tmp = banshee.copy()
    tmp.update(evince)
    ver = compute_caps_hash([], [], tmp)
    txt_record['ver'] = ver
    announcer.update(txt_record)

    # Salut looks up our capabilities
    event = q.expect('stream-iq', connection=incoming,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        CLIENT_NAME + '#' + txt_record['ver']

    # send good reply
    result['id'] = event.stanza['id']
    query['node'] = CLIENT_NAME + '#' + ver

    x = query.addElement((ns.X_DATA, 'x'))
    x['type'] = 'result'

    # FORM_TYPE
    field = x.addElement((None, 'field'))
    field['var'] = 'FORM_TYPE'
    field['type'] = 'hidden'
    field.addElement((None, 'value'), content='urn:ytstenut:capabilities#org.gnome.Evince')

    # type
    field = x.addElement((None, 'field'))
    field['var'] = 'type'
    field.addElement((None, 'value'), content='application')

    # name
    field = x.addElement((None, 'field'))
    field['var'] = 'name'
    field.addElement((None, 'value'), content='en_GB/Evince Picture Viewer')
    field.addElement((None, 'value'), content='fr/Evince uh, ow do you say')

    # capabilities
    field = x.addElement((None, 'field'))
    field['var'] = 'capabilities'
    field.addElement((None, 'value'), content='urn:ytstenut:capabilities:pics')

    incoming.send(result)

    # this will be fired as text channel caps will be fired
    q.expect('dbus-signal', signal='ContactCapabilitiesChanged',
             predicate=lambda e: contact_handle in e.args[0])

    # now finally ensure the sidecar
    path, props = conn.Future.EnsureSidecar(ycs.STATUS_IFACE)
    assertEquals({}, props)

    status = ProxyWrapper(bus.get_object(conn.bus_name, path),
                          ycs.STATUS_IFACE, {})

    discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({contact_name: {
                'org.gnome.Banshee':
                    ('application',
                     {'en_GB': 'Banshee Media Player',
                      'fr': 'Banshee Lecteur de Musique'},
                     ['urn:ytstenut:capabilities:yts-caps-audio',
                      'urn:ytstenut:data:jingle:rtp']),
                  'org.gnome.Evince':
                      ('application',
                       {'en_GB': 'Evince Picture Viewer',
                        'fr': 'Evince uh, ow do you say'},
                       ['urn:ytstenut:capabilities:pics'])}
                }, discovered)
示例#10
0
def test_ft_caps_from_contact(q, bus, conn, client):

    conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS)
    conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS)

    # send presence with FT capa
    ver = compute_caps_hash([], [ns.IQ_OOB], {})
    txt_record = {
        "txtvers": "1",
        "status": "avail",
        "node": client,
        "ver": ver,
        "hash": "sha-1"
    }
    contact_name = "test-caps-ft@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)
    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
                               txt_record)

    # this is the first presence, Salut connects to the contact
    e = q.expect('incoming-connection', listener=listener)
    incoming = e.connection

    # Salut looks up our capabilities
    event = q.expect('stream-iq',
                     connection=incoming,
                     query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + ver, (query_node.attributes['node'], client, ver)

    contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0]

    # send good reply
    result = make_result_iq(event.stanza)
    query = result.firstChildElement()
    query['node'] = client + '#' + ver

    feature = query.addElement('feature')
    feature['var'] = ns.IQ_OOB
    incoming.send(result)

    # FT capa is announced
    e = q.expect('dbus-signal',
                 signal='ContactCapabilitiesChanged',
                 predicate=lambda e: contact_handle in e.args[0])
    caps = e.args[0][contact_handle]
    assertContains(ft_caps, caps)

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities']
    assert caps_via_contacts_iface == caps, caps_via_contacts_iface

    # check if Salut announces the OOB capa
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    self_handle_name = conn.Properties.Get(cs.CONN, "SelfID")

    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()

    receive_presence_and_ask_caps(q, incoming, service, contact_name)

    # capa announced without FT
    ver = compute_caps_hash([], ["http://telepathy.freedesktop.org/xmpp/pony"],
                            {})
    txt_record = {
        "txtvers": "1",
        "status": "avail",
        "node": client,
        "ver": ver,
        "hash": "sha-1"
    }
    contact_name = "test-caps-ft2@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)
    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
                               txt_record)

    # this is the first presence, Salut connects to the contact
    e = q.expect('incoming-connection', listener=listener)
    incoming = e.connection

    # Salut looks up our capabilities
    event = q.expect('stream-iq',
                     connection=incoming,
                     query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + ver, (query_node.attributes['node'], client, ver)

    contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0]

    # send good reply
    result = make_result_iq(event.stanza)
    query = result.firstChildElement()
    query['node'] = client + '#' + ver

    feature = query.addElement('feature')
    feature['var'] = "http://telepathy.freedesktop.org/xmpp/pony"
    incoming.send(result)

    # the FT capability is not announced
    e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
    caps = e.args[0][contact_handle]
    assertDoesNotContain(ft_caps, caps)

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities']
    assert caps_via_contacts_iface == caps, caps_via_contacts_iface

    # no capabilites announced (assume FT is supported to insure interop)
    txt_record = {"txtvers": "1", "status": "avail"}
    contact_name = "test-caps-ft-no-capa2@" + get_host_name()
    contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0]
    listener, port = setup_stream_listener(q, contact_name)
    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
                               txt_record)

    # FT capa is announced
    e = q.expect('dbus-signal',
                 signal='ContactCapabilitiesChanged',
                 predicate=lambda e: contact_handle in e.args[0].keys())

    caps = e.args[0][contact_handle]
    assertContains(ft_caps, caps)

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities']
    assert caps_via_contacts_iface == caps, caps_via_contacts_iface
示例#11
0
def test(q, bus, conn):
    call_async(q, conn.Future, 'EnsureSidecar', ycs.STATUS_IFACE)

    conn.Connect()

    # Now we're connected, the call we made earlier should return.
    e = q.expect('dbus-return', method='EnsureSidecar')
    path, props = e.value
    assertEquals({}, props)

    status = ProxyWrapper(bus.get_object(conn.bus_name, path),
                          ycs.STATUS_IFACE, {})

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({}, discovered)

    # announce a contact with the right caps
    ver = compute_caps_hash([], [], banshee)
    txt_record = {
        "txtvers": "1",
        "status": "avail",
        "node": CLIENT_NAME,
        "ver": ver,
        "hash": "sha-1"
    }
    contact_name = "test-service@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)

    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
                               txt_record)

    handle = wait_for_contact_in_publish(q, bus, conn, contact_name)

    # this is the first presence, Salut connects to the contact
    e = q.expect('incoming-connection', listener=listener)
    incoming = e.connection

    # Salut looks up its capabilities
    event = q.expect('stream-iq', connection=incoming, query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assertEquals(CLIENT_NAME + '#' + ver, query_node.attributes['node'])

    contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact_name])[0]

    # send good reply
    result = make_result_iq(event.stanza)
    query = result.firstChildElement()
    query['node'] = CLIENT_NAME + '#' + ver
    x = query.addElement((ns.X_DATA, 'x'))
    x['type'] = 'result'

    # FORM_TYPE
    field = x.addElement((None, 'field'))
    field['var'] = 'FORM_TYPE'
    field['type'] = 'hidden'
    field.addElement((None, 'value'),
                     content='urn:ytstenut:capabilities#org.gnome.Banshee')

    # type
    field = x.addElement((None, 'field'))
    field['var'] = 'type'
    field.addElement((None, 'value'), content='application')

    # name
    field = x.addElement((None, 'field'))
    field['var'] = 'name'
    field.addElement((None, 'value'), content='en_GB/Banshee Media Player')
    field.addElement((None, 'value'), content='fr/Banshee Lecteur de Musique')

    # capabilities
    field = x.addElement((None, 'field'))
    field['var'] = 'capabilities'
    field.addElement((None, 'value'),
                     content='urn:ytstenut:capabilities:yts-caps-audio')
    field.addElement((None, 'value'), content='urn:ytstenut:data:jingle:rtp')

    incoming.send(result)

    # this will be fired as text channel caps will be fired
    _, e = q.expect_many(
        EventPattern('dbus-signal',
                     signal='ContactCapabilitiesChanged',
                     predicate=lambda e: contact_handle in e.args[0]),
        EventPattern('dbus-signal', signal='ServiceAdded'))

    contact_id, service_name, details = e.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Banshee', service_name)

    type, name_map, caps = details
    assertEquals('application', type)
    assertEquals(
        {
            'en_GB': 'Banshee Media Player',
            'fr': 'Banshee Lecteur de Musique'
        }, name_map)
    assertSameSets([
        'urn:ytstenut:capabilities:yts-caps-audio',
        'urn:ytstenut:data:jingle:rtp'
    ], caps)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Banshee': ('application', {
                    'en_GB': 'Banshee Media Player',
                    'fr': 'Banshee Lecteur de Musique'
                }, [
                    'urn:ytstenut:capabilities:yts-caps-audio',
                    'urn:ytstenut:data:jingle:rtp'
                ])
            },
        }, discovered)

    # add evince
    tmp = banshee.copy()
    tmp.update(evince)
    ver = compute_caps_hash([], [], tmp)
    txt_record['ver'] = ver
    announcer.update(txt_record)

    # Salut looks up our capabilities
    event = q.expect('stream-iq',
                     connection=incoming,
                     query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        CLIENT_NAME + '#' + txt_record['ver']

    # send good reply
    result['id'] = event.stanza['id']
    query['node'] = CLIENT_NAME + '#' + ver

    x = query.addElement((ns.X_DATA, 'x'))
    x['type'] = 'result'

    # FORM_TYPE
    field = x.addElement((None, 'field'))
    field['var'] = 'FORM_TYPE'
    field['type'] = 'hidden'
    field.addElement((None, 'value'),
                     content='urn:ytstenut:capabilities#org.gnome.Evince')

    # type
    field = x.addElement((None, 'field'))
    field['var'] = 'type'
    field.addElement((None, 'value'), content='application')

    # name
    field = x.addElement((None, 'field'))
    field['var'] = 'name'
    field.addElement((None, 'value'), content='en_GB/Evince Picture Viewer')
    field.addElement((None, 'value'), content='fr/Evince uh, ow do you say')

    # capabilities
    field = x.addElement((None, 'field'))
    field['var'] = 'capabilities'
    field.addElement((None, 'value'), content='urn:ytstenut:capabilities:pics')

    incoming.send(result)

    e = q.expect('dbus-signal', signal='ServiceAdded')

    contact_id, service_name, details = e.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Evince', service_name)

    type, name_map, caps = details
    assertEquals('application', type)
    assertEquals(
        {
            'en_GB': 'Evince Picture Viewer',
            'fr': 'Evince uh, ow do you say'
        }, name_map)
    assertSameSets(['urn:ytstenut:capabilities:pics'], caps)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Banshee': ('application', {
                    'en_GB': 'Banshee Media Player',
                    'fr': 'Banshee Lecteur de Musique'
                }, [
                    'urn:ytstenut:capabilities:yts-caps-audio',
                    'urn:ytstenut:data:jingle:rtp'
                ]),
                'org.gnome.Evince': ('application', {
                    'en_GB': 'Evince Picture Viewer',
                    'fr': 'Evince uh, ow do you say'
                }, ['urn:ytstenut:capabilities:pics'])
            }
        }, discovered)

    # remove evince
    ver = compute_caps_hash([], [], banshee)
    txt_record['ver'] = ver

    forbidden = [
        EventPattern('dbus-signal', signal='stream-iq', connection=incoming)
    ]
    q.forbid_events(forbidden)

    announcer.update(txt_record)

    e = q.expect('dbus-signal', signal='ServiceRemoved')

    contact_id, service_name = e.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Evince', service_name)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Banshee': ('application', {
                    'en_GB': 'Banshee Media Player',
                    'fr': 'Banshee Lecteur de Musique'
                }, [
                    'urn:ytstenut:capabilities:yts-caps-audio',
                    'urn:ytstenut:data:jingle:rtp'
                ])
            },
        }, discovered)

    sync_stream(q, incoming)

    q.unforbid_events(forbidden)

    # now just evince
    ver = compute_caps_hash([], [], evince)
    txt_record['ver'] = ver
    announcer.update(txt_record)

    # Salut looks up our capabilities
    event = q.expect('stream-iq',
                     connection=incoming,
                     query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        CLIENT_NAME + '#' + txt_record['ver']

    # send good reply
    result = make_result_iq(event.stanza)
    query = result.firstChildElement()
    query['node'] = CLIENT_NAME + '#' + ver

    x = query.addElement((ns.X_DATA, 'x'))
    x['type'] = 'result'

    # FORM_TYPE
    field = x.addElement((None, 'field'))
    field['var'] = 'FORM_TYPE'
    field['type'] = 'hidden'
    field.addElement((None, 'value'),
                     content='urn:ytstenut:capabilities#org.gnome.Evince')

    # type
    field = x.addElement((None, 'field'))
    field['var'] = 'type'
    field.addElement((None, 'value'), content='application')

    # name
    field = x.addElement((None, 'field'))
    field['var'] = 'name'
    field.addElement((None, 'value'), content='en_GB/Evince Picture Viewer')
    field.addElement((None, 'value'), content='fr/Evince uh, ow do you say')

    # capabilities
    field = x.addElement((None, 'field'))
    field['var'] = 'capabilities'
    field.addElement((None, 'value'), content='urn:ytstenut:capabilities:pics')

    incoming.send(result)

    sa, sr = q.expect_many(
        EventPattern('dbus-signal', signal='ServiceAdded'),
        EventPattern('dbus-signal', signal='ServiceRemoved'))

    contact_id, service_name, details = sa.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Evince', service_name)

    type, name_map, caps = details
    assertEquals('application', type)
    assertEquals(
        {
            'en_GB': 'Evince Picture Viewer',
            'fr': 'Evince uh, ow do you say'
        }, name_map)
    assertSameSets(['urn:ytstenut:capabilities:pics'], caps)

    contact_id, service_name = sr.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Banshee', service_name)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Evince': ('application', {
                    'en_GB': 'Evince Picture Viewer',
                    'fr': 'Evince uh, ow do you say'
                }, ['urn:ytstenut:capabilities:pics'])
            }
        }, discovered)

    # just banshee again
    ver = compute_caps_hash([], [], banshee)
    txt_record['ver'] = ver

    forbidden = [
        EventPattern('dbus-signal', signal='stream-iq', connection=incoming)
    ]
    q.forbid_events(forbidden)

    announcer.update(txt_record)

    sr, sa = q.expect_many(
        EventPattern('dbus-signal', signal='ServiceRemoved'),
        EventPattern('dbus-signal', signal='ServiceAdded'))

    contact_id, service_name = sr.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Evince', service_name)

    contact_id, service_name, details = sa.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Banshee', service_name)

    type, name_map, caps = details
    assertEquals('application', type)
    assertEquals(
        {
            'en_GB': 'Banshee Media Player',
            'fr': 'Banshee Lecteur de Musique'
        }, name_map)
    assertSameSets([
        'urn:ytstenut:capabilities:yts-caps-audio',
        'urn:ytstenut:data:jingle:rtp'
    ], caps)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Banshee': ('application', {
                    'en_GB': 'Banshee Media Player',
                    'fr': 'Banshee Lecteur de Musique'
                }, [
                    'urn:ytstenut:capabilities:yts-caps-audio',
                    'urn:ytstenut:data:jingle:rtp'
                ])
            }
        }, discovered)

    sync_stream(q, incoming)

    q.unforbid_events(forbidden)

    # both again
    ver = compute_caps_hash([], [], tmp)
    txt_record['ver'] = ver
    announcer.update(txt_record)

    sa = q.expect('dbus-signal', signal='ServiceAdded')

    contact_id, service_name, details = sa.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Evince', service_name)

    type, name_map, caps = details
    assertEquals('application', type)
    assertEquals(
        {
            'en_GB': 'Evince Picture Viewer',
            'fr': 'Evince uh, ow do you say'
        }, name_map)
    assertSameSets(['urn:ytstenut:capabilities:pics'], caps)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Banshee': ('application', {
                    'en_GB': 'Banshee Media Player',
                    'fr': 'Banshee Lecteur de Musique'
                }, [
                    'urn:ytstenut:capabilities:yts-caps-audio',
                    'urn:ytstenut:data:jingle:rtp'
                ]),
                'org.gnome.Evince': ('application', {
                    'en_GB': 'Evince Picture Viewer',
                    'fr': 'Evince uh, ow do you say'
                }, ['urn:ytstenut:capabilities:pics'])
            }
        }, discovered)

    sync_stream(q, incoming)

    # and finally, nothing
    ver = compute_caps_hash([], [], {})
    txt_record['ver'] = ver
    announcer.update(txt_record)

    # Salut looks up our capabilities
    event = q.expect('stream-iq',
                     connection=incoming,
                     query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        CLIENT_NAME + '#' + txt_record['ver']

    # send good reply
    result = make_result_iq(event.stanza)
    query = result.firstChildElement()
    query['node'] = CLIENT_NAME + '#' + ver

    incoming.send(result)

    q.expect_many(
        EventPattern('dbus-signal',
                     signal='ServiceRemoved',
                     args=[contact_name, 'org.gnome.Banshee']),
        EventPattern('dbus-signal',
                     signal='ServiceRemoved',
                     args=[contact_name, 'org.gnome.Evince']))

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({}, discovered)
def test_ft_caps_from_contact(q, bus, conn, client):

    conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS)
    conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS)

    # send presence with FT capa
    ver = compute_caps_hash([], [ns.IQ_OOB], {})
    txt_record = { "txtvers": "1", "status": "avail",
        "node": client, "ver": ver, "hash": "sha-1"}
    contact_name = "test-caps-ft@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)
    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
            txt_record)

    # this is the first presence, Salut connects to the contact
    e = q.expect('incoming-connection', listener = listener)
    incoming = e.connection

    # Salut looks up our capabilities
    event = q.expect('stream-iq', connection = incoming,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + ver, (query_node.attributes['node'], client, ver)

    contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0]

    # send good reply
    result = make_result_iq(event.stanza)
    query = result.firstChildElement()
    query['node'] = client + '#' + ver

    feature = query.addElement('feature')
    feature['var'] = ns.IQ_OOB
    incoming.send(result)

    # FT capa is announced
    e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged',
            predicate=lambda e: contact_handle in e.args[0])
    caps = e.args[0][contact_handle]
    assertContains(ft_caps, caps)

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities']
    assert caps_via_contacts_iface == caps, caps_via_contacts_iface

    # check if Salut announces the OOB capa
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    self_handle_name =  conn.Properties.Get(cs.CONN, "SelfID")

    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()

    receive_presence_and_ask_caps(q, incoming, service, contact_name)

    # capa announced without FT
    ver = compute_caps_hash([], ["http://telepathy.freedesktop.org/xmpp/pony"], {})
    txt_record = { "txtvers": "1", "status": "avail",
        "node": client, "ver": ver, "hash": "sha-1"}
    contact_name = "test-caps-ft2@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)
    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
            txt_record)

    # this is the first presence, Salut connects to the contact
    e = q.expect('incoming-connection', listener = listener)
    incoming = e.connection

    # Salut looks up our capabilities
    event = q.expect('stream-iq', connection = incoming,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + ver, (query_node.attributes['node'], client, ver)

    contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0]

    # send good reply
    result = make_result_iq(event.stanza)
    query = result.firstChildElement()
    query['node'] = client + '#' + ver

    feature = query.addElement('feature')
    feature['var'] = "http://telepathy.freedesktop.org/xmpp/pony"
    incoming.send(result)

    # the FT capability is not announced
    e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
    caps = e.args[0][contact_handle]
    assertDoesNotContain(ft_caps, caps)

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities']
    assert caps_via_contacts_iface == caps, caps_via_contacts_iface

    # no capabilites announced (assume FT is supported to insure interop)
    txt_record = { "txtvers": "1", "status": "avail"}
    contact_name = "test-caps-ft-no-capa2@" + get_host_name()
    contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0]
    listener, port = setup_stream_listener(q, contact_name)
    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
            txt_record)

    # FT capa is announced
    e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged',
            predicate=lambda e: contact_handle in e.args[0].keys())

    caps = e.args[0][contact_handle]
    assertContains(ft_caps, caps)

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities']
    assert caps_via_contacts_iface == caps, caps_via_contacts_iface
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)

    # 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(expected_caps, 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)