def _test(jp, q, bus, conn, stream,
          jingle_reason, group_change_reason, stream_error):
    jt = JingleTest2(jp, conn, q, stream, 'test@localhost', '[email protected]/Foo')
    jt.prepare()
    self_handle = conn.GetSelfHandle()
    remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/Foo"])[0]

    # Ring ring!
    jt.incoming_call()
    new_channel, new_session_handler = q.expect_many(
        EventPattern('dbus-signal', signal='NewChannel',
            predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args),
        EventPattern('dbus-signal', signal='NewSessionHandler'))
    assertEquals(cs.CHANNEL_TYPE_STREAMED_MEDIA, new_channel.args[1])
    assertEquals(cs.HT_CONTACT, new_channel.args[2])
    assertEquals(remote_handle, new_channel.args[3])
    assertEquals('rtp', new_session_handler.args[1])

    channel_path = new_channel.args[0]

    # Client calls Ready on new session handler.
    session_handler = make_channel_proxy(
        conn, new_session_handler.args[0], 'Media.SessionHandler')
    session_handler.Ready()

    # Client gets notified about a newly created stream...
    new_stream_handler = q.expect('dbus-signal', signal='NewStreamHandler')
    stream_id = new_stream_handler.args[1]
    stream_handler = make_channel_proxy(
        conn, new_stream_handler.args[0], 'Media.StreamHandler')
    stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus())
    stream_handler.Ready(jt.dbusify_codecs([("FOO", 5, 8000, {})]))

    q.expect('dbus-signal', signal='SetRemoteCodecs')

    msg = u"o noes"

    # ...but something goes wrong.
    stream_handler.Error(stream_error, msg)

    q.expect("stream-iq", iq_type="set",
             predicate=lambda x: _session_terminate_predicate(x, jingle_reason,
                                                              msg, jp))
    # Bye bye members.
    mc = q.expect('dbus-signal', signal='MembersChanged',
                  interface=cs.CHANNEL_IFACE_GROUP, path=channel_path,
                  args=[msg, [], [self_handle, remote_handle], [],
                        [], self_handle, group_change_reason])

    q.expect('dbus-signal', signal='StreamError',
             interface=cs.CHANNEL_TYPE_STREAMED_MEDIA,
             args=[stream_id, stream_error, msg])

    # Bye bye stream
    q.expect('dbus-signal', signal='Close')
    q.expect('dbus-signal', signal='StreamRemoved')

    # Bye bye channel.
    q.expect('dbus-signal', signal='Closed')
    q.expect('dbus-signal', signal='ChannelClosed')
    def make_call(expected_recipient):
        jp = JingleProtocol031()
        jt = JingleTest2(jp, conn, q, stream, 'test@localhost', 'dummy')

        conn.Requests.CreateChannel({
            cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA,
            cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
            cs.TARGET_ID: cat,
            cs.INITIAL_AUDIO: True,
        })

        e = q.expect('dbus-signal', signal='NewSessionHandler')
        session = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler')
        session.Ready()

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

        stream_handler = make_channel_proxy(conn, e.args[0],
            'Media.StreamHandler')
        stream_handler.NewNativeCandidate("fake",
            jt.get_remote_transports_dbus())
        stream_handler.Ready(jt.get_audio_codecs_dbus())
        stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)

        e = q.expect('stream-iq',
            predicate=jp.action_predicate('session-initiate'))
        assertEquals(expected_recipient, e.to)
 def create_ft_channel(self):
     self.channel = make_channel_proxy(self.conn, self.ft_path, 'Channel')
     self.ft_channel = make_channel_proxy(self.conn, self.ft_path,
                                          'Channel.Type.FileTransfer')
     self.ft_props = dbus.Interface(
         self.bus.get_object(self.conn.object.bus_name, self.ft_path),
         PROPERTIES_IFACE)
def test(jp, q, bus, conn, stream):
    if not jp.can_do_video_only():
        return

    remote_jid = '[email protected]/Foo'
    jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid)
    jt.prepare()

    self_handle = conn.GetSelfHandle()
    handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0]

    chan_path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA,
            cs.HT_CONTACT, handle, True)
    chan = wrap_channel(bus.get_object(conn.bus_name, chan_path),
            'StreamedMedia', ['MediaSignalling', 'Group', 'CallState', 'DTMF'])
    chan_props = chan.Properties.GetAll(cs.CHANNEL)
    assert cs.CHANNEL_IFACE_DTMF in chan_props['Interfaces'], \
        chan_props['Interfaces']

    chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO])

    # S-E gets notified about new session handler, and calls Ready on it
    e = q.expect('dbus-signal', signal='NewSessionHandler')
    assert e.args[1] == 'rtp'

    session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler')
    session_handler.Ready()

    e = q.expect('dbus-signal', signal='NewStreamHandler')
    video_path = e.args[0]
    stream_handler = make_channel_proxy(conn, video_path, 'Media.StreamHandler')

    stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus())
    stream_handler.Ready(jt.get_video_codecs_dbus())
    stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)

    e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate'))
    stream.send(make_result_iq(stream, e.stanza))

    jt.parse_session_initiate(e.query)

    jt.accept()

    # Gabble tells s-e to start sending
    q.expect('dbus-signal', signal='SetStreamSending', args=[True],
            path=video_path)

    # We don't actually have an audio stream, so this is a non-starter.
    call_async(q, chan.DTMF, 'StartTone', 666, 3)
    q.expect('dbus-error', method='StartTone', name=cs.NOT_AVAILABLE)
    call_async(q, chan.DTMF, 'MultipleTones', '**666##')
    q.expect('dbus-error', method='MultipleTones', name=cs.NOT_AVAILABLE)

    # We can still stop all the tones that are playing (a no-op).
    call_async(q, chan.DTMF, 'StopTone', 666)
    q.expect('dbus-return', method='StopTone')

    chan.Group.RemoveMembers([self_handle], 'closed')
    e = q.expect('dbus-signal', signal='Closed', path=chan_path)
def _test(jp, q, bus, conn, stream,
          jingle_reason, group_change_reason, stream_error):
    remote_jid = '[email protected]/Foo'
    jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid)

    jt.prepare()

    self_handle = conn.GetSelfHandle()
    remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0]

    path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA,
        cs.HT_CONTACT, remote_handle, True)

    signalling_iface = make_channel_proxy(conn, path, 'Channel.Interface.MediaSignalling')
    media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia')

    media_iface.RequestStreams(remote_handle, [cs.MEDIA_STREAM_TYPE_AUDIO])

    # S-E gets notified about new session handler, and calls Ready on it
    e = q.expect('dbus-signal', signal='NewSessionHandler')
    assert e.args[1] == 'rtp'

    session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler')
    session_handler.Ready()


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

    stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler')

    stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus())
    stream_handler.Ready(jt.get_audio_codecs_dbus())
    stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)

    e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate'))
    stream.send(make_result_iq(stream, e.stanza))

    text = u"begone!"

    jt.parse_session_initiate(e.query)
    jt.terminate(reason=jingle_reason, text=text)

    mc = q.expect('dbus-signal', signal='MembersChanged')
    message, added, removed, lp, rp, actor, reason = mc.args
    assert added == [], added
    assert set(removed) == set([self_handle, remote_handle]), \
        (removed, self_handle, remote_handle)
    assert lp == [], lp
    assert rp == [], rp
    assert actor == remote_handle, (actor, remote_handle)
    if jp.is_modern_jingle():
        assertEquals(text, message)
        assertEquals(group_change_reason, reason)

    if jp.is_modern_jingle() and stream_error:
        se = q.expect('dbus-signal', signal='StreamError')
        assertEquals(stream_error, se.args[1])

    q.expect('dbus-signal', signal='Close') #XXX - match against the path
    def accept_file(self):
        # decline FT
        self. channel.Close()

        e = self.q.expect('dbus-signal', signal='FileTransferStateChanged')
        state, reason = e.args
        assert state == cs.FT_STATE_CANCELLED
        assert reason == cs.FT_STATE_CHANGE_REASON_LOCAL_STOPPED
        self.q.expect('dbus-signal', signal='Closed')

        # Re send offer (this is a regression test as Salut used to crash at this
        # point)
        self.send_ft_offer_iq()

        e = self.q.expect('dbus-signal', signal='NewChannels')
        channels = e.args[0]
        assert len(channels) == 1
        path, props = channels[0]

        channel = make_channel_proxy(self.conn, path, 'Channel')

        # decline FT
        channel.Close()

        e = self.q.expect('dbus-signal', signal='FileTransferStateChanged')
        state, reason = e.args
        assert state == cs.FT_STATE_CANCELLED
        assert reason == cs.FT_STATE_CHANGE_REASON_LOCAL_STOPPED
        self.q.expect('dbus-signal', signal='Closed')

        # stop test
        return True
def _local_content_add(jp, q, bus, conn, stream, initiate_call_func):
    jt, chan = initiate_call_func(jp, q, bus, conn, stream)

    remote_handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0]

    chan.RequestStreams(remote_handle, [cs.MEDIA_STREAM_TYPE_VIDEO])

    nsh = q.expect('dbus-signal', signal='NewStreamHandler')
    stream_handler_path, stream_id, media_type, direction = nsh.args
    video_handler = make_channel_proxy(conn, stream_handler_path,
                                       'Media.StreamHandler')

    video_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus())
    video_handler.Ready(jt.get_audio_codecs_dbus())
    video_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)

    e = q.expect('stream-iq', predicate=jp.action_predicate('content-add'))
    c = e.query.firstChildElement()
    stream.send(make_result_iq(stream, e.stanza))

    node = jp.SetIq(jt.peer, jt.jid, [
            jp.Jingle(jt.sid, jt.peer, 'content-reject', [
                    ('reason', None, {}, [
                            ('failed-application', None, {}, [])]),
                    jp.Content(c['name'], c['creator'], c['senders']) ]) ])
    stream.send(jp.xml(node))

    q.expect('dbus-signal', signal='StreamError',
             args=[stream_id,
                   cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED,
                   ""]),
Exemple #8
0
def test(q, bus, conn, stream):
    jp = JingleProtocol031()
    remote_jid = '[email protected]/misc'
    jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid)

    jt.prepare()

    self_handle = conn.GetSelfHandle()
    remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0]
    path, _ = conn.Requests.CreateChannel({
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA,
        cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
        cs.TARGET_HANDLE: remote_handle})

    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia')

    # In Gabble, the StreamedMedia channel is secretly also the SessionHandler.
    # Let's make up a proxy and call some methods on it. They should fail
    # gracefully, rather than crashing Gabble.
    session_handler = make_channel_proxy(conn, path, 'Media.SessionHandler')

    try:
        session_handler.Ready()
    except DBusException, e:
        assertEquals(cs.NOT_AVAILABLE, e.get_dbus_name())
def test(q, bus, conn, stream):
    conn.Connect()
    q.expect_many(
            EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]),
            EventPattern('irc-connected'))
    q.expect('dbus-signal', signal='SelfHandleChanged',
        args=[1L])
    q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])

    CHANNEL_NAME = "#idletest"

    self_handle = conn.Get(CONN, 'SelfHandle', dbus_interface=PROPERTIES_IFACE)

    # The bouncer initiates a JOIN.
    path = test_join_bouncer(q, conn, stream, CHANNEL_NAME)

    # We PART.
    chan = make_channel_proxy(conn, path, 'Channel')
    chan.RemoveMembers([self_handle], "bye bye cruel world",
        dbus_interface=CHANNEL_IFACE_GROUP)
    q.expect('dbus-signal', signal='MembersChanged')

    # The bouncer initiates a JOIN to force the issue.
    test_join_bouncer(q, conn, stream, CHANNEL_NAME)

    call_async(q, conn, 'Disconnect')
    q.expect_many(
            EventPattern('dbus-return', method='Disconnect'),
            EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]))
    return True
def invite_to_muc(q, group1, conn2, invited_handle, inviter_handle):
    # first connection: invite contact
    group1.AddMembers([invited_handle], "Let's tube!")

    # channel is created on conn2
    e = q.expect('dbus-signal', signal='NewChannel', path=conn2.object_path)
    path = e.args[0]
    group2 = make_channel_proxy(conn2, path, "Channel.Interface.Group")

    # we are invited to the muc
    # added as local pending
    conn2_self_handle = conn2.Properties.Get(cs.CONN, "SelfHandle")
    q.expect('dbus-signal', signal='MembersChanged', path=path,
        args=["Let's tube!", [], [], [conn2_self_handle], [],
            inviter_handle, 4])

    # second connection: accept the invite
    group2.AddMembers([conn2_self_handle], "")

    # added as remote pending
    q.expect('dbus-signal', signal='MembersChanged', path=path,
        args=['', [], [], [], [conn2_self_handle], conn2_self_handle, 0])

    # added as member
    q.expect('dbus-signal', signal='MembersChanged', path=path,
        args=['', [conn2_self_handle], [], [], [], conn2_self_handle, 0])

    return group2
def test(q, bus, conn, stream):
    conn.Connect()
    q.expect_many(
        EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]),
        EventPattern('irc-connected'))
    q.expect('dbus-signal', signal='SelfHandleChanged', args=[1])
    q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])

    CHANNEL_NAME = "#idletest"

    self_handle = conn.Get(CONN, 'SelfHandle', dbus_interface=PROPERTIES_IFACE)

    # The bouncer initiates a JOIN.
    path = test_join_bouncer(q, conn, stream, CHANNEL_NAME)

    # We PART.
    chan = make_channel_proxy(conn, path, 'Channel')
    chan.RemoveMembers([self_handle],
                       "bye bye cruel world",
                       dbus_interface=CHANNEL_IFACE_GROUP)
    q.expect('dbus-signal', signal='MembersChanged')

    # The bouncer initiates a JOIN to force the issue.
    test_join_bouncer(q, conn, stream, CHANNEL_NAME)

    call_async(q, conn, 'Disconnect')
    q.expect_many(
        EventPattern('dbus-return', method='Disconnect'),
        EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]))
    return True
def test(q, bus, conn, stream, call_error_on):
    jt = jingletest.JingleTest(stream, 'test@localhost', '[email protected]/Foo')

    conn.Connect()

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

    remote_handle = conn.RequestHandles(1, ["[email protected]/Foo"])[0]

    # Remote end calls us
    jt.incoming_call()

    # FIXME: these signals are not observable by real clients, since they
    #        happen before NewChannels.
    # The caller is in members
    e = q.expect('dbus-signal', signal='MembersChanged',
             args=[u'', [remote_handle], [], [], [], 0, 0])

    # We're pending because of remote_handle
    e = q.expect('dbus-signal', signal='MembersChanged',
             args=[u'', [], [], [1L], [], remote_handle, cs.GC_REASON_INVITED])

    media_chan_suffix = e.path

    e = q.expect('dbus-signal', signal='NewSessionHandler')
    session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler')

    if call_error_on == 'session':
        session_handler.Error(0, "this has been deprecated for years")
    else:
        session_handler.Ready()

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

        # S-E gets notified about a newly-created stream
        stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler')

        # Something goes wrong immediately!
        stream_handler.Error(0, "i'll have the eggs tostada please")

    # Gabble doesn't fall over, and the channel closes nicely.
    e = q.expect('dbus-signal', signal='Closed', path=media_chan_suffix)
    def make_stream_request(stream_type):
        media_iface.RequestStreams(remote_handle, [stream_type])

        e = q.expect("dbus-signal", signal="NewStreamHandler")
        stream_id = e.args[1]

        stream_handler = make_channel_proxy(conn, e.args[0], "Media.StreamHandler")

        stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus())
        stream_handler.Ready(jt2.get_audio_codecs_dbus())
        stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)
        return (stream_handler, stream_id)
def run_test(q, bus, conn, stream, jt, request_before_presence):
    """
    Requests streams on a media channel to jt.remote_jid, either before their
    presence is received (if request_before_presence is True) or after their
    presence is received but before we've got a disco response for their
    capabilities (otherwise).
    """

    # We intentionally DON'T set remote presence yet. Since Gabble is still
    # unsure whether to treat contact as offline for this purpose, it
    # will tentatively allow channel creation and contact handle addition

    request = dbus.Dictionary(
        {
            cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA,
            cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
            cs.TARGET_ID: jt.remote_jid,
        },
        signature="sv",
    )
    path, props = conn.CreateChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS)
    media_iface = make_channel_proxy(conn, path, "Channel.Type.StreamedMedia")
    handle = props[cs.TARGET_HANDLE]

    sync_dbus(bus, q, conn)

    def call_request_streams():
        call_async(q, media_iface, "RequestStreams", handle, [cs.MEDIA_STREAM_TYPE_AUDIO])

    def send_presence():
        jt.send_remote_presence()
        return q.expect("stream-iq", query_ns=ns.DISCO_INFO, to=jt.remote_jid)

    if request_before_presence:
        # Request streams before either <presence> or caps have arrived. Gabble
        # should wait for both to arrive before returning from RequestStreams.
        call_request_streams()

        # Ensure Gabble's received the method call.
        sync_dbus(bus, q, conn)

        # Now send the presence.
        info_event = send_presence()
    else:
        info_event = send_presence()

        # Now call RequestStreams; it should wait for the disco reply.
        call_request_streams()

    jt.send_remote_disco_reply(info_event.stanza)

    # RequestStreams should now happily complete
    q.expect("dbus-return", method="RequestStreams")
def join_muc(q, conn, muc_name):
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    muc_handle = conn.RequestHandles(cs.HT_ROOM, [muc_name])[0]
    path = conn.RequestChannel(cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM, muc_handle, True)
    # added as remote pending
    q.expect('dbus-signal', signal='MembersChanged', path=path,
        args=['', [], [], [], [self_handle], self_handle, 0])
    # added as member
    q.expect('dbus-signal', signal='MembersChanged', path=path,
        args=['', [self_handle], [], [], [], self_handle, 0])
    group = make_channel_proxy(conn, path, "Channel.Interface.Group")

    return muc_handle, group
def do_one_search(q, bus, conn, stream, fields, expected_search_keys,
                  terms, results):

    call_create(q, conn, server)

    ret, nc_sig = answer_extended_field_query(q, stream, server, fields)

    path, props = ret.value
    props = unwrap(props)

    assert props[cs.CONTACT_SEARCH_SERVER] == server, pformat(props)
    assert sorted(props[cs.CONTACT_SEARCH_ASK]) == expected_search_keys, \
        sorted(props[cs.CONTACT_SEARCH_ASK])
    assert cs.CONTACT_SEARCH_STATE not in props, pformat(props)

    c = make_channel_proxy(conn, path, 'Channel')
    c_props = dbus.Interface(c, cs.PROPERTIES_IFACE)
    c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH)

    state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState')
    assert state == cs.SEARCH_NOT_STARTED, state

    # We make a search.
    iq = make_search(q, c_search, c_props, server, terms)
    query = iq.firstChildElement()
    fields_sent = xpath.queryForNodes(
        '/iq/query[@xmlns="%s"]/x[@xmlns="%s"][@type="submit"]/field'
        % (ns.SEARCH, ns.X_DATA), iq)
    assert fields_sent is not None

    # check FORM_TYPE
    f = fields_sent[0]
    assert f['type'] == 'hidden'
    assert f['var'] == 'FORM_TYPE'
    value = f.firstChildElement()
    assert value.name == 'value'
    assert value.children[0] == ns.SEARCH

    # extract search fields
    search_fields = []
    for f in fields_sent[1:]:
        value = f.firstChildElement()
        assert value.name == 'value'
        search_fields.append((f['var'], value.children[0]))

    # Server sends the results of the search.
    send_results_extended(stream, iq, results, fields)

    return search_fields, c, c_search, c_props
def test(q, bus, conn, stream, channel_type):
    jt = jingletest.JingleTest(stream, 'test@localhost', '[email protected]/Foo')

    # We intentionally DON'T set remote presence yet. Since Gabble is still
    # unsure whether to treat contact as offline for this purpose, it
    # will tentatively allow channel creation and contact handle addition

    handle = conn.RequestHandles(cs.HT_CONTACT, [jt.remote_jid])[0]

    if channel_type == cs.CHANNEL_TYPE_STREAMED_MEDIA:
        path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA,
            cs.HT_CONTACT, handle, True)
        media_iface = make_channel_proxy(conn, path,
            'Channel.Type.StreamedMedia')

    # So it turns out that the calls to RequestStreams and Disconnect could be
    # reordered while the first waits on the result of introspecting the
    # channel's object which is kicked off by making a proxy object for it,
    # whereas the connection proxy is long ago introspected. Isn't dbus-python
    # great? Syncing here forces that introspection to finish so we can rely on
    # the ordering of RequestStreams and Disconnect. Yay.
    sync_dbus(bus, q, conn)

    # Now we request streams before either <presence> or caps have arrived
    if channel_type == cs.CHANNEL_TYPE_STREAMED_MEDIA:
        call_async(q, media_iface, 'RequestStreams', handle,
            [cs.MEDIA_STREAM_TYPE_AUDIO])

        before_events, after_events = disconnect_conn(q, conn, stream,
            [EventPattern('dbus-error', method='RequestStreams')])

        # RequestStreams should now return NotAvailable
        assert before_events[0].error.get_dbus_name() == cs.NOT_AVAILABLE, \
            before_events[0].error
    else:
        call_async(q, conn.Requests, 'CreateChannel',
            { cs.CHANNEL_TYPE: channel_type,
              cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
              cs.TARGET_ID: jt.remote_jid,
              cs.CALL_INITIAL_AUDIO: True
            })

        before_events, after_events = disconnect_conn(q, conn, stream,
            [EventPattern('dbus-error', method='CreateChannel')])

        # CreateChannel should now return Disconnected
        assert before_events[0].error.get_dbus_name() == cs.DISCONNECTED, \
            before_events[0].error
def join_muc(q, conn, muc_name):
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    muc_handle = conn.RequestHandles(cs.HT_ROOM, [muc_name])[0]
    path = conn.RequestChannel(cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM, muc_handle,
                               True)
    # added as remote pending
    q.expect('dbus-signal',
             signal='MembersChanged',
             path=path,
             args=['', [], [], [], [self_handle], self_handle, 0])
    # added as member
    q.expect('dbus-signal',
             signal='MembersChanged',
             path=path,
             args=['', [self_handle], [], [], [], self_handle, 0])
    group = make_channel_proxy(conn, path, "Channel.Interface.Group")

    return muc_handle, group
def cancelled_while_in_progress(q, bus, conn, stream, server):
    call_create(q, conn, server)

    ret, _ = answer_field_query(q, stream, server)
    path, props = ret.value

    c = make_channel_proxy(conn, path, 'Channel')
    c_props = dbus.Interface(c, cs.PROPERTIES_IFACE)
    c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH)

    iq = make_search(q, c_search, c_props, server, { 'x-n-family': 'Threepwood' })

    # Before the server sends back the results, the client cancels the search.
    call_async(q, c_search, 'Stop')
    ret, ssc = q.expect_many(
        EventPattern('dbus-return', method='Stop'),
        EventPattern('dbus-signal', signal='SearchStateChanged'),
        )

    assert ssc.args[0] == cs.SEARCH_FAILED, ssc.args
    assert ssc.args[1] == cs.CANCELLED, ssc.args

    state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState')
    assert state == cs.SEARCH_FAILED, (state, cs.SEARCH_FAILED)

    # Now the server sends us the results; SearchResultReceived shouldn't fire
    search_result_received_event = EventPattern('dbus-signal', signal='SearchResultReceived')
    q.forbid_events([search_result_received_event])

    send_results(stream, iq, results.values())

    # Make sure Gabble's received the results.
    sync_stream(q, stream)

    # Hooray! We survived. Now let's call Stop again; it should succeed but do
    # nothing.
    search_state_changed_event = EventPattern('dbus-signal', signal='SearchStateChanged')
    q.forbid_events([search_state_changed_event])

    call_async(q, c_search, 'Stop')
    ssc = q.expect('dbus-return', method='Stop')

    c.Close()
    q.unforbid_events([search_result_received_event, search_state_changed_event])
def test(q, bus, conn, stream):
    jt = jingletest.JingleTest(stream, 'test@localhost', '[email protected]/Foo')

    conn.Connect()

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

    q.expect('stream-authenticated')
    q.expect('dbus-signal', signal='PresenceUpdate',
        args=[{1L: (0L, {u'available': {}})}])
    q.expect('dbus-signal', signal='StatusChanged',
            args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])

    # We intentionally DON'T set remote presence yet. Since Gabble is still
    # unsure whether to treat contact as offline for this purpose, it
    # will tentatively allow channel creation and contact handle addition

    handle = conn.RequestHandles(cs.HT_CONTACT, [jt.remote_jid])[0]

    path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT,
        handle, True)
    media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia')

    # So it turns out that the calls to RequestStreams and Disconnect could be
    # reordered while the first waits on the result of introspecting the
    # channel's object which is kicked off by making a proxy object for it,
    # whereas the connection proxy is long ago introspected. Isn't dbus-python
    # great? Syncing here forces that introspection to finish so we can rely on
    # the ordering of RequestStreams and Disconnect. Yay.
    sync_dbus(bus, q, conn)

    # Now we request streams before either <presence> or caps have arrived
    call_async(q, media_iface, 'RequestStreams', handle,
        [cs.MEDIA_STREAM_TYPE_AUDIO])

    event = disconnect_conn(q, conn, stream,
        [EventPattern('dbus-error', method='RequestStreams')])[0]

    # RequestStreams should now return NotAvailable
    assert event.error.get_dbus_name() == cs.NOT_AVAILABLE, event.error
def no_x_in_reply(q, bus, conn, stream):
    fields = [('nickname', 'text-single', 'NickName', []),
        ('nick', 'text-single', 'Nick', []),]
    terms = { 'nickname': 'Badger' }

    call_create(q, conn, server)
    ret, nc_sig = answer_extended_field_query(q, stream, server, fields)

    path, _ = ret.value
    c = make_channel_proxy(conn, path, 'Channel')
    c_props = dbus.Interface(c, cs.PROPERTIES_IFACE)
    c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH)

    iq = make_search(q, c_search, c_props, server, terms)

    # The server sends back an IQ with a <query/> but no <x/> inside the query.
    acknowledge_iq(stream, iq)

    # Gabble should tell us the query failed, and not crash.
    event = q.expect('dbus-signal', signal='SearchStateChanged')
    state = event.args[0]
    assertEquals(cs.SEARCH_FAILED, state)
Exemple #22
0
def returns_error_from_search(q, stream, conn):
    server = 'nofunforyou.localhost'
    iq = call_create(q, conn, server)

    result = make_result_iq(stream, iq)
    query = result.firstChildElement()
    query.addElement("first")
    stream.send(result)

    event = q.expect('dbus-return', method='CreateChannel')
    c = make_channel_proxy(conn, event.value[0], 'Channel')
    c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH)

    call_async(q, c_search, 'Search', {'x-n-given': 'World of Goo'})
    iq_event, _ = q.expect_many(
        EventPattern('stream-iq', to=server, query_ns=ns.SEARCH),
        EventPattern('dbus-signal', signal='SearchStateChanged'),
    )

    iq = iq_event.stanza
    error = domish.Element((None, 'error'))
    error['type'] = 'modify'
    error.addElement((ns.STANZA, 'not-acceptable'))
    error.addElement((ns.STANZA, 'text'),
                     content="We don't believe in games here.")
    send_error_reply(stream, iq, error)

    ssc = q.expect('dbus-signal', signal='SearchStateChanged')
    new_state, reason, details = ssc.args

    assert new_state == cs.SEARCH_FAILED, new_state
    assert reason == cs.PERMISSION_DENIED, reason

    # We call stop after the search has failed; it should succeed and do nothing.
    call_async(q, c_search, 'Stop')
    event = q.expect('dbus-return', method='Stop')

    c.Close()
def _remote_content_add(jp, q, bus, conn, stream, initiate_call_func):
    jt, chan = initiate_call_func(jp, q, bus, conn, stream)

    video_codecs = [
        jp.PayloadType(name, str(rate), str(id), parameters) \
            for (name, id, rate, parameters) in jt.video_codecs]

    node = jp.SetIq(jt.peer, jt.jid, [
            jp.Jingle(jt.sid, jt.peer, 'content-add', [
                    jp.Content(
                        'videostream', 'initiator', 'both',
                        jp.Description('video', video_codecs),
                        jp.TransportGoogleP2P()) ]) ])
    stream.send(jp.xml(node))

    _, nsh = q.expect_many(
        EventPattern('dbus-signal', signal='StreamAdded'),
        EventPattern('dbus-signal', signal='NewStreamHandler'))

    stream_handler_path, stream_id, media_type, direction = nsh.args

    video_handler = make_channel_proxy(conn, stream_handler_path,
                                       'Media.StreamHandler')

    video_handler.NewNativeCandidate("fake",
                                     jt.get_remote_transports_dbus())
    video_handler.Ready(jt.dbusify_codecs([("FOO", 5, 8000, {})]))

    msg = u"None of the codecs are good for us, damn!"

    video_handler.Error(cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, msg)

    q.expect_many(
        EventPattern('dbus-signal', signal='StreamError',
                     args=[stream_id,
                           cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED,
                           msg]),
        EventPattern('stream-iq', predicate=_content_reject_predicate))
Exemple #24
0
def returns_bees_from_search(q, stream, conn):
    server = 'hivemind.localhost'
    iq = call_create(q, conn, server)

    result = make_result_iq(stream, iq)
    query = result.firstChildElement()
    query.addElement("nick")
    stream.send(result)

    event = q.expect('dbus-return', method='CreateChannel')
    c = make_channel_proxy(conn, event.value[0], 'Channel')
    c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH)

    call_async(q, c_search, 'Search', {'nickname': 'Buzzy'})
    iq_event, _ = q.expect_many(
        EventPattern('stream-iq', to=server, query_ns=ns.SEARCH),
        EventPattern('dbus-signal', signal='SearchStateChanged'),
    )
    iq = iq_event.stanza

    result = IQ(stream, 'result')
    result['id'] = iq['id']
    result['from'] = iq['to']
    result.addElement((ns.SEARCH, 'bees')).addElement('bzzzzzzz')
    stream.send(result)

    ssc = q.expect('dbus-signal', signal='SearchStateChanged')
    new_state, reason, details = ssc.args

    assert new_state == cs.SEARCH_FAILED, new_state
    assert reason == cs.NOT_AVAILABLE, reason

    # We call stop after the search has failed; it should succeed and do nothing.
    call_async(q, c_search, 'Stop')
    event = q.expect('dbus-return', method='Stop')

    c.Close()
def returns_error_from_search(q, stream, conn):
    server = 'nofunforyou.localhost'
    iq = call_create(q, conn, server)

    result = make_result_iq(stream, iq)
    query = result.firstChildElement()
    query.addElement("first")
    stream.send(result)

    event = q.expect('dbus-return', method='CreateChannel')
    c = make_channel_proxy(conn, event.value[0], 'Channel')
    c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH)

    call_async(q, c_search, 'Search', {'x-n-given': 'World of Goo'})
    iq_event, _ = q.expect_many(
        EventPattern('stream-iq', to=server, query_ns=ns.SEARCH),
        EventPattern('dbus-signal', signal='SearchStateChanged'),
        )

    iq = iq_event.stanza
    error = domish.Element((None, 'error'))
    error['type'] = 'modify'
    error.addElement((ns.STANZA, 'not-acceptable'))
    error.addElement((ns.STANZA, 'text'), content="We don't believe in games here.")
    send_error_reply(stream, iq, error)

    ssc = q.expect('dbus-signal', signal='SearchStateChanged')
    new_state, reason, details = ssc.args

    assert new_state == cs.SEARCH_FAILED, new_state
    assert reason == cs.PERMISSION_DENIED, reason

    # We call stop after the search has failed; it should succeed and do nothing.
    call_async(q, c_search, 'Stop')
    event = q.expect('dbus-return', method='Stop')

    c.Close()
def returns_bees_from_search(q, stream, conn):
    server = 'hivemind.localhost'
    iq = call_create(q, conn, server)

    result = make_result_iq(stream, iq)
    query = result.firstChildElement()
    query.addElement("nick")
    stream.send(result)

    event = q.expect('dbus-return', method='CreateChannel')
    c = make_channel_proxy(conn, event.value[0], 'Channel')
    c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH)

    call_async(q, c_search, 'Search', {'nickname': 'Buzzy'})
    iq_event, _ = q.expect_many(
        EventPattern('stream-iq', to=server, query_ns=ns.SEARCH),
        EventPattern('dbus-signal', signal='SearchStateChanged'),
        )
    iq = iq_event.stanza

    result = IQ(stream, 'result')
    result['id'] = iq['id']
    result['from'] = iq['to']
    result.addElement((ns.SEARCH, 'bees')).addElement('bzzzzzzz')
    stream.send(result)

    ssc = q.expect('dbus-signal', signal='SearchStateChanged')
    new_state, reason, details = ssc.args

    assert new_state == cs.SEARCH_FAILED, new_state
    assert reason == cs.NOT_AVAILABLE, reason

    # We call stop after the search has failed; it should succeed and do nothing.
    call_async(q, c_search, 'Stop')
    event = q.expect('dbus-return', method='Stop')

    c.Close()
def invite_to_muc(q, group1, conn2, invited_handle, inviter_handle):
    # first connection: invite contact
    group1.AddMembers([invited_handle], "Let's tube!")

    # channel is created on conn2
    e = q.expect('dbus-signal', signal='NewChannel', path=conn2.object_path)
    path = e.args[0]
    group2 = make_channel_proxy(conn2, path, "Channel.Interface.Group")

    # we are invited to the muc
    # added as local pending
    conn2_self_handle = conn2.Properties.Get(cs.CONN, "SelfHandle")
    q.expect('dbus-signal',
             signal='MembersChanged',
             path=path,
             args=[
                 "Let's tube!", [], [], [conn2_self_handle], [],
                 inviter_handle, 4
             ])

    # second connection: accept the invite
    group2.AddMembers([conn2_self_handle], "")

    # added as remote pending
    q.expect('dbus-signal',
             signal='MembersChanged',
             path=path,
             args=['', [], [], [], [conn2_self_handle], conn2_self_handle, 0])

    # added as member
    q.expect('dbus-signal',
             signal='MembersChanged',
             path=path,
             args=['', [conn2_self_handle], [], [], [], conn2_self_handle, 0])

    return group2
Exemple #28
0
def run_test(q, bus, conn, stream, jt, decloak_allowed):
    """
    Requests streams on a media channel to jt.remote_jid without having their
    presence at all.
    """

    request = dbus.Dictionary({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA,
                                cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
                                cs.TARGET_ID: jt.remote_jid
                              }, signature='sv')
    path, props = conn.CreateChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS)
    media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia')
    handle = props[cs.TARGET_HANDLE]

    call_async(q, media_iface, 'RequestStreams', handle,
        [cs.MEDIA_STREAM_TYPE_AUDIO])

    e = q.expect('stream-presence',
            to=jt.remote_bare_jid, presence_type=None)
    nodes = xpath.queryForNodes('/presence/temppres[@xmlns="%s"]'
            % ns.TEMPPRES, e.stanza)
    assertLength(1, nodes)
    assertEquals('media', nodes[0].getAttribute('reason'))

    if decloak_allowed:
        jt.send_remote_presence()
        info_event = q.expect('stream-iq', query_ns=ns.DISCO_INFO,
                to=jt.remote_jid)

        jt.send_remote_disco_reply(info_event.stanza)

        # RequestStreams should now happily complete
        q.expect('dbus-return', method='RequestStreams')
    else:
        q.expect('dbus-error', method='RequestStreams',
                name=cs.OFFLINE)
Exemple #29
0
def test(q, bus, conn):
    conn.Connect()
    q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
    basic_txt = {"txtvers": "1", "status": "avail"}

    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    self_handle_name = conn.Properties.Get(cs.CONN, "SelfID")

    contact_name = "test-text-channel@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)

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

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

    # create a clique room
    basic_txt = {"txtvers": "0"}
    AvahiAnnouncer("myroom", "_clique._udp", 41377, basic_txt)

    # connect a stream
    AvahiListener(q).listen_for_service("_presence._tcp")
    e = q.expect('service-added',
                 name=self_handle_name,
                 protocol=avahi.PROTO_INET)
    service = e.service
    service.resolve()

    e = q.expect('service-resolved', service=service)

    xmpp_connection = connect_to_stream(q, contact_name, self_handle_name,
                                        str(e.pt), e.port)

    e = q.expect('connection-result')
    assert e.succeeded, e.reason

    e = q.expect('stream-opened', connection=xmpp_connection)

    # send an invitation
    message = domish.Element(('', 'message'))
    message['type'] = 'normal'
    message.addElement('body', content='You got a Clique chatroom invitation')
    invite = message.addElement((NS_CLIQUE, 'invite'))
    invite.addElement('roomname', content='myroom')
    invite.addElement('reason', content='Inviting to this room')
    invite.addElement('address', content='127.0.0.1')
    invite.addElement('port', content='41377')
    xmpp_connection.send(message)

    # group channel is created
    e = q.expect('dbus-signal',
                 signal='NewChannel',
                 predicate=lambda e: e.args[1] == cs.CHANNEL_TYPE_TEXT and e.
                 args[2] == cs.HT_ROOM)
    path = e.args[0]
    channel = make_channel_proxy(conn, path, 'Channel')
    props_iface = dbus.Interface(bus.get_object(conn.object.bus_name, path),
                                 dbus.PROPERTIES_IFACE)

    q.expect('dbus-signal', signal='MembersChanged', path=path)

    lp_members = props_iface.Get(
        'org.freedesktop.Telepathy.Channel.Interface.Group',
        'LocalPendingMembers')

    assert len(lp_members) == 1
    added, actor, reason, msg = lp_members[0]

    assert added == self_handle
    assert actor == handle
    assert reason == 4  #invited
    assert msg == 'Inviting to this room'

    # decline invitation
    channel.Close()
    q.expect('dbus-signal', signal='Closed')
def test(q, bus, conn):
    self_name = 'testsuite' + '@' + avahitest.get_host_name()

    conn.Connect()

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

    # FIXME: this is a hack to be sure to have all the contact list channels
    # announced so they won't interfere with the muc ones announces.
    wait_for_contact_list(q, conn)

    # check if we can request roomlist channels
    properties = conn.GetAll(
            tp_name_prefix + '.Connection.Interface.Requests',
            dbus_interface='org.freedesktop.DBus.Properties')
    assert ({tp_name_prefix + '.Channel.ChannelType':
                cs.CHANNEL_TYPE_TEXT,
             tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM,
             },
             [tp_name_prefix + '.Channel.TargetHandle',
              tp_name_prefix + '.Channel.TargetID'],
             ) in properties.get('RequestableChannelClasses'),\
                     properties['RequestableChannelClasses']

    requestotron = dbus.Interface(conn,
            tp_name_prefix + '.Connection.Interface.Requests')

    # create muc channel using new API
    call_async(q, requestotron, 'CreateChannel',
            { tp_name_prefix + '.Channel.ChannelType':
                cs.CHANNEL_TYPE_TEXT,
              tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM,
              tp_name_prefix + '.Channel.TargetID': 'my-second-room',
              })

    ret, new_sig = q.expect_many(
        EventPattern('dbus-return', method='CreateChannel'),
        EventPattern('dbus-signal', signal='NewChannels'),
        )
    path2 = ret.value[0]
    chan = make_channel_proxy(conn, path2, "Channel")

    props = ret.value[1]
    assert props[tp_name_prefix + '.Channel.ChannelType'] ==\
            cs.CHANNEL_TYPE_TEXT
    assert props[tp_name_prefix + '.Channel.TargetHandleType'] == cs.HT_ROOM
    assert props[tp_name_prefix + '.Channel.TargetID'] == 'my-second-room'
    assert props[tp_name_prefix + '.Channel.Requested'] == True
    assert props[tp_name_prefix + '.Channel.InitiatorHandle'] \
            == conn.Properties.Get(cs.CONN, "SelfHandle")
    assert props[tp_name_prefix + '.Channel.InitiatorID'] \
            == self_name

    assert new_sig.args[0][0][0] == path2
    assert new_sig.args[0][0][1] == props

    # ensure roomlist channel
    handle = props[tp_name_prefix + '.Channel.TargetHandle']
    yours, ensured_path, ensured_props = ret.value = requestotron.EnsureChannel(
            { tp_name_prefix + '.Channel.ChannelType':
                cs.CHANNEL_TYPE_TEXT,
              tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM,
              tp_name_prefix + '.Channel.TargetHandle': handle,
              })

    assert not yours
    assert ensured_path == path2, (ensured_path, path2)

    conn.Disconnect()

    q.expect_many(
            EventPattern('dbus-signal', signal='Closed',
                path=path2),
            EventPattern('dbus-signal', signal='ChannelClosed', args=[path2]),
            EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]),
            )
def test(q, bus, conn, stream, send_early_description_info=False):
    jp = JingleProtocol031()
    jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', '[email protected]/Foo')
    jt2.prepare()

    self_handle = conn.GetSelfHandle()
    remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/Foo"])[0]

    # Remote end calls us
    jt2.incoming_call()

    # FIXME: these signals are not observable by real clients, since they
    #        happen before NewChannels.
    # The caller is in members
    e = q.expect('dbus-signal', signal='MembersChanged',
             args=[u'', [remote_handle], [], [], [], 0, 0])

    # We're pending because of remote_handle
    e = q.expect('dbus-signal', signal='MembersChanged',
             args=[u'', [], [], [self_handle], [], remote_handle,
                   cs.GC_REASON_INVITED])

    chan = wrap_channel(bus.get_object(conn.bus_name,  e.path),
        'StreamedMedia')

    # S-E gets notified about new session handler, and calls Ready on it
    e = q.expect('dbus-signal', signal='NewSessionHandler')
    assert e.args[1] == 'rtp'

    if send_early_description_info:
        """
        Regression test for a bug where Gabble would crash if you sent it
        description-info before calling Ready() on the relevant StreamHandler,
        and then for a bug where Gabble would never accept the call if a
        description-info was received before all StreamHandlers were Ready().
        """
        node = jp.SetIq(jt2.peer, jt2.jid, [
            jp.Jingle(jt2.sid, jt2.peer, 'description-info', [
                jp.Content('stream1', 'initiator', 'both',
                    jp.Description('audio', [ ])) ]) ])
        stream.send(jp.xml(node))

        sync_stream(q, stream)

    session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler')
    session_handler.Ready()

    chan.Group.AddMembers([self_handle], 'accepted')

    # S-E gets notified about a newly-created stream
    e = q.expect('dbus-signal', signal='NewStreamHandler')
    id1 = e.args[1]

    stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler')

    # We are now in members too
    e = q.expect('dbus-signal', signal='MembersChanged',
             args=[u'', [self_handle], [], [], [], self_handle,
                   cs.GC_REASON_NONE])

    # we are now both in members
    members = chan.Group.GetMembers()
    assert set(members) == set([self_handle, remote_handle]), members

    local_codecs = [('GSM', 3, 8000, {}),
                    ('PCMA', 8, 8000, {'helix':'woo yay'}),
                    ('PCMU', 0, 8000, {}) ]
    local_codecs_dbus = jt2.dbusify_codecs_with_params(local_codecs)

    stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus())
    stream_handler.Ready(local_codecs_dbus)
    stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)

    stream_handler.CodecsUpdated(local_codecs_dbus)

    local_codecs = [('GSM', 3, 8000, {}),
                    ('PCMA', 8, 8000, {'gstreamer':'rock on'}),
                    ('PCMU', 0, 8000, {}) ]
    local_codecs_dbus = jt2.dbusify_codecs_with_params(local_codecs)
    stream_handler.CodecsUpdated(local_codecs_dbus)


    # First IQ is transport-info; also, we expect to be told what codecs the
    # other end wants.
    e, src = q.expect_many(
        EventPattern('stream-iq',
            predicate=jp.action_predicate('transport-info')),
        EventPattern('dbus-signal', signal='SetRemoteCodecs')
        )
    assertEquals('[email protected]/Foo', e.query['initiator'])

    assert jt2.audio_codecs == [ (name, id, rate, parameters)
        for id, name, type, rate, channels, parameters in unwrap(src.args[0]) ], \
        (jt2.audio_codecs, unwrap(src.args[0]))

    stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, [])))

    # S-E reports codec intersection, after which gabble can send acceptance
    stream_handler.SupportedCodecs(local_codecs_dbus)

    # Second one is session-accept
    e = q.expect('stream-iq', predicate=jp.action_predicate('session-accept'))

    # farstream is buggy, and tells tp-fs to tell Gabble to change the third
    # codec's clockrate. This isn't legal, so Gabble says no.
    new_codecs = [ ('GSM', 3, 8000, {}),
                   ('PCMA', 8, 8000, {}),
                   ('PCMU', 0, 4000, {}) ]
    call_async(q, stream_handler, 'CodecsUpdated',
        jt2.dbusify_codecs(new_codecs))
    event = q.expect('dbus-error', method='CodecsUpdated')
    assert event.error.get_dbus_name() == cs.INVALID_ARGUMENT, \
        event.error.get_dbus_name()

    # With its tail between its legs, tp-fs decides it wants to add some
    # parameters to the first two codecs, not changing the third.
    new_codecs = [ ('GSM', 3, 8000, {'type': 'banana'}),
                   ('PCMA', 8, 8000, {'helix': 'BUFFERING'}),
                   ('PCMU', 0, 8000, {}) ]
    stream_handler.CodecsUpdated(jt2.dbusify_codecs_with_params(new_codecs))

    audio_content = jt2.audio_names[0]

    e = q.expect('stream-iq', iq_type='set', predicate=lambda x:
        xpath.queryForNodes("/iq/jingle[@action='description-info']",
            x.stanza))
    payload_types = xpath.queryForNodes(
        "/iq/jingle/content[@name='%s']/description/payload-type"
            % audio_content,
        e.stanza)
    # Gabble SHOULD only include the changed codecs in description-info
    assert len(payload_types) == 2, payload_types

    payload_types_tupled = [ (pt['name'], int(pt['id']), int(pt['clockrate']),
                              extract_params(pt))
                             for pt in payload_types ]
    assert sorted(payload_types_tupled) == sorted(new_codecs[0:2]), \
        (payload_types_tupled, new_codecs[0:2])

    # The remote end decides it wants to change the number of channels in the
    # third codec. This is not meant to happen, so Gabble should send it an IQ
    # error back.
    node = jp.SetIq(jt2.peer, jt2.jid, [
        jp.Jingle(jt2.sid, jt2.peer, 'description-info', [
            jp.Content(audio_content, 'initiator', 'both',
                jp.Description('audio', [
                    jp.PayloadType('PCMU', '1600', '0') ])) ]) ])
    stream.send(jp.xml(node))
    q.expect('stream-iq', iq_type='error',
        predicate=lambda x: x.stanza['id'] == node[2]['id'])

    # Instead, the remote end decides to add a parameter to the third codec.
    new_codecs = [ ('GSM', 3, 8000, {}),
                   ('PCMA', 8, 8000, {}),
                   ('PCMU', 0, 8000, {'choppy': 'false'}),
                 ]
    # As per the XEP, it only sends the ones which have changed.
    c = new_codecs[2]
    node = jp.SetIq(jt2.peer, jt2.jid, [
        jp.Jingle(jt2.sid, jt2.peer, 'description-info', [
            jp.Content(audio_content, 'initiator', 'both',
                jp.Description('audio', [
                    jp.PayloadType(c[0], str(c[2]), str(c[1]), c[3])
                ])) ]) ])
    stream.send(jp.xml(node))

    # Gabble should patch its idea of the remote codecs with the update it just
    # got, and emit SetRemoteCodecs for them all.
    e = q.expect('dbus-signal', signal='SetRemoteCodecs')
    new_codecs_dbus = unwrap(jt2.dbusify_codecs_with_params(new_codecs))
    announced = unwrap(e.args[0])
    assert new_codecs_dbus == announced, (new_codecs_dbus, announced)

    # We close the session by removing the stream
    chan.StreamedMedia.RemoveStreams([id1])

    e = q.expect('stream-iq', iq_type='set', predicate=lambda x:
        xpath.queryForNodes("/iq/jingle[@action='session-terminate']",
            x.stanza))
def test(q, bus, conn, stream):
    conn.Connect()
    q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])

    props = conn.GetAll(cs.CONN, dbus_interface=cs.PROPERTIES_IFACE)

    assertContains(cs.CONN_IFACE_REQUESTS, props['Interfaces'])

    nick = 'foo'
    foo_handle = conn.get_contact_handle_sync(nick)

    properties = conn.GetAll(cs.CONN_IFACE_REQUESTS,
                             dbus_interface=cs.PROPERTIES_IFACE)
    assert properties.get('Channels') == [], properties['Channels']
    assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
             cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
            },
            [cs.TARGET_HANDLE, cs.TARGET_ID],
           ) in properties.get('RequestableChannelClasses'),\
                     properties['RequestableChannelClasses']

    requestotron = dbus.Interface(conn, cs.CONN_IFACE_REQUESTS)
    request = {
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
        cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
        cs.TARGET_ID: nick,
    }
    call_async(q, requestotron, 'CreateChannel', request)

    ret, new_sig = q.expect_many(
        EventPattern('dbus-return', method='CreateChannel'),
        EventPattern('dbus-signal', signal='NewChannels'),
    )

    assert len(ret.value) == 2
    path, emitted_props = ret.value
    assert emitted_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT
    assert emitted_props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT
    assert emitted_props[cs.TARGET_HANDLE] == foo_handle
    assert emitted_props[cs.TARGET_ID] == nick
    assert emitted_props[cs.REQUESTED]
    assert emitted_props[cs.INITIATOR_HANDLE] == props['SelfHandle']
    assert emitted_props[cs.INITIATOR_ID] == stream.nick

    assert len(new_sig.args) == 1
    assert len(new_sig.args[0]) == 1  # one channel
    assert len(new_sig.args[0][0]) == 2  # two struct members
    assert new_sig.args[0][0][0] == ret.value[0]
    assert new_sig.args[0][0][1] == ret.value[1]

    properties = conn.GetAll(cs.CONN_IFACE_REQUESTS,
                             dbus_interface=cs.PROPERTIES_IFACE)

    assert new_sig.args[0][0] in properties['Channels'], \
            (new_sig.args[0][0], properties['Channels'])

    chan = make_channel_proxy(conn, path, 'Channel')

    stream.sendMessage('PRIVMSG', stream.nick, ":oh hai", prefix=nick)
    q.expect('dbus-signal', signal='Received')

    # Without acknowledging the message, we close the channel:
    chan.Close()

    # It should close and respawn!
    q.expect('dbus-signal', signal='ChannelClosed')
    chans, = q.expect('dbus-signal', signal='NewChannels').args
    assert len(chans) == 1
    new_props = chans[0][1]

    # It should look pretty similar...
    assert new_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT
    assert new_props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT
    assert new_props[cs.TARGET_HANDLE] == foo_handle
    assert new_props[cs.TARGET_ID] == nick

    # ...but this time they started it...
    assert not new_props[cs.REQUESTED]
    assert new_props[cs.INITIATOR_HANDLE] == foo_handle
    assert new_props[cs.INITIATOR_ID] == nick

    # ...and it's got some messages in it!
    ms = chan.ListPendingMessages(False, dbus_interface=cs.CHANNEL_TYPE_TEXT)
    assert len(ms) == 1
    assert ms[0][5] == 'oh hai'

    # Without acknowledging the message, we destroy the channel:
    assertContains(
        cs.CHANNEL_IFACE_DESTROYABLE,
        chan.Get(cs.CHANNEL, 'Interfaces', dbus_interface=cs.PROPERTIES_IFACE))
    chan.Destroy(dbus_interface=cs.CHANNEL_IFACE_DESTROYABLE)

    q.expect('dbus-signal', signal='ChannelClosed')

    # It should be gone for good this time.
    channels = conn.Get(cs.CONN_IFACE_REQUESTS,
                        'Channels',
                        dbus_interface=cs.PROPERTIES_IFACE)
    assert channels == [], channels

    call_async(q, conn, 'Disconnect')
    q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
Exemple #33
0
def test(jp, q, bus, conn, stream):
    # this test uses multiple streams
    if not jp.is_modern_jingle():
        return

    remote_jid = '[email protected]/Foo'
    jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid)
    jt.prepare()

    self_handle = conn.GetSelfHandle()
    handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0]

    chan_path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA,
            cs.HT_CONTACT, handle, True)
    chan = wrap_channel(bus.get_object(conn.bus_name, chan_path),
            'StreamedMedia', ['MediaSignalling', 'Group', 'CallState', 'DTMF'])
    chan_props = chan.Properties.GetAll(cs.CHANNEL)
    assert cs.CHANNEL_IFACE_DTMF in chan_props['Interfaces'], \
        chan_props['Interfaces']

    assertEquals('',
            chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'InitialTones'))
    assertEquals('',
            chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones'))

    chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO])

    # S-E gets notified about new session handler, and calls Ready on it
    e = q.expect('dbus-signal', signal='NewSessionHandler')
    assert e.args[1] == 'rtp'

    session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler')
    session_handler.Ready()

    e = q.expect('dbus-signal', signal='NewStreamHandler')
    audio_path = e.args[0]
    stream_handler = make_channel_proxy(conn, audio_path, 'Media.StreamHandler')

    stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus())
    stream_handler.Ready(jt.get_audio_codecs_dbus())
    stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)

    e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate'))
    stream.send(make_result_iq(stream, e.stanza))

    jt.parse_session_initiate(e.query)

    jt.accept()

    # Gabble tells s-e to start sending
    q.expect('dbus-signal', signal='SetStreamSending', args=[True],
            path=audio_path)

    chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO])

    e = q.expect('dbus-signal', signal='NewStreamHandler')
    audio2_path = e.args[0]

    # The Stream_ID is specified to be ignored; we use 666 here.
    call_async(q, chan.DTMF, 'StartTone', 666, 3)
    q.expect_many(
            EventPattern('dbus-signal', signal='StartTelephonyEvent',
                path=audio2_path),
            EventPattern('dbus-signal', signal='StartTelephonyEvent',
                path=audio_path),
            EventPattern('dbus-signal', signal='SendingTones', args=['3'],
                path=chan_path),
            EventPattern('dbus-return', method='StartTone'),
            )

    call_async(q, chan.DTMF, 'StopTone', 666)
    q.expect_many(
            EventPattern('dbus-signal', signal='StopTelephonyEvent',
                path=audio_path),
            EventPattern('dbus-signal', signal='StopTelephonyEvent',
                path=audio2_path),
            EventPattern('dbus-signal', signal='StoppedTones', args=[True],
                path=chan_path),
            EventPattern('dbus-return', method='StopTone'),
            )

    call_async(q, chan.DTMF, 'MultipleTones', '123w*#')
    q.expect_many(
            EventPattern('dbus-signal', signal='StartTelephonyEvent',
                path=audio_path, args=[1]),
            EventPattern('dbus-signal', signal='StartTelephonyEvent',
                path=audio2_path, args=[1]),
            EventPattern('dbus-signal', signal='SendingTones', args=['123w*#'],
                path=chan_path),
            EventPattern('dbus-return', method='MultipleTones'),
            )
    q.expect_many(
            EventPattern('dbus-signal', signal='StopTelephonyEvent',
                path=audio_path),
            EventPattern('dbus-signal', signal='StopTelephonyEvent',
                path=audio2_path),
            )
    q.expect_many(
            EventPattern('dbus-signal', signal='StartTelephonyEvent',
                path=audio_path, args=[2]),
            EventPattern('dbus-signal', signal='StartTelephonyEvent',
                path=audio2_path, args=[2]),
            )
    q.expect_many(
            EventPattern('dbus-signal', signal='StopTelephonyEvent',
                path=audio_path),
            EventPattern('dbus-signal', signal='StopTelephonyEvent',
                path=audio2_path),
            )
    q.expect_many(
            EventPattern('dbus-signal', signal='StartTelephonyEvent',
                path=audio_path, args=[3]),
            EventPattern('dbus-signal', signal='StartTelephonyEvent',
                path=audio2_path, args=[3]),
            )
    q.expect_many(
            EventPattern('dbus-signal', signal='StopTelephonyEvent',
                path=audio_path),
            EventPattern('dbus-signal', signal='StopTelephonyEvent',
                path=audio2_path),
            EventPattern('dbus-signal', signal='StoppedTones', args=[False],
                path=chan_path),
            EventPattern('dbus-signal', signal='TonesDeferred',
                args=['*#']),
            )

    assertEquals('*#',
            chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones'))

    forbidden = [EventPattern('dbus-signal', signal='StartTelephonyEvent',
        args=[9])]
    q.forbid_events(forbidden)

    # This is technically a race condition, but this dialstring is almost
    # certainly long enough that the Python script will win the race, i.e.
    # cancel before Gabble processes the whole dialstring.
    call_async(q, chan.DTMF, 'MultipleTones',
            '1,1' * 100)
    q.expect('dbus-return', method='MultipleTones')

    call_async(q, chan.DTMF, 'MultipleTones', '9')
    q.expect('dbus-error', method='MultipleTones',
            name=cs.SERVICE_BUSY)
    call_async(q, chan.DTMF, 'StartTone', 666, 9)
    q.expect('dbus-error', method='StartTone', name=cs.SERVICE_BUSY)
    call_async(q, chan.DTMF, 'StopTone', 666)
    q.expect_many(
            EventPattern('dbus-signal', signal='StopTelephonyEvent',
                path=audio_path),
            EventPattern('dbus-signal', signal='StopTelephonyEvent',
                path=audio2_path),
            EventPattern('dbus-signal', signal='StoppedTones', args=[True],
                path=chan_path),
            EventPattern('dbus-return', method='StopTone'),
            )

    # emitting any sound resets TonesDeferred
    assertEquals('',
            chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones'))

    q.unforbid_events(forbidden)

    chan.Group.RemoveMembers([self_handle], 'closed')
    e = q.expect('dbus-signal', signal='Closed', path=chan_path)
def test(q, bus, conn):
    self_name = 'testsuite' + '@' + get_host_name()

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

    basic_txt = { "txtvers": "1", "status": "avail" }
    contact_name = "test-request-im@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)

    # FIXME: this is a hack to be sure to have all the contact list channels
    # announced so they won't interfere with the muc ones announces.
    wait_for_contact_list(q, conn)

    AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)

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

    # check if we can request roomlist channels
    properties = conn.GetAll(
            tp_name_prefix + '.Connection.Interface.Requests',
            dbus_interface='org.freedesktop.DBus.Properties')
    assert ({tp_name_prefix + '.Channel.ChannelType':
                cs.CHANNEL_TYPE_TEXT,
             tp_name_prefix + '.Channel.TargetHandleType': cs.HT_CONTACT,
             },
             [tp_name_prefix + '.Channel.TargetHandle',
              tp_name_prefix + '.Channel.TargetID'],
             ) in properties.get('RequestableChannelClasses'),\
                     properties['RequestableChannelClasses']

    # create muc channel
    requestotron = dbus.Interface(conn,
            tp_name_prefix + '.Connection.Interface.Requests')
    call_async(q, requestotron, 'CreateChannel',
            { tp_name_prefix + '.Channel.ChannelType':
                cs.CHANNEL_TYPE_TEXT,
              tp_name_prefix + '.Channel.TargetHandleType': cs.HT_CONTACT,
              tp_name_prefix + '.Channel.TargetID': contact_name,
              })

    ret, new_sig = q.expect_many(
        EventPattern('dbus-return', method='CreateChannel'),
        EventPattern('dbus-signal', signal='NewChannels'),
        )
    path2 = ret.value[0]
    chan = make_channel_proxy(conn, path2, "Channel")

    props = ret.value[1]
    assert props[tp_name_prefix + '.Channel.ChannelType'] ==\
            cs.CHANNEL_TYPE_TEXT
    assert props[tp_name_prefix + '.Channel.TargetHandleType'] == cs.HT_CONTACT
    assert props[tp_name_prefix + '.Channel.TargetHandle'] == handle
    assert props[tp_name_prefix + '.Channel.TargetID'] == contact_name
    assert props[tp_name_prefix + '.Channel.Requested'] == True
    assert props[tp_name_prefix + '.Channel.InitiatorHandle'] \
            == conn.Properties.Get(cs.CONN, "SelfHandle")
    assert props[tp_name_prefix + '.Channel.InitiatorID'] \
            == self_name

    assert new_sig.args[0][0][0] == path2
    assert new_sig.args[0][0][1] == props

    # ensure roomlist channel
    yours, ensured_path, ensured_props = requestotron.EnsureChannel(
            { tp_name_prefix + '.Channel.ChannelType':
                cs.CHANNEL_TYPE_TEXT,
              tp_name_prefix + '.Channel.TargetHandleType': cs.HT_CONTACT,
              tp_name_prefix + '.Channel.TargetHandle': handle,
              })

    assert not yours
    assert ensured_path == path2, (ensured_path, path2)

    conn.Disconnect()

    q.expect_many(
            EventPattern('dbus-signal', signal='Closed',
                path=path2),
            EventPattern('dbus-signal', signal='ChannelClosed', args=[path2]),
            EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]),
            )
def test(jp, q, bus, conn, stream, peer_removes_final_content):
    jt = JingleTest2(jp, conn, q, stream, 'test@localhost', '[email protected]/Foo')
    jt.prepare()

    handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0]
    path = conn.RequestChannel(
        cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True)

    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia')

    chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO])

    # S-E gets notified about new session handler, and calls Ready on it
    e = q.expect('dbus-signal', signal='NewSessionHandler')
    assert e.args[1] == 'rtp'

    session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler')
    session_handler.Ready()

    e = q.expect('dbus-signal', signal='NewStreamHandler')
    stream_id = e.args[1]

    stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler')

    stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus())

    # Before sending the initiate, request another stream

    chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO])

    e = q.expect('dbus-signal', signal='NewStreamHandler')
    stream_id2 = e.args[1]

    stream_handler2 = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler')
    stream_handler2.NewNativeCandidate("fake", jt.get_remote_transports_dbus())

    # Before the CM can initiate session, we modify a stream direction. This
    # should result in a no-op since there's no need to inform the peer of
    # change.
    chan.StreamedMedia.RequestStreamDirection(stream_id2,
        cs.MEDIA_STREAM_DIRECTION_RECEIVE)

    # We set both streams as ready, which will trigger the session initiate
    stream_handler.Ready(jt.get_audio_codecs_dbus())
    stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)
    stream_handler2.Ready(jt.get_audio_codecs_dbus())
    stream_handler2.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)

    # We changed our mind locally, don't want video
    chan.StreamedMedia.RemoveStreams([stream_id2])

    e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate'))
    stream.send(make_result_iq(stream, e.stanza))

    jt.parse_session_initiate(e.query)

    # Gabble sends content-remove for the video stream...
    e2 = q.expect('stream-iq', predicate=jp.action_predicate('content-remove'))

    # ...but before the peer notices, they accept the call.
    jt.accept()

    # Only now the remote end removes the video stream; if gabble mistakenly
    # marked it as accepted on session acceptance, it'll crash right about
    # now. If it's good, stream will be really removed, and
    # we can proceed.
    stream.send(make_result_iq(stream, e2.stanza))

    q.expect('dbus-signal', signal='StreamRemoved')

    # Actually, we *do* want video!
    chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO])

    e = q.expect('dbus-signal', signal='NewStreamHandler')
    stream2_id = e.args[1]

    stream_handler2 = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler')

    stream_handler2.NewNativeCandidate("fake", jt.get_remote_transports_dbus())
    stream_handler2.Ready(jt.get_audio_codecs_dbus())
    stream_handler2.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)

    e = q.expect('stream-iq', predicate=jp.action_predicate('content-add'))
    c = e.query.firstChildElement()
    assertEquals('initiator', c['creator'])
    stream.send(make_result_iq(stream, e.stanza))

    # Peer accepts
    jt.content_accept(e.query, 'video')

    # Let's start sending and receiving video!
    q.expect_many(
        EventPattern('dbus-signal', signal='SetStreamPlaying', args=[True]),
        EventPattern('dbus-signal', signal='SetStreamSending', args=[True]),
        )

    # Now, the call draws to a close.
    # We first remove the original stream
    chan.StreamedMedia.RemoveStreams([stream_id])

    e = q.expect('stream-iq', predicate=jp.action_predicate('content-remove'))
    content_remove_ack = make_result_iq(stream, e.stanza)

    if peer_removes_final_content:
        # The peer removes the final countdo^W content. From a footnote (!) in
        # XEP 0166:
        #     If the content-remove results in zero content definitions for the
        #     session, the entity that receives the content-remove SHOULD send
        #     a session-terminate action to the other party (since a session
        #     with no content definitions is void).
        # So, Gabble should respond to the content-remove with a
        # session-terminate.
        node = jp.SetIq(jt.peer, jt.jid, [
            jp.Jingle(jt.sid, jt.peer, 'content-remove', [
                jp.Content(c['name'], c['creator'], c['senders']) ]) ])
        stream.send(jp.xml(node))
    else:
        # The Telepathy client removes the second stream; Gabble should
        # terminate the session rather than sending a content-remove.
        chan.StreamedMedia.RemoveStreams([stream2_id])

    st, closed = q.expect_many(
        EventPattern('stream-iq',
            predicate=jp.action_predicate('session-terminate')),
        # Gabble shouldn't wait for the peer to ack the terminate before
        # considering the call finished.
        EventPattern('dbus-signal', signal='Closed', path=path))

    # Only now does the peer ack the content-remove. This serves as a
    # regression test for contents outliving the session; if the content didn't
    # die properly, this crashed Gabble.
    stream.send(content_remove_ack)
    sync_stream(q, stream)

    # The peer can ack the terminate too, just for completeness.
    stream.send(make_result_iq(stream, st.stanza))
Exemple #36
0
def test(q, bus, conn):
    self_name = 'testsuite' + '@' + avahitest.get_host_name()

    conn.Connect()

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

    # FIXME: this is a hack to be sure to have all the contact list channels
    # announced so they won't interfere with the muc ones announces.
    wait_for_contact_list(q, conn)

    # check if we can request roomlist channels
    properties = conn.GetAll(tp_name_prefix + '.Connection.Interface.Requests',
                             dbus_interface='org.freedesktop.DBus.Properties')
    assert ({tp_name_prefix + '.Channel.ChannelType':
                cs.CHANNEL_TYPE_TEXT,
             tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM,
             },
             [tp_name_prefix + '.Channel.TargetHandle',
              tp_name_prefix + '.Channel.TargetID'],
             ) in properties.get('RequestableChannelClasses'),\
                     properties['RequestableChannelClasses']

    requestotron = dbus.Interface(
        conn, tp_name_prefix + '.Connection.Interface.Requests')

    # create muc channel using new API
    call_async(
        q, requestotron, 'CreateChannel', {
            tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_TEXT,
            tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM,
            tp_name_prefix + '.Channel.TargetID': 'my-second-room',
        })

    ret, new_sig = q.expect_many(
        EventPattern('dbus-return', method='CreateChannel'),
        EventPattern('dbus-signal', signal='NewChannels'),
    )
    path2 = ret.value[0]
    chan = make_channel_proxy(conn, path2, "Channel")

    props = ret.value[1]
    assert props[tp_name_prefix + '.Channel.ChannelType'] ==\
            cs.CHANNEL_TYPE_TEXT
    assert props[tp_name_prefix + '.Channel.TargetHandleType'] == cs.HT_ROOM
    assert props[tp_name_prefix + '.Channel.TargetID'] == 'my-second-room'
    assert props[tp_name_prefix + '.Channel.Requested'] == True
    assert props[tp_name_prefix + '.Channel.InitiatorHandle'] \
            == conn.Properties.Get(cs.CONN, "SelfHandle")
    assert props[tp_name_prefix + '.Channel.InitiatorID'] \
            == self_name

    assert new_sig.args[0][0][0] == path2
    assert new_sig.args[0][0][1] == props

    # ensure roomlist channel
    handle = props[tp_name_prefix + '.Channel.TargetHandle']
    yours, ensured_path, ensured_props = ret.value = requestotron.EnsureChannel(
        {
            tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_TEXT,
            tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM,
            tp_name_prefix + '.Channel.TargetHandle': handle,
        })

    assert not yours
    assert ensured_path == path2, (ensured_path, path2)

    conn.Disconnect()

    q.expect_many(
        EventPattern('dbus-signal', signal='Closed', path=path2),
        EventPattern('dbus-signal', signal='ChannelClosed', args=[path2]),
        EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]),
    )
def test(q, bus, conn):
    conn.Connect()
    q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
    basic_txt = {"txtvers": "1", "status": "avail"}

    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    self_handle_name = conn.Properties.Get(cs.CONN, "SelfID")

    contact_name = "test-muc@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)

    AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)

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

    requests_iface = dbus.Interface(conn, cs.CONN_IFACE_REQUESTS)

    # Create a connection to send the muc invite
    AvahiListener(q).listen_for_service("_presence._tcp")
    e = q.expect('service-added',
                 name=self_handle_name,
                 protocol=avahi.PROTO_INET)
    service = e.service
    service.resolve()

    e = q.expect('service-resolved', service=service)

    outbound = connect_to_stream(q, contact_name, self_handle_name, str(e.pt),
                                 e.port)

    e = q.expect('connection-result')
    assert e.succeeded, e.reason
    e = q.expect('stream-opened', connection=outbound)

    # connected to Salut, now send the muc invite
    msg = domish.Element((None, 'message'))
    msg['to'] = self_handle_name
    msg['from'] = contact_name
    msg['type'] = 'normal'
    body = msg.addElement('body',
                          content='You got a Clique chatroom invitation')
    invite = msg.addElement((NS_CLIQUE, 'invite'))
    invite.addElement('roomname', content='my-room')
    invite.addElement('reason', content='Inviting to this room')
    # FIXME: we should create a real Clique room and use its IP and port.
    # Hardcode values for now. The IP is a multicast address.
    invite.addElement('address', content='239.255.71.249')
    invite.addElement('port', content='62472')
    outbound.send(msg)

    e = q.expect('dbus-signal',
                 signal='NewChannels',
                 predicate=lambda e: e.args[0][0][1][cs.CHANNEL_TYPE] == cs.
                 CHANNEL_TYPE_TEXT)
    channels = e.args[0]
    assert len(channels) == 1
    path, props = channels[0]
    channel = make_channel_proxy(conn, path, "Channel")
    channel_group = make_channel_proxy(conn, path, "Channel.Interface.Group")

    # check channel properties
    # org.freedesktop.Telepathy.Channel D-Bus properties
    assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT
    assertContains(cs.CHANNEL_IFACE_GROUP, props[cs.INTERFACES])
    assertContains(cs.CHANNEL_IFACE_MESSAGES, props[cs.INTERFACES])
    assert props[cs.TARGET_ID] == 'my-room'
    assert props[cs.TARGET_HANDLE_TYPE] == HT_ROOM
    assert props[cs.REQUESTED] == False
    assert props[cs.INITIATOR_HANDLE] == handle
    assert props[cs.INITIATOR_ID] == contact_name

    # we are added to local pending
    e = q.expect('dbus-signal', signal='MembersChanged')
    msg, added, removed, lp, rp, actor, reason = e.args
    assert msg == 'Inviting to this room'
    assert added == []
    assert removed == []
    assert lp == [self_handle]
    assert rp == []
    assert actor == handle
    assert reason == 4  # invited

    # TODO: join the muc, check if we are added to remote-pending and then
    # to members. This would need some tweak in Salut and/or the test framework
    # as Clique takes too much time to join the room and so the event times
    # out.

    # TODO: test sending invitations

    # FIXME: fd.o #30531: this ought to work, but doesn't
    #channel_group.RemoveMembersWithReason([self_handle], "bored now", 0)
    channel.Close()
    q.expect('dbus-signal', signal='Closed')
def test_call(jp,
              q,
              bus,
              conn,
              stream,
              expected_stun_servers=None,
              google=False,
              google_push_replacements=None,
              expected_relays=[]):
    # Initialize the test values
    jt, remote_handle = init_test(jp, q, conn, stream, google,
                                  google_push_replacements)

    # Advertise that we can do new style calls
    conn.ContactCapabilities.UpdateCapabilities([
        (cs.CLIENT + ".CallHandler", [
            {
                cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL,
                cs.CALL_INITIAL_AUDIO: True
            },
            {
                cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL,
                cs.CALL_INITIAL_VIDEO: True
            },
        ], [
            cs.CHANNEL_TYPE_CALL + '/gtalk-p2p',
            cs.CHANNEL_TYPE_CALL + '/ice',
            cs.CHANNEL_TYPE_CALL + '/video/h264',
        ]),
    ])

    # Remote end calls us
    jt.incoming_call()

    e = q.expect('dbus-signal', signal='ServerInfoRetrieved')
    assertLength(0, e.args)
    assertEquals(e.interface, cs.CALL_STREAM_IFACE_MEDIA)

    e = q.expect(
        'dbus-signal',
        signal='NewChannels',
        predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args)
    assert e.args[0][0][0]

    call_chan = make_channel_proxy(conn, e.args[0][0][0], 'Channel')

    # Exercise channel properties
    channel_props = call_chan.GetAll(cs.CHANNEL,
                                     dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(remote_handle, channel_props['TargetHandle'])
    assertEquals(1, channel_props['TargetHandleType'])
    assertEquals('*****@*****.**', channel_props['TargetID'])
    assertEquals(False, channel_props['Requested'])
    assertEquals('*****@*****.**', channel_props['InitiatorID'])
    assertEquals(remote_handle, channel_props['InitiatorHandle'])

    # Get the call's Content object
    channel_props = call_chan.Get(cs.CHANNEL_TYPE_CALL,
                                  'Contents',
                                  dbus_interface=dbus.PROPERTIES_IFACE)
    assertLength(1, channel_props)
    assert len(channel_props[0]) > 0
    assertNotEquals('/', channel_props[0])

    # Get the call's Stream object
    call_content = make_channel_proxy(conn, channel_props[0],
                                      'Call.Content.Draft')
    content_props = call_content.Get(cs.CALL_CONTENT,
                                     'Streams',
                                     dbus_interface=dbus.PROPERTIES_IFACE)
    assertLength(1, content_props)
    assert len(content_props[0]) > 0
    assertNotEquals('/', content_props[0])

    # Test the call's Stream's properties
    call_stream = make_channel_proxy(conn, content_props[0],
                                     'Call.Stream.Interface.Media.Draft')
    stream_props = call_stream.GetAll(cs.CALL_STREAM_IFACE_MEDIA,
                                      dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(cs.CALL_STREAM_TRANSPORT_GTALK_P2P, stream_props['Transport'])

    test_stun_server(stream_props['STUNServers'], expected_stun_servers)

    assertEquals(expected_relays, stream_props['RelayInfo'])
    assertEquals(True, stream_props['HasServerInfo'])
def worker(jp, q, bus, conn, stream):
    jp.features.append(ns.JINGLE_TRANSPORT_ICEUDP)
    jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', '[email protected]/Foo')
    jt2.prepare()

    remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/Foo"])[0]

    # Remote end calls us
    node = jp.SetIq(jt2.peer, jt2.jid, [
        jp.Jingle(jt2.sid, jt2.peer, 'session-initiate', [
            jp.Content('stream1', 'initiator', 'both',
                jp.Description('audio', [
                    jp.PayloadType(name, str(rate), str(id), parameters) for
                        (name, id, rate, parameters) in jt2.audio_codecs ]),
            jp.TransportIceUdp()) ]) ])
    stream.send(jp.xml(node))

    nc, e = q.expect_many(
        EventPattern('dbus-signal', signal='NewChannel',
            predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args),
        EventPattern('dbus-signal', signal='NewSessionHandler'),
        )
    path = nc.args[0]

    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia')

    hrggh = chan.ListProperties(dbus_interface=cs.TP_AWKWARD_PROPERTIES)
    id = [x for x, name, _, _ in hrggh if name == 'nat-traversal'][0]
    nrgrg = chan.GetProperties([id], dbus_interface=cs.TP_AWKWARD_PROPERTIES)
    _, nat_traversal = nrgrg[0]
    assertEquals('ice-udp', nat_traversal)

    session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler')
    session_handler.Ready()

    chan.Group.AddMembers([conn.GetSelfHandle()], 'accepted')

    # S-E gets notified about a newly-created stream
    e = q.expect('dbus-signal', signal='NewStreamHandler')

    stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler')

    stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus())
    stream_handler.Ready(jt2.get_audio_codecs_dbus())
    stream_handler.StreamState(2)

    # First one is transport-info
    e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info'))
    transport_stanza = xpath.queryForNodes(
        "/iq/jingle/content/transport[@xmlns='%s']"
        % ns.JINGLE_TRANSPORT_ICEUDP, e.stanza)[0]

    # username
    assertEquals(transport_stanza['ufrag'], jt2.remote_transports[0][7])
    # password
    assertEquals(transport_stanza['pwd'], jt2.remote_transports[0][8])

    children = list(transport_stanza.elements())
    assertLength(1, children)

    stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, [])))

    # Set codec intersection so gabble can accept the session
    stream_handler.SupportedCodecs(jt2.get_audio_codecs_dbus())

    # Second one is session-accept
    e = q.expect('stream-iq', predicate=jp.action_predicate('session-accept'))
    assert xpath.queryForNodes("/iq/jingle/content/transport[@xmlns='%s']" %
        ns.JINGLE_TRANSPORT_ICEUDP, e.stanza)

    stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, [])))

    # Connected! Blah, blah, ...

    jt2.terminate()

    e = q.expect('dbus-signal', signal='Close')
 def create_ft_channel(self):
     self.channel = make_channel_proxy(self.conn, self.ft_path, 'Channel')
     self.ft_channel = make_channel_proxy(self.conn, self.ft_path, 'Channel.Type.FileTransfer')
     self.ft_props = dbus.Interface(self.bus.get_object(
         self.conn.object.bus_name, self.ft_path), PROPERTIES_IFACE)
def test(jp, q, bus, conn, stream, peer='[email protected]/Foo'):
    jt = JingleTest2(jp, conn, q, stream, 'test@localhost', peer)
    jt.prepare()

    self_handle = conn.GetSelfHandle()
    remote_handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0]

    # Remote end calls us
    jt.incoming_call()

    # If this is a Jingle dialect that supports it, Gabble should send a
    # <ringing/> notification when it gets the session-initiate until Telepathy
    # has a way for the UI to do this.
    # https://bugs.freedesktop.org/show_bug.cgi?id=21964
    ringing_event = jp.rtp_info_event_list("ringing")

    if jp.dialect == 'gtalk-v0.4':
        # With gtalk4, apparently we have to send transport-accept immediately,
        # not even just before we send our transport-info. wjt tested this, and
        # indeed if we don't send this for incoming calls, the call never
        # connects.
        ta_event = [
            EventPattern('stream-iq', predicate=lambda x:
                xpath.queryForNodes("/iq/session[@type='transport-accept']",
                    x.stanza)),
            ]
    else:
        ta_event = []

    nc, e = q.expect_many(
        EventPattern('dbus-signal', signal='NewChannel',
            predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args),
        EventPattern('dbus-signal', signal='NewSessionHandler'),
        *(ringing_event + ta_event)
        )[0:2]
    path, ct, ht, h, _ = nc.args

    assert ct == cs.CHANNEL_TYPE_STREAMED_MEDIA, ct
    assert ht == cs.HT_CONTACT, ht
    assert h == remote_handle, h

    media_chan = make_channel_proxy(conn, path, 'Channel.Interface.Group')
    media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia')

    # S-E was notified about new session handler, and calls Ready on it
    assert e.args[1] == 'rtp'
    session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler')
    session_handler.Ready()

    nsh_event = q.expect('dbus-signal', signal='NewStreamHandler')

    # S-E gets notified about a newly-created stream
    stream_handler = make_channel_proxy(conn, nsh_event.args[0],
        'Media.StreamHandler')

    streams = media_iface.ListStreams()
    assertLength(1, streams)

    stream_id, stream_handle, stream_type, _, stream_direction, pending_flags =\
        streams[0]
    assertEquals(remote_handle, stream_handle)
    assertEquals(cs.MEDIA_STREAM_TYPE_AUDIO, stream_type)
    assertEquals(cs.MEDIA_STREAM_DIRECTION_RECEIVE, stream_direction)
    assertEquals(cs.MEDIA_STREAM_PENDING_LOCAL_SEND, pending_flags)

    # Exercise channel properties
    channel_props = media_chan.GetAll(
        cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(remote_handle, channel_props['TargetHandle'])
    assertEquals(cs.HT_CONTACT, channel_props['TargetHandleType'])
    assertEquals((cs.HT_CONTACT, remote_handle),
            media_chan.GetHandle(dbus_interface=cs.CHANNEL))
    assertEquals(jt.peer_bare_jid, channel_props['TargetID'])
    assertEquals(jt.peer_bare_jid, channel_props['InitiatorID'])
    assertEquals(remote_handle, channel_props['InitiatorHandle'])
    assertEquals(False, channel_props['Requested'])

    group_props = media_chan.GetAll(
        cs.CHANNEL_IFACE_GROUP, dbus_interface=dbus.PROPERTIES_IFACE)

    assert group_props['SelfHandle'] == self_handle, \
        (group_props['SelfHandle'], self_handle)

    flags = group_props['GroupFlags']
    assert flags & cs.GF_PROPERTIES, flags
    # Changing members in any way other than adding or removing yourself is
    # meaningless for incoming calls, and the flags need not be sent to change
    # your own membership.
    assert not flags & cs.GF_CAN_ADD, flags
    assert not flags & cs.GF_CAN_REMOVE, flags
    assert not flags & cs.GF_CAN_RESCIND, flags

    assert group_props['Members'] == [remote_handle], group_props['Members']
    assert group_props['RemotePendingMembers'] == [], \
        group_props['RemotePendingMembers']
    # We're local pending because remote_handle invited us.
    assert group_props['LocalPendingMembers'] == \
        [(self_handle, remote_handle, cs.GC_REASON_INVITED, '')], \
        unwrap(group_props['LocalPendingMembers'])

    streams = media_chan.ListStreams(
            dbus_interface=cs.CHANNEL_TYPE_STREAMED_MEDIA)
    assert len(streams) == 1, streams
    assert len(streams[0]) == 6, streams[0]
    # streams[0][0] is the stream identifier, which in principle we can't
    # make any assertion about (although in practice it's probably 1)
    assert streams[0][1] == remote_handle, (streams[0], remote_handle)
    assert streams[0][2] == cs.MEDIA_STREAM_TYPE_AUDIO, streams[0]
    # We haven't connected yet
    assert streams[0][3] == cs.MEDIA_STREAM_STATE_DISCONNECTED, streams[0]
    # In Gabble, incoming streams start off with remote send enabled, and
    # local send requested
    assert streams[0][4] == cs.MEDIA_STREAM_DIRECTION_RECEIVE, streams[0]
    assert streams[0][5] == cs.MEDIA_STREAM_PENDING_LOCAL_SEND, streams[0]

    # Connectivity checks happen before we have accepted the call
    stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus())
    stream_handler.Ready(jt.get_audio_codecs_dbus())
    stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)
    stream_handler.SupportedCodecs(jt.get_audio_codecs_dbus())

    # peer gets the transport
    e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info'))
    assertEquals(jt.peer, e.query['initiator'])

    if jp.dialect in ['jingle-v0.15', 'jingle-v0.31']:
        content = xpath.queryForNodes('/iq/jingle/content', e.stanza)[0]
        assertEquals('initiator', content['creator'])

    stream.send(make_result_iq(stream, e.stanza))

    # At last, accept the call
    media_chan.AddMembers([self_handle], 'accepted')

    # Call is accepted, we become a member, and the stream that was pending
    # local send is now sending.
    memb, acc, _, _, _ = q.expect_many(
        EventPattern('dbus-signal', signal='MembersChanged',
            args=[u'', [self_handle], [], [], [], self_handle,
                  cs.GC_REASON_NONE]),
        EventPattern('stream-iq',
            predicate=jp.action_predicate('session-accept')),
        EventPattern('dbus-signal', signal='SetStreamSending', args=[True]),
        EventPattern('dbus-signal', signal='SetStreamPlaying', args=[True]),
        EventPattern('dbus-signal', signal='StreamDirectionChanged',
            args=[stream_id, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0]),
        )

    stream.send(make_result_iq(stream, acc.stanza))

    # Also, if this is a Jingle dialect that supports it, Gabble should send an
    # <active/> notification when the session-accept is acked (until the
    # Telepathy spec lets the UI say it's not ringing any more).
    active_event = jp.rtp_info_event("active")
    if active_event is not None:
        q.expect_many(active_event)

    # we are now both in members
    members = media_chan.GetMembers()
    assert set(members) == set([self_handle, remote_handle]), members

    # Connected! Blah, blah, ...

    # 'Nuff said
    jt.terminate()
    q.expect('dbus-signal', signal='Closed', path=path)
def test(jp, q, bus, conn, stream, busy):
    remote_jid = '[email protected]/Foo'
    jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid)

    jt.prepare()

    self_handle = conn.GetSelfHandle()
    remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0]

    # Remote end calls us
    jt.incoming_call()

    # FIXME: these signals are not observable by real clients, since they
    #        happen before NewChannels.
    # The caller is in members
    e = q.expect('dbus-signal', signal='MembersChanged',
             args=[u'', [remote_handle], [], [], [], 0, 0])

    # We're pending because of remote_handle
    e = q.expect('dbus-signal', signal='MembersChanged',
             args=[u'', [], [], [self_handle], [], remote_handle,
                   cs.GC_REASON_INVITED])

    # S-E gets notified about new session handler, and calls Ready on it
    e = q.expect('dbus-signal', signal='NewSessionHandler')
    assert e.args[1] == 'rtp'

    session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler')
    session_handler.Ready()

    media_chan = make_channel_proxy(conn, e.path, 'Channel.Interface.Group')

    # Exercise channel properties
    channel_props = media_chan.GetAll(cs.CHANNEL,
            dbus_interface=cs.PROPERTIES_IFACE)
    assert channel_props['TargetHandle'] == remote_handle
    assert channel_props['TargetHandleType'] == 1
    assert channel_props['TargetID'] == '*****@*****.**'
    assert channel_props['Requested'] == False
    assert channel_props['InitiatorID'] == '*****@*****.**'
    assert channel_props['InitiatorHandle'] == remote_handle

    if busy:
        # First, try using a reason that doesn't make any sense
        call_async(q, media_chan, 'RemoveMembersWithReason',
            [self_handle], "what kind of a reason is Separated?!",
            cs.GC_REASON_SEPARATED)
        e = q.expect('dbus-error', method='RemoveMembersWithReason')
        assert e.error.get_dbus_name() == cs.INVALID_ARGUMENT

        # Now try a more sensible reason.
        media_chan.RemoveMembersWithReason([self_handle],
            "which part of 'Do Not Disturb' don't you understand?",
            cs.GC_REASON_BUSY)
    else:
        media_chan.RemoveMembers([self_handle], 'rejected')

    iq, mc, _ = q.expect_many(
        EventPattern('stream-iq',
            predicate=jp.action_predicate('session-terminate')),
        EventPattern('dbus-signal', signal='MembersChanged'),
        EventPattern('dbus-signal', signal='Closed'),
        )

    _, added, removed, lp, rp, actor, reason = mc.args
    assert added == [], added
    assert set(removed) == set([self_handle, remote_handle]), \
        (removed, self_handle, remote_handle)
    assert lp == [], lp
    assert rp == [], rp
    assert actor == self_handle, (actor, self_handle)
    if busy:
        assert reason == cs.GC_REASON_BUSY, reason
    else:
        assert reason == cs.GC_REASON_NONE, reason

    if jp.is_modern_jingle():
        jingle = iq.query
        if busy:
            r = "/jingle/reason/busy"
        else:
            r = "/jingle/reason/cancel"
        assert xpath.queryForNodes(r, jingle) is not None, (jingle.toXml(), r)
def test(q, bus, conn):
    conn.Connect()
    q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
    basic_txt = { "txtvers": "1", "status": "avail" }

    contact_name = "test-text-channel@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)

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

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

    t = conn.Requests.CreateChannel({
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
        cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
        cs.TARGET_HANDLE: handle})[0]
    text_channel = make_channel_proxy(conn, t, "Channel.Type.Text")
    text_channel.Send(cs.MT_NORMAL, INCOMING_MESSAGE)

    e = q.expect('incoming-connection', listener = listener)
    incoming = e.connection

    e = q.expect('stream-message', connection = incoming)
    assert e.message_type == "chat"
    body = xpath.queryForNodes("/message/body",  e.stanza )
    assert map(str, body) == [ INCOMING_MESSAGE ]

    # drop the connection
    incoming.transport.loseConnection()

    # Now send a message to salut
    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()


    e = q.expect('service-resolved', service = service)

    outbound = connect_to_stream(q, contact_name,
        self_handle_name, str(e.pt), e.port)

    e = q.expect('connection-result')
    assert e.succeeded, e.reason

    e = q.expect('stream-opened', connection = outbound)

    # connected to salut, now send a message
    message = domish.Element(('', 'message'))
    message['type'] = "chat"
    message.addElement('body', content=OUTGOING_MESSAGE)

    e.connection.send(message)

    e = q.expect('dbus-signal', signal='Received')
    assert e.args[2] == handle
    assert e.args[3] == cs.MT_NORMAL
    assert e.args[5] == OUTGOING_MESSAGE