blob: 6aa1726b4c79c0f4ed38270a683dc30c1d78993d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.mojo.bindings;
import android.support.test.filters.SmallTest;
import org.chromium.base.annotations.SuppressFBWarnings;
import org.chromium.mojo.MojoTestCase;
import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiverWithResponder;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.Core.HandleSignals;
import org.chromium.mojo.system.Handle;
import org.chromium.mojo.system.MessagePipeHandle;
import org.chromium.mojo.system.MojoResult;
import org.chromium.mojo.system.Pair;
import org.chromium.mojo.system.ResultAnd;
import org.chromium.mojo.system.impl.CoreImpl;
import java.nio.ByteBuffer;
import java.util.ArrayList;
/**
* Testing {@link Router}
*/
public class RouterTest extends MojoTestCase {
private MessagePipeHandle mHandle;
private Router mRouter;
private RecordingMessageReceiverWithResponder mReceiver;
private CapturingErrorHandler mErrorHandler;
/**
* @see MojoTestCase#setUp()
*/
@Override
protected void setUp() throws Exception {
super.setUp();
Core core = CoreImpl.getInstance();
Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
mHandle = handles.first;
mRouter = new RouterImpl(handles.second);
mReceiver = new RecordingMessageReceiverWithResponder();
mRouter.setIncomingMessageReceiver(mReceiver);
mErrorHandler = new CapturingErrorHandler();
mRouter.setErrorHandler(mErrorHandler);
mRouter.start();
}
/**
* Testing sending a message via the router that expected a response.
*/
@SmallTest
public void testSendingToRouterWithResponse() {
final int requestMessageType = 0xdead;
final int responseMessageType = 0xbeaf;
// Sending a message expecting a response.
MessageHeader header = new MessageHeader(requestMessageType,
MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, 0);
Encoder encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
header.encode(encoder);
mRouter.acceptWithResponder(encoder.getMessage(), mReceiver);
ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(header.getSize());
ResultAnd<MessagePipeHandle.ReadMessageResult> result =
mHandle.readMessage(receiveBuffer, 0, MessagePipeHandle.ReadFlags.NONE);
assertEquals(MojoResult.OK, result.getMojoResult());
MessageHeader receivedHeader = new Message(
receiveBuffer, new ArrayList<Handle>()).asServiceMessage().getHeader();
assertEquals(header.getType(), receivedHeader.getType());
assertEquals(header.getFlags(), receivedHeader.getFlags());
assertTrue(receivedHeader.getRequestId() != 0);
// Sending the response.
MessageHeader responseHeader = new MessageHeader(responseMessageType,
MessageHeader.MESSAGE_IS_RESPONSE_FLAG, receivedHeader.getRequestId());
encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
responseHeader.encode(encoder);
Message responseMessage = encoder.getMessage();
mHandle.writeMessage(responseMessage.getData(), new ArrayList<Handle>(),
MessagePipeHandle.WriteFlags.NONE);
runLoopUntilIdle();
assertEquals(1, mReceiver.messages.size());
ServiceMessage receivedResponseMessage = mReceiver.messages.get(0).asServiceMessage();
assertEquals(MessageHeader.MESSAGE_IS_RESPONSE_FLAG,
receivedResponseMessage.getHeader().getFlags());
assertEquals(responseMessage.getData(), receivedResponseMessage.getData());
}
/**
* Sends a message to the Router.
*
* @param messageIndex Used when sending multiple messages to indicate the index of this
* message.
* @param requestMessageType The message type to use in the header of the sent message.
* @param requestId The requestId to use in the header of the sent message.
*/
private void sendMessageToRouter(int messageIndex, int requestMessageType, int requestId) {
MessageHeader header = new MessageHeader(
requestMessageType, MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, requestId);
Encoder encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
header.encode(encoder);
Message headerMessage = encoder.getMessage();
mHandle.writeMessage(headerMessage.getData(), new ArrayList<Handle>(),
MessagePipeHandle.WriteFlags.NONE);
runLoopUntilIdle();
assertEquals(messageIndex + 1, mReceiver.messagesWithReceivers.size());
Pair<Message, MessageReceiver> receivedMessage =
mReceiver.messagesWithReceivers.get(messageIndex);
assertEquals(headerMessage.getData(), receivedMessage.first.getData());
}
/**
* Sends a response message from the Router.
*
* @param messageIndex Used when sending responses to multiple messages to indicate the index
* of the message that this message is a response to.
* @param responseMessageType The message type to use in the header of the response message.
*/
private void sendResponseFromRouter(int messageIndex, int responseMessageType) {
Pair<Message, MessageReceiver> receivedMessage =
mReceiver.messagesWithReceivers.get(messageIndex);
long requestId = receivedMessage.first.asServiceMessage().getHeader().getRequestId();
MessageHeader responseHeader = new MessageHeader(
responseMessageType, MessageHeader.MESSAGE_IS_RESPONSE_FLAG, requestId);
Encoder encoder = new Encoder(CoreImpl.getInstance(), responseHeader.getSize());
responseHeader.encode(encoder);
Message message = encoder.getMessage();
receivedMessage.second.accept(message);
ByteBuffer receivedResponseMessage = ByteBuffer.allocateDirect(responseHeader.getSize());
ResultAnd<MessagePipeHandle.ReadMessageResult> result =
mHandle.readMessage(receivedResponseMessage, 0, MessagePipeHandle.ReadFlags.NONE);
assertEquals(MojoResult.OK, result.getMojoResult());
assertEquals(message.getData(), receivedResponseMessage);
}
/**
* Clears {@code mReceiver.messagesWithReceivers} allowing all message receivers to be
* finalized.
* <p>
* Since there is no way to force the Garbage Collector to actually call finalize and we want to
* test the effects of the finalize() method, we explicitly call finalize() on all of the
* message receivers. We do this in a custom thread to better approximate what the JVM does.
*/
private void clearAllMessageReceivers() {
Thread myFinalizerThread = new Thread() {
@Override
@SuppressFBWarnings("FI_EXPLICIT_INVOCATION")
public void run() {
for (Pair<Message, MessageReceiver> receivedMessage :
mReceiver.messagesWithReceivers) {
RouterImpl.ResponderThunk thunk =
(RouterImpl.ResponderThunk) receivedMessage.second;
try {
thunk.finalize();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
};
myFinalizerThread.start();
try {
myFinalizerThread.join();
} catch (InterruptedException e) {
// ignore.
}
mReceiver.messagesWithReceivers.clear();
}
/**
* Testing receiving a message via the router that expected a response.
*/
@SmallTest
public void testReceivingViaRouterWithResponse() {
final int requestMessageType = 0xdead;
final int responseMessageType = 0xbeef;
final int requestId = 0xdeadbeaf;
// Send a message expecting a response.
sendMessageToRouter(0, requestMessageType, requestId);
// Sending the response.
sendResponseFromRouter(0, responseMessageType);
}
/**
* Tests that if a callback is dropped (i.e. becomes unreachable and is finalized
* without being used), then the message pipe will be closed.
*/
@SmallTest
public void testDroppingReceiverWithoutUsingIt() {
// Send 10 messages to the router without sending a response.
for (int i = 0; i < 10; i++) {
sendMessageToRouter(i, i, i);
}
// Now send the 10 responses. This should work fine.
for (int i = 0; i < 10; i++) {
sendResponseFromRouter(i, i);
}
// Clear all MessageRecievers so that the ResponderThunks will
// be finalized.
clearAllMessageReceivers();
// Send another message to the router without sending a response.
sendMessageToRouter(0, 0, 0);
// Clear the MessageReciever so that the ResponderThunk will
// be finalized. Since the RespondeThunk was never used, this
// should close the pipe.
clearAllMessageReceivers();
// The close() occurs asynchronously on this thread.
runLoopUntilIdle();
// Confirm that the pipe was closed on the Router side.
HandleSignals closedFlag = HandleSignals.none().setPeerClosed(true);
assertEquals(closedFlag, mHandle.querySignalsState().getSatisfiedSignals());
}
}