Home | History | Annotate | Download | only in system
      1 /*
      2  * Copyright (C) 2016 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 libcore.dalvik.system;
     18 
     19 import java.lang.reflect.Method;
     20 import java.io.File;
     21 import java.io.FileOutputStream;
     22 import java.io.IOException;
     23 import java.io.InputStream;
     24 import java.io.RandomAccessFile;
     25 import java.nio.ByteBuffer;
     26 import libcore.io.Streams;
     27 import junit.framework.TestCase;
     28 
     29 import dalvik.system.InMemoryDexClassLoader;
     30 
     31 /**
     32  * Tests for the class {@link InMemoryDexClassLoader}.
     33  */
     34 public class InMemoryDexClassLoaderTest extends TestCase {
     35     private static final String PACKAGE_PATH = "dalvik/system/";
     36 
     37     private File srcDir;
     38     private File dex1;
     39     private File dex2;
     40 
     41     protected void setUp() throws Exception {
     42         srcDir = File.createTempFile("src", "");
     43         assertTrue(srcDir.delete());
     44         assertTrue(srcDir.mkdirs());
     45 
     46         dex1 = new File(srcDir, "loading-test.dex");
     47         dex2 = new File(srcDir, "loading-test2.dex");
     48 
     49         copyResource("loading-test.dex", dex1);
     50         copyResource("loading-test2.dex", dex2);
     51     }
     52 
     53     protected void tearDown() {
     54         cleanUpDir(srcDir);
     55     }
     56 
     57     private static void cleanUpDir(File dir) {
     58         if (!dir.isDirectory()) {
     59             return;
     60         }
     61         File[] files = dir.listFiles();
     62         for (File file : files) {
     63             if (file.isDirectory()) {
     64                 cleanUpDir(file);
     65             } else {
     66                 assertTrue(file.delete());
     67             }
     68         }
     69         assertTrue(dir.delete());
     70     }
     71 
     72     /**
     73      * Copy a resource in the package directory to the indicated
     74      * target file.
     75      */
     76     private static void copyResource(String resourceName,
     77             File destination) throws IOException {
     78         ClassLoader loader = InMemoryDexClassLoaderTest.class.getClassLoader();
     79         InputStream in = loader.getResourceAsStream(PACKAGE_PATH + resourceName);
     80         if (in == null) {
     81             throw new IllegalStateException("Resource not found: " + PACKAGE_PATH + resourceName);
     82         }
     83         try (FileOutputStream out = new FileOutputStream(destination)) {
     84             Streams.copy(in, out);
     85         } finally {
     86             in.close();
     87         }
     88     }
     89 
     90     private static ByteBuffer ReadFileToByteBufferDirect(File file) throws IOException {
     91         try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
     92             ByteBuffer buffer = ByteBuffer.allocateDirect((int)file.length());
     93             int done = 0;
     94             while (done != file.length()) {
     95                 done += raf.getChannel().read(buffer);
     96             }
     97             buffer.rewind();
     98             return buffer;
     99         }
    100     }
    101 
    102     private static ByteBuffer ReadFileToByteBufferIndirect(File file) throws IOException {
    103         ByteBuffer direct = ReadFileToByteBufferDirect(file);
    104         byte[] array = new byte[direct.limit()];
    105         direct.get(array);
    106         return ByteBuffer.wrap(array);
    107     }
    108 
    109     /**
    110      * Helper to construct a InMemoryDexClassLoader instance to test.
    111      *
    112      * Creates InMemoryDexClassLoader from ByteBuffer instances that are
    113      * direct allocated.
    114      *
    115      * @param files The .dex files to use for the class path.
    116      */
    117     private static ClassLoader createLoaderDirect(File... files) throws IOException {
    118         assertNotNull(files);
    119         assertTrue(files.length > 0);
    120         ClassLoader result = ClassLoader.getSystemClassLoader();
    121         for (int i = 0; i < files.length; ++i) {
    122             ByteBuffer buffer = ReadFileToByteBufferDirect(files[i]);
    123             result = new InMemoryDexClassLoader(buffer, result);
    124         }
    125         return result;
    126     }
    127 
    128     /**
    129      * Helper to construct a InMemoryDexClassLoader instance to test.
    130      *
    131      * Creates InMemoryDexClassLoader from ByteBuffer instances that are
    132      * heap allocated.
    133      *
    134      * @param files The .dex files to use for the class path.
    135      */
    136     private static ClassLoader createLoaderIndirect(File... files) throws IOException {
    137         assertNotNull(files);
    138         assertTrue(files.length > 0);
    139         ClassLoader result = ClassLoader.getSystemClassLoader();
    140         for (int i = 0; i < files.length; ++i) {
    141             ByteBuffer buffer = ReadFileToByteBufferIndirect(files[i]);
    142             result = new InMemoryDexClassLoader(buffer, result);
    143         }
    144         return result;
    145     }
    146 
    147     /**
    148      * Helper to construct a new InMemoryDexClassLoader via direct
    149      * ByteBuffer instances.
    150      *
    151      * @param className The name of the class of the method to call.
    152      * @param methodName The name of the method to call.
    153      * @param files The .dex or .jar files to use for the class path.
    154      */
    155     private Object createLoaderDirectAndCallMethod(
    156             String className, String methodName, File... files)
    157             throws IOException, ReflectiveOperationException {
    158         ClassLoader cl = createLoaderDirect(files);
    159         Class c = cl.loadClass(className);
    160         Method m = c.getMethod(methodName, (Class[]) null);
    161         assertNotNull(m);
    162         return m.invoke(null, (Object[]) null);
    163     }
    164 
    165     /**
    166      * Helper to construct a new InMemoryDexClassLoader via indirect
    167      * ByteBuffer instances.
    168      *
    169      * @param className The name of the class of the method to call.
    170      * @param methodName The name of the method to call.
    171      * @param files The .dex or .jar files to use for the class path.
    172      */
    173     private Object createLoaderIndirectAndCallMethod(
    174             String className, String methodName, File... files)
    175             throws IOException, ReflectiveOperationException {
    176         ClassLoader cl = createLoaderIndirect(files);
    177         Class c = cl.loadClass(className);
    178         Method m = c.getMethod(methodName, (Class[]) null);
    179         assertNotNull(m);
    180         return m.invoke(null, (Object[]) null);
    181     }
    182 
    183     // ONE_DEX with direct ByteBuffer.
    184 
    185     public void test_oneDexDirect_simpleUse() throws Exception {
    186         String result = (String) createLoaderDirectAndCallMethod("test.Test1", "test", dex1);
    187         assertSame("blort", result);
    188     }
    189 
    190     public void test_oneDexDirect_constructor() throws Exception {
    191         createLoaderDirectAndCallMethod("test.TestMethods", "test_constructor", dex1);
    192     }
    193 
    194     public void test_oneDexDirect_callStaticMethod() throws Exception {
    195         createLoaderDirectAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1);
    196     }
    197 
    198     public void test_oneDexDirect_getStaticVariable() throws Exception {
    199         createLoaderDirectAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1);
    200     }
    201 
    202     public void test_oneDexDirect_callInstanceMethod() throws Exception {
    203         createLoaderDirectAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1);
    204     }
    205 
    206     public void test_oneDexDirect_getInstanceVariable() throws Exception {
    207         createLoaderDirectAndCallMethod("test.TestMethods", "test_getInstanceVariable", dex1);
    208     }
    209 
    210     // ONE_DEX with non-direct ByteBuffer.
    211 
    212     public void test_oneDexIndirect_simpleUse() throws Exception {
    213         String result = (String) createLoaderIndirectAndCallMethod("test.Test1", "test", dex1);
    214         assertSame("blort", result);
    215     }
    216 
    217     public void test_oneDexIndirect_constructor() throws Exception {
    218         createLoaderIndirectAndCallMethod("test.TestMethods", "test_constructor", dex1);
    219     }
    220 
    221     public void test_oneDexIndirect_callStaticMethod() throws Exception {
    222         createLoaderIndirectAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1);
    223     }
    224 
    225     public void test_oneDexIndirect_getStaticVariable() throws Exception {
    226         createLoaderIndirectAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1);
    227     }
    228 
    229     public void test_oneDexIndirect_callInstanceMethod() throws Exception {
    230         createLoaderIndirectAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1);
    231     }
    232 
    233     public void test_oneDexIndirect_getInstanceVariable() throws Exception {
    234         createLoaderIndirectAndCallMethod("test.TestMethods", "test_getInstanceVariable", dex1);
    235     }
    236 
    237     // TWO_DEX with direct ByteBuffer
    238 
    239     public void test_twoDexDirect_simpleUse() throws Exception {
    240         String result = (String) createLoaderDirectAndCallMethod("test.Test1", "test", dex1, dex2);
    241         assertSame("blort", result);
    242     }
    243 
    244     public void test_twoDexDirect_constructor() throws Exception {
    245         createLoaderDirectAndCallMethod("test.TestMethods", "test_constructor", dex1, dex2);
    246     }
    247 
    248     public void test_twoDexDirect_callStaticMethod() throws Exception {
    249         createLoaderDirectAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1, dex2);
    250     }
    251 
    252     public void test_twoDexDirect_getStaticVariable() throws Exception {
    253         createLoaderDirectAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1, dex2);
    254     }
    255 
    256     public void test_twoDexDirect_callInstanceMethod() throws Exception {
    257         createLoaderDirectAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1, dex2);
    258     }
    259 
    260     public void test_twoDexDirect_getInstanceVariable() throws Exception {
    261         createLoaderDirectAndCallMethod(
    262             "test.TestMethods", "test_getInstanceVariable", dex1, dex2);
    263     }
    264 
    265     public void test_twoDexDirect_target2_static_method() throws Exception {
    266         String result =
    267                 (String) createLoaderDirectAndCallMethod("test2.Target2", "frotz", dex1, dex2);
    268         assertSame("frotz", result);
    269     }
    270 
    271     public void test_twoDexDirect_diff_constructor() throws Exception {
    272         // NB Ordering dex2 then dex1 as classloader's are nested and
    273         // each only supports a single DEX image. The
    274         // test.TestMethods.test_diff* methods depend on dex2 hence
    275         // ordering.
    276         createLoaderDirectAndCallMethod("test.TestMethods", "test_diff_constructor", dex2, dex1);
    277     }
    278 
    279     public void test_twoDexDirect_diff_callStaticMethod() throws Exception {
    280         // NB See comment in test_twoDexDirect_diff_constructor.
    281         createLoaderDirectAndCallMethod(
    282             "test.TestMethods", "test_diff_callStaticMethod", dex2, dex1);
    283     }
    284 
    285     public void test_twoDexDirect_diff_getStaticVariable() throws Exception {
    286         // NB See comment in test_twoDexDirect_diff_constructor.
    287         createLoaderDirectAndCallMethod(
    288             "test.TestMethods", "test_diff_getStaticVariable", dex2, dex1);
    289     }
    290 
    291     public void test_twoDexDirect_diff_callInstanceMethod() throws Exception {
    292         // NB See comment in test_twoDexDirect_diff_constructor.
    293         createLoaderDirectAndCallMethod(
    294             "test.TestMethods", "test_diff_callInstanceMethod", dex2, dex1);
    295     }
    296 
    297     public void test_twoDexDirect_diff_getInstanceVariable() throws Exception {
    298         // NB See comment in test_twoDexDirect_diff_constructor.
    299         createLoaderDirectAndCallMethod(
    300             "test.TestMethods", "test_diff_getInstanceVariable", dex2, dex1);
    301     }
    302 }
    303