Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
FlutterEngineGroupComponentTest.java
Go to the documentation of this file.
1// Copyright 2013 The Flutter 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
5package io.flutter.embedding.engine;
6
7import static org.junit.Assert.assertEquals;
8import static org.junit.Assert.assertTrue;
9import static org.mockito.Mockito.any;
10import static org.mockito.Mockito.doAnswer;
11import static org.mockito.Mockito.doReturn;
12import static org.mockito.Mockito.eq;
13import static org.mockito.Mockito.isNull;
14import static org.mockito.Mockito.mock;
15import static org.mockito.Mockito.nullable;
16import static org.mockito.Mockito.spy;
17import static org.mockito.Mockito.times;
18import static org.mockito.Mockito.verify;
19import static org.mockito.Mockito.when;
20
21import android.content.Context;
22import android.content.res.AssetManager;
23import androidx.test.core.app.ApplicationProvider;
24import androidx.test.ext.junit.runners.AndroidJUnit4;
25import io.flutter.FlutterInjector;
26import io.flutter.embedding.engine.dart.DartExecutor.DartEntrypoint;
27import io.flutter.embedding.engine.loader.FlutterLoader;
28import io.flutter.embedding.engine.systemchannels.NavigationChannel;
29import io.flutter.plugin.platform.PlatformViewsController;
30import io.flutter.plugins.GeneratedPluginRegistrant;
31import java.util.ArrayList;
32import java.util.List;
33import org.junit.After;
34import org.junit.Before;
35import org.junit.Test;
36import org.junit.runner.RunWith;
37import org.mockito.Mock;
38import org.mockito.MockitoAnnotations;
39import org.robolectric.annotation.Config;
40
41// It's a component test because it tests the FlutterEngineGroup its components such as the
42// FlutterEngine and the DartExecutor.
43@Config(manifest = Config.NONE)
44@RunWith(AndroidJUnit4.class)
46 private final Context ctx = ApplicationProvider.getApplicationContext();
48 @Mock FlutterLoader mockFlutterLoader;
51 boolean jniAttached;
52
53 @Before
54 public void setUp() {
56
57 MockitoAnnotations.openMocks(this);
58 jniAttached = false;
59 when(mockFlutterJNI.isAttached()).thenAnswer(invocation -> jniAttached);
60 doAnswer(invocation -> jniAttached = true).when(mockFlutterJNI).attachToNative();
61 GeneratedPluginRegistrant.clearRegisteredEngines();
62
63 when(mockFlutterLoader.findAppBundlePath()).thenReturn("some/path/to/flutter_assets");
64
65 FlutterJNI.Factory jniFactory =
66 new FlutterJNI.Factory() {
67 @Override
68 public FlutterJNI provideFlutterJNI() {
69 // The default implementation is that `new FlutterJNI()` will report errors when
70 // creating the engine later,
71 // Change mockFlutterJNI to the default so that we can create the engine directly in
72 // later tests
73 return mockFlutterJNI;
74 }
75 };
76
79 .setFlutterLoader(mockFlutterLoader)
80 .setFlutterJNIFactory(jniFactory)
81 .build());
82
83 firstEngineUnderTest =
84 spy(
85 new FlutterEngine(
86 ctx,
87 mock(FlutterLoader.class),
88 mockFlutterJNI,
89 /*dartVmArgs=*/ new String[] {},
90 /*automaticallyRegisterPlugins=*/ false));
91 engineGroupUnderTest =
92 new FlutterEngineGroup(ctx) {
93 @Override
94 FlutterEngine createEngine(
95 Context context,
96 PlatformViewsController platformViewsController,
97 boolean automaticallyRegisterPlugins,
98 boolean waitForRestorationData) {
99 return firstEngineUnderTest;
100 }
101 };
102 }
103
104 @After
105 public void tearDown() {
106 GeneratedPluginRegistrant.clearRegisteredEngines();
107 engineGroupUnderTest = null;
108 firstEngineUnderTest = null;
109 }
110
111 @Test
113 FlutterEngine firstEngine =
114 engineGroupUnderTest.createAndRunEngine(ctx, mock(DartEntrypoint.class));
115 assertEquals(1, engineGroupUnderTest.activeEngines.size());
116
117 firstEngine.destroy();
118 assertEquals(0, engineGroupUnderTest.activeEngines.size());
119 }
120
121 @Test
122 public void canRecreateEngines() {
123 FlutterEngine firstEngine =
124 engineGroupUnderTest.createAndRunEngine(ctx, mock(DartEntrypoint.class));
125 assertEquals(1, engineGroupUnderTest.activeEngines.size());
126
127 firstEngine.destroy();
128 assertEquals(0, engineGroupUnderTest.activeEngines.size());
129
130 FlutterEngine secondEngine =
131 engineGroupUnderTest.createAndRunEngine(ctx, mock(DartEntrypoint.class));
132 assertEquals(1, engineGroupUnderTest.activeEngines.size());
133 // They happen to be equal in our test since we mocked it to be so.
134 assertEquals(firstEngine, secondEngine);
135 }
136
137 @Test
138 public void canSpawnMoreEngines() {
139 FlutterEngine firstEngine =
140 engineGroupUnderTest.createAndRunEngine(ctx, mock(DartEntrypoint.class));
141 assertEquals(1, engineGroupUnderTest.activeEngines.size());
142
143 doReturn(mock(FlutterEngine.class))
144 .when(firstEngine)
145 .spawn(
146 any(Context.class),
147 any(DartEntrypoint.class),
148 nullable(String.class),
149 nullable(List.class),
150 any(PlatformViewsController.class),
151 any(Boolean.class),
152 any(Boolean.class));
153
154 FlutterEngine secondEngine =
155 engineGroupUnderTest.createAndRunEngine(ctx, mock(DartEntrypoint.class));
156 assertEquals(2, engineGroupUnderTest.activeEngines.size());
157
158 firstEngine.destroy();
159 assertEquals(1, engineGroupUnderTest.activeEngines.size());
160
161 // Now the second spawned engine is the only one left and it will be called to spawn the next
162 // engine in the chain.
163 when(secondEngine.spawn(
164 any(Context.class),
165 any(DartEntrypoint.class),
166 nullable(String.class),
167 nullable(List.class),
168 any(PlatformViewsController.class),
169 any(Boolean.class),
170 any(Boolean.class)))
171 .thenReturn(mock(FlutterEngine.class));
172
173 FlutterEngine thirdEngine =
174 engineGroupUnderTest.createAndRunEngine(ctx, mock(DartEntrypoint.class));
175 assertEquals(2, engineGroupUnderTest.activeEngines.size());
176 }
177
178 @Test
180 FlutterEngine firstEngine =
181 engineGroupUnderTest.createAndRunEngine(
182 ctx,
183 new DartEntrypoint(
184 FlutterInjector.instance().flutterLoader().findAppBundlePath(),
185 "other entrypoint"));
186 assertEquals(1, engineGroupUnderTest.activeEngines.size());
187 verify(mockFlutterJNI, times(1))
188 .runBundleAndSnapshotFromLibrary(
189 eq("some/path/to/flutter_assets"),
190 eq("other entrypoint"),
191 isNull(),
192 any(AssetManager.class),
193 nullable(List.class));
194 }
195
196 @Test
198 when(firstEngineUnderTest.getNavigationChannel()).thenReturn(mock(NavigationChannel.class));
199
200 FlutterEngine firstEngine =
201 engineGroupUnderTest.createAndRunEngine(ctx, mock(DartEntrypoint.class), "/foo");
202 assertEquals(1, engineGroupUnderTest.activeEngines.size());
203 verify(firstEngine.getNavigationChannel(), times(1)).setInitialRoute("/foo");
204
205 when(mockFlutterJNI.isAttached()).thenReturn(true);
206 jniAttached = false;
207 FlutterJNI secondMockFlutterJNI = mock(FlutterJNI.class);
208 when(secondMockFlutterJNI.isAttached()).thenAnswer(invocation -> jniAttached);
209 doAnswer(invocation -> jniAttached = true).when(secondMockFlutterJNI).attachToNative();
210 doReturn(secondMockFlutterJNI)
211 .when(mockFlutterJNI)
212 .spawn(
213 nullable(String.class),
214 nullable(String.class),
215 nullable(String.class),
216 nullable(List.class));
217
218 FlutterEngine secondEngine =
219 engineGroupUnderTest.createAndRunEngine(ctx, mock(DartEntrypoint.class), "/bar");
220
221 assertEquals(2, engineGroupUnderTest.activeEngines.size());
222 verify(mockFlutterJNI, times(1))
223 .spawn(nullable(String.class), nullable(String.class), eq("/bar"), nullable(List.class));
224 }
225
226 @Test
228 List<String> firstDartEntrypointArgs = new ArrayList<String>();
229 FlutterEngine firstEngine =
230 engineGroupUnderTest.createAndRunEngine(
232 .setDartEntrypoint(mock(DartEntrypoint.class))
233 .setDartEntrypointArgs(firstDartEntrypointArgs));
234 assertEquals(1, engineGroupUnderTest.activeEngines.size());
235 verify(mockFlutterJNI, times(1))
236 .runBundleAndSnapshotFromLibrary(
237 nullable(String.class),
238 nullable(String.class),
239 isNull(),
240 any(AssetManager.class),
241 eq(firstDartEntrypointArgs));
242
243 when(mockFlutterJNI.isAttached()).thenReturn(true);
244 jniAttached = false;
245 FlutterJNI secondMockFlutterJNI = mock(FlutterJNI.class);
246 when(secondMockFlutterJNI.isAttached()).thenAnswer(invocation -> jniAttached);
247 doAnswer(invocation -> jniAttached = true).when(secondMockFlutterJNI).attachToNative();
248 doReturn(secondMockFlutterJNI)
249 .when(mockFlutterJNI)
250 .spawn(
251 nullable(String.class),
252 nullable(String.class),
253 nullable(String.class),
254 nullable(List.class));
255 List<String> secondDartEntrypointArgs = new ArrayList<String>();
256 FlutterEngine secondEngine =
257 engineGroupUnderTest.createAndRunEngine(
259 .setDartEntrypoint(mock(DartEntrypoint.class))
260 .setDartEntrypointArgs(secondDartEntrypointArgs));
261
262 assertEquals(2, engineGroupUnderTest.activeEngines.size());
263 verify(mockFlutterJNI, times(1))
264 .spawn(
265 nullable(String.class),
266 nullable(String.class),
267 nullable(String.class),
268 eq(secondDartEntrypointArgs));
269 }
270
271 @Test
273 // Create a new FlutterEngineGroup because the first engine created in engineGroupUnderTest was
274 // changed to firstEngineUnderTest in `setUp()`, so can't use it to validate params.
275 FlutterEngineGroup engineGroup = new FlutterEngineGroup(ctx);
276
277 PlatformViewsController controller = new PlatformViewsController();
278 boolean waitForRestorationData = true;
279 boolean automaticallyRegisterPlugins = true;
280
281 when(FlutterInjector.instance().flutterLoader().automaticallyRegisterPlugins())
282 .thenReturn(true);
283 assertTrue(FlutterInjector.instance().flutterLoader().automaticallyRegisterPlugins());
284 assertEquals(0, GeneratedPluginRegistrant.getRegisteredEngines().size());
285
286 FlutterEngine firstEngine =
287 engineGroup.createAndRunEngine(
289 .setDartEntrypoint(mock(DartEntrypoint.class))
290 .setPlatformViewsController(controller)
291 .setWaitForRestorationData(waitForRestorationData)
292 .setAutomaticallyRegisterPlugins(automaticallyRegisterPlugins));
293
294 assertEquals(1, GeneratedPluginRegistrant.getRegisteredEngines().size());
295 assertEquals(controller, firstEngine.getPlatformViewsController());
296 assertEquals(
297 waitForRestorationData, firstEngine.getRestorationChannel().waitForRestorationData);
298 }
299
300 @Test
302 FlutterEngine firstEngine =
303 engineGroupUnderTest.createAndRunEngine(
304 new FlutterEngineGroup.Options(ctx).setDartEntrypoint(mock(DartEntrypoint.class)));
305 assertEquals(1, engineGroupUnderTest.activeEngines.size());
306 verify(mockFlutterJNI, times(1))
307 .runBundleAndSnapshotFromLibrary(
308 nullable(String.class),
309 nullable(String.class),
310 isNull(),
311 any(AssetManager.class),
312 nullable(List.class));
313
314 when(mockFlutterJNI.isAttached()).thenReturn(true);
315 jniAttached = false;
316 FlutterJNI secondMockFlutterJNI = mock(FlutterJNI.class);
317 when(secondMockFlutterJNI.isAttached()).thenAnswer(invocation -> jniAttached);
318 doAnswer(invocation -> jniAttached = true).when(secondMockFlutterJNI).attachToNative();
319 doReturn(secondMockFlutterJNI)
320 .when(mockFlutterJNI)
321 .spawn(
322 nullable(String.class),
323 nullable(String.class),
324 nullable(String.class),
325 nullable(List.class));
326
327 PlatformViewsController controller = new PlatformViewsController();
328 boolean waitForRestorationData = false;
329 boolean automaticallyRegisterPlugins = false;
330
331 when(FlutterInjector.instance().flutterLoader().automaticallyRegisterPlugins())
332 .thenReturn(true);
333 assertTrue(FlutterInjector.instance().flutterLoader().automaticallyRegisterPlugins());
334 assertEquals(0, GeneratedPluginRegistrant.getRegisteredEngines().size());
335
336 FlutterEngine secondEngine =
337 engineGroupUnderTest.createAndRunEngine(
339 .setDartEntrypoint(mock(DartEntrypoint.class))
340 .setWaitForRestorationData(waitForRestorationData)
341 .setPlatformViewsController(controller)
342 .setAutomaticallyRegisterPlugins(automaticallyRegisterPlugins));
343
344 assertEquals(
345 waitForRestorationData, secondEngine.getRestorationChannel().waitForRestorationData);
346 assertEquals(controller, secondEngine.getPlatformViewsController());
347 assertEquals(0, GeneratedPluginRegistrant.getRegisteredEngines().size());
348 }
349}
static SkISize times(const SkISize &size, float factor)
static bool eq(const SkM44 &a, const SkM44 &b, float tol)
Definition M44Test.cpp:18
Builder setFlutterLoader(@NonNull FlutterLoader flutterLoader)
Builder setFlutterJNIFactory(@NonNull FlutterJNI.Factory factory)
static void setInstance(@NonNull FlutterInjector injector)
Options setDartEntrypointArgs(List< String > dartEntrypointArgs)
Options setPlatformViewsController( @NonNull PlatformViewsController platformViewsController)
Options setWaitForRestorationData(boolean waitForRestorationData)
Options setDartEntrypoint(DartEntrypoint dartEntrypoint)
Options setAutomaticallyRegisterPlugins(boolean automaticallyRegisterPlugins)
FlutterEngine createAndRunEngine( @NonNull Context context, @Nullable DartEntrypoint dartEntrypoint)
PlatformViewsController getPlatformViewsController()
FlutterEngine spawn( @NonNull Context context, @NonNull DartEntrypoint dartEntrypoint, @Nullable String initialRoute, @Nullable List< String > dartEntrypointArgs, @Nullable PlatformViewsController platformViewsController, boolean automaticallyRegisterPlugins, boolean waitForRestorationData)