package info.ebenoit.ebul.cmp; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** Tests for {@link ComponentState} */ public class TestComponentState { /** * A "fake" {@link ComponentRegistry} whose initialisation, activation and failure state can be modified manually */ private static class FakeRegistry extends ComponentRegistry { private boolean initialised; private boolean active; private boolean failed; @Override public boolean isInitialised( ) { return this.initialised; } @Override public boolean isActive( ) { return this.active; } @Override public boolean hasFailed( ) { return this.failed; } } /** Class used to test parametric component handling */ private static class PCmpTest implements ParametricComponent { private final String componentName; public PCmpTest( final String componentName ) { super( ); this.componentName = componentName; } @Override public String getComponentName( ) { return this.componentName; } } /** Class used to test methods that affect the component's lifecycle */ private static class LCATest { private LifecycleStage stage; } /** Class used to test {@link ComponentState#restart()} */ private static class RestartTest { private boolean stopped; private boolean started; private static ComponentState get( final ComponentRegistry reg , final boolean active ) { return TestComponentState.makeState( RestartTest.getDef( ) , reg , true , active ); } private static NewComponentInfo< RestartTest > getDef( ) { return new NewComponentInfo< >( new RestartTest( ) ) // .setLifecycleAction( LifecycleStage.START , o -> { o.started = true; } ) // .setLifecycleAction( LifecycleStage.STOP , o -> { o.stopped = true; } ); } } private static ComponentState makeState( final NewComponentInfo< ? > ci , final ComponentRegistry reg , final boolean initialised , final boolean active ) { final ComponentState cs = new ComponentState( reg , ci ); cs.setInitialised( initialised ); cs.setActive( active ); return cs; } private FakeRegistry reg; @Before public void setup( ) { this.reg = new FakeRegistry( ); } /** Test: initialising a {@link ComponentState} using an existing object */ @Test public void testInitialiseWithObject( ) { final Object object = new Object( ); final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( object ); final ComponentState cs = new ComponentState( this.reg , ci ); Assert.assertSame( object , cs.getComponent( ) ); Assert.assertEquals( "Object" , cs.getName( ) ); } /** Test: initialising a {@link ComponentState} using a component supplier */ @Test public void testInitialiseWithSupplier( ) { final Object object = new Object( ); final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( ( ) -> object ); final ComponentState cs = new ComponentState( this.reg , ci ); Assert.assertSame( object , cs.getComponent( ) ); Assert.assertEquals( "Object" , cs.getName( ) ); } /** * Test: initialising a {@link ComponentState} using a component supplier that throws an exception throws a * {@link ComponentCreationException} */ @Test( expected = ComponentCreationException.class ) public void testInitialiseWithFailingSupplier( ) { final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( ( ) -> { throw new Exception( "failing" ); } ); new ComponentState( this.reg , ci ); } /** Test: initialising a {@link ComponentState} with a supplied name */ @Test public void testInitialiseWithName( ) { final Object object = new Object( ); final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( object ).setName( "Test" ); final ComponentState cs = new ComponentState( this.reg , ci ); Assert.assertEquals( "Test" , cs.getName( ) ); } /** Test: initialising a {@link ComponentState} with {@link ParametricComponent} */ @Test public void testInitialiseWithParametricComponent( ) { final Object object = new PCmpTest( "ParametricName" ); final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( object ); final ComponentState cs = new ComponentState( this.reg , ci ); Assert.assertEquals( "ParametricName" , cs.getName( ) ); } /** Test: initialising a {@link ComponentState} with {@link ParametricComponent} overrides any configured name */ @Test public void testInitialiseWithParametricComponentAndName( ) { final Object object = new PCmpTest( "ParametricName" ); final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( object ).setName( "Test" ); final ComponentState cs = new ComponentState( this.reg , ci ); Assert.assertEquals( "ParametricName" , cs.getName( ) ); } /** Test: {@link ComponentState} copies autostart flag */ @Test public void testInitialiseAutostart( ) { final Object object = new Object( ); NewComponentInfo< Object > ci = new NewComponentInfo< Object >( object ); ComponentState cs = new ComponentState( this.reg , ci ); Assert.assertFalse( cs.hasAutostart( ) ); ci = new NewComponentInfo< Object >( object ).setAutostart( true ); cs = new ComponentState( this.reg , ci ); Assert.assertTrue( cs.hasAutostart( ) ); } /** Test: {@link ComponentState} copies lifecycle actions and can execute them */ @Test public void testInitialiseLCA( ) throws Throwable { final LCATest lca = new LCATest( ); final NewComponentInfo< LCATest > ci = new NewComponentInfo< LCATest >( lca ); for ( final LifecycleStage stage : LifecycleStage.values( ) ) { ci.setLifecycleAction( stage , ( o ) -> { o.stage = stage; } ); } final ComponentState cs = new ComponentState( this.reg , ci ); for ( final LifecycleStage stage : LifecycleStage.values( ) ) { cs.runLifecycleAction( stage ); Assert.assertSame( lca.stage , stage ); } } /** Test: {@link ComponentState#toString()} on anonymous components */ @Test public void testToStringAnon( ) { final Object object = new PCmpTest( null ); final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( object ); final ComponentState cs = new ComponentState( this.reg , ci ); Assert.assertEquals( "anonymous component of type " + PCmpTest.class.getCanonicalName( ) , cs.toString( ) ); } /** Test: {@link ComponentState#toString()} on named components */ @Test public void testToStringNamed( ) { final Object object = new Object( ); final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( object ).setName( "Test" ); final ComponentState cs = new ComponentState( this.reg , ci ); Assert.assertEquals( "component 'Test' of type " + Object.class.getCanonicalName( ) , cs.toString( ) ); } /** Test: {@link ComponentState#addDependency(ComponentState)} adds the specified dependency */ @Test public void testAddDependency( ) { final Object object = new Object( ); final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( object ); final ComponentState cs1 = new ComponentState( this.reg , ci ); final ComponentState cs2 = new ComponentState( this.reg , ci ); cs1.addDependency( cs2 ); Assert.assertEquals( 1 , cs1.getDependencies( ).size( ) ); Assert.assertTrue( cs1.getDependencies( ).contains( cs2 ) ); Assert.assertEquals( 1 , cs2.getReverseDependencies( ).size( ) ); Assert.assertTrue( cs2.getReverseDependencies( ).contains( cs1 ) ); } /** Test: {@link ComponentState#start()} on uninitialised components throws {@link IllegalStateException} */ @Test( expected = IllegalStateException.class ) public void testStartUninitialised( ) { final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( new Object( ) ); final ComponentState cs = new ComponentState( this.reg , ci ); this.reg.active = true; cs.start( ); } /** Test: {@link ComponentState#start()} with a failed registry throws {@link IllegalStateException} */ @Test( expected = IllegalStateException.class ) public void testStartFailedRegistry( ) { final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( new Object( ) ); final ComponentState cs = new ComponentState( this.reg , ci ); cs.setInitialised( true ); this.reg.failed = true; cs.start( ); } /** Test: {@link ComponentState#start()} with an inactive registry throws {@link IllegalStateException} */ @Test( expected = IllegalStateException.class ) public void testStartInactiveRegistry( ) { final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( new Object( ) ); final ComponentState cs = new ComponentState( this.reg , ci ); cs.setInitialised( true ); cs.start( ); } /** Test: {@link ComponentState#start()} with no lifecycle action runs without a hitch */ @Test public void testStartNoAction( ) { final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( new Object( ) ); final ComponentState cs = new ComponentState( this.reg , ci ); cs.setInitialised( true ); this.reg.active = true; cs.start( ); Assert.assertTrue( cs.isActive( ) ); } /** Test: {@link ComponentState#start()} runs the startup action */ @Test public void testStartStartupAction( ) { final LCATest lcaTest = new LCATest( ); final NewComponentInfo< LCATest > ci = new NewComponentInfo< LCATest >( lcaTest ) // .setLifecycleAction( LifecycleStage.START , ( o ) -> { o.stage = LifecycleStage.START; } ); final ComponentState cs = new ComponentState( this.reg , ci ); cs.setInitialised( true ); this.reg.active = true; cs.start( ); Assert.assertSame( LifecycleStage.START , lcaTest.stage ); Assert.assertTrue( cs.isActive( ) ); } /** Test: {@link ComponentState#start()} on an active component does nothing */ @Test public void testStartStartupAlreadyActive( ) { final LCATest lcaTest = new LCATest( ); final NewComponentInfo< LCATest > ci = new NewComponentInfo< LCATest >( lcaTest ) // .setLifecycleAction( LifecycleStage.START , ( o ) -> { o.stage = LifecycleStage.START; } ); final ComponentState cs = TestComponentState.makeState( ci , this.reg , true , true ); this.reg.active = true; cs.start( ); Assert.assertNull( lcaTest.stage ); } /** Test: {@link ComponentState#start()} re-throws {@link ComponentStartupException} */ @Test public void testStartStartupActionExceptionPassthrough( ) { final ComponentStartupException failure = new ComponentStartupException( ); final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( new Object( ) ) // .setLifecycleAction( LifecycleStage.START , ( o ) -> { throw failure; } ); final ComponentState cs = TestComponentState.makeState( ci , this.reg , true , false ); this.reg.active = true; try { cs.start( ); } catch ( final ComponentStartupException e ) { Assert.assertSame( failure , e ); return; } Assert.fail( "no ComponentStartupException" ); } /** Test: {@link ComponentState#start()} transmits other exceptions using a {@link ComponentStartupException} */ @Test public void testStartStartupActionExceptionTransmit( ) { final Exception failure = new Exception( ); final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( new Object( ) ) // .setLifecycleAction( LifecycleStage.START , ( o ) -> { throw failure; } ); final ComponentState cs = TestComponentState.makeState( ci , this.reg , true , false ); this.reg.active = true; try { cs.start( ); } catch ( final ComponentStartupException e ) { Assert.assertSame( failure , e.getCause( ) ); return; } Assert.fail( "no ComponentStartupException" ); } /** Test: {@link ComponentState#start()} attempts to start a component's dependencies */ @Test public void testStartDependencies( ) { final LCATest lcaTest = new LCATest( ); final NewComponentInfo< LCATest > ci1 = new NewComponentInfo< LCATest >( lcaTest ) // .setLifecycleAction( LifecycleStage.START , ( o ) -> { o.stage = LifecycleStage.START; } ); final ComponentState cs1 = TestComponentState.makeState( ci1 , this.reg , true , false ); final NewComponentInfo< Object > ci2 = new NewComponentInfo< Object >( new Object( ) ); final ComponentState cs2 = TestComponentState.makeState( ci2 , this.reg , true , false ); cs2.addDependency( cs1 ); this.reg.active = true; cs2.start( ); Assert.assertSame( LifecycleStage.START , lcaTest.stage ); } /** Test: {@link ComponentState#stop()} with a failed registry throws {@link IllegalStateException} */ @Test( expected = IllegalStateException.class ) public void testStopFailedRegistry( ) { final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( new Object( ) ); final ComponentState cs = TestComponentState.makeState( ci , this.reg , true , true ); this.reg.failed = true; cs.stop( ); } /** Test: {@link ComponentState#stop()} with no configured action works */ @Test public void testStopNoAction( ) { final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( new Object( ) ); final ComponentState cs = TestComponentState.makeState( ci , this.reg , true , true ); this.reg.active = true; Assert.assertNull( cs.stop( ) ); Assert.assertFalse( cs.isActive( ) ); } /** Test: {@link ComponentState#stop()} executes the configured stop action */ @Test public void testStopAction( ) { final LCATest lcaTest = new LCATest( ); final NewComponentInfo< LCATest > ci = new NewComponentInfo< LCATest >( lcaTest ) // .setLifecycleAction( LifecycleStage.STOP , ( o ) -> { o.stage = LifecycleStage.STOP; } ); final ComponentState cs = TestComponentState.makeState( ci , this.reg , true , true ); this.reg.active = true; Assert.assertNull( cs.stop( ) ); Assert.assertFalse( cs.isActive( ) ); Assert.assertSame( LifecycleStage.STOP , lcaTest.stage ); } /** Test: {@link ComponentState#stop()} returns an exception thrown by the configured stop action */ @Test public void testStopActionFailure( ) { final Exception failure = new Exception( ); final NewComponentInfo< Object > ci = new NewComponentInfo< Object >( new Object( ) ) // .setLifecycleAction( LifecycleStage.STOP , ( o ) -> { throw failure; } ); final ComponentState cs = TestComponentState.makeState( ci , this.reg , true , true ); this.reg.active = true; Assert.assertSame( failure , cs.stop( ) ); Assert.assertTrue( cs.isActive( ) ); } /** Test: {@link ComponentState#stop()} stops reverse dependencies */ @Test public void testStopActionStopReverseDeps( ) { final NewComponentInfo< Object > ci1 = new NewComponentInfo< Object >( new Object( ) ); final ComponentState cs1 = TestComponentState.makeState( ci1 , this.reg , true , true ); final NewComponentInfo< Object > ci2 = new NewComponentInfo< Object >( new Object( ) ); final ComponentState cs2 = TestComponentState.makeState( ci2 , this.reg , true , true ); cs2.addDependency( cs1 ); this.reg.active = true; Assert.assertNull( cs1.stop( ) ); Assert.assertFalse( cs2.isActive( ) ); } /** Test: {@link ComponentState#restart()} with a failed registry throws {@link IllegalStateException} */ @Test( expected = IllegalStateException.class ) public void testRestartFailedRegistry( ) { final NewComponentInfo< RestartTest > ci = new NewComponentInfo< >( new RestartTest( ) ); final ComponentState cs = TestComponentState.makeState( ci , this.reg , true , true ); this.reg.failed = true; cs.restart( ); } /** Test: {@link ComponentState#restart()} on a stopped component does nothing */ @Test public void testRestartInactive( ) { final ComponentState cs = RestartTest.get( this.reg , false ); final RestartTest rt = (RestartTest) cs.getComponent( ); this.reg.active = true; cs.restart( ); Assert.assertFalse( rt.stopped ); Assert.assertFalse( rt.started ); } /** Test: {@link ComponentState#restart()} on an active component restarts it */ @Test public void testRestartActive( ) { final ComponentState cs = RestartTest.get( this.reg , true ); final RestartTest rt = (RestartTest) cs.getComponent( ); this.reg.active = true; cs.restart( ); Assert.assertTrue( rt.stopped ); Assert.assertTrue( rt.started ); } /** Test: {@link ComponentState#restart()} throws a {@link ComponentRestartException} when stop fails */ @Test public void testRestartWhenStopFails( ) { final Exception failure = new Exception( ); final NewComponentInfo< RestartTest > ci = RestartTest.getDef( ); ci.setLifecycleAction( LifecycleStage.STOP , ( o ) -> { throw failure; } ); final ComponentState cs = TestComponentState.makeState( ci , this.reg , true , true ); this.reg.active = true; try { cs.restart( ); } catch ( final ComponentRestartException e ) { Assert.assertSame( failure , e.getCause( ) ); Assert.assertTrue( cs.isActive( ) ); return; } Assert.fail( "no ComponentRestartException" ); } /** Test: {@link ComponentState#restart()} throws a {@link ComponentRestartException} when start fails */ @Test public void testRestartWhenStartFails( ) { final ComponentStartupException failure = new ComponentStartupException( ); final NewComponentInfo< RestartTest > ci = RestartTest.getDef( ); ci.setLifecycleAction( LifecycleStage.START , ( o ) -> { throw failure; } ); final ComponentState cs = TestComponentState.makeState( ci , this.reg , true , true ); this.reg.active = true; try { cs.restart( ); } catch ( final ComponentRestartException e ) { Assert.assertSame( failure , e.getCause( ) ); Assert.assertFalse( cs.isActive( ) ); return; } Assert.fail( "no ComponentRestartException" ); } /** Test: {@link ComponentState#restart()} restarts active reverse dependencies */ @Test public void testRestartActiveReverseDeps( ) { final ComponentState cs1 = TestComponentState.makeState( RestartTest.getDef( ) , this.reg , true , true ); final ComponentState cs2 = TestComponentState.makeState( RestartTest.getDef( ) , this.reg , true , true ); cs2.addDependency( cs1 ); this.reg.active = true; cs1.restart( ); final RestartTest rt = (RestartTest) cs2.getComponent( ); Assert.assertTrue( rt.stopped ); Assert.assertTrue( rt.started ); } /** Test: {@link ComponentState#restart()} does not start inactive reverse dependencies */ @Test public void testRestartInactiveReverseDeps( ) { final ComponentState cs1 = TestComponentState.makeState( RestartTest.getDef( ) , this.reg , true , true ); final ComponentState cs2 = TestComponentState.makeState( RestartTest.getDef( ) , this.reg , true , false ); cs2.addDependency( cs1 ); this.reg.active = true; cs1.restart( ); final RestartTest rt = (RestartTest) cs2.getComponent( ); Assert.assertFalse( rt.stopped ); Assert.assertFalse( rt.started ); } }