def test(q, bus, conn, stream):
    conn.Connect()

    def send_roster_iq(stream, jid, subscription):
        iq = IQ(stream, "set")
        iq['id'] = 'push'
        query = iq.addElement('query')
        query['xmlns'] = ns.ROSTER
        item = query.addElement('item')
        item['jid'] = jid
        item['subscription'] = subscription
        stream.send(iq)

    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    event.stanza['type'] = 'result'

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'none'

    stream.send(event.stanza)

    # FIXME: this is somewhat fragile - it's asserting the exact order that
    # things currently happen in roster.c. In reality the order is not
    # significant
    publish = expect_list_channel(q, bus, conn, 'publish', [])
    subscribe = expect_list_channel(q, bus, conn, 'subscribe', [])
    stored = expect_list_channel(q, bus, conn, 'stored', ['*****@*****.**'])

    stored.Group.RemoveMembers([dbus.UInt32(2)], '')
    send_roster_iq(stream, '*****@*****.**', 'remove')

    acknowledge_iq(stream, q.expect('stream-iq').stanza)
def test(q, bus, conn, stream):
    conn.Connect()

    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    event.stanza['type'] = 'result'

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'

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

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'to'

    stream.send(event.stanza)

    # FIXME: this is somewhat fragile - it's asserting the exact order that
    # things currently happen in roster.c. In reality the order is not
    # significant
    expect_list_channel(q, bus, conn, 'publish',
        ['*****@*****.**', '*****@*****.**'])
    expect_list_channel(q, bus, conn, 'subscribe',
        ['*****@*****.**', '*****@*****.**'])
    expect_list_channel(q, bus, conn, 'stored',
        ['*****@*****.**', '*****@*****.**', '*****@*****.**'])
def test(q, bus, conn, stream):
    conn.Connect()

    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    event.stanza['type'] = 'result'

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'
    group = item.addElement('group', content='women')
    group = item.addElement('group', content='affected-by-fdo-12791')

    # This is a broken roster - Amy appears twice. This should only happen
    # if the server is somehow buggy. This was my initial attempt at
    # reproducing fd.o #12791 - I doubt it's very realistic, but we shouldn't
    # assert, regardless of what input we get!
    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'
    group = item.addElement('group', content='women')

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'from'
    group = item.addElement('group', content='men')

    # This is what was *actually* strange about the #12791 submitter's roster -
    # Bob appears, fully subscribed, but also there's an attempt to subscribe
    # to one of Bob's resources. We now ignore such items
    item = event.query.addElement('item')
    item['jid'] = '[email protected]/Resource'
    item['subscription'] = 'none'
    item['ask'] = 'subscribe'

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'to'
    group = item.addElement('group', content='men')

    stream.send(event.stanza)

    # FIXME: this is somewhat fragile - it's asserting the exact order that
    # things currently happen in roster.c. In reality the order is not
    # significant
    expect_list_channel(q, bus, conn, 'publish',
        ['*****@*****.**', '*****@*****.**'])
    expect_list_channel(q, bus, conn, 'subscribe',
        ['*****@*****.**', '*****@*****.**'])
    expect_list_channel(q, bus, conn, 'stored',
        ['*****@*****.**', '*****@*****.**', '*****@*****.**'])
    _expect_group_channel(q, bus, conn, 'women', ['*****@*****.**'])
    _expect_group_channel(q, bus, conn, 'affected-by-fdo-12791', [])
    _expect_group_channel(q, bus, conn, 'men', ['*****@*****.**', '*****@*****.**'])
def test(q, bus, conn, stream, remove, local):
    conn.Connect()

    # Gabble asks for the roster; the server sends back an empty roster.
    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    event.stanza['type'] = 'result'
    stream.send(event.stanza)

    publish = expect_list_channel(q, bus, conn, 'publish', [])
    subscribe = expect_list_channel(q, bus, conn, 'subscribe', [])
    stored = expect_list_channel(q, bus, conn, 'stored', [])

    h = conn.RequestHandles(cs.HT_CONTACT, [jid])[0]

    # Another client logged into our account (Gajim, say) wants to subscribe to
    # Marco's presence. First, per RFC 3921 it 'SHOULD perform a "roster set"
    # for the new roster item':
    #
    #   <iq type='set'>
    #     <query xmlns='jabber:iq:roster'>
    #       <item jid='*****@*****.**'/>
    #     </query>
    #   </iq>
    #
    # 'As a result, the user's server (1) MUST initiate a roster push for the
    # new roster item to all available resources associated with this user that
    # have requested the roster, setting the 'subscription' attribute to a
    # value of "none"':
    iq = IQ(stream, "set")
    item = iq.addElement((ns.ROSTER, 'query')).addElement('item')
    item['jid'] = jid
    item['subscription'] = 'none'
    stream.send(iq)

    # In response, Gabble should add Marco to stored:
    q.expect('dbus-signal', signal='MembersChanged',
        args=['', [h], [], [], [], 0, 0], path=stored.object_path)

    # Gajim sends a <presence type='subscribe'/> to Marco. 'As a result, the
    # user's server MUST initiate a second roster push to all of the user's
    # available resources that have requested the roster, setting [...]
    # ask='subscribe' attribute in the roster item [for Marco]:
    iq = IQ(stream, "set")
    item = iq.addElement((ns.ROSTER, 'query')).addElement('item')
    item['jid'] = jid
    item['subscription'] = 'none'
    item['ask'] = 'subscribe'
    stream.send(iq)

    # In response, Gabble should add Marco to subscribe:remote-pending:
    q.expect('dbus-signal', signal='MembersChanged',
        args=['', [], [], [], [h], 0, 0],
        path=subscribe.object_path)

    # The user decides that they don't care what Marco's baking after all
    # (maybe they read his blog instead?) and:
    if remove:
        # ...removes him from the roster...
        if local:
            # ...by telling Gabble to remove him from stored.
            stored.Group.RemoveMembers([h], '')

            event = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER)
            item = event.query.firstChildElement()
            assertEquals(jid, item['jid'])
            assertEquals('remove', item['subscription'])
        else:
            # ...using the other client.
            pass

        # The server must 'inform all of the user's available resources that
        # have requested the roster of the roster item removal':
        iq = IQ(stream, "set")
        item = iq.addElement((ns.ROSTER, 'query')).addElement('item')
        item['jid'] = jid
        item['subscription'] = 'remove'
        # When Marco found this bug, this roster update included:
        item['ask'] = 'subscribe'
        # which is a bit weird: I don't think the server should send that when
        # the contact's being removed. I think Gabble should ignore it, so I'm
        # including it in the test.
        stream.send(iq)

        # In response, Gabble should announce that Marco has been removed from
        # subscribe:remote-pending and stored:members:
        q.expect_many(
            EventPattern('dbus-signal', signal='MembersChanged',
                args=['', [], [h], [], [], 0, 0],
                path=subscribe.object_path),
            EventPattern('dbus-signal', signal='MembersChanged',
                args=['', [], [h], [], [], 0, 0],
                path=stored.object_path),
            )
    else:
        # ...rescinds the subscription request...
        if local:
            # ...by telling Gabble to remove him from 'subscribe'.
            subscribe.Group.RemoveMembers([h], '')

            q.expect('stream-presence', to=jid, presence_type='unsubscribe')
        else:
            # ...in the other client.
            pass

        # In response, the server sends a roster update:
        iq = IQ(stream, "set")
        item = iq.addElement((ns.ROSTER, 'query')).addElement('item')
        item['jid'] = jid
        item['subscription'] = 'none'
        # no ask='subscribe' any more.
        stream.send(iq)

        # In response, Gabble should announce that Marco has been removed from
        # subscribe:remote-pending. It shouldn't wait for the <presence
        # type='unsubscribed'/> ack before doing so: empirical tests reveal
        # that it's never delivered.
        q.expect('dbus-signal', signal='MembersChanged',
            args=['', [], [h], [], [], 0, 0],
            path=subscribe.object_path)