/******************************************************************************** * CruiseControl, a Continuous Integration Toolkit * Copyright (c) 2001-2003, ThoughtWorks, Inc. * 200 E. Randolph, 25th Floor * Chicago, IL 60601 USA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * + Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * + Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************/ package net.sourceforge.cruisecontrol; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; //import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.Map; import java.util.TimeZone; //import java.util.zip.ZipEntry; //import java.util.zip.ZipInputStream; import net.sourceforge.cruisecontrol.builders.MockBuilder; import net.sourceforge.cruisecontrol.buildloggers.MergeLogger; import net.sourceforge.cruisecontrol.events.BuildProgressEvent; import net.sourceforge.cruisecontrol.events.BuildProgressListener; import net.sourceforge.cruisecontrol.events.BuildResultEvent; import net.sourceforge.cruisecontrol.events.BuildResultListener; import net.sourceforge.cruisecontrol.labelincrementers.DefaultLabelIncrementer; import net.sourceforge.cruisecontrol.testutil.TestUtil; import net.sourceforge.cruisecontrol.testutil.TestUtil.FilesToDelete; import net.sourceforge.cruisecontrol.util.DateUtil; import net.sourceforge.cruisecontrol.util.IO; import net.sourceforge.cruisecontrol.util.Util; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.jdom.Element; //import org.jdom.input.SAXBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; public class ProjectTest { private static final Logger LOG = Logger.getLogger(ProjectTest.class); private static final String TEST_DIR = "tmp"; private Project project; private ProjectConfig projectConfig; private final FilesToDelete filesToDelete = new FilesToDelete(); @Before public void setUp() throws CruiseControlException { project = new Project(); project.setName("TestProject"); projectConfig = new ProjectConfig(); projectConfig.add(new DefaultLabelIncrementer()); project.setProjectConfig(projectConfig); // required for runners where log4j isn't initialized Logger.getLogger(Project.class).setLevel(Level.INFO); filesToDelete.add(new File(TEST_DIR)); } @After public void tearDown() { project.stop(); project = null; projectConfig = null; // minimize logging to the console during test runs Logger.getLogger(Project.class).setLevel(Level.ALL); LOG.getLoggerRepository().setThreshold(Level.ALL); filesToDelete.delete(); } @Test public void testNotifyListeners() { MockListener listener = new MockListener(); ProjectConfig.Listeners listeners = new ProjectConfig.Listeners(); listeners.add(listener); projectConfig.add(listeners); // this for init to work only. project.init(); ProjectEvent event = new ProjectEvent("foo") { }; project.notifyListeners(event); assertTrue(listener.wasNotified()); } @Test public void testBuild() throws CruiseControlException, IOException { final Date now = new Date(); final MockModificationSet modSet = new MockModificationSet(); modSet.setTimeOfCheck(now); final MockSchedule sched = new MockSchedule(); projectConfig.add(sched); final Log log = new Log(); final File logDir = new File(TEST_DIR + File.separator + "test-results"); if (!logDir.exists()) { assertTrue(Util.doMkDirs(logDir)); } filesToDelete.add(logDir); final String myProjectName = "myproject"; log.setProjectName(myProjectName); filesToDelete.add(new File(TestUtil.getTargetDir(), myProjectName + ".ser")); log.setDir(logDir.getAbsolutePath()); log.setEncoding("ISO-8859-1"); log.validate(); projectConfig.add(log); MergeLogger logger = new MergeLogger(); logger.setFile(TEST_DIR + File.separator + "_auxLog1.xml"); logger.validate(); log.add(logger); logger = new MergeLogger(); logger.setDir(TEST_DIR + File.separator + "_auxLogs"); logger.validate(); log.add(logger); projectConfig.add(modSet); project.setProjectConfig(projectConfig); project.setLabel("1.2.2"); project.setName("myproject"); project.setWasLastBuildSuccessful(false); project.setLastBuild(formatTime(now)); project.setLastSuccessfulBuild(formatTime(now)); writeFile(TEST_DIR + File.separator + "_auxLog1.xml", "<one/>"); final File auxLogsDirectory = new File(TEST_DIR + File.separator + "_auxLogs"); if (!auxLogsDirectory.exists()) { assertTrue(Util.doMkDirs(auxLogsDirectory)); } filesToDelete.add(auxLogsDirectory); writeFile(TEST_DIR + File.separator + "_auxLogs/_auxLog2.xml", "<testsuite><properties><property/></properties><testcase/></testsuite>"); writeFile(TEST_DIR + File.separator + "_auxLogs/_auxLog3.xml", "<testsuite/>"); final List<BuildResultEvent> resultEvents = new ArrayList<BuildResultEvent>(); project.addBuildResultListener(new BuildResultListener() { public void handleBuildResult(BuildResultEvent event) { resultEvents.add(event); } }); final List<BuildProgressEvent> progressEvents = new ArrayList<BuildProgressEvent>(); project.addBuildProgressListener(new BuildProgressListener() { public void handleBuildProgress(BuildProgressEvent event) { progressEvents.add(event); } }); projectConfig.add(new DefaultLabelIncrementer()); project.init(); project.start(); project.build(); project.stop(); final File expectedLogFile = new File(logDir, "log" + DateUtil.getFormattedTime(now) + "L1.2.2.xml"); assertTrue(expectedLogFile.isFile()); filesToDelete.add(expectedLogFile); assertTrue(project.isLastBuildSuccessful()); final String expected = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><cruisecontrol><modifications />" + "<info>" + "<property name=\"projectname\" value=\"myproject\" />" + "<property name=\"lastbuild\" value=\"" + DateUtil.getFormattedTime(now) + "\" />" + "<property name=\"lastsuccessfulbuild\" value=\"" + project.getLastSuccessfulBuild() + "\" />" + "<property name=\"builddate\" value=\"" + DateUtil.formatIso8601(now) + "\" />" + "<property name=\"cctimestamp\" value=\"" + DateUtil.getFormattedTime(now) + "\" />" + "<property name=\"label\" value=\"1.2.2\" />" + "<property name=\"interval\" value=\"300\" />" + "<property name=\"lastbuildsuccessful\" value=\"false\" />" + "<property name=\"logdir\" value=\"" + logDir.getAbsolutePath() + "\" />" + "<property name=\"logfile\" value=\"" + "log" + DateUtil.getFormattedTime(now) + "L1.2.2.xml\" />" + "</info><build /><one /><testsuite><testcase /></testsuite><testsuite /></cruisecontrol>"; assertEquals(expected, Util.readFileToString(expectedLogFile)); assertEquals("Didn't increment the label", "1.2.3", project.getLabel().intern()); //look for sourcecontrol properties final Map<String, String> props = sched.getBuildProperties(); assertNotNull("Build properties were null.", props); assertEquals("Build property count.", 10, props.size()); assertTrue("projectname not found.", props.containsKey("projectname")); assertEquals("wrong projectname.", "myproject", props.get("projectname")); assertTrue("filemodified not found.", props.containsKey("filemodified")); assertTrue("fileremoved not found.", props.containsKey("fileremoved")); assertEquals(project.getLastSuccessfulBuild(), props.get("cclastgoodbuildtimestamp")); assertEquals(project.getLastBuild(), props.get("cclastbuildtimestamp")); assertTrue("cvstimestamp not passed.", props.containsKey("cvstimestamp")); // check that the proper events were fired assertEquals("Should be exactly one build result event", 1, resultEvents.size()); final BuildResultEvent resultEvent = resultEvents.get(0); assertTrue("Should be successful build result event", resultEvent.isBuildSuccessful()); assertTrue("Should have at least one of each project state except queued", progressEvents.size() >= 8); } @Test public void testBuildShouldThrowExceptionWhenNoConfig() throws CruiseControlException { project = new Project(); try { project.build(); fail(); } catch (IllegalStateException expected) { assertEquals("projectConfig must be set on project before calling build()", expected.getMessage()); } project.setProjectConfig(projectConfig); project.build(); } @Test public void testBuildRequiresSchedule() throws CruiseControlException { MockProject mockProject = new MockProject() { private static final long serialVersionUID = 7421208807053841468L; public void run() { loop(); } void checkWait() throws InterruptedException { waitIfPaused(); } }; mockProject.setName("MockProject"); mockProject.setProjectConfig(projectConfig); mockProject.start(); mockProject.init(); try { mockProject.build(); fail(); } catch (IllegalStateException expected) { assertEquals("project must have a schedule", expected.getMessage()); } finally { mockProject.stopLooping(); } } /* * With forceonly true, the build should not be called even with a modification */ @Test public void testBuild_forceOnly() throws CruiseControlException { MockProject mockProject = new MockProject() { private static final long serialVersionUID = 6420860433590771835L; public void run() { loop(); } void setBuildStartTime(Date date) { throw new RuntimeException("Should not run"); } }; MockModificationSet modSet = new MockModificationSet(); mockProject.setName("MockProject"); mockProject.setProjectConfig(projectConfig); projectConfig.add(modSet); projectConfig.setForceOnly(true); modSet.setModified(true); mockProject.start(); mockProject.init(); try { mockProject.build(); } finally { mockProject.stopLooping(); } } @Test public void testBuildWithMinimumConfig() throws CruiseControlException { Schedule schedule = new Schedule(); schedule.add(new MockBuilder()); projectConfig.add(schedule); MockLog mockLog = new MockLog(); mockLog.setProjectName(project.getName()); projectConfig.add(mockLog); project.start(); project.setBuildForced(true); project.init(); project.build(); } /* * This test simulates what happens when there are multiple build threads * and the config.xml gets reloaded while a project is building. This was * causing NPEs but has now been fixed. */ @Test public void testBuildWithNewProjectConfigDuringBuild() throws CruiseControlException { projectConfig = new ProjectConfig() { private static final long serialVersionUID = -3851689566246777173L; Project readProject(String projectName) { return project; } }; final String testProjectForNewConfigDuringBuild = "TestProjectForGettingNewProjectConfigDuringBuild"; projectConfig.setName(testProjectForNewConfigDuringBuild); filesToDelete.add(new File(TestUtil.getTargetDir(), testProjectForNewConfigDuringBuild + ".ser")); projectConfig.add(new DefaultLabelIncrementer()); projectConfig.configureProject(); Schedule schedule = new Schedule(); schedule.add(new MockBuilderChangesProjectConfig(projectConfig)); projectConfig.add(schedule); MockLog mockLog = new MockLog(); mockLog.setProjectName(project.getName()); projectConfig.add(mockLog); project.start(); project.setBuildForced(true); project.init(); project.build(); } @Test public void testBadLabel() { try { project.validateLabel("build_0", projectConfig.getLabelIncrementer()); fail("Expected exception due to bad label"); } catch (CruiseControlException expected) { } } @Test public void testPublish() throws CruiseControlException { MockSchedule sched = new MockSchedule(); projectConfig.add(sched); MockPublisher publisher = new MockPublisher(); Publisher exceptionThrower = new MockPublisher() { private static final long serialVersionUID = -8476214888230413824L; public void publish(Element log) throws CruiseControlException { throw new CruiseControlException("exception"); } }; ProjectConfig.Publishers publishers = new ProjectConfig.Publishers(); publishers.add(publisher); publishers.add(exceptionThrower); publishers.add(publisher); projectConfig.add(publishers); project.setName("projectName"); project.setLabel("label.1"); projectConfig.add(new DefaultLabelIncrementer()); projectConfig.add(new Log()); project.init(); project.publish(projectConfig.getLog()); assertEquals(2, publisher.getPublishCount()); } @Test public void testSetLastBuild() throws CruiseControlException { String lastBuild = "20000101120000"; project.setLastBuild(lastBuild); assertEquals(lastBuild, project.getLastBuild()); } @Test public void testNullLastBuild() throws CruiseControlException { try { project.setLastBuild(null); fail("Expected an IllegalArgumentException for a null last build"); } catch (IllegalArgumentException e) { } } @Test public void testBadLastBuild() { try { project.setLastBuild("af32455432"); fail("Expected a CruiseControlException for a bad last build"); } catch (CruiseControlException e) { } } @Test public void testGetFormattedTime() { assertNull(DateUtil.getFormattedTime(null)); } @Test public void testGetModifications() { MockModificationSet modSet = new MockModificationSet(); Element modifications = modSet.retrieveModificationsAsElement(null, null); projectConfig.add(modSet); project.init(); modSet.setModified(true); assertEquals(modifications, project.getModifications(false)); assertEquals(modifications, project.getModifications(true)); modSet.setModified(false); assertEquals(null, project.getModifications(false)); assertEquals(modifications, project.getModifications(true)); // TODO: need tests for when lastBuildSuccessful = false } @Test public void testGetModifications_NoModificationElementRequired() { assertNull(project.getModifications(false)); project.setBuildForced(true); assertNotNull(project.getModifications(true)); } @Test public void testGetModifications_NoModificationElementAndRequireModificationsFalse() { assertNull(project.getModifications(false)); projectConfig.setRequiremodification(false); project.init(); assertNotNull(project.getModifications(false)); } @Test public void testGetModifications_requireModificationsTrue() { MockModificationSet modSet = new MockModificationSet(); projectConfig.add(modSet); projectConfig.setRequiremodification(true); project.init(); modSet.setModified(false); assertNull(project.getModifications(false)); } @Test public void testGetModifications_requireModificationsFalse() { MockModificationSet modSet = new MockModificationSet(); projectConfig.add(modSet); projectConfig.setRequiremodification(false); project.init(); modSet.setModified(false); assertNotNull(project.getModifications(false)); } @Test public void testCheckOnlySinceLastBuild() throws CruiseControlException { project.setLastBuild("20030218010101"); project.setLastSuccessfulBuild("20030218010101"); assertEquals(false, project.checkOnlySinceLastBuild()); project.setLastBuild("20030218020202"); assertEquals(false, project.checkOnlySinceLastBuild()); project.setBuildAfterFailed(false); assertEquals(true, project.checkOnlySinceLastBuild()); project.setLastBuild("20030218010102"); assertEquals(false, project.checkOnlySinceLastBuild()); project.setLastBuild("20020101010101"); assertEquals(false, project.checkOnlySinceLastBuild()); } @Test public void testWaitIfPaused() throws InterruptedException, CruiseControlException { MockProject mockProject = new MockProject() { private static final long serialVersionUID = 4815988988116502160L; public void run() { loop(); } void checkWait() throws InterruptedException { waitIfPaused(); } }; projectConfig.add(new MockSchedule()); mockProject.setProjectConfig(projectConfig); mockProject.init(); new Thread(mockProject).start(); int firstLoopCount = mockProject.getLoopCount(); Thread.sleep(100); int secondLoopCount = mockProject.getLoopCount(); assertTrue("loop counts should have been different when not paused", firstLoopCount != secondLoopCount); mockProject.setPaused(true); Thread.sleep(100); firstLoopCount = mockProject.getLoopCount(); Thread.sleep(100); secondLoopCount = mockProject.getLoopCount(); assertEquals("loop counts should be same when paused", firstLoopCount, secondLoopCount); mockProject.setPaused(false); Thread.sleep(100); int lastLoopCount = mockProject.getLoopCount(); assertTrue("loop count increased after pause ended", lastLoopCount > secondLoopCount); mockProject.stopLooping(); } @Test public void testWaitForNextBuild() throws InterruptedException, CruiseControlException { projectConfig.add(new MockSchedule()); MockProject mockProject = new MockProject() { private static final long serialVersionUID = 4262693344992901684L; public void run() { loop(); } void checkWait() throws InterruptedException { waitForNextBuild(); } }; mockProject.overrideBuildInterval(1000); mockProject.setProjectConfig(projectConfig); mockProject.init(); new Thread(mockProject).start(); Thread.sleep(100); assertEquals(1, mockProject.getLoopCount()); Thread.sleep(100); assertEquals(1, mockProject.getLoopCount()); mockProject.forceBuild(); Thread.sleep(100); assertEquals(2, mockProject.getLoopCount()); mockProject.stopLooping(); } @Test public void testWaitForBuildToFinish() throws InterruptedException { MockProject mockProject = new MockProject() { private static final long serialVersionUID = -8172748698812254496L; public void run() { loop(); } void checkWait() throws InterruptedException { waitForBuildToFinish(); } }; new Thread(mockProject).start(); Thread.sleep(100); assertEquals(1, mockProject.getLoopCount()); Thread.sleep(100); assertEquals(1, mockProject.getLoopCount()); mockProject.buildFinished(); Thread.sleep(100); assertEquals(2, mockProject.getLoopCount()); mockProject.stopLooping(); } @Test public void testNeedToWait() { assertTrue(Project.needToWaitForNextBuild(1)); assertFalse(Project.needToWaitForNextBuild(0)); assertFalse(Project.needToWaitForNextBuild(-1)); } @Test public void testToString() { project.setName("foo"); assertEquals("Project foo: stopped", project.toString()); project.setPaused(true); assertEquals("Project foo: stopped (paused)", project.toString()); } @Test public void testInitShouldThrowExceptionWhenConfigNotSet() throws CruiseControlException { project = new Project(); try { project.init(); fail(); } catch (IllegalStateException expected) { assertEquals("projectConfig must be set on project before calling init()", expected.getMessage()); } project.setProjectConfig(projectConfig); project.init(); } @Test public void testSerialization() throws IOException { final File expectedSerFile = new File(project.getName() + ".ser"); if (expectedSerFile.exists()) { assertTrue(expectedSerFile.delete()); } filesToDelete.add(expectedSerFile); project.serializeProject(); assertTrue(expectedSerFile.exists()); } @Test public void testSerializationWhenProjectNameHasSlash() throws IOException { project.setName("testProjectName/trunk"); final File expectedSerFile = new File(Builder.getFileSystemSafeProjectName(project.getName()) + ".ser"); if (expectedSerFile.exists()) { assertTrue(expectedSerFile.delete()); } filesToDelete.add(expectedSerFile); project.serializeProject(); assertTrue(expectedSerFile.exists()); } @Test public void testDeserializationWhenProjectNameHasSlash() throws IOException { project.setName("testProjectName/trunk"); final File expectedSerFile = new File(Builder.getFileSystemSafeProjectName(project.getName()) + ".ser"); if (expectedSerFile.exists()) { assertTrue(expectedSerFile.delete()); } filesToDelete.add(expectedSerFile); project.serializeProject(); assertTrue(expectedSerFile.exists()); final ProjectConfig config = new ProjectConfig(); final Project deSerProj = config.readProject(project.getName()); assertFalse("Should find file: " + expectedSerFile.getAbsolutePath() + ", making build forced false.", deSerProj.isBuildForced()); } @Test public void testDeserialization() throws Exception { File f = new File("test.ser"); filesToDelete.add(f); FileOutputStream outFile = new FileOutputStream(f); ObjectOutputStream objects = new ObjectOutputStream(outFile); try { objects.writeObject(new Project()); objects.flush(); } finally { objects.close(); } FileInputStream inFile = new FileInputStream(f); ObjectInputStream inObjects = new ObjectInputStream(inFile); Object p; try { p = inObjects.readObject(); } finally { inObjects.close(); } assertNotNull("Read object must not be null", p); assertTrue("Object must be instanceof Project class", p instanceof Project); Project deserializedProject = (Project) p; deserializedProject.addBuildProgressListener(new BuildProgressListener() { public void handleBuildProgress(BuildProgressEvent event) { } }); } @Test public void testStartAfterDeserialization() throws Exception { TestProject beforeSerialization = new TestProject(); projectConfig.add(new MockSchedule()); beforeSerialization.setProjectConfig(projectConfig); beforeSerialization.init(); beforeSerialization.start(); File f = new File("test.ser"); filesToDelete.add(f); FileOutputStream outFile = new FileOutputStream(f); ObjectOutputStream objects = new ObjectOutputStream(outFile); try { objects.writeObject(beforeSerialization); objects.flush(); } finally { objects.close(); } FileInputStream inFile = new FileInputStream(f); ObjectInputStream inObjects = new ObjectInputStream(inFile); Object p; try { p = inObjects.readObject(); } finally { inObjects.close(); } TestProject deserializedProject = (TestProject) p; deserializedProject.resetCreateNewSchedulingThreadCalled(); deserializedProject.setProjectConfig(projectConfig); deserializedProject.start(); assertTrue("failed to create schedule thread", deserializedProject.wasCreateNewSchedulingThreadCalled()); } @Test public void testProgressDefault() throws Exception { TestProject testProject = new TestProject(); assertNotNull(testProject.getProgress()); assertNotNull(testProject.getProgress().getValue()); } @Test public void testBuildForcedAfterSuccessfulBuild() throws Exception { projectConfig.add(new MockSchedule()); final Log log = new Log(); final File logDir = new File(TEST_DIR + File.separator + "test-results"); if (!logDir.exists()) { assertTrue(Util.doMkDirs(logDir)); } filesToDelete.add(logDir); log.setProjectName("myproject"); log.setDir(logDir.getAbsolutePath()); log.setEncoding("ISO-8859-1"); log.validate(); projectConfig.add(log); project.setBuildQueue(new BuildQueue()); final File serializedProjectFile = new File(project.getName() + ".ser"); filesToDelete.add(serializedProjectFile); if (serializedProjectFile.exists()) { assertTrue(serializedProjectFile.delete()); } assertFalse(serializedProjectFile.exists()); assertFalse(project.isBuildForced()); project.setBuildForced(true); project.start(); project.execute(); // performs a build, which performs project serialization final ObjectInputStream inObjects = new ObjectInputStream(new FileInputStream( serializedProjectFile)); final Object p; try { p = inObjects.readObject(); } finally { inObjects.close(); } assertNotNull("Read object must not be null", p); assertTrue("Object must be instanceof Project class", p instanceof Project); final Project deserializedProject = (Project) p; assertFalse("buildForced should be false after deserialization", deserializedProject.isBuildForced()); } @Test public void testGetProjectPropertiesMap() throws CruiseControlException { final String label = "labeL.1"; project.setLabel(label); final String lastBuild = "20000101120000"; project.setLastBuild(lastBuild); final String lastGoodBuild = "19990101120000"; project.setLastSuccessfulBuild(lastGoodBuild); project.setWasLastBuildSuccessful(true); final TimeZone cest = TimeZone.getTimeZone("Europe/Copenhagen"); final Calendar now = new GregorianCalendar(cest); now.set(2005, Calendar.AUGUST, 10, 13, 7, 43); final String cvstimestamp = "2005-08-10 11:07:43 GMT"; projectConfig.add(new MockSchedule()); project.init(); // The returned time is dependent on the default timezone hence // the use of DateUtil.getFormattedTime() final String cctimestamp = DateUtil.getFormattedTime(now.getTime()); final Map<String, String> map = project.getProjectPropertiesMap(now.getTime()); assertEquals(project.getName(), map.get("projectname")); assertEquals(label, map.get("label")); assertEquals(cctimestamp, map.get("cctimestamp")); assertEquals(lastGoodBuild, map.get("cclastgoodbuildtimestamp")); assertEquals(lastBuild, map.get("cclastbuildtimestamp")); assertEquals("true", map.get("lastbuildsuccessful")); assertEquals(cvstimestamp, map.get("cvstimestamp")); assertEquals(String.valueOf(project.getBuildForced()), map.get("buildforced")); assertEquals(8, map.keySet().size()); } @Test public void testGetTimeToNextBuild_AfterShortBuild() { Schedule schedule = new Schedule(); MockBuilder noonBuilder = new MockBuilder(); noonBuilder.setTime("1200"); noonBuilder.setBuildLogXML(new Element("builder1")); schedule.add(noonBuilder); projectConfig.add(schedule); Calendar cal = Calendar.getInstance(); cal.set(2001, Calendar.NOVEMBER, 22); cal.set(Calendar.HOUR_OF_DAY, 12); cal.set(Calendar.MINUTE, 0); Date noonBuild = cal.getTime(); project.setBuildStartTime(noonBuild); project.init(); cal.set(Calendar.SECOND, 30); Date postNoonBuild = cal.getTime(); long time = project.getTimeToNextBuild(postNoonBuild); assertEquals(Schedule.ONE_DAY, time); } /* @todo Look into fixing test below, possibly using smaller 'units'. @Test public void testGetLabels() throws Exception { final Date now = new Date(); final MockModificationSet modSet = new MockModificationSet(); modSet.setTimeOfCheck(now); projectConfig.add(modSet); final MockSchedule sched = new MockSchedule(); projectConfig.add(sched); final Log log = new Log(); final File logDir = new File(TEST_DIR + File.separator + "test-results"); logDir.mkdir(); filesToDelete.add(logDir); final String projectName = "testGetLabels"; log.setProjectName(projectName); filesToDelete.add(new File(TestUtil.getTargetDir(), projectName + ".ser")); log.setDir(logDir.getAbsolutePath()); log.setEncoding("ISO-8859-1"); log.validate(); projectConfig.add(log); project.init(); project.start(); project.build(); project.stop(); List<String> labels = project.getLogLabels(); assertEquals("The logs should be zero", 1, labels.size()); } @Test public void testGetLog() throws Exception { final Date now = new Date(); final MockModificationSet modSet = new MockModificationSet(); modSet.setTimeOfCheck(now); projectConfig.add(modSet); final MockSchedule sched = new MockSchedule(); projectConfig.add(sched); final Log log = new Log(); final File logDir = new File(TEST_DIR + File.separator + "test-results"); logDir.mkdir(); filesToDelete.add(logDir); final String projectName = "testGetLabels"; log.setProjectName(projectName); filesToDelete.add(new File(TestUtil.getTargetDir(), projectName + ".ser")); log.setDir(logDir.getAbsolutePath()); log.setEncoding("ISO-8859-1"); log.validate(); projectConfig.add(log); project.init(); project.start(); project.build(); project.stop(); List<String> labels = project.getLogLabels(); assertEquals("The logs should be zero", 1, labels.size()); ByteArrayInputStream bais = null; ZipInputStream zis = null; try { String logLabel = labels.get(0); byte[] logContents = project.getLog(logLabel); bais = new ByteArrayInputStream(logContents); zis = new ZipInputStream(bais); ZipEntry zipEntry = zis.getNextEntry(); SAXBuilder builder = new SAXBuilder("org.apache.xerces.parsers.SAXParser"); Element el = builder.build(zis).getRootElement(); assertEquals("The root element of the log should be 'cruisecontrol'", "cruisecontrol", el.getName()); } finally { IO.close(bais); IO.close(zis); } } */ private void writeFile(String fileName, String contents) throws CruiseControlException { File f = new File(fileName); filesToDelete.add(f); IO.write(f, contents); } private static String formatTime(Date time) { return new SimpleDateFormat("yyyyMMddHHmmss").format(time); } private static class MockPublisher implements Publisher { private static final long serialVersionUID = 318946828157075347L; private int publishCount = 0; public void validate() { } public void publish(Element log) throws CruiseControlException { publishCount++; } int getPublishCount() { return publishCount; } } private static class MockListener implements Listener { private static final long serialVersionUID = 7151687645856649093L; private boolean notified = false; public boolean wasNotified() { return notified; } public void handleEvent(ProjectEvent event) throws CruiseControlException { notified = true; } public void validate() throws CruiseControlException { } } private static class TestProject extends Project { private static final long serialVersionUID = 8959072893967115075L; private boolean createNewSchedulingThreadCalled = false; protected void createNewSchedulingThread() { createNewSchedulingThreadCalled = true; } boolean wasCreateNewSchedulingThreadCalled() { return createNewSchedulingThreadCalled; } void resetCreateNewSchedulingThreadCalled() { createNewSchedulingThreadCalled = false; } } private static class MockLog extends Log { private static final long serialVersionUID = 8435380397735943132L; protected void callManipulators() { } protected void writeLogFile(File file, Element element) throws CruiseControlException { } } public static class MockBuilderChangesProjectConfig extends MockBuilder { private static final long serialVersionUID = 8740082489736387798L; private final ProjectConfig oldProjectConfig; public MockBuilderChangesProjectConfig(ProjectConfig projectConfig) { oldProjectConfig = projectConfig; } /* * This is to simulate what happens when the config file changes during a build. */ public Element build(final Map<String, String> properties, final Progress progress) { final ProjectConfig newProjectConfig = new ProjectConfig(); newProjectConfig.add(new DefaultLabelIncrementer()); final Schedule schedule = new Schedule(); schedule.add(new MockBuilder()); newProjectConfig.add(schedule); newProjectConfig.add(new MockLog()); try { newProjectConfig.getStateFromOldProject(oldProjectConfig); } catch (CruiseControlException e) { throw new RuntimeException(e); } return super.build(properties, progress); } } /** * Unit test helper method to allow tests access to package visible setter, w/out exposing setter in production API. * @param testProject the unit test Project to be altered * @param buildSuccessful the new value of the build successful flag */ public static void setWasLastBuildSuccessful(final Project testProject, final boolean buildSuccessful) { testProject.setWasLastBuildSuccessful(buildSuccessful); } }