Пример #1
0
def receive_caps(q, conn, stream, contact, contact_handle, features,
                 expected_caps, expect_disco=True, expect_ccc=True):

    caps = {'node': client,
            'ver': compute_caps_hash([], features, {}),
            'hash': 'sha-1'}

    presence_and_disco(q, conn, stream, contact, expect_disco,
                       client, caps, features, initial=False)

    if expect_ccc:
        event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
        announced_ccs, = event.args
        assertSameElements(expected_caps, announced_ccs[contact_handle])
    else:
        # Make sure Gabble's got the caps
        sync_stream(q, stream)

    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assertSameElements(expected_caps, caps[contact_handle])

    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [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)
Пример #2
0
 def wait_for_ft_caps(self):
     caps = get_contacts_capabilities_sync(self.conn, [self.handle])
     if caps != dbus.Dictionary({self.handle:generic_ft_caps}):
         self.q.expect('dbus-signal',
                       signal='ContactCapabilitiesChanged',
                       path=self.conn.object.object_path,
                       args=[{self.handle:generic_ft_caps}])
         caps = get_contacts_capabilities_sync(self.conn, [self.handle])
     assert caps == dbus.Dictionary({self.handle:generic_ft_caps}), caps
 def wait_for_ft_caps(self):
     caps = get_contacts_capabilities_sync(self.conn, [self.handle])
     if caps != dbus.Dictionary({self.handle:generic_ft_caps}):
         self.q.expect('dbus-signal',
                       signal='ContactCapabilitiesChanged',
                       path=self.conn.object.object_path,
                       args=[{self.handle:generic_ft_caps}])
         caps = get_contacts_capabilities_sync(self.conn, [self.handle])
     assert caps == dbus.Dictionary({self.handle:generic_ft_caps}), caps
Пример #4
0
def receive_caps(q,
                 conn,
                 stream,
                 contact,
                 contact_handle,
                 features,
                 expected_caps,
                 expect_disco=True,
                 expect_ccc=True):
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ver'] = compute_caps_hash([], features if features is not None else [],
                                 {})
    c['hash'] = 'sha-1'
    stream.send(presence)

    if expect_disco:
        # Gabble looks up our capabilities
        event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
        query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
        assert query_node.attributes['node'] == \
            client + '#' + c['ver']

        # send good reply
        result = make_result_iq(stream, event.stanza)
        query = result.firstChildElement()
        query['node'] = client + '#' + c['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)
    else:
        # Make sure Gabble's got the caps
        sync_stream(q, stream)

    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assertSameElements(expected_caps, caps)

    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assertSameElements(expected_caps, caps)

    # 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)
Пример #5
0
def receive_caps(q, conn, stream, contact, contact_handle, features,
                 expected_caps, expect_disco=True, expect_ccc=True):
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ver'] = compute_caps_hash([],
        features if features is not None else [],
        {})
    c['hash'] = 'sha-1'
    stream.send(presence)

    if expect_disco:
        # Gabble looks up our capabilities
        event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
        query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
        assert query_node.attributes['node'] == \
            client + '#' + c['ver']

        # send good reply
        result = make_result_iq(stream, event.stanza)
        query = result.firstChildElement()
        query['node'] = client + '#' + c['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)
    else:
        # Make sure Gabble's got the caps
        sync_stream(q, stream)

    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assertSameElements(expected_caps, caps)

    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assertSameElements(expected_caps, caps)

    # 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)
Пример #6
0
def advertise_caps(q, conn, stream, filters, expected_features,
                   unexpected_features, expected_caps):
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    ret_caps = conn.ContactCapabilities.UpdateCapabilities([
        (cs.CLIENT + '.Foo', filters, [])
    ])

    # Expect Gabble to reply with the correct caps
    event, namespaces, _, signaled_caps = receive_presence_and_ask_caps(
        q, stream)

    assertSameElements(expected_caps, signaled_caps)

    assertContains(ns.TUBES, namespaces)

    for var in expected_features:
        assertContains(var, namespaces)

    for var in unexpected_features:
        assertDoesNotContain(var, namespaces)

    # Check our own caps
    caps = get_contacts_capabilities_sync(conn, [self_handle])
    assertSameElements(expected_caps, caps)

    # 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)
def test_caps(q, conn, stream, contact, features, audio, video, google=False):
    caps['ver'] = compute_caps_hash([], features, {})

    h = presence_and_disco(q, conn, stream, contact, True, client, caps,
                           features)

    cflags = 0
    call_expected_media_caps = []

    if audio:
        cflags |= cs.MEDIA_CAP_AUDIO
        call_expected_media_caps.append(cs.CALL_INITIAL_AUDIO)
        call_expected_media_caps.append(cs.CALL_INITIAL_AUDIO_NAME)
    if video:
        cflags |= cs.MEDIA_CAP_VIDEO
        call_expected_media_caps.append(cs.CALL_INITIAL_VIDEO)
        call_expected_media_caps.append(cs.CALL_INITIAL_VIDEO_NAME)

    # If the contact can only do one of audio or video, or uses a Google
    # client, they'll have the ImmutableStreams cap.
    if cflags < (cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO) or google:
        cflags |= cs.MEDIA_CAP_IMMUTABLE_STREAMS
    else:
        call_expected_media_caps.append(cs.CALL_MUTABLE_CONTENTS)

    event, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'))

    # Check Contact capabilities
    assertEquals(len(event.args), 1)
    assertEquals(event.args[0], get_contacts_capabilities_sync(conn, [h]))

    check_contact_caps(event.args[0][h], cs.CHANNEL_TYPE_CALL,
                       call_expected_media_caps)
Пример #8
0
def advertise_caps(q, bus, conn, stream, 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.Properties.Get(cs.CONN, "SelfHandle")
    ret_caps = conn.ContactCapabilities.UpdateCapabilities(
            [(cs.CLIENT + '.Foo', filters, [])])

    # Expect Gabble to reply with the correct caps
    event, namespaces, _, signaled_caps = receive_presence_and_ask_caps(q, stream)

    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 = get_contacts_capabilities_sync(conn, [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)
Пример #9
0
def receive_caps(q,
                 conn,
                 stream,
                 contact,
                 contact_handle,
                 features,
                 expected_caps,
                 expect_disco=True,
                 expect_ccc=True):

    caps = {
        'node': client,
        'ver': compute_caps_hash([], features, {}),
        'hash': 'sha-1'
    }

    presence_and_disco(q,
                       conn,
                       stream,
                       contact,
                       expect_disco,
                       client,
                       caps,
                       features,
                       initial=False)

    if expect_ccc:
        event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
        announced_ccs, = event.args
        assertSameElements(expected_caps, announced_ccs[contact_handle])
    else:
        # Make sure Gabble's got the caps
        sync_stream(q, stream)

    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assertSameElements(expected_caps, caps[contact_handle])

    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [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)
Пример #10
0
def test_ft_caps_from_contact(q, bus, conn, stream, contact):
    contact_handle = conn.get_contact_handle_sync(contact)

    # Check that we don't crash if we haven't seen any caps/presence for this
    # contact yet.
    caps = get_contacts_capabilities_sync(conn, [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(basic_caps, 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, conn, stream, 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, conn, stream, contact, contact_handle,
        [ns.FILE_TRANSFER], 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.FILE_TRANSFER, ns.TP_FT_METADATA]
    receive_caps(q, conn, stream, 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, conn, stream, 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, conn, stream, 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
    # Gabble does not look up our capabilities because of the cache
    receive_caps(q, conn, stream, contact, contact_handle,
        generic_ft_features + [ns.TP_FT_METADATA + '#daap'], daap_caps,
        expect_disco=False)
Пример #11
0
def check_contact_caps(conn, handle, with_ft):
    conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS)

    if with_ft:
        expected_caps = dbus.Dictionary({handle: generic_ft_caps})
    else:
        expected_caps = dbus.Dictionary({handle: generic_caps})

    caps = get_contacts_capabilities_sync(conn, [handle])
    assert caps == expected_caps, caps
    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [handle][cs.ATTR_CONTACT_CAPABILITIES]
    assert caps_via_contacts_iface == caps[handle], \
        caps_via_contacts_iface
def check_contact_caps(conn, handle, with_ft):
    conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS)

    if with_ft:
        expected_caps = dbus.Dictionary({handle: generic_ft_caps})
    else:
        expected_caps = dbus.Dictionary({handle: generic_caps})

    caps = get_contacts_capabilities_sync(conn, [handle])
    assert caps == expected_caps, caps
    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [handle][cs.ATTR_CONTACT_CAPABILITIES]
    assert caps_via_contacts_iface == caps[handle], \
        caps_via_contacts_iface
Пример #13
0
def test(q, bus, conn, stream):
    conn.Connect()

    # bob is offline
    jid = '*****@*****.**'

    event = q.expect('stream-iq', query_ns=ns.ROSTER)

    event.stanza['type'] = 'result'

    item = event.query.addElement('item')
    item['jid'] = jid
    item['subscription'] = 'from'

    stream.send(event.stanza)

    q.expect('dbus-signal',
             signal='StatusChanged',
             args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]),

    bob_handle = conn.get_contact_handle_sync(jid)

    # new ContactCapabilities
    ccaps_map = get_contacts_capabilities_sync(conn, [bob_handle])
    assertLength(1, ccaps_map)

    assertLength(1, ccaps_map[bob_handle])

    fixed, allowed = ccaps_map[bob_handle][0]

    assertEquals(
        {
            cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
            cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT
        }, fixed)
    assertSameSets([cs.TARGET_HANDLE], allowed)
Пример #14
0
def test_caps(q, conn, stream, contact, features, audio, video, google=False):
    caps['ver'] = compute_caps_hash ([], features, {})

    h = presence_and_disco(q, conn, stream, contact, True,
        client, caps, features)

    cflags = 0
    call_expected_media_caps = []

    if audio:
      cflags |= cs.MEDIA_CAP_AUDIO
      call_expected_media_caps.append (cs.CALL_INITIAL_AUDIO)
      call_expected_media_caps.append (cs.CALL_INITIAL_AUDIO_NAME)
    if video:
      cflags |= cs.MEDIA_CAP_VIDEO
      call_expected_media_caps.append (cs.CALL_INITIAL_VIDEO)
      call_expected_media_caps.append (cs.CALL_INITIAL_VIDEO_NAME)

    # If the contact can only do one of audio or video, or uses a Google
    # client, they'll have the ImmutableStreams cap.
    if cflags < (cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO) or google:
        cflags |= cs.MEDIA_CAP_IMMUTABLE_STREAMS
    else:
        call_expected_media_caps.append(cs.CALL_MUTABLE_CONTENTS)

    event, = q.expect_many(
            EventPattern('dbus-signal', signal='ContactCapabilitiesChanged')
        )

    # Check Contact capabilities
    assertEquals(len(event.args), 1)
    assertEquals (event.args[0],
        get_contacts_capabilities_sync(conn, [h]))

    check_contact_caps (event.args[0][h],
        cs.CHANNEL_TYPE_CALL, call_expected_media_caps)
Пример #15
0
def test_ft_caps_to_contact(q, bus, conn, stream):
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")

    basic_caps = [
        (text_fixed_properties, text_allowed_properties),
        (stream_tube_fixed_properties, stream_tube_allowed_properties),
        (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
        ]
    simple_ft_caps = basic_caps + [
        (ft_fixed_properties, ft_allowed_properties_with_metadata),
        ]
    daap_caps = simple_ft_caps + [
        (bidir_daap_fixed_properties, ft_allowed_properties + [cs.FT_METADATA]),
        ]
    xiangqi_caps = simple_ft_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 = simple_ft_caps + [
        (go_fixed_properties, ft_allowed_properties + [cs.FT_METADATA]),
        ]

    #
    # Check our own caps; we should have no FT caps
    #
    caps = get_contacts_capabilities_sync(conn, [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 = get_contacts_capabilities_sync(conn, [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_stream(q, stream)

    #
    # Advertise FT but with no service name
    #
    conn.ContactCapabilities.UpdateCapabilities(
            [(cs.CLIENT + '.Foo', [no_service_fixed_properties], [])])

    # Check our own caps
    caps = get_contacts_capabilities_sync(conn, [self_handle])
    assertLength(1, caps)
    assertEquals(simple_ft_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_stream(q, stream)

    #
    # Advertise a Requested=True FT cap
    #
    conn.ContactCapabilities.UpdateCapabilities(
            [(cs.CLIENT + '.Foo', [outgoing_daap_fixed_properties], [])])

    # Check our own caps
    caps = get_contacts_capabilities_sync(conn, [self_handle])
    assertLength(1, caps)
    assertEquals(simple_ft_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, stream,
        [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, stream,
        [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, stream,
        [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, stream,
        [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)
Пример #16
0
def test_ft_caps_to_contact(q, bus, conn, stream):
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")

    basic_caps = [
        (text_fixed_properties, text_allowed_properties),
        (stream_tube_fixed_properties, stream_tube_allowed_properties),
        (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
    ]
    simple_ft_caps = basic_caps + [
        (ft_fixed_properties, ft_allowed_properties_with_metadata),
    ]
    daap_caps = simple_ft_caps + [
        (bidir_daap_fixed_properties,
         ft_allowed_properties + [cs.FT_METADATA]),
    ]
    xiangqi_caps = simple_ft_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 = simple_ft_caps + [
        (go_fixed_properties, ft_allowed_properties + [cs.FT_METADATA]),
    ]

    #
    # Check our own caps; we should have no FT caps
    #
    caps = get_contacts_capabilities_sync(conn, [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 = get_contacts_capabilities_sync(conn, [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_stream(q, stream)

    #
    # Advertise FT but with no service name
    #
    conn.ContactCapabilities.UpdateCapabilities([
        (cs.CLIENT + '.Foo', [no_service_fixed_properties], [])
    ])

    # Check our own caps
    caps = get_contacts_capabilities_sync(conn, [self_handle])
    assertLength(1, caps)
    assertEquals(simple_ft_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_stream(q, stream)

    #
    # Advertise a Requested=True FT cap
    #
    conn.ContactCapabilities.UpdateCapabilities([
        (cs.CLIENT + '.Foo', [outgoing_daap_fixed_properties], [])
    ])

    # Check our own caps
    caps = get_contacts_capabilities_sync(conn, [self_handle])
    assertLength(1, caps)
    assertEquals(simple_ft_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, stream, [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, stream,
                   [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, stream,
                   [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, stream, [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)
Пример #17
0
def test_tube_caps_from_contact(q, bus, conn, stream, contact):
    contact_handle = conn.get_contact_handle_sync(contact)

    # Check that we don't crash if we haven't seen any caps/presence for this
    # contact yet.
    caps = get_contacts_capabilities_sync(conn, [contact_handle])

    basic_caps = dbus.Dictionary(
        {contact_handle: [(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(basic_caps, caps)

    # send presence with no tube cap
    # We don't expect ContactCapabilitiesChanged to be emitted here: we always
    # assume people can do text channels.
    receive_caps(q,
                 conn,
                 stream,
                 contact,
                 contact_handle, [],
                 basic_caps,
                 expect_ccc=False)

    # send presence with generic tubes caps
    generic_tubes_caps = dbus.Dictionary({
        contact_handle:
        [(text_fixed_properties, text_allowed_properties),
         (stream_tube_fixed_properties, stream_tube_allowed_properties),
         (dbus_tube_fixed_properties, dbus_tube_allowed_properties)]
    })
    receive_caps(q, conn, stream, contact, contact_handle, [ns.TUBES],
                 generic_tubes_caps)

    # send presence with 1 stream tube cap
    daap_caps = dbus.Dictionary({
        contact_handle:
        [(text_fixed_properties, text_allowed_properties),
         (stream_tube_fixed_properties, stream_tube_allowed_properties),
         (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
         (incoming_daap_fixed_properties, specialized_tube_allowed_properties)]
    })
    receive_caps(q, conn, stream, contact, contact_handle,
                 [ns.TUBES + '/stream#daap'], daap_caps)

    # send presence with 1 D-Bus tube cap
    xiangqi_caps = dbus.Dictionary({
        contact_handle:
        [(text_fixed_properties, text_allowed_properties),
         (stream_tube_fixed_properties, stream_tube_allowed_properties),
         (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
         (xiangqi_fixed_properties, specialized_tube_allowed_properties)]
    })
    receive_caps(q, conn, stream, contact, contact_handle,
                 [ns.TUBES + '/dbus#com.example.Xiangqi'], xiangqi_caps)

    # send presence with both D-Bus and stream tube caps
    daap_xiangqi_caps = dbus.Dictionary({
        contact_handle:
        [(text_fixed_properties, text_allowed_properties),
         (stream_tube_fixed_properties, stream_tube_allowed_properties),
         (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
         (incoming_daap_fixed_properties, specialized_tube_allowed_properties),
         (xiangqi_fixed_properties, specialized_tube_allowed_properties)]
    })
    receive_caps(q, conn, stream, contact, contact_handle, [
        ns.TUBES + '/dbus#com.example.Xiangqi',
        ns.TUBES + '/stream#daap',
    ], daap_xiangqi_caps)

    # send presence with 4 tube caps
    all_tubes_caps = dbus.Dictionary({
        contact_handle:
        [(text_fixed_properties, text_allowed_properties),
         (stream_tube_fixed_properties, stream_tube_allowed_properties),
         (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
         (incoming_daap_fixed_properties, specialized_tube_allowed_properties),
         (http_fixed_properties, specialized_tube_allowed_properties),
         (xiangqi_fixed_properties, specialized_tube_allowed_properties),
         (go_fixed_properties, specialized_tube_allowed_properties)]
    })
    receive_caps(q, conn, stream, contact, contact_handle, [
        ns.TUBES + '/dbus#com.example.Xiangqi',
        ns.TUBES + '/dbus#com.example.Go',
        ns.TUBES + '/stream#daap',
        ns.TUBES + '/stream#http',
    ], all_tubes_caps)

    # send presence with both D-Bus and stream tube caps
    # Gabble does not look up our capabilities because of the cache
    receive_caps(q,
                 conn,
                 stream,
                 contact,
                 contact_handle, [
                     ns.TUBES + '/dbus#com.example.Xiangqi',
                     ns.TUBES + '/stream#daap',
                 ],
                 daap_xiangqi_caps,
                 expect_disco=False)
Пример #18
0
def test(q, bus, conn, stream):
    bob = conn.get_contact_handle_sync('*****@*****.**')

    presence = make_presence('[email protected]/Foo', status='hello')
    stream.send(presence)

    q.expect('dbus-signal', signal='PresencesChanged',
           args=[{bob: (cs.PRESENCE_AVAILABLE, u'available', 'hello')}])

    basic_caps = [(bob, cs.CHANNEL_TYPE_TEXT, 3, 0)]

    # only Text
    for rcc in get_contacts_capabilities_sync(conn, [bob])[bob]:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    # holding the handle here: see below
    assertEquals(
            { bob: {
                cs.ATTR_CONTACT_CAPABILITIES:
                    get_contacts_capabilities_sync(conn, [bob])[bob],
                cs.CONN + '/contact-id': '*****@*****.**',
                },
            },
            conn.Contacts.GetContactAttributes([bob], [cs.CONN_IFACE_CONTACT_CAPS], True))

    # send updated presence with Jingle audio/video caps info. we turn on both
    # audio and video at the same time to test that all of the capabilities are
    # discovered before any capabilities change signal is emitted
    presence = make_presence('[email protected]/Foo', status='hello',
        caps={
            'node': 'http://telepathy.freedesktop.org/fake-client',
            'ver' : '0.1',
            'ext' : 'video',
        })
    stream.send(presence)

    # Gabble looks up both the version and the video bundles, in any order
    (version_event, video_event) = q.expect_many(
        EventPattern('stream-iq', to='[email protected]/Foo',
            query_ns='http://jabber.org/protocol/disco#info',
            query_node='http://telepathy.freedesktop.org/fake-client#0.1'),
        EventPattern('stream-iq', to='[email protected]/Foo',
            query_ns='http://jabber.org/protocol/disco#info',
            query_node='http://telepathy.freedesktop.org/fake-client#video'))

    # reply to the video bundle query first - this capability alone is not
    # sufficient to make us callable
    result = make_result_iq(stream, video_event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle/description/video'
    stream.send(result)

    # reply to the version bundle query, which should make us audio and
    # video callable
    result = make_result_iq(stream, version_event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle'
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle/description/audio'
    feature = query.addElement('feature')
    feature['var'] = 'http://www.google.com/transport/p2p'
    stream.send(result)

    # we can now do audio and video calls
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged',
            predicate=lambda e: check_rccs_callable(e.args[0][bob])),
        )
    assert_rccs_callable(cc.args[0][bob], require_video=True,
            mutable_contents=True)

    assertEquals(
            { bob: {
                cs.ATTR_CONTACT_CAPABILITIES:
                    cc.args[0][bob],
                cs.CONN + '/contact-id': '*****@*****.**',
                },
            },
            conn.Contacts.GetContactAttributes([bob],
                [cs.CONN_IFACE_CONTACT_CAPS], True))

    # send updated presence without video support
    presence = make_presence('[email protected]/Foo', status='hello',
        caps={
            'node': 'http://telepathy.freedesktop.org/fake-client',
            'ver' : '0.1',
        })
    stream.send(presence)

    # we can now do only audio calls (and as a result have the ImmutableStreams
    # cap)
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        )
    assert_rccs_callable(cc.args[0][bob])
    assert_rccs_not_callable(cc.args[0][bob], require_audio=False,
            require_video=True, mutable_contents=False)

    assertEquals(
            { bob: {
                cs.ATTR_CONTACT_CAPABILITIES:
                    cc.args[0][bob],
                cs.CONN + '/contact-id': '*****@*****.**',
                },
            },
            conn.Contacts.GetContactAttributes([bob],
                [cs.CONN_IFACE_CONTACT_CAPS], True))

    # go offline
    presence = make_presence('[email protected]/Foo', type='unavailable')
    stream.send(presence)

    # can't do audio calls any more
    q.expect_many(
            EventPattern('dbus-signal', signal='PresencesChanged',
                args=[{bob: (cs.PRESENCE_OFFLINE, 'offline', '')}]),
            EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
            )

    # Contact went offline. Previously, this test asserted that the handle
    # became invalid, but that's not guaranteed to happen immediately; so we
    # now hold the handle (above), to guarantee that it does *not* become
    # invalid.
    rccs = get_contacts_capabilities_sync(conn, [bob])[bob]
    for rcc in rccs:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    assertEquals(
            { bob: {
                cs.ATTR_CONTACT_CAPABILITIES: rccs,
                cs.CONN + '/contact-id': '*****@*****.**',
                },
            },
            conn.Contacts.GetContactAttributes([bob],
                [cs.CONN_IFACE_CONTACT_CAPS], True))

    # What about a handle that's not valid?
    assertEquals({}, conn.Contacts.GetContactAttributes(
        [31337], [cs.CONN_IFACE_CONTACT_CAPS], False))
Пример #19
0
def test_tube_caps_from_contact(q, bus, conn, stream, contact):
    contact_handle = conn.get_contact_handle_sync(contact)

    # Check that we don't crash if we haven't seen any caps/presence for this
    # contact yet.
    caps = get_contacts_capabilities_sync(conn, [contact_handle])

    basic_caps = dbus.Dictionary({contact_handle:
            [(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(basic_caps, caps)

    # send presence with no tube cap
    # We don't expect ContactCapabilitiesChanged to be emitted here: we always
    # assume people can do text channels.
    receive_caps(q, conn, stream, contact, contact_handle, [], basic_caps,
        expect_ccc=False)

    # send presence with generic tubes caps
    generic_tubes_caps = dbus.Dictionary({contact_handle:
            [(text_fixed_properties, text_allowed_properties),
             (stream_tube_fixed_properties, stream_tube_allowed_properties),
             (dbus_tube_fixed_properties, dbus_tube_allowed_properties)]})
    receive_caps(q, conn, stream, contact, contact_handle, [ns.TUBES],
        generic_tubes_caps)

    # send presence with 1 stream tube cap
    daap_caps = dbus.Dictionary({contact_handle:
            [(text_fixed_properties, text_allowed_properties),
             (stream_tube_fixed_properties, stream_tube_allowed_properties),
             (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
             (incoming_daap_fixed_properties, specialized_tube_allowed_properties)]})
    receive_caps(q, conn, stream, contact, contact_handle,
        [ns.TUBES + '/stream#daap'], daap_caps)

    # send presence with 1 D-Bus tube cap
    xiangqi_caps = dbus.Dictionary({contact_handle:
            [(text_fixed_properties, text_allowed_properties),
             (stream_tube_fixed_properties, stream_tube_allowed_properties),
             (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
            (xiangqi_fixed_properties, specialized_tube_allowed_properties)]})
    receive_caps(q, conn, stream, contact, contact_handle,
        [ns.TUBES + '/dbus#com.example.Xiangqi'], xiangqi_caps)

    # send presence with both D-Bus and stream tube caps
    daap_xiangqi_caps = dbus.Dictionary({contact_handle:
        [(text_fixed_properties, text_allowed_properties),
        (stream_tube_fixed_properties, stream_tube_allowed_properties),
        (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
        (incoming_daap_fixed_properties, specialized_tube_allowed_properties),
        (xiangqi_fixed_properties, specialized_tube_allowed_properties)]})
    receive_caps(q, conn, stream, contact, contact_handle,
        [ns.TUBES + '/dbus#com.example.Xiangqi',
         ns.TUBES + '/stream#daap',
        ], daap_xiangqi_caps)

    # send presence with 4 tube caps
    all_tubes_caps = dbus.Dictionary({contact_handle:
        [(text_fixed_properties, text_allowed_properties),
        (stream_tube_fixed_properties, stream_tube_allowed_properties),
        (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
        (incoming_daap_fixed_properties, specialized_tube_allowed_properties),
        (http_fixed_properties, specialized_tube_allowed_properties),
        (xiangqi_fixed_properties, specialized_tube_allowed_properties),
        (go_fixed_properties, specialized_tube_allowed_properties)]})
    receive_caps(q, conn, stream, contact, contact_handle,
        [ns.TUBES + '/dbus#com.example.Xiangqi',
         ns.TUBES + '/dbus#com.example.Go',
         ns.TUBES + '/stream#daap',
         ns.TUBES + '/stream#http',
        ], all_tubes_caps)

    # send presence with both D-Bus and stream tube caps
    # Gabble does not look up our capabilities because of the cache
    receive_caps(q, conn, stream, contact, contact_handle,
        [ns.TUBES + '/dbus#com.example.Xiangqi',
         ns.TUBES + '/stream#daap',
        ], daap_xiangqi_caps, expect_disco=False)
Пример #20
0
def test_tube_caps_to_contact(q, bus, conn, stream):
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")

    basic_caps = dbus.Dictionary({
        self_handle: [
            (text_fixed_properties, text_allowed_properties),
            (stream_tube_fixed_properties, stream_tube_allowed_properties),
            (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
        ]
    })
    daap_caps = dbus.Dictionary({
        self_handle:
        [(text_fixed_properties, text_allowed_properties),
         (stream_tube_fixed_properties, stream_tube_allowed_properties),
         (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
         (incoming_daap_fixed_properties, specialized_tube_allowed_properties),
         (ft_fixed_properties, ft_allowed_properties)]
    })
    xiangqi_caps = dbus.Dictionary({
        self_handle:
        [(text_fixed_properties, text_allowed_properties),
         (stream_tube_fixed_properties, stream_tube_allowed_properties),
         (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
         (xiangqi_fixed_properties, specialized_tube_allowed_properties),
         (ft_fixed_properties, ft_allowed_properties)]
    })
    daap_xiangqi_caps = dbus.Dictionary({
        self_handle:
        [(text_fixed_properties, text_allowed_properties),
         (stream_tube_fixed_properties, stream_tube_allowed_properties),
         (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
         (incoming_daap_fixed_properties, specialized_tube_allowed_properties),
         (xiangqi_fixed_properties, specialized_tube_allowed_properties),
         (ft_fixed_properties, ft_allowed_properties)]
    })
    all_tubes_caps = dbus.Dictionary({
        self_handle:
        [(text_fixed_properties, text_allowed_properties),
         (stream_tube_fixed_properties, stream_tube_allowed_properties),
         (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
         (incoming_daap_fixed_properties, specialized_tube_allowed_properties),
         (http_fixed_properties, specialized_tube_allowed_properties),
         (xiangqi_fixed_properties, specialized_tube_allowed_properties),
         (go_fixed_properties, specialized_tube_allowed_properties),
         (ft_fixed_properties, ft_allowed_properties)]
    })

    # Check our own caps
    caps = get_contacts_capabilities_sync(conn, [self_handle])
    assertEquals(basic_caps, caps)

    # 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 = get_contacts_capabilities_sync(conn, [self_handle])
    assertLength(1, caps)
    assertEquals(basic_caps, caps)

    # 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_stream(q, stream)

    # Advertise a Requested=True tube cap
    conn.ContactCapabilities.UpdateCapabilities([
        (cs.CLIENT + '.Foo', [outgoing_daap_fixed_properties], [])
    ])

    # Check our own caps
    caps = get_contacts_capabilities_sync(conn, [self_handle])
    assertLength(1, caps)
    assertEquals(basic_caps, caps)

    # 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_stream(q, stream)

    advertise_caps(q, conn, stream, [bidir_daap_fixed_properties],
                   [ns.TUBES + '/stream#daap'], [
                       ns.TUBES + '/stream#http',
                       ns.TUBES + '/dbus#com.example.Go',
                       ns.TUBES + '/dbus#com.example.Xiangqi',
                   ], daap_caps)

    advertise_caps(q, conn, stream,
                   [outgoing_daap_fixed_properties, xiangqi_fixed_properties],
                   [ns.TUBES + '/dbus#com.example.Xiangqi'], [
                       ns.TUBES + '/stream#daap',
                       ns.TUBES + '/stream#http',
                       ns.TUBES + '/dbus#com.example.Go',
                   ], xiangqi_caps)

    advertise_caps(q, conn, stream, [
        incoming_daap_fixed_properties, outgoing_daap_fixed_properties,
        xiangqi_fixed_properties
    ], [
        ns.TUBES + '/dbus#com.example.Xiangqi',
        ns.TUBES + '/stream#daap',
    ], [
        ns.TUBES + '/stream#http',
        ns.TUBES + '/dbus#com.example.Go',
    ], daap_xiangqi_caps)

    advertise_caps(q, conn, stream, [
        incoming_daap_fixed_properties, http_fixed_properties,
        go_fixed_properties, xiangqi_fixed_properties
    ], [
        ns.TUBES + '/dbus#com.example.Xiangqi',
        ns.TUBES + '/stream#daap',
        ns.TUBES + '/stream#http',
        ns.TUBES + '/dbus#com.example.Go',
    ], [], all_tubes_caps)

    # test daap + xiangqi again for some reason
    advertise_caps(q, conn, stream,
                   [incoming_daap_fixed_properties, xiangqi_fixed_properties],
                   [
                       ns.TUBES + '/dbus#com.example.Xiangqi',
                       ns.TUBES + '/stream#daap',
                   ], [
                       ns.TUBES + '/stream#http',
                       ns.TUBES + '/dbus#com.example.Go',
                   ], daap_xiangqi_caps)
def test_ft_caps_from_contact(q, bus, conn, stream, contact, contact_handle, 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 no FT cap
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ver'] = compute_caps_hash([], [], {})
    c['hash'] = 'sha-1'
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + c['ver']

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

    # no change in ContactCapabilities, so no signal ContactCapabilitiesChanged
    sync_stream(q, stream)

    # no special capabilities
    basic_caps = dbus.Dictionary({contact_handle:
            [(text_fixed_properties, text_allowed_properties)]})
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == basic_caps, caps
    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == basic_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.ATTR_CONTACT_CAPABILITIES]
    assert caps_via_contacts_iface == caps[contact_handle], \
                                    caps_via_contacts_iface

    # send presence with ft capa
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ver'] = compute_caps_hash([], [ns.FILE_TRANSFER], {})
    c['hash'] = 'sha-1'
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + c['ver']

    # send good reply
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    query['node'] = client + '#' + c['ver']
    feature = query.addElement('feature')
    feature['var'] = ns.FILE_TRANSFER
    stream.send(result)

    generic_tubes_caps = dbus.Dictionary({contact_handle:
            [(text_fixed_properties, text_allowed_properties),
             (ft_fixed_properties, ft_allowed_properties)]})

    event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
    assert len(event.args) == 1
    assert event.args[0] == generic_tubes_caps

    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == generic_tubes_caps, caps
    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == generic_tubes_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.ATTR_CONTACT_CAPABILITIES]
    assert caps_via_contacts_iface == caps[contact_handle], \
                                    caps_via_contacts_iface
Пример #22
0
def test_ft_caps_from_contact(q, bus, conn, stream, contact):
    contact_handle = conn.get_contact_handle_sync(contact)

    # Check that we don't crash if we haven't seen any caps/presence for this
    # contact yet.
    caps = get_contacts_capabilities_sync(conn, [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(basic_caps, 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,
                 conn,
                 stream,
                 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, conn, stream, contact, contact_handle, [ns.FILE_TRANSFER],
                 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.FILE_TRANSFER, ns.TP_FT_METADATA]
    receive_caps(q, conn, stream, 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, conn, stream, 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, conn, stream, 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
    # Gabble does not look up our capabilities because of the cache
    receive_caps(q,
                 conn,
                 stream,
                 contact,
                 contact_handle,
                 generic_ft_features + [ns.TP_FT_METADATA + '#daap'],
                 daap_caps,
                 expect_disco=False)
Пример #23
0
def check_caps(conn, h):
    caps = get_contacts_capabilities_sync(conn, [h])
    assert_rccs_callable(caps[h], require_video=True)
Пример #24
0
def test_tube_caps_to_contact(q, bus, conn, stream):
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")

    basic_caps = dbus.Dictionary({self_handle:
        [(text_fixed_properties, text_allowed_properties),
         (stream_tube_fixed_properties, stream_tube_allowed_properties),
         (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
         ]})
    daap_caps = dbus.Dictionary({self_handle:
        [(text_fixed_properties, text_allowed_properties),
        (stream_tube_fixed_properties, stream_tube_allowed_properties),
        (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
        (incoming_daap_fixed_properties, specialized_tube_allowed_properties),
        (ft_fixed_properties, ft_allowed_properties)]})
    xiangqi_caps = dbus.Dictionary({self_handle:
        [(text_fixed_properties, text_allowed_properties),
        (stream_tube_fixed_properties, stream_tube_allowed_properties),
        (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
        (xiangqi_fixed_properties, specialized_tube_allowed_properties),
        (ft_fixed_properties, ft_allowed_properties)]})
    daap_xiangqi_caps = dbus.Dictionary({self_handle:
        [(text_fixed_properties, text_allowed_properties),
        (stream_tube_fixed_properties, stream_tube_allowed_properties),
        (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
        (incoming_daap_fixed_properties, specialized_tube_allowed_properties),
        (xiangqi_fixed_properties, specialized_tube_allowed_properties),
        (ft_fixed_properties, ft_allowed_properties)]})
    all_tubes_caps = dbus.Dictionary({self_handle:
        [(text_fixed_properties, text_allowed_properties),
        (stream_tube_fixed_properties, stream_tube_allowed_properties),
        (dbus_tube_fixed_properties, dbus_tube_allowed_properties),
        (incoming_daap_fixed_properties, specialized_tube_allowed_properties),
        (http_fixed_properties, specialized_tube_allowed_properties),
        (xiangqi_fixed_properties, specialized_tube_allowed_properties),
        (go_fixed_properties, specialized_tube_allowed_properties),
        (ft_fixed_properties, ft_allowed_properties)]})

    # Check our own caps
    caps = get_contacts_capabilities_sync(conn, [self_handle])
    assertEquals(basic_caps, caps)

    # 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 = get_contacts_capabilities_sync(conn, [self_handle])
    assertLength(1, caps)
    assertEquals(basic_caps, caps)

    # 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_stream(q, stream)

    # Advertise a Requested=True tube cap
    conn.ContactCapabilities.UpdateCapabilities(
            [(cs.CLIENT + '.Foo', [outgoing_daap_fixed_properties], [])])

    # Check our own caps
    caps = get_contacts_capabilities_sync(conn, [self_handle])
    assertLength(1, caps)
    assertEquals(basic_caps, caps)

    # 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_stream(q, stream)

    advertise_caps(q, conn, stream,
        [bidir_daap_fixed_properties],
        [ns.TUBES + '/stream#daap'],
        [ns.TUBES + '/stream#http',
         ns.TUBES + '/dbus#com.example.Go',
         ns.TUBES + '/dbus#com.example.Xiangqi',
        ],
        daap_caps)

    advertise_caps(q, conn, stream,
        [outgoing_daap_fixed_properties, xiangqi_fixed_properties],
        [ns.TUBES + '/dbus#com.example.Xiangqi'],
        [ns.TUBES + '/stream#daap',
         ns.TUBES + '/stream#http',
         ns.TUBES + '/dbus#com.example.Go',
        ],
        xiangqi_caps)

    advertise_caps(q, conn, stream,
        [incoming_daap_fixed_properties, outgoing_daap_fixed_properties,
         xiangqi_fixed_properties],
        [ns.TUBES + '/dbus#com.example.Xiangqi',
         ns.TUBES + '/stream#daap',
        ],
        [ns.TUBES + '/stream#http',
         ns.TUBES + '/dbus#com.example.Go',
        ],
        daap_xiangqi_caps)

    advertise_caps(q, conn, stream,
        [incoming_daap_fixed_properties, http_fixed_properties,
         go_fixed_properties, xiangqi_fixed_properties],
        [ns.TUBES + '/dbus#com.example.Xiangqi',
         ns.TUBES + '/stream#daap',
         ns.TUBES + '/stream#http',
         ns.TUBES + '/dbus#com.example.Go',
        ],
        [],
        all_tubes_caps)

    # test daap + xiangqi again for some reason
    advertise_caps(q, conn, stream,
        [incoming_daap_fixed_properties, xiangqi_fixed_properties],
        [ns.TUBES + '/dbus#com.example.Xiangqi',
         ns.TUBES + '/stream#daap',
        ],
        [ns.TUBES + '/stream#http',
         ns.TUBES + '/dbus#com.example.Go',
        ],
        daap_xiangqi_caps)
Пример #25
0
def test_two_clients(q, bus, conn, stream, contact1, contact2,
        contact_handle1, contact_handle2, client, broken_hash):

    presence = make_presence(contact1, status='hello')
    stream.send(presence)

    q.expect('dbus-signal', signal='PresencesChanged',
            args=[{contact_handle1:
                (2, u'available', 'hello')}])

    presence = make_presence(contact2, status='hello')
    stream.send(presence)

    q.expect('dbus-signal', signal='PresencesChanged',
            args=[{contact_handle2:
                (2, u'available', 'hello')}])

    # no special capabilities
    for h in (contact_handle1, contact_handle2):
        for rcc in get_contacts_capabilities_sync(conn, [h])[h]:
            assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    # send updated presence with Jingle caps info
    ver = compute_caps_hash(some_identities, jingle_av_features, {})
    caps = {
        'node': client,
        'ver': ver,
        'hash': 'sha-1',
        }
    presence = make_presence(contact1, status='hello', caps=caps)
    stream.send(presence)
    presence = make_presence(contact2, status='hello', caps=caps)
    stream.send(presence)

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

    # don't receive any D-Bus signal
    forbidden = [
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        ]
    q.forbid_events(forbidden)
    sync_dbus(bus, q, conn)
    q.unforbid_events(forbidden)

    result = make_caps_disco_reply(
        stream, event.stanza, some_identities, jingle_av_features)

    if broken_hash:
        # make the hash break!
        query = result.firstChildElement()
        query.addElement('feature')['var'] = 'http://example.com/another-feature'

    stream.send(result)

    if broken_hash:
        # Gabble looks up our capabilities again because the first contact
        # failed to provide a valid hash
        event = q.expect('stream-iq', to=contact2,
            query_ns='http://jabber.org/protocol/disco#info')
        query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
        assert query_node.attributes['node'] == \
            client + '#' + ver

        # don't receive any D-Bus signal
        q.forbid_events(forbidden)
        sync_dbus(bus, q, conn)
        q.unforbid_events(forbidden)

        # send good reply
        send_disco_reply(stream, event.stanza, some_identities, jingle_av_features)

    # we can now do audio calls
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged',
            predicate=lambda e: contact_handle2 in e.args[0]),
        )
    assert_rccs_callable(cc.args[0][contact_handle2])

    if not broken_hash:
        # if the first contact failed to provide a good hash, it does not
        # deserve its capabilities to be understood by Gabble!
        cc, = q.expect_many(
            EventPattern('dbus-signal', signal='ContactCapabilitiesChanged',
                predicate=lambda e: contact_handle1 in e.args[0]),
            )
        assert_rccs_callable(cc.args[0][contact_handle1])

    # don't receive any further signals
    q.forbid_events(forbidden)
    sync_dbus(bus, q, conn)
    q.unforbid_events(forbidden)
Пример #26
0
def check_caps(conn, h):
    caps = get_contacts_capabilities_sync(conn, [h])
    assert_rccs_callable(caps[h], require_video=True)
Пример #27
0
def test_hash(q, bus, conn, stream, contact, contact_handle, client):
    presence = make_presence(contact, status='hello')
    stream.send(presence)

    q.expect('dbus-signal', signal='PresencesChanged',
            args=[{contact_handle:
                (2, u'available', 'hello')}])

    # no special capabilities
    for rcc in get_contacts_capabilities_sync(conn, [contact_handle])[contact_handle]:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    # send updated presence with Jingle caps info
    presence = make_presence(contact, status='hello',
        caps={'node': client,
              'ver':  '0.1',
             })
    stream.send(presence)

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

    # send good reply
    send_disco_reply(stream, event.stanza, [], jingle_av_features)

    # we can now do audio calls
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        )

    assert_rccs_callable(cc.args[0][contact_handle])
    assertEquals(cc.args[0],
            get_contacts_capabilities_sync(conn, [contact_handle]))

    # Send presence without any capabilities. XEP-0115 §8.4 Caps Optimization
    # says “receivers of presence notifications MUST NOT expect an annotation
    # on every presence notification they receive”, so the contact should still
    # be media-capable afterwards.
    stream.send(make_presence(contact, status='very capable'))
    q.expect('dbus-signal', signal='PresencesChanged',
        args=[{contact_handle: (2, u'available', 'very capable')}])
    # still exactly the same capabilities
    assertEquals(cc.args[0],
            get_contacts_capabilities_sync(conn, [contact_handle]))

    # send bogus presence
    caps = {
        'node': client,
        'ver':  'ceci=nest=pas=un=hash',
        'hash': 'sha-1',
        }
    presence = make_presence(contact, status='hello', caps=caps)
    stream.send(presence)

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

    # send bogus reply
    send_disco_reply(stream, event.stanza, [],
        ['http://jabber.org/protocol/bogus-feature'])

    # don't receive any D-Bus signal
    forbidden = [
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        ]
    q.forbid_events(forbidden)
    sync_dbus(bus, q, conn)
    sync_stream(q, stream)

    # send presence with empty caps
    presence = make_presence(contact, status='hello',
        caps={'node': client,
              'ver':  '0.0',
             })
    stream.send(presence)

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

    # still don't receive any D-Bus signal
    sync_dbus(bus, q, conn)

    # send good reply
    q.unforbid_events(forbidden)
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    stream.send(result)

    # we can now do nothing
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        )
    for rcc in cc.args[0][contact_handle]:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))
    assert_rccs_not_callable(cc.args[0][contact_handle])
    assertEquals(cc.args[0],
            get_contacts_capabilities_sync(conn, [contact_handle]))

    # send correct presence
    ver = compute_caps_hash(some_identities, jingle_av_features, fake_client_dataforms)
    caps = {
        'node': client,
        'ver':  ver,
        'hash': 'sha-1',
        }
    presence = make_presence(contact, status='hello', caps=caps)
    stream.send(presence)

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

    # don't receive any D-Bus signal
    q.forbid_events(forbidden)
    sync_dbus(bus, q, conn)
    q.unforbid_events(forbidden)

    # send good reply
    send_disco_reply(
        stream, event.stanza, some_identities, jingle_av_features, fake_client_dataforms)

    # we can now do audio calls
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        )
    assert_rccs_callable(cc.args[0][contact_handle])
    assertEquals(cc.args[0],
            get_contacts_capabilities_sync(conn, [contact_handle]))
def test_ft_caps_from_contact(q, bus, conn, stream, contact, contact_handle,
                              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 no FT cap
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ver'] = compute_caps_hash([], [], {})
    c['hash'] = 'sha-1'
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + c['ver']

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

    # no change in ContactCapabilities, so no signal ContactCapabilitiesChanged
    sync_stream(q, stream)

    # no special capabilities
    basic_caps = dbus.Dictionary(
        {contact_handle: [(text_fixed_properties, text_allowed_properties)]})
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == basic_caps, caps
    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == basic_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.ATTR_CONTACT_CAPABILITIES]
    assert caps_via_contacts_iface == caps[contact_handle], \
                                    caps_via_contacts_iface

    # send presence with ft capa
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ver'] = compute_caps_hash([], [ns.FILE_TRANSFER], {})
    c['hash'] = 'sha-1'
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + c['ver']

    # send good reply
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    query['node'] = client + '#' + c['ver']
    feature = query.addElement('feature')
    feature['var'] = ns.FILE_TRANSFER
    stream.send(result)

    generic_tubes_caps = dbus.Dictionary({
        contact_handle: [(text_fixed_properties, text_allowed_properties),
                         (ft_fixed_properties, ft_allowed_properties)]
    })

    event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
    assert len(event.args) == 1
    assert event.args[0] == generic_tubes_caps

    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == generic_tubes_caps, caps
    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == generic_tubes_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.ATTR_CONTACT_CAPABILITIES]
    assert caps_via_contacts_iface == caps[contact_handle], \
                                    caps_via_contacts_iface
Пример #29
0
def test(q, bus, conn, stream):
    bob = conn.get_contact_handle_sync('*****@*****.**')

    presence = make_presence('[email protected]/Foo', status='hello')
    stream.send(presence)

    q.expect('dbus-signal',
             signal='PresencesChanged',
             args=[{
                 bob: (cs.PRESENCE_AVAILABLE, u'available', 'hello')
             }])

    basic_caps = [(bob, cs.CHANNEL_TYPE_TEXT, 3, 0)]

    # only Text
    for rcc in get_contacts_capabilities_sync(conn, [bob])[bob]:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    # holding the handle here: see below
    assertEquals(
        {
            bob: {
                cs.ATTR_CONTACT_CAPABILITIES:
                get_contacts_capabilities_sync(conn, [bob])[bob],
                cs.CONN + '/contact-id':
                '*****@*****.**',
            },
        },
        conn.Contacts.GetContactAttributes([bob], [cs.CONN_IFACE_CONTACT_CAPS],
                                           True))

    # send updated presence with Jingle audio/video caps info. we turn on both
    # audio and video at the same time to test that all of the capabilities are
    # discovered before any capabilities change signal is emitted
    presence = make_presence(
        '[email protected]/Foo',
        status='hello',
        caps={
            'node': 'http://telepathy.freedesktop.org/fake-client',
            'ver': '0.1',
            'ext': 'video',
        })
    stream.send(presence)

    # Gabble looks up both the version and the video bundles, in any order
    (version_event, video_event) = q.expect_many(
        EventPattern(
            'stream-iq',
            to='[email protected]/Foo',
            query_ns='http://jabber.org/protocol/disco#info',
            query_node='http://telepathy.freedesktop.org/fake-client#0.1'),
        EventPattern(
            'stream-iq',
            to='[email protected]/Foo',
            query_ns='http://jabber.org/protocol/disco#info',
            query_node='http://telepathy.freedesktop.org/fake-client#video'))

    # reply to the video bundle query first - this capability alone is not
    # sufficient to make us callable
    result = make_result_iq(stream, video_event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle/description/video'
    stream.send(result)

    # reply to the version bundle query, which should make us audio and
    # video callable
    result = make_result_iq(stream, version_event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle'
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle/description/audio'
    feature = query.addElement('feature')
    feature['var'] = 'http://www.google.com/transport/p2p'
    stream.send(result)

    # we can now do audio and video calls
    cc, = q.expect_many(
        EventPattern(
            'dbus-signal',
            signal='ContactCapabilitiesChanged',
            predicate=lambda e: check_rccs_callable(e.args[0][bob])), )
    assert_rccs_callable(cc.args[0][bob],
                         require_video=True,
                         mutable_contents=True)

    assertEquals(
        {
            bob: {
                cs.ATTR_CONTACT_CAPABILITIES: cc.args[0][bob],
                cs.CONN + '/contact-id': '*****@*****.**',
            },
        },
        conn.Contacts.GetContactAttributes([bob], [cs.CONN_IFACE_CONTACT_CAPS],
                                           True))

    # send updated presence without video support
    presence = make_presence(
        '[email protected]/Foo',
        status='hello',
        caps={
            'node': 'http://telepathy.freedesktop.org/fake-client',
            'ver': '0.1',
        })
    stream.send(presence)

    # we can now do only audio calls (and as a result have the ImmutableStreams
    # cap)
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), )
    assert_rccs_callable(cc.args[0][bob])
    assert_rccs_not_callable(cc.args[0][bob],
                             require_audio=False,
                             require_video=True,
                             mutable_contents=False)

    assertEquals(
        {
            bob: {
                cs.ATTR_CONTACT_CAPABILITIES: cc.args[0][bob],
                cs.CONN + '/contact-id': '*****@*****.**',
            },
        },
        conn.Contacts.GetContactAttributes([bob], [cs.CONN_IFACE_CONTACT_CAPS],
                                           True))

    # go offline
    presence = make_presence('[email protected]/Foo', type='unavailable')
    stream.send(presence)

    # can't do audio calls any more
    q.expect_many(
        EventPattern('dbus-signal',
                     signal='PresencesChanged',
                     args=[{
                         bob: (cs.PRESENCE_OFFLINE, 'offline', '')
                     }]),
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
    )

    # Contact went offline. Previously, this test asserted that the handle
    # became invalid, but that's not guaranteed to happen immediately; so we
    # now hold the handle (above), to guarantee that it does *not* become
    # invalid.
    rccs = get_contacts_capabilities_sync(conn, [bob])[bob]
    for rcc in rccs:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    assertEquals(
        {
            bob: {
                cs.ATTR_CONTACT_CAPABILITIES: rccs,
                cs.CONN + '/contact-id': '*****@*****.**',
            },
        },
        conn.Contacts.GetContactAttributes([bob], [cs.CONN_IFACE_CONTACT_CAPS],
                                           True))

    # What about a handle that's not valid?
    assertEquals({},
                 conn.Contacts.GetContactAttributes(
                     [31337], [cs.CONN_IFACE_CONTACT_CAPS], False))