• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dalvik.system;
18 
19 import java.io.FilenameFilter;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import libcore.io.Streams;
27 import junit.framework.TestCase;
28 
29 /**
30  * Tests for the class {@link DexClassLoader}.
31  */
32 public class DexClassLoaderTest extends TestCase {
33     private static final String PACKAGE_PATH = "dalvik/system/";
34 
35     private File srcDir;
36     private File dex1;
37     private File dex2;
38     private File jar1;
39     private File jar2;
40     private File optimizedDir;
41 
setUp()42     protected void setUp() throws Exception {
43         srcDir = File.createTempFile("src", "");
44         assertTrue(srcDir.delete());
45         assertTrue(srcDir.mkdirs());
46 
47         dex1 = new File(srcDir, "loading-test.dex");
48         dex2 = new File(srcDir, "loading-test2.dex");
49         jar1 = new File(srcDir, "loading-test.jar");
50         jar2 = new File(srcDir, "loading-test2.jar");
51 
52         copyResource("loading-test.dex", dex1);
53         copyResource("loading-test2.dex", dex2);
54         copyResource("loading-test.jar", jar1);
55         copyResource("loading-test2.jar", jar2);
56 
57         optimizedDir = File.createTempFile("optimized", "");
58         assertTrue(optimizedDir.delete());
59         assertTrue(optimizedDir.mkdirs());
60     }
61 
tearDown()62     protected void tearDown() {
63         cleanUpDir(srcDir);
64         cleanUpDir(optimizedDir);
65     }
66 
cleanUpDir(File dir)67     private static void cleanUpDir(File dir) {
68         if (!dir.isDirectory()) {
69             return;
70         }
71         File[] files = dir.listFiles();
72         for (File file : files) {
73             if (file.isDirectory()) {
74                 cleanUpDir(file);
75             } else {
76                 assertTrue(file.delete());
77             }
78         }
79     }
80 
81     /**
82      * Copy a resource in the package directory to the indicated
83      * target file.
84      */
copyResource(String resourceName, File destination)85     private static void copyResource(String resourceName,
86             File destination) throws IOException {
87         ClassLoader loader = DexClassLoaderTest.class.getClassLoader();
88         assertFalse(destination.exists());
89         InputStream in = loader.getResourceAsStream(PACKAGE_PATH + resourceName);
90         if (in == null) {
91             throw new IllegalStateException("Resource not found: " + PACKAGE_PATH + resourceName);
92         }
93 
94         try (FileOutputStream out = new FileOutputStream(destination)) {
95             Streams.copy(in, out);
96         } finally {
97             in.close();
98         }
99     }
100 
101     static final FilenameFilter DEX_FILE_NAME_FILTER = new FilenameFilter() {
102         @Override
103         public boolean accept(File file, String s) {
104             return s.endsWith(".dex");
105         }
106     };
107 
108     /**
109      * Helper to construct a DexClassLoader instance to test.
110      *
111      * @param files The .dex or .jar files to use for the class path.
112      */
createLoader(File... files)113     private ClassLoader createLoader(File... files) {
114         assertNotNull(files);
115         assertTrue(files.length > 0);
116         String path = files[0].getAbsolutePath();
117         for (int i = 1; i < files.length; i++) {
118             path += File.pathSeparator + files[i].getAbsolutePath();
119         }
120         return new DexClassLoader(path, optimizedDir.getAbsolutePath(), null,
121             ClassLoader.getSystemClassLoader());
122     }
123 
124     /**
125      * Helper to construct a new DexClassLoader instance to test, using the
126      * given files as the class path, and call a named no-argument static
127      * method on a named class.
128      *
129      * @param className The name of the class of the method to call.
130      * @param methodName The name of the method to call.
131      * @param files The .dex or .jar files to use for the class path.
132      */
createLoaderAndCallMethod( String className, String methodName, File... files)133     public Object createLoaderAndCallMethod(
134             String className, String methodName, File... files)
135             throws ReflectiveOperationException {
136         ClassLoader cl = createLoader(files);
137         Class c = cl.loadClass(className);
138         Method m = c.getMethod(methodName, (Class[]) null);
139         return m.invoke(null, (Object[]) null);
140     }
141 
142     /**
143      * Helper to construct a new DexClassLoader instance to test, using the
144      * given files as the class path, and read the contents of the named
145      * resource as a String.
146      *
147      * @param resourceName The name of the resource to get.
148      * @param files The .dex or .jar files to use for the class path.
149      */
createLoaderAndGetResource(String resourceName, File... files)150     private String createLoaderAndGetResource(String resourceName, File... files) throws Exception {
151         ClassLoader cl = createLoader(files);
152         InputStream in = cl.getResourceAsStream(resourceName);
153         if (in == null) {
154             throw new IllegalStateException("Resource not found: " + resourceName);
155         }
156 
157         byte[] contents = Streams.readFully(in);
158         return new String(contents, "UTF-8");
159     }
160 
161     // ONE_JAR
162 
163     /**
164      * Just a trivial test of construction. This one merely makes
165      * sure that a valid construction doesn't fail. It doesn't try
166      * to verify anything about the constructed instance, other than
167      * checking for the existence of optimized dex files.
168      */
test_oneJar_init()169     public void test_oneJar_init() throws Exception {
170         ClassLoader cl = createLoader(jar1);
171         File[] files = optimizedDir.listFiles(DEX_FILE_NAME_FILTER);
172         assertNotNull(files);
173         assertEquals(1, files.length);
174     }
175 
176     /**
177      * Check that a class in the jar/dex file may be used successfully. In this
178      * case, a trivial static method is called.
179      */
test_oneJar_simpleUse()180     public void test_oneJar_simpleUse() throws Exception {
181         String result = (String) createLoaderAndCallMethod("test.Test1", "test", jar1);
182         assertSame("blort", result);
183     }
184 
185     /*
186      * All the following tests are just pass-throughs to test code
187      * that lives inside the loading-test dex/jar file.
188      */
189 
test_oneJar_constructor()190     public void test_oneJar_constructor() throws Exception {
191         createLoaderAndCallMethod("test.TestMethods", "test_constructor", jar1);
192     }
193 
test_oneJar_callStaticMethod()194     public void test_oneJar_callStaticMethod() throws Exception {
195         createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", jar1);
196     }
197 
test_oneJar_getStaticVariable()198     public void test_oneJar_getStaticVariable() throws Exception {
199         createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", jar1);
200     }
201 
test_oneJar_callInstanceMethod()202     public void test_oneJar_callInstanceMethod() throws Exception {
203         createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", jar1);
204     }
205 
test_oneJar_getInstanceVariable()206     public void test_oneJar_getInstanceVariable() throws Exception {
207         createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", jar1);
208     }
209 
210     // ONE_DEX
211 
test_oneDex_init()212     public void test_oneDex_init() throws Exception {
213         ClassLoader cl = createLoader(dex1);
214         File[] files = optimizedDir.listFiles(DEX_FILE_NAME_FILTER);
215         assertNotNull(files);
216         assertEquals(1, files.length);
217     }
218 
test_oneDex_simpleUse()219     public void test_oneDex_simpleUse() throws Exception {
220         String result = (String) createLoaderAndCallMethod("test.Test1", "test", dex1);
221         assertSame("blort", result);
222     }
223 
test_oneDex_constructor()224     public void test_oneDex_constructor() throws Exception {
225         createLoaderAndCallMethod("test.TestMethods", "test_constructor", dex1);
226     }
227 
test_oneDex_callStaticMethod()228     public void test_oneDex_callStaticMethod() throws Exception {
229         createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1);
230     }
231 
test_oneDex_getStaticVariable()232     public void test_oneDex_getStaticVariable() throws Exception {
233         createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1);
234     }
235 
test_oneDex_callInstanceMethod()236     public void test_oneDex_callInstanceMethod() throws Exception {
237         createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1);
238     }
239 
test_oneDex_getInstanceVariable()240     public void test_oneDex_getInstanceVariable() throws Exception {
241         createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", dex1);
242     }
243 
244     // TWO_JAR
245 
test_twoJar_init()246     public void test_twoJar_init() throws Exception {
247         ClassLoader cl = createLoader(jar1, jar2);
248         File[] files = optimizedDir.listFiles(DEX_FILE_NAME_FILTER);
249         assertNotNull(files);
250         assertEquals(2, files.length);
251     }
252 
test_twoJar_simpleUse()253     public void test_twoJar_simpleUse() throws Exception {
254         String result = (String) createLoaderAndCallMethod("test.Test1", "test", jar1, jar2);
255         assertSame("blort", result);
256     }
257 
test_twoJar_constructor()258     public void test_twoJar_constructor() throws Exception {
259         createLoaderAndCallMethod("test.TestMethods", "test_constructor", jar1, jar2);
260     }
261 
test_twoJar_callStaticMethod()262     public void test_twoJar_callStaticMethod() throws Exception {
263         createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", jar1, jar2);
264     }
265 
test_twoJar_getStaticVariable()266     public void test_twoJar_getStaticVariable() throws Exception {
267         createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", jar1, jar2);
268     }
269 
test_twoJar_callInstanceMethod()270     public void test_twoJar_callInstanceMethod() throws Exception {
271         createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", jar1, jar2);
272     }
273 
test_twoJar_getInstanceVariable()274     public void test_twoJar_getInstanceVariable() throws Exception {
275         createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", jar1, jar2);
276     }
277 
test_twoJar_diff_constructor()278     public void test_twoJar_diff_constructor() throws Exception {
279         createLoaderAndCallMethod("test.TestMethods", "test_diff_constructor", jar1, jar2);
280     }
281 
test_twoJar_diff_callStaticMethod()282     public void test_twoJar_diff_callStaticMethod() throws Exception {
283         createLoaderAndCallMethod("test.TestMethods", "test_diff_callStaticMethod", jar1, jar2);
284     }
285 
test_twoJar_diff_getStaticVariable()286     public void test_twoJar_diff_getStaticVariable() throws Exception {
287         createLoaderAndCallMethod("test.TestMethods", "test_diff_getStaticVariable", jar1, jar2);
288     }
289 
test_twoJar_diff_callInstanceMethod()290     public void test_twoJar_diff_callInstanceMethod() throws Exception {
291         createLoaderAndCallMethod("test.TestMethods", "test_diff_callInstanceMethod", jar1, jar2);
292     }
293 
test_twoJar_diff_getInstanceVariable()294     public void test_twoJar_diff_getInstanceVariable() throws Exception {
295         createLoaderAndCallMethod("test.TestMethods", "test_diff_getInstanceVariable", jar1, jar2);
296     }
297 
298     // TWO_DEX
299 
test_twoDex_init()300     public void test_twoDex_init() throws Exception {
301         ClassLoader cl = createLoader(dex1, dex2);
302         File[] files = optimizedDir.listFiles(DEX_FILE_NAME_FILTER);
303         assertNotNull(files);
304         assertEquals(2, files.length);
305     }
306 
test_twoDex_simpleUse()307     public void test_twoDex_simpleUse() throws Exception {
308         String result = (String) createLoaderAndCallMethod("test.Test1", "test", dex1, dex2);
309         assertSame("blort", result);
310     }
311 
test_twoDex_constructor()312     public void test_twoDex_constructor() throws Exception {
313         createLoaderAndCallMethod("test.TestMethods", "test_constructor", dex1, dex2);
314     }
315 
test_twoDex_callStaticMethod()316     public void test_twoDex_callStaticMethod() throws Exception {
317         createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1, dex2);
318     }
319 
test_twoDex_getStaticVariable()320     public void test_twoDex_getStaticVariable() throws Exception {
321         createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1, dex2);
322     }
323 
test_twoDex_callInstanceMethod()324     public void test_twoDex_callInstanceMethod() throws Exception {
325         createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1, dex2);
326     }
327 
test_twoDex_getInstanceVariable()328     public void test_twoDex_getInstanceVariable() throws Exception {
329         createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", dex1, dex2);
330     }
331 
test_twoDex_diff_constructor()332     public void test_twoDex_diff_constructor() throws Exception {
333         createLoaderAndCallMethod("test.TestMethods", "test_diff_constructor", dex1, dex2);
334     }
335 
test_twoDex_diff_callStaticMethod()336     public void test_twoDex_diff_callStaticMethod() throws Exception {
337         createLoaderAndCallMethod("test.TestMethods", "test_diff_callStaticMethod", dex1, dex2);
338     }
339 
test_twoDex_diff_getStaticVariable()340     public void test_twoDex_diff_getStaticVariable() throws Exception {
341         createLoaderAndCallMethod("test.TestMethods", "test_diff_getStaticVariable", dex1, dex2);
342     }
343 
test_twoDex_diff_callInstanceMethod()344     public void test_twoDex_diff_callInstanceMethod() throws Exception {
345         createLoaderAndCallMethod("test.TestMethods", "test_diff_callInstanceMethod", dex1, dex2);
346     }
347 
test_twoDex_diff_getInstanceVariable()348     public void test_twoDex_diff_getInstanceVariable() throws Exception {
349         createLoaderAndCallMethod("test.TestMethods", "test_diff_getInstanceVariable", dex1, dex2);
350     }
351 
352     /*
353      * Tests specifically for resource-related functionality.  Since
354      * raw dex files don't contain resources, these test only work
355      * with jar files.
356      */
357 
358     /**
359      * Check that a resource in the jar file is retrievable and contains
360      * the expected contents.
361      */
test_oneJar_directGetResourceAsStream()362     public void test_oneJar_directGetResourceAsStream() throws Exception {
363         String result = createLoaderAndGetResource("test/Resource1.txt", jar1);
364         assertEquals("Muffins are tasty!\n", result);
365     }
366 
367     /**
368      * Check that a resource in the jar file can be retrieved from
369      * a class within that jar file.
370      */
test_oneJar_getResourceAsStream()371     public void test_oneJar_getResourceAsStream() throws Exception {
372         createLoaderAndCallMethod("test.TestMethods", "test_getResourceAsStream", jar1);
373     }
374 
test_twoJar_directGetResourceAsStream()375     public void test_twoJar_directGetResourceAsStream() throws Exception {
376         String result = createLoaderAndGetResource("test/Resource1.txt", jar1, jar2);
377         assertEquals("Muffins are tasty!\n", result);
378     }
379 
test_twoJar_getResourceAsStream()380     public void test_twoJar_getResourceAsStream() throws Exception {
381         createLoaderAndCallMethod("test.TestMethods", "test_getResourceAsStream", jar1, jar2);
382     }
383 
384     /**
385      * Check that a resource in the second jar file is retrievable and
386      * contains the expected contents.
387      */
test_twoJar_diff_directGetResourceAsStream()388     public void test_twoJar_diff_directGetResourceAsStream() throws Exception {
389         String result = createLoaderAndGetResource("test2/Resource2.txt", jar1, jar2);
390         assertEquals("Who doesn't like a good biscuit?\n", result);
391     }
392 
393     /**
394      * Check that a resource in a jar file can be retrieved from
395      * a class within the other jar file.
396      */
test_twoJar_diff_getResourceAsStream()397     public void test_twoJar_diff_getResourceAsStream() throws Exception {
398         createLoaderAndCallMethod("test.TestMethods", "test_diff_getResourceAsStream", jar1, jar2);
399     }
400 
401     /**
402      * Test that a DexClassLoader can be used to generate optimized code, then
403      * a subsequent PathClassLoader can be used to load the optimized code.
404      * (b/19937016).
405      */
testDexThenPathClassLoader()406     public void testDexThenPathClassLoader() throws Exception {
407         // Use a DexClassLoader to create optimized code.
408         File dex = new File(srcDir, "dex-then-path.dex");
409         copyResource("loading-test.dex", dex);
410 
411         File oatDir = new File(new File(srcDir, "oat"), VMRuntime.getCurrentInstructionSet());
412         assertTrue(oatDir.mkdirs());
413 
414         DexClassLoader dexloader = new DexClassLoader(dex.getAbsolutePath(),
415                 oatDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());
416         Class c1 = dexloader.loadClass("test.Test1");
417         Method m1 = c1.getMethod("test", (Class[]) null);
418         assertSame("blort", m1.invoke(null, (Object[]) null));
419 
420         // Move the optimized code to the right location to be used by a
421         // PathClassLoader.
422         File odexForDexClassLoader = new File(oatDir, "dex-then-path.dex");
423         File odexForPathClassLoader = new File(oatDir, "dex-then-path.odex");
424         assertTrue(DexFile.isDexOptNeeded(dex.getAbsolutePath()));
425         assertTrue(odexForDexClassLoader.renameTo(odexForPathClassLoader));
426         assertFalse(DexFile.isDexOptNeeded(dex.getAbsolutePath()));
427 
428         // Use a PathClassLoader that loads and runs the optimized code.
429         PathClassLoader pathloader = new PathClassLoader(dex.getAbsolutePath(),
430                 ClassLoader.getSystemClassLoader());
431         Class c2 = pathloader.loadClass("test.Test1");
432         Method m2 = c2.getMethod("test", (Class[]) null);
433         assertSame("blort", m2.invoke(null, (Object[]) null));
434     }
435 }
436