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 setUp()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 testSendingToRouterWithResponse()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 */ sendMessageToRouter(int messageIndex, int requestMessageType, int requestId)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 */ sendResponseFromRouter(int messageIndex, int responseMessageType)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 */ clearAllMessageReceivers()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 testReceivingViaRouterWithResponse()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 testDroppingReceiverWithoutUsingIt()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