|
|
@@ -1,425 +1,425 @@ |
|
|
|
/*
|
|
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
|
|
* this work for additional information regarding copyright ownership.
|
|
|
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
|
* (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
package org.apache.tools.ant.taskdefs.optional.junit;
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.io.PrintWriter;
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
import java.util.Date;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.SortedSet;
|
|
|
|
import java.util.TreeSet;
|
|
|
|
import java.util.Vector;
|
|
|
|
|
|
|
|
import junit.framework.AssertionFailedError;
|
|
|
|
import junit.framework.Test;
|
|
|
|
|
|
|
|
import org.apache.tools.ant.BuildEvent;
|
|
|
|
import org.apache.tools.ant.BuildException;
|
|
|
|
import org.apache.tools.ant.BuildListener;
|
|
|
|
import org.apache.tools.ant.Project;
|
|
|
|
import org.apache.tools.ant.ProjectComponent;
|
|
|
|
import org.apache.tools.ant.util.FileUtils;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <p>Collects all failing test <i>cases</i> and creates a new JUnit test class containing
|
|
|
|
* a suite() method which calls these failed tests.</p>
|
|
|
|
* <p>Having classes <i>A</i> ... <i>D</i> with each several testcases you could earn a new
|
|
|
|
* test class like
|
|
|
|
* <pre>
|
|
|
|
* // generated on: 2007.08.06 09:42:34,555
|
|
|
|
* import junit.framework.*;
|
|
|
|
* public class FailedTests extends TestCase {
|
|
|
|
* public FailedTests(String testname) {
|
|
|
|
* super(testname);
|
|
|
|
* }
|
|
|
|
* public static Test suite() {
|
|
|
|
* TestSuite suite = new TestSuite();
|
|
|
|
* suite.addTest( new B("test04") );
|
|
|
|
* suite.addTest( new org.D("test10") );
|
|
|
|
* return suite;
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
* Because each running test case gets its own formatter, we collect
|
|
|
|
* the failing test cases in a static list. Because we dont have a finalizer
|
|
|
|
* method in the formatters "lifecycle", we register this formatter as
|
|
|
|
* BuildListener and generate the new java source on taskFinished event.
|
|
|
|
*
|
|
|
|
* @since Ant 1.8.0
|
|
|
|
*/
|
|
|
|
public class FailureRecorder extends ProjectComponent implements JUnitResultFormatter, BuildListener {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is the name of a magic System property ({@value}). The value of this
|
|
|
|
* <b>System</b> property should point to the location where to store the
|
|
|
|
* generated class (without suffix).
|
|
|
|
* Default location and name is defined in DEFAULT_CLASS_LOCATION.
|
|
|
|
* @see #DEFAULT_CLASS_LOCATION
|
|
|
|
*/
|
|
|
|
public static final String MAGIC_PROPERTY_CLASS_LOCATION
|
|
|
|
= "ant.junit.failureCollector";
|
|
|
|
|
|
|
|
/** Default location and name for the generated JUnit class file. {@value} */
|
|
|
|
public static final String DEFAULT_CLASS_LOCATION
|
|
|
|
= System.getProperty("java.io.tmpdir") + "FailedTests";
|
|
|
|
|
|
|
|
/** Prefix for logging. {@value} */
|
|
|
|
private static final String LOG_PREFIX = " [junit]";
|
|
|
|
|
|
|
|
/** Class names of failed tests without duplicates. */
|
|
|
|
private static SortedSet/*<TestInfos>*/ failedTests = new TreeSet();
|
|
|
|
|
|
|
|
/** A writer for writing the generated source to. */
|
|
|
|
private PrintWriter writer;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Location and name of the generated JUnit class.
|
|
|
|
* Lazy instantiated via getLocationName().
|
|
|
|
*/
|
|
|
|
private static String locationName;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the (lazy evaluated) location for the collector class.
|
|
|
|
* Order for evaluation: System property > Ant property > default value
|
|
|
|
* @return location for the collector class
|
|
|
|
* @see #MAGIC_PROPERTY_CLASS_LOCATION
|
|
|
|
* @see #DEFAULT_CLASS_LOCATION
|
|
|
|
*/
|
|
|
|
private String getLocationName() {
|
|
|
|
if (locationName == null) {
|
|
|
|
String syspropValue = System.getProperty(MAGIC_PROPERTY_CLASS_LOCATION);
|
|
|
|
String antpropValue = getProject().getProperty(MAGIC_PROPERTY_CLASS_LOCATION);
|
|
|
|
|
|
|
|
if (syspropValue != null) {
|
|
|
|
locationName = syspropValue;
|
|
|
|
verbose("System property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' set, so use "
|
|
|
|
+ "its value '" + syspropValue + "' as location for collector class.");
|
|
|
|
} else if (antpropValue != null) {
|
|
|
|
locationName = antpropValue;
|
|
|
|
verbose("Ant property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' set, so use "
|
|
|
|
+ "its value '" + antpropValue + "' as location for collector class.");
|
|
|
|
} else {
|
|
|
|
locationName = DEFAULT_CLASS_LOCATION;
|
|
|
|
verbose("System property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' not set, so use "
|
|
|
|
+ "value as location for collector class: '"
|
|
|
|
+ DEFAULT_CLASS_LOCATION + "'");
|
|
|
|
}
|
|
|
|
|
|
|
|
File locationFile = new File(locationName);
|
|
|
|
if (!locationFile.isAbsolute()) {
|
|
|
|
File f = new File(getProject().getBaseDir(), locationName);
|
|
|
|
locationName = f.getAbsolutePath();
|
|
|
|
verbose("Location file is relative (" + locationFile + ")"
|
|
|
|
+ " use absolute path instead (" + locationName + ")");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return locationName;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method is called by the Ant runtime by reflection. We use the project reference for
|
|
|
|
* registration of this class as BuildListener.
|
|
|
|
*
|
|
|
|
* @param project
|
|
|
|
* project reference
|
|
|
|
*/
|
|
|
|
public void setProject(Project project) {
|
|
|
|
// store project reference for logging
|
|
|
|
super.setProject(project);
|
|
|
|
// check if already registered
|
|
|
|
boolean alreadyRegistered = false;
|
|
|
|
Vector allListeners = project.getBuildListeners();
|
|
|
|
for (int i = 0; i < allListeners.size(); i++) {
|
|
|
|
Object listener = allListeners.get(i);
|
|
|
|
if (listener instanceof FailureRecorder) {
|
|
|
|
alreadyRegistered = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// register if needed
|
|
|
|
if (!alreadyRegistered) {
|
|
|
|
verbose("Register FailureRecorder (@" + this.hashCode() + ") as BuildListener");
|
|
|
|
project.addBuildListener(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ===== JUnitResultFormatter =====
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void endTestSuite(JUnitTest suite) throws BuildException {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the failed test to the list.
|
|
|
|
* @param test the test that errored.
|
|
|
|
* @param throwable the reason it errored.
|
|
|
|
* @see junit.framework.TestListener#addError(junit.framework.Test, java.lang.Throwable)
|
|
|
|
*/
|
|
|
|
public void addError(Test test, Throwable throwable) {
|
|
|
|
failedTests.add(new TestInfos(test));
|
|
|
|
}
|
|
|
|
|
|
|
|
// CheckStyle:LineLengthCheck OFF - @see is long
|
|
|
|
/**
|
|
|
|
* Add the failed test to the list.
|
|
|
|
* @param test the test that failed.
|
|
|
|
* @param error the assertion that failed.
|
|
|
|
* @see junit.framework.TestListener#addFailure(junit.framework.Test, junit.framework.AssertionFailedError)
|
|
|
|
*/
|
|
|
|
// CheckStyle:LineLengthCheck ON
|
|
|
|
public void addFailure(Test test, AssertionFailedError error) {
|
|
|
|
failedTests.add(new TestInfos(test));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void setOutput(OutputStream out) {
|
|
|
|
// unused, close output file so it can be deleted before the VM exits
|
|
|
|
if (out != System.out) {
|
|
|
|
FileUtils.close(out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void setSystemError(String err) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void setSystemOutput(String out) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void startTestSuite(JUnitTest suite) throws BuildException {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void endTest(Test test) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void startTest(Test test) {
|
|
|
|
}
|
|
|
|
|
|
|
|
// ===== "Templates" for generating the JUnit class =====
|
|
|
|
|
|
|
|
private void writeJavaClass() {
|
|
|
|
try {
|
|
|
|
File sourceFile = new File((getLocationName() + ".java"));
|
|
|
|
verbose("Write collector class to '" + sourceFile.getAbsolutePath() + "'");
|
|
|
|
|
|
|
|
sourceFile.delete();
|
|
|
|
writer = new PrintWriter(new FileOutputStream(sourceFile));
|
|
|
|
|
|
|
|
createClassHeader();
|
|
|
|
createSuiteMethod();
|
|
|
|
createClassFooter();
|
|
|
|
|
|
|
|
FileUtils.close(writer);
|
|
|
|
} catch (FileNotFoundException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void createClassHeader() {
|
|
|
|
String className = getLocationName().replace('\\', '/');
|
|
|
|
if (className.indexOf('/') > -1) {
|
|
|
|
className = className.substring(className.lastIndexOf('/') + 1);
|
|
|
|
}
|
|
|
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss,SSS");
|
|
|
|
writer.print("// generated on: ");
|
|
|
|
writer.println(sdf.format(new Date()));
|
|
|
|
writer.println("import junit.framework.*;");
|
|
|
|
writer.print("public class ");
|
|
|
|
writer.print(className);
|
|
|
|
// If this class does not extend TC, Ant doesnt run these
|
|
|
|
writer.println(" extends TestCase {");
|
|
|
|
// standard String-constructor
|
|
|
|
writer.print(" public ");
|
|
|
|
writer.print(className);
|
|
|
|
writer.println("(String testname) {");
|
|
|
|
writer.println(" super(testname);");
|
|
|
|
writer.println(" }");
|
|
|
|
}
|
|
|
|
|
|
|
|
private void createSuiteMethod() {
|
|
|
|
writer.println(" public static Test suite() {");
|
|
|
|
writer.println(" TestSuite suite = new TestSuite();");
|
|
|
|
for (Iterator iter = failedTests.iterator(); iter.hasNext();) {
|
|
|
|
TestInfos testInfos = (TestInfos) iter.next();
|
|
|
|
writer.print(" suite.addTest(");
|
|
|
|
writer.print(testInfos);
|
|
|
|
writer.println(");");
|
|
|
|
}
|
|
|
|
writer.println(" return suite;");
|
|
|
|
writer.println(" }");
|
|
|
|
}
|
|
|
|
|
|
|
|
private void createClassFooter() {
|
|
|
|
writer.println("}");
|
|
|
|
}
|
|
|
|
|
|
|
|
// ===== Helper classes and methods =====
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Logging facade in INFO-mode.
|
|
|
|
* @param message Log-message
|
|
|
|
*/
|
|
|
|
public void log(String message) {
|
|
|
|
getProject().log(LOG_PREFIX + " " + message, Project.MSG_INFO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Logging facade in VERBOSE-mode.
|
|
|
|
* @param message Log-message
|
|
|
|
*/
|
|
|
|
public void verbose(String message) {
|
|
|
|
getProject().log(LOG_PREFIX + " " + message, Project.MSG_VERBOSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TestInfos holds information about a given test for later use.
|
|
|
|
*/
|
|
|
|
public class TestInfos implements Comparable {
|
|
|
|
|
|
|
|
/** The class name of the test. */
|
|
|
|
private String className;
|
|
|
|
|
|
|
|
/** The method name of the testcase. */
|
|
|
|
private String methodName;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This constructor extracts the needed information from the given test.
|
|
|
|
* @param test Test to analyze
|
|
|
|
*/
|
|
|
|
public TestInfos(Test test) {
|
|
|
|
className = test.getClass().getName();
|
|
|
|
methodName = test.toString();
|
|
|
|
methodName = methodName.substring(0, methodName.indexOf('('));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This String-Representation can directly be used for instantiation of
|
|
|
|
* the JUnit testcase.
|
|
|
|
* @return the string representation.
|
|
|
|
* @see java.lang.Object#toString()
|
|
|
|
* @see FailureRecorder#createSuiteMethod()
|
|
|
|
*/
|
|
|
|
public String toString() {
|
|
|
|
return "new " + className + "(\"" + methodName + "\")";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The SortedMap needs comparable elements.
|
|
|
|
* @param other the object to compare to.
|
|
|
|
* @return the result of the comparison.
|
|
|
|
* @see java.lang.Comparable#compareTo(T)
|
|
|
|
* @see SortedSet#comparator()
|
|
|
|
*/
|
|
|
|
public int compareTo(Object other) {
|
|
|
|
if (other instanceof TestInfos) {
|
|
|
|
TestInfos otherInfos = (TestInfos) other;
|
|
|
|
return toString().compareTo(otherInfos.toString());
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ===== BuildListener =====
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void buildFinished(BuildEvent event) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void buildStarted(BuildEvent event) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void messageLogged(BuildEvent event) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void targetFinished(BuildEvent event) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void targetStarted(BuildEvent event) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The task outside of this JUnitResultFormatter is the <junit> task. So all tests passed
|
|
|
|
* and we could create the new java class.
|
|
|
|
* @param event not used
|
|
|
|
* @see org.apache.tools.ant.BuildListener#taskFinished(org.apache.tools.ant.BuildEvent)
|
|
|
|
*/
|
|
|
|
public void taskFinished(BuildEvent event) {
|
|
|
|
if (!failedTests.isEmpty()) {
|
|
|
|
writeJavaClass();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Not used
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public void taskStarted(BuildEvent event) {
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
/* |
|
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
|
|
|
* contributor license agreements. See the NOTICE file distributed with |
|
|
|
* this work for additional information regarding copyright ownership. |
|
|
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|
|
|
* (the "License"); you may not use this file except in compliance with |
|
|
|
* the License. You may obtain a copy of the License at |
|
|
|
* |
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
|
|
* |
|
|
|
* Unless required by applicable law or agreed to in writing, software |
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
|
|
* See the License for the specific language governing permissions and |
|
|
|
* limitations under the License. |
|
|
|
* |
|
|
|
*/ |
|
|
|
package org.apache.tools.ant.taskdefs.optional.junit; |
|
|
|
|
|
|
|
import java.io.File; |
|
|
|
import java.io.FileNotFoundException; |
|
|
|
import java.io.FileOutputStream; |
|
|
|
import java.io.OutputStream; |
|
|
|
import java.io.PrintWriter; |
|
|
|
import java.text.SimpleDateFormat; |
|
|
|
import java.util.Date; |
|
|
|
import java.util.Iterator; |
|
|
|
import java.util.SortedSet; |
|
|
|
import java.util.TreeSet; |
|
|
|
import java.util.Vector; |
|
|
|
|
|
|
|
import junit.framework.AssertionFailedError; |
|
|
|
import junit.framework.Test; |
|
|
|
|
|
|
|
import org.apache.tools.ant.BuildEvent; |
|
|
|
import org.apache.tools.ant.BuildException; |
|
|
|
import org.apache.tools.ant.BuildListener; |
|
|
|
import org.apache.tools.ant.Project; |
|
|
|
import org.apache.tools.ant.ProjectComponent; |
|
|
|
import org.apache.tools.ant.util.FileUtils; |
|
|
|
|
|
|
|
/** |
|
|
|
* <p>Collects all failing test <i>cases</i> and creates a new JUnit test class containing |
|
|
|
* a suite() method which calls these failed tests.</p> |
|
|
|
* <p>Having classes <i>A</i> ... <i>D</i> with each several testcases you could earn a new |
|
|
|
* test class like |
|
|
|
* <pre> |
|
|
|
* // generated on: 2007.08.06 09:42:34,555 |
|
|
|
* import junit.framework.*; |
|
|
|
* public class FailedTests extends TestCase { |
|
|
|
* public FailedTests(String testname) { |
|
|
|
* super(testname); |
|
|
|
* } |
|
|
|
* public static Test suite() { |
|
|
|
* TestSuite suite = new TestSuite(); |
|
|
|
* suite.addTest( new B("test04") ); |
|
|
|
* suite.addTest( new org.D("test10") ); |
|
|
|
* return suite; |
|
|
|
* } |
|
|
|
* } |
|
|
|
* </pre> |
|
|
|
* |
|
|
|
* Because each running test case gets its own formatter, we collect |
|
|
|
* the failing test cases in a static list. Because we dont have a finalizer |
|
|
|
* method in the formatters "lifecycle", we register this formatter as |
|
|
|
* BuildListener and generate the new java source on taskFinished event. |
|
|
|
* |
|
|
|
* @since Ant 1.8.0 |
|
|
|
*/ |
|
|
|
public class FailureRecorder extends ProjectComponent implements JUnitResultFormatter, BuildListener { |
|
|
|
|
|
|
|
/** |
|
|
|
* This is the name of a magic System property ({@value}). The value of this |
|
|
|
* <b>System</b> property should point to the location where to store the |
|
|
|
* generated class (without suffix). |
|
|
|
* Default location and name is defined in DEFAULT_CLASS_LOCATION. |
|
|
|
* @see #DEFAULT_CLASS_LOCATION |
|
|
|
*/ |
|
|
|
public static final String MAGIC_PROPERTY_CLASS_LOCATION |
|
|
|
= "ant.junit.failureCollector"; |
|
|
|
|
|
|
|
/** Default location and name for the generated JUnit class file. {@value} */ |
|
|
|
public static final String DEFAULT_CLASS_LOCATION |
|
|
|
= System.getProperty("java.io.tmpdir") + "FailedTests"; |
|
|
|
|
|
|
|
/** Prefix for logging. {@value} */ |
|
|
|
private static final String LOG_PREFIX = " [junit]"; |
|
|
|
|
|
|
|
/** Class names of failed tests without duplicates. */ |
|
|
|
private static SortedSet/*<TestInfos>*/ failedTests = new TreeSet(); |
|
|
|
|
|
|
|
/** A writer for writing the generated source to. */ |
|
|
|
private PrintWriter writer; |
|
|
|
|
|
|
|
/** |
|
|
|
* Location and name of the generated JUnit class. |
|
|
|
* Lazy instantiated via getLocationName(). |
|
|
|
*/ |
|
|
|
private static String locationName; |
|
|
|
|
|
|
|
/** |
|
|
|
* Returns the (lazy evaluated) location for the collector class. |
|
|
|
* Order for evaluation: System property > Ant property > default value |
|
|
|
* @return location for the collector class |
|
|
|
* @see #MAGIC_PROPERTY_CLASS_LOCATION |
|
|
|
* @see #DEFAULT_CLASS_LOCATION |
|
|
|
*/ |
|
|
|
private String getLocationName() { |
|
|
|
if (locationName == null) { |
|
|
|
String syspropValue = System.getProperty(MAGIC_PROPERTY_CLASS_LOCATION); |
|
|
|
String antpropValue = getProject().getProperty(MAGIC_PROPERTY_CLASS_LOCATION); |
|
|
|
|
|
|
|
if (syspropValue != null) { |
|
|
|
locationName = syspropValue; |
|
|
|
verbose("System property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' set, so use " |
|
|
|
+ "its value '" + syspropValue + "' as location for collector class."); |
|
|
|
} else if (antpropValue != null) { |
|
|
|
locationName = antpropValue; |
|
|
|
verbose("Ant property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' set, so use " |
|
|
|
+ "its value '" + antpropValue + "' as location for collector class."); |
|
|
|
} else { |
|
|
|
locationName = DEFAULT_CLASS_LOCATION; |
|
|
|
verbose("System property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' not set, so use " |
|
|
|
+ "value as location for collector class: '" |
|
|
|
+ DEFAULT_CLASS_LOCATION + "'"); |
|
|
|
} |
|
|
|
|
|
|
|
File locationFile = new File(locationName); |
|
|
|
if (!locationFile.isAbsolute()) { |
|
|
|
File f = new File(getProject().getBaseDir(), locationName); |
|
|
|
locationName = f.getAbsolutePath(); |
|
|
|
verbose("Location file is relative (" + locationFile + ")" |
|
|
|
+ " use absolute path instead (" + locationName + ")"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return locationName; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* This method is called by the Ant runtime by reflection. We use the project reference for |
|
|
|
* registration of this class as BuildListener. |
|
|
|
* |
|
|
|
* @param project |
|
|
|
* project reference |
|
|
|
*/ |
|
|
|
public void setProject(Project project) { |
|
|
|
// store project reference for logging |
|
|
|
super.setProject(project); |
|
|
|
// check if already registered |
|
|
|
boolean alreadyRegistered = false; |
|
|
|
Vector allListeners = project.getBuildListeners(); |
|
|
|
for (int i = 0; i < allListeners.size(); i++) { |
|
|
|
Object listener = allListeners.get(i); |
|
|
|
if (listener instanceof FailureRecorder) { |
|
|
|
alreadyRegistered = true; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
// register if needed |
|
|
|
if (!alreadyRegistered) { |
|
|
|
verbose("Register FailureRecorder (@" + this.hashCode() + ") as BuildListener"); |
|
|
|
project.addBuildListener(this); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ===== JUnitResultFormatter ===== |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void endTestSuite(JUnitTest suite) throws BuildException { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Add the failed test to the list. |
|
|
|
* @param test the test that errored. |
|
|
|
* @param throwable the reason it errored. |
|
|
|
* @see junit.framework.TestListener#addError(junit.framework.Test, java.lang.Throwable) |
|
|
|
*/ |
|
|
|
public void addError(Test test, Throwable throwable) { |
|
|
|
failedTests.add(new TestInfos(test)); |
|
|
|
} |
|
|
|
|
|
|
|
// CheckStyle:LineLengthCheck OFF - @see is long |
|
|
|
/** |
|
|
|
* Add the failed test to the list. |
|
|
|
* @param test the test that failed. |
|
|
|
* @param error the assertion that failed. |
|
|
|
* @see junit.framework.TestListener#addFailure(junit.framework.Test, junit.framework.AssertionFailedError) |
|
|
|
*/ |
|
|
|
// CheckStyle:LineLengthCheck ON |
|
|
|
public void addFailure(Test test, AssertionFailedError error) { |
|
|
|
failedTests.add(new TestInfos(test)); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void setOutput(OutputStream out) { |
|
|
|
// unused, close output file so it can be deleted before the VM exits |
|
|
|
if (out != System.out) { |
|
|
|
FileUtils.close(out); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void setSystemError(String err) { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void setSystemOutput(String out) { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void startTestSuite(JUnitTest suite) throws BuildException { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void endTest(Test test) { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void startTest(Test test) { |
|
|
|
} |
|
|
|
|
|
|
|
// ===== "Templates" for generating the JUnit class ===== |
|
|
|
|
|
|
|
private void writeJavaClass() { |
|
|
|
try { |
|
|
|
File sourceFile = new File((getLocationName() + ".java")); |
|
|
|
verbose("Write collector class to '" + sourceFile.getAbsolutePath() + "'"); |
|
|
|
|
|
|
|
sourceFile.delete(); |
|
|
|
writer = new PrintWriter(new FileOutputStream(sourceFile)); |
|
|
|
|
|
|
|
createClassHeader(); |
|
|
|
createSuiteMethod(); |
|
|
|
createClassFooter(); |
|
|
|
|
|
|
|
FileUtils.close(writer); |
|
|
|
} catch (FileNotFoundException e) { |
|
|
|
e.printStackTrace(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void createClassHeader() { |
|
|
|
String className = getLocationName().replace('\\', '/'); |
|
|
|
if (className.indexOf('/') > -1) { |
|
|
|
className = className.substring(className.lastIndexOf('/') + 1); |
|
|
|
} |
|
|
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss,SSS"); |
|
|
|
writer.print("// generated on: "); |
|
|
|
writer.println(sdf.format(new Date())); |
|
|
|
writer.println("import junit.framework.*;"); |
|
|
|
writer.print("public class "); |
|
|
|
writer.print(className); |
|
|
|
// If this class does not extend TC, Ant doesnt run these |
|
|
|
writer.println(" extends TestCase {"); |
|
|
|
// standard String-constructor |
|
|
|
writer.print(" public "); |
|
|
|
writer.print(className); |
|
|
|
writer.println("(String testname) {"); |
|
|
|
writer.println(" super(testname);"); |
|
|
|
writer.println(" }"); |
|
|
|
} |
|
|
|
|
|
|
|
private void createSuiteMethod() { |
|
|
|
writer.println(" public static Test suite() {"); |
|
|
|
writer.println(" TestSuite suite = new TestSuite();"); |
|
|
|
for (Iterator iter = failedTests.iterator(); iter.hasNext();) { |
|
|
|
TestInfos testInfos = (TestInfos) iter.next(); |
|
|
|
writer.print(" suite.addTest("); |
|
|
|
writer.print(testInfos); |
|
|
|
writer.println(");"); |
|
|
|
} |
|
|
|
writer.println(" return suite;"); |
|
|
|
writer.println(" }"); |
|
|
|
} |
|
|
|
|
|
|
|
private void createClassFooter() { |
|
|
|
writer.println("}"); |
|
|
|
} |
|
|
|
|
|
|
|
// ===== Helper classes and methods ===== |
|
|
|
|
|
|
|
/** |
|
|
|
* Logging facade in INFO-mode. |
|
|
|
* @param message Log-message |
|
|
|
*/ |
|
|
|
public void log(String message) { |
|
|
|
getProject().log(LOG_PREFIX + " " + message, Project.MSG_INFO); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Logging facade in VERBOSE-mode. |
|
|
|
* @param message Log-message |
|
|
|
*/ |
|
|
|
public void verbose(String message) { |
|
|
|
getProject().log(LOG_PREFIX + " " + message, Project.MSG_VERBOSE); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* TestInfos holds information about a given test for later use. |
|
|
|
*/ |
|
|
|
public class TestInfos implements Comparable { |
|
|
|
|
|
|
|
/** The class name of the test. */ |
|
|
|
private String className; |
|
|
|
|
|
|
|
/** The method name of the testcase. */ |
|
|
|
private String methodName; |
|
|
|
|
|
|
|
/** |
|
|
|
* This constructor extracts the needed information from the given test. |
|
|
|
* @param test Test to analyze |
|
|
|
*/ |
|
|
|
public TestInfos(Test test) { |
|
|
|
className = test.getClass().getName(); |
|
|
|
methodName = test.toString(); |
|
|
|
methodName = methodName.substring(0, methodName.indexOf('(')); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* This String-Representation can directly be used for instantiation of |
|
|
|
* the JUnit testcase. |
|
|
|
* @return the string representation. |
|
|
|
* @see java.lang.Object#toString() |
|
|
|
* @see FailureRecorder#createSuiteMethod() |
|
|
|
*/ |
|
|
|
public String toString() { |
|
|
|
return "new " + className + "(\"" + methodName + "\")"; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* The SortedMap needs comparable elements. |
|
|
|
* @param other the object to compare to. |
|
|
|
* @return the result of the comparison. |
|
|
|
* @see java.lang.Comparable#compareTo(T) |
|
|
|
* @see SortedSet#comparator() |
|
|
|
*/ |
|
|
|
public int compareTo(Object other) { |
|
|
|
if (other instanceof TestInfos) { |
|
|
|
TestInfos otherInfos = (TestInfos) other; |
|
|
|
return toString().compareTo(otherInfos.toString()); |
|
|
|
} else { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ===== BuildListener ===== |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void buildFinished(BuildEvent event) { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void buildStarted(BuildEvent event) { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void messageLogged(BuildEvent event) { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void targetFinished(BuildEvent event) { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void targetStarted(BuildEvent event) { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* The task outside of this JUnitResultFormatter is the <junit> task. So all tests passed |
|
|
|
* and we could create the new java class. |
|
|
|
* @param event not used |
|
|
|
* @see org.apache.tools.ant.BuildListener#taskFinished(org.apache.tools.ant.BuildEvent) |
|
|
|
*/ |
|
|
|
public void taskFinished(BuildEvent event) { |
|
|
|
if (!failedTests.isEmpty()) { |
|
|
|
writeJavaClass(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Not used |
|
|
|
* {@inheritDoc} |
|
|
|
*/ |
|
|
|
public void taskStarted(BuildEvent event) { |
|
|
|
} |
|
|
|
|
|
|
|
} |