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.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 
     82         Configuration(int expectedFiles, File... files) {
     83             this(DEFAULT_OPTIMIZED_DIR, expectedFiles, files);
     84         }
     85 
     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 
     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 
    112     protected void tearDown() {
    113         cleanUpDir(DEFAULT_OPTIMIZED_DIR);
    114         cleanUpDir(INIT1_OPTIMIZED_DIR);
    115         cleanUpDir(INIT2_OPTIMIZED_DIR);
    116     }
    117 
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    210     private static void test_constructor(Configuration config)
    211             throws Exception {
    212         createInstanceAndCallStaticMethod(
    213             config, "test.TestMethods", "test_constructor");
    214     }
    215 
    216     private static void test_callStaticMethod(Configuration config)
    217             throws Exception {
    218         createInstanceAndCallStaticMethod(
    219             config, "test.TestMethods", "test_callStaticMethod");
    220     }
    221 
    222     private static void test_getStaticVariable(Configuration config)
    223             throws Exception {
    224         createInstanceAndCallStaticMethod(
    225             config, "test.TestMethods", "test_getStaticVariable");
    226     }
    227 
    228     private static void test_callInstanceMethod(Configuration config)
    229             throws Exception {
    230         createInstanceAndCallStaticMethod(
    231             config, "test.TestMethods", "test_callInstanceMethod");
    232     }
    233 
    234     private static void test_getInstanceVariable(Configuration config)
    235             throws Exception {
    236         createInstanceAndCallStaticMethod(
    237             config, "test.TestMethods", "test_getInstanceVariable");
    238     }
    239 
    240     private static void test_diff_constructor(Configuration config)
    241             throws Exception {
    242         createInstanceAndCallStaticMethod(
    243             config, "test.TestMethods", "test_diff_constructor");
    244     }
    245 
    246     private static void test_diff_callStaticMethod(Configuration config)
    247             throws Exception {
    248         createInstanceAndCallStaticMethod(
    249             config, "test.TestMethods", "test_diff_callStaticMethod");
    250     }
    251 
    252     private static void test_diff_getStaticVariable(Configuration config)
    253             throws Exception {
    254         createInstanceAndCallStaticMethod(
    255             config, "test.TestMethods", "test_diff_getStaticVariable");
    256     }
    257 
    258     private static void test_diff_callInstanceMethod(Configuration config)
    259             throws Exception {
    260         createInstanceAndCallStaticMethod(
    261             config, "test.TestMethods", "test_diff_callInstanceMethod");
    262     }
    263 
    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 
    277     public void test_oneJar_init() throws Exception {
    278         test_init(Configuration.ONE_JAR_INIT);
    279     }
    280 
    281     public void test_oneJar_simpleUse() throws Exception {
    282         test_simpleUse(Configuration.ONE_JAR);
    283     }
    284 
    285     public void test_oneJar_constructor() throws Exception {
    286         test_constructor(Configuration.ONE_JAR);
    287     }
    288 
    289     public void test_oneJar_callStaticMethod() throws Exception {
    290         test_callStaticMethod(Configuration.ONE_JAR);
    291     }
    292 
    293     public void test_oneJar_getStaticVariable() throws Exception {
    294         test_getStaticVariable(Configuration.ONE_JAR);
    295     }
    296 
    297     public void test_oneJar_callInstanceMethod() throws Exception {
    298         test_callInstanceMethod(Configuration.ONE_JAR);
    299     }
    300 
    301     public void test_oneJar_getInstanceVariable() throws Exception {
    302         test_getInstanceVariable(Configuration.ONE_JAR);
    303     }
    304 
    305     // ONE_DEX
    306 
    307     public void test_oneDex_init() throws Exception {
    308         test_init(Configuration.ONE_DEX_INIT);
    309     }
    310 
    311     public void test_oneDex_simpleUse() throws Exception {
    312         test_simpleUse(Configuration.ONE_DEX);
    313     }
    314 
    315     public void test_oneDex_constructor() throws Exception {
    316         test_constructor(Configuration.ONE_DEX);
    317     }
    318 
    319     public void test_oneDex_callStaticMethod() throws Exception {
    320         test_callStaticMethod(Configuration.ONE_DEX);
    321     }
    322 
    323     public void test_oneDex_getStaticVariable() throws Exception {
    324         test_getStaticVariable(Configuration.ONE_DEX);
    325     }
    326 
    327     public void test_oneDex_callInstanceMethod() throws Exception {
    328         test_callInstanceMethod(Configuration.ONE_DEX);
    329     }
    330 
    331     public void test_oneDex_getInstanceVariable() throws Exception {
    332         test_getInstanceVariable(Configuration.ONE_DEX);
    333     }
    334 
    335     // TWO_JAR
    336 
    337     public void test_twoJar_init() throws Exception {
    338         test_init(Configuration.TWO_JAR_INIT);
    339     }
    340 
    341     public void test_twoJar_simpleUse() throws Exception {
    342         test_simpleUse(Configuration.TWO_JAR);
    343     }
    344 
    345     public void test_twoJar_constructor() throws Exception {
    346         test_constructor(Configuration.TWO_JAR);
    347     }
    348 
    349     public void test_twoJar_callStaticMethod() throws Exception {
    350         test_callStaticMethod(Configuration.TWO_JAR);
    351     }
    352 
    353     public void test_twoJar_getStaticVariable() throws Exception {
    354         test_getStaticVariable(Configuration.TWO_JAR);
    355     }
    356 
    357     public void test_twoJar_callInstanceMethod() throws Exception {
    358         test_callInstanceMethod(Configuration.TWO_JAR);
    359     }
    360 
    361     public void test_twoJar_getInstanceVariable() throws Exception {
    362         test_getInstanceVariable(Configuration.TWO_JAR);
    363     }
    364 
    365     public static void test_twoJar_diff_constructor() throws Exception {
    366         test_diff_constructor(Configuration.TWO_JAR);
    367     }
    368 
    369     public static void test_twoJar_diff_callStaticMethod() throws Exception {
    370         test_diff_callStaticMethod(Configuration.TWO_JAR);
    371     }
    372 
    373     public static void test_twoJar_diff_getStaticVariable() throws Exception {
    374         test_diff_getStaticVariable(Configuration.TWO_JAR);
    375     }
    376 
    377     public static void test_twoJar_diff_callInstanceMethod()
    378             throws Exception {
    379         test_diff_callInstanceMethod(Configuration.TWO_JAR);
    380     }
    381 
    382     public static void test_twoJar_diff_getInstanceVariable()
    383             throws Exception {
    384         test_diff_getInstanceVariable(Configuration.TWO_JAR);
    385     }
    386 
    387     // TWO_DEX
    388 
    389     public void test_twoDex_init() throws Exception {
    390         test_init(Configuration.TWO_DEX_INIT);
    391     }
    392 
    393     public void test_twoDex_simpleUse() throws Exception {
    394         test_simpleUse(Configuration.TWO_DEX);
    395     }
    396 
    397     public void test_twoDex_constructor() throws Exception {
    398         test_constructor(Configuration.TWO_DEX);
    399     }
    400 
    401     public void test_twoDex_callStaticMethod() throws Exception {
    402         test_callStaticMethod(Configuration.TWO_DEX);
    403     }
    404 
    405     public void test_twoDex_getStaticVariable() throws Exception {
    406         test_getStaticVariable(Configuration.TWO_DEX);
    407     }
    408 
    409     public void test_twoDex_callInstanceMethod() throws Exception {
    410         test_callInstanceMethod(Configuration.TWO_DEX);
    411     }
    412 
    413     public void test_twoDex_getInstanceVariable() throws Exception {
    414         test_getInstanceVariable(Configuration.TWO_DEX);
    415     }
    416 
    417     public static void test_twoDex_diff_constructor() throws Exception {
    418         test_diff_constructor(Configuration.TWO_DEX);
    419     }
    420 
    421     public static void test_twoDex_diff_callStaticMethod() throws Exception {
    422         test_diff_callStaticMethod(Configuration.TWO_DEX);
    423     }
    424 
    425     public static void test_twoDex_diff_getStaticVariable() throws Exception {
    426         test_diff_getStaticVariable(Configuration.TWO_DEX);
    427     }
    428 
    429     public static void test_twoDex_diff_callInstanceMethod()
    430             throws Exception {
    431         test_diff_callInstanceMethod(Configuration.TWO_DEX);
    432     }
    433 
    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      */
    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      */
    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      */
    475     private static void test_getResourceAsStream(Configuration config)
    476             throws Exception {
    477         createInstanceAndCallStaticMethod(
    478             config, "test.TestMethods", "test_getResourceAsStream");
    479     }
    480 
    481     public void test_oneJar_directGetResourceAsStream() throws Exception {
    482         test_directGetResourceAsStream(Configuration.ONE_JAR);
    483     }
    484 
    485     public void test_oneJar_getResourceAsStream() throws Exception {
    486         test_getResourceAsStream(Configuration.ONE_JAR);
    487     }
    488 
    489     public void test_twoJar_directGetResourceAsStream() throws Exception {
    490         test_directGetResourceAsStream(Configuration.TWO_JAR);
    491     }
    492 
    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      */
    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      */
    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