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

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

Issue 1965583002: Move //webapk to //chrome/android/webapk (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.os.FileObserver;
12 import android.os.IBinder;
13 import android.os.RemoteException;
14 import android.test.InstrumentationTestCase;
15 import android.test.suitebuilder.annotation.MediumTest;
16
17 import dalvik.system.DexFile;
18
19 import org.chromium.base.FileUtils;
20 import org.chromium.content.browser.test.util.CallbackHelper;
21 import org.chromium.webapk.lib.common.WebApkUtils;
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 mRemoteDexFile;
55 private File mLocalDexDir;
56 private IDexOptimizerService mDexOptimizerService;
57 private ServiceConnection mServiceConnection;
58
59 /**
60 * Monitors read files and modified files in the directory passed to the con structor.
61 */
62 private class FileMonitor extends FileObserver {
63 public ArrayList<String> mReadPaths = new ArrayList<String>();
64 public ArrayList<String> mModifiedPaths = new ArrayList<String>();
65
66 public FileMonitor(File directory) {
67 super(directory.getPath());
68 }
69
70 @Override
71 public void onEvent(int event, String path) {
72 switch (event) {
73 case FileObserver.ACCESS:
74 mReadPaths.add(path);
75 break;
76 case FileObserver.CREATE:
77 case FileObserver.DELETE:
78 case FileObserver.DELETE_SELF:
79 case FileObserver.MODIFY:
80 mModifiedPaths.add(path);
81 break;
82 default:
83 break;
84 }
85 }
86 }
87
88 @Override
89 protected void setUp() {
90 mContext = getInstrumentation().getTargetContext();
91
92 mRemoteContext = WebApkUtils.getHostBrowserContext(mContext);
93 if (mRemoteContext == null) {
94 fail("Failed to get remote context.");
95 }
96
97 mLocalDexDir = mContext.getDir("dex", Context.MODE_PRIVATE);
98 if (mLocalDexDir.exists()) {
99 FileUtils.recursivelyDeleteFile(mLocalDexDir);
100 if (mLocalDexDir.exists()) {
101 fail("Could not delete local dex directory.");
102 }
103 }
104
105 connectToDexOptimizerService();
106
107 try {
108 if (!mDexOptimizerService.deleteDexDirectory()) {
109 fail("Could not delete remote dex directory.");
110 }
111 } catch (RemoteException e) {
112 e.printStackTrace();
113 fail("Remote crashed during setup.");
114 }
115 }
116
117 @Override
118 public void tearDown() {
119 mContext.unbindService(mServiceConnection);
120 }
121
122 /**
123 * Test that {@DexLoader#load()} can create a ClassLoader from a dex and opt imized dex in
124 * another app's data directory.
125 */
126 @MediumTest
127 public void testLoadFromRemoteDataDir() {
128 // Extract the dex file into another app's data directory and optimize t he dex.
129 String remoteDexFilePath = null;
130 try {
131 remoteDexFilePath = mDexOptimizerService.extractAndOptimizeDex();
132 } catch (RemoteException e) {
133 e.printStackTrace();
134 fail("Remote crashed.");
135 }
136
137 if (remoteDexFilePath == null) {
138 fail("Could not extract and optimize dex.");
139 }
140
141 // Check that the Android OS knows about the optimized dex file for
142 // {@link remoteDexFilePath}.
143 File remoteDexFile = new File(remoteDexFilePath);
144 assertFalse(isDexOptNeeded(remoteDexFile));
145
146 ClassLoader loader = DexLoader.load(
147 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, remoteDexFile , mLocalDexDir);
148 assertNotNull(loader);
149 assertTrue(canLoadCanaryClass(loader));
150
151 // Check that {@link DexLoader#load()} did not use the fallback path.
152 assertFalse(mLocalDexDir.exists());
153 }
154
155 /**
156 * That that {@link DexLoader#load()} falls back to extracting the dex from the APK to the
157 * local data directory and creating the ClassLoader from the extracted dex if creating the
158 * ClassLoader from the cached data in the remote Context's data directory f ails.
159 */
160 @MediumTest
161 public void testLoadFromLocalDataDir() {
162 ClassLoader loader = DexLoader.load(
163 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, null, mLocalD exDir);
164 assertNotNull(loader);
165 assertTrue(canLoadCanaryClass(loader));
166
167 // Check that the dex file was extracted to the local data directory and that a directory
168 // was created for the optimized dex.
169 assertTrue(mLocalDexDir.exists());
170 File[] localDexDirFiles = mLocalDexDir.listFiles();
171 Arrays.sort(mLocalDexDir.listFiles());
172 assertEquals(2, localDexDirFiles.length);
173 assertEquals(DEX_ASSET_NAME, localDexDirFiles[0].getName());
174 assertFalse(localDexDirFiles[0].isDirectory());
175 assertEquals("optimized", localDexDirFiles[1].getName());
176 assertTrue(localDexDirFiles[1].isDirectory());
177 }
178
179 /**
180 * Test that {@link DexLoader#load()} does not extract the dex file from the APK if the dex file
181 * was extracted in a previous call to {@link DexLoader#load()}
182 */
183 @MediumTest
184 public void testPreviouslyLoadedFromLocalDataDir() {
185 mLocalDexDir.mkdir();
186
187 {
188 // Load dex the first time. This should extract the dex file from th e APK's assets and
189 // generate the optimized dex file.
190 FileMonitor localDexDirMonitor = new FileMonitor(mLocalDexDir);
191 localDexDirMonitor.startWatching();
192 ClassLoader loader = DexLoader.load(
193 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, null, mLo calDexDir);
194 localDexDirMonitor.stopWatching();
195
196 assertNotNull(loader);
197 assertTrue(canLoadCanaryClass(loader));
198
199 assertTrue(localDexDirMonitor.mReadPaths.contains(DEX_ASSET_NAME));
200 assertTrue(localDexDirMonitor.mModifiedPaths.contains(DEX_ASSET_NAME ));
201 }
202 {
203 // Load dex a second time. We should use the already extracted dex f ile.
204 FileMonitor localDexDirMonitor = new FileMonitor(mLocalDexDir);
205 localDexDirMonitor.startWatching();
206 ClassLoader loader = DexLoader.load(
207 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, null, mLo calDexDir);
208 localDexDirMonitor.stopWatching();
209
210 // The returned ClassLoader should be valid.
211 assertNotNull(loader);
212 assertTrue(canLoadCanaryClass(loader));
213
214 // We should not have modified any files and have used the already e xtracted dex file.
215 assertTrue(localDexDirMonitor.mReadPaths.contains(DEX_ASSET_NAME));
216 assertTrue(localDexDirMonitor.mModifiedPaths.isEmpty());
217 }
218 }
219
220 /**
221 * Test that {@link DexLoader#load()} re-extracts the dex file from the APK after a call to
222 * {@link DexLoader#deleteCachedDexes()}.
223 */
224 @MediumTest
225 public void testLoadAfterDeleteCachedDexes() {
226 mLocalDexDir.mkdir();
227
228 {
229 // Load dex the first time. This should extract the dex file from th e APK's assets and
230 // generate the optimized dex file.
231 FileMonitor localDexDirMonitor = new FileMonitor(mLocalDexDir);
232 localDexDirMonitor.startWatching();
233 ClassLoader loader = DexLoader.load(
234 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, null, mLo calDexDir);
235 localDexDirMonitor.stopWatching();
236
237 assertNotNull(loader);
238 assertTrue(canLoadCanaryClass(loader));
239
240 assertTrue(localDexDirMonitor.mReadPaths.contains(DEX_ASSET_NAME));
241 assertTrue(localDexDirMonitor.mModifiedPaths.contains(DEX_ASSET_NAME ));
242 }
243
244 DexLoader.deleteCachedDexes(mLocalDexDir);
245
246 {
247 // Load dex a second time.
248 FileMonitor localDexDirMonitor = new FileMonitor(mLocalDexDir);
249 localDexDirMonitor.startWatching();
250 ClassLoader loader = DexLoader.load(
251 mRemoteContext, DEX_ASSET_NAME, CANARY_CLASS_NAME, null, mLo calDexDir);
252 localDexDirMonitor.stopWatching();
253
254 // The returned ClassLoader should be valid.
255 assertNotNull(loader);
256 assertTrue(canLoadCanaryClass(loader));
257
258 // We should have re-extracted the dex from the APK's assets.
259 assertTrue(localDexDirMonitor.mReadPaths.contains(DEX_ASSET_NAME));
260 assertTrue(localDexDirMonitor.mModifiedPaths.contains(DEX_ASSET_NAME ));
261 }
262 }
263
264 /**
265 * Connects to the DexOptimizerService.
266 */
267 private void connectToDexOptimizerService() {
268 Intent intent = new Intent();
269 intent.setComponent(
270 new ComponentName(DEX_OPTIMIZER_SERVICE_PACKAGE, DEX_OPTIMIZER_S ERVICE_CLASS_NAME));
271 final CallbackHelper connectedCallback = new CallbackHelper();
272
273 mServiceConnection = new ServiceConnection() {
274 @Override
275 public void onServiceConnected(ComponentName name, IBinder service) {
276 mDexOptimizerService = IDexOptimizerService.Stub.asInterface(ser vice);
277 connectedCallback.notifyCalled();
278 }
279
280 @Override
281 public void onServiceDisconnected(ComponentName name) {}
282 };
283
284 try {
285 mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_C REATE);
286 } catch (SecurityException e) {
287 e.printStackTrace();
288 fail();
289 }
290
291 try {
292 connectedCallback.waitForCallback(0);
293 } catch (Exception e) {
294 e.printStackTrace();
295 fail("Could not connect to remote.");
296 }
297 }
298
299 /** Returns whether the Android OS thinks that a dex file needs to be re-opt imized */
300 private boolean isDexOptNeeded(File dexFile) {
301 try {
302 return DexFile.isDexOptNeeded(dexFile.getPath());
303 } catch (Exception e) {
304 e.printStackTrace();
305 fail();
306 return false;
307 }
308 }
309
310 /** Returns whether the ClassLoader can load {@link CANARY_CLASS_NAME} */
311 private boolean canLoadCanaryClass(ClassLoader loader) {
312 try {
313 loader.loadClass(CANARY_CLASS_NAME);
314 return true;
315 } catch (Exception e) {
316 return false;
317 }
318 }
319 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698