package net.sourceforge.cruisecontrol.distributed; import junit.framework.TestCase; import junit.framework.Test; import junit.framework.TestSuite; import org.apache.log4j.Logger; import net.sourceforge.cruisecontrol.builders.DistributedMasterBuilderTest; import net.sourceforge.cruisecontrol.distributed.core.MulticastDiscovery; import net.jini.core.lookup.ServiceRegistrar; import net.jini.core.lookup.ServiceTemplate; import net.jini.core.entry.Entry; import net.jini.lookup.ServiceIDListener; import java.rmi.RemoteException; import java.awt.GraphicsEnvironment; import java.util.prefs.Preferences; /** * @author Dan Rollo * Date: Jul 6, 2005 * Time: 4:12:20 PM */ public class BuildAgentTest extends TestCase { private static final Logger LOG = Logger.getLogger(BuildAgentTest.class); private static final PropertyEntry[] EMPTY_PROP_ENTRIES = new PropertyEntry[]{}; /** * Use LUSTestSetup decorator to run Jini LUS once for this test class. * @return a TestSuite wrapper by the LUSTestSetup decorator */ public static Test suite() { final TestSuite ts = new TestSuite(); ts.addTestSuite(BuildAgentTest.class); return new DistributedMasterBuilderTest.LUSTestSetup(ts); } // @todo Remove one slash in front of "/*" below to run individual tests in an IDE //* protected void setUp() throws Exception { BuildAgentTest.setSkipMainSystemExit(); BuildAgentTest.setTerminateFast(); } protected void tearDown() throws Exception { BuildAgent.kill(); } //*/ // @todo Add one slash in front of "/*" below to run individual tests in an IDE /* private static DistributedMasterBuilderTest.ProcessInfoPump jiniProcessPump; protected void setUp() throws Exception { jiniProcessPump = DistributedMasterBuilderTest.startJini(); BuildAgent.setSkipMainSystemExit(); BuildAgentTest.setTerminateFast(); } protected void tearDown() throws Exception { DistributedMasterBuilderTest.killJini(jiniProcessPump); BuildAgent.kill(); } //*/ private static void assertFindAgent(final ServiceRegistrar reg, final int retries, final boolean expectedFoundResult) throws RemoteException, InterruptedException { final Entry[] entries = SearchablePropertyEntries.getPropertiesAsEntryArray( new SearchablePropertyEntries( BuildAgentServiceImplTest.TEST_USER_DEFINED_PROPERTIES_FILE).getProperties() ); findAgent(reg, retries, expectedFoundResult, entries); } private static Object findAgent(final ServiceRegistrar reg, final int retries, final boolean expectedFoundResult, final Entry[] entries) throws RemoteException, InterruptedException { int retryCount = 0; Object result; boolean isFound; do { if (retryCount > 0) { LOG.info("\tFind agent unit test retry " + retryCount + "...Entries: " + MulticastDiscovery.toStringEntries(entries)); Thread.sleep(1000 + (250 * retryCount)); // wait a bit before retry, longer for subsequent retries } result = reg.lookup(new ServiceTemplate(null, new Class[]{BuildAgentService.class}, entries)); isFound = (result != null); retryCount++; } while ((expectedFoundResult != isFound) && (retryCount < retries)); if (expectedFoundResult) { assertNotNull("Should find agent", result); } else { assertNull("Should not find agent", result); } return result; } public static void setSkipMainSystemExit() { BuildAgent.setSkipMainSystemExit(); } public static void setTerminateFast() { BuildAgent.setTerminateFast(); } public static BuildAgent createTestBuildAgent(final String propsFile, final String userDefinedPropertiesFilename, final boolean isSkipUI, final ServiceIDListener testListener, final int testAgentID) { return new BuildAgent(propsFile, userDefinedPropertiesFilename, isSkipUI, testListener, testAgentID); } public static void terminateTestAgent(final BuildAgent agent) { agent.terminateTestAgent(agent); } /** * This test requires a bunch of manual steps: * 1. Build the cc-agent.war (created via: ant war-agent). * 2. Deploy cc-agent.war to a web server. * 3. Manually launch agent via webstat (http://localhost:8080/cc-agent/agent.jnlp). * 4. Manually run this test. * @throws Exception if anything unexpected goes wrong in the test */ public void manual_testRestart() throws Exception { final ServiceRegistrar reg = DistributedMasterBuilderTest.findTestLookupService(20 * 1000); assertNotNull("Couldn't find registrar.", reg); final Entry[] entries = SearchablePropertyEntries.getPropertiesAsEntryArray( new SearchablePropertyEntries( BuildAgentServiceImplTest.TEST_USER_DEFINED_PROPERTIES_FILE).getProperties() ); // work around timestamp prefix in build.type entry final int idxBuildTypeEntry = 3; assertEquals("Wrong entry in position where we expected to find 'build.type'.", BuildAgentServiceImplTest.ENTRY_NAME_BUILD_TYPE, ((PropertyEntry) entries[idxBuildTypeEntry]).name); entries[idxBuildTypeEntry] = new PropertyEntry(((PropertyEntry) entries[idxBuildTypeEntry]).name, "test"); final BuildAgentService agentService = (BuildAgentService) findAgent(reg, 3, true, entries); assertNotNull(agentService.getMachineName()); agentService.restart(false); // allow time for the relaunched agent to spin up and register Thread.sleep(20 * 1000); // verify first agent is dead try { agentService.getMachineName(); fail("Agent should be dead"); } catch (Exception e) { // good, this is what we want. } // find the newly relaunched agent final BuildAgentService agentService2 = (BuildAgentService) findAgent(reg, 3, true, entries); assertNotNull(agentService2.getMachineName()); agentService2.kill(false); } public void testKillNoUI() throws Exception { ServiceRegistrar reg = DistributedMasterBuilderTest.findTestLookupService(20 * 1000); assertNotNull("Couldn't find registrar.", reg); assertFindAgent(reg, 3, false); final Thread t = new Thread("BuildAgentTest testKillNoUI Thread") { public void run() { BuildAgent.main(new String[] { "-" + BuildAgent.MAIN_ARG_AGENT_PROPS, BuildAgentServiceImplTest.TEST_AGENT_PROPERTIES_FILE, "-" + BuildAgent.MAIN_ARG_USER_PROPS, BuildAgentServiceImplTest.TEST_USER_DEFINED_PROPERTIES_FILE, "-" + BuildAgent.MAIN_ARG_SKIP_UI }); } }; t.start(); // allow BuildAgent main to load and register final int maxWaitStartup = 30; int count = 0; while (count < maxWaitStartup && t.isAlive() && BuildAgent.getMainThread() == null) { Thread.sleep(500); count++; } assertTrue("Agent start thread should be alive.", t.isAlive()); assertNotNull("Agent didn't start before timeout.", BuildAgent.getMainThread()); assertTrue("Agent didn't init before timeout.", BuildAgent.getMainThread().isAlive()); assertFindAgent(reg, 10, true); final Thread mainThread = BuildAgent.getMainThread(); // hold onto main thread since kill nullifies it assertNotNull("Main thread should not be null.", mainThread); BuildAgent.kill(); assertFalse("Agent start thread should be dead.", t.isAlive()); assertFalse("Agent didn't die before timeout.", mainThread.isAlive()); // check held thread assertFindAgent(reg, 10, false); } public void testKill() throws Exception { if (GraphicsEnvironment.isHeadless()) { LOG.warn("WARNING: DETECTED HEADLESS ENVIRONMENT. Skipping test: " + getClass().getName() + ".testKill()"); return; } ServiceRegistrar reg = DistributedMasterBuilderTest.findTestLookupService(20 * 1000); assertNotNull("Couldn't find registrar.", reg); assertFindAgent(reg, 3, false); final Thread t = new Thread("BuildAgentTest testKill Thread") { public void run() { BuildAgent.main(new String[] { "-" + BuildAgent.MAIN_ARG_AGENT_PROPS, BuildAgentServiceImplTest.TEST_AGENT_PROPERTIES_FILE, "-" + BuildAgent.MAIN_ARG_USER_PROPS, BuildAgentServiceImplTest.TEST_USER_DEFINED_PROPERTIES_FILE }); } }; t.start(); // allow BuildAgent main to load and register final int maxWaitStartup = 30; int count = 0; while (count < maxWaitStartup && t.isAlive() && BuildAgent.getMainThread() == null) { Thread.sleep(500); count++; } assertTrue("Agent start thread should be alive.", t.isAlive()); assertNotNull("Agent didn't start before timeout.", BuildAgent.getMainThread()); assertTrue("Agent didn't init before timeout.", BuildAgent.getMainThread().isAlive()); assertFindAgent(reg, 10, true); final Thread mainThread = BuildAgent.getMainThread(); // hold onto main thread since kill nullifies it assertNotNull("Main thread should not be null.", mainThread); BuildAgent.kill(); assertFalse("Agent start thread should be dead.", t.isAlive()); assertFalse("Agent didn't die before timeout.", mainThread.isAlive()); // check held thread assertFindAgent(reg, 20, false); } /** * Info holder class used to restore any existing agent entryOverrides. */ static final class ClearEntryOverridesInfo { final Preferences parentNode; final String origPrefsRootName; final PropertyEntry[] origEntryOverrides; ClearEntryOverridesInfo() throws InterruptedException { final BuildAgent agentReadPrefs = DistributedMasterBuilderTest.createBuildAgent(false); parentNode = agentReadPrefs.getPrefsRoot().parent(); origPrefsRootName = agentReadPrefs.getPrefsRoot().name(); origEntryOverrides = agentReadPrefs.getEntryOverrides(); agentReadPrefs.clearEntryOverrides(); terminateTestAgent(agentReadPrefs); } void restoreOriginalEntryOverrides() { // restore entryOverrides, otherwise building CC clears JRE prefs storage on disk in build farm agents // need to recreate prefs sub-node deleted during cleanup above final Preferences prefsEntryOverrides // recreate deleted agent node = parentNode.node(origPrefsRootName) // recreate deleted entry overrides node .node(BuildAgent.PREFS_NODE_ENTRY_OVERRIDES); BuildAgent.putEntryOverrides(prefsEntryOverrides, origEntryOverrides); } } public void testSetEntryOverrides() throws Exception { // hold reference original info to restore any entryOverrides that existed before this test ran final ClearEntryOverridesInfo origEntryOverridesInfo = new ClearEntryOverridesInfo(); final BuildAgent buildAgent = DistributedMasterBuilderTest.createBuildAgent(false); try { final int expectedOrigEntryCount = 5; final PropertyEntry[] origEntries = buildAgent.getEntries(); assertEquals("Did the unit test props file change? : " + BuildAgentServiceImplTest.TEST_USER_DEFINED_PROPERTIES_FILE, expectedOrigEntryCount, origEntries.length); // add new prop entry final PropertyEntry newPropEntry = new PropertyEntry("newEntryName", "newEntryValue"); buildAgent.setEntryOverrides(new PropertyEntry[] { newPropEntry }); assertEquals(expectedOrigEntryCount + 1, buildAgent.getEntries().length); // set to empty array buildAgent.setEntryOverrides(EMPTY_PROP_ENTRIES); assertEquals(expectedOrigEntryCount, buildAgent.getEntries().length); // test clear overrides buildAgent.setEntryOverrides(new PropertyEntry[] { newPropEntry }); assertEquals(expectedOrigEntryCount + 1, buildAgent.getEntries().length); buildAgent.clearEntryOverrides(); assertEquals(expectedOrigEntryCount, buildAgent.getEntries().length); // test multiple clear calls buildAgent.clearEntryOverrides(); assertEquals(expectedOrigEntryCount, buildAgent.getEntries().length); // test override user-defined entry and system entry final PropertyEntry newOverrideUser = new PropertyEntry("build.type", "OverrideTest"); final PropertyEntry newOverrideSystem = new PropertyEntry(SearchablePropertyEntries.SYSTEM_ENTRY_KEYS[0], "OverrideSystemEntry"); buildAgent.setEntryOverrides(new PropertyEntry[] { newPropEntry, newOverrideUser, newOverrideSystem }); assertEquals(expectedOrigEntryCount + 1, buildAgent.getEntries().length); buildAgent.clearEntryOverrides(); assertEquals(expectedOrigEntryCount, buildAgent.getEntries().length); } finally { // clear overrides buildAgent.clearEntryOverrides(); // clear all agent prefs buildAgent.getPrefsRoot().removeNode(); buildAgent.getPrefsRoot().flush(); // restore entryOverrides, otherwise building CC clears JRE prefs storage on disk in build farm agents origEntryOverridesInfo.restoreOriginalEntryOverrides(); terminateTestAgent(buildAgent); } } }