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