Home | History | Annotate | Download | only in system
      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 
     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 
     62     protected void tearDown() {
     63         cleanUpDir(srcDir);
     64         cleanUpDir(optimizedDir);
     65     }
     66 
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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 
    190     public void test_oneJar_constructor() throws Exception {
    191         createLoaderAndCallMethod("test.TestMethods", "test_constructor", jar1);
    192     }
    193 
    194     public void test_oneJar_callStaticMethod() throws Exception {
    195         createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", jar1);
    196     }
    197 
    198     public void test_oneJar_getStaticVariable() throws Exception {
    199         createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", jar1);
    200     }
    201 
    202     public void test_oneJar_callInstanceMethod() throws Exception {
    203         createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", jar1);
    204     }
    205 
    206     public void test_oneJar_getInstanceVariable() throws Exception {
    207         createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", jar1);
    208     }
    209 
    210     // ONE_DEX
    211 
    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 
    219     public void test_oneDex_simpleUse() throws Exception {
    220         String result = (String) createLoaderAndCallMethod("test.Test1", "test", dex1);
    221         assertSame("blort", result);
    222     }
    223 
    224     public void test_oneDex_constructor() throws Exception {
    225         createLoaderAndCallMethod("test.TestMethods", "test_constructor", dex1);
    226     }
    227 
    228     public void test_oneDex_callStaticMethod() throws Exception {
    229         createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1);
    230     }
    231 
    232     public void test_oneDex_getStaticVariable() throws Exception {
    233         createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1);
    234     }
    235 
    236     public void test_oneDex_callInstanceMethod() throws Exception {
    237         createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1);
    238     }
    239 
    240     public void test_oneDex_getInstanceVariable() throws Exception {
    241         createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", dex1);
    242     }
    243 
    244     // TWO_JAR
    245 
    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 
    253     public void test_twoJar_simpleUse() throws Exception {
    254         String result = (String) createLoaderAndCallMethod("test.Test1", "test", jar1, jar2);
    255         assertSame("blort", result);
    256     }
    257 
    258     public void test_twoJar_constructor() throws Exception {
    259         createLoaderAndCallMethod("test.TestMethods", "test_constructor", jar1, jar2);
    260     }
    261 
    262     public void test_twoJar_callStaticMethod() throws Exception {
    263         createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", jar1, jar2);
    264     }
    265 
    266     public void test_twoJar_getStaticVariable() throws Exception {
    267         createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", jar1, jar2);
    268     }
    269 
    270     public void test_twoJar_callInstanceMethod() throws Exception {
    271         createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", jar1, jar2);
    272     }
    273 
    274     public void test_twoJar_getInstanceVariable() throws Exception {
    275         createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", jar1, jar2);
    276     }
    277 
    278     public void test_twoJar_diff_constructor() throws Exception {
    279         createLoaderAndCallMethod("test.TestMethods", "test_diff_constructor", jar1, jar2);
    280     }
    281 
    282     public void test_twoJar_diff_callStaticMethod() throws Exception {
    283         createLoaderAndCallMethod("test.TestMethods", "test_diff_callStaticMethod", jar1, jar2);
    284     }
    285 
    286     public void test_twoJar_diff_getStaticVariable() throws Exception {
    287         createLoaderAndCallMethod("test.TestMethods", "test_diff_getStaticVariable", jar1, jar2);
    288     }
    289 
    290     public void test_twoJar_diff_callInstanceMethod() throws Exception {
    291         createLoaderAndCallMethod("test.TestMethods", "test_diff_callInstanceMethod", jar1, jar2);
    292     }
    293 
    294     public void test_twoJar_diff_getInstanceVariable() throws Exception {
    295         createLoaderAndCallMethod("test.TestMethods", "test_diff_getInstanceVariable", jar1, jar2);
    296     }
    297 
    298     // TWO_DEX
    299 
    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 
    307     public void test_twoDex_simpleUse() throws Exception {
    308         String result = (String) createLoaderAndCallMethod("test.Test1", "test", dex1, dex2);
    309         assertSame("blort", result);
    310     }
    311 
    312     public void test_twoDex_constructor() throws Exception {
    313         createLoaderAndCallMethod("test.TestMethods", "test_constructor", dex1, dex2);
    314     }
    315 
    316     public void test_twoDex_callStaticMethod() throws Exception {
    317         createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1, dex2);
    318     }
    319 
    320     public void test_twoDex_getStaticVariable() throws Exception {
    321         createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1, dex2);
    322     }
    323 
    324     public void test_twoDex_callInstanceMethod() throws Exception {
    325         createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1, dex2);
    326     }
    327 
    328     public void test_twoDex_getInstanceVariable() throws Exception {
    329         createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", dex1, dex2);
    330     }
    331 
    332     public void test_twoDex_diff_constructor() throws Exception {
    333         createLoaderAndCallMethod("test.TestMethods", "test_diff_constructor", dex1, dex2);
    334     }
    335 
    336     public void test_twoDex_diff_callStaticMethod() throws Exception {
    337         createLoaderAndCallMethod("test.TestMethods", "test_diff_callStaticMethod", dex1, dex2);
    338     }
    339 
    340     public void test_twoDex_diff_getStaticVariable() throws Exception {
    341         createLoaderAndCallMethod("test.TestMethods", "test_diff_getStaticVariable", dex1, dex2);
    342     }
    343 
    344     public void test_twoDex_diff_callInstanceMethod() throws Exception {
    345         createLoaderAndCallMethod("test.TestMethods", "test_diff_callInstanceMethod", dex1, dex2);
    346     }
    347 
    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      */
    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      */
    371     public void test_oneJar_getResourceAsStream() throws Exception {
    372         createLoaderAndCallMethod("test.TestMethods", "test_getResourceAsStream", jar1);
    373     }
    374 
    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 
    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      */
    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      */
    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      */
    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