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