All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.test.net.sf.cglib.proxy.TestEnhancer Maven / Gradle / Ivy

Go to download

EasyMock provides an easy way to create Mock Objects for interfaces and classes generating them on the fly

There is a newer version: 5.2.0
Show newest version
/*
 * Copyright 2002,2003,2004 The Apache Software Foundation
 *
 *  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 org.easymock.cglib.proxy;

import java.io.*;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


import junit.framework.*;
import org.easymock.cglib.CodeGenTestCase;
import org.easymock.cglib.core.DefaultNamingPolicy;
import org.easymock.cglib.core.ReflectUtils;
import org.easymock.cglib.reflect.FastClass;

/**
 *@author     Juozas Baliuka 
 *      [email protected]
 *@version    $Id: TestEnhancer.java,v 1.58 2012/07/27 16:02:49 baliuka Exp $
 */
public class TestEnhancer extends CodeGenTestCase {
    private static final MethodInterceptor TEST_INTERCEPTOR = new TestInterceptor();
    
    private static final Class [] EMPTY_ARG = new Class[]{};
    
    private boolean invokedProtectedMethod = false;
    
    private boolean invokedPackageMethod   = false;
    
    private boolean invokedAbstractMethod  = false;
    
    public TestEnhancer(String testName) {
        super(testName);
    }
    
    
    
    public static Test suite() {
        return new TestSuite(TestEnhancer.class);
    }
    
    public static void main(String args[]) {
        String[] testCaseName = {TestEnhancer.class.getName()};
        junit.textui.TestRunner.main(testCaseName);
    }

    public void testEnhance()throws Throwable{
        
        java.util.Vector vector1 = (java.util.Vector)Enhancer.create(
        java.util.Vector.class,
        new Class[]{java.util.List.class}, TEST_INTERCEPTOR );
        
        java.util.Vector vector2  = (java.util.Vector)Enhancer.create(
        java.util.Vector.class,
        new Class[]{java.util.List.class}, TEST_INTERCEPTOR );
        
        
        
        
        assertTrue("Cache failed",vector1.getClass() == vector2.getClass());
    }
    
   
    public void testMethods()throws Throwable{
        
        MethodInterceptor interceptor =
        new TestInterceptor(){
            
            public Object afterReturn(  Object obj, Method method,
            Object args[],
            boolean invokedSuper, Object retValFromSuper,
            java.lang.Throwable e )throws java.lang.Throwable{
                
                int mod =  method.getModifiers();
                
                if( Modifier.isProtected( mod ) ){
                    invokedProtectedMethod = true;
                }
                
                if( Modifier.isAbstract(mod) ){
                    invokedAbstractMethod = true;
                }
                
                
                if( ! ( Modifier.isProtected( mod ) || Modifier.isPublic( mod ) )){
                    invokedPackageMethod = true;
                }
                
                return retValFromSuper;//return the same as supper
            }
            
        };
        
        
        Source source =  (Source)Enhancer.create(
        Source.class,
        null,interceptor );
        
        source.callAll();
        assertTrue("protected", invokedProtectedMethod );
        assertTrue("package", invokedPackageMethod );
        assertTrue("abstract", invokedAbstractMethod );
    }
    
    public void testEnhanced()throws Throwable{
        
        Source source =  (Source)Enhancer.create(
        Source.class,
        null, TEST_INTERCEPTOR );
        
        
        TestCase.assertTrue("enhance", Source.class != source.getClass() );
        
    }

    public void testEnhanceObject() throws Throwable {
        EA obj = new EA();
        EA save = obj;
        obj.setName("herby");
        EA proxy = (EA)Enhancer.create( EA.class,  new DelegateInterceptor(save) );
     
        assertTrue(proxy.getName().equals("herby"));

        Factory factory = (Factory)proxy;
        assertTrue(((EA)factory.newInstance(factory.getCallbacks())).getName().equals("herby"));
    }

    class DelegateInterceptor implements MethodInterceptor {
      Object delegate;
        DelegateInterceptor(Object delegate){
          this.delegate = delegate;
        }
        public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
            return proxy.invoke(delegate,args);
        }
        
    }
    public void testEnhanceObjectDelayed() throws Throwable {
        
        DelegateInterceptor mi = new DelegateInterceptor(null);
        EA proxy = (EA)Enhancer.create( EA.class, mi);
        EA obj = new EA();
        obj.setName("herby");
        mi.delegate = obj;
       assertTrue(proxy.getName().equals("herby"));
    }
    
    
    public void testTypes()throws Throwable{
        
        Source source =  (Source)Enhancer.create(
        Source.class,
        null, TEST_INTERCEPTOR );
        TestCase.assertTrue("intType",   1   == source.intType(1));
        TestCase.assertTrue("longType",  1L  == source.longType(1L));
        TestCase.assertTrue("floatType", 1.1f  == source.floatType(1.1f));
        TestCase.assertTrue("doubleType",1.1 == source.doubleType(1.1));
        TestCase.assertEquals("objectType","1", source.objectType("1") );
        TestCase.assertEquals("objectType","",  source.toString() );
        source.arrayType( new int[]{} );    
        
    }
    

    public void testModifiers()throws Throwable{
        
        Source source =  (Source)Enhancer.create(
        Source.class,
        null, TEST_INTERCEPTOR );
        
        Class enhancedClass = source.getClass();
        
        assertTrue("isProtected" , Modifier.isProtected( enhancedClass.getDeclaredMethod("protectedMethod", EMPTY_ARG ).getModifiers() ));
        int mod =  enhancedClass.getDeclaredMethod("packageMethod", EMPTY_ARG ).getModifiers() ;
        assertTrue("isPackage" , !( Modifier.isProtected(mod)|| Modifier.isPublic(mod) ) );
        
        //not sure about this (do we need it for performace ?)
        assertTrue("isFinal" ,  Modifier.isFinal( mod ) );
        
        mod =  enhancedClass.getDeclaredMethod("synchronizedMethod", EMPTY_ARG ).getModifiers() ;
        assertTrue("isSynchronized" ,  !Modifier.isSynchronized( mod ) );
        
        
    }
    
    public void testObject()throws Throwable{
        
        Object source =  Enhancer.create(
        null,
        null, TEST_INTERCEPTOR );
        
        assertTrue("parent is object",
        source.getClass().getSuperclass() == Object.class  );
        
    }

    public void testSystemClassLoader()throws Throwable{
        
        Object source =  enhance(
        null,
        null, TEST_INTERCEPTOR , ClassLoader.getSystemClassLoader());
        source.toString();
        assertTrue("SystemClassLoader",
        source.getClass().getClassLoader()
        == ClassLoader.getSystemClassLoader()  );
        
    }
    
    
    
    public void testCustomClassLoader()throws Throwable{
        
        ClassLoader custom = new ClassLoader(this.getClass().getClassLoader()){};
        
        Object source =  enhance( null, null, TEST_INTERCEPTOR, custom);
        source.toString();
        assertTrue("Custom classLoader", source.getClass().getClassLoader() == custom  );
        
        custom = new ClassLoader(){};
        
        source =  enhance( null, null, TEST_INTERCEPTOR, custom);
        source.toString();
        assertTrue("Custom classLoader", source.getClass().getClassLoader() == custom  );
        
        
    }

    public void testRuntimException()throws Throwable{
    
        Source source =  (Source)Enhancer.create(
        Source.class,
        null, TEST_INTERCEPTOR );
        
        try{
            
            source.throwIndexOutOfBoundsException();
            fail("must throw an exception");
            
        }catch( IndexOutOfBoundsException ok  ){
            
        }
    
    }
    
  static abstract class CastTest{
     CastTest(){} 
    abstract int getInt();
  }
  
  class CastTestInterceptor implements MethodInterceptor{
     
      public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
          return new Short((short)0);
      }
      
  }  
  
    
  public void testCast()throws Throwable{
    
    CastTest castTest =  (CastTest)Enhancer.create(CastTest.class, null, new  CastTestInterceptor());
  
    assertTrue(castTest.getInt() == 0);
    
  }
  
   public void testABC() throws Throwable{
       Enhancer.create(EA.class, null, TEST_INTERCEPTOR);
       Enhancer.create(EC1.class, null, TEST_INTERCEPTOR).toString();
       ((EB)Enhancer.create(EB.class, null, TEST_INTERCEPTOR)).finalTest();
       assertTrue("abstract method",( (EC1)Enhancer.create(EC1.class,
                     null, TEST_INTERCEPTOR) ).compareTo( new EC1() ) == -1 );
       Enhancer.create(ED.class, null, TEST_INTERCEPTOR).toString();
       Enhancer.create(ClassLoader.class, null, TEST_INTERCEPTOR).toString();
   }

    public static class AroundDemo {
        public String getFirstName() {
            return "Chris";
        }
        public String getLastName() {
            return "Nokleberg";
        }
    }

    public void testAround() throws Throwable {
        AroundDemo demo = (AroundDemo)Enhancer.create(AroundDemo.class, null, new MethodInterceptor() {
                public Object intercept(Object obj, Method method, Object[] args,
                                           MethodProxy proxy) throws Throwable {
                    if (method.getName().equals("getFirstName")) {
                        return "Christopher";
                    }
                    return proxy.invokeSuper(obj, args);
                }
            });
        assertTrue(demo.getFirstName().equals("Christopher"));
        assertTrue(demo.getLastName().equals("Nokleberg"));
    }
 
    
    public static interface TestClone extends Cloneable{
     public Object clone()throws java.lang.CloneNotSupportedException;

    }
    public static class TestCloneImpl implements TestClone{
     public Object clone()throws java.lang.CloneNotSupportedException{
         return super.clone();
     }
    }

    public void testClone() throws Throwable{
    
      TestClone testClone = (TestClone)Enhancer.create( TestCloneImpl.class,
                                                          TEST_INTERCEPTOR );
      assertTrue( testClone.clone() != null );  
      
            
      testClone = (TestClone)Enhancer.create( TestClone.class,
         new MethodInterceptor(){
      
           public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                        MethodProxy proxy) throws Throwable{
                     return  proxy.invokeSuper(obj, args);
           }
  
      
      } );

      assertTrue( testClone.clone() != null );  
      
      
    }
    
    public void testSamples() throws Throwable{
        samples.Trace.main(new String[]{});
        samples.Beans.main(new String[]{});
    }

    public static interface FinalA {
        void foo();
    }

    public static class FinalB implements FinalA {
        final public void foo() { }
    }

    public void testFinal() throws Throwable {
        ((FinalA)Enhancer.create(FinalB.class, TEST_INTERCEPTOR)).foo();
    }

    public static interface ConflictA {
        int foo();
    }

    public static interface ConflictB {
        String foo();
    }

    public void testConflict() throws Throwable {
        Object foo =
            Enhancer.create(Object.class, new Class[]{ ConflictA.class, ConflictB.class }, TEST_INTERCEPTOR);
        ((ConflictA)foo).foo();
        ((ConflictB)foo).foo();
    }

    // TODO: make this work again
     public void testArgInit() throws Throwable{

         Enhancer e = new Enhancer();
         e.setSuperclass(ArgInit.class);
         e.setCallbackType(MethodInterceptor.class);
         Class f = e.createClass();
         ArgInit a = (ArgInit)ReflectUtils.newInstance(f,
                                                       new Class[]{ String.class },
                                                       new Object[]{ "test" });
         assertEquals("test", a.toString());
         ((Factory)a).setCallback(0, TEST_INTERCEPTOR);
         assertEquals("test", a.toString());

         Callback[] callbacks = new Callback[]{ TEST_INTERCEPTOR };
         ArgInit b = (ArgInit)((Factory)a).newInstance(new Class[]{ String.class },
                                                       new Object[]{ "test2" },
                                                       callbacks);
         assertEquals("test2", b.toString());
         try{
             ((Factory)a).newInstance(new Class[]{  String.class, String.class },
                                      new Object[]{"test"},
                                      callbacks);
             fail("must throw exception");
         }catch( IllegalArgumentException iae ){
         
         }
    }

    public static class Signature {
        public int interceptor() {
            return 42;
        }
    }

    public void testSignature() throws Throwable {
        Signature sig = (Signature)Enhancer.create(Signature.class, TEST_INTERCEPTOR);
        assertTrue(((Factory)sig).getCallback(0) == TEST_INTERCEPTOR);
        assertTrue(sig.interceptor() == 42);
    }

    public abstract static class AbstractMethodCallInConstructor {
        public AbstractMethodCallInConstructor() {
            foo();
        }
    
        public abstract void foo();
    }

    public void testAbstractMethodCallInConstructor() throws Throwable {
        AbstractMethodCallInConstructor obj = (AbstractMethodCallInConstructor)
            Enhancer.create(AbstractMethodCallInConstructor.class,
                     TEST_INTERCEPTOR);
        obj.foo();
    }

    public void testProxyIface() throws Throwable {
        final DI1 other = new DI1() {
                public String herby() {
                    return "boop";
                }
            };
        DI1 d = (DI1)Enhancer.create(DI1.class, new MethodInterceptor() {
                public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                                        MethodProxy proxy) throws Throwable {
                    return proxy.invoke(other, args);
                }
            });
        assertTrue("boop".equals(d.herby()));
    }

    static class NamingPolicyDummy {}

    public void testNamingPolicy() throws Throwable {
      Enhancer e = new Enhancer();
      e.setSuperclass(NamingPolicyDummy.class);
      e.setUseCache(false);
      e.setUseFactory(false);
      e.setNamingPolicy(new DefaultNamingPolicy() {
        public String getTag() {
          return "ByHerby";
        }
          public String toString() {
            return getTag();
          }
      });
      e.setCallbackType(MethodInterceptor.class);
      Class proxied = e.createClass();
      final boolean[] ran = new boolean[1];
      Enhancer.registerStaticCallbacks(proxied, new Callback[]{
        new MethodInterceptor() {
          public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            ran[0] = true;
            assertTrue(proxy.getSuperFastClass().getClass().getName().indexOf("$FastClassByHerby$") >= 0);
            return proxy.invokeSuper(obj, args);
          }
        }
      });
      NamingPolicyDummy dummy = (NamingPolicyDummy) proxied.newInstance();
      dummy.toString();
      assertTrue(ran[0]);
    }

    public static Object enhance(Class cls, Class interfaces[], Callback callback, ClassLoader loader) {
        Enhancer e = new Enhancer();
        e.setSuperclass(cls);
        e.setInterfaces(interfaces);
        e.setCallback(callback);
        e.setClassLoader(loader);
        return e.create();
    }

    public interface PublicClone extends Cloneable {
        Object clone() throws CloneNotSupportedException;
    }

    public void testNoOpClone() throws Exception {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(PublicClone.class);
        enhancer.setCallback(NoOp.INSTANCE);
        ((PublicClone)enhancer.create()).clone();
    }

    public void testNoFactory() throws Exception {
        noFactoryHelper();
        noFactoryHelper();
    }

    private void noFactoryHelper() {
        MethodInterceptor mi = new MethodInterceptor() {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                return "Foo";
            }
        };
        Enhancer enhancer = new Enhancer();
        enhancer.setUseFactory(false);
        enhancer.setSuperclass(AroundDemo.class);
        enhancer.setCallback(mi);
        AroundDemo obj = (AroundDemo)enhancer.create();
        assertTrue(obj.getFirstName().equals("Foo"));
        assertTrue(!(obj instanceof Factory));
    }

    interface MethDec {
        void foo();
    }
    
    abstract static class MethDecImpl implements MethDec {
    }

    public void testMethodDeclarer() throws Exception {
        final boolean[] result = new boolean[]{ false };
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MethDecImpl.class);
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                
                result[0] = method.getDeclaringClass().getName().equals(MethDec.class.getName());
                return null;
            }
        });
        ((MethDecImpl)enhancer.create()).foo();
        assertTrue(result[0]);
    }


    interface ClassOnlyX { }
    public void testClassOnlyFollowedByInstance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ClassOnlyX.class);
        enhancer.setCallbackType(NoOp.class);
        Class type = enhancer.createClass();

        enhancer = new Enhancer();
        enhancer.setSuperclass(ClassOnlyX.class);
        enhancer.setCallback(NoOp.INSTANCE);
        Object instance = enhancer.create();

        assertTrue(instance instanceof ClassOnlyX);
        assertTrue(instance.getClass().equals(type));
    }

     public void testSql() {
         Enhancer.create(null, new Class[]{ java.sql.PreparedStatement.class }, TEST_INTERCEPTOR);
     }

    public void testEquals() throws Exception {
        final boolean[] result = new boolean[]{ false };
        EqualsInterceptor intercept = new EqualsInterceptor();
        Object obj = Enhancer.create(null, intercept);
        obj.equals(obj);
        assertTrue(intercept.called);
    }

    public static class EqualsInterceptor implements MethodInterceptor {
        final static Method EQUALS_METHOD = ReflectUtils.findMethod("Object.equals(Object)");
        boolean called;

        public Object intercept(Object obj,
                                Method method,
                                Object[] args,
                                MethodProxy proxy) throws Throwable {
            if (method.equals(EQUALS_METHOD)) {
                return proxy.invoke(this, args);
            } else {
                return proxy.invokeSuper(obj, args);
            }
        }

        public boolean equals(Object other) {
            called = true;
            return super.equals(other);
        }
    }

    private static interface ExceptionThrower {
        void throwsThrowable(int arg) throws Throwable;
        void throwsException(int arg) throws Exception;
        void throwsNothing(int arg);
    }

    private static class MyThrowable extends Throwable { }
    private static class MyException extends Exception { }
    private static class MyRuntimeException extends RuntimeException { }
    
    public void testExceptions() {
        Enhancer e = new Enhancer();
        e.setSuperclass(ExceptionThrower.class);
        e.setCallback(new MethodInterceptor() {
            public Object intercept(Object obj,
                                    Method method,
                                    Object[] args,
                                    MethodProxy proxy) throws Throwable {
                switch (((Integer)args[0]).intValue()) {
                case 1:
                    throw new MyThrowable();
                case 2:
                    throw new MyException();
                case 3:
                    throw new MyRuntimeException();
                default:
                    return null;
                }
            }
        });
        ExceptionThrower et = (ExceptionThrower)e.create();
        try { et.throwsThrowable(1); } catch (MyThrowable t) { } catch (Throwable t) { fail(); }
        try { et.throwsThrowable(2); } catch (MyException t) { } catch (Throwable t) { fail(); }
        try { et.throwsThrowable(3); } catch (MyRuntimeException t) { } catch (Throwable t) { fail(); }

        try { et.throwsException(1); } catch (Throwable t) { assertTrue(t instanceof MyThrowable); }
        try { et.throwsException(2); } catch (MyException t) { } catch (Throwable t) { fail(); }
        try { et.throwsException(3); } catch (MyRuntimeException t) { } catch (Throwable t) { fail(); }
        try { et.throwsException(4); } catch (Throwable t) { fail(); }

        try { et.throwsNothing(1); } catch (Throwable t) { assertTrue(t instanceof MyThrowable); }
        try { et.throwsNothing(2); } catch (Exception t) { assertTrue(t instanceof MyException); }
        try { et.throwsNothing(3); } catch (MyRuntimeException t) { } catch (Throwable t) { fail(); }
        try { et.throwsNothing(4); } catch (Throwable t) { fail(); }
    }

    public void testUnusedCallback() {
        Enhancer e = new Enhancer();
        e.setCallbackTypes(new Class[]{ MethodInterceptor.class, NoOp.class });
        e.setCallbackFilter(new CallbackFilter() {
            public int accept(Method method) {
                return 0;
            }
        });
        e.createClass();
    }

    private static ArgInit newArgInit(Class clazz, String value) {
        return (ArgInit)ReflectUtils.newInstance(clazz,
                                                 new Class[]{ String.class },
                                                 new Object[]{ value });
    }

    private static class StringValue
    implements MethodInterceptor
    {
        private String value;

        public StringValue(String value) {
            this.value = value;
        }
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
            return value;
        }
    }

    public void testRegisterCallbacks()
    throws InterruptedException
    {
         Enhancer e = new Enhancer();
         e.setSuperclass(ArgInit.class);
         e.setCallbackType(MethodInterceptor.class);
         e.setUseFactory(false);
         final Class clazz = e.createClass();

         assertTrue(!Factory.class.isAssignableFrom(clazz));
         assertEquals("test", newArgInit(clazz, "test").toString());

         Enhancer.registerCallbacks(clazz, new Callback[]{ new StringValue("fizzy") });
         assertEquals("fizzy", newArgInit(clazz, "test").toString());
         assertEquals("fizzy", newArgInit(clazz, "test").toString());

         Enhancer.registerCallbacks(clazz, new Callback[]{ null });
         assertEquals("test", newArgInit(clazz, "test").toString());

         Enhancer.registerStaticCallbacks(clazz, new Callback[]{ new StringValue("soda") });
         assertEquals("test", newArgInit(clazz, "test").toString());

         Enhancer.registerCallbacks(clazz, null);
         assertEquals("soda", newArgInit(clazz, "test").toString());
         
         Thread thread = new Thread(){
             public void run() {
                 assertEquals("soda", newArgInit(clazz, "test").toString());
             }
         };
         thread.start();
         thread.join();
    }
    
   public void perform(ClassLoader loader) throws Exception{
    
           enhance( Source.class , null, TEST_INTERCEPTOR, loader);
    
    }
    
  
    public void testCallbackHelper() {
        final ArgInit delegate = new ArgInit("helper");
        Class sc = ArgInit.class;
        Class[] interfaces = new Class[]{ DI1.class, DI2.class };

        CallbackHelper helper = new CallbackHelper(sc, interfaces) {
            protected Object getCallback(final Method method) {
                return new FixedValue() {
                    public Object loadObject() {
                        return "You called method " + method.getName();
                    }
                };
            }
        };

        Enhancer e = new Enhancer();
        e.setSuperclass(sc);
        e.setInterfaces(interfaces);
        e.setCallbacks(helper.getCallbacks());
        e.setCallbackFilter(helper);

        ArgInit proxy = (ArgInit)e.create(new Class[]{ String.class }, new Object[]{ "whatever" });
        assertEquals("You called method toString", proxy.toString());
        assertEquals("You called method herby", ((DI1)proxy).herby());
        assertEquals("You called method derby", ((DI2)proxy).derby());
    }

   
    
    public void testSerialVersionUID() throws Exception {
        Long suid = new Long(0xABBADABBAD00L);

        Enhancer e = new Enhancer();
        e.setSerialVersionUID(suid);
        e.setCallback(NoOp.INSTANCE);
        Object obj = e.create();

        Field field = obj.getClass().getDeclaredField("serialVersionUID");
        field.setAccessible(true);
        assertEquals(suid, field.get(obj));
    }

    interface ReturnTypeA { int foo(String x); }
    interface ReturnTypeB { String foo(String x); }
    public void testMethodsDifferingByReturnTypeOnly() throws IOException {
        Enhancer e = new Enhancer();
        e.setInterfaces(new Class[]{ ReturnTypeA.class, ReturnTypeB.class });
        e.setCallback(new MethodInterceptor() {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                if (method.getReturnType().equals(String.class))
                    return "hello";
                return new Integer(42);
            }
        });
        Object obj = e.create();
        assertEquals(42, ((ReturnTypeA)obj).foo("foo"));
        assertEquals("hello", ((ReturnTypeB)obj).foo("foo"));
        assertEquals(-1, FastClass.create(obj.getClass()).getIndex("foo", new Class[]{ String.class }));
    }


    public static class ConstructorCall
    {
        private String x;
        public ConstructorCall() {
            x = toString();
        }
        public String toString() {
            return "foo";
        }
    }
    
    public void testInterceptDuringConstruction() {
        FixedValue fixedValue = new FixedValue() {
            public Object loadObject() {
                return "bar";
            }
        };

        Enhancer e = new Enhancer();
        e.setSuperclass(ConstructorCall.class);
        e.setCallback(fixedValue);
        assertEquals("bar", ((ConstructorCall)e.create()).x);

        e = new Enhancer();
        e.setSuperclass(ConstructorCall.class);
        e.setCallback(fixedValue);
        e.setInterceptDuringConstruction(false);
        assertEquals("foo", ((ConstructorCall)e.create()).x);
    }
    
    
    
   void assertThreadLocalCallbacks(Class cls)throws Exception{
        
        Field field = cls.getDeclaredField("CGLIB$THREAD_CALLBACKS");
        field.setAccessible(true);
        
        assertNull(((ThreadLocal) field.get(null)).get());
    }
    
    public void testThreadLocalCleanup1()throws Exception{
        
        Enhancer e = new Enhancer();
        e.setUseCache(false);    
        e.setCallbackType(NoOp.class);
        Class cls = e.createClass();
        
        assertThreadLocalCallbacks(cls);
        
      
        

    }
    
    
    public void testThreadLocalCleanup2()throws Exception{
        
        Enhancer e = new Enhancer();
        e.setCallback(NoOp.INSTANCE);
        Object obj = e.create();
        
        assertThreadLocalCallbacks(obj.getClass());
        
        

    }
    
    public void testThreadLocalCleanup3()throws Exception{
        
        Enhancer e = new Enhancer();
        e.setCallback(NoOp.INSTANCE);
        Factory obj = (Factory) e.create();
        obj.newInstance(NoOp.INSTANCE);
        
        assertThreadLocalCallbacks(obj.getClass());
        
        

    }
    
    public void testBridgeForcesInvokeVirtual() {
        List retTypes = new ArrayList();
        List paramTypes = new ArrayList();
        Interceptor interceptor = new Interceptor(retTypes, paramTypes);

        Enhancer e = new Enhancer();
        e.setSuperclass(Impl.class);
        e.setCallbackFilter(new CallbackFilter() {
            public int accept(Method method) {
                return method.getDeclaringClass() != Object.class ? 0 : 1;
            }
        });
        e.setCallbacks(new Callback[] { interceptor, NoOp.INSTANCE });
        // We expect the bridge ('ret') to be called & forward us to the non-bridge 'erased'
        Interface intf = (Interface)e.create();
        intf.aMethod(null);
        // Make sure the right things got called in the right order:
        assertEquals(Arrays.asList(RetType.class, ErasedType.class), retTypes);
        
        // Validate calling the refined just gives us that.
        retTypes.clear();
        Impl impl = (Impl)intf;
        impl.aMethod((Refined)null);
        assertEquals(Arrays.asList(Refined.class), retTypes);
        
        // When calling from the impl, we are dispatched directly to the non-bridge,
        // because that's just how it works.
        retTypes.clear();
        impl.aMethod((RetType)null);
        assertEquals(Arrays.asList(ErasedType.class), retTypes);
        
        // Do a whole bunch of checks for the other methods too
        
        paramTypes.clear();
        intf.intReturn(null);
        assertEquals(Arrays.asList(RetType.class, ErasedType.class), paramTypes);
        
        paramTypes.clear();
        intf.voidReturn(null);
        assertEquals(Arrays.asList(RetType.class, ErasedType.class), paramTypes);
        
        paramTypes.clear();
        intf.widenReturn(null);
        assertEquals(Arrays.asList(RetType.class, ErasedType.class), paramTypes);
    }
    
    public void testBridgeForcesInvokeVirtualEvenWithoutInterceptingBridge() {
        List retTypes = new ArrayList();
        Interceptor interceptor = new Interceptor(retTypes);

        Enhancer e = new Enhancer();
        e.setSuperclass(Impl.class);
        e.setCallbackFilter(new CallbackFilter() {
            public int accept(Method method) {
              // Ideally this would be:
              // return !method.isBridge() && method.getDeclaringClass() != Object.class ? 0 : 1;
              // But Eclipse sometimes labels the wrong things as bridge methods, so we're more
              // explicit:
              return method.getDeclaringClass() != Object.class
                  && method.getReturnType() != RetType.class ? 0 : 1;
            }
        });
        e.setCallbacks(new Callback[] { interceptor, NoOp.INSTANCE });
        // We expect the bridge ('ret') to be called & forward us to non-bridge ('erased'),
        // and we only intercept on the non-bridge.
        Interface intf = (Interface)e.create();
        intf.aMethod(null);
        assertEquals(Arrays.asList(ErasedType.class), retTypes);
        
        // Validate calling the refined just gives us that.
        retTypes.clear();
        Impl impl = (Impl)intf;
        impl.aMethod((Refined)null);
        assertEquals(Arrays.asList(Refined.class), retTypes);
        
        // Make sure we still get our non-bride interception if we didn't intercept the bridge.
        retTypes.clear();
        impl.aMethod((RetType)null);
        assertEquals(Arrays.asList(ErasedType.class), retTypes);
    }

    public void testReverseBridge() {
        List retTypes = new ArrayList();
        Interceptor interceptor = new Interceptor(retTypes);

        Enhancer e = new Enhancer();
        e.setSuperclass(ReverseImpl.class);
        e.setCallbackFilter(new CallbackFilter() {
            public int accept(Method method) {
                return method.getDeclaringClass() != Object.class ? 0 : 1;
            }
        });
        e.setCallbacks(new Callback[] { interceptor, NoOp.INSTANCE });
        // We expect the bridge ('erased') to be called & forward us to 'ret' (non-bridge)
        ReverseSuper superclass = (ReverseSuper)e.create();
        superclass.aMethod(null, null, null, null);
        assertEquals(Arrays.asList(ErasedType.class, RetType.class), retTypes);
        
        // Calling the Refined type gives us just that.
        retTypes.clear();
        ReverseImpl impl2 = (ReverseImpl)superclass;
        impl2.aMethod(null, (Refined)null, null, null);
        assertEquals(Arrays.asList(Refined.class), retTypes);

        retTypes.clear();
        impl2.aMethod(null, (RetType)null, null, null);
        assertEquals(Arrays.asList(RetType.class), retTypes);
    }
    
    public void testBridgeForMoreViz() {
        List retTypes = new ArrayList();
        List paramTypes = new ArrayList();
        Interceptor interceptor = new Interceptor(retTypes, paramTypes);

        Enhancer e = new Enhancer();
        e.setSuperclass(PublicViz.class);
        e.setCallbackFilter(new CallbackFilter() {
            public int accept(Method method) {
                return method.getDeclaringClass() != Object.class ? 0 : 1;
            }
        });
        e.setCallbacks(new Callback[] { interceptor, NoOp.INSTANCE });

        VizIntf intf = (VizIntf)e.create();
        intf.aMethod(null);
        assertEquals(Arrays.asList(Concrete.class), paramTypes);
    }
    
    
    
    static class ErasedType {}
    static class RetType extends ErasedType {}
    static class Refined extends RetType {}
    
    static abstract class Superclass {
        // Check narrowing return value & parameters
        public T aMethod(T t) { return null; }
        // Check void return value
        public void voidReturn(T t) { }
        // Check primitive return value
        public int intReturn(T t) { return 1; }
        // Check widening return value
        public RetType widenReturn(T t) { return null; }
    }
    public interface Interface { // the usage of the interface forces the bridge
        RetType aMethod(RetType obj);
        void voidReturn(RetType obj);
        int intReturn(RetType obj);
        // a wider type than in superclass
        ErasedType widenReturn(RetType obj);
    }
    public static class Impl extends Superclass implements Interface {
        // An even more narrowed type, just to make sure
        // it doesn't confuse us.
        public Refined aMethod(Refined obj) { return null; }
    }
    
    // Another set of classes -- this time with the bridging in reverse,
    // to make sure that if we define the concrete type, a bridge
    // is created to call it from an erased type.
    static abstract class ReverseSuper {
        // the various parameters are to make sure we only
        // change signature when we have to -- only 'c' goes
        // from ErasedType -> RetType
        public T aMethod(Concrete b, T c, RetType d, ErasedType e) { return null; }
    }
    static class Concrete {}
    static class ReverseImpl extends ReverseSuper {
        public Refined aMethod(Concrete b, Refined c, RetType d, ErasedType e) { return null; }
        public RetType aMethod(Concrete b, RetType c, RetType d, ErasedType e) { return null; }
    }
    
    public interface VizIntf {
        public void aMethod(Concrete a);
    }
    static abstract class PackageViz implements VizIntf {
        public void aMethod(Concrete e) {  }
    }
    // inherits aMethod from PackageViz, but bridges to make it
    // publicly accessible.  the bridge here has the same
    // target signature, so it absolutely requires invokespecial,
    // otherwise we recurse forever.
    public static class PublicViz extends PackageViz implements VizIntf {}
    
    private static class Interceptor implements MethodInterceptor {
        private final List retList;
        private final List paramList;
        
        public Interceptor(List retList) {
            this(retList, new ArrayList());
        }
        
        public Interceptor(List retList, List paramList) {
            this.retList = retList;
            this.paramList = paramList;
        }

        public Object intercept(Object obj, Method method, Object[] args,
                MethodProxy proxy) throws Throwable {
            retList.add(method.getReturnType());
            if (method.getParameterTypes().length > 0) {
                paramList.add(method.getParameterTypes()[0]);
            }
            return proxy.invokeSuper(obj, args);
        }
    }
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy