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