/********************************************************************************
* 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 java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.StringReader;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import junit.framework.TestCase;
import net.sourceforge.cruisecontrol.logmanipulators.DeleteManipulator;
import net.sourceforge.cruisecontrol.logmanipulators.GZIPManipulator;
import net.sourceforge.cruisecontrol.testutil.TestUtil;
import net.sourceforge.cruisecontrol.testutil.TestUtil.FilesToDelete;
import net.sourceforge.cruisecontrol.util.DateUtil;
import org.jdom.CDATA;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
public class LogTest extends TestCase {
private final FilesToDelete filesToDelete = new FilesToDelete();
private static final String LOG_DIR = "LogTest";
protected void setUp() {
filesToDelete.add(new File(TestUtil.getTargetDir(), LOG_DIR));
}
protected void tearDown() {
filesToDelete.delete();
}
public void testSetProjectNameShouldThrowIAEIfPassedNull() {
Log log = new Log();
try {
log.setProjectName(null);
fail();
} catch (IllegalArgumentException expected) {
assertEquals("projectName can't be null", expected.getMessage());
}
}
public void testValidateShouldFailWhenProjectNameNotSet() {
Log log = new Log();
try {
log.validate();
fail();
} catch (CruiseControlException expected) {
assertEquals("projectName must be set", expected.getMessage());
}
}
public void testDefaultLogLocation() {
Log log = new Log();
log.setProjectName("foo");
assertEquals("logs" + File.separatorChar + "foo", log.getLogDir());
}
public void testFormatLogFileName() {
Calendar augTweleveCalendar = Calendar.getInstance();
augTweleveCalendar.set(2004, 7, 12, 1, 1, 1);
Date augTweleve = augTweleveCalendar.getTime();
String expected = "log20040812010101.xml";
String actual = Log.formatLogFileName(augTweleve);
assertEquals(
expected + "--" + actual,
expected, actual);
assertEquals("log20040812010101Lbuild.1.xml", Log.formatLogFileName(augTweleve, "build.1"));
}
public void testWasSuccessfulBuild() {
assertTrue(Log.wasSuccessfulBuild("log20040812010101Lbuild.1.xml"));
assertFalse(Log.wasSuccessfulBuild("log20040812010101.xml"));
assertFalse(Log.wasSuccessfulBuild(null));
}
public void testParseDateFromLogFileName() throws CruiseControlException {
Calendar augTweleveCalendar = Calendar.getInstance();
augTweleveCalendar.set(2004, 7, 12, 1, 1, 1);
Date augTweleve = augTweleveCalendar.getTime();
assertEquals(augTweleve.toString(), Log.parseDateFromLogFileName("log20040812010101Lbuild.1.xml").toString());
assertEquals(augTweleve.toString(), Log.parseDateFromLogFileName("log20040812010101.xml").toString());
}
public void testParseLabelFromLogFileName() {
assertEquals("build.1", Log.parseLabelFromLogFileName("log20040812010101Lbuild.1.xml"));
assertEquals("", Log.parseLabelFromLogFileName("log20040812010101.xml"));
}
public void testXMLEncoding()
throws CruiseControlException, IOException, JDOMException {
final String[] encodings = { "UTF-8", "ISO-8859-1", null };
final SAXBuilder builder = new SAXBuilder("org.apache.xerces.parsers.SAXParser");
final XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
for (final String encoding : encodings) {
final Log log = new Log();
log.setProjectName("testXMLEncoding");
log.setDir(LOG_DIR);
if (encoding != null) {
log.setEncoding(encoding);
}
log.validate();
// log.validate() creates a new dir, so make sure we leave no side effects and delete this dir
filesToDelete.add(new File(log.getLogDir()));
// Add a minimal buildLog
log.addContent(getBuildLogInfo());
final Element build = new Element("build");
log.addContent(build);
log.addContent(new Element("modifications"));
// Add 8-bit characters
build.setText("Something with special characters: \u00c6\u00d8\u00c5");
final Date now = new Date();
// Write and read the file
log.writeLogFile(now);
final String expectFilename = "log" + DateUtil.getFormattedTime(now) + "L.xml";
final File logFile = new File(LOG_DIR, expectFilename);
assertTrue(logFile.isFile());
filesToDelete.add(logFile);
final Element actualContent = builder.build(logFile).getRootElement();
// content.toString() only returns the root element but not the
// children: [Element: <cruisecontrol/>]
// Use an XMLOutputter (that trims whitespace) instead.
final String expected = outputter.outputString(log.getContent());
final String actual = outputter.outputString(actualContent);
assertEquals(expected, actual);
}
}
/**
* Asserts that leading and trailing whitespace in CDATA elements is preserved.
*
* @throws Exception if test fails
*/
public void testXMLWhitespacePreservation() throws Exception {
final String testString = " la dee da\t";
// test default
assertXMLWhiteSpacePreservation(testString, testString, new Log());
final Log logTrimmed = new Log();
logTrimmed.setTrimWhitespace(true);
assertXMLWhiteSpacePreservation(testString.trim(), testString, logTrimmed);
final Log logWhitespacePreserved = new Log();
logWhitespacePreserved.setTrimWhitespace(false);
assertXMLWhiteSpacePreservation(testString, testString, logWhitespacePreserved);
}
private void assertXMLWhiteSpacePreservation(final String expectedLogText, final String testString, final Log log)
throws Exception {
log.setProjectName(getName());
log.setDir(LOG_DIR);
log.addContent(getBuildLogInfo());
final Element buildElem = new Element("build");
log.addContent(buildElem);
final Element msgElem = new Element("message");
msgElem.setAttribute("priority", "info");
msgElem.addContent(new CDATA(testString));
buildElem.addContent(msgElem);
log.validate();
// log.validate() creates a new dir, so make sure we leave no side effects and delete this dir
filesToDelete.add(new File(log.getLogDir()));
final Date now = new Date();
log.writeLogFile(now);
final String expectFilename = "log" + DateUtil.getFormattedTime(now) + "L.xml";
final File logFile = new File(LOG_DIR, expectFilename);
assertTrue(logFile.isFile());
Element elem = new SAXBuilder("org.apache.xerces.parsers.SAXParser").build(logFile).getRootElement();
elem = elem.getChild("build");
elem = elem.getChild("message");
final CDATA cdata = (CDATA) elem.getContent(0);
assertEquals(expectedLogText, cdata.getText());
}
public void testManipulateLog() throws Exception {
final String testProjectName = "testBackupLog";
// Test backup of 12 Months
Calendar date = Calendar.getInstance();
date.set(Calendar.YEAR, date.get(Calendar.YEAR) - 1);
date.set(Calendar.MONTH, date.get(Calendar.MONTH) - 13);
GZIPManipulator gzip = new GZIPManipulator();
gzip.setEvery(12);
gzip.setUnit("month");
// create old log
getWrittenTestLog(testProjectName, date.getTime());
// create new log
Log log = getWrittenTestLog(testProjectName, new Date());
log.add(gzip);
log.validate();
assertBackupsHelper(log, 2, 1, 1);
// Test Backup of 2 days
date = Calendar.getInstance();
date.set(Calendar.DAY_OF_MONTH, date.get(Calendar.DAY_OF_MONTH) - 2);
gzip = new GZIPManipulator();
gzip.setEvery(2);
gzip.setUnit("day");
log = getWrittenTestLog(testProjectName, date.getTime());
log.add(gzip);
log.validate();
assertBackupsHelper(log, 3, 1, 2);
// Test delete of logfile
date = Calendar.getInstance();
date.set(Calendar.DAY_OF_MONTH, date.get(Calendar.DAY_OF_MONTH) - 2);
DeleteManipulator deleteManipulator = new DeleteManipulator();
deleteManipulator.setEvery(2);
deleteManipulator.setUnit("day");
log = getWrittenTestLog(testProjectName, date.getTime());
log.add(deleteManipulator);
log.validate();
assertBackupsHelper(log, 3, 1, 2);
date = Calendar.getInstance();
date.set(Calendar.DAY_OF_MONTH, date.get(Calendar.DAY_OF_MONTH) - 2);
deleteManipulator = new DeleteManipulator();
deleteManipulator.setEvery(2);
deleteManipulator.setUnit("day");
// This should delete the gz-files too
deleteManipulator.setIgnoreSuffix(true);
log = getWrittenTestLog(testProjectName, date.getTime());
log.add(deleteManipulator);
log.validate();
assertBackupsHelper(log, 1, 1, 0);
//Validation Error
gzip = new GZIPManipulator();
gzip.setUnit("day");
log = getWrittenTestLog(testProjectName, date.getTime());
log.add(gzip);
try {
log.validate();
fail("Validation should fail!");
} catch (CruiseControlException e) {
assertTrue(true);
}
}
public void testIsExistingLogLabel() throws Exception {
final Calendar date = Calendar.getInstance();
final Log log = getWrittenTestLog("testGetLogLabels", date.getTime());
final List<String> labels = log.getLogLabels();
final String validLabel = labels.get(0);
assertTrue(log.isExistingLogLabel(validLabel));
assertFalse(log.isExistingLogLabel("../" + validLabel));
}
public void testGetFileFromLabel() throws Exception {
final Calendar date = Calendar.getInstance();
final Log log = getWrittenTestLog("testGetLogLabels", date.getTime());
final List<String> labels = log.getLogLabels();
final String validLabel = labels.get(0);
assertTrue(log.getFileFromLabel(validLabel).exists());
try {
log.getFileFromLabel("../" + validLabel);
fail("non-label filename should fail");
} catch (IllegalArgumentException e) {
assertEquals(Log.MSG_PREFIX_INVALID_LABEL + "../" + validLabel, e.getMessage());
}
}
public void testGetLogLabelLinesBadLabel() throws Exception {
final Calendar date = Calendar.getInstance();
final Log log = getWrittenTestLog(getName(), date.getTime());
final List<String> labels = log.getLogLabels();
assertEquals("There must be one log file", 1, labels.size());
final String badLabel = "../" + labels.get(0);
try {
log.getLogLabelLines(badLabel, 0);
fail("non-label filename should fail");
} catch (IllegalArgumentException e) {
assertEquals(Log.MSG_PREFIX_INVALID_LABEL + badLabel, e.getMessage());
}
}
public void testGetLogLabelLinesNegativeFirstLine() throws Exception {
final Calendar date = Calendar.getInstance();
final Log log = getWrittenTestLog(getName(), date.getTime());
final List<String> labels = log.getLogLabels();
assertEquals("There must be one log file", 1, labels.size());
final String label = labels.get(0);
final String[] logContents = log.getLogLabelLines(label, -1);
assertEquals(16, logContents.length);
}
public void testGetLogLabelLinesFirstLineEnd() throws Exception {
final Calendar date = Calendar.getInstance();
final Log log = getWrittenTestLog(getName(), date.getTime());
final List<String> labels = log.getLogLabels();
assertEquals("There must be one log file", 1, labels.size());
final String label = labels.get(0);
assertEquals(1, log.getLogLabelLines(label, 15).length);
assertEquals(0, log.getLogLabelLines(label, 16).length);
assertEquals(0, log.getLogLabelLines(label, 17).length);
assertEquals(0, log.getLogLabelLines(label, 100).length);
}
public void testGetLogLabelLines() throws Exception {
final Calendar date = Calendar.getInstance();
final Log log = getWrittenTestLog(getName(), date.getTime());
final List<String> labels = log.getLogLabels();
assertEquals("There must be one log file", 1, labels.size());
final String label = labels.get(0);
final String[] logContents = log.getLogLabelLines(label, 0);
assertNotNull("logContents should not be null", logContents);
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", logContents[0]);
assertEquals("<cruisecontrol>", logContents[1]);
assertEquals("</cruisecontrol>", logContents[14]);
assertEquals("", logContents[15]);
}
public void testGetZeroLogLabels() throws Exception {
final Log log = new Log();
log.setProjectName(getName());
log.setDir(LOG_DIR);
log.validate();
final List<String> labels = log.getLogLabels();
assertEquals("There must be zero log files", 0, labels.size());
}
public void testGetLogLabels() throws Exception {
final Calendar date = Calendar.getInstance();
date.set(Calendar.MINUTE, date.get(Calendar.MINUTE) - 5);
getWrittenTestLog(getName(), date.getTime());
final Log log = getWrittenTestLog(getName(), new Date());
final List<String> labels = log.getLogLabels();
assertEquals("There must be two log files", 2, labels.size());
}
private void assertBackupsHelper(final Log log,
final int expectedLength, final int expectedXML, final int expectedGZIP) {
log.callManipulators();
final File[] logfiles = new File(log.getLogDir()).listFiles(new FilenameFilter() {
public boolean accept(final File file, final String fileName) {
return fileName.startsWith("log20")
&& (fileName.endsWith(".xml") || fileName
.endsWith(".gz"));
}
});
int countGzip = 0;
int countXML = 0;
for (final File file : logfiles) {
if (file.getName().endsWith(".gz")) {
filesToDelete.add(file);
countGzip++;
} else if (file.getName().endsWith(".xml")) {
countXML++;
} else {
fail("Other log files exists");
}
}
assertEquals("Wrong number of gzip log files after manipulation", expectedGZIP, countGzip);
assertEquals("Wrong number of xml log files after manipulation", expectedXML, countXML);
assertEquals("Wrong total number of log files after manipulation", expectedLength, logfiles.length);
}
private Log getWrittenTestLog(final String projectName, final Date date)
throws CruiseControlException, JDOMException, IOException {
final Log log = new Log();
log.setProjectName(projectName);
log.setDir(LOG_DIR);
log.validate();
// log.validate() creates a new dir, so make sure we leave no side effects and delete this dir
filesToDelete.add(new File(log.getLogDir()));
log.addContent(getBuildLogInfo());
final Element build = new Element("build");
log.addContent(build);
log.addContent(new Element("modifications"));
log.writeLogFile(date);
final String expectFilename = "log" + DateUtil.getFormattedTime(date) + "L.xml";
final File logFile = new File(LOG_DIR, expectFilename);
filesToDelete.add(logFile);
assertTrue(logFile.isFile());
return log;
}
// Get a minimal info element for the buildLog
private Element getBuildLogInfo() throws JDOMException, IOException {
SAXBuilder builder = new SAXBuilder("org.apache.xerces.parsers.SAXParser");
String infoXML = "<info><property name=\"label\" value=\"\"/>"
+ "<property name=\"lastbuildtime\" value=\"\"/>"
+ "<property name=\"lastgoodbuildtime\" value=\"\"/>"
+ "<property name=\"lastbuildsuccessful\" value=\"\"/>"
+ "<property name=\"buildfile\" value=\"\"/>"
+ "<property name=\"buildtarget\" value=\"\"/>"
+ "</info>";
Element info = builder.build(new StringReader(infoXML)).getRootElement();
return (Element) info.clone();
}
}