Flutter Engine
The Flutter Engine
DartMessengerTest.java
Go to the documentation of this file.
1package io.flutter.embedding.engine.dart;
2
3import static android.os.Looper.getMainLooper;
4import static junit.framework.TestCase.assertEquals;
5import static junit.framework.TestCase.assertNotNull;
6import static junit.framework.TestCase.assertTrue;
7import static org.junit.Assert.assertArrayEquals;
8import static org.mockito.ArgumentMatchers.any;
9import static org.mockito.ArgumentMatchers.anyInt;
10import static org.mockito.ArgumentMatchers.eq;
11import static org.mockito.Mockito.mock;
12import static org.mockito.Mockito.never;
13import static org.mockito.Mockito.times;
14import static org.mockito.Mockito.verify;
15import static org.robolectric.Shadows.shadowOf;
16
17import androidx.test.ext.junit.runners.AndroidJUnit4;
18import io.flutter.embedding.engine.FlutterJNI;
19import io.flutter.embedding.engine.dart.DartMessenger.DartMessengerTaskQueue;
20import io.flutter.plugin.common.BinaryMessenger;
21import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler;
22import java.nio.ByteBuffer;
23import java.util.LinkedList;
24import java.util.Random;
25import java.util.concurrent.CountDownLatch;
26import java.util.concurrent.ExecutorService;
27import java.util.concurrent.Executors;
28import org.junit.Test;
29import org.junit.runner.RunWith;
30import org.mockito.ArgumentCaptor;
31import org.mockito.Mockito;
32import org.robolectric.annotation.Config;
33
34@Config(manifest = Config.NONE)
35@RunWith(AndroidJUnit4.class)
36public class DartMessengerTest {
37 SynchronousTaskQueue synchronousTaskQueue = new SynchronousTaskQueue();
38
39 private static class ReportingUncaughtExceptionHandler
40 implements Thread.UncaughtExceptionHandler {
41 public Throwable latestException;
42
43 @Override
44 public void uncaughtException(Thread t, Throwable e) {
45 latestException = e;
46 }
47 }
48
49 private static class SynchronousTaskQueue implements DartMessengerTaskQueue {
50 public void dispatch(Runnable runnable) {
51 runnable.run();
52 }
53 }
54
55 @Test
56 public void itHandlesErrors() {
57 // Setup test.
58 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
59 final Thread currentThread = Thread.currentThread();
60 final Thread.UncaughtExceptionHandler savedHandler =
61 currentThread.getUncaughtExceptionHandler();
62 final ReportingUncaughtExceptionHandler reportingHandler =
63 new ReportingUncaughtExceptionHandler();
64 currentThread.setUncaughtExceptionHandler(reportingHandler);
65
66 // Create object under test.
67 final DartMessenger messenger =
68 new DartMessenger(fakeFlutterJni, (options) -> synchronousTaskQueue);
69 final BinaryMessageHandler throwingHandler = mock(BinaryMessageHandler.class);
70 Mockito.doThrow(AssertionError.class)
71 .when(throwingHandler)
72 .onMessage(any(ByteBuffer.class), any(DartMessenger.Reply.class));
73 BinaryMessenger.TaskQueue taskQueue = messenger.makeBackgroundTaskQueue();
74 messenger.setMessageHandler("test", throwingHandler, taskQueue);
75 messenger.handleMessageFromDart("test", ByteBuffer.allocate(0), 0, 0);
76 assertNotNull(reportingHandler.latestException);
77 assertTrue(reportingHandler.latestException instanceof AssertionError);
78 currentThread.setUncaughtExceptionHandler(savedHandler);
79 }
80
81 @Test
82 public void givesDirectByteBuffer() {
83 // Setup test.
84 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
85 final DartMessenger messenger =
86 new DartMessenger(fakeFlutterJni, (options) -> synchronousTaskQueue);
87 final String channel = "foobar";
88 final boolean[] wasDirect = {false};
89 final BinaryMessenger.BinaryMessageHandler handler =
90 (message, reply) -> {
91 wasDirect[0] = message.isDirect();
92 };
93 BinaryMessenger.TaskQueue taskQueue = messenger.makeBackgroundTaskQueue();
94 messenger.setMessageHandler(channel, handler, taskQueue);
95 final ByteBuffer message = ByteBuffer.allocateDirect(4 * 2);
96 message.rewind();
97 message.putChar('a');
98 message.putChar('b');
99 message.putChar('c');
100 message.putChar('d');
101 messenger.handleMessageFromDart(channel, message, /*replyId=*/ 123, 0);
102 assertTrue(wasDirect[0]);
103 }
104
105 @Test
107 // Setup test.
108 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
109 final DartMessenger messenger =
110 new DartMessenger(fakeFlutterJni, (options) -> synchronousTaskQueue);
111 final String channel = "foobar";
112 final ByteBuffer[] byteBuffers = {null};
113 final int bufferSize = 4 * 2;
114 final BinaryMessenger.BinaryMessageHandler handler =
115 (message, reply) -> {
116 byteBuffers[0] = message;
117 assertEquals(bufferSize, byteBuffers[0].limit());
118 };
119 BinaryMessenger.TaskQueue taskQueue = messenger.makeBackgroundTaskQueue();
120 messenger.setMessageHandler(channel, handler, taskQueue);
121 final ByteBuffer message = ByteBuffer.allocateDirect(bufferSize);
122 message.rewind();
123 message.putChar('a');
124 message.putChar('b');
125 message.putChar('c');
126 message.putChar('d');
127 messenger.handleMessageFromDart(channel, message, /*replyId=*/ 123, 0);
128 assertNotNull(byteBuffers[0]);
129 assertTrue(byteBuffers[0].isDirect());
130 assertEquals(0, byteBuffers[0].limit());
131 }
132
133 @Test
135 // Setup test.
136 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
137 final DartMessenger messenger = new DartMessenger(fakeFlutterJni);
138 final ByteBuffer message = ByteBuffer.allocateDirect(4 * 2);
139 final String channel = "foobar";
140 message.rewind();
141 message.putChar('a');
142 message.putChar('b');
143 message.putChar('c');
144 message.putChar('d');
145 final ByteBuffer[] byteBuffers = {null};
146 BinaryMessenger.BinaryReply callback =
147 (reply) -> {
148 assertTrue(reply.isDirect());
149 byteBuffers[0] = reply;
150 };
151 messenger.send(channel, null, callback);
153 assertEquals(0, byteBuffers[0].limit());
154 }
155
156 @Test
158 /// Setup test.
159 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
160 final DartMessenger messenger = new DartMessenger(fakeFlutterJni);
161 final String channel = "foobar";
162 messenger.send(channel, null, null);
163 verify(fakeFlutterJni, times(1)).dispatchEmptyPlatformMessage(eq("foobar"), eq(1));
164 messenger.send(channel, null, null);
165 verify(fakeFlutterJni, times(1)).dispatchEmptyPlatformMessage(eq("foobar"), eq(2));
166 }
167
168 @Test
169 public void cleansUpMessageData() throws InterruptedException {
170 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
171 final DartMessenger messenger =
172 new DartMessenger(fakeFlutterJni, (options) -> synchronousTaskQueue);
173 BinaryMessenger.TaskQueue taskQueue = messenger.makeBackgroundTaskQueue();
174 String channel = "foobar";
175 BinaryMessenger.BinaryMessageHandler handler =
176 (ByteBuffer message, BinaryMessenger.BinaryReply reply) -> {
177 reply.reply(null);
178 };
179 messenger.setMessageHandler(channel, handler, taskQueue);
180 final ByteBuffer message = ByteBuffer.allocateDirect(4 * 2);
181 final int replyId = 1;
182 final long messageData = 1234;
183 messenger.handleMessageFromDart(channel, message, replyId, messageData);
184 verify(fakeFlutterJni).cleanupMessageData(eq(messageData));
185 }
186
187 @Test
188 public void cleansUpMessageDataOnError() throws InterruptedException {
189 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
190 final DartMessenger messenger =
191 new DartMessenger(fakeFlutterJni, (options) -> synchronousTaskQueue);
192 BinaryMessenger.TaskQueue taskQueue = messenger.makeBackgroundTaskQueue();
193 String channel = "foobar";
194 BinaryMessenger.BinaryMessageHandler handler =
195 (ByteBuffer message, BinaryMessenger.BinaryReply reply) -> {
196 throw new RuntimeException("hello");
197 };
198 messenger.setMessageHandler(channel, handler, taskQueue);
199 final ByteBuffer message = ByteBuffer.allocateDirect(4 * 2);
200 final int replyId = 1;
201 final long messageData = 1234;
202
203 messenger.handleMessageFromDart(channel, message, replyId, messageData);
204 verify(fakeFlutterJni).cleanupMessageData(eq(messageData));
205 }
206
207 @Test
208 public void emptyResponseWhenHandlerIsNotSet() throws InterruptedException {
209 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
210 final DartMessenger messenger =
211 new DartMessenger(fakeFlutterJni, (options) -> synchronousTaskQueue);
212 final String channel = "foobar";
213 final ByteBuffer message = ByteBuffer.allocateDirect(4 * 2);
214 final int replyId = 1;
215 final long messageData = 1234;
216
217 messenger.handleMessageFromDart(channel, message, replyId, messageData);
218 shadowOf(getMainLooper()).idle();
219 verify(fakeFlutterJni).invokePlatformMessageEmptyResponseCallback(replyId);
220 }
221
222 @Test
223 public void buffersResponseWhenHandlerIsNotSet() throws InterruptedException {
224 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
225 final DartMessenger messenger =
226 new DartMessenger(fakeFlutterJni, (options) -> synchronousTaskQueue);
227 final BinaryMessenger.TaskQueue taskQueue = messenger.makeBackgroundTaskQueue();
228 final String channel = "foobar";
229 final ByteBuffer message = ByteBuffer.allocateDirect(4 * 2);
230 final int replyId = 1;
231 final long messageData = 1234;
232
233 messenger.enableBufferingIncomingMessages();
234 messenger.handleMessageFromDart(channel, message, replyId, messageData);
235
236 shadowOf(getMainLooper()).idle();
237 verify(fakeFlutterJni, never()).invokePlatformMessageEmptyResponseCallback(eq(replyId));
238
239 final BinaryMessenger.BinaryMessageHandler handler =
240 (ByteBuffer msg, BinaryMessenger.BinaryReply reply) -> {
241 reply.reply(ByteBuffer.wrap("done".getBytes()));
242 };
243 messenger.setMessageHandler(channel, handler, taskQueue);
244
245 shadowOf(getMainLooper()).idle();
246 verify(fakeFlutterJni, never()).invokePlatformMessageEmptyResponseCallback(eq(replyId));
247
248 final ArgumentCaptor<ByteBuffer> response = ArgumentCaptor.forClass(ByteBuffer.class);
249 verify(fakeFlutterJni)
250 .invokePlatformMessageResponseCallback(anyInt(), response.capture(), anyInt());
251 assertArrayEquals("done".getBytes(), response.getValue().array());
252 }
253
254 @Test
256 throws InterruptedException {
257 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
258 final DartMessenger messenger =
259 new DartMessenger(fakeFlutterJni, (options) -> synchronousTaskQueue);
260 final String channel = "foobar";
261 final ByteBuffer message = ByteBuffer.allocateDirect(4 * 2);
262 final int replyId = 1;
263 final long messageData = 1234;
264
265 messenger.enableBufferingIncomingMessages();
266 messenger.handleMessageFromDart(channel, message, replyId, messageData);
267 shadowOf(getMainLooper()).idle();
268 verify(fakeFlutterJni, never()).invokePlatformMessageEmptyResponseCallback(replyId);
269
271 shadowOf(getMainLooper()).idle();
272 verify(fakeFlutterJni).invokePlatformMessageEmptyResponseCallback(replyId);
273 }
274
275 @Test
276 public void emptyResponseWhenHandlerIsUnregistered() throws InterruptedException {
277 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
278 final DartMessenger messenger =
279 new DartMessenger(fakeFlutterJni, (options) -> synchronousTaskQueue);
280 final BinaryMessenger.TaskQueue taskQueue = messenger.makeBackgroundTaskQueue();
281 final String channel = "foobar";
282 final ByteBuffer message = ByteBuffer.allocateDirect(4 * 2);
283 final int replyId = 1;
284 final long messageData = 1234;
285
286 messenger.enableBufferingIncomingMessages();
287 messenger.handleMessageFromDart(channel, message, replyId, messageData);
288
289 shadowOf(getMainLooper()).idle();
290 verify(fakeFlutterJni, never()).invokePlatformMessageEmptyResponseCallback(eq(replyId));
291
292 final BinaryMessenger.BinaryMessageHandler handler =
293 (ByteBuffer msg, BinaryMessenger.BinaryReply reply) -> {
294 reply.reply(ByteBuffer.wrap("done".getBytes()));
295 };
296 messenger.setMessageHandler(channel, handler, taskQueue);
297
298 shadowOf(getMainLooper()).idle();
299 verify(fakeFlutterJni, never()).invokePlatformMessageEmptyResponseCallback(eq(replyId));
300
301 final ArgumentCaptor<ByteBuffer> response = ArgumentCaptor.forClass(ByteBuffer.class);
302 verify(fakeFlutterJni)
303 .invokePlatformMessageResponseCallback(anyInt(), response.capture(), anyInt());
304 assertArrayEquals("done".getBytes(), response.getValue().array());
305
307 messenger.setMessageHandler(channel, null, null); // Unregister handler.
308
309 messenger.handleMessageFromDart(channel, message, replyId, messageData);
310 shadowOf(getMainLooper()).idle();
311 verify(fakeFlutterJni).invokePlatformMessageEmptyResponseCallback(replyId);
312 }
313
314 @Test
315 public void testSerialTaskQueue() throws InterruptedException {
316 final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
317 final DartMessenger messenger = new DartMessenger(fakeFlutterJni);
318 final ExecutorService taskQueuePool = Executors.newFixedThreadPool(4);
319 final DartMessengerTaskQueue taskQueue = new DartMessenger.SerialTaskQueue(taskQueuePool);
320 final int count = 5000;
321 final LinkedList<Integer> ints = new LinkedList<>();
322 Random rand = new Random();
323 for (int i = 0; i < count; ++i) {
324 final int value = i;
325 taskQueue.dispatch(
326 () -> {
327 try {
328 Thread.sleep(rand.nextInt(10));
329 } catch (InterruptedException ex) {
330 System.out.println(ex.toString());
331 }
332 ints.add(value);
333 });
334 taskQueuePool.execute(
335 () -> {
336 // Add some extra noise to make sure we aren't always handling on the same thread.
337 try {
338 Thread.sleep(rand.nextInt(10));
339 } catch (InterruptedException ex) {
340 System.out.println(ex.toString());
341 }
342 });
343 }
344 CountDownLatch latch = new CountDownLatch(1);
345 taskQueue.dispatch(
346 () -> {
347 latch.countDown();
348 });
349 latch.await();
350 assertEquals(count, ints.size());
351 for (int i = 0; i < count - 1; ++i) {
352 assertEquals((int) ints.get(i), (int) (ints.get(i + 1)) - 1);
353 }
354 }
355}
static SkISize times(const SkISize &size, float factor)
const char * options
int count
Definition: FontMgrTest.cpp:50
static bool eq(const SkM44 &a, const SkM44 &b, float tol)
Definition: M44Test.cpp:18
void setMessageHandler( @NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler)
void handleMessageFromDart( @NonNull String channel, @Nullable ByteBuffer message, int replyId, long messageData)
TaskQueue makeBackgroundTaskQueue(TaskQueueOptions options)
void send(@NonNull String channel, @NonNull ByteBuffer message)
void handlePlatformMessageResponse(int replyId, @Nullable ByteBuffer reply)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
uint8_t value
Win32Message message
SIT bool any(const Vec< 1, T > &x)
Definition: SkVx.h:530