/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2004 * Copyright by ESO (in the framework of the ALMA collaboration), * All rights reserved * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package alma.ACS.MasterComponentImpl; import alma.ACS.MasterComponent; import alma.ACS.ROstringSeq; import alma.ACS.SUBSYSSTATE_AVAILABLE; import alma.ACS.SUBSYSSTATE_INITIALIZING_PASS1; import alma.ACS.SUBSYSSTATE_OFFLINE; import alma.ACS.SUBSYSSTATE_ONLINE; import alma.ACS.SUBSYSSTATE_OPERATIONAL; import alma.ACS.SUBSYSSTATE_PREINITIALIZED; import alma.ACS.SUBSYSSTATE_PRESHUTDOWN; import alma.ACS.SUBSYSSTATE_SHUTDOWN; import alma.ACS.MasterComponentPackage.SubsystemStateEvent; import alma.ACSErr.ACSErrTypeOK; import alma.ACSErr.CompletionHolder; import alma.ACSErrTypeCommon.IllegalStateEventEx; import alma.ACSErrTypeOK.ACSErrOK; import alma.acs.component.client.ComponentClientTestCase; import alma.acs.exceptions.AcsJCompletion; import alma.acs.genfw.runtime.sm.AcsStateUtil; /** * @author hsommer * created Apr 13, 2004 4:32:51 PM */ public class MasterComponentTest extends ComponentClientTestCase { private MasterComponent m_masterComp; /** * Constructor for MasterComponentTest. */ public MasterComponentTest() throws Exception { super("MASTERCOMP1-Test"); } protected void setUp() throws Exception { super.setUp(); org.omg.CORBA.Object compObj = getContainerServices().getComponent("MASTERCOMP1"); assertNotNull(compObj); m_masterComp = alma.ACS.MasterComponentHelper.narrow(compObj); assertNotNull(m_masterComp); // MasterComponentReadOnly maCompRO = alma.ACS.MasterComponentReadOnlyHelper.narrow(compObj); // assertNotNull(maCompRO); } protected void tearDown() throws Exception { // will release MASTERCOMP1 super.tearDown(); } public void testInitPass1() throws Exception { ROstringSeq statesProperty = m_masterComp.currentStateHierarchy(); assertNotNull(statesProperty); // verify initial state String[] expectedHierarchy = new String[] { SUBSYSSTATE_AVAILABLE.value, SUBSYSSTATE_OFFLINE.value, SUBSYSSTATE_SHUTDOWN.value }; verifyCurrentState(statesProperty, expectedHierarchy); // send event m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_INITPASS1); // verify transient state (available for 1 second thanks to a sleepy test component action) expectedHierarchy[2] = SUBSYSSTATE_INITIALIZING_PASS1.value; verifyCurrentState(statesProperty, expectedHierarchy); // verify new state Thread.sleep(3000); expectedHierarchy[2] = SUBSYSSTATE_PREINITIALIZED.value; verifyCurrentState(statesProperty, expectedHierarchy); // verify correct error for illegal event try { m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_STOP); fail("expected IllegalStateEventEx"); } catch (IllegalStateEventEx e) { // assertEquals("xyz", e.errorTrace.shortDescription); m_logger.info("got IllegalStateEventEx as expected."); } catch (Exception e) { fail("expected IllegalStateEventEx, but not this one: " + e.toString()); } } public void testFullLifecycle() throws Exception { ROstringSeq statesProperty = m_masterComp.currentStateHierarchy(); assertNotNull(statesProperty); // verify initial state String[] expectedHierarchy = new String[] { SUBSYSSTATE_AVAILABLE.value, SUBSYSSTATE_OFFLINE.value, SUBSYSSTATE_SHUTDOWN.value }; verifyCurrentState(statesProperty, expectedHierarchy); m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_INITPASS1); // component-side action method initSubsysPass1 will sleep 1000 ms, during which component is in state SUBSYSSTATE_INITIALIZING_PASS1 // thus after 1500 ms we should have reached SUBSYSSTATE_PREINITIALIZED and can send the next event Thread.sleep(1500); m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_INITPASS2); // the action method initSubsysPass2 does not do anything, so we should rather quickly go via SUBSYSSTATE_INITIALIZING_PASS2 to SUBSYSSTATE_ONLINE Thread.sleep(500); expectedHierarchy = new String[] { SUBSYSSTATE_AVAILABLE.value, SUBSYSSTATE_ONLINE.value }; verifyCurrentState(statesProperty, expectedHierarchy); // todo: send more events } /** * Test method for multiple runs of {@link #_testEventSync()}. */ public void testEventSyncMultipleRuns() throws Exception { ROstringSeq statesProperty = m_masterComp.currentStateHierarchy(); assertNotNull(statesProperty); StateChangeListener listener = new StateChangeListener(m_logger); listener.createMonitor(statesProperty, getContainerServices()); for (int i=0; i < 100; i++) { m_logger.info("\n*** testEventSyncMultipleRuns(" + i + ") ***"); _testEventSync(listener); } listener.destroyMonitor(); } /** * Uses state change notification to synchronize sending the next event, * instead of stupid Thread.sleep like the above tests. * <p> * This method could be taken as an example of how subsystems can use these synchronization helper classes * to write unit tests for their own master components. */ private void _testEventSync(StateChangeListener listener) throws Exception { String[] expectedHierarchy = new String[] { SUBSYSSTATE_AVAILABLE.value, SUBSYSSTATE_OFFLINE.value, SUBSYSSTATE_SHUTDOWN.value }; listener.verifyCurrentState(expectedHierarchy); StateChangeSemaphore sync = listener.getStateChangeSemaphore(); // @TODO: At the moment it is not deterministic whether we have received already the notification // for the MC going to initial shutdown state, // or if we never get it because we registered the monitor too late (check with Matej if history is sent to new monitors...), // or if we will get the shutdown state notification later, which will cause a test failure because // the test assumes a clean starting state without pending notifications. // As a really ugly workaround we sleep for a second, to make "sure" we got the shutdown notification if we get it at all... Thread.sleep(1000); sync.reset(); m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_INITPASS1); sync.waitForStateChanges(2); // SUBSYSSTATE_INITIALIZING_PASS1, SUBSYSSTATE_PREINITIALIZED expectedHierarchy[2] = SUBSYSSTATE_PREINITIALIZED.value; assertTrue(listener.verifyCurrentState(expectedHierarchy)); m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_INITPASS2); sync.waitForStateChanges(2); // // SUBSYSSTATE_INITIALIZING_PASS2, SUBSYSSTATE_ONLINE expectedHierarchy = new String[] {SUBSYSSTATE_AVAILABLE.value, SUBSYSSTATE_ONLINE.value }; assertTrue(listener.verifyCurrentState(expectedHierarchy)); m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_START); sync.waitForStateChanges(1); // SUBSYSSTATE_OPERATIONAL expectedHierarchy[1] = SUBSYSSTATE_OPERATIONAL.value; assertTrue(listener.verifyCurrentState(expectedHierarchy)); m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_REINIT); sync.waitForStateChanges(2); // SUBSYSSTATE_REINITIALIZING, SUBSYSSTATE_ONLINE expectedHierarchy[1] = SUBSYSSTATE_ONLINE.value; assertTrue(listener.verifyCurrentState(expectedHierarchy)); m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_START); sync.waitForStateChanges(1); // SUBSYSSTATE_OPERATIONAL expectedHierarchy[1] = SUBSYSSTATE_OPERATIONAL.value; assertTrue(listener.verifyCurrentState(expectedHierarchy)); m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_STOP); sync.waitForStateChanges(1); // SUBSYSSTATE_ONLINE expectedHierarchy[1] = SUBSYSSTATE_ONLINE.value; assertTrue(listener.verifyCurrentState(expectedHierarchy)); m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_SHUTDOWNPASS1); sync.waitForStateChanges(2); // SUBSYSSTATE_SHUTTINGDOWN_PASS1, SUBSYSSTATE_PRESHUTDOWN expectedHierarchy = new String[] {SUBSYSSTATE_AVAILABLE.value, SUBSYSSTATE_OFFLINE.value, SUBSYSSTATE_PRESHUTDOWN.value }; assertTrue(listener.verifyCurrentState(expectedHierarchy)); m_masterComp.doTransition(SubsystemStateEvent.SUBSYSEVENT_SHUTDOWNPASS2); sync.waitForStateChanges(2); // SUBSYSSTATE_SHUTTINGDOWN_PASS2, SUBSYSSTATE_SHUTDOWN expectedHierarchy[2] = SUBSYSSTATE_SHUTDOWN.value; assertTrue(listener.verifyCurrentState(expectedHierarchy)); } /** * Helper method for the repeated task of getting the current state hierarchy and * comparing it against the expected hierarchy. * <p> * This method is a replica of {@link StateChangeListener#verifyCurrentState(String[])} * which is necessary for testing w/o using the synchronization facilities offered by <code>StateChangeListener</code>. */ private void verifyCurrentState(ROstringSeq statesProperty, String[] expectedHierarchy) { CompletionHolder ch = new CompletionHolder(); String[] states = statesProperty.get_sync(ch); AcsJCompletion statesSyncCompletion = AcsJCompletion.fromCorbaCompletion(ch.value); assertFalse(statesSyncCompletion.isError()); assertEquals(ACSErrTypeOK.value, statesSyncCompletion.getType()); assertEquals(ACSErrOK.value, statesSyncCompletion.getCode()); assertNotNull(states); // verify state String expectedPath = AcsStateUtil.stateHierarchyNamesToString(expectedHierarchy); String actualPath = AcsStateUtil.stateHierarchyNamesToString(states); assertEquals("current states hierarchy was not as expected!", expectedPath, actualPath); } }