def remove_buddy_from_view(stream, id, jid):
    message = create_gadget_message("test@localhost")
    added = message.addElement((ns.OLPC_BUDDY, 'removed'))
    added['id'] = id
    buddy = added.addElement((None, 'buddy'))
    buddy['jid'] = jid
    stream.send(message)
def test(q, bus, conn, stream):
    conn.Connect()

    handles = {}

    _, iq_event, disco_event = q.expect_many(
        EventPattern('dbus-signal', signal='StatusChanged',
            args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]),
        EventPattern('stream-iq', to=None, query_ns='vcard-temp',
            query_name='vCard'),
        EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS))

    acknowledge_iq(stream, iq_event.stanza)

    activity_prop_iface = dbus.Interface(conn,
            'org.laptop.Telepathy.ActivityProperties')
    buddy_prop_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo')
    gadget_iface = dbus.Interface(conn, 'org.laptop.Telepathy.Gadget')
    requests_iface = dbus.Interface(conn, cs.CONN_IFACE_REQUESTS)

    # Gadget was not announced yet
    call_async(q, requests_iface, 'CreateChannel',
        { cs.CHANNEL_TYPE:
            'org.laptop.Telepathy.Channel.Type.ActivityView',
            'org.laptop.Telepathy.Channel.Interface.View.MaxSize': 5,
          })

    event = q.expect('dbus-error', method='CreateChannel')
    announce_gadget(q, stream, disco_event.stanza)
    sync_stream(q, stream)

    # check if we can request Activity views
    properties = conn.GetAll(
        cs.CONN_IFACE_REQUESTS, dbus_interface=dbus.PROPERTIES_IFACE)

    assert ({cs.CHANNEL_TYPE:
            olpc_name_prefix + '.Channel.Type.ActivityView'},

            [olpc_name_prefix + '.Channel.Interface.View.MaxSize',
             olpc_name_prefix + '.Channel.Type.ActivityView.Properties',
             olpc_name_prefix + '.Channel.Type.ActivityView.Participants'],
         ) in properties.get('RequestableChannelClasses'),\
                 properties['RequestableChannelClasses']

    # request 3 random activities (view 1)
    view_path = request_random_activity_view(q, stream, conn, 3, '1',
            [('activity1', '*****@*****.**',
                {'color': ('str', '#005FE4,#00A0FF')},
                [('lucien@localhost', {'color': ('str', '#AABBCC,#CCBBAA')}),
                 ('jean@localhost', {})]),])

    view1 = bus.get_object(conn.bus_name, view_path)

    # check org.freedesktop.Telepathy.Channel D-Bus properties
    props = view1.GetAll(cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE)

    assert props['ChannelType'] == 'org.laptop.Telepathy.Channel.Type.ActivityView'
    assert 'org.laptop.Telepathy.Channel.Interface.View' in props['Interfaces']
    assert props['TargetHandle'] == 0
    assert props['TargetID'] == ''
    assert props['TargetHandleType'] == 0

    # check org.laptop.Telepathy.Channel.Interface.View D-Bus properties
    props = view1.GetAll(
        'org.laptop.Telepathy.Channel.Interface.View',
        dbus_interface=dbus.PROPERTIES_IFACE)

    assert props['MaxSize'] == 3

    # check org.laptop.Telepathy.Channel.Type.ActivityView D-Bus properties
    props = view1.GetAll(
        'org.laptop.Telepathy.Channel.Type.ActivityView',
        dbus_interface=dbus.PROPERTIES_IFACE)

    assert props['Properties'] == {}
    assert props['Participants'] == []

    assert view1.GetChannelType(dbus_interface=cs.CHANNEL) ==\
            'org.laptop.Telepathy.Channel.Type.ActivityView'

    ## Current views ##
    # view 1: activity 1 (with: Lucien, Jean)

    handles['lucien'], handles['jean'] = \
            conn.RequestHandles(1, ['lucien@localhost', 'jean@localhost'])

    event = q.expect('dbus-signal', signal='ActivityPropertiesChanged')
    handles['room1'], props = event.args
    assert conn.InspectHandles(2, [handles['room1']])[0] == \
            '*****@*****.**'
    assert props == {'color': '#005FE4,#00A0FF'}

    q.expect('dbus-signal', signal='ActivitiesChanged',
            interface='org.laptop.Telepathy.Channel.Interface.View',
            args=[[('activity1', handles['room1'])], []])

    # participants are added to view
    q.expect('dbus-signal', signal='BuddiesChanged',
            args=[[handles['lucien'], handles['jean']], []])

    # participants are added to activity
    q.expect_many(
        EventPattern('dbus-signal', signal='ActivitiesChanged',
            interface='org.laptop.Telepathy.BuddyInfo',
            args=[handles['lucien'], [('activity1', handles['room1'])]]),
        EventPattern('dbus-signal', signal='ActivitiesChanged',
            interface='org.laptop.Telepathy.BuddyInfo',
            args=[handles['jean'], [('activity1', handles['room1'])]]))

    # check activities and buddies in view
    check_view(view1, conn, [('activity1', handles['room1'])],
            ['lucien@localhost', 'jean@localhost'])

    # we can now get activity properties
    props = activity_prop_iface.GetProperties(handles['room1'])
    assert props == {'color': '#005FE4,#00A0FF'}

    # and we can get participant's properties too
    props = buddy_prop_iface.GetProperties(handles['lucien'])
    assert props == {'color': '#AABBCC,#CCBBAA'}

    # and their activities
    call_async (q, buddy_prop_iface, 'GetActivities', handles['lucien'])

    event = q.expect('stream-iq', to='lucien@localhost', query_name='pubsub',
            query_ns=ns.PUBSUB)
    # return an error, we can't query pubsub node
    answer_error_to_pubsub_request(stream, event.stanza)

    q.expect('dbus-return', method='GetActivities',
            value=([('activity1', handles['room1'])],))

    # activity search by properties (view 2)
    props = dbus.Dictionary({'color': '#AABBCC,#001122'}, signature='sv')

    call_async(q, requests_iface, 'CreateChannel',
        { cs.CHANNEL_TYPE:
            'org.laptop.Telepathy.Channel.Type.ActivityView',
            'org.laptop.Telepathy.Channel.Interface.View.MaxSize': 5,
            'org.laptop.Telepathy.Channel.Type.ActivityView.Properties': props,
          })

    iq_event, return_event = q.expect_many(
        EventPattern('stream-iq', to='gadget.localhost',
            query_ns=ns.OLPC_ACTIVITY),
        EventPattern('dbus-return', method='CreateChannel'))

    properties_nodes = xpath.queryForNodes('/iq/view/activity/properties',
            iq_event.stanza)
    props = parse_properties(properties_nodes[0])
    assert props == {'color': ('str', '#AABBCC,#001122')}

    view = iq_event.stanza.firstChildElement()
    assert view.name == 'view'
    assert view['id'] == '2'
    assert view['size'] == '5'

    send_reply_to_activity_view_request(stream, iq_event.stanza,
            [('activity2', '*****@*****.**',
                {'color': ('str', '#AABBCC,#001122')}, [])])

    ## Current views ##
    # view 1: activity 1 (with: Lucien, Jean)
    # view 2: activity 2

    view_path = return_event.value[0]
    view2 = bus.get_object(conn.bus_name, view_path)

    props = return_event.value[1]
    assert props['org.laptop.Telepathy.Channel.Type.ActivityView.Properties'] == \
            {'color': '#AABBCC,#001122'}
    assert props['org.laptop.Telepathy.Channel.Type.ActivityView.Participants'] == []

    event = q.expect('dbus-signal', signal='ActivityPropertiesChanged')
    handles['room2'], props = event.args
    assert conn.InspectHandles(2, [handles['room2']])[0] == '*****@*****.**'
    assert props == {'color': '#AABBCC,#001122'}

    q.expect('dbus-signal', signal='ActivitiesChanged',
            interface='org.laptop.Telepathy.Channel.Interface.View',
            args=[[('activity2', handles['room2'])], []])

    act = view2.Get(olpc_name_prefix + '.Channel.Interface.View',
        'Activities', dbus_interface=dbus.PROPERTIES_IFACE)
    assert sorted(act) == [('activity2', handles['room2'])]

    buddies = view2.Get(olpc_name_prefix + '.Channel.Interface.View',
        'Buddies', dbus_interface=dbus.PROPERTIES_IFACE)
    assert buddies == []

    # activity search by participants (view 3)
    participants = conn.RequestHandles(1, ["alice@localhost", "bob@localhost"])

    call_async(q, requests_iface, 'CreateChannel',
        { cs.CHANNEL_TYPE:
            'org.laptop.Telepathy.Channel.Type.ActivityView',
            'org.laptop.Telepathy.Channel.Interface.View.MaxSize': 5,
            'org.laptop.Telepathy.Channel.Type.ActivityView.Participants': participants,
          })

    iq_event, return_event = q.expect_many(
        EventPattern('stream-iq', to='gadget.localhost',
            query_ns=ns.OLPC_ACTIVITY),
        EventPattern('dbus-return', method='CreateChannel'))

    buddies = xpath.queryForNodes('/iq/view/activity/buddy', iq_event.stanza)
    view = iq_event.stanza.firstChildElement()
    assert view.name == 'view'
    assert view['id'] == '3'
    assert view['size'] == '5'
    assert len(buddies) == 2
    assert (buddies[0]['jid'], buddies[1]['jid']) == ('alice@localhost',
            'bob@localhost')

    send_reply_to_activity_view_request(stream, iq_event.stanza,
            [('activity3', '*****@*****.**',
                {'color': ('str', '#AABBCC,#001122')}, [])])

    ## Current views ##
    # view 1: activity 1 (with: Lucien, Jean)
    # view 2: activity 2
    # view 3: activity 3

    view_path = return_event.value[0]
    view3 = bus.get_object(conn.bus_name, view_path)

    event = q.expect('dbus-signal', signal='ActivityPropertiesChanged')
    handles['room3'], props = event.args
    assert conn.InspectHandles(2, [handles['room3']])[0] == '*****@*****.**'
    assert props == {'color': '#AABBCC,#001122'}

    q.expect('dbus-signal', signal='ActivitiesChanged',
            interface='org.laptop.Telepathy.Channel.Interface.View',
            args=[[('activity3', handles['room3'])], []])

    act = view3.Get(olpc_name_prefix + '.Channel.Interface.View',
        'Activities',
        dbus_interface=dbus.PROPERTIES_IFACE)
    assert sorted(act) == [('activity3', handles['room3'])]

    # add activity 4 to view 1
    message = create_gadget_message('alice@localhost')

    added = message.addElement((ns.OLPC_ACTIVITY, 'added'))
    added['id'] = '1'
    activity = added.addElement((None, 'activity'))
    activity['id'] = 'activity4'
    activity['room'] = '*****@*****.**'
    properties = activity.addElement((ns.OLPC_ACTIVITY_PROPS, "properties"))
    for node in properties_to_xml({'color': ('str', '#DDEEDD,#EEDDEE')}):
        properties.addChild(node)
    buddy = activity.addElement((None, 'buddy'))
    buddy['jid'] = 'fernand@localhost'
    properties = buddy.addElement((ns.OLPC_BUDDY_PROPS, "properties"))
    for node in properties_to_xml({'color': ('str', '#AABBAA,#BBAABB')}):
        properties.addChild(node)
    buddy = activity.addElement((None, 'buddy'))
    buddy['jid'] = 'jean@localhost'

    stream.send(message)

    ## Current views ##
    # view 1: activity 1 (with: Lucien, Jean), activity 4 (with: Fernand, Jean)
    # view 2: activity 2
    # view 3: activity 3
    # participants are added to view

    handles['fernand'] = conn.RequestHandles(1, ['fernand@localhost',])[0]

    # activity is added
    event = q.expect('dbus-signal', signal='ActivitiesChanged',
            interface='org.laptop.Telepathy.Channel.Interface.View')
    added, removed = event.args
    assert len(added) == 1
    id, handles['room4'] = added[0]
    assert id == 'activity4'
    assert sorted(conn.InspectHandles(2, [handles['room4']])) == \
            ['*****@*****.**']

    # buddies are added to view
    q.expect('dbus-signal', signal='BuddiesChanged',
            args=[[handles['fernand'], handles['jean']], []])

    # buddies are added to activity
    q.expect_many(
        EventPattern('dbus-signal', signal='ActivitiesChanged',
            interface='org.laptop.Telepathy.BuddyInfo',
            args=[handles['fernand'], [('activity4', handles['room4'])]]),
        EventPattern('dbus-signal', signal='ActivitiesChanged',
            interface='org.laptop.Telepathy.BuddyInfo',
            args=[handles['jean'], [('activity1', handles['room1']),
                ('activity4', handles['room4'])]]))

    # check activities and buddies in view
    check_view(view1, conn, [
        ('activity1', handles['room1']), ('activity4', handles['room4'])],
        ['fernand@localhost', 'lucien@localhost', 'jean@localhost'])

    # check activity's properties
    props = activity_prop_iface.GetProperties(handles['room4'])
    assert props == {'color': '#DDEEDD,#EEDDEE'}

    # Gadget informs us about an activity properties change
    message = create_gadget_message('alice@localhost')

    change = message.addElement((ns.OLPC_ACTIVITY, 'change'))
    change['activity'] = 'activity1'
    change['room'] = '*****@*****.**'
    change['id'] = '1'
    properties = change.addElement((ns.OLPC_ACTIVITY_PROPS, 'properties'))
    for node in properties_to_xml({'tags': ('str', 'game'), \
            'color': ('str', '#AABBAA,#BBAABB')}):
        properties.addChild(node)

    stream.send(message)

    q.expect('dbus-signal', signal='ActivityPropertiesChanged',
            args=[handles['room1'], {'tags': 'game', 'color': '#AABBAA,#BBAABB'}])

    # we now get the new activity properties
    props = activity_prop_iface.GetProperties(handles['room1'])
    assert props == {'tags': 'game', 'color': '#AABBAA,#BBAABB'}

    # Marcel joined activity 1
    message = create_gadget_message('alice@localhost')

    activity = message.addElement((ns.OLPC_ACTIVITY, 'activity'))
    activity['room'] = '*****@*****.**'
    activity['view'] = '1'
    joined = activity.addElement((None, 'joined'))
    joined['jid'] = 'marcel@localhost'
    properties = joined.addElement((ns.OLPC_BUDDY_PROPS, "properties"))
    for node in properties_to_xml({'color': ('str', '#CCCCCC,#DDDDDD')}):
        properties.addChild(node)

    stream.send(message)

    ## Current views ##
    # view 1: activity 1 (with: Lucien, Jean, Marcel), activity 4 (with
    # Fernand, Jean)
    # view 2: activity 2
    # view 3: activity 3

    handles['marcel'] = conn.RequestHandles(1, ['marcel@localhost',])[0]

    q.expect_many(
            EventPattern('dbus-signal', signal='BuddiesChanged',
                args=[[handles['marcel']], []]),
            EventPattern('dbus-signal', signal='PropertiesChanged',
                args=[handles['marcel'], {'color': '#CCCCCC,#DDDDDD'}]),
            EventPattern('dbus-signal', signal='ActivitiesChanged',
                interface='org.laptop.Telepathy.BuddyInfo',
                args=[handles['marcel'], [('activity1', handles['room1'])]]))

    # check activities and buddies in view
    check_view(view1, conn, [
        ('activity1', handles['room1']),('activity4', handles['room4'])],
        ['fernand@localhost', 'lucien@localhost', 'jean@localhost',
            'marcel@localhost'])

    # Marcel left activity 1
    message = create_gadget_message('alice@localhost')

    activity = message.addElement((ns.OLPC_ACTIVITY, 'activity'))
    activity['room'] = '*****@*****.**'
    activity['view'] = '1'
    left = activity.addElement((None, 'left'))
    left['jid'] = 'marcel@localhost'

    stream.send(message)

    ## Current views ##
    # view 1: activity 1 (with: Lucien, Jean), activity 4 (with Fernand, Jean)
    # view 2: activity 2
    # view 3: activity 3

    q.expect_many(
            EventPattern('dbus-signal', signal='BuddiesChanged',
                args=[[], [handles['marcel']]]),
            EventPattern('dbus-signal', signal='ActivitiesChanged',
                interface='org.laptop.Telepathy.BuddyInfo',
                args=[handles['marcel'], []]))

    # check activities and buddies in view
    check_view(view1, conn, [
        ('activity1', handles['room1']),('activity4', handles['room4'])],
        ['fernand@localhost', 'lucien@localhost', 'jean@localhost'])

    # Jean left activity 1
    message = create_gadget_message('alice@localhost')

    activity = message.addElement((ns.OLPC_ACTIVITY, 'activity'))
    activity['room'] = '*****@*****.**'
    activity['view'] = '1'
    left = activity.addElement((None, 'left'))
    left['jid'] = 'jean@localhost'

    stream.send(message)

    ## Current views ##
    # view 1: activity 1 (with: Lucien), activity 4 (with Fernand, Jean)
    # view 2: activity 2
    # view 3: activity 3

    q.expect('dbus-signal', signal='ActivitiesChanged',
            interface='org.laptop.Telepathy.BuddyInfo',
            args=[handles['jean'], [('activity4', handles['room4'])]])

    # Jean wasn't removed from the view as he is still in activity 4
    check_view(view1, conn, [
        ('activity1', handles['room1']),('activity4', handles['room4'])],
        ['fernand@localhost', 'lucien@localhost', 'jean@localhost'])

    # remove activity 1 from view 1
    message = create_gadget_message('alice@localhost')

    removed = message.addElement((ns.OLPC_ACTIVITY, 'removed'))
    removed['id'] = '1'
    activity = removed.addElement((None, 'activity'))
    activity['id'] = 'activity1'
    activity['room'] = '*****@*****.**'

    stream.send(message)

    ## Current views ##
    # view 1: activity 4 (with Jean, Fernand)
    # view 2: activity 2
    # view 3: activity 3

    q.expect_many(
    # participants are removed from the view
            EventPattern('dbus-signal', signal='BuddiesChanged',
                args=[[], [handles['lucien']]]),
    # activity is removed from the view
            EventPattern('dbus-signal', signal='ActivitiesChanged',
                interface='org.laptop.Telepathy.Channel.Interface.View',
                args=[[], [('activity1', handles['room1'])]]),
            EventPattern('dbus-signal', signal='ActivitiesChanged',
                interface='org.laptop.Telepathy.BuddyInfo',
                args=[handles['lucien'], []]))

    # check activities and buddies in view
    check_view(view1, conn, [
        ('activity4', handles['room4'])],
        ['fernand@localhost', 'jean@localhost'])

    # close view 1
    call_async(q, view1, 'Close')
    event_msg, _, _, _ = q.expect_many(
        EventPattern('stream-message', to='gadget.localhost'),
        EventPattern('dbus-return', method='Close'),
        # Jean and Fernand left activity4
        EventPattern('dbus-signal', signal='ActivitiesChanged',
            interface='org.laptop.Telepathy.BuddyInfo',
            args=[handles['jean'], []]),
        EventPattern('dbus-signal', signal='ActivitiesChanged',
            interface='org.laptop.Telepathy.BuddyInfo',
            args=[handles['fernand'], []]))

    close = xpath.queryForNodes('/message/close', event_msg.stanza)
    assert len(close) == 1
    assert close[0]['id'] == '1'

    close_view(q, view2, '2')

    close_view(q, view3, '3')

    # View request without MaxSize property
    call_async(q, requests_iface, 'CreateChannel',
        { cs.CHANNEL_TYPE:
            'org.laptop.Telepathy.Channel.Type.ActivityView',
          })

    event = q.expect('dbus-error', method='CreateChannel')
    assert event.error.get_dbus_name() == cs.INVALID_ARGUMENT

    # test participants and properties search
    props = dbus.Dictionary({'color': '#AABBCC,#001122'}, signature='sv')
    participants = conn.RequestHandles(1, ["alice@localhost", "bob@localhost"])

    call_async(q, requests_iface, 'CreateChannel',
        { cs.CHANNEL_TYPE:
            'org.laptop.Telepathy.Channel.Type.ActivityView',
            'org.laptop.Telepathy.Channel.Interface.View.MaxSize': 5,
            'org.laptop.Telepathy.Channel.Type.ActivityView.Properties': props,
            'org.laptop.Telepathy.Channel.Type.ActivityView.Participants': participants,
          })

    iq_event, return_event = q.expect_many(
        EventPattern('stream-iq', to='gadget.localhost', query_ns=ns.OLPC_ACTIVITY),
        EventPattern('dbus-return', method='CreateChannel'))

    view = iq_event.stanza.firstChildElement()
    assert view.name == 'view'
    assert view['id'] == '4'
    assert view['size'] == '5'

    properties_nodes = xpath.queryForNodes('/iq/view/activity/properties',
            iq_event.stanza)
    props = parse_properties(properties_nodes[0])
    assert props == {'color': ('str', '#AABBCC,#001122')}

    buddies = xpath.queryForNodes('/iq/view/activity/buddy', iq_event.stanza)
    assert len(buddies) == 2
    assert (buddies[0]['jid'], buddies[1]['jid']) == ('alice@localhost',
            'bob@localhost')

    view_path = return_event.value[0]
    props = return_event.value[1]
    view4 = bus.get_object(conn.bus_name, view_path)

    assert props['org.laptop.Telepathy.Channel.Type.ActivityView.Properties'] == \
            dbus.Dictionary({'color': '#AABBCC,#001122'}, signature='sv')
    assert conn.InspectHandles(1, props['org.laptop.Telepathy.Channel.Type.ActivityView.Participants']) == \
            ["alice@localhost", "bob@localhost"]
def test(q, bus, conn, stream):
    conn.Connect()

    _, iq_event, disco_event = q.expect_many(
        EventPattern('dbus-signal', signal='StatusChanged',
            args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]),
        EventPattern('stream-iq', to=None, query_ns='vcard-temp',
            query_name='vCard'),
        EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS))

    acknowledge_iq(stream, iq_event.stanza)

    buddy_info_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo')
    gadget_iface = dbus.Interface(conn, 'org.laptop.Telepathy.Gadget')
    requests_iface = dbus.Interface(conn, tp_name_prefix + '.Connection.Interface.Requests')

    # Gadget was not announced yet
    call_async(q, requests_iface, 'CreateChannel',
        { cs.CHANNEL_TYPE:
            'org.laptop.Telepathy.Channel.Type.BuddyView',
            'org.laptop.Telepathy.Channel.Interface.View.MaxSize': 5,
          })

    event = q.expect('dbus-error', method='CreateChannel')
    announce_gadget(q, stream, disco_event.stanza)
    call_async(q, conn, 'RequestHandles', 1, ['bob@localhost'])

    event = q.expect('dbus-return', method='RequestHandles')
    handles = event.value[0]
    bob_handle = handles[0]

    call_async(q, buddy_info_iface, 'GetProperties', bob_handle)

    # wait for pubsub query
    event = q.expect('stream-iq', to='bob@localhost', query_ns=ns.PUBSUB)
    query = event.stanza
    assert query['to'] == 'bob@localhost'

    # send an error as reply
    reply = IQ(stream, 'error')
    reply['id'] = query['id']
    reply['to'] = 'alice@localhost'
    reply['from'] = 'bob@localhost'
    stream.send(reply)

    # wait for buddy search query
    event = q.expect('stream-iq', to='gadget.localhost',
            query_ns=ns.OLPC_BUDDY)
    buddies = xpath.queryForNodes('/iq/query/buddy', event.stanza)
    assert len(buddies) == 1
    buddy = buddies[0]
    assert buddy['jid'] == 'bob@localhost'

    # send reply to the search query
    reply = make_result_iq(stream, event.stanza)
    reply['from'] = 'gadget.localhost'
    reply['to'] = 'alice@localhost'
    query = xpath.queryForNodes('/iq/query', reply)[0]
    buddy = query.addElement((None, "buddy"))
    buddy['jid'] = 'bob@localhost'
    properties = buddy.addElement((ns.OLPC_BUDDY_PROPS, "properties"))
    for node in properties_to_xml({'color': ('str', '#005FE4,#00A0FF')}):
        properties.addChild(node)
    stream.send(reply)

    event = q.expect('dbus-return', method='GetProperties')
    props = event.value[0]

    assert props == {'color': '#005FE4,#00A0FF' }

    # check if we can request Buddy views
    properties = conn.GetAll(
        cs.CONN_IFACE_REQUESTS, dbus_interface=dbus.PROPERTIES_IFACE)

    assert ({tp_name_prefix + '.Channel.ChannelType':
            olpc_name_prefix + '.Channel.Type.BuddyView'},
            [olpc_name_prefix + '.Channel.Interface.View.MaxSize',
             olpc_name_prefix + '.Channel.Type.BuddyView.Properties',
             olpc_name_prefix + '.Channel.Type.BuddyView.Alias'],
         ) in properties.get('RequestableChannelClasses'),\
                 properties['RequestableChannelClasses']

    # request 3 random buddies
    call_async(q, requests_iface, 'CreateChannel',
        { tp_name_prefix + '.Channel.ChannelType':
            olpc_name_prefix + '.Channel.Type.BuddyView',
          olpc_name_prefix + '.Channel.Interface.View.MaxSize': 3
          })

    iq_event, return_event, new_channels_event, new_channel_event = q.expect_many(
        EventPattern('stream-iq', to='gadget.localhost',
            query_ns=ns.OLPC_BUDDY),
        EventPattern('dbus-return', method='CreateChannel'),
        EventPattern('dbus-signal', signal='NewChannels'),
        EventPattern('dbus-signal', signal='NewChannel'))

    view = iq_event.stanza.firstChildElement()
    assert view.name == 'view'
    assert view['id'] == '1'
    assert view['size'] == '3'

    # reply to random query
    reply = make_result_iq(stream, iq_event.stanza)
    reply['from'] = 'gadget.localhost'
    reply['to'] = 'alice@localhost'
    view = xpath.queryForNodes('/iq/view', reply)[0]
    buddy = view.addElement((None, "buddy"))
    buddy['jid'] = 'charles@localhost'
    properties = buddy.addElement((ns.OLPC_BUDDY_PROPS, "properties"))
    for node in properties_to_xml({'color': ('str', '#AAAAAA,#BBBBBB')}):
        properties.addChild(node)
    buddy = view.addElement((None, "buddy"))
    buddy['jid'] = 'bob@localhost'
    properties = buddy.addElement((ns.OLPC_BUDDY_PROPS, "properties"))
    for node in properties_to_xml({'color': ('str', '#005FE4,#00A0FF')}):
        properties.addChild(node)
    stream.send(reply)

    view_path = return_event.value[0]
    props = return_event.value[1]
    view1 = bus.get_object(conn.bus_name, view_path)

    # check NewChannels arg
    channels = new_channels_event.args[0]
    assert len(channels) == 1
    chan, props_ = channels[0]
    assert chan == view_path
    assert props == props_

    # check NewChannel arg
    chan = new_channel_event.args[0]
    assert chan == view_path

    assert props['org.laptop.Telepathy.Channel.Type.BuddyView.Properties'] == {}
    assert props['org.laptop.Telepathy.Channel.Type.BuddyView.Alias'] == ''

    # check org.freedesktop.Telepathy.Channel D-Bus properties
    props = view1.GetAll(cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE)

    assert props['ChannelType'] == 'org.laptop.Telepathy.Channel.Type.BuddyView'
    assert 'org.laptop.Telepathy.Channel.Interface.View' in props['Interfaces']
    assert props['TargetHandle'] == 0
    assert props['TargetID'] == ''
    assert props['TargetHandleType'] == 0

    # check org.laptop.Telepathy.Channel.Interface.View D-Bus properties
    props = view1.GetAll(
        'org.laptop.Telepathy.Channel.Interface.View',
        dbus_interface=dbus.PROPERTIES_IFACE)

    assert props['MaxSize'] == 3

    # check org.laptop.Telepathy.Channel.Type.BuddyView D-Bus properties
    props = view1.GetAll(
        'org.laptop.Telepathy.Channel.Type.BuddyView',
        dbus_interface=dbus.PROPERTIES_IFACE)

    assert props['Properties'] == {}
    assert props['Alias'] == ''

    assert view1.GetChannelType(dbus_interface=cs.CHANNEL) ==\
            'org.laptop.Telepathy.Channel.Type.BuddyView'

    event = q.expect('dbus-signal', signal='BuddiesChanged')
    added, removed = event.args
    assert removed == []
    assert len(added) == 2
    assert sorted(conn.InspectHandles(1, added)) == ['bob@localhost',
            'charles@localhost']

    event = q.expect('dbus-signal', signal='PropertiesChanged')
    event = q.expect('dbus-signal', signal='PropertiesChanged')

    # we can now get bob's properties
    bob_handle = conn.RequestHandles(1, ['bob@localhost'])[0]
    props = buddy_info_iface.GetProperties(bob_handle)
    assert props == {'color': '#005FE4,#00A0FF'}

    # Bob changed his properties
    message = create_gadget_message("test@localhost")

    change = message.addElement((ns.OLPC_BUDDY, 'change'))
    change['jid'] = 'bob@localhost'
    change['id'] = '1'
    properties = change.addElement((ns.OLPC_BUDDY_PROPS, 'properties'))
    for node in properties_to_xml({'color': ('str', '#FFFFFF,#AAAAAA')}):
        properties.addChild(node)

    stream.send(message)

    event = q.expect('dbus-signal', signal='PropertiesChanged',
            args=[bob_handle, {'color': '#FFFFFF,#AAAAAA'}])

    # we now get the new properties
    props = buddy_info_iface.GetProperties(bob_handle)
    assert props == {'color': '#FFFFFF,#AAAAAA'}

    # buddy search
    props = dbus.Dictionary({'color': '#AABBCC,#001122'}, signature='sv')
    call_async(q, requests_iface, 'CreateChannel',
        { tp_name_prefix + '.Channel.ChannelType':
            olpc_name_prefix + '.Channel.Type.BuddyView',
          olpc_name_prefix + '.Channel.Interface.View.MaxSize': 10,
          olpc_name_prefix + '.Channel.Type.BuddyView.Properties': props
          })

    iq_event, return_event = q.expect_many(
        EventPattern('stream-iq', to='gadget.localhost', query_ns=ns.OLPC_BUDDY),
        EventPattern('dbus-return', method='CreateChannel'))

    properties_node = xpath.queryForNodes('/iq/view/buddy/properties',
            iq_event.stanza)
    props = parse_properties(properties_node[0])
    assert props == {'color': ('str', '#AABBCC,#001122')}

    view = iq_event.stanza.firstChildElement()
    assert view.name == 'view'
    assert view['id'] == '2'
    assert view['size'] == '10'

    # reply to request
    reply = make_result_iq(stream, iq_event.stanza)
    reply['from'] = 'gadget.localhost'
    reply['to'] = 'alice@localhost'
    view = xpath.queryForNodes('/iq/view', reply)[0]
    buddy = view.addElement((None, "buddy"))
    buddy['jid'] = 'charles@localhost'
    properties = buddy.addElement((ns.OLPC_BUDDY_PROPS, "properties"))
    for node in properties_to_xml({'color': ('str', '#AABBCC,#001122')}):
        properties.addChild(node)
    stream.send(reply)

    view_path = return_event.value[0]
    props = return_event.value[1]
    view2 = bus.get_object(conn.bus_name, view_path)

    assert props['org.laptop.Telepathy.Channel.Type.BuddyView.Properties'] == dbus.Dictionary({'color': '#AABBCC,#001122'}, signature='sv')
    assert props['org.laptop.Telepathy.Channel.Type.BuddyView.Alias'] == ''

    # check org.laptop.Telepathy.Channel.Type.BuddyView D-Bus properties
    props = view2.GetAll(
        'org.laptop.Telepathy.Channel.Type.BuddyView',
        dbus_interface=dbus.PROPERTIES_IFACE)

    assert props['Properties'] == {'color': '#AABBCC,#001122'}
    assert props['Alias'] == ''

    event = q.expect('dbus-signal', signal='BuddiesChanged')
    added, removed = event.args
    assert removed == []
    assert len(added) == 1
    handle = added[0]
    assert conn.InspectHandles(1, [handle])[0] == 'charles@localhost'

    event = q.expect('dbus-signal', signal='PropertiesChanged')
    handle, props = event.args
    assert conn.InspectHandles(1, [handle])[0] == 'charles@localhost'
    assert props == {'color': '#AABBCC,#001122'}

    # add a buddy to view 1
    message = create_gadget_message("test@localhost")

    added = message.addElement((ns.OLPC_BUDDY, 'added'))
    added['id'] = '1'
    buddy = added.addElement((None, 'buddy'))
    buddy['jid'] = 'oscar@localhost'
    properties = buddy.addElement((ns.OLPC_BUDDY_PROPS, "properties"))
    for node in properties_to_xml({'color': ('str', '#000000,#AAAAAA')}):
        properties.addChild(node)

    stream.send(message)

    event = q.expect('dbus-signal', signal='BuddiesChanged')
    added, removed = event.args
    assert removed == []
    assert len(added) == 1
    handle = added[0]
    assert conn.InspectHandles(1, added)[0] == 'oscar@localhost'

    members = view1.Get(olpc_name_prefix + '.Channel.Interface.View',
        'Buddies', dbus_interface=dbus.PROPERTIES_IFACE)

    members = sorted(conn.InspectHandles(1, members))
    assert sorted(members) == ['bob@localhost', 'charles@localhost',
            'oscar@localhost']

    # remove a buddy from view 1
    message = create_gadget_message("test@localhost")

    added = message.addElement((ns.OLPC_BUDDY, 'removed'))
    added['id'] = '1'
    buddy = added.addElement((None, 'buddy'))
    buddy['jid'] = 'bob@localhost'

    stream.send(message)

    event = q.expect('dbus-signal', signal='BuddiesChanged')
    added, removed = event.args
    assert added == []
    assert len(removed) == 1
    handle = removed[0]
    assert conn.InspectHandles(1, [handle])[0] == 'bob@localhost'

    members = view1.Get(olpc_name_prefix + '.Channel.Interface.View',
        'Buddies', dbus_interface=dbus.PROPERTIES_IFACE)
    members = sorted(conn.InspectHandles(1, members))
    assert sorted(members) == ['charles@localhost', 'oscar@localhost']

    # test alias search
    call_async(q, requests_iface, 'CreateChannel',
        { tp_name_prefix + '.Channel.ChannelType':
            olpc_name_prefix + '.Channel.Type.BuddyView',
          olpc_name_prefix + '.Channel.Interface.View.MaxSize': 10,
          olpc_name_prefix + '.Channel.Type.BuddyView.Alias': 'tom'
          })


    iq_event, return_event = q.expect_many(
        EventPattern('stream-iq', to='gadget.localhost',
            query_ns=ns.OLPC_BUDDY),
        EventPattern('dbus-return', method='CreateChannel'))

    view = iq_event.stanza.firstChildElement()
    assert view.name == 'view'
    assert view['id'] == '3'
    assert view['size'] == '10'
    buddy = xpath.queryForNodes('/iq/view/buddy', iq_event.stanza)
    assert len(buddy) == 1
    assert buddy[0]['alias'] == 'tom'

    # reply to random query
    reply = make_result_iq(stream, iq_event.stanza)
    reply['from'] = 'gadget.localhost'
    reply['to'] = 'alice@localhost'
    view = xpath.queryForNodes('/iq/view', reply)[0]
    buddy = view.addElement((None, "buddy"))
    buddy['jid'] = 'tom@localhost'
    buddy = view.addElement((None, "buddy"))
    buddy['jid'] = 'thomas@localhost'
    stream.send(reply)

    view_path = return_event.value[0]
    props = return_event.value[1]
    view3 = bus.get_object(conn.bus_name, view_path)

    assert props['org.laptop.Telepathy.Channel.Type.BuddyView.Properties'] == {}
    assert props['org.laptop.Telepathy.Channel.Type.BuddyView.Alias'] == 'tom'

    # check org.laptop.Telepathy.Channel.Type.BuddyView D-Bus properties
    props = view3.GetAll(
        'org.laptop.Telepathy.Channel.Type.BuddyView',
        dbus_interface=dbus.PROPERTIES_IFACE)

    assert props['Properties'] == {}
    assert props['Alias'] == 'tom'

    event = q.expect('dbus-signal', signal='BuddiesChanged')
    added, removed = event.args
    assert removed == []
    assert len(added) == 2
    assert sorted(conn.InspectHandles(1, added)) == ['thomas@localhost',
            'tom@localhost']

    close_view(q, view1, '1')

    close_view(q, view2, '2')

    close_view(q, view3, '3')

    # View request without MaxSize property
    call_async(q, requests_iface, 'CreateChannel',
        { cs.CHANNEL_TYPE:
            'org.laptop.Telepathy.Channel.Type.BuddyView',
          })

    event = q.expect('dbus-error', method='CreateChannel')
    assert event.error.get_dbus_name() == cs.INVALID_ARGUMENT

    # test alias and properties search
    props = dbus.Dictionary({'color': '#AABBCC,#001122'}, signature='sv')
    call_async(q, requests_iface, 'CreateChannel',
        { tp_name_prefix + '.Channel.ChannelType':
            olpc_name_prefix + '.Channel.Type.BuddyView',
          olpc_name_prefix + '.Channel.Interface.View.MaxSize': 5,
          olpc_name_prefix + '.Channel.Type.BuddyView.Properties': props,
          olpc_name_prefix + '.Channel.Type.BuddyView.Alias': 'jean'
          })

    iq_event, return_event = q.expect_many(
        EventPattern('stream-iq', to='gadget.localhost', query_ns=ns.OLPC_BUDDY),
        EventPattern('dbus-return', method='CreateChannel'))

    view = iq_event.stanza.firstChildElement()
    assert view.name == 'view'
    assert view['id'] == '4'
    assert view['size'] == '5'

    properties_node = xpath.queryForNodes('/iq/view/buddy/properties',
            iq_event.stanza)
    props = parse_properties(properties_node[0])
    assert props == {'color': ('str', '#AABBCC,#001122')}

    buddy = xpath.queryForNodes('/iq/view/buddy', iq_event.stanza)
    assert len(buddy) == 1
    assert buddy[0]['alias'] == 'jean'

    view_path = return_event.value[0]
    props = return_event.value[1]
    view4 = bus.get_object(conn.bus_name, view_path)

    assert props['org.laptop.Telepathy.Channel.Type.BuddyView.Properties'] == dbus.Dictionary({'color': '#AABBCC,#001122'}, signature='sv')
    assert props['org.laptop.Telepathy.Channel.Type.BuddyView.Alias'] == 'jean'