Example #1
0
def test_buggy_done_handler_is_logged():
    def on_done(req):
        undefined_var

    with MyRPCProcessor() as rpc:
        # First for a request
        request = RPCRequestOut('add_async', [1], on_done)
        rpc.send_request(request)
        request.cancel()
        with rpc.expect_asyncio_log('Exception in callback'):
            rpc.yield_to_loop()

    with MyRPCProcessor() as rpc:
        # Now a request in a batch
        batch = RPCBatchOut()
        batch.add_request('add_async', [2], on_done)
        rpc.send_batch(batch)
        batch.items[0].cancel()
        with rpc.expect_asyncio_log('Exception in callback'):
            rpc.yield_to_loop()

    with MyRPCProcessor() as rpc:
        # Finally the batch itself
        batch = RPCBatchOut()
        batch.add_request('add_async', [1])
        rpc.send_batch(batch, on_done)
        batch.cancel()
        with rpc.expect_asyncio_log('Exception in callback'):
            rpc.yield_to_loop()
Example #2
0
def test_direct_set_etc():
    loop = asyncio.get_event_loop()

    done = set()

    def on_done(request):
        done.add(request.request_id)

    # Send 4 requests
    requests = [
        RPCRequestOut('some_call', [1], on_done),
        RPCRequestOut('some_call', [2], on_done),
        RPCRequestOut('some_call', [3], on_done),
        RPCRequestOut('some_call', [4], on_done),
    ]

    with MyRPCProcessor() as rpc:
        for request in requests:
            rpc.send_request(request)

        # Interfere with all apart from one
        requests[0].set_result(6)
        response = RPCResponse(7, requests[1].request_id)
        rpc.message_received(rpc.protocol.response_message(response))
        requests[2].cancel()
        requests[3].set_exception(ValueError())
        rpc.yield_to_loop()  # Process done functions
        assert done == {requests[n].request_id for n in range(len(requests))}
        assert not rpc.requests
Example #3
0
def test_bad_reponses():
    def handle_add(request):
        assert request.method == 'add'
        assert request.args == [1, 5, 10]
        assert request.result() == 16
        handled.append(request.method)

    handled = []
    requests = [
        RPCRequestOut('add', [1, 5, 10], handle_add),
    ]

    with MyRPCProcessor() as rpc:
        # Send each request and feed them back into the RPC object as if
        # it were receiving its own messages.
        for request in requests:
            # Send it and fake receiving it
            rpc.send_request(request)
            rpc.message_received(rpc.responses.pop())

        # Now process the queue and the async jobs, generating queued responses
        rpc.process_all()

        # Get the queued responses and send them back to ourselves
        response, = rpc.consume_responses()
        assert rpc.all_done()

        # Send response twice.
        rpc.message_received(response)
        rpc.yield_to_loop()
        assert handled == ['add']
        assert not rpc.debug_messages
        rpc.message_received(response)
        # Handler NOT called twice
        assert handled == ['add']
        rpc.expect_debug_message('response to unsent')
        rpc.debug_clear()
        # Send a response with no ID
        rpc.message_received(
            rpc.protocol.response_message(RPCResponse(6, None)))
        rpc.expect_debug_message('missing id')

        # Responses are handled synchronously so no process_all() is needed
        assert rpc.all_done()
Example #4
0
def test_request_round_trip():
    '''Round trip test - we send binary requests to ourselves, process the
    requests, send binary responses in response also to ourselves, and then
    process the results.

    This tests request and response serialization, and also that request
    handlers are invoked when a response is received.

    The tests cover a range of valid and invalid requests, and problem
    triggers.

    We also insert a fake duplicate response, and a fake response without
    a request ID, to test they are appropriately handled.
    '''
    handled = []

    def handle_add(request):
        assert request.method == 'add'
        if request.args[1] == "a":
            result = request.exception()
            assert isinstance(result, RPCError)
            assert result.code == -1
            assert "numbers" in result.message
            handled.append('add_bad')
        else:
            result = request.result()
            assert request.args == [1, 5, 10]
            assert result == 16
            handled.append(request.method)

    def handle_add_async(request):
        assert request.method == 'add_async'
        if request.args[0] == "b":
            result = request.exception()
            assert isinstance(result, RPCError)
            assert result.code == -1
            assert "numbers" in result.message
            handled.append('add_async_bad')
        else:
            result = request.result()
            assert request.args == [1, 5, 10]
            assert result == 16
            handled.append(request.method)

    def handle_echo(request):
        assert request.method == 'echo'
        assert request.args[0] == request.result()
        handled.append(request.method)

    def handle_bad_echo(request):
        assert request.method == 'echo'
        assert not request.args
        result = request.exception()
        assert isinstance(result, RPCError)
        assert result.code == rpc.protocol.INVALID_ARGS
        handled.append('bad_echo')

    def bad_request_handler(request):
        assert request.method == 'bad_request'
        result = request.exception()
        assert isinstance(result, RPCError)
        assert result.code == rpc.protocol.INTERNAL_ERROR
        handled.append(request.method)

    null_handler_request = RPCRequestOut('echo', [2], None)
    requests = [
        RPCRequestOut('add', [1, 5, 10], handle_add),
        # Bad type
        RPCRequestOut('add', [1, "a", 10], handle_add),
        # Test a notification, and a bad one
        RPCRequestOut('echo', ["ping"], handle_echo),
        RPCRequest('echo', [], None),
        # Test a None response
        RPCRequestOut('echo', [None], handle_echo),
        # Throw in an async request
        RPCRequestOut('add_async', [1, 5, 10], handle_add_async),
        RPCRequestOut('add_async', ["b"], handle_add_async),
        # An invalid request
        RPCRequestOut('echo', [], handle_bad_echo),
        # test a null handler
        null_handler_request,
        # test a remote bad request getter
        RPCRequestOut('bad_request', [], bad_request_handler),
    ]

    with MyRPCProcessor() as rpc:
        # Send each request and feed them back into the RPC object as if
        # it were receiving its own messages.
        for request in requests:
            # Send it and fake receiving it
            rpc.send_request(request)
            rpc.message_received(rpc.responses.pop())
        assert rpc.sent_count == len(requests)

        # Check all_requests
        assert isinstance(rpc.all_requests(), list)
        assert set(rpc.all_requests()) == set(
            req for req in requests if isinstance(req, RPCRequestOut))

        # Now process the queue and the async jobs, generating queued responses
        rpc.process_all()

        # Get the queued responses and send them back to ourselves
        responses = rpc.consume_responses()
        assert rpc.all_done()

        # Did we did get the null handler response - no other way to detect it
        text = f'"id": {null_handler_request.request_id}'.encode()
        assert any(text in response for response in responses)

        for response in responses:
            rpc.message_received(response)
        rpc.yield_to_loop()

        # Nothing left
        assert not rpc.all_requests()

        assert sorted(handled) == [
            'add', 'add_async', 'add_async_bad', 'add_bad', 'bad_echo',
            'bad_request', 'echo', 'echo'
        ]

        # Responses are handled synchronously so no process_all() is needed
        assert rpc.all_done()
Example #5
0
def test_RPCRequest():
    request = RPCRequestOut('method', [1], None)
    assert request.method == 'method'
    assert request.args == [1]
    assert request.request_id == 0
    assert not request.is_notification()
    assert repr(request) == "RPCRequest('method', [1], 0)"
    request = RPCRequestOut('method', (1, ), None)
    assert request.method == 'method'
    assert request.args == (1, )
    assert request.request_id == 1
    assert not request.is_notification()
    assert repr(request) == "RPCRequest('method', (1,), 1)"
    request = RPCRequestOut('foo', {"bar": 1}, None)
    assert request.method == 'foo'
    assert request.args == {"bar": 1}
    assert request.request_id == 2
    assert not request.is_notification()
    assert repr(request) == "RPCRequest('foo', {'bar': 1}, 2)"
    request = RPCRequest('foo', [], None)
    assert request.method == 'foo'
    assert request.args == []
    assert request.request_id is None
    assert request.is_notification()
    assert repr(request) == "RPCRequest('foo', [], None)"
    request = RPCRequest('foo', (), None)
    assert request.method == 'foo'
    assert request.args == ()
    assert request.request_id is None
    assert request.is_notification()
    assert repr(request) == "RPCRequest('foo', (), None)"
    # Check {} is preserved (different call semantics)
    for request in [RPCRequest('add', {}, 0), RPCRequestOut('add', {}, None)]:
        assert request.method == 'add'
        assert request.args == {}
        request = RPCRequest('add', {}, 0)
    # Check None gives []
    for request in [
            RPCRequest('add', None, 0),
            RPCRequestOut('add', None, None)
    ]:
        request = RPCRequest('add', None, 0)
        assert request.method == 'add'
        assert request.args == []

    loop = asyncio.get_event_loop()
    # Result setting
    valid = False

    def on_done(req):
        nonlocal valid
        valid = req is request

    # Test args good type
    with pytest.raises(ValueError):
        RPCRequestOut('method', 0, on_done)

    request = RPCRequestOut('method', [0], on_done)
    assert not request.done()
    with pytest.raises(asyncio.InvalidStateError):
        request.result()
    assert not request.done()
    request.set_result(42)

    assert not valid  # Not scheduled yet
    loop.run_until_complete(asyncio.sleep(0))
    assert valid

    assert request.result() == 42
    with pytest.raises(asyncio.InvalidStateError):
        request.set_result(35)
    assert request.result() == 42

    # Result setting
    request = RPCRequestOut('method', None, on_done)
    loop.call_later(0.001, request.set_result, 45)
    valid = False
    assert loop.run_until_complete(request) == 45
    assert valid
    assert request.result() == 45

    request = RPCRequestOut('method', None, on_done)
    loop = asyncio.get_event_loop()
    request.set_result(46)
    assert loop.run_until_complete(request) == 46
Example #6
0
def test_odd_calls():
    handled = []

    def expect(answer, request):
        result = request.result()
        if result == answer:
            handled.append(request.method)

    def error(text, request):
        result = request.exception()
        if isinstance(result, RPCError) and text in result.message:
            handled.append(text)

    requests = [
        # Gives code coverage of notify func
        RPCRequestOut('notify', [1, 2, 3], partial(expect, 6)),
        RPCRequestOut('add_many', [1, "b"], partial(error, 'numbers')),
        RPCRequestOut('add_many', [], partial(error, 'requires 1')),
        RPCRequestOut('add_many', [1], partial(expect, 1)),
        RPCRequestOut('add_many', [5, 50, 500], partial(expect, 555)),
        RPCRequestOut('add_many', list(range(10)), partial(expect, 45)),
        RPCRequestOut('add_many', {'first': 1}, partial(expect, 1)),
        RPCRequestOut('add_many', {
            'first': 1,
            'second': 10
        }, partial(expect, 11)),
        RPCRequestOut('add_many', {
            'first': 1,
            'values': []
        }, partial(error, 'values')),
        RPCRequestOut('pow', [2, 3], partial(expect, 8)),
        RPCRequestOut('pow', [2, 3, 5], partial(expect, 3)),
        RPCRequestOut('pow', {
            "x": 2,
            "y": 3
        }, partial(error, 'cannot be called')),
        RPCRequestOut('echo_2', ['ping'], partial(expect, ['ping', 2])),
        RPCRequestOut('echo_2', ['ping', 'pong'], partial(error, 'at most 1')),
        RPCRequestOut('echo_2', {
            'first': 1,
            'second': 8
        }, partial(expect, [1, 8])),
        RPCRequestOut('echo_2', {
            'first': 1,
            'second': 8,
            '3rd': 1
        }, partial(error, '3rd')),
        RPCRequestOut('kwargs', [], partial(error, 'requires 1')),
        RPCRequestOut('kwargs', [1], partial(expect, 1)),
        RPCRequestOut('kwargs', [1, 2], partial(expect, 2)),
        RPCRequestOut('kwargs', {'end': 4}, partial(error, "start")),
        RPCRequestOut('kwargs', {'start': 3}, partial(expect, 3)),
        RPCRequestOut('kwargs', {
            'start': 3,
            'end': 1,
            '3rd': 1
        }, partial(error, '3rd')),
        RPCRequestOut('both', [], partial(expect, 2)),
        RPCRequestOut('both', [1], partial(expect, 1)),
        RPCRequestOut('both', [5, 2], partial(expect, 15)),
        RPCRequestOut('both', {'end': 4}, partial(expect, 6)),
        RPCRequestOut('both', {'start': 3}, partial(expect, 3)),
        RPCRequestOut('both', {
            'start': 3,
            'end': 1,
            '3rd': 1
        }, partial(expect, 11)),
    ]

    with MyRPCProcessor() as rpc:
        # Send each request and feed them back into the RPC object as if
        # it were receiving its own messages.
        for request in requests:
            rpc.send_request(request)
            rpc.message_received(rpc.responses.pop())

        # Now process the queue and the async jobs, generating queued responses
        rpc.process_all()

        # Get the queued responses and send them back to ourselves
        responses = rpc.consume_responses()
        assert rpc.all_done()

        for response in responses:
            rpc.message_received(response)

        rpc.yield_to_loop()
        assert len(handled) == len(requests)

        assert rpc.all_done()
Example #7
0
def test_outgoing_request_cancellation_and_setting():
    '''Tests cancelling requests.'''
    called = 0

    def on_done(req):
        nonlocal called
        called += 1

    with MyRPCProcessor() as rpc:
        request = RPCRequestOut('add_async', [1], on_done)
        rpc.send_request(request)
        assert request.request_id in rpc.requests
        request.cancel()
        rpc.yield_to_loop()
        assert called == 1
        assert request.cancelled()
        assert not rpc.requests
        with pytest.raises(asyncio.CancelledError):
            request.result()

        request = RPCRequestOut('add_async', [1], on_done)
        rpc.send_request(request)
        assert request.request_id in rpc.requests
        request.set_result(4)
        rpc.yield_to_loop()
        assert called == 2
        assert request.result() == 4
        assert not rpc.requests

        request = RPCRequestOut('add_async', [1], on_done)
        rpc.send_request(request)
        assert request.request_id in rpc.requests
        request.set_exception(ValueError())
        rpc.yield_to_loop()
        assert called == 3
        assert isinstance(request.exception(), ValueError)
        with pytest.raises(ValueError):
            request.result()
        assert not rpc.requests