diff options
author | Mark Brophy <mbrophy@google.com> | 2012-10-12 18:40:08 +0100 |
---|---|---|
committer | Mark Brophy <mbrophy@google.com> | 2012-10-17 13:35:50 +0100 |
commit | 95689a700bfea5e2d78380a442fc2903cc40a3f2 (patch) | |
tree | b2336499a9314e5e4593ae4dc4f4df5b4c70aeb5 | |
parent | c1ec15d8c19315b6f9a8706762a9d4d246713e76 (diff) | |
download | dexmaker-95689a700bfea5e2d78380a442fc2903cc40a3f2.tar.gz |
Update to latest dexmaker.
This fixes the bug below which prevented mocking of any ViewGroup
extension.
https://code.google.com/p/dexmaker/issues/detail?id=12
Change-Id: I459fb259244476b89b9a4b50c6a6cf88f2e1f2f6
9 files changed, 125 insertions, 70 deletions
@@ -3,7 +3,7 @@ This folder contains the dexmaker library. The latest version of dexmaker can be found at this url: http://code.google.com/p/dexmaker/ -Version: b4fdb175545f +Version: f54f6eac0c81120f53265b30adf9ce602e8dfc41 License: Apache 2.0 Description: diff --git a/src/main/java/com/google/dexmaker/AppDataDirGuesser.java b/src/main/java/com/google/dexmaker/AppDataDirGuesser.java index b59670e..86f34a1 100644 --- a/src/main/java/com/google/dexmaker/AppDataDirGuesser.java +++ b/src/main/java/com/google/dexmaker/AppDataDirGuesser.java @@ -61,7 +61,7 @@ class AppDataDirGuesser { } // Parsing toString() method: yuck. But no other way to get the path. - // Strip out the bit between angle brackets, that's our path. + // Strip out the bit between square brackets, that's our path. String result = classLoader.toString(); int index = result.lastIndexOf('['); result = (index == -1) ? result : result.substring(index + 1); @@ -71,13 +71,7 @@ class AppDataDirGuesser { File[] guessPath(String input) { List<File> results = new ArrayList<File>(); - // Post JB, the system property is set to the applications private data cache - // directory. - File tmpDir = new File(System.getProperty("java.io.tmpdir")); - if (isWriteableDirectory(tmpDir)) { - results.add(tmpDir); - } - for (String potential : input.split(":")) { + for (String potential : splitPathList(input)) { if (!potential.startsWith("/data/app/")) { continue; } @@ -105,6 +99,18 @@ class AppDataDirGuesser { return results.toArray(new File[results.size()]); } + static String[] splitPathList(String input) { + String trimmed = input; + if (input.startsWith("dexPath=")) { + int start = "dexPath=".length(); + int end = input.indexOf(','); + + trimmed = (end == -1) ? input.substring(start) : input.substring(start, end); + } + + return trimmed.split(":"); + } + boolean fileOrDirExists(File file) { return file.exists(); } diff --git a/src/main/java/com/google/dexmaker/Code.java b/src/main/java/com/google/dexmaker/Code.java index 4ea5e67..54409a5 100644 --- a/src/main/java/com/google/dexmaker/Code.java +++ b/src/main/java/com/google/dexmaker/Code.java @@ -100,7 +100,7 @@ import java.util.List; * unconditionally with {@link #jump jump(Label)} or conditionally based on a * comparison using {@link #compare compare()}. * - * <p>Most methods should contain either a return instruction. Void methods + * <p>Most methods should contain a return instruction. Void methods * should use {@link #returnVoid()}; non-void methods should use {@link * #returnValue returnValue()} with a local whose return type matches the * method's return type. Constructors are considered void methods and should @@ -506,9 +506,12 @@ public final class Code { } /** - * Executes {@code op} and sets {@code target} to the result. + * Executes {@code op} and sets {@code target} to the result. For most + * binary operations, the types of {@code a} and {@code b} must be the same. + * Shift operations (like {@link BinaryOp#SHIFT_LEFT}) require {@code b} to + * be an {@code int}, even when {@code a} is a {@code long}. */ - public <T> void op(BinaryOp op, Local<T> target, Local<T> a, Local<T> b) { + public <T1, T2> void op(BinaryOp op, Local<T1> target, Local<T1> a, Local<T2> b) { Rop rop = op.rop(StdTypeList.make(a.type.ropType, b.type.ropType)); RegisterSpecList sources = RegisterSpecList.make(a.spec(), b.spec()); diff --git a/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java b/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java index 639b3dc..774c5da 100644 --- a/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java +++ b/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java @@ -595,11 +595,12 @@ public final class ProxyBuilder<T> { */ private Method[] getMethodsToProxyRecursive() { Set<MethodSetEntry> methodsToProxy = new HashSet<MethodSetEntry>(); + Set<MethodSetEntry> seenFinalMethods = new HashSet<MethodSetEntry>(); for (Class<?> c = baseClass; c != null; c = c.getSuperclass()) { - getMethodsToProxy(methodsToProxy, c); + getMethodsToProxy(methodsToProxy, seenFinalMethods, c); } for (Class<?> c : interfaces) { - getMethodsToProxy(methodsToProxy, c); + getMethodsToProxy(methodsToProxy, seenFinalMethods, c); } Method[] results = new Method[methodsToProxy.size()]; @@ -610,10 +611,14 @@ public final class ProxyBuilder<T> { return results; } - private void getMethodsToProxy(Set<MethodSetEntry> sink, Class<?> c) { + private void getMethodsToProxy(Set<MethodSetEntry> sink, Set<MethodSetEntry> seenFinalMethods, + Class<?> c) { for (Method method : c.getDeclaredMethods()) { if ((method.getModifiers() & Modifier.FINAL) != 0) { - // Skip final methods, we can't override them. + // Skip final methods, we can't override them. We + // also need to remember them, in case the same + // method exists in a parent class. + seenFinalMethods.add(new MethodSetEntry(method)); continue; } if ((method.getModifiers() & STATIC) != 0) { @@ -624,11 +629,17 @@ public final class ProxyBuilder<T> { // Skip finalize method, it's likely important that it execute as normal. continue; } - sink.add(new MethodSetEntry(method)); + MethodSetEntry entry = new MethodSetEntry(method); + if (seenFinalMethods.contains(entry)) { + // This method is final in a child class. + // We can't override it. + continue; + } + sink.add(entry); } - + for (Class<?> i : c.getInterfaces()) { - getMethodsToProxy(sink, i); + getMethodsToProxy(sink, seenFinalMethods, i); } } diff --git a/src/mockito/java/com/google/dexmaker/mockito/DexmakerMockMaker.java b/src/mockito/java/com/google/dexmaker/mockito/DexmakerMockMaker.java index bf33342..b29c267 100644 --- a/src/mockito/java/com/google/dexmaker/mockito/DexmakerMockMaker.java +++ b/src/mockito/java/com/google/dexmaker/mockito/DexmakerMockMaker.java @@ -22,14 +22,16 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.Set; import org.mockito.exceptions.base.MockitoException; +import org.mockito.exceptions.stacktrace.StackTraceCleaner; import org.mockito.invocation.MockHandler; import org.mockito.mock.MockCreationSettings; import org.mockito.plugins.MockMaker; +import org.mockito.plugins.StackTraceCleanerProvider; /** * Generates mock instances on Android's runtime. */ -public final class DexmakerMockMaker implements MockMaker { +public final class DexmakerMockMaker implements MockMaker, StackTraceCleanerProvider { private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) { @@ -94,4 +96,15 @@ public final class DexmakerMockMaker implements MockMaker { return null; } + + public StackTraceCleaner getStackTraceCleaner(final StackTraceCleaner defaultCleaner) { + return new StackTraceCleaner() { + public boolean isOut(StackTraceElement candidate) { + return defaultCleaner.isOut(candidate) + || candidate.getClassName().endsWith("_Proxy") // dexmaker class proxies + || candidate.getClassName().startsWith("$Proxy") // dalvik interface proxies + || candidate.getClassName().startsWith("com.google.dexmaker.mockito."); + } + }; + } } diff --git a/src/mockito/resources/mockito-extensions/org.mockito.plugins.StackTraceCleanerProvider b/src/mockito/resources/mockito-extensions/org.mockito.plugins.StackTraceCleanerProvider new file mode 100644 index 0000000..9f6df44 --- /dev/null +++ b/src/mockito/resources/mockito-extensions/org.mockito.plugins.StackTraceCleanerProvider @@ -0,0 +1 @@ +com.google.dexmaker.mockito.DexmakerMockMaker diff --git a/src/test/java/com/google/dexmaker/AppDataDirGuesserTest.java b/src/test/java/com/google/dexmaker/AppDataDirGuesserTest.java index 36ac383..bbceaa3 100644 --- a/src/test/java/com/google/dexmaker/AppDataDirGuesserTest.java +++ b/src/test/java/com/google/dexmaker/AppDataDirGuesserTest.java @@ -66,6 +66,15 @@ public final class AppDataDirGuesserTest extends TestCase { .shouldGive("/data/data/com.google.android.voicesearch/cache"); } + public void testSplitPathList() { + final String[] expected = { "foo", "bar" }; + assertTrue(Arrays.equals(expected, AppDataDirGuesser.splitPathList("foo:bar"))); + assertTrue(Arrays.equals(expected, + AppDataDirGuesser.splitPathList("dexPath=foo:bar"))); + assertTrue(Arrays.equals(expected, + AppDataDirGuesser.splitPathList("dexPath=foo:bar,bazPath=bar:bar2"))); + } + private interface TestCondition { TestCondition withNonWriteable(String... files); void shouldGive(String... files); diff --git a/src/test/java/com/google/dexmaker/DexMakerTest.java b/src/test/java/com/google/dexmaker/DexMakerTest.java index 2355916..47fb340 100644 --- a/src/test/java/com/google/dexmaker/DexMakerTest.java +++ b/src/test/java/com/google/dexmaker/DexMakerTest.java @@ -730,16 +730,16 @@ public final class DexMakerTest extends TestCase { } public void testIntBinaryOps() throws Exception { - Method add = binaryOpMethod(int.class, BinaryOp.ADD); + Method add = binaryOpMethod(int.class, int.class, BinaryOp.ADD); assertEquals(79, add.invoke(null, 75, 4)); - Method subtract = binaryOpMethod(int.class, BinaryOp.SUBTRACT); + Method subtract = binaryOpMethod(int.class, int.class, BinaryOp.SUBTRACT); assertEquals(71, subtract.invoke(null, 75, 4)); - Method multiply = binaryOpMethod(int.class, BinaryOp.MULTIPLY); + Method multiply = binaryOpMethod(int.class, int.class, BinaryOp.MULTIPLY); assertEquals(300, multiply.invoke(null, 75, 4)); - Method divide = binaryOpMethod(int.class, BinaryOp.DIVIDE); + Method divide = binaryOpMethod(int.class, int.class, BinaryOp.DIVIDE); assertEquals(18, divide.invoke(null, 75, 4)); try { divide.invoke(null, 75, 0); @@ -748,7 +748,7 @@ public final class DexMakerTest extends TestCase { assertEquals(ArithmeticException.class, expected.getCause().getClass()); } - Method remainder = binaryOpMethod(int.class, BinaryOp.REMAINDER); + Method remainder = binaryOpMethod(int.class, int.class, BinaryOp.REMAINDER); assertEquals(3, remainder.invoke(null, 75, 4)); try { remainder.invoke(null, 75, 0); @@ -757,117 +757,117 @@ public final class DexMakerTest extends TestCase { assertEquals(ArithmeticException.class, expected.getCause().getClass()); } - Method and = binaryOpMethod(int.class, BinaryOp.AND); + Method and = binaryOpMethod(int.class, int.class, BinaryOp.AND); assertEquals(0xff000000, and.invoke(null, 0xff00ff00, 0xffff0000)); - Method or = binaryOpMethod(int.class, BinaryOp.OR); + Method or = binaryOpMethod(int.class, int.class, BinaryOp.OR); assertEquals(0xffffff00, or.invoke(null, 0xff00ff00, 0xffff0000)); - Method xor = binaryOpMethod(int.class, BinaryOp.XOR); + Method xor = binaryOpMethod(int.class, int.class, BinaryOp.XOR); assertEquals(0x00ffff00, xor.invoke(null, 0xff00ff00, 0xffff0000)); - Method shiftLeft = binaryOpMethod(int.class, BinaryOp.SHIFT_LEFT); + Method shiftLeft = binaryOpMethod(int.class, int.class, BinaryOp.SHIFT_LEFT); assertEquals(0xcd123400, shiftLeft.invoke(null, 0xabcd1234, 8)); - Method shiftRight = binaryOpMethod(int.class, BinaryOp.SHIFT_RIGHT); + Method shiftRight = binaryOpMethod(int.class, int.class, BinaryOp.SHIFT_RIGHT); assertEquals(0xffabcd12, shiftRight.invoke(null, 0xabcd1234, 8)); Method unsignedShiftRight = binaryOpMethod(int.class, - BinaryOp.UNSIGNED_SHIFT_RIGHT); + int.class, BinaryOp.UNSIGNED_SHIFT_RIGHT); assertEquals(0x00abcd12, unsignedShiftRight.invoke(null, 0xabcd1234, 8)); } public void testLongBinaryOps() throws Exception { - Method add = binaryOpMethod(long.class, BinaryOp.ADD); - assertEquals(79L, add.invoke(null, 75L, 4L)); + Method add = binaryOpMethod(long.class, long.class, BinaryOp.ADD); + assertEquals(30000000079L, add.invoke(null, 10000000075L, 20000000004L)); - Method subtract = binaryOpMethod(long.class, BinaryOp.SUBTRACT); - assertEquals(71L, subtract.invoke(null, 75L, 4L)); + Method subtract = binaryOpMethod(long.class, long.class, BinaryOp.SUBTRACT); + assertEquals(20000000071L, subtract.invoke(null, 30000000075L, 10000000004L)); - Method multiply = binaryOpMethod(long.class, BinaryOp.MULTIPLY); - assertEquals(300L, multiply.invoke(null, 75L, 4L)); + Method multiply = binaryOpMethod(long.class, long.class, BinaryOp.MULTIPLY); + assertEquals(-8742552812415203028L, multiply.invoke(null, 30000000075L, 20000000004L)); - Method divide = binaryOpMethod(long.class, BinaryOp.DIVIDE); - assertEquals(18L, divide.invoke(null, 75L, 4L)); + Method divide = binaryOpMethod(long.class, long.class, BinaryOp.DIVIDE); + assertEquals(-2L, divide.invoke(null, -8742552812415203028L, 4142552812415203028L)); try { - divide.invoke(null, 75L, 0L); + divide.invoke(null, -8742552812415203028L, 0L); fail(); } catch (InvocationTargetException expected) { assertEquals(ArithmeticException.class, expected.getCause().getClass()); } - Method remainder = binaryOpMethod(long.class, BinaryOp.REMAINDER); - assertEquals(3L, remainder.invoke(null, 75L, 4L)); + Method remainder = binaryOpMethod(long.class, long.class, BinaryOp.REMAINDER); + assertEquals(10000000004L, remainder.invoke(null, 30000000079L, 20000000075L)); try { - remainder.invoke(null, 75L, 0L); + remainder.invoke(null, 30000000079L, 0L); fail(); } catch (InvocationTargetException expected) { assertEquals(ArithmeticException.class, expected.getCause().getClass()); } - Method and = binaryOpMethod(long.class, BinaryOp.AND); + Method and = binaryOpMethod(long.class, long.class, BinaryOp.AND); assertEquals(0xff00ff0000000000L, and.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L)); - Method or = binaryOpMethod(long.class, BinaryOp.OR); + Method or = binaryOpMethod(long.class, long.class, BinaryOp.OR); assertEquals(0xffffffffff00ff00L, or.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L)); - Method xor = binaryOpMethod(long.class, BinaryOp.XOR); + Method xor = binaryOpMethod(long.class, long.class, BinaryOp.XOR); assertEquals(0x00ff00ffff00ff00L, xor.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L)); - Method shiftLeft = binaryOpMethod(long.class, BinaryOp.SHIFT_LEFT); - assertEquals(0xcdef012345678900L, shiftLeft.invoke(null, 0xabcdef0123456789L, 8L)); + Method shiftLeft = binaryOpMethod(long.class, int.class, BinaryOp.SHIFT_LEFT); + assertEquals(0xcdef012345678900L, shiftLeft.invoke(null, 0xabcdef0123456789L, 8)); - Method shiftRight = binaryOpMethod(long.class, BinaryOp.SHIFT_RIGHT); - assertEquals(0xffabcdef01234567L, shiftRight.invoke(null, 0xabcdef0123456789L, 8L)); + Method shiftRight = binaryOpMethod(long.class, int.class, BinaryOp.SHIFT_RIGHT); + assertEquals(0xffabcdef01234567L, shiftRight.invoke(null, 0xabcdef0123456789L, 8)); - Method unsignedShiftRight = binaryOpMethod(long.class, - BinaryOp.UNSIGNED_SHIFT_RIGHT); - assertEquals(0x00abcdef01234567L, unsignedShiftRight.invoke(null, 0xabcdef0123456789L, 8L)); + Method unsignedShiftRight = binaryOpMethod( + long.class, int.class, BinaryOp.UNSIGNED_SHIFT_RIGHT); + assertEquals(0x00abcdef01234567L, unsignedShiftRight.invoke(null, 0xabcdef0123456789L, 8)); } public void testFloatBinaryOps() throws Exception { - Method add = binaryOpMethod(float.class, BinaryOp.ADD); + Method add = binaryOpMethod(float.class, float.class, BinaryOp.ADD); assertEquals(6.75f, add.invoke(null, 5.5f, 1.25f)); - Method subtract = binaryOpMethod(float.class, BinaryOp.SUBTRACT); + Method subtract = binaryOpMethod(float.class, float.class, BinaryOp.SUBTRACT); assertEquals(4.25f, subtract.invoke(null, 5.5f, 1.25f)); - Method multiply = binaryOpMethod(float.class, BinaryOp.MULTIPLY); + Method multiply = binaryOpMethod(float.class, float.class, BinaryOp.MULTIPLY); assertEquals(6.875f, multiply.invoke(null, 5.5f, 1.25f)); - Method divide = binaryOpMethod(float.class, BinaryOp.DIVIDE); + Method divide = binaryOpMethod(float.class, float.class, BinaryOp.DIVIDE); assertEquals(4.4f, divide.invoke(null, 5.5f, 1.25f)); assertEquals(Float.POSITIVE_INFINITY, divide.invoke(null, 5.5f, 0.0f)); - Method remainder = binaryOpMethod(float.class, BinaryOp.REMAINDER); + Method remainder = binaryOpMethod(float.class, float.class, BinaryOp.REMAINDER); assertEquals(0.5f, remainder.invoke(null, 5.5f, 1.25f)); assertEquals(Float.NaN, remainder.invoke(null, 5.5f, 0.0f)); } public void testDoubleBinaryOps() throws Exception { - Method add = binaryOpMethod(double.class, BinaryOp.ADD); + Method add = binaryOpMethod(double.class, double.class, BinaryOp.ADD); assertEquals(6.75, add.invoke(null, 5.5, 1.25)); - Method subtract = binaryOpMethod(double.class, BinaryOp.SUBTRACT); + Method subtract = binaryOpMethod(double.class, double.class, BinaryOp.SUBTRACT); assertEquals(4.25, subtract.invoke(null, 5.5, 1.25)); - Method multiply = binaryOpMethod(double.class, BinaryOp.MULTIPLY); + Method multiply = binaryOpMethod(double.class, double.class, BinaryOp.MULTIPLY); assertEquals(6.875, multiply.invoke(null, 5.5, 1.25)); - Method divide = binaryOpMethod(double.class, BinaryOp.DIVIDE); + Method divide = binaryOpMethod(double.class, double.class, BinaryOp.DIVIDE); assertEquals(4.4, divide.invoke(null, 5.5, 1.25)); assertEquals(Double.POSITIVE_INFINITY, divide.invoke(null, 5.5, 0.0)); - Method remainder = binaryOpMethod(double.class, BinaryOp.REMAINDER); + Method remainder = binaryOpMethod(double.class, double.class, BinaryOp.REMAINDER); assertEquals(0.5, remainder.invoke(null, 5.5, 1.25)); assertEquals(Double.NaN, remainder.invoke(null, 5.5, 0.0)); } - private <T> Method binaryOpMethod(Class<T> valueClass, BinaryOp op) - throws Exception { + private <T1, T2> Method binaryOpMethod( + Class<T1> valueAClass, Class<T2> valueBClass, BinaryOp op) throws Exception { /* * public static int binaryOp(int a, int b) { * int result = a + b; @@ -875,12 +875,13 @@ public final class DexMakerTest extends TestCase { * } */ reset(); - TypeId<T> valueType = TypeId.get(valueClass); - MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", valueType, valueType); + TypeId<T1> valueAType = TypeId.get(valueAClass); + TypeId<T2> valueBType = TypeId.get(valueBClass); + MethodId<?, T1> methodId = GENERATED.getMethod(valueAType, "call", valueAType, valueBType); Code code = dexMaker.declare(methodId, PUBLIC | STATIC); - Local<T> localA = code.getParameter(0, valueType); - Local<T> localB = code.getParameter(1, valueType); - Local<T> localResult = code.newLocal(valueType); + Local<T1> localA = code.getParameter(0, valueAType); + Local<T2> localB = code.getParameter(1, valueBType); + Local<T1> localResult = code.newLocal(valueAType); code.op(op, localResult, localA, localB); code.returnValue(localResult); return getMethod(); diff --git a/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java b/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java index 1b65ea8..62e0e6c 100644 --- a/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java +++ b/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java @@ -771,6 +771,17 @@ public class ProxyBuilderTest extends TestCase { .build(); } + public static class FinalToString { + @Override public final String toString() { + return "no proxy"; + } + } + + // https://code.google.com/p/dexmaker/issues/detail?id=12 + public void testFinalToString() throws Throwable { + assertEquals("no proxy", proxyFor(FinalToString.class).build().toString()); + } + /** Simple helper to add the most common args for this test to the proxy builder. */ private <T> ProxyBuilder<T> proxyFor(Class<T> clazz) throws Exception { return ProxyBuilder.forClass(clazz) |