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    package org.picocontainer.behaviors;
009    
010    import static junit.framework.Assert.assertNull;
011    import static org.junit.Assert.assertEquals;
012    import static org.junit.Assert.assertNotNull;
013    import static org.junit.Assert.assertSame;
014    import static org.junit.Assert.fail;
015    import org.picocontainer.*;
016    import static org.picocontainer.tck.MockFactory.mockeryWithCountingNamingScheme;
017    
018    import org.jmock.Expectations;
019    import org.jmock.Mockery;
020    import org.jmock.integration.junit4.JMock;
021    import org.junit.Test;
022    import org.junit.runner.RunWith;
023    import org.picocontainer.containers.EmptyPicoContainer;
024    import org.picocontainer.injectors.ConstructorInjection;
025    import org.picocontainer.lifecycle.StartableLifecycleStrategy;
026    import org.picocontainer.monitors.NullComponentMonitor;
027    import org.picocontainer.testmodel.SimpleTouchable;
028    import org.picocontainer.testmodel.Touchable;
029    
030    import java.lang.reflect.Field;
031    
032    
033    /**
034     * @author Mauro Talevi
035     */
036    @RunWith(JMock.class)
037    public class CachedTestCase {
038    
039            private Mockery mockery = mockeryWithCountingNamingScheme();
040            
041        @Test public void testComponentIsNotStartedWhenCachedAndCanBeStarted() {
042            Cached adapter = new Cached(
043                    mockComponentAdapterSupportingLifecycleStrategy(true, false, false, false, false));
044            PicoContainer pico = new DefaultPicoContainer();
045            adapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
046            adapter.start(pico);
047        }
048    
049        @Test public void testComponentCanBeStartedAgainAfterBeingStopped() {
050            Cached adapter = new Cached(
051                    mockComponentAdapterSupportingLifecycleStrategy(true, true, false, false, false));
052            PicoContainer pico = new DefaultPicoContainer();
053            adapter.start(pico);
054            Object instanceAfterFirstStart = adapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
055            adapter.stop(pico);
056            adapter.start(pico);
057            Object instanceAfterSecondStart = adapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
058            assertSame(instanceAfterFirstStart, instanceAfterSecondStart);
059        }
060    
061        @Test public void testComponentCannotBeStartedIfDisposed() {
062            Cached adapter = new Cached(
063                    mockComponentAdapterSupportingLifecycleStrategy(false, false, true, true, true));
064            PicoContainer pico = new DefaultPicoContainer();
065            adapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
066            adapter.dispose(pico);
067            try {
068                adapter.start(pico);
069                fail("IllegalStateException expected");
070            } catch (Exception e) {
071                assertEquals("'interface org.picocontainer.testmodel.Touchable' already disposed", e.getMessage());
072            }
073        }
074    
075        @Test public void testComponentDisposedEffectivelyIgnoredIfNotInstantiated() throws NoSuchFieldException, IllegalAccessException {
076            Cached adapter = new Cached(
077                    mockComponentAdapterSupportingLifecycleStrategy(false, false, false, false, false));
078            PicoContainer pico = new DefaultPicoContainer();
079            adapter.dispose(pico);
080            assertNull(adapter.getStoredObject());
081        }
082    
083        @Test public void testComponentCannotBeStartedIfAlreadyStarted() {
084            Cached adapter = new Cached(
085                    mockComponentAdapterSupportingLifecycleStrategy(true, false, false, true, false));
086            PicoContainer pico = new DefaultPicoContainer();
087            adapter.start(pico);
088            try {
089                adapter.start(pico);
090                fail("IllegalStateException expected");
091            } catch (Exception e) {
092                assertEquals("'interface org.picocontainer.testmodel.Touchable' already started", e.getMessage());
093            }
094        }
095    
096        @Test public void testComponentCannotBeStoppedIfDisposed() {
097            Cached adapter = new Cached(
098                    mockComponentAdapterSupportingLifecycleStrategy(false, false, true, true, false));
099            PicoContainer pico = new DefaultPicoContainer();
100            adapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);        
101            adapter.dispose(pico);
102            try {
103                adapter.stop(pico);
104                fail("IllegalStateException expected");
105            } catch (Exception e) {
106                assertEquals("'interface org.picocontainer.testmodel.Touchable' already disposed", e.getMessage());
107            }
108        }
109    
110        @Test public void testComponentCannotBeStoppedIfNotInstantiated() {
111            Cached adapter = new Cached(
112                    mockComponentAdapterSupportingLifecycleStrategy(false, false, false, true, false));
113            PicoContainer pico = new DefaultPicoContainer();
114            try {
115                adapter.stop(pico);
116                fail("IllegalStateException expected");
117            } catch (Exception e) {
118                assertEquals("'interface org.picocontainer.testmodel.Touchable' not instantiated", e.getMessage());
119            }
120        }
121    
122        @Test public void testComponentCannotBeStoppedIfNotStarted() {
123            Cached adapter = new Cached(
124                    mockComponentAdapterSupportingLifecycleStrategy(true, true, false, true, false));
125            PicoContainer pico = new DefaultPicoContainer();
126            adapter.start(pico);
127            adapter.stop(pico);
128            try {
129            adapter.stop(pico);
130                fail("IllegalStateException expected");
131            } catch (Exception e) {
132                assertEquals("'interface org.picocontainer.testmodel.Touchable' not started", e.getMessage());
133            }
134        }
135    
136        @Test public void testComponentCannotBeDisposedIfAlreadyDisposed() {
137            Cached adapter = new Cached(
138                    mockComponentAdapterSupportingLifecycleStrategy(true, true, true, true, false));
139            PicoContainer pico = new DefaultPicoContainer();
140            adapter.start(pico);
141            adapter.stop(pico);
142            adapter.dispose(pico);
143            try {
144                adapter.dispose(pico);
145                fail("IllegalStateException expected");
146            } catch (Exception e) {
147                assertEquals("'interface org.picocontainer.testmodel.Touchable' already disposed", e.getMessage());
148            }
149        }
150    
151        @Test public void testComponentIsStoppedAndDisposedIfStartedWhenFlushed() {
152            Cached adapter = new Cached(
153                    mockComponentAdapterSupportingLifecycleStrategy(true, true, true, false, false));
154            PicoContainer pico = new DefaultPicoContainer();
155            adapter.start(pico);
156            adapter.flush();
157        }
158    
159        @Test public void testComponentIsNotStoppedAndDisposedWhenFlushedIfNotStarted() {
160            Cached adapter = new Cached(
161                    mockComponentAdapterSupportingLifecycleStrategy(false, false, false, false, false));
162            adapter.flush();
163        }
164    
165        @Test public void testComponentIsNotStoppedAndDisposedWhenFlushedIfDelegateDoesNotSupportLifecycle() {
166            Cached adapter = new Cached(
167                    mockComponentAdapterNotSupportingLifecycleStrategy());
168            adapter.flush();
169        }
170    
171        @Test public void testLifecycleIsIgnoredIfDelegateDoesNotSupportIt() {
172            Cached adapter = new Cached(
173                    mockComponentAdapterNotSupportingLifecycleStrategy());
174            PicoContainer pico = new DefaultPicoContainer();
175            adapter.start(pico);
176            adapter.stop(pico);
177            adapter.dispose(pico);
178        }
179    
180        @Test public void testCanStopAComponentThatWasNeverStartedBecauseItHasNoLifecycle() {
181            MutablePicoContainer pico = new DefaultPicoContainer();
182    
183            pico.addComponent(StringBuffer.class);
184    
185            pico.start();
186    
187            assertNotNull(pico.getComponent(StringBuffer.class));
188    
189            pico.stop();
190            pico.dispose();
191        }
192    
193        private ComponentAdapter mockComponentAdapterNotSupportingLifecycleStrategy() {
194            return mockery.mock(ComponentAdapter.class);
195        }
196    
197        private ComponentAdapter mockComponentAdapterSupportingLifecycleStrategy(
198                final boolean start, final boolean stop, final boolean dispose, final boolean getKey, final boolean instantiate) {
199            final boolean hasLifecycle = start || stop || dispose;
200            final ComponentAdapterSupportingLifecycleStrategy ca = mockery.mock(ComponentAdapterSupportingLifecycleStrategy.class);
201            mockery.checking(new Expectations(){{
202                if (getKey) {
203                    atLeast(1).of(ca).getComponentKey();
204                    will(returnValue(Touchable.class));
205                }
206                if (start) {
207                    atLeast(1).of(ca).start(with(any(Touchable.class)));
208                }
209                if (stop) {
210                    one(ca).stop(with(any(Touchable.class)));
211                }
212                if (dispose) {
213                    one(ca).dispose(with(any(Touchable.class)));
214                }
215                if (hasLifecycle || instantiate) {
216                    one(ca).getComponentInstance(with(any(PicoContainer.class)), with(same(ComponentAdapter.NOTHING.class)));
217                    will(returnValue(new SimpleTouchable()));
218                }
219                one(ca).getComponentImplementation();
220                will(returnValue(SimpleTouchable.class));
221                one(ca).hasLifecycle(with(same(SimpleTouchable.class)));
222                will(returnValue(true));
223            }});
224            return ca;
225        }
226    
227        public static interface ComponentAdapterSupportingLifecycleStrategy extends ComponentAdapter,
228                LifecycleStrategy {
229        }
230    }