/*******************************************************************************
* CruiseControl, a Continuous Integration Toolkit Copyright (c) 2001,
* 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 java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import junit.framework.TestCase;
import net.sourceforge.cruisecontrol.CruiseControlException;
import net.sourceforge.cruisecontrol.testutil.TestUtil.FilesToDelete;
import net.sourceforge.cruisecontrol.testutil.TestUtil;
import net.sourceforge.cruisecontrol.util.Commandline;
import net.sourceforge.cruisecontrol.util.IO;
import net.sourceforge.cruisecontrol.util.MockCommandline;
import net.sourceforge.cruisecontrol.util.MockProcess;
import net.sourceforge.cruisecontrol.util.Util;
import org.jdom.Attribute;
import org.jdom.CDATA;
import org.jdom.DataConversionException;
import org.jdom.Element;
public class NantBuilderTest extends TestCase {
private final FilesToDelete filesToDelete = new FilesToDelete();
private NantBuilder builder;
static class InputBasedMockCommandLineBuilder {
Commandline buildCommandline(final InputStream inputStream) {
final MockCommandline mockCommandline = getMockCommandline();
mockCommandline.setAssertCorrectCommandline(false);
mockCommandline.setProcessErrorStream(new PipedInputStream());
mockCommandline.setProcessInputStream(inputStream);
mockCommandline.setProcessOutputStream(new PipedOutputStream());
return mockCommandline;
}
MockCommandline getMockCommandline() {
return new MockCommandline();
}
}
// process that times out...
static class TimeoutProcess extends MockProcess {
private final long timeoutMillis;
TimeoutProcess(long timeoutMillis) {
this.timeoutMillis = timeoutMillis;
}
public synchronized void destroy() {
notifyAll();
}
public int waitFor() throws InterruptedException {
synchronized (this) {
try {
this.wait(timeoutMillis);
} catch (InterruptedException e) {
}
}
return super.waitFor();
}
}
protected void setUp() throws Exception {
builder = new NantBuilder();
builder.setTarget("target");
builder.setBuildFile("buildfile");
// Must be a cleaner way to do this...
// builder.setNantWorkingDir(new File(
// new URI(ClassLoader.getSystemResource("test.build").toString())).getParent());
final File tempDir = new File(System.getProperty("java.io.tmpdir"));
final File rootTempDir = new File(tempDir, "testRoot");
rootTempDir.mkdir();
filesToDelete.add(rootTempDir);
}
public void tearDown() {
filesToDelete.delete();
builder = null;
}
public void testValidate() {
builder = new NantBuilder();
try {
builder.validate();
} catch (CruiseControlException e) {
fail("nantbuilder is missing required attributes");
}
builder.setTime("0100");
builder.setBuildFile("buildfile");
builder.setTarget("target");
try {
builder.validate();
} catch (CruiseControlException e) {
fail("validate should not throw exceptions when options are set.");
}
builder.setSaveLogDir("I/hope/this/dir/does/not/exist/");
try {
builder.validate();
fail("validate should throw exceptions when saveLogDir doesn't exist");
} catch (CruiseControlException e) {
}
builder.setSaveLogDir(null);
builder.setMultiple(2);
try {
builder.validate();
fail("validate should throw exceptions when multiple and time are both set.");
} catch (CruiseControlException e) {
}
}
public void testTranslateNantErrorElementsWithBuildResultsErrorAttribute()
throws CruiseControlException, DataConversionException {
Element buildLogElement = new Element("buildresults");
Attribute errorAttribute = new Attribute("error", "true");
buildLogElement.setAttribute(errorAttribute);
buildLogElement = builder.translateNantErrorElements(buildLogElement);
assertEquals("build", buildLogElement.getName());
assertTrue(buildLogElement.getAttribute("error").getBooleanValue());
}
public void testTranslateNantErrorElementsWithFailureElements()
throws CruiseControlException {
Element buildLogElement = new Element("buildresults");
Element failureElement = new Element("failure");
buildLogElement.addContent(failureElement);
try {
buildLogElement = builder.translateNantErrorElements(buildLogElement);
fail("Expected a CruiseControlException for invalid nant log output format");
} catch (CruiseControlException e) { /** expected **/ }
Element buildErrorElement = new Element("builderror");
failureElement.addContent(buildErrorElement);
try {
buildLogElement = builder.translateNantErrorElements(buildLogElement);
fail("Expected a CruiseControlException for invalid nant log output format");
} catch (CruiseControlException e) { /** expected **/ }
Element messageElement = new Element("message");
buildErrorElement.addContent(messageElement);
try {
buildLogElement = builder.translateNantErrorElements(buildLogElement);
fail("Expected a CruiseControlException for invalid nant log output format");
} catch (CruiseControlException e) { /** expected **/ }
messageElement.setContent(new CDATA("test failure"));
buildLogElement = builder.translateNantErrorElements(buildLogElement);
assertEquals("build", buildLogElement.getName());
Attribute errorAttribute = buildLogElement.getAttribute("error");
assertNotNull(errorAttribute);
assertEquals(Attribute.UNDECLARED_TYPE, errorAttribute.getAttributeType());
assertEquals("test failure", errorAttribute.getValue());
}
public void testBuild() throws Exception {
final String logName = "nantbuilder-build1.txt";
final InputStream emptyInputStream = new ByteArrayInputStream("".getBytes());
final NantBuilder mybuilder = new NantBuilder() {
protected NantScript getNantScript() {
return new NantScript() {
public Commandline getCommandLine() {
return new InputBasedMockCommandLineBuilder().buildCommandline(emptyInputStream);
}
};
}
protected Element getNantLogAsElement(File file) throws CruiseControlException {
assertEquals("notLog.xml", file.getPath());
final URL resource = getClass().getResource(logName);
assertNotNull("missing test case resource: " + logName, resource);
String path = resource.getPath();
try {
path = URLDecoder.decode(path, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
File simulatedFile;
try {
//done this way to handle spaces in the full pathname
simulatedFile = new File(new URI(resource.toString()));
} catch (URISyntaxException e) {
throw new CruiseControlException(e);
}
return super.getNantLogAsElement(simulatedFile);
}
};
mybuilder.setTarget("target");
mybuilder.setBuildFile("buildfile");
mybuilder.setBuildFile("test.build");
mybuilder.setTempFile("notLog.xml");
mybuilder.setTarget("init");
final Map<String, String> buildProperties = new HashMap<String, String>();
Element buildElement = mybuilder.build(buildProperties, null);
int initCount = getInitCount(buildElement);
assertEquals(1, initCount);
// TODO: Don't know if this is a valid test with NAnt's file format. Need to verify or convert to Ant format.
// builder.setTarget("init init");
// buildElement = builder.build(buildProperties);
// initCount = getInitCount(buildElement);
// assertEquals(2, initCount);
}
public void testGetCommandLineArgs_DebugAndQuiet() {
builder.setUseDebug(true);
builder.setUseQuiet(true);
try {
builder.validate();
fail("validate() should throw CruiseControlException when both useDebug and useQuiet are true");
} catch (CruiseControlException expected) {
}
}
public int getInitCount(Element buildElement) {
int initFoundCount = 0;
Iterator targetIterator = buildElement.getChildren("target").iterator();
String name;
while (targetIterator.hasNext()) {
name = ((Element) targetIterator.next()).getAttributeValue("name");
if (name.equals("init")) {
initFoundCount++;
}
}
return initFoundCount;
}
public void testBuildTimeout() throws Exception {
final int buildSleepSecs = 2;
final MockProcess timeoutProcess = new TimeoutProcess(buildSleepSecs * 1000);
final MockCommandline timeoutCommandline = new MockCommandline() {
public MockProcess getMockProcess() {
return timeoutProcess;
}
};
final InputStream emptyInputStream = new ByteArrayInputStream("".getBytes());
timeoutCommandline.setAssertCorrectCommandline(false);
timeoutCommandline.setProcessErrorStream(emptyInputStream);
timeoutCommandline.setProcessInputStream(emptyInputStream);
timeoutCommandline.setProcessOutputStream(System.out);
final NantBuilder mybuilder = new NantBuilder() {
protected NantScript getNantScript() {
return new NantScript() {
public Commandline getCommandLine() {
return timeoutCommandline;
}
};
}
protected Element getNantLogAsElement(File file) throws CruiseControlException {
fail("We should time out... we have nothing to read anyway");
return super.getNantLogAsElement(file); // please compiler
}
};
mybuilder.setBuildFile("test.build");
mybuilder.setTarget("timeout-test-target");
final int testTimeoutSecs = buildSleepSecs / 2;
mybuilder.setTimeout(testTimeoutSecs);
mybuilder.setUseDebug(true);
mybuilder.setUseLogger(true);
final Map<String, String> buildProperties = new HashMap<String, String>();
long startTime = System.currentTimeMillis();
Element buildElement = mybuilder.build(buildProperties, null);
final long elapsedMillis = System.currentTimeMillis() - startTime;
assertTrue(buildElement.getAttributeValue("error").indexOf("timeout") >= 0);
assertTrue("Too much time has elapsed (" + elapsedMillis + " millis) for NantBuilder timeout of "
+ testTimeoutSecs + " secs.",
elapsedMillis < ((testTimeoutSecs + 5) * 1000L));
// test we don't fail when there is no NAnt log file
mybuilder.setTimeout(1);
mybuilder.setUseDebug(false);
mybuilder.setUseLogger(false);
mybuilder.setTempFile("shouldNot.xml");
buildElement = mybuilder.build(buildProperties, null);
assertTrue(buildElement.getAttributeValue("error").indexOf("timeout") >= 0);
}
public void testSaveNantLog() throws IOException {
final String originalDirName = "target";
filesToDelete.add(new File(TestUtil.getTargetDir(), originalDirName));
final String logName = "log.xml";
final String saveDirName = "target/reports/nant";
builder.setSaveLogDir(saveDirName);
builder.setTempFile(logName);
final File originalDir = new File(originalDirName);
final File originalLog = new File(originalDir, logName);
Util.doMkDirs(originalDir);
originalLog.createNewFile();
final File saveDir = new File(saveDirName);
final File savedLog = new File(saveDir, logName);
Util.doMkDirs(saveDir);
savedLog.delete();
builder.saveNantLog(originalLog);
assertTrue(savedLog.exists());
savedLog.delete();
builder.setSaveLogDir("");
builder.saveNantLog(originalLog);
assertFalse(savedLog.exists());
builder.setSaveLogDir(null);
builder.saveNantLog(originalLog);
assertFalse(savedLog.exists());
}
public void testGetNantLogAsElement() throws CruiseControlException {
Element buildLogElement = new Element("build");
File logFile = new File("_tempNantLog.xml");
filesToDelete.add(logFile);
IO.write(logFile,
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<?xml-stylesheet "
+ "type=\"text/xsl\" href=\"log.xsl\"?>\n<build></build>");
assertEquals(buildLogElement.toString(), builder.getNantLogAsElement(logFile).toString());
}
public void testGetNantLogAsElement_NoLogFile() {
File doesNotExist = new File("blah blah blah does not exist");
try {
builder.getNantLogAsElement(doesNotExist);
fail();
} catch (CruiseControlException expected) {
assertEquals("NAnt logfile " + doesNotExist.getAbsolutePath() + " does not exist.", expected.getMessage());
}
}
}