From f5e36a2c4e471ca7877ff25c1cd806ab4550c4d4 Mon Sep 17 00:00:00 2001 From: Scokart Gilles Date: Sat, 14 Feb 2009 10:32:46 +0000 Subject: [PATCH] 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 --- .../ant/antunit/AntUnitScriptRunner.java | 6 +- .../apache/ant/antunit/ProjectFactory.java | 4 +- .../ant/antunit/junit3/AntUnitSuite.java | 87 ++++++++++++++----- .../ant/antunit/junit3/AntUnitTestCase.java | 56 ++++++++++-- .../antunit/junit4/AntUnitSuiteRunner.java | 3 + .../ant/antunit/junit3/AntUnitSuiteTest.java | 19 +++- .../antunit/junit3/AntUnitTestCaseTest.java | 39 ++++++++- 7 files changed, 176 insertions(+), 38 deletions(-) diff --git a/src/main/org/apache/ant/antunit/AntUnitScriptRunner.java b/src/main/org/apache/ant/antunit/AntUnitScriptRunner.java index 3b9fe80..316755c 100644 --- a/src/main/org/apache/ant/antunit/AntUnitScriptRunner.java +++ b/src/main/org/apache/ant/antunit/AntUnitScriptRunner.java @@ -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(); diff --git a/src/main/org/apache/ant/antunit/ProjectFactory.java b/src/main/org/apache/ant/antunit/ProjectFactory.java index ebf7a5f..9cf241e 100644 --- a/src/main/org/apache/ant/antunit/ProjectFactory.java +++ b/src/main/org/apache/ant/antunit/ProjectFactory.java @@ -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; } diff --git a/src/main/org/apache/ant/antunit/junit3/AntUnitSuite.java b/src/main/org/apache/ant/antunit/junit3/AntUnitSuite.java index 9b7f13c..337fe7b 100644 --- a/src/main/org/apache/ant/antunit/junit3/AntUnitSuite.java +++ b/src/main/org/apache/ant/antunit/junit3/AntUnitSuite.java @@ -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/**/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 { diff --git a/src/main/org/apache/ant/antunit/junit3/AntUnitTestCase.java b/src/main/org/apache/ant/antunit/junit3/AntUnitTestCase.java index 7aa4888..346b040 100644 --- a/src/main/org/apache/ant/antunit/junit3/AntUnitTestCase.java +++ b/src/main/org/apache/ant/antunit/junit3/AntUnitTestCase.java @@ -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, diff --git a/src/main/org/apache/ant/antunit/junit4/AntUnitSuiteRunner.java b/src/main/org/apache/ant/antunit/junit4/AntUnitSuiteRunner.java index a59ed47..5f7e597 100644 --- a/src/main/org/apache/ant/antunit/junit4/AntUnitSuiteRunner.java +++ b/src/main/org/apache/ant/antunit/junit4/AntUnitSuiteRunner.java @@ -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)); } diff --git a/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitSuiteTest.java b/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitSuiteTest.java index 020ce1b..091e2b6 100644 --- a/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitSuiteTest.java +++ b/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitSuiteTest.java @@ -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 } diff --git a/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitTestCaseTest.java b/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitTestCaseTest.java index 6e51632..99b24be 100644 --- a/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitTestCaseTest.java +++ b/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitTestCaseTest.java @@ -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")); + } + }