Home | History | Annotate | Download | only in bindings
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.mojo.bindings;
      6 
      7 import android.test.suitebuilder.annotation.SmallTest;
      8 
      9 import org.chromium.base.annotations.SuppressFBWarnings;
     10 import org.chromium.mojo.MojoTestCase;
     11 import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
     12 import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiverWithResponder;
     13 import org.chromium.mojo.system.Core;
     14 import org.chromium.mojo.system.Core.HandleSignals;
     15 import org.chromium.mojo.system.Core.WaitResult;
     16 import org.chromium.mojo.system.Handle;
     17 import org.chromium.mojo.system.MessagePipeHandle;
     18 import org.chromium.mojo.system.MojoResult;
     19 import org.chromium.mojo.system.Pair;
     20 import org.chromium.mojo.system.ResultAnd;
     21 import org.chromium.mojo.system.impl.CoreImpl;
     22 
     23 import java.nio.ByteBuffer;
     24 import java.util.ArrayList;
     25 
     26 /**
     27  * Testing {@link Router}
     28  */
     29 public class RouterTest extends MojoTestCase {
     30 
     31     private MessagePipeHandle mHandle;
     32     private Router mRouter;
     33     private RecordingMessageReceiverWithResponder mReceiver;
     34     private CapturingErrorHandler mErrorHandler;
     35 
     36     /**
     37      * @see MojoTestCase#setUp()
     38      */
     39     @Override
     40     protected void setUp() throws Exception {
     41         super.setUp();
     42         Core core = CoreImpl.getInstance();
     43         Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
     44         mHandle = handles.first;
     45         mRouter = new RouterImpl(handles.second);
     46         mReceiver = new RecordingMessageReceiverWithResponder();
     47         mRouter.setIncomingMessageReceiver(mReceiver);
     48         mErrorHandler = new CapturingErrorHandler();
     49         mRouter.setErrorHandler(mErrorHandler);
     50         mRouter.start();
     51     }
     52 
     53     /**
     54      * Testing sending a message via the router that expected a response.
     55      */
     56     @SmallTest
     57     public void testSendingToRouterWithResponse() {
     58         final int requestMessageType = 0xdead;
     59         final int responseMessageType = 0xbeaf;
     60 
     61         // Sending a message expecting a response.
     62         MessageHeader header = new MessageHeader(requestMessageType,
     63                 MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, 0);
     64         Encoder encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
     65         header.encode(encoder);
     66         mRouter.acceptWithResponder(encoder.getMessage(), mReceiver);
     67         ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(header.getSize());
     68         ResultAnd<MessagePipeHandle.ReadMessageResult> result =
     69                 mHandle.readMessage(receiveBuffer, 0, MessagePipeHandle.ReadFlags.NONE);
     70 
     71         assertEquals(MojoResult.OK, result.getMojoResult());
     72         MessageHeader receivedHeader = new Message(
     73                 receiveBuffer, new ArrayList<Handle>()).asServiceMessage().getHeader();
     74 
     75         assertEquals(header.getType(), receivedHeader.getType());
     76         assertEquals(header.getFlags(), receivedHeader.getFlags());
     77         assertTrue(receivedHeader.getRequestId() != 0);
     78 
     79         // Sending the response.
     80         MessageHeader responseHeader = new MessageHeader(responseMessageType,
     81                 MessageHeader.MESSAGE_IS_RESPONSE_FLAG, receivedHeader.getRequestId());
     82         encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
     83         responseHeader.encode(encoder);
     84         Message responseMessage = encoder.getMessage();
     85         mHandle.writeMessage(responseMessage.getData(), new ArrayList<Handle>(),
     86                 MessagePipeHandle.WriteFlags.NONE);
     87         runLoopUntilIdle();
     88 
     89         assertEquals(1, mReceiver.messages.size());
     90         ServiceMessage receivedResponseMessage = mReceiver.messages.get(0).asServiceMessage();
     91         assertEquals(MessageHeader.MESSAGE_IS_RESPONSE_FLAG,
     92                 receivedResponseMessage.getHeader().getFlags());
     93         assertEquals(responseMessage.getData(), receivedResponseMessage.getData());
     94     }
     95 
     96     /**
     97      * Sends a message to the Router.
     98      *
     99      * @param messageIndex Used when sending multiple messages to indicate the index of this
    100      * message.
    101      * @param requestMessageType The message type to use in the header of the sent message.
    102      * @param requestId The requestId to use in the header of the sent message.
    103      */
    104     private void sendMessageToRouter(int messageIndex, int requestMessageType, int requestId) {
    105         MessageHeader header = new MessageHeader(
    106                 requestMessageType, MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, requestId);
    107         Encoder encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
    108         header.encode(encoder);
    109         Message headerMessage = encoder.getMessage();
    110         mHandle.writeMessage(headerMessage.getData(), new ArrayList<Handle>(),
    111                 MessagePipeHandle.WriteFlags.NONE);
    112         runLoopUntilIdle();
    113 
    114         assertEquals(messageIndex + 1, mReceiver.messagesWithReceivers.size());
    115         Pair<Message, MessageReceiver> receivedMessage =
    116                 mReceiver.messagesWithReceivers.get(messageIndex);
    117         assertEquals(headerMessage.getData(), receivedMessage.first.getData());
    118     }
    119 
    120     /**
    121      * Sends a response message from the Router.
    122      *
    123      * @param messageIndex Used when sending responses to multiple messages to indicate the index
    124      * of the message that this message is a response to.
    125      * @param responseMessageType The message type to use in the header of the response message.
    126      */
    127     private void sendResponseFromRouter(int messageIndex, int responseMessageType) {
    128         Pair<Message, MessageReceiver> receivedMessage =
    129                 mReceiver.messagesWithReceivers.get(messageIndex);
    130 
    131         long requestId = receivedMessage.first.asServiceMessage().getHeader().getRequestId();
    132 
    133         MessageHeader responseHeader = new MessageHeader(
    134                 responseMessageType, MessageHeader.MESSAGE_IS_RESPONSE_FLAG, requestId);
    135         Encoder encoder = new Encoder(CoreImpl.getInstance(), responseHeader.getSize());
    136         responseHeader.encode(encoder);
    137         Message message = encoder.getMessage();
    138         receivedMessage.second.accept(message);
    139 
    140         ByteBuffer receivedResponseMessage = ByteBuffer.allocateDirect(responseHeader.getSize());
    141         ResultAnd<MessagePipeHandle.ReadMessageResult> result =
    142                 mHandle.readMessage(receivedResponseMessage, 0, MessagePipeHandle.ReadFlags.NONE);
    143 
    144         assertEquals(MojoResult.OK, result.getMojoResult());
    145         assertEquals(message.getData(), receivedResponseMessage);
    146     }
    147 
    148     /**
    149      * Clears {@code mReceiver.messagesWithReceivers} allowing all message receivers to be
    150      * finalized.
    151      * <p>
    152      * Since there is no way to force the Garbage Collector to actually call finalize and we want to
    153      * test the effects of the finalize() method, we explicitly call finalize() on all of the
    154      * message receivers. We do this in a custom thread to better approximate what the JVM does.
    155      */
    156     private void clearAllMessageReceivers() {
    157         Thread myFinalizerThread = new Thread() {
    158             @Override
    159             @SuppressFBWarnings("FI_EXPLICIT_INVOCATION")
    160             public void run() {
    161                 for (Pair<Message, MessageReceiver> receivedMessage :
    162                         mReceiver.messagesWithReceivers) {
    163                     RouterImpl.ResponderThunk thunk =
    164                             (RouterImpl.ResponderThunk) receivedMessage.second;
    165                     try {
    166                         thunk.finalize();
    167                     } catch (Throwable e) {
    168                         throw new RuntimeException(e);
    169                     }
    170                 }
    171             }
    172         };
    173         myFinalizerThread.start();
    174         try {
    175             myFinalizerThread.join();
    176         } catch (InterruptedException e) {
    177             // ignore.
    178         }
    179         mReceiver.messagesWithReceivers.clear();
    180     }
    181 
    182     /**
    183      * Testing receiving a message via the router that expected a response.
    184      */
    185     @SmallTest
    186     public void testReceivingViaRouterWithResponse() {
    187         final int requestMessageType = 0xdead;
    188         final int responseMessageType = 0xbeef;
    189         final int requestId = 0xdeadbeaf;
    190 
    191         // Send a message expecting a response.
    192         sendMessageToRouter(0, requestMessageType, requestId);
    193 
    194         // Sending the response.
    195         sendResponseFromRouter(0, responseMessageType);
    196     }
    197 
    198     /**
    199      * Tests that if a callback is dropped (i.e. becomes unreachable and is finalized
    200      * without being used), then the message pipe will be closed.
    201      */
    202     @SmallTest
    203     public void testDroppingReceiverWithoutUsingIt() {
    204         // Send 10 messages to the router without sending a response.
    205         for (int i = 0; i < 10; i++) {
    206             sendMessageToRouter(i, i, i);
    207         }
    208 
    209         // Now send the 10 responses. This should work fine.
    210         for (int i = 0; i < 10; i++) {
    211             sendResponseFromRouter(i, i);
    212         }
    213 
    214         // Clear all MessageRecievers so that the ResponderThunks will
    215         // be finalized.
    216         clearAllMessageReceivers();
    217 
    218         // Send another  message to the router without sending a response.
    219         sendMessageToRouter(0, 0, 0);
    220 
    221         // Clear the MessageReciever so that the ResponderThunk will
    222         // be finalized. Since the RespondeThunk was never used, this
    223         // should close the pipe.
    224         clearAllMessageReceivers();
    225         // The close() occurs asynchronously on this thread.
    226         runLoopUntilIdle();
    227 
    228         // Confirm that the pipe was closed on the Router side.
    229         HandleSignals closedFlag = HandleSignals.none().setPeerClosed(true);
    230         WaitResult result = mHandle.wait(closedFlag, 0);
    231         assertEquals(MojoResult.OK, result.getMojoResult());
    232         assertEquals(closedFlag, result.getHandleSignalsState().getSatisfiedSignals());
    233     }
    234 }
    235