• 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.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import libcore.io.Streams;
26 import junit.framework.TestCase;
27 
28 /**
29  * Tests for the class {@link DexClassLoader}.
30  */
31 public class DexClassLoaderTest extends TestCase {
32     // Use /data not /sdcard because optimized cannot be noexec mounted
33     private static final File WORKING_DIR;
34     static {
35       // First try to use the test runner directory for cts, fall back to
36       // shell-writable directory for vogar
37       File runner_dir = new File("/data/data/android.core.tests.runner");
38       if (runner_dir.exists()) {
39         WORKING_DIR = runner_dir;
40       } else {
41         WORKING_DIR = new File("/data/local/tmp");
42       }
43     }
44     private static final File TMP_DIR = new File(WORKING_DIR, "loading-test");
45     private static final String PACKAGE_PATH = "dalvik/system/";
46     private static final String JAR_NAME = "loading-test.jar";
47     private static final String DEX_NAME = "loading-test.dex";
48     private static final String JAR2_NAME = "loading-test2.jar";
49     private static final String DEX2_NAME = "loading-test2.dex";
50     private static final File JAR_FILE = new File(TMP_DIR, JAR_NAME);
51     private static final File DEX_FILE = new File(TMP_DIR, DEX_NAME);
52     private static final File JAR2_FILE = new File(TMP_DIR, JAR2_NAME);
53     private static final File DEX2_FILE = new File(TMP_DIR, DEX2_NAME);
54     private static final File DEFAULT_OPTIMIZED_DIR = new File(TMP_DIR, "optimized");
55     // Init tests need to use different optimized directories because the tests are executed in the
56     // same runtime. This means we can't reliably count the number of generated file since they
57     // might be cached by the runtime.
58     private static final File INIT1_OPTIMIZED_DIR = new File(TMP_DIR, "optimized_init1");
59     private static final File INIT2_OPTIMIZED_DIR = new File(TMP_DIR, "optimized_init2");
60 
61     private static enum Configuration {
62         /** just one classpath element, a raw dex file */
63         ONE_DEX(1, DEX_FILE),
64         ONE_DEX_INIT(INIT1_OPTIMIZED_DIR, 1, DEX_FILE),
65 
66         /** just one classpath element, a jar file */
67         ONE_JAR(1, JAR_FILE),
68         ONE_JAR_INIT(INIT1_OPTIMIZED_DIR, 1, JAR_FILE),
69 
70         /** two classpath elements, both raw dex files */
71         TWO_DEX(2, DEX_FILE, DEX2_FILE),
72         TWO_DEX_INIT(INIT2_OPTIMIZED_DIR, 2, DEX_FILE, DEX2_FILE),
73 
74         /** two classpath elements, both jar files */
75         TWO_JAR(2, JAR_FILE, JAR2_FILE),
76         TWO_JAR_INIT(INIT2_OPTIMIZED_DIR, 2, JAR_FILE, JAR2_FILE);
77 
78         public final int expectedFiles;
79         public final File optimizedDir;
80         public final String path;
81 
Configuration(int expectedFiles, File... files)82         Configuration(int expectedFiles, File... files) {
83             this(DEFAULT_OPTIMIZED_DIR, expectedFiles, files);
84         }
85 
Configuration(File optimizedDir, int expectedFiles, File... files)86         Configuration(File optimizedDir, int expectedFiles, File... files) {
87             assertTrue(files != null && files.length > 0);
88 
89             this.expectedFiles = expectedFiles;
90             this.optimizedDir = optimizedDir;
91             String path = files[0].getAbsolutePath();
92             for (int i = 1; i < files.length; i++) {
93                 path += File.pathSeparator + files[i].getAbsolutePath();
94             }
95             this.path = path;
96         }
97     }
98 
setUp()99     protected void setUp() throws Exception {
100         assertTrue(TMP_DIR.exists() || TMP_DIR.mkdirs());
101         assertTrue(DEFAULT_OPTIMIZED_DIR.exists() || DEFAULT_OPTIMIZED_DIR.mkdirs());
102         assertTrue(INIT1_OPTIMIZED_DIR.exists() || INIT1_OPTIMIZED_DIR.mkdirs());
103         assertTrue(INIT2_OPTIMIZED_DIR.exists() || INIT2_OPTIMIZED_DIR.mkdirs());
104 
105         ClassLoader cl = DexClassLoaderTest.class.getClassLoader();
106         copyResource(cl, JAR_NAME, JAR_FILE);
107         copyResource(cl, DEX_NAME, DEX_FILE);
108         copyResource(cl, JAR2_NAME, JAR2_FILE);
109         copyResource(cl, DEX2_NAME, DEX2_FILE);
110     }
111 
tearDown()112     protected void tearDown() {
113         cleanUpDir(DEFAULT_OPTIMIZED_DIR);
114         cleanUpDir(INIT1_OPTIMIZED_DIR);
115         cleanUpDir(INIT2_OPTIMIZED_DIR);
116     }
117 
cleanUpDir(File dir)118     private void cleanUpDir(File dir) {
119         if (!dir.isDirectory()) {
120             return;
121         }
122         File[] files = dir.listFiles();
123         for (File file : files) {
124             assertTrue(file.delete());
125         }
126     }
127 
128     /**
129      * Copy a resource in the package directory to the indicated
130      * target file, but only if the target file doesn't exist.
131      */
copyResource(ClassLoader loader, String resourceName, File destination)132     private static void copyResource(ClassLoader loader, String resourceName,
133             File destination) throws IOException {
134         if (destination.exists()) {
135             return;
136         }
137 
138         InputStream in =
139             loader.getResourceAsStream(PACKAGE_PATH + resourceName);
140         FileOutputStream out = new FileOutputStream(destination);
141         Streams.copy(in, out);
142         in.close();
143         out.close();
144     }
145 
146     /**
147      * Helper to construct an instance to test.
148      *
149      * @param config how to configure the classpath
150      */
createInstance(Configuration config)151     private static DexClassLoader createInstance(Configuration config) {
152         return new DexClassLoader(
153             config.path, config.optimizedDir.getAbsolutePath(), null,
154             ClassLoader.getSystemClassLoader());
155     }
156 
157     /**
158      * Helper to construct an instance to test, using the jar file as
159      * the source, and call a named no-argument static method on a
160      * named class.
161      *
162      * @param config how to configure the classpath
163      */
createInstanceAndCallStaticMethod( Configuration config, String className, String methodName)164     public static Object createInstanceAndCallStaticMethod(
165             Configuration config, String className, String methodName)
166             throws ClassNotFoundException, NoSuchMethodException,
167             IllegalAccessException, InvocationTargetException {
168         DexClassLoader dcl = createInstance(config);
169         Class c = dcl.loadClass(className);
170         Method m = c.getMethod(methodName, (Class[]) null);
171         return m.invoke(null, (Object[]) null);
172     }
173 
174     /*
175      * Tests that are parametric with respect to whether to use a jar
176      * file or a dex file as the source of the code
177      */
178 
179     /**
180      * Just a trivial test of construction. This one merely makes
181      * sure that a valid construction doesn't fail. It doesn't try
182      * to verify anything about the constructed instance, other than
183      * checking for the existence of optimized dex files.
184      */
test_init(Configuration config)185     private static void test_init(Configuration config) {
186         createInstance(config);
187 
188         int expectedFiles = config.expectedFiles;
189         int actualFiles = config.optimizedDir.listFiles().length;
190 
191         assertEquals(expectedFiles, actualFiles);
192     }
193 
194     /**
195      * Check that a class in the jar/dex file may be used successfully. In this
196      * case, a trivial static method is called.
197      */
test_simpleUse(Configuration config)198     private static void test_simpleUse(Configuration config) throws Exception {
199         String result = (String)
200             createInstanceAndCallStaticMethod(config, "test.Test1", "test");
201 
202         assertSame("blort", result);
203     }
204 
205     /*
206      * All the following tests are just pass-throughs to test code
207      * that lives inside the loading-test dex/jar file.
208      */
209 
test_constructor(Configuration config)210     private static void test_constructor(Configuration config)
211             throws Exception {
212         createInstanceAndCallStaticMethod(
213             config, "test.TestMethods", "test_constructor");
214     }
215 
test_callStaticMethod(Configuration config)216     private static void test_callStaticMethod(Configuration config)
217             throws Exception {
218         createInstanceAndCallStaticMethod(
219             config, "test.TestMethods", "test_callStaticMethod");
220     }
221 
test_getStaticVariable(Configuration config)222     private static void test_getStaticVariable(Configuration config)
223             throws Exception {
224         createInstanceAndCallStaticMethod(
225             config, "test.TestMethods", "test_getStaticVariable");
226     }
227 
test_callInstanceMethod(Configuration config)228     private static void test_callInstanceMethod(Configuration config)
229             throws Exception {
230         createInstanceAndCallStaticMethod(
231             config, "test.TestMethods", "test_callInstanceMethod");
232     }
233 
test_getInstanceVariable(Configuration config)234     private static void test_getInstanceVariable(Configuration config)
235             throws Exception {
236         createInstanceAndCallStaticMethod(
237             config, "test.TestMethods", "test_getInstanceVariable");
238     }
239 
test_diff_constructor(Configuration config)240     private static void test_diff_constructor(Configuration config)
241             throws Exception {
242         createInstanceAndCallStaticMethod(
243             config, "test.TestMethods", "test_diff_constructor");
244     }
245 
test_diff_callStaticMethod(Configuration config)246     private static void test_diff_callStaticMethod(Configuration config)
247             throws Exception {
248         createInstanceAndCallStaticMethod(
249             config, "test.TestMethods", "test_diff_callStaticMethod");
250     }
251 
test_diff_getStaticVariable(Configuration config)252     private static void test_diff_getStaticVariable(Configuration config)
253             throws Exception {
254         createInstanceAndCallStaticMethod(
255             config, "test.TestMethods", "test_diff_getStaticVariable");
256     }
257 
test_diff_callInstanceMethod(Configuration config)258     private static void test_diff_callInstanceMethod(Configuration config)
259             throws Exception {
260         createInstanceAndCallStaticMethod(
261             config, "test.TestMethods", "test_diff_callInstanceMethod");
262     }
263 
test_diff_getInstanceVariable(Configuration config)264     private static void test_diff_getInstanceVariable(Configuration config)
265             throws Exception {
266         createInstanceAndCallStaticMethod(
267             config, "test.TestMethods", "test_diff_getInstanceVariable");
268     }
269 
270     /*
271      * These methods are all essentially just calls to the
272      * parametrically-defined tests above.
273      */
274 
275     // ONE_JAR
276 
test_oneJar_init()277     public void test_oneJar_init() throws Exception {
278         test_init(Configuration.ONE_JAR_INIT);
279     }
280 
test_oneJar_simpleUse()281     public void test_oneJar_simpleUse() throws Exception {
282         test_simpleUse(Configuration.ONE_JAR);
283     }
284 
test_oneJar_constructor()285     public void test_oneJar_constructor() throws Exception {
286         test_constructor(Configuration.ONE_JAR);
287     }
288 
test_oneJar_callStaticMethod()289     public void test_oneJar_callStaticMethod() throws Exception {
290         test_callStaticMethod(Configuration.ONE_JAR);
291     }
292 
test_oneJar_getStaticVariable()293     public void test_oneJar_getStaticVariable() throws Exception {
294         test_getStaticVariable(Configuration.ONE_JAR);
295     }
296 
test_oneJar_callInstanceMethod()297     public void test_oneJar_callInstanceMethod() throws Exception {
298         test_callInstanceMethod(Configuration.ONE_JAR);
299     }
300 
test_oneJar_getInstanceVariable()301     public void test_oneJar_getInstanceVariable() throws Exception {
302         test_getInstanceVariable(Configuration.ONE_JAR);
303     }
304 
305     // ONE_DEX
306 
test_oneDex_init()307     public void test_oneDex_init() throws Exception {
308         test_init(Configuration.ONE_DEX_INIT);
309     }
310 
test_oneDex_simpleUse()311     public void test_oneDex_simpleUse() throws Exception {
312         test_simpleUse(Configuration.ONE_DEX);
313     }
314 
test_oneDex_constructor()315     public void test_oneDex_constructor() throws Exception {
316         test_constructor(Configuration.ONE_DEX);
317     }
318 
test_oneDex_callStaticMethod()319     public void test_oneDex_callStaticMethod() throws Exception {
320         test_callStaticMethod(Configuration.ONE_DEX);
321     }
322 
test_oneDex_getStaticVariable()323     public void test_oneDex_getStaticVariable() throws Exception {
324         test_getStaticVariable(Configuration.ONE_DEX);
325     }
326 
test_oneDex_callInstanceMethod()327     public void test_oneDex_callInstanceMethod() throws Exception {
328         test_callInstanceMethod(Configuration.ONE_DEX);
329     }
330 
test_oneDex_getInstanceVariable()331     public void test_oneDex_getInstanceVariable() throws Exception {
332         test_getInstanceVariable(Configuration.ONE_DEX);
333     }
334 
335     // TWO_JAR
336 
test_twoJar_init()337     public void test_twoJar_init() throws Exception {
338         test_init(Configuration.TWO_JAR_INIT);
339     }
340 
test_twoJar_simpleUse()341     public void test_twoJar_simpleUse() throws Exception {
342         test_simpleUse(Configuration.TWO_JAR);
343     }
344 
test_twoJar_constructor()345     public void test_twoJar_constructor() throws Exception {
346         test_constructor(Configuration.TWO_JAR);
347     }
348 
test_twoJar_callStaticMethod()349     public void test_twoJar_callStaticMethod() throws Exception {
350         test_callStaticMethod(Configuration.TWO_JAR);
351     }
352 
test_twoJar_getStaticVariable()353     public void test_twoJar_getStaticVariable() throws Exception {
354         test_getStaticVariable(Configuration.TWO_JAR);
355     }
356 
test_twoJar_callInstanceMethod()357     public void test_twoJar_callInstanceMethod() throws Exception {
358         test_callInstanceMethod(Configuration.TWO_JAR);
359     }
360 
test_twoJar_getInstanceVariable()361     public void test_twoJar_getInstanceVariable() throws Exception {
362         test_getInstanceVariable(Configuration.TWO_JAR);
363     }
364 
test_twoJar_diff_constructor()365     public static void test_twoJar_diff_constructor() throws Exception {
366         test_diff_constructor(Configuration.TWO_JAR);
367     }
368 
test_twoJar_diff_callStaticMethod()369     public static void test_twoJar_diff_callStaticMethod() throws Exception {
370         test_diff_callStaticMethod(Configuration.TWO_JAR);
371     }
372 
test_twoJar_diff_getStaticVariable()373     public static void test_twoJar_diff_getStaticVariable() throws Exception {
374         test_diff_getStaticVariable(Configuration.TWO_JAR);
375     }
376 
test_twoJar_diff_callInstanceMethod()377     public static void test_twoJar_diff_callInstanceMethod()
378             throws Exception {
379         test_diff_callInstanceMethod(Configuration.TWO_JAR);
380     }
381 
test_twoJar_diff_getInstanceVariable()382     public static void test_twoJar_diff_getInstanceVariable()
383             throws Exception {
384         test_diff_getInstanceVariable(Configuration.TWO_JAR);
385     }
386 
387     // TWO_DEX
388 
test_twoDex_init()389     public void test_twoDex_init() throws Exception {
390         test_init(Configuration.TWO_DEX_INIT);
391     }
392 
test_twoDex_simpleUse()393     public void test_twoDex_simpleUse() throws Exception {
394         test_simpleUse(Configuration.TWO_DEX);
395     }
396 
test_twoDex_constructor()397     public void test_twoDex_constructor() throws Exception {
398         test_constructor(Configuration.TWO_DEX);
399     }
400 
test_twoDex_callStaticMethod()401     public void test_twoDex_callStaticMethod() throws Exception {
402         test_callStaticMethod(Configuration.TWO_DEX);
403     }
404 
test_twoDex_getStaticVariable()405     public void test_twoDex_getStaticVariable() throws Exception {
406         test_getStaticVariable(Configuration.TWO_DEX);
407     }
408 
test_twoDex_callInstanceMethod()409     public void test_twoDex_callInstanceMethod() throws Exception {
410         test_callInstanceMethod(Configuration.TWO_DEX);
411     }
412 
test_twoDex_getInstanceVariable()413     public void test_twoDex_getInstanceVariable() throws Exception {
414         test_getInstanceVariable(Configuration.TWO_DEX);
415     }
416 
test_twoDex_diff_constructor()417     public static void test_twoDex_diff_constructor() throws Exception {
418         test_diff_constructor(Configuration.TWO_DEX);
419     }
420 
test_twoDex_diff_callStaticMethod()421     public static void test_twoDex_diff_callStaticMethod() throws Exception {
422         test_diff_callStaticMethod(Configuration.TWO_DEX);
423     }
424 
test_twoDex_diff_getStaticVariable()425     public static void test_twoDex_diff_getStaticVariable() throws Exception {
426         test_diff_getStaticVariable(Configuration.TWO_DEX);
427     }
428 
test_twoDex_diff_callInstanceMethod()429     public static void test_twoDex_diff_callInstanceMethod()
430             throws Exception {
431         test_diff_callInstanceMethod(Configuration.TWO_DEX);
432     }
433 
test_twoDex_diff_getInstanceVariable()434     public static void test_twoDex_diff_getInstanceVariable()
435             throws Exception {
436         test_diff_getInstanceVariable(Configuration.TWO_DEX);
437     }
438 
439     /*
440      * Tests specifically for resource-related functionality.  Since
441      * raw dex files don't contain resources, these test only work
442      * with jar files. The first couple methods here are helpers,
443      * and they are followed by the tests per se.
444      */
445 
446     /**
447      * Check that a given resource (by name) is retrievable and contains
448      * the given expected contents.
449      */
test_directGetResourceAsStream(Configuration config, String resourceName, String expectedContents)450     private static void test_directGetResourceAsStream(Configuration config,
451             String resourceName, String expectedContents)
452             throws Exception {
453         DexClassLoader dcl = createInstance(config);
454         InputStream in = dcl.getResourceAsStream(resourceName);
455         byte[] contents = Streams.readFully(in);
456         String s = new String(contents, "UTF-8");
457 
458         assertEquals(expectedContents, s);
459     }
460 
461     /**
462      * Check that a resource in the jar file is retrievable and contains
463      * the expected contents.
464      */
test_directGetResourceAsStream(Configuration config)465     private static void test_directGetResourceAsStream(Configuration config)
466             throws Exception {
467         test_directGetResourceAsStream(
468             config, "test/Resource1.txt", "Muffins are tasty!\n");
469     }
470 
471     /**
472      * Check that a resource in the jar file can be retrieved from
473      * a class within that jar file.
474      */
test_getResourceAsStream(Configuration config)475     private static void test_getResourceAsStream(Configuration config)
476             throws Exception {
477         createInstanceAndCallStaticMethod(
478             config, "test.TestMethods", "test_getResourceAsStream");
479     }
480 
test_oneJar_directGetResourceAsStream()481     public void test_oneJar_directGetResourceAsStream() throws Exception {
482         test_directGetResourceAsStream(Configuration.ONE_JAR);
483     }
484 
test_oneJar_getResourceAsStream()485     public void test_oneJar_getResourceAsStream() throws Exception {
486         test_getResourceAsStream(Configuration.ONE_JAR);
487     }
488 
test_twoJar_directGetResourceAsStream()489     public void test_twoJar_directGetResourceAsStream() throws Exception {
490         test_directGetResourceAsStream(Configuration.TWO_JAR);
491     }
492 
test_twoJar_getResourceAsStream()493     public void test_twoJar_getResourceAsStream() throws Exception {
494         test_getResourceAsStream(Configuration.TWO_JAR);
495     }
496 
497     /**
498      * Check that a resource in the second jar file is retrievable and
499      * contains the expected contents.
500      */
test_twoJar_diff_directGetResourceAsStream()501     public void test_twoJar_diff_directGetResourceAsStream()
502             throws Exception {
503         test_directGetResourceAsStream(
504             Configuration.TWO_JAR, "test2/Resource2.txt",
505             "Who doesn't like a good biscuit?\n");
506     }
507 
508     /**
509      * Check that a resource in a jar file can be retrieved from
510      * a class within the other jar file.
511      */
test_twoJar_diff_getResourceAsStream()512     public void test_twoJar_diff_getResourceAsStream()
513             throws Exception {
514         createInstanceAndCallStaticMethod(
515             Configuration.TWO_JAR, "test.TestMethods",
516             "test_diff_getResourceAsStream");
517     }
518 }
519