Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(431)

Side by Side Diff: chrome/android/webapk/shell_apk/javatests/src/org/chromium/webapk/shell_apk/DexLoaderTest.java

Issue 1981473002: Upstream: DexOptimizer and DexLoader classes (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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.webapk.shell_apk;
6
7 import android.content.ComponentName;
8 import android.content.Context;
9 import android.content.Intent;
10 import android.content.ServiceConnection;
11 import android.content.pm.PackageManager.NameNotFoundException;
12 import android.os.FileObserver;
13 import android.os.IBinder;
14 import android.os.RemoteException;
15 import android.test.InstrumentationTestCase;
16 import android.test.suitebuilder.annotation.MediumTest;
17
18 import dalvik.system.DexFile;
19
20 import org.chromium.base.FileUtils;
21 import org.chromium.content.browser.test.util.CallbackHelper;
22 import org.chromium.webapk.shell_apk.test.dex_optimizer.IDexOptimizerService;
23
24 import java.io.File;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27
28 public class DexLoaderTest extends InstrumentationTestCase {
29 /**
30 * Package of APK to load dex file from and package which provides DexOptimi zerService.
31 */
32 private static final String DEX_OPTIMIZER_SERVICE_PACKAGE =
33 "org.chromium.webapk.shell_apk.test.dex_optimizer";
34
35 /**
36 * Class which implements DexOptimizerService.
37 */
38 private static final String DEX_OPTIMIZER_SERVICE_CLASS_NAME =
39 "org.chromium.webapk.shell_apk.test.dex_optimizer.DexOptimizerServic eImpl";
40
41 /**
42 * Name of the dex file in DexOptimizer.apk.
43 */
44 private static final String DEX_ASSET_NAME = "canary.dex";
45
46 /**
47 * Class to load to check whether dex is valid.
48 */
49 private static final String CANARY_CLASS_NAME =
50 "org.chromium.webapk.shell_apk.test.canary.Canary";
51
52 private Context mContext;
53 private Context mRemoteContext;
54 private File mLocalDexDir;
55 private IDexOptimizerService mDexOptimizerService;
56 private ServiceConnection mServiceConnection;
57
58 /**
59 * Monitors read files and modified files in the directory passed to the con structor.
60 */
61 private static class FileMonitor extends FileObserver {
62 public ArrayList<String> mReadPaths = new ArrayList<String>();
63 public ArrayList<String> mModifiedPaths = new ArrayList<String>();
64
65 public FileMonitor(File directory) {
66 super(directory.getPath());
67 }
68
69 @Override
70 public void onEvent(int event, String path) {
71 switch (event) {
72 case FileObserver.ACCESS:
73 mReadPaths.add(path);
74 break;
75 case FileObserver.CREATE:
76 case FileObserver.DELETE:
77 case FileObserver.DELETE_SELF:
78 case FileObserver.MODIFY:
79 mModifiedPaths.add(path);
80 break;
81 default:
82 break;
83 }
84 }
85 }
86
87 @Override
88 protected void setUp() {
89 mContext = getInstrumentation().getTargetContext();
90 mRemoteContext = getRemoteContext(mContext);
91
92 mLocalDexDir = mContext.getDir("dex", Context.MODE_PRIVATE);
93 if (mLocalDexDir.exists()) {
94 FileUtils.recursivelyDeleteFile(mLocalDexDir);
95 if (mLocalDexDir.exists()) {
96 fail("Could not delete local dex directory.");
97 }
98 }
99
100 connectToDexOptimizerService();
101
102 try {
103 if (!mDexOptimizerService.deleteDexDirectory()) {
104 fail("Could not delete remote dex directory.");
105 }
106 } catch (RemoteException e) {
107 e.printStackTrace();
108 fail("Remote crashed during setup.");
109 }
110 }
111
112 @Override
113 public void tearDown() throws Exception {
114 mContext.unbindService(mServiceConnection);
115 super.tearDown();
116 }
117
118 /**
119 * Test that {@DexLoader#load()} can create a ClassLoader from a dex and opt imized dex in
120 * another app's data directory.
121 */
122 @MediumTest
123 public void testLoadFromRemoteDataDir() {
124 // Extract the dex file into another app's data directory and optimize t he dex.
125 String remoteDexFilePath = null;
126 try {
127 remoteDexFilePath = mDexOptimizerService.extractAndOptimizeDex();
128 } catch (RemoteException e) {
129 e.printStackTrace();
130 fail("Remote crashed.");
131 }
132
133 if (remoteDexFilePath == null) {
134 fail("Could not extract and optimize dex.");
135 }
136
137 // Check that the Android OS knows about the optimized dex file for
138 // {@link remoteDexFilePath}.
139 File remoteDexFile = new File(remoteDexFilePath);
140 assertFalse(isDexOptNeeded(remoteDexFile));
141
142 ClassLoader loader = DexLoader.load(
143 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, remoteDexFile , mLocalDexDir);
144 assertNotNull(loader);
145 assertTrue(canLoadCanaryClass(loader));
146
147 // Check that {@link DexLoader#load()} did not use the fallback path.
148 assertFalse(mLocalDexDir.exists());
149 }
150
151 /**
152 * That that {@link DexLoader#load()} falls back to extracting the dex from the APK to the
153 * local data directory and creating the ClassLoader from the extracted dex if creating the
154 * ClassLoader from the cached data in the remote Context's data directory f ails.
155 */
156 @MediumTest
157 public void testLoadFromLocalDataDir() {
158 ClassLoader loader = DexLoader.load(
159 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, null, mLocalD exDir);
160 assertNotNull(loader);
161 assertTrue(canLoadCanaryClass(loader));
162
163 // Check that the dex file was extracted to the local data directory and that a directory
164 // was created for the optimized dex.
165 assertTrue(mLocalDexDir.exists());
166 File[] localDexDirFiles = mLocalDexDir.listFiles();
167 assertNotNull(localDexDirFiles);
168 Arrays.sort(localDexDirFiles);
169 assertEquals(2, localDexDirFiles.length);
170 assertEquals(DEX_ASSET_NAME, localDexDirFiles[0].getName());
171 assertFalse(localDexDirFiles[0].isDirectory());
172 assertEquals("optimized", localDexDirFiles[1].getName());
173 assertTrue(localDexDirFiles[1].isDirectory());
174 }
175
176 /**
177 * Test that {@link DexLoader#load()} does not extract the dex file from the APK if the dex file
178 * was extracted in a previous call to {@link DexLoader#load()}
179 */
180 @MediumTest
181 public void testPreviouslyLoadedFromLocalDataDir() {
182 assertTrue(mLocalDexDir.mkdir());
183
184 {
185 // Load dex the first time. This should extract the dex file from th e APK's assets and
186 // generate the optimized dex file.
187 FileMonitor localDexDirMonitor = new FileMonitor(mLocalDexDir);
188 localDexDirMonitor.startWatching();
189 ClassLoader loader = DexLoader.load(
190 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, null, mLo calDexDir);
191 localDexDirMonitor.stopWatching();
192
193 assertNotNull(loader);
194 assertTrue(canLoadCanaryClass(loader));
195
196 assertTrue(localDexDirMonitor.mReadPaths.contains(DEX_ASSET_NAME));
197 assertTrue(localDexDirMonitor.mModifiedPaths.contains(DEX_ASSET_NAME ));
198 }
199 {
200 // Load dex a second time. We should use the already extracted dex f ile.
201 FileMonitor localDexDirMonitor = new FileMonitor(mLocalDexDir);
202 localDexDirMonitor.startWatching();
203 ClassLoader loader = DexLoader.load(
204 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, null, mLo calDexDir);
205 localDexDirMonitor.stopWatching();
206
207 // The returned ClassLoader should be valid.
208 assertNotNull(loader);
209 assertTrue(canLoadCanaryClass(loader));
210
211 // We should not have modified any files and have used the already e xtracted dex file.
212 assertTrue(localDexDirMonitor.mReadPaths.contains(DEX_ASSET_NAME));
213 assertTrue(localDexDirMonitor.mModifiedPaths.isEmpty());
214 }
215 }
216
217 /**
218 * Test that {@link DexLoader#load()} re-extracts the dex file from the APK after a call to
219 * {@link DexLoader#deleteCachedDexes()}.
220 */
221 @MediumTest
222 public void testLoadAfterDeleteCachedDexes() {
223 assertTrue(mLocalDexDir.mkdir());
224
225 {
226 // Load dex the first time. This should extract the dex file from th e APK's assets and
227 // generate the optimized dex file.
228 FileMonitor localDexDirMonitor = new FileMonitor(mLocalDexDir);
229 localDexDirMonitor.startWatching();
230 ClassLoader loader = DexLoader.load(
231 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, null, mLo calDexDir);
232 localDexDirMonitor.stopWatching();
233
234 assertNotNull(loader);
235 assertTrue(canLoadCanaryClass(loader));
236
237 assertTrue(localDexDirMonitor.mReadPaths.contains(DEX_ASSET_NAME));
238 assertTrue(localDexDirMonitor.mModifiedPaths.contains(DEX_ASSET_NAME ));
239 }
240
241 DexLoader.deleteCachedDexes(mLocalDexDir);
242
243 {
244 // Load dex a second time.
245 FileMonitor localDexDirMonitor = new FileMonitor(mLocalDexDir);
246 localDexDirMonitor.startWatching();
247 ClassLoader loader = DexLoader.load(
248 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, null, mLo calDexDir);
249 localDexDirMonitor.stopWatching();
250
251 // The returned ClassLoader should be valid.
252 assertNotNull(loader);
253 assertTrue(canLoadCanaryClass(loader));
254
255 // We should have re-extracted the dex from the APK's assets.
256 assertTrue(localDexDirMonitor.mReadPaths.contains(DEX_ASSET_NAME));
257 assertTrue(localDexDirMonitor.mModifiedPaths.contains(DEX_ASSET_NAME ));
258 }
259 }
260
261 /**
262 * Connects to the DexOptimizerService.
263 */
264 private void connectToDexOptimizerService() {
265 Intent intent = new Intent();
266 intent.setComponent(
267 new ComponentName(DEX_OPTIMIZER_SERVICE_PACKAGE, DEX_OPTIMIZER_S ERVICE_CLASS_NAME));
268 final CallbackHelper connectedCallback = new CallbackHelper();
269
270 mServiceConnection = new ServiceConnection() {
271 @Override
272 public void onServiceConnected(ComponentName name, IBinder service) {
273 mDexOptimizerService = IDexOptimizerService.Stub.asInterface(ser vice);
274 connectedCallback.notifyCalled();
275 }
276
277 @Override
278 public void onServiceDisconnected(ComponentName name) {}
279 };
280
281 try {
282 mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_C REATE);
283 } catch (SecurityException e) {
284 e.printStackTrace();
285 fail();
286 }
287
288 try {
289 connectedCallback.waitForCallback(0);
290 } catch (Exception e) {
291 e.printStackTrace();
292 fail("Could not connect to remote.");
293 }
294 }
295
296 /**
297 * Returns the Context of the APK which provides DexOptimizerService.
298 * @param context The test application's Context.
299 * @return Context of the APK whcih provide DexOptimizerService.
300 */
301 private Context getRemoteContext(Context context) {
302 try {
303 return context.getApplicationContext().createPackageContext(
304 DEX_OPTIMIZER_SERVICE_PACKAGE,
305 Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CO DE);
306 } catch (NameNotFoundException e) {
307 e.printStackTrace();
308 fail("Could not get remote context");
309 return null;
310 }
311 }
312
313 /** Returns whether the Android OS thinks that a dex file needs to be re-opt imized */
314 private boolean isDexOptNeeded(File dexFile) {
315 try {
316 return DexFile.isDexOptNeeded(dexFile.getPath());
317 } catch (Exception e) {
318 e.printStackTrace();
319 fail();
320 return false;
321 }
322 }
323
324 /** Returns whether the ClassLoader can load {@link CANARY_CLASS_NAME} */
325 private boolean canLoadCanaryClass(ClassLoader loader) {
326 try {
327 loader.loadClass(CANARY_CLASS_NAME);
328 return true;
329 } catch (Exception e) {
330 return false;
331 }
332 }
333 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698