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     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 
     60         Configuration(int expectedFiles) {
     61             this.expectedFiles = expectedFiles;
     62         }
     63     }
     64 
     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      */
     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      */
    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      */
    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      */
    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      */
    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 
    179     private static void test_constructor(Configuration config)
    180             throws Exception {
    181         createInstanceAndCallStaticMethod(
    182             config, "test.TestMethods", "test_constructor");
    183     }
    184 
    185     private static void test_callStaticMethod(Configuration config)
    186             throws Exception {
    187         createInstanceAndCallStaticMethod(
    188             config, "test.TestMethods", "test_callStaticMethod");
    189     }
    190 
    191     private static void test_getStaticVariable(Configuration config)
    192             throws Exception {
    193         createInstanceAndCallStaticMethod(
    194             config, "test.TestMethods", "test_getStaticVariable");
    195     }
    196 
    197     private static void test_callInstanceMethod(Configuration config)
    198             throws Exception {
    199         createInstanceAndCallStaticMethod(
    200             config, "test.TestMethods", "test_callInstanceMethod");
    201     }
    202 
    203     private static void test_getInstanceVariable(Configuration config)
    204             throws Exception {
    205         createInstanceAndCallStaticMethod(
    206             config, "test.TestMethods", "test_getInstanceVariable");
    207     }
    208 
    209     private static void test_diff_constructor(Configuration config)
    210             throws Exception {
    211         createInstanceAndCallStaticMethod(
    212             config, "test.TestMethods", "test_diff_constructor");
    213     }
    214 
    215     private static void test_diff_callStaticMethod(Configuration config)
    216             throws Exception {
    217         createInstanceAndCallStaticMethod(
    218             config, "test.TestMethods", "test_diff_callStaticMethod");
    219     }
    220 
    221     private static void test_diff_getStaticVariable(Configuration config)
    222             throws Exception {
    223         createInstanceAndCallStaticMethod(
    224             config, "test.TestMethods", "test_diff_getStaticVariable");
    225     }
    226 
    227     private static void test_diff_callInstanceMethod(Configuration config)
    228             throws Exception {
    229         createInstanceAndCallStaticMethod(
    230             config, "test.TestMethods", "test_diff_callInstanceMethod");
    231     }
    232 
    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 
    246     public void test_oneJar_init() throws Exception {
    247         test_init(Configuration.ONE_JAR);
    248     }
    249 
    250     public void test_oneJar_simpleUse() throws Exception {
    251         test_simpleUse(Configuration.ONE_JAR);
    252     }
    253 
    254     public void test_oneJar_constructor() throws Exception {
    255         test_constructor(Configuration.ONE_JAR);
    256     }
    257 
    258     public void test_oneJar_callStaticMethod() throws Exception {
    259         test_callStaticMethod(Configuration.ONE_JAR);
    260     }
    261 
    262     public void test_oneJar_getStaticVariable() throws Exception {
    263         test_getStaticVariable(Configuration.ONE_JAR);
    264     }
    265 
    266     public void test_oneJar_callInstanceMethod() throws Exception {
    267         test_callInstanceMethod(Configuration.ONE_JAR);
    268     }
    269 
    270     public void test_oneJar_getInstanceVariable() throws Exception {
    271         test_getInstanceVariable(Configuration.ONE_JAR);
    272     }
    273 
    274     // ONE_DEX
    275 
    276     public void test_oneDex_init() throws Exception {
    277         test_init(Configuration.ONE_DEX);
    278     }
    279 
    280     public void test_oneDex_simpleUse() throws Exception {
    281         test_simpleUse(Configuration.ONE_DEX);
    282     }
    283 
    284     public void test_oneDex_constructor() throws Exception {
    285         test_constructor(Configuration.ONE_DEX);
    286     }
    287 
    288     public void test_oneDex_callStaticMethod() throws Exception {
    289         test_callStaticMethod(Configuration.ONE_DEX);
    290     }
    291 
    292     public void test_oneDex_getStaticVariable() throws Exception {
    293         test_getStaticVariable(Configuration.ONE_DEX);
    294     }
    295 
    296     public void test_oneDex_callInstanceMethod() throws Exception {
    297         test_callInstanceMethod(Configuration.ONE_DEX);
    298     }
    299 
    300     public void test_oneDex_getInstanceVariable() throws Exception {
    301         test_getInstanceVariable(Configuration.ONE_DEX);
    302     }
    303 
    304     // TWO_JAR
    305 
    306     public void test_twoJar_init() throws Exception {
    307         test_init(Configuration.TWO_JAR);
    308     }
    309 
    310     public void test_twoJar_simpleUse() throws Exception {
    311         test_simpleUse(Configuration.TWO_JAR);
    312     }
    313 
    314     public void test_twoJar_constructor() throws Exception {
    315         test_constructor(Configuration.TWO_JAR);
    316     }
    317 
    318     public void test_twoJar_callStaticMethod() throws Exception {
    319         test_callStaticMethod(Configuration.TWO_JAR);
    320     }
    321 
    322     public void test_twoJar_getStaticVariable() throws Exception {
    323         test_getStaticVariable(Configuration.TWO_JAR);
    324     }
    325 
    326     public void test_twoJar_callInstanceMethod() throws Exception {
    327         test_callInstanceMethod(Configuration.TWO_JAR);
    328     }
    329 
    330     public void test_twoJar_getInstanceVariable() throws Exception {
    331         test_getInstanceVariable(Configuration.TWO_JAR);
    332     }
    333 
    334     public static void test_twoJar_diff_constructor() throws Exception {
    335         test_diff_constructor(Configuration.TWO_JAR);
    336     }
    337 
    338     public static void test_twoJar_diff_callStaticMethod() throws Exception {
    339         test_diff_callStaticMethod(Configuration.TWO_JAR);
    340     }
    341 
    342     public static void test_twoJar_diff_getStaticVariable() throws Exception {
    343         test_diff_getStaticVariable(Configuration.TWO_JAR);
    344     }
    345 
    346     public static void test_twoJar_diff_callInstanceMethod()
    347             throws Exception {
    348         test_diff_callInstanceMethod(Configuration.TWO_JAR);
    349     }
    350 
    351     public static void test_twoJar_diff_getInstanceVariable()
    352             throws Exception {
    353         test_diff_getInstanceVariable(Configuration.TWO_JAR);
    354     }
    355 
    356     // TWO_DEX
    357 
    358     public void test_twoDex_init() throws Exception {
    359         test_init(Configuration.TWO_DEX);
    360     }
    361 
    362     public void test_twoDex_simpleUse() throws Exception {
    363         test_simpleUse(Configuration.TWO_DEX);
    364     }
    365 
    366     public void test_twoDex_constructor() throws Exception {
    367         test_constructor(Configuration.TWO_DEX);
    368     }
    369 
    370     public void test_twoDex_callStaticMethod() throws Exception {
    371         test_callStaticMethod(Configuration.TWO_DEX);
    372     }
    373 
    374     public void test_twoDex_getStaticVariable() throws Exception {
    375         test_getStaticVariable(Configuration.TWO_DEX);
    376     }
    377 
    378     public void test_twoDex_callInstanceMethod() throws Exception {
    379         test_callInstanceMethod(Configuration.TWO_DEX);
    380     }
    381 
    382     public void test_twoDex_getInstanceVariable() throws Exception {
    383         test_getInstanceVariable(Configuration.TWO_DEX);
    384     }
    385 
    386     public static void test_twoDex_diff_constructor() throws Exception {
    387         test_diff_constructor(Configuration.TWO_DEX);
    388     }
    389 
    390     public static void test_twoDex_diff_callStaticMethod() throws Exception {
    391         test_diff_callStaticMethod(Configuration.TWO_DEX);
    392     }
    393 
    394     public static void test_twoDex_diff_getStaticVariable() throws Exception {
    395         test_diff_getStaticVariable(Configuration.TWO_DEX);
    396     }
    397 
    398     public static void test_twoDex_diff_callInstanceMethod()
    399             throws Exception {
    400         test_diff_callInstanceMethod(Configuration.TWO_DEX);
    401     }
    402 
    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      */
    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      */
    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      */
    444     private static void test_getResourceAsStream(Configuration config)
    445             throws Exception {
    446         createInstanceAndCallStaticMethod(
    447             config, "test.TestMethods", "test_getResourceAsStream");
    448     }
    449 
    450     public void test_oneJar_directGetResourceAsStream() throws Exception {
    451         test_directGetResourceAsStream(Configuration.ONE_JAR);
    452     }
    453 
    454     public void test_oneJar_getResourceAsStream() throws Exception {
    455         test_getResourceAsStream(Configuration.ONE_JAR);
    456     }
    457 
    458     public void test_twoJar_directGetResourceAsStream() throws Exception {
    459         test_directGetResourceAsStream(Configuration.TWO_JAR);
    460     }
    461 
    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      */
    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      */
    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