Details
-
Bug
-
Status: Open
-
Major
-
Resolution: Unresolved
-
1.7.10, 1.8.0
-
None
-
None
-
Patch
Description
If I want to mock a class with a method having a single byte[] paremeter like this one:
def baisContext = new MockFor(FileInputStream) baisContext.demand.read(1..1) {byte[] b -> ... } def bais = baisContext.proxyDelegateInstance("/dev/null") bais.read(new byte[2]) // will throws ClassCastException
it will fail with the following exception:
java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.Number at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.convertToByteArray(DefaultTypeTransformation.java:701) at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.convertToPrimitiveArray(DefaultTypeTransformation.java:802) at org.codehaus.groovy.reflection.stdclasses.ArrayCachedClass.coerceArgument(ArrayCachedClass.java:38) at org.codehaus.groovy.reflection.ParameterTypes.coerceArgumentsToClasses(ParameterTypes.java:138) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:231) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:884) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:39) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:54) at groovy.mock.interceptor.MockInterceptor.beforeInvoke(MockInterceptor.groovy:33) at groovy.mock.interceptor.MockProxyMetaClass.invokeMethod(MockProxyMetaClass.java:78) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:39) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
This is because MockInterceptor invoke the target closure by expanding the arguments array like this:
return result(*arguments)
resulting in the following type coercion from ParameterTypes#coerceArgumentsToClasses(Object[]) (closure with 1 argument of array type is considered as a variable argument method call):
Object[] {byte[]} => Object[] {Object[] {ArrayList<Byte>}} => byte[]
instead of the excepted coercion:
Object[] {byte[]} => Object[] {Byte[]} => byte[]
I think this issue does not only affects byte[] arguments but most certainly all the kind of arrays.
Current workaround is to use the type List<Byte> instead of byte[] in the closure intercepting the call.
I attached a test case to reproduce the bug plus a proposed fix by using InvokerHelper.invokeClosure() instead of expanding the arguments array.