Home | History | Annotate | Download | only in system
      1 /*
      2  * Copyright (C) 2017 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 junit.framework.TestCase;
     20 import libcore.io.Streams;
     21 
     22 import java.io.File;
     23 import java.io.InputStream;
     24 import java.lang.reflect.Method;
     25 import java.net.URL;
     26 import java.nio.charset.StandardCharsets;
     27 import java.util.ArrayList;
     28 import java.util.Arrays;
     29 import java.util.Enumeration;
     30 import java.util.List;
     31 import java.util.Map;
     32 
     33 import dalvik.system.DelegateLastClassLoader;
     34 import dalvik.system.PathClassLoader;
     35 
     36 public class DelegateLastClassLoaderTest extends TestCase {
     37 
     38     // NOTE: the jar files used by this test are created by create_test_jars.sh in the same
     39     // directory as this test.
     40     //
     41     // Contents of child.jar
     42     // ----------------------------------------------------
     43     // A.java
     44     // ------
     45     // package libcore.test.delegatelast;
     46     // public class A {
     47     //   public String toString() {
     48     //     return "A_child";
     49     //   }
     50     // }
     51     //
     52     // Child.java
     53     // ----------
     54     // package libcore.test.delegatelast;
     55     //
     56     // public class Child {
     57     //   public String toString() {
     58     //     return "Child_child";
     59     //   }
     60     // }
     61     //
     62     // Contents of parent.jar
     63     // ----------------------------------------------------
     64     // A.java
     65     // ------
     66     // package libcore.test.delegatelast;
     67     // public class A {
     68     //   public String toString() {
     69     //     return "A_parent";
     70     //   }
     71     // }
     72     //
     73     // Parent.java
     74     // ----------
     75     // package libcore.test.delegatelast;
     76     //
     77     // public class Parent {
     78     //   public String toString() {
     79     //     return "Parent_parent";
     80     //   }
     81     // }
     82 
     83     private Map<String, File> resourcesMap;
     84 
     85     @Override
     86     public void setUp() throws Exception {
     87         resourcesMap = ClassLoaderTestSupport.setupAndCopyResources(
     88                 Arrays.asList("parent.jar", "child.jar", "bootoverride.jar"));
     89     }
     90 
     91     @Override
     92     public void tearDown() throws Exception {
     93         ClassLoaderTestSupport.cleanUpResources(resourcesMap);
     94     }
     95 
     96     private ClassLoader createClassLoader(String parentName, String thisName) {
     97         File parentPath = resourcesMap.get(parentName);
     98         File thisPath = resourcesMap.get(thisName);
     99         assertNotNull(parentPath);
    100         assertNotNull(thisPath);
    101 
    102         ClassLoader parent = new PathClassLoader(parentPath.getAbsolutePath(),
    103                 Object.class.getClassLoader());
    104 
    105         return new DelegateLastClassLoader(thisPath.getAbsolutePath(), parent);
    106     }
    107 
    108     private static String callMethod(ClassLoader cl, String name) throws Exception {
    109         Class<?> clazz = cl.loadClass("libcore.test.delegatelast.A");
    110         assertSame(cl, clazz.getClassLoader());
    111 
    112         Method method = clazz.getMethod(name);
    113         Object obj = clazz.newInstance();
    114         return (String) method.invoke(obj);
    115     }
    116 
    117     private static String readResource(ClassLoader cl, String resourceName) throws Exception {
    118         InputStream in = cl.getResourceAsStream(resourceName);
    119         assertNotNull(in);
    120 
    121         byte[] contents = Streams.readFully(in);
    122         return new String(contents, StandardCharsets.UTF_8);
    123     }
    124 
    125     private static List<String> readResources(ClassLoader cl, String resourceName)
    126             throws Exception {
    127         Enumeration<URL> resources = cl.getResources(resourceName);
    128 
    129         List<String> contents = new ArrayList<>();
    130         while (resources.hasMoreElements()) {
    131             URL url = resources.nextElement();
    132 
    133             try (InputStream is = url.openStream()) {
    134                 byte[] bytes = Streams.readFully(is);
    135                 contents.add(new String(bytes, StandardCharsets.UTF_8));
    136             }
    137         }
    138 
    139         return contents;
    140     }
    141 
    142     public void testLookupOrder_loadClass() throws Exception {
    143         ClassLoader delegate = createClassLoader("parent.jar", "child.jar");
    144         assertEquals("A_child", callMethod(delegate, "toString"));
    145 
    146         // Note that the order is reversed here. "parent" is looked up before "child".
    147         delegate = createClassLoader("child.jar", "parent.jar");
    148         assertEquals("A_parent", callMethod(delegate, "toString"));
    149     }
    150 
    151     public void testLookupOrder_foundInParent() throws Exception {
    152         ClassLoader delegate = createClassLoader("parent.jar", "child.jar");
    153 
    154         Class<?> child = delegate.loadClass("libcore.test.delegatelast.Child");
    155         assertSame(delegate, child.getClassLoader());
    156 
    157         Class<?> parent = delegate.loadClass("libcore.test.delegatelast.Parent");
    158         assertSame(delegate.getParent(), parent.getClassLoader());
    159     }
    160 
    161     public void testLoadClass_exceptionMessages() throws Exception {
    162         ClassLoader delegate = createClassLoader("parent.jar", "child.jar");
    163 
    164         ClassNotFoundException msgFromDelegate = null;
    165         try {
    166             delegate.loadClass("libcore.test.delegatelast.NonExistent");
    167             fail();
    168         } catch (ClassNotFoundException ex) {
    169             msgFromDelegate = ex;
    170         }
    171 
    172         ClassLoader pathClassLoader = new PathClassLoader(
    173                 resourcesMap.get("child.jar").getAbsolutePath(), null, null);
    174 
    175         ClassNotFoundException msgFromPathClassloader = null;
    176         try {
    177             pathClassLoader.loadClass("libcore.test.delegatelast.NonExistent");
    178             fail();
    179         } catch (ClassNotFoundException ex) {
    180             msgFromPathClassloader = ex;
    181         }
    182 
    183         assertEquals(msgFromPathClassloader.getMessage(), msgFromDelegate.getMessage());
    184     }
    185 
    186     public void testLookupOrder_getResource() throws Exception {
    187         ClassLoader delegate = createClassLoader("parent.jar", "child.jar");
    188         assertEquals("child", readResource(delegate, "resource.txt"));
    189 
    190         delegate = createClassLoader("child.jar", "parent.jar");
    191         assertEquals("parent", readResource(delegate, "resource.txt"));
    192     }
    193 
    194     public void testLookupOrder_getResources() throws Exception {
    195         ClassLoader delegate = createClassLoader("parent.jar", "child.jar");
    196         List<String> resources = readResources(delegate, "resource.txt");
    197 
    198         assertEquals(2, resources.size());
    199         assertEquals("child", resources.get(0));
    200         assertEquals("parent", resources.get(1));
    201 
    202         delegate = createClassLoader("child.jar", "parent.jar");
    203         resources = readResources(delegate, "resource.txt");
    204 
    205         assertEquals(2, resources.size());
    206         assertEquals("parent", resources.get(0));
    207         assertEquals("child", resources.get(1));
    208     }
    209 
    210     public void testLookupOrder_bootOverride() throws Exception {
    211         // The dex file in this jar contains a single class ("java.util.HashMap") and a single
    212         // resource ("android/icu/ICUConfig.properties"). Both of these appear in the boot
    213         // classpath as well.
    214         File override = resourcesMap.get("bootoverride.jar");
    215         assertNotNull(override);
    216 
    217         ClassLoader cl = new DelegateLastClassLoader(override.getAbsolutePath(), null);
    218 
    219         Class<?> clazz = cl.loadClass("java.util.HashMap");
    220         // This should be loaded by the boot classloader and not "cl".
    221         assertNotSame(cl, clazz.getClassLoader());
    222 
    223         String resource = readResource(cl, "android/icu/ICUConfig.properties");
    224         assertFalse("NOT ICU".equals(resource));
    225 
    226         List<String> resources = readResources(cl, "android/icu/ICUConfig.properties");
    227         assertEquals(2, resources.size());
    228         assertFalse("NOT ICU".equals(resources.get(0)));
    229         assertTrue("NOT ICU".equals(resources.get(1)));
    230     }
    231 }
    232