/********************************************************************************
* CruiseControl, a Continuous Integration Toolkit
* Copyright (c) 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.builders;
import junit.framework.TestCase;
import net.sourceforge.cruisecontrol.Builder;
import net.sourceforge.cruisecontrol.BuilderTest;
import net.sourceforge.cruisecontrol.CruiseControlException;
import net.sourceforge.cruisecontrol.testutil.TestUtil;
import net.sourceforge.cruisecontrol.util.Util;
import org.jdom.Element;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
public class Maven2BuilderTest extends TestCase {
private static final String MOCK_SUCCESS = "successful build";
private static final String MOCK_BUILD_FAILURE = "failed build";
private static final String MOCK_BUILD_ERROR = "download failure";
private static final String MOCK_FATAL_ERROR = "fatal error";
private static final String MOCK_SCRIPT_TIMEOUT = "build timeout";
private static final String MSG_PREFIX_HOME_SCRIPT_BOTH_SET = "'mvnhome' and 'mvnscript' cannot both be set.";
private File createTestMvnScriptFile() throws IOException, CruiseControlException {
File testScript = File.createTempFile("Maven2BuilderTest.testValidate", "_testmaven.bat");
testScript.deleteOnExit();
MavenBuilderTest.makeTestFile(testScript, "@echo This is a fake maven.bat\n", true, filesToDelete);
return testScript;
}
private File createTestMvnProjectFile() throws IOException, CruiseControlException {
File testProject = File.createTempFile("Maven2BuilderTest.testValidate", "_testproject.xml");
testProject.deleteOnExit();
MavenBuilderTest.makeTestFile(testProject,
"<project><!-- This is a fake Maven project file --></project>\n", true, filesToDelete);
return testProject;
}
private Maven2Builder createValidM2Builder() throws IOException, CruiseControlException {
Maven2Builder mb = new Maven2Builder();
// these files must also exist for Maven2Builder to be happy.
final File testScript = createTestMvnScriptFile();
filesToDelete.add(testScript);
final File testProject = createTestMvnProjectFile();
filesToDelete.add(testProject);
mb.setMultiple(1);
mb.setMvnHome(testScript.getParentFile().getAbsolutePath());
mb.setPomFile(testProject.getAbsolutePath());
mb.setGoal("mygoal");
return mb;
}
private final TestUtil.FilesToDelete filesToDelete = new TestUtil.FilesToDelete();
protected void setUp() throws Exception {
}
protected void tearDown() {
filesToDelete.delete();
}
// @todo Remove when deprecated methods get/setShowBuildOutput are removed.
@SuppressWarnings("deprecation")
public void testSetShowBuildOutputLinkedToSetLiveOutput() {
final Maven2Builder builder = new Maven2Builder();
assertTrue(builder.isLiveOutput());
assertTrue(builder.getShowBuildOutput());
builder.setShowBuildOutput(false);
assertFalse(builder.isLiveOutput());
assertFalse(builder.getShowBuildOutput());
builder.setLiveOutput(true);
assertTrue(builder.isLiveOutput());
assertTrue(builder.getShowBuildOutput());
}
public void testFindMaven2Script() throws Exception {
final Maven2Builder mb = new Maven2Builder();
try {
mb.findMaven2Script(false);
fail("expected exception");
} catch (CruiseControlException e) {
assertEquals("mvnhome attribute not set.", e.getMessage());
}
try {
mb.findMaven2Script(true);
fail("expected exception");
} catch (CruiseControlException e) {
assertEquals("mvnhome attribute not set.", e.getMessage());
}
final String testMvnHome = "isWindowsTestScript";
mb.setMvnHome(testMvnHome);
assertEquals(testMvnHome + File.separator + Maven2Builder.MVN_BIN_DIR + "mvn.bat",
mb.findMaven2Script(true));
assertEquals(testMvnHome + File.separator + Maven2Builder.MVN_BIN_DIR + "mvn",
mb.findMaven2Script(false));
}
public void testValidateMvnHomeAndMvnScriptSet() throws Exception {
final Maven2Builder mb = createValidM2Builder();
// make invalid by setting both Home and Script
final File testScript = createTestMvnScriptFile();
mb.setMvnHome(testScript.getParentFile().getAbsolutePath());
mb.setMvnScript(testScript.getAbsolutePath());
try {
mb.validate();
fail();
} catch (CruiseControlException e) {
assertTrue(e.getMessage().startsWith(MSG_PREFIX_HOME_SCRIPT_BOTH_SET));
}
mb.setMvnScript(null);
mb.validate();
// rerun validate to test for reuse issues
try {
mb.validate();
fail("Second call to validate() should have failed.");
} catch (CruiseControlException e) {
assertTrue(e.getMessage().startsWith(MSG_PREFIX_HOME_SCRIPT_BOTH_SET));
}
}
/**
* Test validation with MvnHome set and resuse issues.
* @throws Exception if anything breaks
*/
public void testValidateMvnHomeReuse() throws Exception {
final Maven2Builder mb = createValidM2Builder();
mb.validate();
// rerun validate to test for reuse issues
try {
mb.validate();
} catch (CruiseControlException e) {
assertTrue(e.getMessage().startsWith(MSG_PREFIX_HOME_SCRIPT_BOTH_SET));
}
}
public void testValidate() throws Exception {
Maven2Builder mb = new Maven2Builder();
try {
mb.validate();
fail("Maven2Builder should throw exceptions when required fields are not set.");
} catch (CruiseControlException e) {
assertEquals("'mvnhome' or 'mvnscript' must be set.", e.getMessage());
}
// these files must also exist for Maven2Builder to be happy.
final File testScript = createTestMvnScriptFile();
final File testProject = createTestMvnProjectFile();
mb.setMultiple(1);
mb.setMvnScript(testScript.getAbsolutePath());
try {
mb.validate();
fail("Maven2Builder should throw exceptions when required fields are not set.");
} catch (CruiseControlException e) {
assertEquals("'pomfile' is required for Maven2Builder", e.getMessage());
}
mb.setPomFile(testProject.getAbsolutePath());
try {
mb.validate();
fail("Maven2Builder should throw exceptions when required fields are not set.");
} catch (CruiseControlException e) {
assertEquals("'goal' is required for Maven2Builder", e.getMessage());
}
mb.setGoal("");
try {
mb.validate();
fail("Maven2Builder should throw exceptions when required fields are not set.");
} catch (CruiseControlException e) {
assertEquals("'goal' is required for Maven2Builder", e.getMessage());
}
mb.setGoal("mygoal");
mb.validate();
// do validate again, just to check reuse issues
mb.validate();
}
public void testValidatePomFile() throws Exception {
Maven2Builder mb = new Maven2Builder();
final File testProject = createTestMvnProjectFile();
mb.validatePomFile(testProject);
try {
mb.validatePomFile(testProject.getParentFile());
fail("directories are not valid pom files");
} catch (CruiseControlException e) {
assertTrue(e.getMessage().startsWith("the pom file can't be a directory"));
}
assertTrue(testProject.delete());
assertFalse(testProject.exists());
try {
mb.validatePomFile(testProject);
fail("pom files must exist");
} catch (CruiseControlException e) {
assertTrue(e.getMessage().startsWith("the pom file could not be found"));
}
}
public void testBuild_Success() throws IOException, CruiseControlException {
Maven2Builder mb = new Maven2Builder();
internalTestBuild(MOCK_SUCCESS, mb);
}
public void testBuild_BuildFailure() throws IOException, CruiseControlException {
Maven2Builder mb = new Maven2Builder();
internalTestBuild(MOCK_BUILD_FAILURE, mb);
}
public void testBuild_Error() throws IOException, CruiseControlException {
Maven2Builder mb = new Maven2Builder();
internalTestBuild(MOCK_BUILD_ERROR, mb);
}
public void testBuild_FatalError() throws IOException, CruiseControlException {
Maven2Builder mb = new Maven2Builder();
internalTestBuild(MOCK_FATAL_ERROR, mb);
}
/**
* Element build(Map). mockFailure == (Mock a failure?).
*
* @param statusType The exit status to be tested
* @param mb a Maven2Builder instance
* @throws IOException if something breaks
* @throws CruiseControlException if something breaks
*/
private void internalTestBuild(final String statusType, final Maven2Builder mb)
throws IOException, CruiseControlException {
setupMockBuild(statusType, mb);
// some fake goal is still needed to start working (no '|' here!)
mb.setGoal("fakegoal");
// this should "succeed"
Element logElement = mb.build(getUnitTestBuildProperties(), null);
assertNotNull(statusType, logElement);
List goalTags = logElement.getChildren("mavengoal");
assertNotNull(statusType, goalTags);
assertEquals(statusType, 2, goalTags.size());
Element we = (Element) goalTags.get(0);
assertEquals(statusType, "java:compile", we.getAttribute("name").getValue());
we = (Element) goalTags.get(1);
assertEquals(statusType, "test:test", we.getAttribute("name").getValue());
assertBuildState(statusType, logElement);
// this time let's test multiple runs
mb.setGoal("fakegoal|otherfakegoal");
// this should "double succeed"
final Map<String, String> buildProperties = BuilderTest.createPropsWithProjectName(getName());
logElement = mb.build(buildProperties, null);
assertNotNull(statusType, logElement);
goalTags = logElement.getChildren("mavengoal");
assertNotNull(statusType, goalTags);
we = (Element) goalTags.get(0);
assertEquals(statusType, "java:compile", we.getAttribute("name").getValue());
we = (Element) goalTags.get(1);
assertEquals(statusType, "test:test", we.getAttribute("name").getValue());
assertBuildState(statusType, logElement);
if (!isExpectedSuccess(statusType)) {
// if we mocked a failure, the second run should never happen
assertEquals(statusType, 2, goalTags.size());
} else {
assertEquals(statusType, 4, goalTags.size());
we = (Element) goalTags.get(2);
assertEquals(statusType, "java:compile", we.getAttribute("name").getValue());
we = (Element) goalTags.get(3);
assertEquals(statusType, "test:test", we.getAttribute("name").getValue());
}
}
private void setupMockBuild(String statusType, Maven2Builder mb) throws IOException, CruiseControlException {
final String statusText = getStatusText(statusType);
// Prepare mock files.
final String tempFilePrefix = "Maven2BuilderTest.internalTestBuild";
final File testScript;
if (Util.isWindows()) {
testScript = File.createTempFile(tempFilePrefix, "_testmaven.bat");
testScript.deleteOnExit();
MavenBuilderTest.makeTestFile(
testScript,
"@rem This is a fake maven.bat\n"
+ "@echo [INFO] [java:compile]\n"
+ "@echo [INFO] Bla-bla-compile\n"
+ "@echo [INFO] [test:test]\n"
+ "@echo "
+ statusText
+ "\n",
true, filesToDelete);
} else {
testScript = File.createTempFile(tempFilePrefix, "_testmaven.sh");
testScript.deleteOnExit();
MavenBuilderTest.makeTestFile(
testScript,
"#!/bin/sh\n"
+ "\n"
+ "# This is a fake maven.sh\n"
+ "echo [INFO] [java:compile]\n"
+ "echo [INFO] Bla-bla-compile\n"
+ "echo [INFO] [test:test]\n"
+ "echo "
+ statusText
+ "\n",
false, filesToDelete);
}
mb.setMvnScript(testScript.getAbsolutePath());
// pom must exist before call to build()
final File testPom = File.createTempFile(tempFilePrefix, "don-t-care-pom.xml", new File("."));
filesToDelete.add(testPom);
testPom.deleteOnExit();
mb.setPomFile(testPom.getName());
}
private boolean isExpectedSuccess(final String statusType) {
return statusType.equals(MOCK_SUCCESS);
}
private void assertBuildState(final String statusType, final Element logElement) {
if (!isExpectedSuccess(statusType)) {
assertNotNull("error attribute not found when " + statusType, logElement.getAttribute("error"));
assertNull(statusType, logElement.getAttribute("success"));
} else {
assertNull(statusType, logElement.getAttribute("error"));
assertNotNull(statusType, logElement.getAttribute("success"));
}
assertNotNull(statusType + " missing 'time' attribute", logElement.getAttribute("time"));
}
private static Map<String, String> getUnitTestBuildProperties() {
final Map<String, String> buildProperties = new HashMap<String, String>();
buildProperties.put("cclastbuildtimestamp", "20070614095818");
buildProperties.put("cclastgoodbuildtimestamp", "20070607114812");
buildProperties.put("label", "build.11");
buildProperties.put("lastbuildsuccessful", "false");
buildProperties.put("cvstimestamp", "2007-06-14 07:59:22 GMT"); // should have spaces
buildProperties.put(Builder.BUILD_PROP_PROJECTNAME, "java_32.A01");
buildProperties.put("cctimestamp", "20070614095922");
return buildProperties;
}
/**
* List getGoalSets()
*/
public void testGetGoalSets() {
Maven2Builder mb = new Maven2Builder();
List gsList;
mb.setGoal(null);
gsList = mb.getGoalSets();
assertNotNull(gsList);
assertEquals("No goal produces non-empty list", 0, gsList.size());
mb.setGoal("clean "); // I want the space there..
gsList = mb.getGoalSets();
assertNotNull(gsList);
assertEquals("One goal should produce one item", 1, gsList.size());
// but notice, no space below
assertEquals("One goal produces bad list content", "clean", (String) gsList.get(0));
mb.setGoal(" clean|update "); // Notice the spaces here
gsList = mb.getGoalSets();
assertNotNull(gsList);
assertEquals("Two goals should produce two items", 2, gsList.size());
// but not here
assertEquals("First run produces bad goal", "clean", (String) gsList.get(0));
assertEquals("Second run produces bad goal", "update", (String) gsList.get(1));
// full-featured test
mb.setGoal("clean update|\ttest||"); // Notice the spaces here
gsList = mb.getGoalSets();
assertNotNull(gsList);
assertEquals("Complex goal should produce two goalsets", 2, gsList.size());
// but not here
assertEquals("First cplx run produces bad goal", "clean update", (String) gsList.get(0));
assertEquals("Second cplx run produces bad goal", "test", (String) gsList.get(1));
}
private static final int TIMEOUT_SLEEP = 2;
public void testBuildTimeout() throws Exception {
if (Util.isWindows()) {
System.out.println("Skipping testBuildTimeout(), no DOS 'sleep' command");
return;
}
Maven2Builder mb = new Maven2Builder();
mb.setTimeout(TIMEOUT_SLEEP);
//Element logElement = internalTestBuild(MOCK_SCRIPT_TIMEOUT, mb);
setupMockBuild(MOCK_SCRIPT_TIMEOUT, mb);
mb.setGoal("fakegoal");
final long startTime = System.currentTimeMillis();
// this should "succeed"
final Element logElement = mb.build(getUnitTestBuildProperties(), null);
final long endTime = System.currentTimeMillis();
assertNotNull("Build should have timed out.", logElement.getAttributeValue("error"));
assertTrue(logElement.getAttributeValue("error").indexOf("timeout") >= 0);
assertTrue((endTime - startTime) < 9 * 1000L);
}
/**
* Text for build status
*
* @param statusCode The exit status to be tested
* @return the expected script output text for the given build status
*/
private String getStatusText(String statusCode) {
if (statusCode.equals(MOCK_SUCCESS)) {
return "[INFO] BUILD SUCCESSFUL";
} else if (statusCode.equals(MOCK_BUILD_FAILURE)) {
return "[ERROR] BUILD FAILURE";
} else if (statusCode.equals(MOCK_BUILD_ERROR)) {
return "[ERROR] BUILD ERROR";
} else if (statusCode.equals(MOCK_FATAL_ERROR)) {
return "[ERROR] FATAL ERROR";
} else if (statusCode.equals(MOCK_SCRIPT_TIMEOUT)) {
int sleepTime = TIMEOUT_SLEEP * 2;
return "[INFO] BUILD SUCCESSFUL"
+ "\n" + (Util.isWindows() ? "@" : "") + "echo sleeping " + sleepTime + "..."
+ "\nsleep " + sleepTime;
}
throw new IllegalArgumentException("please use one of the constants");
}
}