| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package libcore.dalvik.system; |
| |
| import java.lang.reflect.Method; |
| import java.io.File; |
| import java.io.InputStream; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Map; |
| |
| import libcore.io.Streams; |
| import junit.framework.TestCase; |
| |
| import dalvik.system.DexClassLoader; |
| |
| /** |
| * Tests for the class {@link DexClassLoader}. |
| */ |
| public class DexClassLoaderTest extends TestCase { |
| |
| private File dex1; |
| private File dex2; |
| private File jar1; |
| private File jar2; |
| private File nativeLib1; |
| |
| private Map<String, File> resourcesMap; |
| |
| protected void setUp() throws Exception { |
| resourcesMap = ClassLoaderTestSupport.setupAndCopyResources(Arrays.asList( |
| "loading-test.dex", "loading-test2.dex", "loading-test.jar", "loading-test2.jar", |
| "libfake.so")); |
| |
| dex1 = resourcesMap.get("loading-test.dex"); |
| assertNotNull(dex1); |
| dex2 = resourcesMap.get("loading-test2.dex"); |
| assertNotNull(dex2); |
| jar1 = resourcesMap.get("loading-test.jar"); |
| assertNotNull(jar1); |
| jar2 = resourcesMap.get("loading-test2.jar"); |
| assertNotNull(jar2); |
| nativeLib1 = resourcesMap.get("libfake.so"); |
| assertNotNull(nativeLib1); |
| } |
| |
| protected void tearDown() { |
| ClassLoaderTestSupport.cleanUpResources(resourcesMap); |
| } |
| |
| /** |
| * Helper to construct a DexClassLoader instance to test. |
| * |
| * @param files The .dex or .jar files to use for the class path. |
| */ |
| private ClassLoader createLoader(File... files) { |
| assertNotNull(files); |
| assertTrue(files.length > 0); |
| String path = files[0].getAbsolutePath(); |
| for (int i = 1; i < files.length; i++) { |
| path += File.pathSeparator + files[i].getAbsolutePath(); |
| } |
| return new DexClassLoader(path, null, null, |
| ClassLoader.getSystemClassLoader()); |
| } |
| |
| /** |
| * Helper to construct a new DexClassLoader instance to test, using the |
| * given files as the class path, and call a named no-argument static |
| * method on a named class. |
| * |
| * @param className The name of the class of the method to call. |
| * @param methodName The name of the method to call. |
| * @param files The .dex or .jar files to use for the class path. |
| */ |
| public Object createLoaderAndCallMethod( |
| String className, String methodName, File... files) |
| throws ReflectiveOperationException { |
| ClassLoader cl = createLoader(files); |
| Class c = cl.loadClass(className); |
| Method m = c.getMethod(methodName, (Class[]) null); |
| return m.invoke(null, (Object[]) null); |
| } |
| |
| /** |
| * Helper to construct a new DexClassLoader instance to test, using the |
| * given files as the class path, and read the contents of the named |
| * resource as a String. |
| * |
| * @param resourceName The name of the resource to get. |
| * @param files The .dex or .jar files to use for the class path. |
| */ |
| private String createLoaderAndGetResource(String resourceName, File... files) throws Exception { |
| ClassLoader cl = createLoader(files); |
| InputStream in = cl.getResourceAsStream(resourceName); |
| if (in == null) { |
| throw new IllegalStateException("Resource not found: " + resourceName); |
| } |
| |
| byte[] contents = Streams.readFully(in); |
| return new String(contents, "UTF-8"); |
| } |
| |
| // ONE_JAR |
| |
| /** |
| * Just a trivial test of construction. This one merely makes |
| * sure that a valid construction doesn't fail. It doesn't try |
| * to verify anything about the constructed instance, other than |
| * checking for the existence of optimized dex files. |
| */ |
| public void test_oneJar_init() throws Exception { |
| ClassLoader cl = createLoader(jar1); |
| } |
| |
| /** |
| * Check that a class in the jar/dex file may be used successfully. In this |
| * case, a trivial static method is called. |
| */ |
| public void test_oneJar_simpleUse() throws Exception { |
| String result = (String) createLoaderAndCallMethod("test.Test1", "test", jar1); |
| assertSame("blort", result); |
| } |
| |
| /* |
| * All the following tests are just pass-throughs to test code |
| * that lives inside the loading-test dex/jar file. |
| */ |
| |
| public void test_oneJar_constructor() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_constructor", jar1); |
| } |
| |
| public void test_oneJar_callStaticMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", jar1); |
| } |
| |
| public void test_oneJar_getStaticVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", jar1); |
| } |
| |
| public void test_oneJar_callInstanceMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", jar1); |
| } |
| |
| public void test_oneJar_getInstanceVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", jar1); |
| } |
| |
| // ONE_DEX |
| |
| public void test_oneDex_init() throws Exception { |
| ClassLoader cl = createLoader(dex1); |
| } |
| |
| public void test_oneDex_simpleUse() throws Exception { |
| String result = (String) createLoaderAndCallMethod("test.Test1", "test", dex1); |
| assertSame("blort", result); |
| } |
| |
| public void test_oneDex_constructor() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_constructor", dex1); |
| } |
| |
| public void test_oneDex_callStaticMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1); |
| } |
| |
| public void test_oneDex_getStaticVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1); |
| } |
| |
| public void test_oneDex_callInstanceMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1); |
| } |
| |
| public void test_oneDex_getInstanceVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", dex1); |
| } |
| |
| // TWO_JAR |
| |
| public void test_twoJar_init() throws Exception { |
| ClassLoader cl = createLoader(jar1, jar2); |
| } |
| |
| public void test_twoJar_simpleUse() throws Exception { |
| String result = (String) createLoaderAndCallMethod("test.Test1", "test", jar1, jar2); |
| assertSame("blort", result); |
| } |
| |
| public void test_twoJar_constructor() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_constructor", jar1, jar2); |
| } |
| |
| public void test_twoJar_callStaticMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", jar1, jar2); |
| } |
| |
| public void test_twoJar_getStaticVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", jar1, jar2); |
| } |
| |
| public void test_twoJar_callInstanceMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", jar1, jar2); |
| } |
| |
| public void test_twoJar_getInstanceVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", jar1, jar2); |
| } |
| |
| public void test_twoJar_diff_constructor() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_diff_constructor", jar1, jar2); |
| } |
| |
| public void test_twoJar_diff_callStaticMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_diff_callStaticMethod", jar1, jar2); |
| } |
| |
| public void test_twoJar_diff_getStaticVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_diff_getStaticVariable", jar1, jar2); |
| } |
| |
| public void test_twoJar_diff_callInstanceMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_diff_callInstanceMethod", jar1, jar2); |
| } |
| |
| public void test_twoJar_diff_getInstanceVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_diff_getInstanceVariable", jar1, jar2); |
| } |
| |
| // TWO_DEX |
| |
| public void test_twoDex_init() throws Exception { |
| ClassLoader cl = createLoader(dex1, dex2); |
| } |
| |
| public void test_twoDex_simpleUse() throws Exception { |
| String result = (String) createLoaderAndCallMethod("test.Test1", "test", dex1, dex2); |
| assertSame("blort", result); |
| } |
| |
| public void test_twoDex_constructor() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_constructor", dex1, dex2); |
| } |
| |
| public void test_twoDex_callStaticMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1, dex2); |
| } |
| |
| public void test_twoDex_getStaticVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1, dex2); |
| } |
| |
| public void test_twoDex_callInstanceMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1, dex2); |
| } |
| |
| public void test_twoDex_getInstanceVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_getInstanceVariable", dex1, dex2); |
| } |
| |
| public void test_twoDex_diff_constructor() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_diff_constructor", dex1, dex2); |
| } |
| |
| public void test_twoDex_diff_callStaticMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_diff_callStaticMethod", dex1, dex2); |
| } |
| |
| public void test_twoDex_diff_getStaticVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_diff_getStaticVariable", dex1, dex2); |
| } |
| |
| public void test_twoDex_diff_callInstanceMethod() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_diff_callInstanceMethod", dex1, dex2); |
| } |
| |
| public void test_twoDex_diff_getInstanceVariable() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_diff_getInstanceVariable", dex1, dex2); |
| } |
| |
| /* |
| * Tests specifically for resource-related functionality. Since |
| * raw dex files don't contain resources, these test only work |
| * with jar files. |
| */ |
| |
| /** |
| * Check that a resource in the jar file is retrievable and contains |
| * the expected contents. |
| */ |
| public void test_oneJar_directGetResourceAsStream() throws Exception { |
| String result = createLoaderAndGetResource("test/Resource1.txt", jar1); |
| assertEquals("Muffins are tasty!\n", result); |
| } |
| |
| /** |
| * Check that a resource in the jar file can be retrieved from |
| * a class within that jar file. |
| */ |
| public void test_oneJar_getResourceAsStream() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_getResourceAsStream", jar1); |
| } |
| |
| public void test_twoJar_directGetResourceAsStream() throws Exception { |
| String result = createLoaderAndGetResource("test/Resource1.txt", jar1, jar2); |
| assertEquals("Muffins are tasty!\n", result); |
| } |
| |
| public void test_twoJar_getResourceAsStream() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_getResourceAsStream", jar1, jar2); |
| } |
| |
| /** |
| * Check that a resource in the second jar file is retrievable and |
| * contains the expected contents. |
| */ |
| public void test_twoJar_diff_directGetResourceAsStream() throws Exception { |
| String result = createLoaderAndGetResource("test2/Resource2.txt", jar1, jar2); |
| assertEquals("Who doesn't like a good biscuit?\n", result); |
| } |
| |
| /** |
| * Check that a resource in a jar file can be retrieved from |
| * a class within the other jar file. |
| */ |
| public void test_twoJar_diff_getResourceAsStream() throws Exception { |
| createLoaderAndCallMethod("test.TestMethods", "test_diff_getResourceAsStream", jar1, jar2); |
| } |
| |
| /* |
| * Tests native modification behaviors |
| */ |
| |
| /** |
| * Checks that a adding a native library to an existing class loader makes it visible for |
| * subsequent calls. |
| * @throws Exception |
| */ |
| public void test_oneDex_addNative_findsLibrary() throws Exception { |
| String path = nativeLib1.getParentFile().getAbsolutePath(); |
| DexClassLoader classLoader = (DexClassLoader) createLoader(dex1); |
| |
| assertNull("findLibrary should not find un-added path", |
| classLoader.findLibrary("fake")); |
| |
| classLoader.addNativePath(Collections.singletonList(path)); |
| |
| assertNotNull("findLibrary should find newly added path", |
| classLoader.findLibrary("fake")); |
| } |
| } |