Flutter Engine
The Flutter Engine
PlayStoreDeferredComponentManagerTest.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.deferredcomponents;
6
7import static junit.framework.TestCase.assertEquals;
8import static junit.framework.TestCase.assertFalse;
9import static junit.framework.TestCase.assertTrue;
10import static org.mockito.Mockito.any;
11import static org.mockito.Mockito.anyInt;
12import static org.mockito.Mockito.doReturn;
13import static org.mockito.Mockito.mock;
14import static org.mockito.Mockito.spy;
15import static org.mockito.Mockito.when;
16
17import android.content.Context;
18import android.content.pm.ApplicationInfo;
19import android.content.pm.PackageManager;
20import android.content.pm.PackageManager.NameNotFoundException;
21import android.content.res.AssetManager;
22import android.os.Bundle;
23import androidx.annotation.NonNull;
24import androidx.test.core.app.ApplicationProvider;
25import androidx.test.ext.junit.runners.AndroidJUnit4;
26import io.flutter.embedding.engine.FlutterJNI;
27import io.flutter.embedding.engine.loader.ApplicationInfoLoader;
28import java.io.File;
29import org.junit.Test;
30import org.junit.runner.RunWith;
31import org.robolectric.annotation.Config;
32
33@Config(manifest = Config.NONE)
34@RunWith(AndroidJUnit4.class)
36 private class TestFlutterJNI extends FlutterJNI {
37 public int loadDartDeferredLibraryCalled = 0;
38 public int updateAssetManagerCalled = 0;
39 public int deferredComponentInstallFailureCalled = 0;
40 public String[] searchPaths;
41 public int loadingUnitId;
42 public AssetManager assetManager;
43 public String assetBundlePath;
44
45 public TestFlutterJNI() {}
46
47 @Override
48 public void loadDartDeferredLibrary(int loadingUnitId, @NonNull String[] searchPaths) {
49 loadDartDeferredLibraryCalled++;
50 this.searchPaths = searchPaths;
51 this.loadingUnitId = loadingUnitId;
52 }
53
54 @Override
55 public void updateJavaAssetManager(
56 @NonNull AssetManager assetManager, @NonNull String assetBundlePath) {
57 updateAssetManagerCalled++;
58 this.loadingUnitId = loadingUnitId;
59 this.assetManager = assetManager;
60 this.assetBundlePath = assetBundlePath;
61 }
62
63 @Override
64 public void deferredComponentInstallFailure(
65 int loadingUnitId, @NonNull String error, boolean isTransient) {
66 deferredComponentInstallFailureCalled++;
67 }
68 }
69
70 // Skips the download process to directly call the loadAssets and loadDartLibrary methods.
71 private class TestPlayStoreDeferredComponentManager extends PlayStoreDeferredComponentManager {
72 public TestPlayStoreDeferredComponentManager(Context context, FlutterJNI jni) {
73 super(context, jni);
74 }
75
76 @Override
77 public void installDeferredComponent(int loadingUnitId, String componentName) {
78 // Override this to skip the online SplitInstallManager portion.
79 loadAssets(loadingUnitId, componentName);
80 loadDartLibrary(loadingUnitId, componentName);
81 }
82 }
83
84 @SuppressWarnings("deprecation")
85 // getApplicationInfo
86 private Context createSpyContext(Bundle metadata) throws NameNotFoundException {
87 Context spyContext = spy(ApplicationProvider.getApplicationContext());
88 doReturn(spyContext).when(spyContext).createPackageContext(any(), anyInt());
89 if (metadata == null) {
90 metadata = new Bundle();
91 }
92 PackageManager packageManager = mock(PackageManager.class);
93 ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
94 applicationInfo.metaData = metadata;
95 applicationInfo.splitSourceDirs = new String[1];
96 applicationInfo.splitSourceDirs[0] = "some.invalid.apk";
97 when(packageManager.getApplicationInfo(any(String.class), any(int.class)))
98 .thenReturn(applicationInfo);
99 doReturn(packageManager).when(spyContext).getPackageManager();
100 doReturn(applicationInfo).when(spyContext).getApplicationInfo();
101 return spyContext;
102 }
103
104 @Test
105 public void downloadCallsJNIFunctions() throws NameNotFoundException {
106 TestFlutterJNI jni = new TestFlutterJNI();
107 Context spyContext = createSpyContext(null);
108 doReturn(null).when(spyContext).getAssets();
109 String soTestFilename = "libapp.so-123.part.so";
110 String soTestPath = "test/path/" + soTestFilename;
111 doReturn(new File(soTestPath)).when(spyContext).getFilesDir();
112 TestPlayStoreDeferredComponentManager playStoreManager =
113 new TestPlayStoreDeferredComponentManager(spyContext, jni);
114 jni.setDeferredComponentManager(playStoreManager);
115 assertEquals(jni.loadingUnitId, 0);
116
117 playStoreManager.installDeferredComponent(123, "TestModuleName");
118 assertEquals(jni.loadDartDeferredLibraryCalled, 1);
119 assertEquals(jni.updateAssetManagerCalled, 1);
120 assertEquals(jni.deferredComponentInstallFailureCalled, 0);
121
122 assertEquals(jni.searchPaths[0], soTestFilename);
123 assertTrue(jni.searchPaths[1].endsWith(soTestPath));
124 assertEquals(jni.searchPaths.length, 2);
125 assertEquals(jni.loadingUnitId, 123);
126 assertEquals(jni.assetBundlePath, "flutter_assets");
127 }
128
129 @Test
130 public void downloadCallsJNIFunctionsWithFilenameFromManifest() throws NameNotFoundException {
131 TestFlutterJNI jni = new TestFlutterJNI();
132
133 Bundle bundle = new Bundle();
134 bundle.putString(ApplicationInfoLoader.PUBLIC_AOT_SHARED_LIBRARY_NAME, "custom_name.so");
135 bundle.putString(ApplicationInfoLoader.PUBLIC_FLUTTER_ASSETS_DIR_KEY, "custom_assets");
136
137 Context spyContext = createSpyContext(bundle);
138 doReturn(null).when(spyContext).getAssets();
139
140 String soTestFilename = "custom_name.so-123.part.so";
141 String soTestPath = "test/path/" + soTestFilename;
142 doReturn(new File(soTestPath)).when(spyContext).getFilesDir();
143 TestPlayStoreDeferredComponentManager playStoreManager =
144 new TestPlayStoreDeferredComponentManager(spyContext, jni);
145 jni.setDeferredComponentManager(playStoreManager);
146 assertEquals(jni.loadingUnitId, 0);
147
148 playStoreManager.installDeferredComponent(123, "TestModuleName");
149 assertEquals(jni.loadDartDeferredLibraryCalled, 1);
150 assertEquals(jni.updateAssetManagerCalled, 1);
151 assertEquals(jni.deferredComponentInstallFailureCalled, 0);
152
153 assertEquals(jni.searchPaths[0], soTestFilename);
154 assertTrue(jni.searchPaths[1].endsWith(soTestPath));
155 assertEquals(jni.searchPaths.length, 2);
156 assertEquals(jni.loadingUnitId, 123);
157 assertEquals(jni.assetBundlePath, "custom_assets");
158 }
159
160 @Test
162 throws NameNotFoundException {
163 TestFlutterJNI jni = new TestFlutterJNI();
164
165 Bundle bundle = new Bundle();
166 bundle.putString(PlayStoreDeferredComponentManager.MAPPING_KEY, "123:module:custom_name.so");
167 bundle.putString(ApplicationInfoLoader.PUBLIC_FLUTTER_ASSETS_DIR_KEY, "custom_assets");
168
169 Context spyContext = createSpyContext(bundle);
170 doReturn(null).when(spyContext).getAssets();
171
172 String soTestFilename = "custom_name.so";
173 String soTestPath = "test/path/" + soTestFilename;
174 doReturn(new File(soTestPath)).when(spyContext).getFilesDir();
175 TestPlayStoreDeferredComponentManager playStoreManager =
176 new TestPlayStoreDeferredComponentManager(spyContext, jni);
177 jni.setDeferredComponentManager(playStoreManager);
178 assertEquals(jni.loadingUnitId, 0);
179
180 playStoreManager.installDeferredComponent(123, "TestModuleName");
181 assertEquals(jni.loadDartDeferredLibraryCalled, 1);
182 assertEquals(jni.updateAssetManagerCalled, 1);
183 assertEquals(jni.deferredComponentInstallFailureCalled, 0);
184
185 assertEquals(jni.searchPaths[0], soTestFilename);
186 assertTrue(jni.searchPaths[1].endsWith(soTestPath));
187 assertEquals(jni.searchPaths.length, 2);
188 assertEquals(jni.loadingUnitId, 123);
189 assertEquals(jni.assetBundlePath, "custom_assets");
190 }
191
192 @Test
193 public void manifestMappingHandlesBaseModuleEmptyString() throws NameNotFoundException {
194 TestFlutterJNI jni = new TestFlutterJNI();
195
196 Bundle bundle = new Bundle();
197 bundle.putString(
198 PlayStoreDeferredComponentManager.MAPPING_KEY, "123:module:custom_name.so,3:,4:");
199 bundle.putString(ApplicationInfoLoader.PUBLIC_FLUTTER_ASSETS_DIR_KEY, "custom_assets");
200
201 Context spyContext = createSpyContext(bundle);
202 doReturn(null).when(spyContext).getAssets();
203
204 String soTestFilename = "libapp.so-3.part.so";
205 String soTestPath = "test/path/" + soTestFilename;
206 doReturn(new File(soTestPath)).when(spyContext).getFilesDir();
207 PlayStoreDeferredComponentManager playStoreManager =
208 new PlayStoreDeferredComponentManager(spyContext, jni);
209 jni.setDeferredComponentManager(playStoreManager);
210 assertEquals(jni.loadingUnitId, 0);
211
212 playStoreManager.installDeferredComponent(3, null);
213 assertEquals(jni.loadDartDeferredLibraryCalled, 1);
214 assertEquals(jni.updateAssetManagerCalled, 0); // no assets to load for base
215 assertEquals(jni.deferredComponentInstallFailureCalled, 0);
216
217 assertEquals(jni.searchPaths[0], soTestFilename);
218 assertTrue(jni.searchPaths[1].endsWith(soTestPath));
219 assertEquals(jni.searchPaths.length, 2);
220 assertEquals(jni.loadingUnitId, 3);
221 }
222
223 @Test
224 public void searchPathsAddsApks() throws NameNotFoundException {
225 TestFlutterJNI jni = new TestFlutterJNI();
226 Context spyContext = createSpyContext(null);
227 doReturn(null).when(spyContext).getAssets();
228 String apkTestPath = "test/path/TestModuleName_armeabi_v7a.apk";
229 doReturn(new File(apkTestPath)).when(spyContext).getFilesDir();
230 TestPlayStoreDeferredComponentManager playStoreManager =
231 new TestPlayStoreDeferredComponentManager(spyContext, jni);
232 jni.setDeferredComponentManager(playStoreManager);
233
234 assertEquals(jni.loadingUnitId, 0);
235
236 playStoreManager.installDeferredComponent(123, "TestModuleName");
237 assertEquals(jni.loadDartDeferredLibraryCalled, 1);
238 assertEquals(jni.updateAssetManagerCalled, 1);
239 assertEquals(jni.deferredComponentInstallFailureCalled, 0);
240
241 assertEquals(jni.searchPaths[0], "libapp.so-123.part.so");
242 assertTrue(jni.searchPaths[1].endsWith(apkTestPath + "!lib/armeabi-v7a/libapp.so-123.part.so"));
243 assertEquals(jni.searchPaths.length, 2);
244 assertEquals(jni.loadingUnitId, 123);
245 }
246
247 @Test
248 public void searchPathsSearchesSplitConfig() throws NameNotFoundException {
249 TestFlutterJNI jni = new TestFlutterJNI();
250 Context spyContext = createSpyContext(null);
251 doReturn(null).when(spyContext).getAssets();
252 String apkTestPath = "test/path/split_config.armeabi_v7a.apk";
253 doReturn(new File(apkTestPath)).when(spyContext).getFilesDir();
254 TestPlayStoreDeferredComponentManager playStoreManager =
255 new TestPlayStoreDeferredComponentManager(spyContext, jni);
256 jni.setDeferredComponentManager(playStoreManager);
257
258 assertEquals(jni.loadingUnitId, 0);
259
260 playStoreManager.installDeferredComponent(123, "TestModuleName");
261 assertEquals(jni.loadDartDeferredLibraryCalled, 1);
262 assertEquals(jni.updateAssetManagerCalled, 1);
263 assertEquals(jni.deferredComponentInstallFailureCalled, 0);
264
265 assertEquals(jni.searchPaths[0], "libapp.so-123.part.so");
266 assertTrue(jni.searchPaths[1].endsWith(apkTestPath + "!lib/armeabi-v7a/libapp.so-123.part.so"));
267 assertEquals(jni.searchPaths.length, 2);
268 assertEquals(jni.loadingUnitId, 123);
269 }
270
271 @Test
272 public void invalidSearchPathsAreIgnored() throws NameNotFoundException {
273 TestFlutterJNI jni = new TestFlutterJNI();
274 Context spyContext = createSpyContext(null);
275 doReturn(null).when(spyContext).getAssets();
276 String apkTestPath = "test/path/invalidpath.apk";
277 doReturn(new File(apkTestPath)).when(spyContext).getFilesDir();
278 TestPlayStoreDeferredComponentManager playStoreManager =
279 new TestPlayStoreDeferredComponentManager(spyContext, jni);
280 jni.setDeferredComponentManager(playStoreManager);
281
282 assertEquals(jni.loadingUnitId, 0);
283
284 playStoreManager.installDeferredComponent(123, "TestModuleName");
285 assertEquals(jni.loadDartDeferredLibraryCalled, 1);
286 assertEquals(jni.updateAssetManagerCalled, 1);
287 assertEquals(jni.deferredComponentInstallFailureCalled, 0);
288
289 assertEquals(jni.searchPaths[0], "libapp.so-123.part.so");
290 assertEquals(jni.searchPaths.length, 1);
291 assertEquals(jni.loadingUnitId, 123);
292 }
293
294 @Test
295 public void assetManagerUpdateInvoked() throws NameNotFoundException {
296 TestFlutterJNI jni = new TestFlutterJNI();
297 Context spyContext = createSpyContext(null);
298 doReturn(null).when(spyContext).getAssets();
299 AssetManager assetManager = spyContext.getAssets();
300 String apkTestPath = "blah doesn't matter here";
301 doReturn(new File(apkTestPath)).when(spyContext).getFilesDir();
302 TestPlayStoreDeferredComponentManager playStoreManager =
303 new TestPlayStoreDeferredComponentManager(spyContext, jni);
304 jni.setDeferredComponentManager(playStoreManager);
305
306 assertEquals(jni.loadingUnitId, 0);
307
308 playStoreManager.installDeferredComponent(123, "TestModuleName");
309 assertEquals(jni.loadDartDeferredLibraryCalled, 1);
310 assertEquals(jni.updateAssetManagerCalled, 1);
311 assertEquals(jni.deferredComponentInstallFailureCalled, 0);
312
313 assertEquals(jni.assetManager, assetManager);
314 }
315
316 @Test
317 public void stateGetterReturnsUnknowByDefault() throws NameNotFoundException {
318 TestFlutterJNI jni = new TestFlutterJNI();
319 Context spyContext = createSpyContext(null);
320 doReturn(null).when(spyContext).getAssets();
321 TestPlayStoreDeferredComponentManager playStoreManager =
322 new TestPlayStoreDeferredComponentManager(spyContext, jni);
323 assertEquals(playStoreManager.getDeferredComponentInstallState(-1, "invalidName"), "unknown");
324 }
325
326 @Test
327 public void loadingUnitMappingFindsMatch() throws NameNotFoundException {
328 TestFlutterJNI jni = new TestFlutterJNI();
329 Bundle bundle = new Bundle();
330 bundle.putString(PlayStoreDeferredComponentManager.MAPPING_KEY, "2:module1,5:module2");
331 Context spyContext = createSpyContext(bundle);
332 TestPlayStoreDeferredComponentManager playStoreManager =
333 new TestPlayStoreDeferredComponentManager(spyContext, jni);
334
335 assertTrue(playStoreManager.uninstallDeferredComponent(5, null));
336 assertTrue(playStoreManager.uninstallDeferredComponent(2, null));
337 assertFalse(playStoreManager.uninstallDeferredComponent(3, null));
338 }
339
340 @Test
341 public void assetOnlyMappingParses() throws NameNotFoundException {
342 TestFlutterJNI jni = new TestFlutterJNI();
343 Bundle bundle = new Bundle();
345 Context spyContext = createSpyContext(bundle);
346 TestPlayStoreDeferredComponentManager playStoreManager =
347 new TestPlayStoreDeferredComponentManager(spyContext, jni);
348 }
349}
const uint8_t uint32_t uint32_t GError ** error
SIT bool any(const Vec< 1, T > &x)
Definition: SkVx.h:530