예제 #1
0
def test_props_present(q, bus, conn, stream):
    chan = setup(q, bus, conn, stream)

    props = chan.Properties.GetAll(cs.CHANNEL_IFACE_ROOM_CONFIG)
    assertContains('PasswordProtected', props)
    assertContains('Password', props)
    assertContains('Description', props)
    assertContains('Title', props)
    assertContains('ConfigurationRetrieved', props)
    assertContains('Persistent', props)
    assertContains('Private', props)
    assertContains('Limit', props)
    assertContains('Anonymous', props)
    assertContains('CanUpdateConfiguration', props)
    assertContains('PasswordHint', props)
    assertContains('Moderated', props)
    assertContains('InviteOnly', props)
    assertContains('MutableProperties', props)

    # this should do nothing
    forbidden = [EventPattern('dbus-signal', signal='PropertiesChanged')]
    q.forbid_events(forbidden)
    call_async(q, chan.RoomConfig1, 'UpdateConfiguration', {})

    sync_stream(q, stream)
    q.unforbid_events(forbidden)

    # we should have these mutable ones
    mutable_props = ['InviteOnly',
                     'Limit',
                     'Moderated',
                     'Private',
                     'PasswordProtected',
                     'Password']
    assertEquals(mutable_props, props['MutableProperties'])
예제 #2
0
def test_limit(q, bus, conn, stream):
    chan = setup(q, bus, conn, stream)

    # do nothing, really
    forbidden = [EventPattern('stream-MODE'),
                 EventPattern('dbus-signal', signal='PropertiesChanged')]
    q.forbid_events(forbidden)

    call_async(q, chan.RoomConfig1, 'UpdateConfiguration',
               {'Limit': dbus.UInt32(0)})

    q.expect_many(EventPattern('dbus-return', method='UpdateConfiguration'))

    sync_stream(q, stream)
    q.unforbid_events(forbidden)

    # set a limit
    call_async(q, chan.RoomConfig1, 'UpdateConfiguration',
               {'Limit': dbus.UInt32(1337)}) # totally 1337

    q.expect_many(EventPattern('dbus-return', method='UpdateConfiguration'),
                  EventPattern('stream-MODE', data=['#test', '+l', '1337']),
                  EventPattern('dbus-signal', signal='PropertiesChanged',
                               args=[cs.CHANNEL_IFACE_ROOM_CONFIG,
                                     {'Limit': 1337},
                                     []])
                  )

    # unset the limit
    call_async(q, chan.RoomConfig1, 'UpdateConfiguration',
               {'Limit': dbus.UInt32(0)})

    q.expect_many(EventPattern('dbus-return', method='UpdateConfiguration'),
                  EventPattern('stream-MODE', data=['#test', '-l']),
                  EventPattern('dbus-signal', signal='PropertiesChanged',
                               args=[cs.CHANNEL_IFACE_ROOM_CONFIG,
                                     {'Limit': 0},
                                     []])
                  )
예제 #3
0
def test(q, bus, conn, stream, use_room=False):
    conn.Connect()
    q.expect_many(
        EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]),
        EventPattern('irc-connected'))
    q.expect('dbus-signal', signal='SelfHandleChanged')
    q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])

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

    request = build_request(conn, '#idletest', use_room)
    call_async(q, conn.Requests, 'CreateChannel', request)

    # Idle should try to join the channel.
    q.expect('stream-JOIN')

    # Meanwhile, in another application...

    call_async(q,
               conn,
               'EnsureChannel',
               request,
               dbus_interface=cs.CONN_IFACE_REQUESTS)

    sync_dbus(bus, q, conn)

    # Now the ircd responds:
    stream.sendJoin('#idletest')

    cc, ec = q.expect_many(
        EventPattern('dbus-return', method='CreateChannel'),
        EventPattern('dbus-return', method='EnsureChannel'),
    )
    nc = q.expect('dbus-signal', signal='NewChannels')

    path, props = cc.value

    assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT
    assertSameSets([
        cs.CHANNEL_IFACE_GROUP,
        cs.CHANNEL_IFACE_PASSWORD,
        cs.CHANNEL_IFACE_MESSAGES,
        cs.CHANNEL_IFACE_ROOM,
        cs.CHANNEL_IFACE_SUBJECT,
        cs.CHANNEL_IFACE_ROOM_CONFIG,
        cs.CHANNEL_IFACE_DESTROYABLE,
    ], props[cs.INTERFACES])
    assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM
    assert props[cs.TARGET_ID] == '#idletest'
    assertEquals('#idletest', props[cs.ROOM_NAME])
    assertEquals('', props[cs.ROOM_SERVER])
    assert props[cs.REQUESTED]
    assert props[cs.INITIATOR_HANDLE] == self_handle
    assert props[cs.INITIATOR_ID] == \
            conn.inspect_contacts_sync([self_handle])[0]

    ec_yours, ec_path, ec_props = ec.value
    assert not ec_yours
    assert ec_path == path
    assert ec_props == props

    channels = nc.args[0]
    assert len(channels) == 1
    nc_path, nc_props = channels[0]
    assert nc_path == path
    assert nc_props == props

    # And again?
    ec_ = conn.EnsureChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS)
    assert ec.value == ec_

    chans = conn.Get(cs.CONN_IFACE_REQUESTS,
                     'Channels',
                     dbus_interface=cs.PROPERTIES_IFACE)
    assert len(chans) == 1
    assert chans[0] == (path, props)

    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
                        ['Destroyable', 'Messages'])

    # Put an unacknowledged message into the channel
    stream.sendMessage('PRIVMSG', '#idletest', ':oi oi', prefix='lol')
    q.expect('dbus-signal', signal='MessageReceived', path=path)

    # Make sure Close()ing the channel makes it respawn. This avoids the old
    # bug where empathy-chat crashing booted you out of all your channels.
    patterns = [EventPattern('stream-PART')]
    q.forbid_events(patterns)
    chan.Close()
    q.expect('dbus-signal', signal='Closed', path=chan.object_path)
    e = q.expect('dbus-signal', signal='NewChannels')

    path, props = e.args[0][0]
    assertEquals(chan.object_path, path)
    # We requested the channel originally, but we didn't request it popping
    # back up.
    assertEquals(0, props[cs.INITIATOR_HANDLE])
    assert not props[cs.REQUESTED]

    # The unacknowledged message should still be there and be marked as rescued.
    messages = chan.Properties.Get(cs.CHANNEL_IFACE_MESSAGES,
                                   'PendingMessages')
    assertLength(1, messages)
    assert messages[0][0]['rescued'], messages[0]

    # Check that ensuring a respawned channel does what you'd expect.
    ec_yours, ec_path, ec_props = conn.EnsureChannel(
        request, dbus_interface=cs.CONN_IFACE_REQUESTS)
    assert not ec_yours
    assertEquals(chan.object_path, ec_path)
    assertEquals(props, ec_props)

    sync_stream(q, stream)
    q.unforbid_events(patterns)

    chan.RemoveMembers([self_handle],
                       "bye bye cruel\r\nworld",
                       dbus_interface=cs.CHANNEL_IFACE_GROUP)

    part_event = q.expect('stream-PART')

    # This is a regression test for
    # <https://bugs.freedesktop.org/show_bug.cgi?id=34812>, where part messages
    # were not correctly colon-quoted.
    #
    # It is also a regression test for
    # <https://bugs.freedesktop.org/show_bug.cgi?id=34840>, where newlines
    # weren't stripped from part messages. We check that both \r and \n are
    # replaced by harmless spaces.
    assertEquals("bye bye cruel  world", part_event.data[1])

    stream.sendPart('#idletest', stream.nick)

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

    chans = conn.Get(cs.CONN_IFACE_REQUESTS,
                     'Channels',
                     dbus_interface=cs.PROPERTIES_IFACE)
    assert len(chans) == 0
예제 #4
0
def test(q, bus, conn, stream):
    conn.Connect()

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

    alice_handle, bob_handle = conn.get_contact_handles_sync(['alice', 'bob'])

    call_async(
        q, conn.Requests, 'CreateChannel', {
            CHANNEL_TYPE: CHANNEL_TYPE_TEXT,
            TARGET_HANDLE_TYPE: HT_ROOM,
            TARGET_ID: room
        })

    q.expect('stream-JOIN')
    event = q.expect('dbus-return', method='CreateChannel')
    path = event.value[0]

    channel = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
                           ['Subject2'])

    assertContains(CHANNEL_IFACE_SUBJECT,
                   channel.Properties.Get(CHANNEL, 'Interfaces'))

    # No topic set
    subject_props = channel.Properties.GetAll(CHANNEL_IFACE_SUBJECT)
    assertEquals('', subject_props['Subject'])
    assertEquals(0x7fffffffffffffffL, subject_props['Timestamp'])
    assertEquals('', subject_props['Actor'])
    assertEquals(0, subject_props['ActorHandle'])

    # Before the topic arrives from the server, check that our API works okay.
    # FIXME: when we make SetSubject return asynchronously, this will need
    # revising.
    test_can_set(q, stream, channel)

    # We're told the channel's topic, and (in a separte message) who set it and
    # when.
    stream.sendMessage('332',
                       stream.nick,
                       room,
                       ':Test123',
                       prefix='idle.test.server')
    stream.sendMessage('333',
                       stream.nick,
                       room,
                       'bob',
                       '1307802600',
                       prefix='idle.test.server')

    # FIXME: signal these together, if possible.
    expect_subject_props_changed(q, {'Subject': 'Test123'})
    expect_subject_props_changed(q, {
        'Timestamp': 1307802600,
        'Actor': 'bob',
        'ActorHandle': bob_handle,
    },
                                 exact_timestamp=True)

    # Another user changes the topic.
    stream.sendMessage('TOPIC',
                       room,
                       ':I am as high as a kite',
                       prefix='alice')
    expect_subject_props_changed(
        q, {
            'Subject': 'I am as high as a kite',
            'Actor': 'alice',
            'ActorHandle': alice_handle,
            'Timestamp': 1234,
        })

    # BIP omits the : for the trailing parameter if it's a single word, make
    # sure we pass that as well
    stream.sendMessage('TOPIC', room, 'badgers!', prefix='alice')
    expect_subject_props_changed(
        q, {
            'Subject': 'badgers!',
            'Actor': 'alice',
            'ActorHandle': alice_handle,
            'Timestamp': 1234,
        })

    test_can_set(q, stream, channel)

    # Topic is read/write, if we get ops it should stay that way
    forbidden = [
        EventPattern('dbus-signal',
                     signal='PropertiesChanged',
                     predicate=lambda e: e.args[0] == CHANNEL_IFACE_SUBJECT)
    ]
    q.forbid_events(forbidden)

    # Set ops, check that t flag becomes a no-op
    change_channel_mode(stream, '+o ' + stream.nick)
    change_channel_mode(stream, '+t')
    change_channel_mode(stream, '-t')
    change_channel_mode(stream, '-o ' + stream.nick)

    # Check that other flags don't cause issues
    change_channel_mode(stream, '+n')
    change_channel_mode(stream, '+n')

    change_channel_mode(stream, '+to ' + stream.nick)
    change_channel_mode(stream, '-to ' + stream.nick)

    sync_stream(q, stream)
    sync_dbus(bus, q, conn)
    q.unforbid_events(forbidden)

    # back to normal?
    test_can_set(q, stream, channel)

    # Check if setting ops gives us write access on +t channels
    change_channel_mode(stream, '+t')
    expect_and_check_can_set(q, channel, False)

    change_channel_mode(stream, '+o ' + stream.nick)
    expect_and_check_can_set(q, channel, True)

    change_channel_mode(stream, '-o ' + stream.nick)
    expect_and_check_can_set(q, channel, False)

    change_channel_mode(stream, '-t')
    expect_and_check_can_set(q, channel, True)

    # And back to normal again ?
    test_can_set(q, stream, channel)

    channel.Subject2.SetSubject('')
    # Verify that we send an empty final parameter ("clear the topic") as
    # opposed to no final parameter ("what is the topic").
    q.expect('stream-TOPIC', data=[room, ''])
예제 #5
0
def test_simple_bools(q, bus, conn, stream):
    chan = setup(q, bus, conn, stream)

    # the three easy booleans
    for (prop, mode) in [('InviteOnly', 'i'),
                         ('Moderated', 'm'),
                         ('Private', 's')]:
        # first set them all to true
        call_async(q, chan.RoomConfig1, 'UpdateConfiguration', {prop: True})
        q.expect_many(EventPattern('dbus-return', method='UpdateConfiguration'),
                      EventPattern('stream-MODE', data=['#test', '+' + mode]),
                      EventPattern('dbus-signal', signal='PropertiesChanged',
                                   args=[cs.CHANNEL_IFACE_ROOM_CONFIG,
                                         {prop: True}, []])
                      )
        # then them all to false
        call_async(q, chan.RoomConfig1, 'UpdateConfiguration', {prop: False})
        q.expect_many(EventPattern('dbus-return', method='UpdateConfiguration'),
                      EventPattern('stream-MODE', data=['#test', '-' + mode]),
                      EventPattern('dbus-signal', signal='PropertiesChanged',
                                   args=[cs.CHANNEL_IFACE_ROOM_CONFIG,
                                         {prop: False}, []])
                      )

    # set them all to true now
    call_async(q, chan.RoomConfig1, 'UpdateConfiguration',
               {'InviteOnly': True,
                'Moderated': True,
                'Private': True})

    # ... and a monster return
    q.expect_many(EventPattern('dbus-return', method='UpdateConfiguration'),
                  EventPattern('stream-MODE', data=['#test', '+i']),
                  EventPattern('stream-MODE', data=['#test', '+m']),
                  EventPattern('stream-MODE', data=['#test', '+s']),
                  EventPattern('dbus-signal', signal='PropertiesChanged',
                               args=[cs.CHANNEL_IFACE_ROOM_CONFIG,
                                     {'InviteOnly': True,
                                      'Moderated': True,
                                      'Private': True},
                                     []])
                  )

    # set only moderated to false,
    forbidden = [EventPattern('stream-MODE', data=['#test', '+i']),
                 EventPattern('stream-MODE', data=['#test', '+s'])]
    q.forbid_events(forbidden)

    call_async(q, chan.RoomConfig1, 'UpdateConfiguration',
               {'InviteOnly': True,
                'Moderated': False,
                'Private': True})

    # ... and another monster return
    q.expect_many(EventPattern('dbus-return', method='UpdateConfiguration'),
                  EventPattern('stream-MODE', data=['#test', '-m']),
                  EventPattern('dbus-signal', signal='PropertiesChanged',
                               args=[cs.CHANNEL_IFACE_ROOM_CONFIG,
                                     {'Moderated': False},
                                     []])
                  )

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

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

    request = build_request(conn, '#idletest', use_room)
    call_async(q, conn.Requests, 'CreateChannel', request)

    # Idle should try to join the channel.
    q.expect('stream-JOIN')

    # Meanwhile, in another application...

    call_async(q, conn, 'EnsureChannel', request,
        dbus_interface=cs.CONN_IFACE_REQUESTS)

    sync_dbus(bus, q, conn)

    # Now the ircd responds:
    stream.sendJoin('#idletest')

    cc, ec = q.expect_many(
        EventPattern('dbus-return', method='CreateChannel'),
        EventPattern('dbus-return', method='EnsureChannel'),
        )
    nc = q.expect('dbus-signal', signal='NewChannels')

    path, props = cc.value

    assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT
    assertSameSets(
        [cs.CHANNEL_IFACE_GROUP,
         cs.CHANNEL_IFACE_PASSWORD,
         cs.CHANNEL_IFACE_MESSAGES,
         cs.CHANNEL_IFACE_ROOM,
         cs.CHANNEL_IFACE_SUBJECT,
         cs.CHANNEL_IFACE_ROOM_CONFIG,
         cs.CHANNEL_IFACE_DESTROYABLE,
        ], props[cs.INTERFACES])
    assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM
    assert props[cs.TARGET_ID] == '#idletest'
    assertEquals('#idletest', props[cs.ROOM_NAME])
    assertEquals('', props[cs.ROOM_SERVER])
    assert props[cs.REQUESTED]
    assert props[cs.INITIATOR_HANDLE] == self_handle
    assert props[cs.INITIATOR_ID] == \
            conn.inspect_contacts_sync([self_handle])[0]

    ec_yours, ec_path, ec_props = ec.value
    assert not ec_yours
    assert ec_path == path
    assert ec_props == props

    channels = nc.args[0]
    assert len(channels) == 1
    nc_path, nc_props = channels[0]
    assert nc_path == path
    assert nc_props == props

    # And again?
    ec_ = conn.EnsureChannel(request,
        dbus_interface=cs.CONN_IFACE_REQUESTS)
    assert ec.value == ec_

    chans = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels',
        dbus_interface=cs.PROPERTIES_IFACE)
    assert len(chans) == 1
    assert chans[0] == (path, props)

    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
        ['Destroyable', 'Messages'])

    # Put an unacknowledged message into the channel
    stream.sendMessage('PRIVMSG', '#idletest', ':oi oi', prefix='lol')
    q.expect('dbus-signal', signal='MessageReceived', path=path)

    # Make sure Close()ing the channel makes it respawn. This avoids the old
    # bug where empathy-chat crashing booted you out of all your channels.
    patterns = [EventPattern('stream-PART')]
    q.forbid_events(patterns)
    chan.Close()
    q.expect('dbus-signal', signal='Closed', path=chan.object_path)
    e = q.expect('dbus-signal', signal='NewChannels')

    path, props = e.args[0][0]
    assertEquals(chan.object_path, path)
    # We requested the channel originally, but we didn't request it popping
    # back up.
    assertEquals(0, props[cs.INITIATOR_HANDLE])
    assert not props[cs.REQUESTED]

    # The unacknowledged message should still be there and be marked as rescued.
    messages = chan.Properties.Get(cs.CHANNEL_IFACE_MESSAGES, 'PendingMessages')
    assertLength(1, messages)
    assert messages[0][0]['rescued'], messages[0]

    # Check that ensuring a respawned channel does what you'd expect.
    ec_yours, ec_path, ec_props = conn.EnsureChannel(request,
        dbus_interface=cs.CONN_IFACE_REQUESTS)
    assert not ec_yours
    assertEquals(chan.object_path, ec_path)
    assertEquals(props, ec_props)

    sync_stream(q, stream)
    q.unforbid_events(patterns)

    chan.RemoveMembers([self_handle], "bye bye cruel\r\nworld",
        dbus_interface=cs.CHANNEL_IFACE_GROUP)

    part_event = q.expect('stream-PART')

    # This is a regression test for
    # <https://bugs.freedesktop.org/show_bug.cgi?id=34812>, where part messages
    # were not correctly colon-quoted.
    #
    # It is also a regression test for
    # <https://bugs.freedesktop.org/show_bug.cgi?id=34840>, where newlines
    # weren't stripped from part messages. We check that both \r and \n are
    # replaced by harmless spaces.
    assertEquals("bye bye cruel  world", part_event.data[1])

    stream.sendPart('#idletest', stream.nick)

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

    chans = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels',
        dbus_interface=cs.PROPERTIES_IFACE)
    assert len(chans) == 0
def test(q, bus, conn, stream):
    conn.Connect()

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

    alice_handle, bob_handle = conn.get_contact_handles_sync(['alice', 'bob'])

    call_async(q, conn.Requests, 'CreateChannel',
            { CHANNEL_TYPE: CHANNEL_TYPE_TEXT,
              TARGET_HANDLE_TYPE: HT_ROOM,
              TARGET_ID: room })

    q.expect('stream-JOIN')
    event = q.expect('dbus-return', method='CreateChannel')
    path = event.value[0]

    channel = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
        ['Subject2'])

    assertContains(CHANNEL_IFACE_SUBJECT,
        channel.Properties.Get(CHANNEL, 'Interfaces'))

    # No topic set
    subject_props = channel.Properties.GetAll(CHANNEL_IFACE_SUBJECT)
    assertEquals('', subject_props['Subject'])
    assertEquals(0x7fffffffffffffffL, subject_props['Timestamp'])
    assertEquals('', subject_props['Actor'])
    assertEquals(0, subject_props['ActorHandle'])

    # Before the topic arrives from the server, check that our API works okay.
    # FIXME: when we make SetSubject return asynchronously, this will need
    # revising.
    test_can_set(q, stream, channel)

    # We're told the channel's topic, and (in a separte message) who set it and
    # when.
    stream.sendMessage('332', stream.nick, room, ':Test123',
        prefix='idle.test.server')
    stream.sendMessage('333', stream.nick, room, 'bob', '1307802600',
        prefix='idle.test.server')

    # FIXME: signal these together, if possible.
    expect_subject_props_changed(q, { 'Subject': 'Test123' })
    expect_subject_props_changed(q,
        { 'Timestamp': 1307802600,
          'Actor': 'bob',
          'ActorHandle': bob_handle,
        }, exact_timestamp=True)

    # Another user changes the topic.
    stream.sendMessage('TOPIC', room, ':I am as high as a kite',
        prefix='alice')
    expect_subject_props_changed(q,
        { 'Subject': 'I am as high as a kite',
          'Actor': 'alice',
          'ActorHandle': alice_handle,
          'Timestamp': 1234,
        })

    # BIP omits the : for the trailing parameter if it's a single word, make
    # sure we pass that as well
    stream.sendMessage('TOPIC', room, 'badgers!',
        prefix='alice')
    expect_subject_props_changed(q,
        { 'Subject': 'badgers!',
          'Actor': 'alice',
          'ActorHandle': alice_handle,
          'Timestamp': 1234,
        })

    test_can_set(q, stream, channel)

    # Topic is read/write, if we get ops it should stay that way
    forbidden = [
        EventPattern('dbus-signal', signal='PropertiesChanged',
            predicate=lambda e: e.args[0] == CHANNEL_IFACE_SUBJECT)
    ]
    q.forbid_events(forbidden)

    # Set ops, check that t flag becomes a no-op
    change_channel_mode (stream, '+o ' + stream.nick)
    change_channel_mode (stream, '+t')
    change_channel_mode (stream, '-t')
    change_channel_mode (stream, '-o ' + stream.nick)

    # Check that other flags don't cause issues
    change_channel_mode (stream, '+n')
    change_channel_mode (stream, '+n')

    change_channel_mode (stream, '+to ' + stream.nick)
    change_channel_mode (stream, '-to ' + stream.nick)

    sync_stream(q, stream)
    sync_dbus(bus, q, conn)
    q.unforbid_events(forbidden)

    # back to normal?
    test_can_set(q, stream, channel)

    # Check if setting ops gives us write access on +t channels
    change_channel_mode (stream, '+t')
    expect_and_check_can_set(q, channel, False)

    change_channel_mode (stream, '+o ' + stream.nick)
    expect_and_check_can_set(q, channel, True)

    change_channel_mode (stream, '-o ' + stream.nick)
    expect_and_check_can_set(q, channel, False)

    change_channel_mode (stream, '-t')
    expect_and_check_can_set(q, channel, True)

    # And back to normal again ?
    test_can_set(q, stream, channel)

    channel.Subject2.SetSubject('')
    # Verify that we send an empty final parameter ("clear the topic") as
    # opposed to no final parameter ("what is the topic").
    q.expect('stream-TOPIC', data=[room, ''])