Details
Description
Hi, I'm a member of Android's Dalvik team. Lots of Android developers have been asking for EasyMock support for Android, so we've written some code to make that possible.
The first part is a new library called 'dexmaker' that assumes the role of ASM+cglib on Android. Read more about dexmaker here:
http://code.google.com/p/dexmaker/
This library is not yet available in maven. Please let me know if this blocks forward progress on the patch; I'll see what I can do.
The second part is a patch to EasyMock to integrate dexmaker. The patch applies to SVN r269. I've attempted to avoid making unnecessary changes in the patch, though I admit it does require a lot of churn. The main change is that there's a new class, "AndroidClassProxyFactory" that compliments the existing "ClassProxyFactory". Much of the changes are in test code, cleaning up tests that fail on Android for various reasons. For example one test assumed implementation details about ArrayList that aren't true on Android.
Unfortunately it's slightly cumbersome to confirm that the tests pass on Android because there's no easy way to run JUnit 4 tests on Android. I hacked together the vogar test runner (http://code.google.com/p/vogar/) to run the tests; it would also be possible to run tests as a part of an Android application. All of the tests pass with the following exceptions:
- org.easymock.tests.CglibTest#test (cglib doesn't run at all on Android)
- org.easymock.tests2.SerializationTest#test (I elected to omit support for serialization; it's not popular on Android)
-
Hide
- dexmaker.jar
- 11/Jan/12 11:29 PM
- 657 kB
- Jesse Wilson
-
- META-INF/MANIFEST.MF 0.1 kB
- com/google/dexmaker/dx/Version.class 0.4 kB
- com/google/dexmaker/.../cf/code/Merger.class 1 kB
- com/google/dexmaker/.../dex/DexFormat.class 1 kB
- com/google/dexmaker/.../dex/DexOptions.class 0.6 kB
- com/google/dexmaker/dx/dex/SizeOf.class 0.7 kB
- com/google/dexmaker/.../code/ArrayData.class 5 kB
- com/google/.../code/BlockAddresses.class 3 kB
- com/google/.../code/CatchBuilder.class 0.4 kB
- com/google/.../CatchHandlerList$Entry.class 2 kB
- com/google/.../code/CatchHandlerList.class 3 kB
- com/google/.../code/CatchTable$Entry.class 2 kB
- com/google/.../code/CatchTable.class 2 kB
- com/google/.../code/CodeAddress.class 1 kB
- com/google/dexmaker/.../code/CstInsn.class 3 kB
- com/.../DalvCode$AssignIndicesCallback.class 0.3 kB
- com/google/dexmaker/.../code/DalvCode.class 3 kB
- com/google/dexmaker/.../code/DalvInsn.class 7 kB
- com/google/.../code/DalvInsnList.class 5 kB
- com/google/dexmaker/.../dex/code/Dop.class 2 kB
- com/google/dexmaker/.../dex/code/Dops.class 20 kB
- com/google/.../code/FixedSizeInsn.class 2 kB
- com/google/.../code/HighRegisterPrefix.class 4 kB
- com/google/.../code/InsnFormat.class 10 kB
- com/google/dexmaker/.../code/LocalEnd.class 2 kB
- com/google/.../LocalList$Disposition.class 1 kB
- com/google/.../code/LocalList$Entry.class 4 kB
- com/google/.../LocalList$MakeState.class 7 kB
- com/google/dexmaker/.../code/LocalList.class 5 kB
- com/google/.../code/LocalSnapshot.class 2 kB
-
- easymock-on-android.patch
- 11/Jan/12 11:29 PM
- 41 kB
- Jesse Wilson
-
- easymock-on-android20120305.patch
- 05/Mar/12 9:48 PM
- 44 kB
- Jesse Wilson
Activity
@Jesse I'm attempting to get your patched version to build in order to test it as a replacement for android-mock, but so far with no luck.
I applied your patch to r269 of the EasyMock trunk and attempted to run mvn install but the build fails. It then occurred to me that it would probably need dexmaker.jar included as a dependency, so I added this to the pom.xml file as a system-scoped local dependency:
<dependency> <groupId>dexmaker</groupId> <artifactId>dexmaker-nodep</artifactId> <version>1.0</version> <scope>system</scope> <systemPath>${basedir}/lib/dexmaker.jar</systemPath> </dependency>
The build got a little further, but I still get compile errors:
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building EasyMock
[INFO] task-segment: [install]
[INFO] ------------------------------------------------------------------------
[INFO] [timestamp:create {execution: date}]
[INFO] [timestamp:create {execution: year}]
[INFO] [remote-resources:process {execution: default}]
[debug] execute contextualize
[INFO] [resources:resources {execution: default-resources}]
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/tom/Code/third_party/easymock/trunk/easymock/src/main/resources
[INFO] Copying 2 resources
[INFO] [compiler:compile {execution: default-compile}]
[WARNING] File encoding has not been set, using platform encoding MacRoman, i.e. build is platform dependent!
[INFO] Compiling 71 source files to /Users/tom/Code/third_party/easymock/trunk/easymock/target/classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/ClassProxyFactory.java:[40,58] type org.easymock.internal.IProxyFactory does not take parameters
[ERROR] /Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/AndroidClassProxyFactory.java:[46,61] getConstructorToUse(java.lang.Class<?>) has private access in org.easymock.internal.DefaultClassInstantiator
[ERROR] /Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/AndroidClassProxyFactory.java:[47,50] getArgsForTypes(java.lang.Class<?>[]) has private access in org.easymock.internal.DefaultClassInstantiator
[ERROR] /Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/AndroidClassProxyFactory.java:[84,40] cannot find symbol
symbol : method isCallerMockInvocationHandlerInvoke(java.lang.Throwable)
location: class org.easymock.internal.ClassProxyFactory
[ERROR] /Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/MocksControl.java:[122,39] incompatible types
found : org.easymock.internal.ClassProxyFactory
required: org.easymock.internal.IProxyFactory
[ERROR] /Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/ClassProxyFactory.java:[182,16] cannot find symbol
symbol : variable ClassExtensionHelper
location: class org.easymock.internal.ClassProxyFactory<T>
[ERROR] /Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/ClassProxyFactory.java:[184,45] cannot find symbol
symbol : variable ClassExtensionHelper
location: class org.easymock.internal.ClassProxyFactory<T>
[INFO] 7 errors
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure
/Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/ClassProxyFactory.java:[40,58] type org.easymock.internal.IProxyFactory does not take parameters
/Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/AndroidClassProxyFactory.java:[46,61] getConstructorToUse(java.lang.Class<?>) has private access in org.easymock.internal.DefaultClassInstantiator
/Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/AndroidClassProxyFactory.java:[47,50] getArgsForTypes(java.lang.Class<?>[]) has private access in org.easymock.internal.DefaultClassInstantiator
/Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/AndroidClassProxyFactory.java:[84,40] cannot find symbol
symbol : method isCallerMockInvocationHandlerInvoke(java.lang.Throwable)
location: class org.easymock.internal.ClassProxyFactory
/Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/MocksControl.java:[122,39] incompatible types
found : org.easymock.internal.ClassProxyFactory
required: org.easymock.internal.IProxyFactory
/Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/ClassProxyFactory.java:[182,16] cannot find symbol
symbol : variable ClassExtensionHelper
location: class org.easymock.internal.ClassProxyFactory<T>
/Users/tom/Code/third_party/easymock/trunk/easymock/src/main/java/org/easymock/internal/ClassProxyFactory.java:[184,45] cannot find symbol
symbol : variable ClassExtensionHelper
location: class org.easymock.internal.ClassProxyFactory<T>
[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3 seconds
[INFO] Finished at: Fri Mar 02 11:03:30 CST 2012
[INFO] Final Memory: 26M/81M
[INFO] ------------------------------------------------------------------------
Any hints where I might be going wrong? Alternatively, any chance you could attach a JAR you've already built
?
Tom, the dexmerge code has undergone some changes since I first sent this out; I'll take a look.
There are probably also some small structural changes I can make that'll simplify deployment of easymock+Android... On the related Mockito change we made the mock class generator pluggable. When running on a JVM everything is the same; when running on an Android with dexmaker.jar on your classpath it picks that up instead. We should use the same strategy with EasyMock: provide a service interface and a simple mechanism to look up its implementation.
The patch was applied almost as is except for some tests that were failing.
In parallel with this contribution I've been preparing a similar contribution for Mockito. Over there we've created a new interface "MockMaker" that is implemented internally using cglib. When run on Android another implementation is selected automatically using ServiceLoader. This approach seems to work quite well and prevents Mockito from having a direct dependency on any Android-specific code.
Here's their tracking bug if you'd like to compare:
http://code.google.com/p/mockito/issues/detail?id=308