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