    001    /*****************************************************************************
    002     * Copyright (C) PicoContainer Organization. All rights reserved.            *
    003     * ------------------------------------------------------------------------- *
    004     * The software in this package is published under the terms of the BSD      *
    005     * style license a copy of which has been included with this distribution in *
    006     * the LICENSE.txt file.                                                     *
    007     *                                                                           *
    008     * Original code by Joerg Schaible                                           *
    009     *****************************************************************************/
    010    package org.picocontainer.tck;
    011
    012    import static org.junit.Assert.assertEquals;
    013    import static org.junit.Assert.assertFalse;
    014    import static org.junit.Assert.assertNotNull;
    015    import static org.junit.Assert.assertNotSame;
    016    import static org.junit.Assert.assertSame;
    017    import static org.junit.Assert.assertTrue;
    018    import static org.junit.Assert.fail;
    019
    020    import java.io.ByteArrayInputStream;
    021    import java.io.ByteArrayOutputStream;
    022    import java.io.IOException;
    023    import java.io.ObjectInputStream;
    024    import java.io.ObjectOutputStream;
    025    import java.lang.reflect.Constructor;
    026    import java.lang.reflect.Type;
    027    import java.util.ArrayList;
    028    import java.util.Collection;
    029    import java.util.HashSet;
    030    import java.util.Iterator;
    031    import java.util.LinkedList;
    032    import java.util.List;
    033    import java.util.Set;
    034
    035    import junit.framework.Assert;
    036    import junit.framework.AssertionFailedError;
    037
    038    import org.junit.Test;
    039    import org.picocontainer.ComponentAdapter;
    040    import org.picocontainer.ComponentFactory;
    041    import org.picocontainer.DefaultPicoContainer;
    042    import org.picocontainer.LifecycleStrategy;
    043    import org.picocontainer.MutablePicoContainer;
    044    import org.picocontainer.ObjectReference;
    045    import org.picocontainer.Parameter;
    046    import org.picocontainer.PicoCompositionException;
    047    import org.picocontainer.PicoContainer;
    048    import org.picocontainer.behaviors.AbstractBehavior;
    049    import org.picocontainer.injectors.AbstractInjector;
    050    import org.picocontainer.injectors.AdaptingInjection;
    051    import org.picocontainer.injectors.ConstructorInjection;
    052    import org.picocontainer.parameters.ConstantParameter;
    053    import org.picocontainer.references.SimpleReference;
    054    import org.picocontainer.visitors.AbstractPicoVisitor;
    055
    056    import com.thoughtworks.xstream.XStream;
    057    import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
    058    import com.thoughtworks.xstream.io.xml.XppDriver;
    059
    060    /**
    061     * Test suite for a ComponentAdapter implementation.
    062     *
    063     * @author J&ouml;rg Schaible
    064     */
    065    @SuppressWarnings("serial")
    066    public abstract class AbstractComponentAdapterTest  {
    067
    068        public static final int SERIALIZABLE = 1;
    069        public static final int VERIFYING = 2;
    070        public static final int INSTANTIATING = 4;
    071        public static final int RESOLVING = 8;
    072
    073        protected abstract Class getComponentAdapterType();
    074
    075        protected int getComponentAdapterNature() {
    076            return SERIALIZABLE | VERIFYING | INSTANTIATING | RESOLVING;
    077        }
    078
    079        protected ComponentFactory createDefaultComponentFactory() {
    080            return new AdaptingInjection();
    081        }
    082
    083        // ============================================
    084        // Default
    085        // ============================================
    086
    087        /**
    088         * Prepare the test <em>verifyWithoutDependencyWorks</em>.
    089         *
    090         * @param picoContainer container, may probably not be used.
    091         * @return a ComponentAdapter of the type to test for a component without dependencies. Registration in the pico is
    092         *         not necessary.
    093         */
    094        protected abstract ComponentAdapter prepDEF_verifyWithoutDependencyWorks(MutablePicoContainer picoContainer);
    095
    096        final @Test public void testDEF_verifyWithoutDependencyWorks() {
    097            final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    098            final ComponentAdapter componentAdapter = prepDEF_verifyWithoutDependencyWorks(picoContainer);
    099            assertSame(getComponentAdapterType(), componentAdapter.getClass());
    100            componentAdapter.verify(picoContainer);
    101        }
    102
    103        /**
    104         * Prepare the test <em>verifyDoesNotInstantiate</em>.
    105         *
    106         * @param picoContainer container, may probably not be used.
    107         * @return a ComponentAdapter of the type to test for a component that may throw on instantiation. Registration in
    108         *         the pico is not necessary.
    109         */
    110        protected abstract ComponentAdapter prepDEF_verifyDoesNotInstantiate(MutablePicoContainer picoContainer);
    111
    112        final @Test public void testDEF_verifyDoesNotInstantiate() {
    113            final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    114            final ComponentAdapter componentAdapter = prepDEF_verifyDoesNotInstantiate(picoContainer);
    115            assertSame(getComponentAdapterType(), componentAdapter.getClass());
    116            final ComponentAdapter notInstantiatablecomponentAdapter = new NotInstantiatableBehavior(
    117                    componentAdapter);
    118            final PicoContainer wrappedPicoContainer = wrapComponentInstances(
    119                    NotInstantiatableBehavior.class, picoContainer, null);
    120            notInstantiatablecomponentAdapter.verify(wrappedPicoContainer);
    121        }
    122
    123        /**
    124         * Prepare the test <em>visitable</em>.
    125         *
    126         * @return a ComponentAdapter of the type to test. If the ComponentAdapter supports {@link Parameter}, you have to
    127         *         select a component, that have some.
    128         */
    129        protected abstract ComponentAdapter prepDEF_visitable();
    130
    131        final @Test public void testDEF_visitable() {
    132            final ComponentAdapter componentAdapter = prepDEF_visitable();
    133            final Class type = getComponentAdapterType();
    134            assertSame(type, componentAdapter.getClass());
    135            boolean hasParameters = supportsParameters(type);
    136            final RecordingVisitor visitor = new RecordingVisitor();
    137            visitor.traverse(componentAdapter);
    138            final List visitedElements = new ArrayList(visitor.getVisitedElements());
    139            assertSame(componentAdapter, visitedElements.get(0));
    140            if (hasParameters) {
    141                hasParameters = false;
    142                for (final Iterator iter = visitedElements.iterator(); iter.hasNext() && !hasParameters;) {
    143                    hasParameters = Parameter.class.isAssignableFrom(iter.next().getClass());
    144                }
    145                assertTrue("ComponentAdapter " + type + " supports parameters, provide some", hasParameters);
    146            }
    147        }
    148
    149        /**
    150         * Prepare the test <em>isAbleToTakeParameters</em>. Overload this function, if the ComponentAdapter to test
    151         * supports {@link Parameter}.
    152         *
    153         * @param picoContainer container, may probably not be used.
    154         * @return a ComponentAdapter of the type to test. Select a component, that has some parameters. Registration in the
    155         *         pico is not necessary.
    156         */
    157        protected ComponentAdapter prepDEF_isAbleToTakeParameters(MutablePicoContainer picoContainer) {
    158            final Class type = getComponentAdapterType();
    159            boolean hasParameters = supportsParameters(type);
    160            if (hasParameters) {
    161                fail("You have to overwrite this method for a useful test");
    162            }
    163            return null;
    164        }
    165
    166        final @Test public void testDEF_isAbleToTakeParameters() {
    167            final Class type = getComponentAdapterType();
    168            boolean hasParameters = supportsParameters(type);
    169            if (hasParameters) {
    170                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    171                final ComponentAdapter componentAdapter = prepDEF_isAbleToTakeParameters(picoContainer);
    172                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    173                final RecordingVisitor visitor = new RecordingVisitor();
    174                visitor.traverse(componentAdapter);
    175                final List visitedElements = visitor.getVisitedElements();
    176                if (hasParameters) {
    177                    hasParameters = false;
    178                    for (final Iterator iter = visitedElements.iterator(); iter.hasNext() && !hasParameters;) {
    179                        hasParameters = Parameter.class.isAssignableFrom(iter.next().getClass());
    180                    }
    181                    assertTrue("ComponentAdapter " + type + " supports parameters, provide some", hasParameters);
    182                }
    183                final Object instance = componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    184                assertNotNull(instance);
    185            }
    186        }
    187
    188        // ============================================
    189        // Serializable
    190        // ============================================
    191
    192        /**
    193         * Prepare the test <em>isSerializable</em>. Overload this function, if the ComponentAdapter supports
    194         * serialization.
    195         *
    196         * @param picoContainer container, may probably not be used.
    197         * @return a ComponentAdapter of the type to test. Registration in the pico is not necessary.
    198         */
    199        protected ComponentAdapter prepSER_isSerializable(MutablePicoContainer picoContainer) {
    200            throw new AssertionFailedError("You have to overwrite this method for a useful test");
    201        }
    202
    203        final @Test public void testSER_isSerializable() throws IOException, ClassNotFoundException {
    204            if ((getComponentAdapterNature() & SERIALIZABLE) > 0) {
    205                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    206                final ComponentAdapter componentAdapter = prepSER_isSerializable(picoContainer);
    207                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    208                final Object instance = componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    209                assertNotNull(instance);
    210                final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    211                final ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream);
    212                outputStream.writeObject(componentAdapter);
    213                outputStream.close();
    214                final ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream
    215                        .toByteArray()));
    216                final ComponentAdapter serializedComponentAdapter = (ComponentAdapter)inputStream.readObject();
    217                inputStream.close();
    218                assertEquals(componentAdapter.getComponentKey(), serializedComponentAdapter.getComponentKey());
    219                final Object instanceAfterSerialization = serializedComponentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    220                assertNotNull(instanceAfterSerialization);
    221                assertSame(instance.getClass(), instanceAfterSerialization.getClass());
    222            }
    223        }
    224
    225        /**
    226         * Prepare the test <em>isXStreamSerializable</em>. Overload this function, if the ComponentAdapter supports
    227         * serialization.
    228         *
    229         * @param picoContainer container, may probably not be used.
    230         * @return a ComponentAdapter of the type to test. Registration in the pico is not necessary.
    231         */
    232        protected ComponentAdapter prepSER_isXStreamSerializable(MutablePicoContainer picoContainer) {
    233            throw new AssertionFailedError("You have to overwrite this method for a useful test");
    234        }
    235
    236        final @Test public void testSER_isXStreamSerializableWithPureReflection() {
    237            if ((getComponentAdapterNature() & SERIALIZABLE) > 0) {
    238                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    239                final ComponentAdapter componentAdapter = prepSER_isXStreamSerializable(picoContainer);
    240                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    241                final Object instance = componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    242                assertNotNull(instance);
    243                final XStream xstream = new XStream(new PureJavaReflectionProvider(), new XppDriver());
    244                final String xml = xstream.toXML(componentAdapter);
    245                final ComponentAdapter serializedComponentAdapter = (ComponentAdapter)xstream.fromXML(xml);
    246                assertEquals(componentAdapter.getComponentKey(), serializedComponentAdapter.getComponentKey());
    247                final Object instanceAfterSerialization = serializedComponentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    248                assertNotNull(instanceAfterSerialization);
    249                assertSame(instance.getClass(), instanceAfterSerialization.getClass());
    250            }
    251        }
    252
    253        final @Test public void testSER_isXStreamSerializable() {
    254            if ((getComponentAdapterNature() & SERIALIZABLE) > 0) {
    255                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    256                final ComponentAdapter componentAdapter = prepSER_isXStreamSerializable(picoContainer);
    257                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    258                final Object instance = componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    259                assertNotNull(instance);
    260                final XStream xstream = new XStream(new XppDriver());
    261                final String xml = xstream.toXML(componentAdapter);
    262                final ComponentAdapter serializedComponentAdapter = (ComponentAdapter)xstream.fromXML(xml);
    263                assertEquals(componentAdapter.getComponentKey(), serializedComponentAdapter.getComponentKey());
    264                final Object instanceAfterSerialization = serializedComponentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    265                assertNotNull(instanceAfterSerialization);
    266                assertSame(instance.getClass(), instanceAfterSerialization.getClass());
    267            }
    268        }
    269
    270        // ============================================
    271        // Verifying
    272        // ============================================
    273
    274        /**
    275         * Prepare the test <em>verificationFailsWithUnsatisfiedDependency</em>. Overload this function, if the
    276         * ComponentAdapter's verification can fail e.g. due to an unresolved dependency.
    277         *
    278         * @param picoContainer container, may probably not be used.
    279         * @return a ComponentAdapter of the type to test, that fails for the verification, e.g. because of a compoennt with
    280         *         missing dependencies. Registration in the pico is not necessary.
    281         */
    282        protected ComponentAdapter prepVER_verificationFails(MutablePicoContainer picoContainer) {
    283            throw new AssertionFailedError("You have to overwrite this method for a useful test");
    284        }
    285
    286        final @Test public void testVER_verificationFails() {
    287            if ((getComponentAdapterNature() & VERIFYING) > 0) {
    288                final MutablePicoContainer picoContainer = new DefaultPicoContainer();
    289                final ComponentAdapter componentAdapter = prepVER_verificationFails(picoContainer);
    290                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    291                try {
    292                    componentAdapter.verify(picoContainer);
    293                    fail("PicoCompositionException expected");
    294                } catch (PicoCompositionException e) {
    295                } catch (Exception e) {
    296                    fail("PicoCompositionException expected, but got " + e.getClass().getName());
    297                }
    298                try {
    299                    componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    300                    fail("PicoCompositionException or PicoCompositionException expected");
    301                } catch (PicoCompositionException e) {
    302                } catch (Exception e) {
    303                    fail("PicoCompositionException or PicoCompositionException expected, but got "
    304                            + e.getClass().getName());
    305                }
    306            }
    307        }
    308
    309        // ============================================
    310        // Instantiating
    311        // ============================================
    312
    313        /**
    314         * Prepare the test <em>createsNewInstances</em>. Overload this function, if the ComponentAdapter is
    315         * instantiating. It should create a new instance with every call.
    316         *
    317         * @param picoContainer container, may probably not be used.
    318         * @return a ComponentAdapter of the type to test. Registration in the pico is not necessary.
    319         */
    320        protected ComponentAdapter prepINS_createsNewInstances(MutablePicoContainer picoContainer) {
    321            throw new AssertionFailedError("You have to overwrite this method for a useful test");
    322        }
    323
    324        final @Test public void testINS_createsNewInstances() {
    325            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
    326                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    327                final ComponentAdapter componentAdapter = prepINS_createsNewInstances(picoContainer);
    328                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    329                final Object instance = componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    330                assertNotNull(instance);
    331                assertNotSame(instance, componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class));
    332                assertSame(instance.getClass(), componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class).getClass());
    333            }
    334        }
    335
    336        /**
    337         * Prepare the test <em>errorIsRethrown</em>. Overload this function, if the ComponentAdapter is instantiating.
    338         *
    339         * @param picoContainer container, may probably not be used.
    340         * @return a ComponentAdapter of the type to test with a component that fails with an {@link Error} at
    341         *         instantiation. Registration in the pico is not necessary.
    342         */
    343        protected ComponentAdapter prepINS_errorIsRethrown(MutablePicoContainer picoContainer) {
    344            throw new AssertionFailedError("You have to overwrite this method for a useful test");
    345        }
    346
    347        final @Test public void testINS_errorIsRethrown() {
    348            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
    349                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    350                final ComponentAdapter componentAdapter = prepINS_errorIsRethrown(picoContainer);
    351                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    352                try {
    353                    componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    354                    fail("Thrown Error excpected");
    355                } catch (final Error e) {
    356                    assertEquals("test", e.getMessage());
    357                }
    358            }
    359        }
    360
    361        /**
    362         * Prepare the test <em>runtimeExceptionIsRethrown</em>. Overload this function, if the ComponentAdapter is
    363         * instantiating.
    364         *
    365         * @param picoContainer container, may probably not be used.
    366         * @return a ComponentAdapter of the type to test with a component that fails with a {@link RuntimeException} at
    367         *         instantiation. Registration in the pico is not necessary.
    368         */
    369        protected ComponentAdapter prepINS_runtimeExceptionIsRethrown(MutablePicoContainer picoContainer) {
    370            throw new AssertionFailedError("You have to overwrite this method for a useful test");
    371        }
    372
    373        final @Test public void testINS_runtimeExceptionIsRethrown() {
    374            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
    375                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    376                final ComponentAdapter componentAdapter = prepINS_runtimeExceptionIsRethrown(picoContainer);
    377                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    378                try {
    379                    componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    380                    fail("Thrown RuntimeException excpected");
    381                } catch (final RuntimeException e) {
    382                    assertEquals("test", e.getMessage());
    383                }
    384            }
    385        }
    386
    387        /**
    388         * Prepare the test <em>normalExceptionIsRethrownInsidePicoInvocationTargetInitializationException</em>. Overload
    389         * this function, if the ComponentAdapter is instantiating.
    390         *
    391         * @param picoContainer container, may probably not be used.
    392         * @return a ComponentAdapter of the type to test with a component that fails with a
    393         *         {@link PicoCompositionException} at instantiation. Registration in the pico is not
    394         *         necessary.
    395         */
    396        protected ComponentAdapter prepINS_normalExceptionIsRethrownInsidePicoInitializationException(
    397                MutablePicoContainer picoContainer) {
    398            throw new AssertionFailedError("You have to overwrite this method for a useful test");
    399        }
    400
    401        final @Test public void testINS_normalExceptionIsRethrownInsidePicoInitializationException() {
    402            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
    403                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    404                final ComponentAdapter componentAdapter = prepINS_normalExceptionIsRethrownInsidePicoInitializationException(picoContainer);
    405                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    406                try {
    407                    componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
    408                    fail("Thrown PicoCompositionException excpected");
    409                } catch (final PicoCompositionException e) {
    410                    assertTrue(e.getCause() instanceof Exception);
    411                    assertTrue(e.getCause().getMessage().endsWith("test"));
    412                }
    413            }
    414        }
    415
    416        // ============================================
    417        // Resolving
    418        // ============================================
    419
    420        /**
    421         * Prepare the test <em>dependenciesAreResolved</em>. Overload this function, if the ComponentAdapter is resolves
    422         * dependencies.
    423         *
    424         * @param picoContainer container, used to register dependencies.
    425         * @return a ComponentAdapter of the type to test with a component that has dependencies. Registration in the pico
    426         *         is not necessary.
    427         */
    428        protected ComponentAdapter prepRES_dependenciesAreResolved(MutablePicoContainer picoContainer) {
    429            throw new AssertionFailedError("You have to overwrite this method for a useful test");
    430        }
    431
    432        final @Test public void testRES_dependenciesAreResolved() {
    433            if ((getComponentAdapterNature() & RESOLVING) > 0) {
    434                final List dependencies = new LinkedList();
    435                final Object[] wrapperDependencies = new Object[]{dependencies};
    436                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    437                final ComponentAdapter componentAdapter = prepRES_dependenciesAreResolved(picoContainer);
    438                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    439                assertFalse(picoContainer.getComponentAdapters().contains(componentAdapter));
    440                final PicoContainer wrappedPicoContainer = wrapComponentInstances(
    441                        CollectingBehavior.class, picoContainer, wrapperDependencies);
    442                final Object instance = componentAdapter.getComponentInstance(wrappedPicoContainer, ComponentAdapter.NOTHING.class);
    443                assertNotNull(instance);
    444                assertTrue(dependencies.size() > 0);
    445            }
    446        }
    447
    448        /**
    449         * Prepare the test <em>failingVerificationWithCyclicDependencyException</em>. Overload this function, if the
    450         * ComponentAdapter is resolves dependencies.
    451         *
    452         * @param picoContainer container, used to register dependencies.
    453         * @return a ComponentAdapter of the type to test with a component that has cyclic dependencies. You have to
    454         *         register the component itself in the pico.
    455         */
    456        protected ComponentAdapter prepRES_failingVerificationWithCyclicDependencyException(
    457                MutablePicoContainer picoContainer) {
    458            throw new AssertionFailedError("You have to overwrite this method for a useful test");
    459        }
    460
    461        final @Test public void testRES_failingVerificationWithCyclicDependencyException() {
    462            if ((getComponentAdapterNature() & RESOLVING) > 0) {
    463                final Set cycleInstances = new HashSet();
    464                final ObjectReference cycleCheck = new SimpleReference();
    465                final Object[] wrapperDependencies = new Object[]{cycleInstances, cycleCheck};
    466                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    467                final ComponentAdapter componentAdapter = prepRES_failingVerificationWithCyclicDependencyException(picoContainer);
    468                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    469                assertTrue(picoContainer.getComponentAdapters().contains(componentAdapter));
    470                final PicoContainer wrappedPicoContainer = wrapComponentInstances(
    471                        CycleDetectorBehavior.class, picoContainer, wrapperDependencies);
    472                try {
    473                    componentAdapter.verify(wrappedPicoContainer);
    474                    fail("Thrown PicoVerificationException excpected");
    475                } catch (final AbstractInjector.CyclicDependencyException cycle) {
    476                    final Class[] dependencies = cycle.getDependencies();
    477                    assertSame(dependencies[0], dependencies[dependencies.length - 1]);
    478                }
    479            }
    480        }
    481
    482        /**
    483         * Prepare the test <em>failingInstantiationWithCyclicDependencyException</em>. Overload this function, if the
    484         * ComponentAdapter is resolves dependencies.
    485         *
    486         * @param picoContainer container, used to register dependencies.
    487         * @return a ComponentAdapter of the type to test with a component that has cyclic dependencies. You have to
    488         *         register the component itself in the pico.
    489         */
    490        protected ComponentAdapter prepRES_failingInstantiationWithCyclicDependencyException(
    491                MutablePicoContainer picoContainer) {
    492            throw new AssertionFailedError("You have to overwrite this method for a useful test");
    493        }
    494
    495        final @Test public void testRES_failingInstantiationWithCyclicDependencyException() {
    496            if ((getComponentAdapterNature() & RESOLVING) > 0) {
    497                final Set cycleInstances = new HashSet();
    498                final ObjectReference cycleCheck = new SimpleReference();
    499                final Object[] wrapperDependencies = new Object[]{cycleInstances, cycleCheck};
    500                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentFactory());
    501                final ComponentAdapter componentAdapter = prepRES_failingInstantiationWithCyclicDependencyException(picoContainer);
    502                assertSame(getComponentAdapterType(), componentAdapter.getClass());
    503                assertTrue(picoContainer.getComponentAdapters().contains(componentAdapter));
    504                final PicoContainer wrappedPicoContainer = wrapComponentInstances(
    505                        CycleDetectorBehavior.class, picoContainer, wrapperDependencies);
    506                try {
    507                    componentAdapter.getComponentInstance(wrappedPicoContainer, ComponentAdapter.NOTHING.class);
    508                    fail("Thrown CyclicDependencyException excpected");
    509                } catch (final AbstractInjector.CyclicDependencyException e) {
    510                    final Class[] dependencies = e.getDependencies();
    511                    assertSame(dependencies[0], dependencies[dependencies.length - 1]);
    512                }
    513            }
    514        }
    515
    516        // ============================================
    517        // Model & Helpers
    518        // ============================================
    519
    520        static class RecordingVisitor extends AbstractPicoVisitor {
    521            private final List visitedElements = new LinkedList();
    522
    523            public boolean visitContainer(PicoContainer pico) {
    524                visitedElements.add(pico);
    525                return CONTINUE_TRAVERSAL;
    526            }
    527
    528            public void visitComponentAdapter(ComponentAdapter componentAdapter) {
    529                visitedElements.add(componentAdapter);
    530            }
    531
    532            public void visitComponentFactory(ComponentFactory componentFactory) {
    533                visitedElements.add(componentFactory);
    534            }
    535
    536            public void visitParameter(Parameter parameter) {
    537                visitedElements.add(parameter);
    538            }
    539
    540            List getVisitedElements() {
    541                return visitedElements;
    542            }
    543        }
    544
    545        static public class NotInstantiatableBehavior extends AbstractBehavior {
    546            public NotInstantiatableBehavior(final ComponentAdapter delegate) {
    547                super(delegate);
    548            }
    549
    550            public Object getComponentInstance(final PicoContainer container, Type into) {
    551                Assert.fail("Not instantiatable");
    552                return null;
    553            }
    554            public String getDescriptor() {
    555                return null;
    556            }
    557
    558        }
    559
    560        static public class CollectingBehavior extends AbstractBehavior {
    561            final List list;
    562
    563            public CollectingBehavior(final ComponentAdapter delegate, final List list) {
    564                super(delegate);
    565                this.list = list;
    566            }
    567
    568            public Object getComponentInstance(final PicoContainer container, Type into) {
    569                final Object result = super.getComponentInstance(container, into);
    570                list.add(result);
    571                return result;
    572            }
    573
    574            public String getDescriptor() {
    575                return "xxx";
    576            }
    577        }
    578
    579        static public class CycleDetectorBehavior extends AbstractBehavior {
    580            private final Set set;
    581            private final ObjectReference reference;
    582
    583            public CycleDetectorBehavior(
    584                    final ComponentAdapter delegate, final Set set, final ObjectReference reference) {
    585                super(delegate);
    586                this.set = set;
    587                this.reference = reference;
    588            }
    589
    590            public Object getComponentInstance(final PicoContainer container, Type into) {
    591                if (set.contains(this)) {
    592                    reference.set(this);
    593                } else {
    594                    set.add(this);
    595                }
    596                return super.getComponentInstance(container, into);
    597            }
    598
    599            public String getDescriptor() {
    600                return "xxx";
    601            }
    602        }
    603
    604        public static final class RecordingLifecycleStrategy implements LifecycleStrategy {
    605            private final StringBuffer recorder;
    606
    607            public RecordingLifecycleStrategy(StringBuffer recorder) {
    608                this.recorder = recorder;
    609            }
    610
    611            public void start(Object component) {
    612                recorder.append("<start");
    613            }
    614
    615            public void stop(Object component) {
    616                recorder.append("<stop");
    617            }
    618
    619            public void dispose(Object component) {
    620                recorder.append("<dispose");
    621            }
    622
    623            public boolean hasLifecycle(Class type) {
    624                return true;
    625            }
    626
    627            public String recording() {
    628                return recorder.toString();
    629            }
    630        }
    631
    632        final protected PicoContainer wrapComponentInstances(
    633                final Class decoratingComponentAdapterClass, final PicoContainer picoContainer,
    634                final Object[] wrapperDependencies) {
    635            assertTrue(AbstractBehavior.class.isAssignableFrom(decoratingComponentAdapterClass));
    636            final MutablePicoContainer mutablePicoContainer = new DefaultPicoContainer();
    637            final int size = (wrapperDependencies != null ? wrapperDependencies.length : 0) + 1;
    638            final Collection allComponentAdapters = picoContainer.getComponentAdapters();
    639            for (Object allComponentAdapter : allComponentAdapters) {
    640                final Parameter[] parameters = new Parameter[size];
    641                parameters[0] = new ConstantParameter(allComponentAdapter);
    642                for (int i = 1; i < parameters.length; i++) {
    643                    parameters[i] = new ConstantParameter(wrapperDependencies[i - 1]);
    644                }
    645                final MutablePicoContainer instantiatingPicoContainer = new DefaultPicoContainer(
    646                    new ConstructorInjection());
    647                instantiatingPicoContainer.addComponent(
    648                    "decorator", decoratingComponentAdapterClass, parameters);
    649                mutablePicoContainer.addAdapter((ComponentAdapter)instantiatingPicoContainer
    650                    .getComponent("decorator"));
    651            }
    652            return mutablePicoContainer;
    653        }
    654
    655        private boolean supportsParameters(final Class type) {
    656            boolean hasParameters = false;
    657            final Constructor[] constructors = type.getConstructors();
    658            for (int i = 0; i < constructors.length && !hasParameters; i++) {
    659                final Constructor constructor = constructors[i];
    660                final Class[] parameterTypes = constructor.getParameterTypes();
    661                for (final Class parameterType : parameterTypes) {
    662                    if (Parameter.class.isAssignableFrom(parameterType)
    663                        || (parameterType.isArray() && Parameter.class.isAssignableFrom(parameterType
    664                        .getComponentType()))) {
    665                        hasParameters = true;
    666                        break;
    667                    }
    668                }
    669            }
    670            return hasParameters;
    671        }
    672    }