package net.sourceforge.cruisecontrol.distributed;
import junit.framework.TestCase;
import java.util.List;
import java.util.Properties;
import java.util.Map;
import java.util.HashMap;
import java.util.Arrays;
import java.util.Date;
import java.io.File;
import java.io.IOException;
import java.rmi.RemoteException;
import org.jdom.CDATA;
import org.jdom.Element;
import org.apache.log4j.Logger;
import net.sourceforge.cruisecontrol.distributed.core.PropertiesHelper;
import net.sourceforge.cruisecontrol.distributed.core.CCDistVersion;
import net.sourceforge.cruisecontrol.distributed.core.RemoteResult;
import net.sourceforge.cruisecontrol.distributed.core.RemoteResultTest;
import net.sourceforge.cruisecontrol.distributed.core.jnlputil.JNLPServiceUtil;
import net.sourceforge.cruisecontrol.builders.MockBuilder;
import net.sourceforge.cruisecontrol.builders.DistributedMasterBuilderTest;
import net.sourceforge.cruisecontrol.builders.AntBuilder;
import net.sourceforge.cruisecontrol.builders.CompositeBuilder;
import net.sourceforge.cruisecontrol.builders.AntScript;
import net.sourceforge.cruisecontrol.CruiseControlException;
import net.sourceforge.cruisecontrol.Progress;
import net.sourceforge.cruisecontrol.util.Util;
import javax.jnlp.UnavailableServiceException;
/**
* @author Dan Rollo
* Date: May 25, 2005
* Time: 1:54:38 PM
*/
public class BuildAgentServiceImplTest extends TestCase {
private static final Logger LOG = Logger.getLogger(BuildAgentServiceImplTest.class);
public static final String TEST_AGENT_PROPERTIES_FILE = "testdist.agent.properties";
public static final String TEST_USER_DEFINED_PROPERTIES_FILE = "testdist.user-defined.properties";
public static final String ENTRY_NAME_BUILD_TYPE = "build.type";
private static final File DIR_LOGS = new File(PropertiesHelper.RESULT_TYPE_LOGS);
private static final File DIR_OUTPUT = new File(PropertiesHelper.RESULT_TYPE_OUTPUT);
private static final File DIR_FILE = new File(PropertiesHelper.RESULT_TYPE_DIR);
private static final RemoteResult[] REMOTE_RESULTS_EMPTY = new RemoteResult[]{};
public static final RemoteResult[] REMOTE_RESULTS_ONE = new RemoteResult[] { new RemoteResult(0) };
static {
REMOTE_RESULTS_ONE[0].setAgentDir(PropertiesHelper.RESULT_TYPE_DIR);
REMOTE_RESULTS_ONE[0].setMasterDir("masterResultFile");
}
private static final RemoteResult[] REMOTE_RESULTS_TWO_WITHEMPTYDIR = new RemoteResult[] {
REMOTE_RESULTS_ONE[0], new RemoteResult(1)
};
static {
REMOTE_RESULTS_TWO_WITHEMPTYDIR[1].setAgentDir(PropertiesHelper.RESULT_TYPE_DIR + "2");
REMOTE_RESULTS_TWO_WITHEMPTYDIR[1].setMasterDir("masterResultFile2");
}
private Properties origSysProps;
private static final String TEST_PROJECT_FAIL = "testproject-fail";
private static final String TEST_PROJECT_SUCCESS = "testproject-success";
private static final int KILL_DELAY = 1000;
private static final String EXPECTED_SUFFIX_AS_STRING_VER
= "\n\tVersion: " + CCDistVersion.getVersion()
+ " (Compiled: " + CCDistVersion.getVersionBuildDate() + ")";
protected void setUp() throws Exception {
DIR_LOGS.delete();
DIR_OUTPUT.delete();
DIR_FILE.delete();
origSysProps = System.getProperties();
RemoteResultTest.resetTempZippedFile(REMOTE_RESULTS_ONE[0]);
}
protected void tearDown() throws Exception {
System.setProperties(origSysProps);
deleteDirConfirm(DIR_LOGS);
deleteDirConfirm(DIR_OUTPUT);
deleteDirConfirm(DIR_FILE);
}
private static void deleteDirConfirm(final File dirToDelete) {
if (dirToDelete.exists()) {
assertTrue("Error cleaning up test directory: " + dirToDelete.getAbsolutePath()
+ "\nDir Contents:\n" + Arrays.asList(dirToDelete.listFiles()),
dirToDelete.delete());
}
}
private static class MyAgentStatusListener implements BuildAgent.AgentStatusListener
{
private int agentStatusChangeCount;
public void statusChanged(BuildAgentService buildAgentServiceImpl) {
agentStatusChangeCount++;
}
int getAgentStatusChangeCount() {
return agentStatusChangeCount;
}
void resetAgentStatusChangeCount() {
this.agentStatusChangeCount = 0;
}
}
private static final File LOGGER_JAR_REAL_MAIN_DIST
// @todo Change path to main/dist if we move CCDist into main
= new File(DistributedMasterBuilderTest.MAIN_CCDIST_DIR + "../../main/dist",
AntScript.LIBNAME_PROGRESS_LOGGER);
private static final File LOGGER_JAR_MOVED = new File(LOGGER_JAR_REAL_MAIN_DIST.getParentFile(),
"hidden" + AntScript.LIBNAME_PROGRESS_LOGGER);
private static void hideAntProgressLoggerLib() {
LOGGER_JAR_REAL_MAIN_DIST.renameTo(LOGGER_JAR_MOVED);
}
private static void unhideAntProgressLoggerLib() {
LOGGER_JAR_MOVED.renameTo(LOGGER_JAR_REAL_MAIN_DIST);
}
public void testInjectProgressLibBeforeValidation() throws Exception {
final class MyAntBuilder extends AntBuilder {
static final String MSG_FORCED_FAILURE = "Forced Failure for unit testing";
private boolean isValidateCalled;
boolean isValidateCalled() { return isValidateCalled; }
private boolean isBuildCalled;
boolean isBuildCalled() { return isBuildCalled; }
void resetFlags() {
isValidateCalled = false;
isBuildCalled = false;
}
public void validate() throws CruiseControlException {
isValidateCalled = true;
super.validate();
}
public Element build(Map buildProperties, Progress progressIn)
throws CruiseControlException {
isBuildCalled = true;
// for failure
throw new CruiseControlException(MSG_FORCED_FAILURE);
}
}
final MyAntBuilder antBuilder = new MyAntBuilder();
antBuilder.setUseLogger(true);
antBuilder.setLiveOutput(true);
antBuilder.setProgressLoggerLib(LOGGER_JAR_REAL_MAIN_DIST.getAbsolutePath());
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
final Map<String, String> distributedAgentProps = new HashMap<String, String>();
final Map<String, String> projectProperties = new HashMap<String, String>();
projectProperties.put(PropertiesHelper.PROJECT_NAME, TEST_PROJECT_SUCCESS);
hideAntProgressLoggerLib();
try {
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
try {
try {
agentImpl.doBuild(antBuilder, projectProperties, distributedAgentProps, null, REMOTE_RESULTS_EMPTY);
fail("Missing progressLoggerLib should have failed validation");
} catch (RemoteException e) {
assertTrue(e.getMessage().startsWith(
"Failed to validate nested Builder on agent"));
assertTrue(e.getCause() instanceof CruiseControlException);
final CruiseControlException cce = (CruiseControlException) e.getCause();
assertTrue(cce.getMessage().startsWith("File specified ["));
}
assertTrue(antBuilder.isValidateCalled());
assertFalse(antBuilder.isBuildCalled());
} finally {
agentImpl.clearOutputFiles();
}
} finally {
unhideAntProgressLoggerLib();
}
antBuilder.resetFlags();
try {
try {
agentImpl.doBuild(antBuilder, projectProperties, distributedAgentProps, null, REMOTE_RESULTS_EMPTY);
fail("build failure should have been forced");
} catch (RemoteException e) {
assertTrue("Wrong message: " + e.getMessage(),
e.getMessage().startsWith("Failed to complete build on agent"));
assertTrue(e.getCause() instanceof CruiseControlException);
final CruiseControlException cce = (CruiseControlException) e.getCause();
assertEquals(MyAntBuilder.MSG_FORCED_FAILURE, cce.getMessage());
}
assertTrue(antBuilder.isValidateCalled());
assertTrue(antBuilder.isBuildCalled());
} finally {
agentImpl.clearOutputFiles();
}
}
public static BuildAgentServiceImpl createTestAgentImpl() {
final BuildAgentServiceImpl agentImpl = new BuildAgentServiceImpl(null);
return agentImpl;
}
public void testInjectAntProgressLoggerLibIfNeeded() throws Exception {
hideAntProgressLoggerLib();
try {
final AntBuilder antBuilder = new AntBuilder();
// should attempt injection
try {
BuildAgentServiceImpl.injectAntProgressLoggerLibIfNeeded(antBuilder);
fail("Expected JNLP Basic Service to be unavailable");
} catch (JNLPServiceUtil.JNLPServiceException e) {
assertTrue("Unexpected exception cause.",
e.getCause() instanceof UnavailableServiceException);
}
// test found via default lib location
unhideAntProgressLoggerLib();
BuildAgentServiceImpl.injectAntProgressLoggerLibIfNeeded(antBuilder);
hideAntProgressLoggerLib();
// test path already set
antBuilder.setProgressLoggerLib("someProgressLoggerLibPath");
BuildAgentServiceImpl.injectAntProgressLoggerLibIfNeeded(antBuilder);
} finally {
unhideAntProgressLoggerLib();
}
}
public void testInjectAntProgressLoggerLibIfNeededWithComposite() throws Exception {
hideAntProgressLoggerLib();
try {
final CompositeBuilder compositeBuilder = new CompositeBuilder();
BuildAgentServiceImpl.injectAntProgressLoggerLibIfNeeded(compositeBuilder);
compositeBuilder.add(new MockBuilder());
BuildAgentServiceImpl.injectAntProgressLoggerLibIfNeeded(compositeBuilder);
final AntBuilder antBuilder = new AntBuilder();
compositeBuilder.add(antBuilder);
// should attempt injection
try {
BuildAgentServiceImpl.injectAntProgressLoggerLibIfNeeded(compositeBuilder);
fail("Expected JNLP Basic Service to be unavailable");
} catch (JNLPServiceUtil.JNLPServiceException e) {
assertTrue("Unexpected exception cause.",
e.getCause() instanceof UnavailableServiceException);
}
// test path already set
antBuilder.setProgressLoggerLib("someProgressLoggerLibPath");
BuildAgentServiceImpl.injectAntProgressLoggerLibIfNeeded(compositeBuilder);
// test with composite inside composite
final CompositeBuilder compositeBuilder2 = new CompositeBuilder();
compositeBuilder.add(compositeBuilder2);
BuildAgentServiceImpl.injectAntProgressLoggerLibIfNeeded(compositeBuilder);
final AntBuilder antBuilder2 = new AntBuilder();
compositeBuilder2.add(antBuilder2);
// should attempt injection
try {
BuildAgentServiceImpl.injectAntProgressLoggerLibIfNeeded(compositeBuilder);
fail("Expected JNLP Basic Service to be unavailable");
} catch (JNLPServiceUtil.JNLPServiceException e) {
assertTrue("Unexpected exception cause.",
e.getCause() instanceof UnavailableServiceException);
}
// test path already set
antBuilder2.setProgressLoggerLib("someProgressLoggerLibPath");
BuildAgentServiceImpl.injectAntProgressLoggerLibIfNeeded(compositeBuilder);
} finally {
unhideAntProgressLoggerLib();
}
}
@SuppressWarnings(value = "unchecked")
public void testAgentBuildAddRemoteInfoToBuildResults() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
final Map<String, String> distributedAgentProps = new HashMap<String, String>();
final MockBuilder mockBuilder = createMockBuilder(false, REMOTE_RESULTS_EMPTY);
assertNull(mockBuilder.getTarget());
final Map<String, String> projectProperties = new HashMap<String, String>();
projectProperties.put(PropertiesHelper.PROJECT_NAME, TEST_PROJECT_SUCCESS);
final Element buildResult;
try {
buildResult = agentImpl.doBuild(
mockBuilder, projectProperties, distributedAgentProps, null, REMOTE_RESULTS_EMPTY);
assertEquals("build", buildResult.getName());
final List<Element> content = (List<Element>) buildResult.getContent();
int idx = 0;
final Element elmTarget = content.get(idx++);
assertEquals("[Element: <target/>]", elmTarget.toString());
assertEquals("agent", elmTarget.getAttribute("name").getValue());
assertNotNull(elmTarget.getAttribute("time").getValue());
final Element elmTask = (Element) elmTarget.getContent(0);
assertEquals("agent-childbuilder", elmTask.getAttribute("name").getValue());
final Element elmMessage = content.get(idx);
assertEquals("[Element: <message/>]", elmMessage.toString());
final CDATA elmCData = (CDATA) elmMessage.getContent(0);
assertEquals(BuildAgentServiceImpl.LOGMSGPREFIX_PREFIX + agentImpl.getMachineName() + "; "
+ mockBuilder.getClass().getName() + "; agent build attributes: ",
elmCData.getValue());
assertEquals(2, content.size());
clearDefaultSuccessResultDirs();
assertNull(mockBuilder.getTarget());
} finally {
agentImpl.clearOutputFiles();
}
}
public void testBuildOverrideTarget() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
final Map<String, String> distributedAgentProps = new HashMap<String, String>();
// build w/out override to verify null target value after build
distributedAgentProps.put(PropertiesHelper.DISTRIBUTED_OVERRIDE_TARGET, null);
final MockBuilder mockBuilder = createMockBuilder(false, REMOTE_RESULTS_EMPTY);
assertNull(mockBuilder.getTarget());
final Map<String, String> projectProperties = new HashMap<String, String>();
projectProperties.put(PropertiesHelper.PROJECT_NAME, TEST_PROJECT_SUCCESS);
try {
assertNotNull(agentImpl.doBuild(
mockBuilder, projectProperties, distributedAgentProps, null, REMOTE_RESULTS_EMPTY));
clearDefaultSuccessResultDirs();
assertNull(mockBuilder.getTarget());
} finally {
agentImpl.clearOutputFiles();
}
// build with override
distributedAgentProps.put(PropertiesHelper.DISTRIBUTED_OVERRIDE_TARGET, TEST_PROJECT_SUCCESS);
assertNull(mockBuilder.getTarget());
try {
assertNotNull(agentImpl.doBuild(
mockBuilder, projectProperties, distributedAgentProps, null, REMOTE_RESULTS_EMPTY));
clearDefaultSuccessResultDirs();
assertEquals(TEST_PROJECT_SUCCESS, mockBuilder.getTarget());
} finally {
agentImpl.clearOutputFiles();
}
}
/**
* Re-use of builder caused problems with null value in overrideTarget.
* This test verifies null values in the Map are allowed.
* @throws Exception if anything unexpected goes wrong in the test
*/
public void testGetPropertiesMap() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
MyAgentStatusListener agentListener = new MyAgentStatusListener();
agentImpl.addAgentStatusListener(agentListener);
String agentAsString = agentImpl.asString();
assertTrue("Wrong value: " + agentAsString,
agentAsString.startsWith("Machine Name: "));
String expectedSuffix = "Busy: false;\tSince: null;\tProject: null\n\t"
+ "Pending Restart: false;\tPending Restart Since: null\n\t"
+ "Pending Kill: false;\tPending Kill Since: null"
+ EXPECTED_SUFFIX_AS_STRING_VER;
assertTrue("Wrong value: \n" + agentAsString + "\n expected suffix: \n" + expectedSuffix,
agentAsString.endsWith(expectedSuffix));
final Map<String, String> distributedAgentProps = new HashMap<String, String>();
final String testProjectName = "testProjectName";
final Map<String, String> projectProperties = new HashMap<String, String>();
projectProperties.put(PropertiesHelper.PROJECT_NAME, testProjectName);
distributedAgentProps.put(PropertiesHelper.DISTRIBUTED_OVERRIDE_TARGET, null);
try {
// fails at nestedBuilder.validate() due to null nestedBuilder
agentImpl.doBuild(null, projectProperties, distributedAgentProps, null, null);
fail("should fail w/ NPE");
} catch (NullPointerException e) {
assertEquals(null, e.getMessage());
}
assertEquals("Wrong agent status change count", 2, agentListener.getAgentStatusChangeCount());
distributedAgentProps.remove(PropertiesHelper.DISTRIBUTED_OVERRIDE_TARGET);
agentImpl.setBusy(false);
agentListener.resetAgentStatusChangeCount();
try {
// gets far enough to fire 2nd agent status change
agentImpl.doBuild(null, projectProperties, distributedAgentProps, null, null);
fail("should fail w/ NPE");
} catch (NullPointerException e) {
assertEquals(null, e.getMessage());
}
assertEquals("Wrong agent status", 2, agentListener.getAgentStatusChangeCount());
agentAsString = agentImpl.asString();
assertTrue("Wrong value: " + agentAsString,
agentAsString.startsWith("Machine Name: "));
assertTrue("Wrong value: " + agentAsString,
agentAsString.indexOf("Busy: true;\tSince: ") > -1);
expectedSuffix = ";\tProject: " + testProjectName
+ "\n\tPending Restart: false;\tPending Restart Since: null\n\t"
+ "Pending Kill: false;\tPending Kill Since: null"
+ EXPECTED_SUFFIX_AS_STRING_VER;
assertTrue("Wrong value: \n" + agentAsString + "\n expected suffix: \n" + expectedSuffix,
agentAsString.endsWith(expectedSuffix));
}
public void testGetIDRemoteNullProject() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
assertFalse(agentImpl.getIDRemote().equals(agentImpl.getIDRemote()));
}
public void testGetIDRemote() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
final String testProjectName = "testProjectName";
final Map<String, String> projectProperties = new HashMap<String, String>();
projectProperties.put(PropertiesHelper.PROJECT_NAME, testProjectName);
try {
// gets far enough to set Project name...
agentImpl.doBuild(null, projectProperties, null, null, null);
fail("should fail w/ NPE");
} catch (NullPointerException e) {
assertEquals(null, e.getMessage());
}
assertEquals(agentImpl.getIDRemote(), agentImpl.getIDRemote());
}
public void testRetrieveLinesRemoteNullProject() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
assertEquals(0, agentImpl.retrieveLinesRemote(0).length);
}
public void testRetrieveLinesRemote() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
final String testProjectName = "testProjectName";
final Map<String, String> projectProperties = new HashMap<String, String>();
projectProperties.put(PropertiesHelper.PROJECT_NAME, testProjectName);
try {
// gets far enough to set Project name...
agentImpl.doBuild(null, projectProperties, null, null, null);
fail("should fail w/ NPE");
} catch (NullPointerException e) {
assertEquals(null, e.getMessage());
}
final String[] lines = agentImpl.retrieveLinesRemote(0);
assertEquals("Expected no lines: " + Arrays.asList(lines).toString(), 0, lines.length);
}
public void testAsString() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
String agentAsString = agentImpl.asString();
assertTrue("Wrong value: " + agentAsString,
agentAsString.startsWith("Machine Name: "));
assertTrue("Wrong value: " + agentAsString,
agentAsString.endsWith("Busy: false;\tSince: null;\tProject: null\n\t"
+ "Pending Restart: false;\tPending Restart Since: null\n\t"
+ "Pending Kill: false;\tPending Kill Since: null"
+ EXPECTED_SUFFIX_AS_STRING_VER));
final Map<String, String> distributedAgentProps = new HashMap<String, String>();
final String testProjectName = "testProjectName";
final Map<String, String> projectProperties = new HashMap<String, String>();
projectProperties.put(PropertiesHelper.PROJECT_NAME, testProjectName);
try {
// gets far enough to set Project name...
agentImpl.doBuild(null, projectProperties, distributedAgentProps, null, null);
fail("should fail w/ NPE");
} catch (NullPointerException e) {
assertEquals(null, e.getMessage());
}
agentAsString = agentImpl.asString();
assertTrue("Wrong value: " + agentAsString,
agentAsString.startsWith("Machine Name: "));
assertTrue("Wrong value: " + agentAsString,
agentAsString.indexOf("Busy: true;\tSince: ") > -1);
assertTrue("Wrong value: " + agentAsString,
agentAsString.endsWith(";\tProject: " + testProjectName
+ "\n\tPending Restart: false;\tPending Restart Since: null\n\t"
+ "Pending Kill: false;\tPending Kill Since: null"
+ EXPECTED_SUFFIX_AS_STRING_VER));
agentImpl.kill(true);
agentAsString = agentImpl.asString();
assertTrue("Wrong value: " + agentAsString,
agentAsString.indexOf("Busy: true;\tSince: ") > -1);
assertTrue("Wrong value: " + agentAsString,
agentAsString.indexOf(";\tProject: " + testProjectName
+ "\n\tPending Restart: false;\tPending Restart Since: null\n\t"
+ "Pending Kill: true;\tPending Kill Since: ") > -1);
assertNotNull(agentImpl.getPendingKillSince());
agentImpl.setBusy(false); // fake build finish
assertTrue("Pending kill should keep agent marked busy", agentImpl.isBusy());
agentAsString = agentImpl.asString();
assertTrue("Wrong value: " + agentAsString,
agentAsString.indexOf("Pending Restart: false;\tPending Restart Since: null\n\t"
+ "Pending Kill: true;\tPending Kill Since: ") > -1);
assertNotNull(agentImpl.getPendingKillSince());
}
public void testGetBuildingProject() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
assertNull(agentImpl.getProjectName());
final Map<String, String> distributedAgentProps = new HashMap<String, String>();
final String testProjectName = "testProjectName";
final Map<String, String> projectProperties = new HashMap<String, String>();
projectProperties.put(PropertiesHelper.PROJECT_NAME, testProjectName);
try {
// gets far enough to set Project name...
agentImpl.doBuild(null, projectProperties, distributedAgentProps, null, null);
fail("should fail w/ NPE");
} catch (NullPointerException e) {
assertEquals(null, e.getMessage());
}
assertEquals(testProjectName, agentImpl.getProjectName());
agentImpl.setBusy(false); // fake build finish
assertNull(agentImpl.getProjectName());
}
public void testKillNoWait() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
assertFalse(agentImpl.isBusy());
assertFalse(agentImpl.isPendingKill());
assertNull(agentImpl.getPendingKillSince());
assertFalse(agentImpl.isPendingRestart());
assertNull(agentImpl.getPendingRestartSince());
// make agent think it's building now
agentImpl.claim();
assertTrue(agentImpl.isBusy());
assertFalse(agentImpl.isPendingKill());
assertFalse(agentImpl.isPendingRestart());
agentImpl.kill(false);
assertTrue(agentImpl.isBusy());
assertTrue(agentImpl.isPendingKill());
assertNotNull(agentImpl.getPendingKillSince());
assertFalse(agentImpl.isPendingRestart());
assertNull(agentImpl.getPendingRestartSince());
// fake finish agent build - not really needed here
agentImpl.setBusy(false);
}
public void testRestartNoWait() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
assertFalse(agentImpl.isBusy());
assertFalse(agentImpl.isPendingKill());
assertNull(agentImpl.getPendingKillSince());
assertFalse(agentImpl.isPendingRestart());
assertNull(agentImpl.getPendingRestartSince());
// make agent think it's building now
agentImpl.claim();
assertTrue(agentImpl.isBusy());
assertFalse(agentImpl.isPendingKill());
assertFalse(agentImpl.isPendingRestart());
// fake finish agent build
try {
agentImpl.restart(false);
fail("Restart should fail outside of webstart");
} catch (RuntimeException e) {
assertEquals("Couldn't find webstart Basic Service. Is Agent running outside of webstart?",
e.getMessage());
}
assertTrue(agentImpl.isBusy());
assertFalse(agentImpl.isPendingKill());
assertNull(agentImpl.getPendingKillSince());
assertTrue(agentImpl.isPendingRestart());
assertNotNull(agentImpl.getPendingRestartSince());
}
public void testKillWithWait() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
assertFalse(agentImpl.isBusy());
assertFalse(agentImpl.isPendingKill());
assertNull(agentImpl.getPendingKillSince());
assertFalse(agentImpl.isPendingRestart());
assertNull(agentImpl.getPendingRestartSince());
// make agent think it's building now
agentImpl.claim();
assertTrue(agentImpl.isBusy());
assertFalse(agentImpl.isPendingKill());
assertFalse(agentImpl.isPendingRestart());
agentImpl.kill(true);
assertTrue(agentImpl.isBusy());
assertTrue(agentImpl.isPendingKill());
assertNotNull(agentImpl.getPendingKillSince());
assertFalse(agentImpl.isPendingRestart());
// fake finish agent build
agentImpl.setBusy(false);
}
public void testRestartWithWait() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
assertFalse(agentImpl.isBusy());
assertFalse(agentImpl.isPendingKill());
assertNull(agentImpl.getPendingKillSince());
assertFalse(agentImpl.isPendingRestart());
assertNull(agentImpl.getPendingRestartSince());
// make agent think it's building now
agentImpl.claim();
assertTrue(agentImpl.isBusy());
assertFalse(agentImpl.isPendingKill());
assertNull(agentImpl.getPendingRestartSince());
assertFalse(agentImpl.isPendingRestart());
assertNull(agentImpl.getPendingRestartSince());
assertNull(agentImpl.getLastDelayedAction());
agentImpl.restart(true);
assertTrue(agentImpl.isBusy());
assertFalse(agentImpl.isPendingKill());
assertTrue(agentImpl.isPendingRestart());
assertNotNull(agentImpl.getPendingRestartSince());
// use a fairly short delay in unit test
System.setProperty(BuildAgentServiceImpl.SYSPROP_CCDIST_DELAY_MS_KILLRESTART, KILL_DELAY + "");
// fake finish agent build
assertTrue(agentImpl.isBusy());
agentImpl.setBusy(false);
assertTrue("Agent should still be busy due to pending restart", agentImpl.isBusy());
assertNotNull(agentImpl.getLastDelayedAction());
assertEquals(BuildAgentServiceImpl.DelayedAction.Type.RESTART,
agentImpl.getLastDelayedAction().getType());
waitForDelayedAction(agentImpl);
assertEquals("Couldn't find webstart Basic Service. Is Agent running outside of webstart?",
agentImpl.getLastDelayedAction().getThrown().getMessage());
}
public void testRecursiveFilesExist() throws Exception {
final File testDir = new File(DIR_LOGS, "testDir");
testDir.deleteOnExit();
assertFalse("Non-existant base dir should show empty",
BuildAgentServiceImpl.recursiveFilesExist(testDir));
final File testDirSub = new File(testDir, "testSubDir");
testDirSub.deleteOnExit();
final File testDirSubSub = new File(testDirSub, "testSubSubDir");
testDirSubSub.deleteOnExit();
final File testFileSub = new File(testDirSub, "testFileSub");
testFileSub.deleteOnExit();
try {
Util.doMkDirs(testDir);
assertFalse("Existant, empty base dir should show empty",
BuildAgentServiceImpl.recursiveFilesExist(testDir));
Util.doMkDirs(testDirSub);
assertFalse("Existant, empty base dir, empty sub dir should show empty",
BuildAgentServiceImpl.recursiveFilesExist(testDir));
Util.doMkDirs(testDirSubSub);
assertFalse("Existant, empty base dir, empty sub, empty sub 2 should show empty",
BuildAgentServiceImpl.recursiveFilesExist(testDir));
testFileSub.createNewFile();
assertTrue("Existant, empty base dir, non-empty sub dir should show files exist",
BuildAgentServiceImpl.recursiveFilesExist(testDir));
} finally {
testDirSubSub.delete();
testFileSub.delete();
testDirSub.delete();
testDir.delete();
}
}
public void testRemoteResultOneExistsOneEmpty() throws Exception {
// test the case where one RemoteResult exists, but a second does not
final String resultType = PropertiesHelper.RESULT_TYPE_DIR;
final RemoteResult[] remoteResults = REMOTE_RESULTS_TWO_WITHEMPTYDIR;
final File resultTypeBaseDir = new File(resultType);
final File testDirResult = new File(resultTypeBaseDir,
(PropertiesHelper.RESULT_TYPE_LOGS.equals(resultType)
? "myTest" + resultType + "Dir"
: TEST_PROJECT_SUCCESS)); // use default dir since no property is set
testDirResult.deleteOnExit();
Util.doMkDirs(testDirResult);
REMOTE_RESULTS_TWO_WITHEMPTYDIR[1].getAgentDir().deleteOnExit();
Util.doMkDirs(REMOTE_RESULTS_TWO_WITHEMPTYDIR[1].getAgentDir());
final Map<String, String> distributedAgentProps = new HashMap<String, String>();
distributedAgentProps.put(PropertiesHelper.DISTRIBUTED_AGENT_LOGDIR,
testDirResult.getAbsolutePath());
final BuildAgentServiceImpl agentImpl = createTestAgentDoBuild(false, distributedAgentProps, remoteResults);
// clear out default result dirs
clearDefaultSuccessResultDirs();
// second RemoteResult dir is left empty by MockBuilder
try {
assertFalse(agentImpl.remoteResultExists(0));
assertFalse(agentImpl.remoteResultExists(1));
// make file in first RemoteResultDir
new File(resultTypeBaseDir, "testRemoteResult1").createNewFile();
assertTrue(agentImpl.remoteResultExists(0));
assertFalse(agentImpl.remoteResultExists(1));
assertTrue(agentImpl.retrieveRemoteResult(0).length > 0);
// second RemoteResult dir is left empty by MockBuilder
try {
agentImpl.retrieveRemoteResult(1);
fail("retrieveRemoteResult on missing result should fail");
} catch (RuntimeException e) {
assertEquals("Unable to get remote result file: "
+ REMOTE_RESULTS_TWO_WITHEMPTYDIR[1].getAgentDir().getAbsolutePath(),
e.getMessage());
}
} finally {
// cleanup left over files
agentImpl.clearOutputFiles();
testDirResult.delete();
deleteDirConfirm(REMOTE_RESULTS_TWO_WITHEMPTYDIR[1].getAgentDir());
}
}
public void testResultsExistAfterBuild() throws Exception {
checkResultsExistByType(PropertiesHelper.RESULT_TYPE_LOGS, REMOTE_RESULTS_EMPTY);
checkResultsExistByType(PropertiesHelper.RESULT_TYPE_OUTPUT, REMOTE_RESULTS_EMPTY);
checkResultsExistByType(PropertiesHelper.RESULT_TYPE_DIR, REMOTE_RESULTS_ONE);
}
private static void checkResultsExistByType(final String resultType, final RemoteResult[] remoteResults)
throws IOException {
final File resultTypeBaseDir = new File(resultType);
final File testDirResult = new File(resultTypeBaseDir,
(PropertiesHelper.RESULT_TYPE_LOGS.equals(resultType)
? "myTest" + resultType + "Dir"
: TEST_PROJECT_SUCCESS)); // use default dir since no property is set
testDirResult.deleteOnExit();
Util.doMkDirs(testDirResult);
final Map<String, String> distributedAgentProps = new HashMap<String, String>();
distributedAgentProps.put(PropertiesHelper.DISTRIBUTED_AGENT_LOGDIR,
testDirResult.getAbsolutePath());
final BuildAgentServiceImpl agentImpl = createTestAgentDoBuild(false, distributedAgentProps, remoteResults);
// clear out default result dirs
clearDefaultSuccessResultDirs();
final File testFileResult = new File(testDirResult, "myTestFile");
final File testDirResultSub = new File(testDirResult, "myTestResultSubDir");
testDirResultSub.deleteOnExit();
final File testDirResultSubSub = new File(testDirResultSub, "myTestSubSubDir");
testDirResultSubSub.deleteOnExit();
final File testFileSub = new File(testDirResultSub, "myTestFileSub");
// test behavior if log dir exists, and is empty except for sub dirs.
// use case: subdirs hold test report files, and a compile error occurs
// before unit tests are run, and hence no test log files have been created yet
// in sub dirs.
try {
assertResultsExistByType(null, false, resultType, agentImpl);
Util.doMkDirs(testDirResultSubSub);
assertResultsExistByType("Result dir with no files, only subdirs, should be considered empty", false,
resultType, agentImpl);
testFileSub.createNewFile();
assertResultsExistByType("Any file in tree should produce results", true,
resultType, agentImpl);
testFileResult.createNewFile();
testFileResult.deleteOnExit();
assertResultsExistByType("Multiple files in tree should produce results", true,
resultType, agentImpl);
} finally {
// cleanup left over files
agentImpl.clearOutputFiles();
testFileSub.delete();
testFileResult.delete();
testDirResultSubSub.delete();
testDirResultSub.delete();
testDirResult.delete();
}
}
private static void assertResultsExistByType(final String msg, final boolean expectedExists,
final String resultType, BuildAgentServiceImpl agentImpl)
throws RemoteException {
if (PropertiesHelper.RESULT_TYPE_DIR.equals(resultType)) {
assertEquals(msg, expectedExists, agentImpl.remoteResultExists(0));
} else {
assertEquals(msg, expectedExists, agentImpl.resultsExist(resultType));
}
}
private static void clearDefaultSuccessResultDirs() {
final File tmpLogSuccessDir = new File(DIR_LOGS, TEST_PROJECT_SUCCESS);
new File(tmpLogSuccessDir, "TEST-bogustestclassSuccess.xml").delete();
deleteDirConfirm(tmpLogSuccessDir);
final File tmpOutputSuccessDir = new File(DIR_OUTPUT, TEST_PROJECT_SUCCESS);
new File(tmpOutputSuccessDir, "testoutputSuccess").delete();
deleteDirConfirm(tmpOutputSuccessDir);
final File tmpResultSuccessDir = new File(DIR_FILE, TEST_PROJECT_SUCCESS);
new File(tmpResultSuccessDir, "remoteResult").delete();
deleteDirConfirm(tmpResultSuccessDir);
}
public void testRetrieveResultsWithAgentLogDir() throws Exception {
final File testLogDir = new File(DIR_LOGS, "myTestLogDir");
testLogDir.deleteOnExit();
Util.doMkDirs(testLogDir);
final Map<String, String> distributedAgentProps = new HashMap<String, String>();
distributedAgentProps.put(PropertiesHelper.DISTRIBUTED_AGENT_LOGDIR,
testLogDir.getAbsolutePath());
final BuildAgentServiceImpl agentImpl
= createTestAgentDoBuild(false, distributedAgentProps, REMOTE_RESULTS_EMPTY);
// delete result zips here to avoid left overs when prepareLogsAndArtifacts() is called below.
//assertTrue(agentImpl.getResultsZip(PropertiesHelper.RESULT_TYPE_LOGS).delete());
assertTrue(agentImpl.getResultsZip(PropertiesHelper.RESULT_TYPE_OUTPUT).delete());
// clear out default log dir
clearDefaultSuccessResultDirs();
final File testLog = new File(testLogDir, "myTestLog");
final File testLogSubDir = new File(testLogDir, "myTestLogSubDir");
testLogSubDir.deleteOnExit();
final File testLogSubSubDir = new File(testLogSubDir, "myTestLogSubSubDir");
testLogSubSubDir.deleteOnExit();
final File testLogSub = new File(testLogSubDir, "myTestLogSubSubFile");
try {
// test behavior if log dir exists, and is empty except for sub dirs.
// use case: subdirs hold test report files, and a compile error occurs
// before unit tests are run, and hence no test log files have been created yet
// in sub dirs.
assertFalse(agentImpl.resultsExist(PropertiesHelper.RESULT_TYPE_LOGS));
Util.doMkDirs(testLogSubSubDir);
assertFalse("Result dir with no files, only subdirs, should be considered empty",
agentImpl.resultsExist(PropertiesHelper.RESULT_TYPE_LOGS));
agentImpl.prepareLogsAndArtifacts();
try {
agentImpl.retrieveResultsAsZip(PropertiesHelper.RESULT_TYPE_LOGS);
fail("Attempt to retrieve empty zip file should fail");
} catch (RuntimeException e) {
assertTrue(e.getMessage().startsWith("Unable to get file "
+ new File(testLogDir.getParentFile().getCanonicalPath())
.getParentFile().getCanonicalPath()));
}
testLogSub.createNewFile();
assertTrue("Any file in tree should produce results",
agentImpl.resultsExist(PropertiesHelper.RESULT_TYPE_LOGS));
// make sure agent looks in myTestLog dir for files
testLog.createNewFile();
testLog.deleteOnExit();
assertTrue(agentImpl.resultsExist(PropertiesHelper.RESULT_TYPE_LOGS));
assertFalse("Default output files should have been deleted during unit test",
agentImpl.resultsExist(PropertiesHelper.RESULT_TYPE_OUTPUT));
assertTrue("Agent should be busy until build results are retrived and cleared.",
agentImpl.isBusy());
} finally {
// cleanup left over files
agentImpl.clearOutputFiles();
testLog.delete();
testLogDir.delete();
}
assertFalse(agentImpl.isBusy());
}
public void testRetrieveResultsAsZipBuildSuccess() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentDoBuild(false,
new HashMap<String, String>(), REMOTE_RESULTS_ONE);
try {
assertTrue(agentImpl.resultsExist(PropertiesHelper.RESULT_TYPE_LOGS));
assertTrue(agentImpl.resultsExist(PropertiesHelper.RESULT_TYPE_OUTPUT));
assertTrue(agentImpl.remoteResultExists(0));
assertTrue("Agent should be busy until build results are retrived and cleared.",
agentImpl.isBusy());
// make sure we can actually get the results
assertNonEmptyResultsZip(agentImpl, PropertiesHelper.RESULT_TYPE_LOGS);
assertNonEmptyResultsZip(agentImpl, PropertiesHelper.RESULT_TYPE_OUTPUT);
assertNonEmptyResultsZip(agentImpl, PropertiesHelper.RESULT_TYPE_DIR);
} finally {
// cleanup left over files
agentImpl.clearOutputFiles();
}
assertFalse(agentImpl.isBusy());
}
private static void assertNonEmptyResultsZip(final BuildAgentServiceImpl agentImpl, final String resultType)
throws RemoteException {
final byte[] resultZip;
if (PropertiesHelper.RESULT_TYPE_DIR.equals(resultType)) {
resultZip = agentImpl.retrieveRemoteResult(0);
} else {
resultZip = agentImpl.retrieveResultsAsZip(resultType);
}
assertNotNull("Null " + resultType + " zip bytes.", resultZip);
assertTrue("Empty " + resultType + " zip bytes.", resultZip.length > 0);
}
public void testRetrieveResultsAsZipBuildFail() throws Exception {
final BuildAgentServiceImpl agentImpl = createTestAgentDoBuild(true,
new HashMap<String, String>(), REMOTE_RESULTS_ONE);
try {
assertTrue(agentImpl.resultsExist(PropertiesHelper.RESULT_TYPE_LOGS));
assertNonEmptyResultsZip(agentImpl, PropertiesHelper.RESULT_TYPE_LOGS);
assertFalse(agentImpl.resultsExist(PropertiesHelper.RESULT_TYPE_OUTPUT));
try {
agentImpl.retrieveResultsAsZip(PropertiesHelper.RESULT_TYPE_OUTPUT);
fail("should've failed retrieval of non-existant results.");
} catch (RuntimeException e) {
assertTrue(e.getMessage().startsWith("Unable to get file "));
}
assertFalse(agentImpl.remoteResultExists(0));
try {
agentImpl.retrieveRemoteResult(0);
fail("should've failed retrieval of non-existant results.");
} catch (RuntimeException e) {
assertTrue("Wrong message: " + e.getMessage(),
e.getMessage().startsWith("Unable to get remote result file: "));
}
assertTrue("Agent should be busy until build results are retrived and cleared.",
agentImpl.isBusy());
} finally {
// cleanup left over files
agentImpl.clearOutputFiles();
}
assertFalse(agentImpl.isBusy());
}
public void testClearOutputFilesBuildValidateError() throws Exception {
final Map<String, String> projectProperties = new HashMap<String, String>();
projectProperties.put(PropertiesHelper.PROJECT_NAME, TEST_PROJECT_FAIL);
final HashMap<String, String> distributedAgentProps = new HashMap<String, String>();
final BuildAgentServiceImpl agentImpl = createTestAgent(distributedAgentProps);
try {
callTestDoBuild(projectProperties, agentImpl, distributedAgentProps, REMOTE_RESULTS_ONE, true);
} catch (RemoteException e) {
assertTrue("Wrong root cause of exception", e.getCause() instanceof CruiseControlException);
CruiseControlException ce = (CruiseControlException) e.getCause();
assertEquals("Wrong excecption msg", MSG_BUILDER_VALIDATE_FAIL, ce.getMessage());
}
try {
// NOTE: prepareLogsAndArtifacts() sets log/output member vars, but is not called if
// builder.validate() fails. May want to change this?
//agentImpl.prepareLogsAndArtifacts();
//assertFalse(agentImpl.resultsExist(PropertiesHelper.RESULT_TYPE_LOGS));
//assertFalse(agentImpl.resultsExist(PropertiesHelper.RESULT_TYPE_OUTPUT));
assertFalse("Agent should NOT be busy after builder.validate() error.",
agentImpl.isBusy());
} finally {
// cleanup left over files
agentImpl.clearOutputFiles();
}
assertFalse(agentImpl.isBusy());
}
private static BuildAgentServiceImpl createTestAgentDoBuild(final boolean isBuildFailure,
final Map<String, String> distributedAgentProps,
final RemoteResult[] remoteResults)
throws RemoteException {
final BuildAgentServiceImpl agentImpl = createTestAgent(distributedAgentProps);
final Map<String, String> projectProperties = new HashMap<String, String>();
if (isBuildFailure) {
projectProperties.put(PropertiesHelper.PROJECT_NAME, TEST_PROJECT_FAIL);
} else {
projectProperties.put(PropertiesHelper.PROJECT_NAME, TEST_PROJECT_SUCCESS);
}
callTestDoBuild(projectProperties, agentImpl, distributedAgentProps, remoteResults, false);
return agentImpl;
}
private static BuildAgentServiceImpl createTestAgent(final Map<String, String> distributedAgentProps) {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
if (!distributedAgentProps.containsKey(PropertiesHelper.DISTRIBUTED_OVERRIDE_TARGET)) {
distributedAgentProps.put(PropertiesHelper.DISTRIBUTED_OVERRIDE_TARGET, null);
}
return agentImpl;
}
/**
* Call doBuild() on a build that should succeed on the given agent using test settings
* @param agent the build agent on which to run the build
* @return the build result jdom element
* @throws RemoteException if something else dies
*/
public static Element callTestDoBuildSuccess(final BuildAgentService agent)
throws RemoteException {
final Map<String, String> projectProperties = new HashMap<String, String>();
projectProperties.put(PropertiesHelper.PROJECT_NAME, TEST_PROJECT_SUCCESS);
final Map<String, String> distributedAgentProps = new HashMap<String, String>();
// handle re-use case of DMB when overrideTarget is null
distributedAgentProps.put(PropertiesHelper.DISTRIBUTED_OVERRIDE_TARGET, null);
return callTestDoBuild(projectProperties, agent, distributedAgentProps, REMOTE_RESULTS_ONE, false);
}
/**
* Call doBuild() on the given agent using test settings
* @param projectProperties map passed to build() method, contains "projectname" property
* @param agent the build agent on which to run the build
* @param distributedAgentProps the map of distributed props used by the build agent
* @param remoteResults the array of (possible) build results to be returned from agent
* @param isValidateFailure if true, force the builder.validate() call to fail.
* @return the build result jdom element
* @throws RemoteException if something else dies
*/
private static Element callTestDoBuild(final Map<String, String> projectProperties,
final BuildAgentService agent,
final Map<String, String> distributedAgentProps,
final RemoteResult[] remoteResults,
final boolean isValidateFailure)
throws RemoteException {
final MockBuilder mockBuilder = createMockBuilder(isValidateFailure, remoteResults);
return agent.doBuild(mockBuilder, projectProperties, distributedAgentProps, null, remoteResults);
}
private static final String MSG_BUILDER_VALIDATE_FAIL = "Unit Test forced builder.validate() failure.";
private static class CCDistMockChildBuilder extends MockBuilder {
final boolean isValidateFailure;
final RemoteResult[] remoteResults;
public CCDistMockChildBuilder(final boolean isValidateFailure, final RemoteResult[] remoteResults) {
super("testCCDistMockChildBuilder");
this.isValidateFailure = isValidateFailure;
this.remoteResults = remoteResults;
}
public void validate() throws CruiseControlException {
super.validate();
if (isValidateFailure) {
throw new CruiseControlException(MSG_BUILDER_VALIDATE_FAIL);
}
}
public Element build(final Map<String, String> properties, final Progress progress) {
final String projectName = properties.get(PropertiesHelper.PROJECT_NAME);
final boolean isBuildFailure = TEST_PROJECT_FAIL.equals(projectName);
final Element retVal = super.build(properties, progress);
// create a file in the expected dirs
createExpectedBuildArtifact(new File("logs/" + projectName + "/TEST-bogustestclassSuccess.xml"));
if (!isBuildFailure) {
createExpectedBuildArtifact(new File("output/" + projectName + "/testoutputSuccess"));
for (RemoteResult remoteResult : remoteResults) {
// skip artifact creation for empty RemoteResult test
if (!REMOTE_RESULTS_TWO_WITHEMPTYDIR[1].equals(remoteResult)) {
createExpectedBuildArtifact(
new File(remoteResult.getAgentDir(), projectName + "/remoteResult"));
}
}
}
return retVal;
}
}
private static MockBuilder createMockBuilder(final boolean isValidateFailure, final RemoteResult[] remoteResults) {
return new CCDistMockChildBuilder(isValidateFailure, remoteResults);
}
private static void createExpectedBuildArtifact(File buildProducedFile) {
if (!buildProducedFile.getParentFile().exists()) {
Util.doMkDirs(buildProducedFile.getParentFile());
}
try {
if (!buildProducedFile.exists()) {
assertTrue(buildProducedFile.createNewFile());
}
} catch (IOException e) {
fail("couldn't create expected output build produced file: " + buildProducedFile.getAbsolutePath());
}
buildProducedFile.deleteOnExit();
buildProducedFile.getParentFile().deleteOnExit();
buildProducedFile.getParentFile().getParentFile().deleteOnExit();
}
public void testClaim() {
final BuildAgentServiceImpl agentImpl = createAndClaimNewBuildAgent();
final Date firstClaimDate = agentImpl.getDateClaimed();
try {
agentImpl.claim();
fail("Should throw exception if attempt is made to claim a busy agent");
} catch (IllegalStateException e) {
assertTrue("Unexpected error message w/ invalid call to claim(): " + e.getMessage(),
e.getMessage().startsWith("Cannot claim agent on "));
assertTrue("Unexpected error message w/ invalid call to claim(): " + e.getMessage(),
e.getMessage().indexOf("that is busy building project: null") > 0);
}
assertEquals(firstClaimDate, agentImpl.getDateClaimed());
releaseClaimedAgent(agentImpl, firstClaimDate);
}
// NOTE: Can't do test w/ RestartPending, since Restart requires Agent running in Webstart.
public void testClaimWhileKillPending() throws Exception {
final BuildAgentServiceImpl agentImpl = createAndClaimNewBuildAgent();
final Date firstClaimDate = agentImpl.getDateClaimed();
// use a fairly short delay in unit test
System.setProperty(BuildAgentServiceImpl.SYSPROP_CCDIST_DELAY_MS_KILLRESTART, KILL_DELAY + "");
// set a pending Restart
agentImpl.kill(true);
assertFalse("Kill should not have executed.", agentImpl.isDoKillExecuted());
// call doBuild() on agent
callDoBuildWithNulls(agentImpl, firstClaimDate);
assertEquals("Agent kill should be delayed " + KILL_DELAY
+ " milliseconds, and agent should be busy during delay.",
true, agentImpl.isBusy());
assertFalse("Kill should not have executed.", agentImpl.isDoKillExecuted());
// wait for doKill to execute
waitForDelayedAction(agentImpl);
assertTrue("Kill should have executed.", agentImpl.isDoKillExecuted());
// Agent will still show as "claimed" in unit tests (since it can't system.exit).
// That is the correct behavior since is prevents agent from being acquired while
// an action is pending.
assertEquals("Agent should be busy after doKill executes in unit test.",
true, agentImpl.isBusy());
assertEquals(firstClaimDate, agentImpl.getDateClaimed());
}
private static void waitForDelayedAction(BuildAgentServiceImpl agentImpl) throws InterruptedException {
// wait for Restart() outside of webstart exception
final long begin = System.currentTimeMillis();
final BuildAgentServiceImpl.DelayedAction delayedAction = agentImpl.getLastDelayedAction();
assertNotNull("Last DelayedAction should not be null", delayedAction);
final BuildAgentServiceImpl.DelayedAction.FinishedListener finishedListener
= new BuildAgentServiceImpl.DelayedAction.FinishedListener() {
public void finished(final BuildAgentServiceImpl.DelayedAction delayedAction) {
synchronized (delayedAction) {
delayedAction.notifyAll();
}
}
};
delayedAction.setFinishedListener(finishedListener);
try {
synchronized (delayedAction) {
int count = 0;
while (!delayedAction.isFinished() && count < 6) {
delayedAction.wait(10 * 1000);
count++;
}
}
} finally {
delayedAction.setFinishedListener(null);
}
final float elapsedSecs = (System.currentTimeMillis() - begin) / 1000f;
assertTrue("Delayed action didn't finish before timeout. elapsed: \n"
+ elapsedSecs + " sec", delayedAction.isFinished());
LOG.info(DistributedMasterBuilderTest.MSG_PREFIX_STATS + "Unit test Agent Delayed Action took: "
+ elapsedSecs + " sec");
}
// @todo should agent expose a release() method to clear busy flag? Very dangerous if agent is building...
private static void releaseClaimedAgent(BuildAgentServiceImpl agentImpl, Date firstClaimDate)
{
callDoBuildWithNulls(agentImpl, firstClaimDate);
assertFalse("Expected agent busy flag to be false after cleanup call.", agentImpl.isBusy());
assertNull(agentImpl.getDateClaimed());
}
private static void callDoBuildWithNulls(BuildAgentServiceImpl agentImpl, Date firstClaimDate)
{
try {
agentImpl.doBuild(null, null, null, null, null);
fail("Should have failed to build");
} catch (NullPointerException e) {
assertEquals("Unexpected build error: " + e.getMessage(), null, e.getMessage());
} catch (RemoteException e) {
assertTrue("Unexpected build error: " + e.getMessage(),
e.getMessage().startsWith("Failed to complete build on agent; nested exception is: \n"
+ "\tnet.sourceforge.cruisecontrol.CruiseControlException: ant logfile "));
}
assertEquals(firstClaimDate, agentImpl.getDateClaimed());
// we now avoid NPE error during cleanup since logDir and outputDir can be null
agentImpl.clearOutputFiles();
}
private static BuildAgentServiceImpl createAndClaimNewBuildAgent() {
final BuildAgentServiceImpl agentImpl = createTestAgentImpl();
agentImpl.setAgentPropertiesFilename(TEST_AGENT_PROPERTIES_FILE);
assertFalse(agentImpl.isBusy());
assertNull(agentImpl.getDateClaimed());
agentImpl.claim();
assertTrue(agentImpl.isBusy());
assertNotNull(agentImpl.getDateClaimed());
return agentImpl;
}
}