Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
FlutterFragmentTest.java
Go to the documentation of this file.
1package io.flutter.embedding.android;
2
3import static org.junit.Assert.assertArrayEquals;
4import static org.junit.Assert.assertEquals;
5import static org.junit.Assert.assertFalse;
6import static org.junit.Assert.assertNotNull;
7import static org.junit.Assert.assertNull;
8import static org.junit.Assert.assertTrue;
9import static org.mockito.ArgumentMatchers.any;
10import static org.mockito.Mockito.doAnswer;
11import static org.mockito.Mockito.mock;
12import static org.mockito.Mockito.never;
13import static org.mockito.Mockito.spy;
14import static org.mockito.Mockito.times;
15import static org.mockito.Mockito.verify;
16import static org.mockito.Mockito.when;
17
18import android.content.Context;
19import androidx.activity.OnBackPressedCallback;
20import androidx.fragment.app.FragmentActivity;
21import androidx.test.core.app.ApplicationProvider;
22import androidx.test.ext.junit.runners.AndroidJUnit4;
23import io.flutter.embedding.engine.FlutterEngine;
24import io.flutter.embedding.engine.FlutterEngineCache;
25import io.flutter.embedding.engine.FlutterJNI;
26import io.flutter.embedding.engine.loader.FlutterLoader;
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.concurrent.atomic.AtomicBoolean;
30import org.junit.Test;
31import org.junit.runner.RunWith;
32import org.robolectric.Robolectric;
33import org.robolectric.annotation.Config;
34
35@Config(manifest = Config.NONE)
36@RunWith(AndroidJUnit4.class)
37public class FlutterFragmentTest {
38 private final Context ctx = ApplicationProvider.getApplicationContext();
40
53
54 @Test
57 TestDelegateFactory delegateFactory =
59 fragment.setDelegateFactory(delegateFactory);
60
61 assertEquals("main", fragment.getDartEntrypointFunctionName());
62 assertNull(fragment.getDartEntrypointLibraryUri());
63 assertNull(fragment.getDartEntrypointArgs());
64 assertEquals("/", fragment.getInitialRoute());
65 assertArrayEquals(new String[] {}, fragment.getFlutterShellArgs().toArray());
66 assertTrue(fragment.shouldAttachEngineToActivity());
67 assertFalse(fragment.shouldHandleDeeplinking());
68 assertNull(fragment.getCachedEngineId());
69 assertTrue(fragment.shouldDestroyEngineWithHost());
70 assertEquals(RenderMode.surface, fragment.getRenderMode());
71 assertEquals(TransparencyMode.transparent, fragment.getTransparencyMode());
72 assertFalse(fragment.shouldDelayFirstAndroidViewDraw());
73 }
74
75 @Test
77 FlutterFragment fragment =
79 .dartEntrypoint("custom_entrypoint")
80 .dartLibraryUri("package:foo/bar.dart")
81 .dartEntrypointArgs(new ArrayList<String>(Arrays.asList("foo", "bar")))
82 .initialRoute("/custom/route")
83 .shouldAttachEngineToActivity(false)
84 .handleDeeplinking(true)
85 .renderMode(RenderMode.texture)
86 .transparencyMode(TransparencyMode.opaque)
87 .build();
88 TestDelegateFactory delegateFactory =
90 fragment.setDelegateFactory(delegateFactory);
91
92 assertEquals("custom_entrypoint", fragment.getDartEntrypointFunctionName());
93 assertEquals("package:foo/bar.dart", fragment.getDartEntrypointLibraryUri());
94 assertEquals("/custom/route", fragment.getInitialRoute());
95 assertArrayEquals(new String[] {"foo", "bar"}, fragment.getDartEntrypointArgs().toArray());
96 assertArrayEquals(new String[] {}, fragment.getFlutterShellArgs().toArray());
97 assertFalse(fragment.shouldAttachEngineToActivity());
98 assertTrue(fragment.shouldHandleDeeplinking());
99 assertNull(fragment.getCachedEngineId());
100 assertTrue(fragment.shouldDestroyEngineWithHost());
101 assertEquals(RenderMode.texture, fragment.getRenderMode());
102 assertEquals(TransparencyMode.opaque, fragment.getTransparencyMode());
103 }
104
105 @Test
107 FlutterFragment fragment =
108 FlutterFragment.withNewEngineInGroup("my_cached_engine_group")
109 .dartEntrypoint("custom_entrypoint")
110 .initialRoute("/custom/route")
111 .shouldAttachEngineToActivity(false)
112 .handleDeeplinking(true)
113 .renderMode(RenderMode.texture)
114 .transparencyMode(TransparencyMode.opaque)
115 .build();
116
117 TestDelegateFactory delegateFactory =
119
120 fragment.setDelegateFactory(delegateFactory);
121
122 assertEquals("my_cached_engine_group", fragment.getCachedEngineGroupId());
123 assertEquals("custom_entrypoint", fragment.getDartEntrypointFunctionName());
124 assertEquals("/custom/route", fragment.getInitialRoute());
125 assertArrayEquals(new String[] {}, fragment.getFlutterShellArgs().toArray());
126 assertFalse(fragment.shouldAttachEngineToActivity());
127 assertTrue(fragment.shouldHandleDeeplinking());
128 assertNull(fragment.getCachedEngineId());
129 assertTrue(fragment.shouldDestroyEngineWithHost());
130 assertEquals(RenderMode.texture, fragment.getRenderMode());
131 assertEquals(TransparencyMode.opaque, fragment.getTransparencyMode());
132 }
133
134 @Test
136 FlutterFragment fragment =
137 FlutterFragment.withNewEngine().shouldDelayFirstAndroidViewDraw(true).build();
138
139 assertNotNull(fragment.shouldDelayFirstAndroidViewDraw());
140 }
141
142 @Test
144 FlutterFragment fragment = FlutterFragment.withCachedEngine("my_cached_engine").build();
145
146 assertTrue(fragment.shouldAttachEngineToActivity());
147 assertEquals("my_cached_engine", fragment.getCachedEngineId());
148 assertFalse(fragment.shouldDestroyEngineWithHost());
149 assertFalse(fragment.shouldDelayFirstAndroidViewDraw());
150 }
151
152 @Test
154 FlutterFragment fragment =
155 FlutterFragment.withCachedEngine("my_cached_engine")
156 .destroyEngineWithFragment(true)
157 .build();
158
159 assertTrue(fragment.shouldAttachEngineToActivity());
160 assertEquals("my_cached_engine", fragment.getCachedEngineId());
161 assertTrue(fragment.shouldDestroyEngineWithHost());
162 }
163
164 @Test
166 FlutterFragment fragment =
167 FlutterFragment.withCachedEngine("my_cached_engine")
168 .shouldDelayFirstAndroidViewDraw(true)
169 .build();
170
171 assertNotNull(fragment.shouldDelayFirstAndroidViewDraw());
172 }
173
174 @Test
178 TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
179 FlutterFragment fragment =
180 FlutterFragment.withCachedEngine("my_cached_engine")
181 .destroyEngineWithFragment(true)
182 .build();
183
184 isDelegateAttached = true;
185 when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
186 doAnswer(invocation -> isDelegateAttached = false).when(mockDelegate).onDetach();
187
188 fragment.setDelegateFactory(delegateFactory);
189 fragment.onStart();
190 fragment.onResume();
191 fragment.onPostResume();
192
193 verify(mockDelegate, times(1)).onStart();
194 verify(mockDelegate, times(1)).onResume();
195 verify(mockDelegate, times(1)).onPostResume();
196
197 fragment.onPause();
198 fragment.detachFromFlutterEngine();
199 verify(mockDelegate, times(1)).onPause();
200 verify(mockDelegate, times(1)).onDestroyView();
201 verify(mockDelegate, times(1)).onDetach();
202
203 fragment.onStop();
204 verify(mockDelegate, never()).onStop();
205
206 fragment.onStart();
207 fragment.onResume();
208 fragment.onPostResume();
209 // No more events through to the delegate.
210 verify(mockDelegate, times(1)).onStart();
211 verify(mockDelegate, times(1)).onResume();
212 verify(mockDelegate, times(1)).onPostResume();
213
214 fragment.onDestroy();
215 // 1 time same as before.
216 verify(mockDelegate, times(1)).onDestroyView();
217 verify(mockDelegate, times(1)).onDetach();
218 }
219
220 @Test
224 isDelegateAttached = true;
225 when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
226 doAnswer(invocation -> isDelegateAttached = false).when(mockDelegate).onDetach();
227 TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
228
229 FlutterFragment fragment =
230 FlutterFragment.withCachedEngine("my_cached_engine")
231 .destroyEngineWithFragment(true)
232 .build();
233
234 fragment.setDelegateFactory(delegateFactory);
235 fragment.onStart();
236 fragment.onResume();
237 fragment.onPostResume();
238 fragment.onPause();
239
240 assertTrue(mockDelegate.isAttached());
241 fragment.detachFromFlutterEngine();
242 verify(mockDelegate, times(1)).onDetach();
243 verify(mockDelegate, never()).release();
244 assertFalse(mockDelegate.isAttached());
245 }
246
247 @Test
251 isDelegateAttached = true;
252 when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
253 doAnswer(invocation -> isDelegateAttached = false).when(mockDelegate).onDetach();
254 TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
255
256 FlutterFragment fragment =
257 spy(
258 FlutterFragment.withCachedEngine("my_cached_engine")
259 .destroyEngineWithFragment(true)
260 .build());
261 when(fragment.getContext()).thenReturn(mock(Context.class));
262
263 fragment.setDelegateFactory(delegateFactory);
264 fragment.onStart();
265 fragment.onResume();
266 fragment.onPostResume();
267 fragment.onPause();
268
269 assertTrue(mockDelegate.isAttached());
270 fragment.onDetach();
271 verify(mockDelegate, times(1)).onDetach();
272 verify(mockDelegate, times(1)).release();
273 assertFalse(mockDelegate.isAttached());
274 }
275
276 @Test
280 TestDelegateFactory delegateFactory = new TestDelegateFactory(delegate);
281 fragment.setDelegateFactory(delegateFactory);
282
283 assertEquals(fragment.getExclusiveAppComponent(), delegate);
284 }
285
286 @SuppressWarnings("deprecation")
287 private FragmentActivity getMockFragmentActivity() {
288 // TODO(reidbaker): https://github.com/flutter/flutter/issues/133151
289 return Robolectric.setupActivity(FragmentActivity.class);
290 }
291
292 @Test
294 // We need to mock FlutterJNI to avoid triggering native code.
295 FlutterJNI flutterJNI = mock(FlutterJNI.class);
296 when(flutterJNI.isAttached()).thenReturn(true);
297
298 FlutterEngine flutterEngine =
299 new FlutterEngine(ctx, new FlutterLoader(), flutterJNI, null, false);
300 FlutterEngineCache.getInstance().put("my_cached_engine", flutterEngine);
301
302 FlutterFragment fragment =
303 FlutterFragment.withCachedEngine("my_cached_engine")
304 .shouldAutomaticallyHandleOnBackPressed(true)
305 .build();
306 FragmentActivity activity = getMockFragmentActivity();
307 activity
308 .getSupportFragmentManager()
309 .beginTransaction()
310 .add(android.R.id.content, fragment)
311 .commitNow();
312
315 isDelegateAttached = true;
316 when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
317 doAnswer(invocation -> isDelegateAttached = false).when(mockDelegate).onDetach();
318 TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
319 fragment.setDelegateFactory(delegateFactory);
320
321 activity.onBackPressed();
322
323 verify(mockDelegate, times(1)).onBackPressed();
324 }
325
326 @SuppressWarnings("deprecation")
327 // Robolectric.setupActivity
328 // TODO(reidbaker): https://github.com/flutter/flutter/issues/133151
329 @Test
331 // We need to mock FlutterJNI to avoid triggering native code.
332 FlutterJNI flutterJNI = mock(FlutterJNI.class);
333 when(flutterJNI.isAttached()).thenReturn(true);
334
335 FlutterEngine flutterEngine =
336 new FlutterEngine(ctx, new FlutterLoader(), flutterJNI, null, false);
337 FlutterEngineCache.getInstance().put("my_cached_engine", flutterEngine);
338
339 FlutterFragment fragment =
340 FlutterFragment.withCachedEngine("my_cached_engine")
341 .shouldAutomaticallyHandleOnBackPressed(true)
342 .build();
343 FragmentActivity activity = getMockFragmentActivity();
344 activity
345 .getSupportFragmentManager()
346 .beginTransaction()
347 .add(android.R.id.content, fragment)
348 .commitNow();
349 final AtomicBoolean onBackPressedCalled = new AtomicBoolean(false);
350 OnBackPressedCallback callback =
351 new OnBackPressedCallback(true) {
352 @Override
353 public void handleOnBackPressed() {
354 onBackPressedCalled.set(true);
355 }
356 };
357 activity.getOnBackPressedDispatcher().addCallback(callback);
358
361 TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
362 fragment.setDelegateFactory(delegateFactory);
363
364 assertTrue(fragment.popSystemNavigator());
365
366 verify(mockDelegate, never()).onBackPressed();
367 assertTrue(onBackPressedCalled.get());
368 }
369
370 @Test
374 isDelegateAttached = true;
375 when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
376 doAnswer(invocation -> isDelegateAttached = false).when(mockDelegate).onDetach();
377 TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
378
379 Context spyCtx = spy(ctx);
380 // We need to mock FlutterJNI to avoid triggering native code.
381 FlutterJNI flutterJNI = mock(FlutterJNI.class);
382 when(flutterJNI.isAttached()).thenReturn(true);
383
384 FlutterEngine flutterEngine =
385 new FlutterEngine(spyCtx, new FlutterLoader(), flutterJNI, null, false);
386 FlutterEngineCache.getInstance().put("my_cached_engine", flutterEngine);
387
388 FlutterFragment fragment = spy(FlutterFragment.withCachedEngine("my_cached_engine").build());
389 when(fragment.getContext()).thenReturn(spyCtx);
390 fragment.setDelegateFactory(delegateFactory);
391
392 fragment.onAttach(spyCtx);
393 verify(spyCtx, times(1)).registerComponentCallbacks(any());
394 verify(spyCtx, never()).unregisterComponentCallbacks(any());
395
396 fragment.onDetach();
397 verify(spyCtx, times(1)).registerComponentCallbacks(any());
398 verify(spyCtx, times(1)).unregisterComponentCallbacks(any());
399 }
400}
static SkISize times(const SkISize &size, float factor)
FlutterActivityAndFragmentDelegate createDelegate(FlutterActivityAndFragmentDelegate.Host host)
static NewEngineFragmentBuilder withNewEngine()
static NewEngineInGroupFragmentBuilder withNewEngineInGroup( @NonNull String engineGroupId)
static CachedEngineFragmentBuilder withCachedEngine(@NonNull String engineId)
ExclusiveAppComponent< Activity > getExclusiveAppComponent()
void setDelegateFactory( @NonNull FlutterActivityAndFragmentDelegate.DelegateFactory delegateFactory)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback