• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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