Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-4843

Mocking a method with byte[] parameter type throws a ClassCastException: ArrayList cannot be cast to Number

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Open
    • Major
    • Resolution: Unresolved
    • 1.7.10, 1.8.0
    • None
    • mocks and stubs
    • 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.

      Attachments

        Activity

          People

            Unassigned Unassigned
            yabon Sébastien Launay
            Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated: