From e688bb366c7cd8d8fbf0276ea795624d022a30d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Wed, 16 Sep 2015 09:38:53 +0200 Subject: [PATCH] Component state - tests --- .../ebenoit/ebul/cmp/TestComponentState.java | 598 ++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 src/test/java/info/ebenoit/ebul/cmp/TestComponentState.java diff --git a/src/test/java/info/ebenoit/ebul/cmp/TestComponentState.java b/src/test/java/info/ebenoit/ebul/cmp/TestComponentState.java new file mode 100644 index 0000000..3a05f33 --- /dev/null +++ b/src/test/java/info/ebenoit/ebul/cmp/TestComponentState.java @@ -0,0 +1,598 @@ +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 ); + } + +}