Add error handling in the initalisation of the project

git-svn-id: https://svn.apache.org/repos/asf/ant/antlibs/antunit/trunk@744468 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Scokart Gilles 2009-02-14 10:32:46 +00:00
parent 9fafac9d17
commit f5e36a2c4e
7 changed files with 176 additions and 38 deletions

View File

@ -97,8 +97,9 @@ public class AntUnitScriptRunner {
* Create a new AntScriptRunner on the given environment.
* @param prjFactory A factory for the ant project that will contains the antunit test to execute.
* The factory might be invoked multiple time in order to provide test isolation.
* @throws BuildException The project can not be parsed
*/
public AntUnitScriptRunner(ProjectFactory prjFactory) {
public AntUnitScriptRunner(ProjectFactory prjFactory) throws BuildException {
this.prjFactory = prjFactory;
Project newProject = getCurrentProject();
Map targets = newProject.getTargets();
@ -119,8 +120,9 @@ public class AntUnitScriptRunner {
/**
* Get the project currently in use. The caller is not allowed to invoke a target or
* do anything that would break the isolation of the test targets.
* @throws BuildException The project can not be parsed
*/
public final Project getCurrentProject() {
public final Project getCurrentProject() throws BuildException {
//Method is final because it is called from the constructor
if (project == null) {
project = prjFactory.createProject();

View File

@ -1,5 +1,6 @@
package org.apache.ant.antunit;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
/**
@ -14,7 +15,8 @@ public interface ProjectFactory {
/**
* Creates a new project instance and configures it according to the execution context.
* @throws BuildException The project can not be created (probably parsed)
*/
public Project createProject();
public Project createProject() throws BuildException;
}

View File

@ -28,11 +28,13 @@ import java.util.Iterator;
import java.util.List;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestResult;
import junit.framework.TestSuite;
import org.apache.ant.antunit.AntUnitScriptRunner;
import org.apache.ant.antunit.ProjectFactory;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.MagicNames;
import org.apache.tools.ant.Project;
@ -47,6 +49,7 @@ public class AntUnitSuite extends TestSuite {
private final AntUnitScriptRunner antScriptRunner;
private final MultiProjectDemuxOutputStream stderr;
private final MultiProjectDemuxOutputStream stdout;
private final Test initializationReportingTest;
/**
* Create a JUnit TestSuite that when executed will run the given ant
@ -62,22 +65,25 @@ public class AntUnitSuite extends TestSuite {
* The test class that creates this suite. This is used to give
* a name to the suite so that an IDE can reexecute this suite.
*/
public AntUnitSuite(final File scriptFile, Class rootClass) {
this(scriptFile);
setName(rootClass.getName());// Allows eclipse to reexecute the test
}
/**
* Constructor used by AntUnitTestCase when a single test case is created.
* The difference with the public constructor is this version doesn't set
* the name.
*/
AntUnitSuite(final File scriptFile) {
MyProjectFactory prjFactory = new MyProjectFactory(scriptFile);
antScriptRunner = new AntUnitScriptRunner(prjFactory);
public AntUnitSuite(File scriptFile, Class rootClass) {
AntUnitScriptRunner createdScriptRunner = null;
try {
MyProjectFactory prjFactory = new MyProjectFactory(scriptFile);
createdScriptRunner = new AntUnitScriptRunner(prjFactory);
} catch (BuildException e) {
antScriptRunner = null;
stdout = null;
stderr = null;
initializationReportingTest = error(e);
addTest(initializationReportingTest);
return;
}
antScriptRunner = createdScriptRunner;
initializationReportingTest = null;
stdout = new MultiProjectDemuxOutputStream(antScriptRunner, false);
stderr = new MultiProjectDemuxOutputStream(antScriptRunner, true);
setName(antScriptRunner.getName() + "[" + scriptFile + "]");
setName(antScriptRunner.getName() + "[" + scriptFile + "]");
setName(rootClass.getName());// Allows eclipse to reexecute the test
List testTargets = antScriptRunner.getTestTartgets();
for (Iterator it = testTargets.iterator(); it.hasNext();) {
String target = (String) it.next();
@ -86,24 +92,58 @@ public class AntUnitSuite extends TestSuite {
}
}
/**
* Constructor used by AntUnitTestCase when a single test case is created.
* The difference with the public constructor is this version doesn't set
* the name.
* @throws BuildException when the file project can not be create (parsed/read)
*/
AntUnitSuite(AntUnitTestCase singleTc , File scriptFile) throws BuildException {
MyProjectFactory prjFactory = new MyProjectFactory(scriptFile);
antScriptRunner = new AntUnitScriptRunner(prjFactory);
//the exception is throwed, and it is up to the AntUnitTestCase to handle it.
initializationReportingTest = null;
stdout = new MultiProjectDemuxOutputStream(antScriptRunner, false);
stderr = new MultiProjectDemuxOutputStream(antScriptRunner, true);
setName(antScriptRunner.getName() + "[" + scriptFile + "]");
addTest(singleTc);
}
/**
* @Override Run the full AntUnit suite
*/
public void run(TestResult testResult) {
List testTartgets = antScriptRunner.getTestTartgets();
runInContainer(testTartgets, testResult, tests());
if (initializationReportingTest!=null) {
initializationReportingTest.run(testResult);
} else {
List testTartgets = antScriptRunner.getTestTartgets();
runInContainer(testTartgets, testResult);
}
}
private static Test error(final BuildException ex) {
return new TestCase("warning") {
protected void runTest() throws BuildException {
throw ex;
}
};
}
/**
* @Override Run a single test target of the AntUnit suite. suiteSetUp,
* setUp, tearDown and suiteTearDown are executed around it.
*/
public void runTest(Test test, TestResult result) {
String targetName = ((AntUnitTestCase) test).getTarget();
List singleTargetList = Collections.singletonList(targetName);
Enumeration singleTestList = Collections.enumeration(Collections
.singletonList(test));
runInContainer(singleTargetList, result, singleTestList);
if (initializationReportingTest!=null) {
initializationReportingTest.run(result);
} else {
String targetName = ((AntUnitTestCase) test).getTarget();
List singleTargetList = Collections.singletonList(targetName);
runInContainer(singleTargetList, result);
}
}
/**
@ -119,10 +159,9 @@ public class AntUnitSuite extends TestSuite {
* @param tests
* The JUnit3 Test classes instances to use in the notification.
*/
private void runInContainer(List targetList, TestResult result,
Enumeration/*<Test>*/tests) {
private void runInContainer(List targetList, TestResult result) {
JUnitNotificationAdapter notifier = new JUnitNotificationAdapter(
result, tests);
result, tests());
PrintStream savedErr = System.err;
PrintStream savedOut = System.out;
try {

View File

@ -22,6 +22,8 @@ package org.apache.ant.antunit.junit3;
import java.io.File;
import org.apache.tools.ant.BuildException;
import junit.framework.TestCase;
import junit.framework.TestResult;
@ -48,6 +50,15 @@ public class AntUnitTestCase extends TestCase {
*/
private final String target;
/**
* Store the exception when the project can not be parsed, but only if this
* class has been created directly by the IDE from its name.
* In case of initialisation problem when the test is build from the suite,
* the problem is handled at the level of the suite (and this object is never
* created)
*/
private final BuildException initialisationException;
/**
* Prepare an AntUnitTestCase that will be executed alone. This constructor
* is typically used by a junit 3 runner that will reexecute a specific
@ -59,10 +70,19 @@ public class AntUnitTestCase extends TestCase {
*/
public AntUnitTestCase(String name) {
super(name);
TestCaseName nameParser = new TestCaseName(name);
target = nameParser.getTarget();
suite = new AntUnitSuite(nameParser.getScript());
// TODO : check that target is in the list
BuildException catchedEx = null;
AntUnitSuite createdSuite = null;
TestCaseName nameParser = new TestCaseName(name);
try {
createdSuite = new AntUnitSuite(this, nameParser.getScript());
} catch (BuildException e) {
catchedEx = e;
}
this.initialisationException = catchedEx;
this.suite = createdSuite;
this.target = nameParser.getTarget();
//There is no need to check here if the target still exists. This check
//will be done during execution, and we will get a very nice error
}
/**
@ -80,6 +100,7 @@ public class AntUnitTestCase extends TestCase {
super(new TestCaseName(scriptFile, target).getName());
this.target = target;
this.suite = suite;
this.initialisationException = null;
}
/** Get the AntUnit test target name */
@ -87,11 +108,34 @@ public class AntUnitTestCase extends TestCase {
return target;
}
/** @overwrite */
/**
* Called by a Junit Runner that want to executes specifically
* this test target.
* This implementation delegates the call to the suite.
* @Overwrite
*/
public void run(TestResult result) {
suite.runTest(this, result);
if (initialisationException==null && suite!=null) {
//normal case, the test is executed from the suite
suite.runTest(this, result);
} else {
//special case, the suite failed to be created
//the execution will be handled by this object
//directly
super.run(result);
}
}
/**
* Normally never used because this object delegates all execution
* to an AntUnitSuite. However, when the suite can not be created
* (because the ant project is invalid), this object is executed
* and just throws the exception.
*/
protected void runTest() throws BuildException {
throw initialisationException;
}
/**
* Handle the serialization and the parsing of the name of a TestCase. The
* name of the TestCase contains the filename of the script and the target,

View File

@ -44,6 +44,9 @@ public class AntUnitSuiteRunner extends CompositeRunner {
super(suite.getName());
Enumeration tests = suite.tests();
while (tests.hasMoreElements()) {
//TODO Handle the the case of FileNotFound.
//In that case the suite contains an error Test and we have
//a ClassCastException instead of a nice & clear error
AntUnitTestCase tc = (AntUnitTestCase) tests.nextElement();
add(new AntUnitTestCaseRunner(tc, junitTestClass));
}

View File

@ -27,6 +27,7 @@ import java.io.IOException;
import java.util.Enumeration;
import junit.framework.TestCase;
import junit.framework.TestFailure;
import junit.framework.TestResult;
import org.apache.tools.ant.util.FileUtils;
@ -34,7 +35,7 @@ import org.apache.tools.ant.util.FileUtils;
public class AntUnitSuiteTest extends TestCase {
AntUnitSuite suite = new AntUnitSuite(new File(
"src/etc/testcases/antunit/junit.xml"));
"src/etc/testcases/antunit/junit.xml"), AntUnitSuiteTest.class);
File outFile = new File("target/test_output/junit_out.xml");
public void testRunSuiteSetUp() throws FileNotFoundException, IOException {
@ -51,8 +52,8 @@ public class AntUnitSuiteTest extends TestCase {
}
public void testSuiteName() {
assertTrue("Expected non empty suite name", suite.getName().trim()
.length() > 0);
assertTrue("Expected non empty suite name",
suite.getName().trim().length() > 0);
}
public void testChildNames() {
@ -84,4 +85,16 @@ public class AntUnitSuiteTest extends TestCase {
"suiteSetUp-setUp-test1-tearDown-suiteTearDown".equals(output));
}
public void testFileNotFound() throws Exception {
suite = new AntUnitSuite(new File("xxxx"), AntUnitSuiteTest.class);
TestResult testResult = new TestResult();
suite.run(testResult);
assertEquals(1 , testResult.errorCount());
TestFailure error = (TestFailure) testResult.errors().nextElement();
assertTrue("Unexpected error : " + error.exceptionMessage(),
error.exceptionMessage().contains("xxxx"));
}
//TODO test missing target error reporting
}

View File

@ -30,12 +30,16 @@ import org.apache.tools.ant.util.FileUtils;
import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestFailure;
import junit.framework.TestResult;
public class AntUnitTestCaseTest extends TestCase {
File f = new File("src/etc/testcases/antunit/junit.xml");
File invalidF = new File("invalidFile");
String test1Name = new AntUnitTestCase.TestCaseName(f, "test1").getName();
String unknownName = new AntUnitTestCase.TestCaseName(f, "unknown").getName();
String nameForInvalidF = new AntUnitTestCase.TestCaseName(invalidF, "x").getName();
File outFile = new File("target/test_output/junit_out.xml");
@ -84,7 +88,38 @@ public class AntUnitTestCaseTest extends TestCase {
AntUnitTestCase antUnitTestCase = new AntUnitTestCase(test1Name);
antUnitTestCase.run(testResultMock);
Assert.assertSame(antUnitTestCase, startedTest);
Assert.assertSame(antUnitTestCase, endedTest);
assertSame(antUnitTestCase, startedTest);
assertSame(antUnitTestCase, endedTest);
}
public void testUnknownTarget() {
//when the antscript has changed (the target has been removed) and the user try
//to rerun this target.
TestResult testResult = new TestResult();
AntUnitTestCase antUnitTestCase = new AntUnitTestCase(unknownName);
antUnitTestCase.run(testResult);
assertEquals(1 , testResult.errorCount());
TestFailure error = (TestFailure) testResult.errors().nextElement();
assertSame(antUnitTestCase, error.failedTest());
assertTrue("Unexpected error : " + error.exceptionMessage(),
error.exceptionMessage().contains("unknown"));
}
public void testInvalidFile() {
//when the ant script has changed (or just disappeared) and the user try
//to rerun this target.
TestResult testResult = new TestResult();
AntUnitTestCase antUnitTestCase = new AntUnitTestCase(nameForInvalidF);
antUnitTestCase.run(testResult);
assertEquals(1 , testResult.errorCount());
TestFailure error = (TestFailure) testResult.errors().nextElement();
assertSame(antUnitTestCase, error.failedTest());
assertTrue("Unexpected error : " + error.exceptionMessage(),
error.exceptionMessage().contains("invalidFile"));
}
}