From 5220af86b58156d2a960ac5ed9778a9f68e46e8b Mon Sep 17 00:00:00 2001 From: Scokart Gilles Date: Thu, 12 Feb 2009 22:21:28 +0000 Subject: [PATCH] Add Junit3 and Junit4 adapters allowing to launch AntUnit script from JUnit runner (I still have some weakness to fix in the junit4 runner) git-svn-id: https://svn.apache.org/repos/asf/ant/antlibs/antunit/trunk@743906 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 3 + changes.xml | 3 + contributors.xml | 4 + src/etc/testcases/antunit/junit.xml | 59 ++++++ .../apache/ant/antunit/ProjectFactory.java | 2 +- .../ant/antunit/junit3/AntUnitSuite.java | 168 ++++++++++++++++++ .../ant/antunit/junit3/AntUnitTestCase.java | 133 ++++++++++++++ .../junit3/JunitNotificationAdapter.java | 69 +++++++ .../junit3/MultiProjectDemuxOutputStream.java | 72 ++++++++ .../antunit/junit4/AntUnitSuiteRunner.java | 75 ++++++++ .../antunit/junit4/AntUnitTestCaseRunner.java | 77 ++++++++ .../ant/antunit/junit3/AntUnitSuiteTest.java | 87 +++++++++ .../antunit/junit3/AntUnitTestCaseTest.java | 71 ++++++++ .../antunit/junit3/EatYourOwnDogFoodTest.java | 22 +++ .../ant/antunit/junit4/AntUnitSuiteTest.java | 110 ++++++++++++ 15 files changed, 954 insertions(+), 1 deletion(-) create mode 100644 src/etc/testcases/antunit/junit.xml create mode 100644 src/main/org/apache/ant/antunit/junit3/AntUnitSuite.java create mode 100644 src/main/org/apache/ant/antunit/junit3/AntUnitTestCase.java create mode 100644 src/main/org/apache/ant/antunit/junit3/JunitNotificationAdapter.java create mode 100644 src/main/org/apache/ant/antunit/junit3/MultiProjectDemuxOutputStream.java create mode 100644 src/main/org/apache/ant/antunit/junit4/AntUnitSuiteRunner.java create mode 100644 src/main/org/apache/ant/antunit/junit4/AntUnitTestCaseRunner.java create mode 100644 src/tests/junit/org/apache/ant/antunit/junit3/AntUnitSuiteTest.java create mode 100644 src/tests/junit/org/apache/ant/antunit/junit3/AntUnitTestCaseTest.java create mode 100644 src/tests/junit/org/apache/ant/antunit/junit3/EatYourOwnDogFoodTest.java create mode 100644 src/tests/junit/org/apache/ant/antunit/junit4/AntUnitSuiteTest.java diff --git a/build.xml b/build.xml index 55dc355..d05b528 100644 --- a/build.xml +++ b/build.xml @@ -25,5 +25,8 @@ under the License. + + + diff --git a/changes.xml b/changes.xml index 4cb282d..6d43760 100644 --- a/changes.xml +++ b/changes.xml @@ -38,6 +38,9 @@ + + Add Junit3 and Junit4 adapters allowing to launch AntUnit script from JUnit runner + expectfailure report the original build exception chained when failing diff --git a/contributors.xml b/contributors.xml index aec08d7..4d2993b 100644 --- a/contributors.xml +++ b/contributors.xml @@ -62,4 +62,8 @@ Steve Loughran + + Gilles + Scokart + diff --git a/src/etc/testcases/antunit/junit.xml b/src/etc/testcases/antunit/junit.xml new file mode 100644 index 0000000..957c2ab --- /dev/null +++ b/src/etc/testcases/antunit/junit.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/org/apache/ant/antunit/ProjectFactory.java b/src/main/org/apache/ant/antunit/ProjectFactory.java index a056c76..ebf7a5f 100644 --- a/src/main/org/apache/ant/antunit/ProjectFactory.java +++ b/src/main/org/apache/ant/antunit/ProjectFactory.java @@ -4,7 +4,7 @@ import org.apache.tools.ant.Project; /** * Provides project instances for AntUnit execution.
- * The aproach to creates a project depends on the context. When invoked from an + * The approach to creates a project depends on the context. When invoked from an * ant project, some elements might be intialized from the parent project. When * executed in a junit runner, a brand new project must be initialized.
* The AntScriptRunner will usually creates multiple project in order to provide test isolation. diff --git a/src/main/org/apache/ant/antunit/junit3/AntUnitSuite.java b/src/main/org/apache/ant/antunit/junit3/AntUnitSuite.java new file mode 100644 index 0000000..9b7f13c --- /dev/null +++ b/src/main/org/apache/ant/antunit/junit3/AntUnitSuite.java @@ -0,0 +1,168 @@ +/* + * 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.ant.antunit.junit3; + +import java.io.File; +import java.io.PrintStream; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; + +import junit.framework.Test; +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.DefaultLogger; +import org.apache.tools.ant.MagicNames; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectHelper; + +/** + * A JUnit 3 TestSuite that group a suite of AntUnit targets coming from an ant + * script. + */ +public class AntUnitSuite extends TestSuite { + + private final AntUnitScriptRunner antScriptRunner; + private final MultiProjectDemuxOutputStream stderr; + private final MultiProjectDemuxOutputStream stdout; + + /** + * Create a JUnit TestSuite that when executed will run the given ant + * script.
+ * Note that it is the responsibility of the caller to give the correct + * File reference. Namely, if the File is a relative file, it will + * be resolve relatively to the execution directory (which might be + * different different from the project root directory). + * + * @param scriptFile + * AntUnit script file + * @param rootClass + * 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); + stdout = new MultiProjectDemuxOutputStream(antScriptRunner, false); + stderr = new MultiProjectDemuxOutputStream(antScriptRunner, true); + setName(antScriptRunner.getName() + "[" + scriptFile + "]"); + List testTargets = antScriptRunner.getTestTartgets(); + for (Iterator it = testTargets.iterator(); it.hasNext();) { + String target = (String) it.next(); + AntUnitTestCase tc = new AntUnitTestCase(this, scriptFile, target); + addTest(tc); + } + } + + /** + * @Override Run the full AntUnit suite + */ + public void run(TestResult testResult) { + List testTartgets = antScriptRunner.getTestTartgets(); + runInContainer(testTartgets, testResult, tests()); + } + + /** + * @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); + } + + /** + * Execute the test suite in a 'container' similar to the ant 'container'. + * When ant executes a project it redirect the input and the output. In this + * context we will only redirect output (unit test are not supposed to be + * interactive) + * + * @param targetList + * The list of test target to execute + * @param result + * The JUnit3 TestResult receiving result notification + * @param tests + * The JUnit3 Test classes instances to use in the notification. + */ + private void runInContainer(List targetList, TestResult result, + Enumeration/**/tests) { + JUnitNotificationAdapter notifier = new JUnitNotificationAdapter( + result, tests); + PrintStream savedErr = System.err; + PrintStream savedOut = System.out; + try { + System.setOut(new PrintStream(stdout)); + System.setErr(new PrintStream(stderr)); + antScriptRunner.runSuite(targetList, notifier); + } finally { + System.setOut(savedOut); + System.setErr(savedErr); + } + } + + /** + * The antscript project factory that creates projects in a junit context. + */ + private static class MyProjectFactory implements ProjectFactory { + + private final File scriptFile; + private final PrintStream realStdErr = System.err; + private final PrintStream realStdOut = System.out; + + public MyProjectFactory(File scriptFile) { + this.scriptFile = scriptFile; + } + + public Project createProject() { + ProjectHelper prjHelper = ProjectHelper.getProjectHelper(); + Project prj = new Project(); + DefaultLogger logger = new DefaultLogger(); + logger.setMessageOutputLevel(Project.MSG_INFO); + logger.setErrorPrintStream(realStdErr); + logger.setOutputPrintStream(realStdOut); + prj.addBuildListener(logger); + String absolutePath = scriptFile.getAbsolutePath(); + prj.setUserProperty(MagicNames.ANT_FILE, absolutePath); + prj.addReference(ProjectHelper.PROJECTHELPER_REFERENCE, prjHelper); + prj.init(); + prjHelper.parse(prj, scriptFile); + return prj; + } + } + +} diff --git a/src/main/org/apache/ant/antunit/junit3/AntUnitTestCase.java b/src/main/org/apache/ant/antunit/junit3/AntUnitTestCase.java new file mode 100644 index 0000000..7aa4888 --- /dev/null +++ b/src/main/org/apache/ant/antunit/junit3/AntUnitTestCase.java @@ -0,0 +1,133 @@ +/* + * 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.ant.antunit.junit3; + +import java.io.File; + +import junit.framework.TestCase; +import junit.framework.TestResult; + +/** + * JUnit TestCase that will executes a single AntUnit target. This class is not + * supposed to be used directly.
+ * It is public only because junit must access it as a public. + */ +public class AntUnitTestCase extends TestCase { + // We have to extends TestCase, and not implements Test because otherwise + // JUnit4 will derive the Description composing the suite description from + // this className only (AntUnitTestCase), and not from the name. + // However, during execution it use the right Description (base on the + // toString) + + /** + * AntUnitSuite that contains this AntUnitTestCase. Execution is done via + * this suite + */ + private final AntUnitSuite suite; + + /** + * The test target + */ + private final String target; + + /** + * Prepare an AntUnitTestCase that will be executed alone. This constructor + * is typically used by a junit 3 runner that will reexecute a specific + * test.
+ * The execution of this test will be embed in a suiteSetUp and + * suiteTearDown. + * @param name The name of the AntUnitTestCase, normally obtained from a + * previous execution. + */ + 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 + } + + /** + * Prepare an AntUnitTestCase that will be executed in a suite. It is the + * suite that prepare the antScriptRunner and the JUnitExcutionPlatform. It + * is the responsibility of the suite to execute the suiteSetUp and the + * suiteTearDown. + * + * @param target + * @param antScriptRunner + * @param executionEnv + */ + public AntUnitTestCase(AntUnitSuite suite, File scriptFile, String target) { + // The name can be reused by eclipse when running a single test + super(new TestCaseName(scriptFile, target).getName()); + this.target = target; + this.suite = suite; + } + + /** Get the AntUnit test target name */ + public String getTarget() { + return target; + } + + /** @overwrite */ + public void run(TestResult result) { + suite.runTest(this, result); + } + + /** + * 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, + * so that the name uniquely identify the TestCase, and a TestCase can be + * executed from its name. + */ + static class TestCaseName { + private final String name; + private final File script; + private final String target; + + public TestCaseName(String name) { + this.name = name; + this.target = name.substring(0, name.indexOf(' ')); + String filename = name.substring(name.indexOf(' ') + 2, name + .length() - 1); + this.script = new File(filename); + } + + public TestCaseName(File script, String target) { + this.script = script; + this.target = target; + this.name = target + " [" + script + "]"; + } + + public String getName() { + return name; + } + + public File getScript() { + return script; + } + + public String getTarget() { + return target; + } + } + +} diff --git a/src/main/org/apache/ant/antunit/junit3/JunitNotificationAdapter.java b/src/main/org/apache/ant/antunit/junit3/JunitNotificationAdapter.java new file mode 100644 index 0000000..1c315c1 --- /dev/null +++ b/src/main/org/apache/ant/antunit/junit3/JunitNotificationAdapter.java @@ -0,0 +1,69 @@ +/* + * 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.ant.antunit.junit3; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.apache.ant.antunit.AntUnitExecutionNotifier; +import org.apache.ant.antunit.AssertionFailedException; + +/** + * Adapt AntUnitExecutionNotifier events into JUnit3 TestResult events + */ +class JUnitNotificationAdapter implements AntUnitExecutionNotifier { + + private final TestResult junitTestResult; + private Map testByTarget = new HashMap(); + + public JUnitNotificationAdapter(TestResult testResult, Enumeration tests) { + this.junitTestResult = testResult; + while(tests.hasMoreElements()) { + AntUnitTestCase test = (AntUnitTestCase) tests.nextElement(); + testByTarget.put(test.getTarget(), test); + } + } + + public void fireStartTest(String targetName) { + //TODO : if it is null, eclipse stop the unit test (add a unit test) + junitTestResult.startTest((Test) testByTarget.get(targetName)); + } + + public void fireEndTest(String targetName) { + junitTestResult.endTest((Test) testByTarget.get(targetName)); + } + + public void fireError(String targetName, Throwable t) { + junitTestResult.addError((Test) testByTarget.get(targetName), t); + } + + public void fireFail(String targetName, AssertionFailedException ae) { + //I don't see how to transform the AntUnit assertion exception into + //junit assertion exception (we would loose the stack trace). + //So failures will be reported as errors + junitTestResult.addError((Test) testByTarget.get(targetName), ae); + } + +} diff --git a/src/main/org/apache/ant/antunit/junit3/MultiProjectDemuxOutputStream.java b/src/main/org/apache/ant/antunit/junit3/MultiProjectDemuxOutputStream.java new file mode 100644 index 0000000..5bb5db2 --- /dev/null +++ b/src/main/org/apache/ant/antunit/junit3/MultiProjectDemuxOutputStream.java @@ -0,0 +1,72 @@ +/* + * 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.ant.antunit.junit3; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.ant.antunit.AntUnitScriptRunner; +import org.apache.tools.ant.DemuxOutputStream; +import org.apache.tools.ant.Project; + + +/** + * Forward stdout or stderr operation to the current antunit project. + */ +class MultiProjectDemuxOutputStream extends OutputStream { + + private final AntUnitScriptRunner scriptRunner; + + private Project lastProject; + private DemuxOutputStream lastDemuxOutputStream = null; + + private final boolean isErrorStream; + + public MultiProjectDemuxOutputStream(AntUnitScriptRunner scriptRunner, boolean isErrorStream) { + this.scriptRunner = scriptRunner; + this.isErrorStream = isErrorStream; + } + + + private DemuxOutputStream getDemuxOutputStream() { + if (lastProject != scriptRunner.getCurrentProject()) { + lastProject = scriptRunner.getCurrentProject(); + lastDemuxOutputStream = new DemuxOutputStream(lastProject,isErrorStream); + } + return lastDemuxOutputStream; + } + + public void write(int b) throws IOException { + getDemuxOutputStream().write(b); + } + + public void write(byte[] b, int off, int len) throws IOException { + getDemuxOutputStream().write(b, off, len); + } + + public void close() throws IOException { + getDemuxOutputStream().close(); + } + + public void flush() throws IOException { + getDemuxOutputStream().flush(); + } +} diff --git a/src/main/org/apache/ant/antunit/junit4/AntUnitSuiteRunner.java b/src/main/org/apache/ant/antunit/junit4/AntUnitSuiteRunner.java new file mode 100644 index 0000000..a59ed47 --- /dev/null +++ b/src/main/org/apache/ant/antunit/junit4/AntUnitSuiteRunner.java @@ -0,0 +1,75 @@ +/* + * 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.ant.antunit.junit4; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Enumeration; + +import org.apache.ant.antunit.junit3.AntUnitSuite; +import org.apache.ant.antunit.junit3.AntUnitTestCase; +import org.junit.internal.runners.CompositeRunner; +import org.junit.internal.runners.InitializationError; + +/** + * JUnit4 Runner to put in a RunWith annotation of the AntUnitSuite when using a + * JUnit4 runner. Using this runner is not mandatory because junit4 is able to + * run junit3 test. However, the test will be faster (TODO make that true :-) ) + * with this Runner. Also, more features are available when this runner is used + * (filtering & sorting) + * TODO Support filtering and sorting + */ +public class AntUnitSuiteRunner extends CompositeRunner { + + private AntUnitSuiteRunner(AntUnitSuite suite, Class junitTestClass) { + super(suite.getName()); + Enumeration tests = suite.tests(); + while (tests.hasMoreElements()) { + AntUnitTestCase tc = (AntUnitTestCase) tests.nextElement(); + add(new AntUnitTestCaseRunner(tc, junitTestClass)); + } + } + + public AntUnitSuiteRunner(Class testCaseClass) throws InitializationError { + this(getJUnit3AntSuite(testCaseClass), testCaseClass); + } + + private static AntUnitSuite getJUnit3AntSuite(Class testCaseClass) + throws InitializationError { + try { + Method suiteMethod = testCaseClass.getMethod("suite", new Class[0]); + if (!Modifier.isStatic(suiteMethod.getModifiers())) { + throw new InitializationError("suite method must be static"); + } + return (AntUnitSuite) suiteMethod.invoke(null, new Object[0]); + } catch (NoSuchMethodException e) { + throw new InitializationError(new Throwable[] { e }); + } catch (IllegalAccessException e) { + throw new InitializationError(new Throwable[] { e }); + } catch (InvocationTargetException e) { + throw new InitializationError(new Throwable[] { e }); + } catch (ClassCastException e) { + throw new InitializationError(new Throwable[] { e }); + } + } + +} diff --git a/src/main/org/apache/ant/antunit/junit4/AntUnitTestCaseRunner.java b/src/main/org/apache/ant/antunit/junit4/AntUnitTestCaseRunner.java new file mode 100644 index 0000000..a494190 --- /dev/null +++ b/src/main/org/apache/ant/antunit/junit4/AntUnitTestCaseRunner.java @@ -0,0 +1,77 @@ +/* + * 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.ant.antunit.junit4; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestListener; +import junit.framework.TestResult; + +import org.apache.ant.antunit.junit3.AntUnitTestCase; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; + + +class AntUnitTestCaseRunner extends Runner { + + private final AntUnitTestCase fTest; + private final Class junitTestClass; + + public AntUnitTestCaseRunner(AntUnitTestCase testCase, Class junitTestClass) { + this.fTest = testCase; + this.junitTestClass = junitTestClass; + } + + public void run(final RunNotifier notifier) { + final Description description = getDescription(); + TestListener testListener = new TestListener() { + // TODO implement directly the mapping from AntUnitExecutionNotifier + // to junit4 RunNotifier + public void endTest(Test test) { + notifier.fireTestFinished(description); + } + + public void startTest(Test test) { + notifier.fireTestStarted(description); + } + + public void addError(Test test, Throwable t) { + Failure failure = new Failure(description, t); + notifier.fireTestFailure(failure); + } + + public void addFailure(Test test, AssertionFailedError t) { + addError(test, t); + } + }; + TestResult result = new TestResult(); + result.addListener(testListener); + fTest.run(result); + } + + public Description getDescription() { + return Description.createTestDescription(junitTestClass, fTest + .getName()); + } + +} diff --git a/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitSuiteTest.java b/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitSuiteTest.java new file mode 100644 index 0000000..020ce1b --- /dev/null +++ b/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitSuiteTest.java @@ -0,0 +1,87 @@ +/* + * 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.ant.antunit.junit3; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Enumeration; + +import junit.framework.TestCase; +import junit.framework.TestResult; + +import org.apache.tools.ant.util.FileUtils; + +public class AntUnitSuiteTest extends TestCase { + + AntUnitSuite suite = new AntUnitSuite(new File( + "src/etc/testcases/antunit/junit.xml")); + File outFile = new File("target/test_output/junit_out.xml"); + + public void testRunSuiteSetUp() throws FileNotFoundException, IOException { + + TestResult testResult = new TestResult(); + suite.run(testResult); + assertTrue(testResult.wasSuccessful()); + + String output = FileUtils.readFully(new FileReader(outFile)); + String EXPECT1 = "suiteSetUp-setUp-test1-tearDown-setUp-test2-tearDown-suiteTearDown"; + String EXPECT2 = "suiteSetUp-setUp-test2-tearDown-setUp-test1-tearDown-suiteTearDown"; + assertTrue("unexted output : " + output, EXPECT1.equals(output) + || EXPECT2.equals(output)); + } + + public void testSuiteName() { + assertTrue("Expected non empty suite name", suite.getName().trim() + .length() > 0); + } + + public void testChildNames() { + assertTrue("Expected more test, received " + suite.testCount(), + suite.testCount() >= 1); + + Enumeration/**/tests = suite.tests(); + StringBuffer testTargets = new StringBuffer(); + while (tests.hasMoreElements()) { + String nextName = tests.nextElement().toString(); + testTargets.append(" ").append(nextName).append(" ,"); + } + + assertTrue("test1 not found in child : " + testTargets, testTargets + .toString().contains(" test1 ")); + } + + public void testSingleTestRunSuiteSetUp() throws Exception { + AntUnitTestCase test1 = (AntUnitTestCase) suite.testAt(0); + if (test1.getTarget().equals("test2")) { + test1 = (AntUnitTestCase) suite.testAt(1); + } + TestResult testResult = new TestResult(); + suite.runTest(test1, testResult); + assertTrue(testResult.wasSuccessful()); + + String output = FileUtils.readFully(new FileReader(outFile)); + assertTrue("unexted output : " + output, + "suiteSetUp-setUp-test1-tearDown-suiteTearDown".equals(output)); + } + +} diff --git a/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitTestCaseTest.java b/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitTestCaseTest.java new file mode 100644 index 0000000..7637f42 --- /dev/null +++ b/src/tests/junit/org/apache/ant/antunit/junit3/AntUnitTestCaseTest.java @@ -0,0 +1,71 @@ +package org.apache.ant.antunit.junit3; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +import org.apache.ant.antunit.junit3.AntUnitTestCase; +import org.apache.tools.ant.util.FileUtils; + +import junit.framework.Assert; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; + +public class AntUnitTestCaseTest extends TestCase { + + File f = new File("src/etc/testcases/antunit/junit.xml"); + String test1Name = new AntUnitTestCase.TestCaseName(f, "test1").getName(); + + File outFile = new File("target/test_output/junit_out.xml"); + + public void testNameParsing() { + AntUnitTestCase.TestCaseName nameObj = new AntUnitTestCase.TestCaseName( + test1Name); + assertEquals(f, nameObj.getScript()); + assertEquals("test1", nameObj.getTarget()); + } + + public void testRunSuiteSetUp() throws FileNotFoundException, IOException { + // When eclipse has to run a specific testCase (user click Run on it), + // an AntUnitTestCase(name) is created, and the run should execute the + // suiteSetup/SuiteTearDown + AntUnitTestCase antUnitTestCase = new AntUnitTestCase(test1Name); + + TestResult testResult = new TestResult(); + antUnitTestCase.run(testResult); + assertTrue(testResult.wasSuccessful()); + + String output = FileUtils.readFully(new FileReader(outFile)); + assertEquals("suiteSetUp-setUp-test1-tearDown-suiteTearDown", output); + } + + private Test startedTest = null; + private Test endedTest = null; + + public void testTestIdentityInNotification() { + // When eclipse has to run a specific testCase (user click Run on it), + // an AntUnitTestCase(name) is created, and this instance must be used + // in the notification (otherwise the test appears twice, once normal + // but never executed, and once with "Unrooted Tests" parent. + TestResult testResultMock = new TestResult() { + public void startTest(Test test) { + // Note that putting an assertion here to fail fatser doesn't + // work because + // exceptions are catched by the runner + startedTest = test; + } + + public void endTest(Test test) { + endedTest = test; + } + }; + + AntUnitTestCase antUnitTestCase = new AntUnitTestCase(test1Name); + antUnitTestCase.run(testResultMock); + + Assert.assertSame(antUnitTestCase, startedTest); + Assert.assertSame(antUnitTestCase, endedTest); + } +} diff --git a/src/tests/junit/org/apache/ant/antunit/junit3/EatYourOwnDogFoodTest.java b/src/tests/junit/org/apache/ant/antunit/junit3/EatYourOwnDogFoodTest.java new file mode 100644 index 0000000..a9e9bab --- /dev/null +++ b/src/tests/junit/org/apache/ant/antunit/junit3/EatYourOwnDogFoodTest.java @@ -0,0 +1,22 @@ +package org.apache.ant.antunit.junit3; + +import java.io.File; + +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.apache.ant.antunit.junit4.AntUnitSuiteRunner; +import org.junit.runner.RunWith; + +/** + * A unit test using the junit3 and junit4 adapter. + */ +@RunWith(AntUnitSuiteRunner.class) +public class EatYourOwnDogFoodTest extends TestCase { + + public static TestSuite suite() { + File script = new File("src/etc/testcases/antunit/java-io.xml"); + return new AntUnitSuite(script, EatYourOwnDogFoodTest.class); + } + +} diff --git a/src/tests/junit/org/apache/ant/antunit/junit4/AntUnitSuiteTest.java b/src/tests/junit/org/apache/ant/antunit/junit4/AntUnitSuiteTest.java new file mode 100644 index 0000000..7bbf1b3 --- /dev/null +++ b/src/tests/junit/org/apache/ant/antunit/junit4/AntUnitSuiteTest.java @@ -0,0 +1,110 @@ +package org.apache.ant.antunit.junit4; + +import java.io.File; +import java.util.ArrayList; + +import junit.framework.TestCase; + +import org.apache.ant.antunit.junit3.AntUnitSuite; +import org.junit.Ignore; +import org.junit.internal.runners.InitializationError; +import org.junit.runner.Description; +import org.junit.runner.notification.RunNotifier; + +public class AntUnitSuiteTest extends TestCase { + + private boolean mockExecutionOK = false; + private String mockExcutionError = ""; + + /** + * When a test is executed, the description used in the notification must be + * equals to the description declared, otherwise the runner is confused (for + * example in eclipse you have all the tests listed twice, but reported only + * once as executed. + * + * @throws InitializationError + */ + public void testDescriptionsReportedInNotifier() throws InitializationError { + final AntUnitSuiteRunner runner = new AntUnitSuiteRunner( + JUnit4AntUnitRunnable.class); + final ArrayList tDescs = runner.getDescription().getChildren(); + + final int TEST_STARTED = 1, TEST_FINISHED = 2; + RunNotifier notifierMock = new RunNotifier() { + Description curTest = null; + + public void fireTestStarted(Description description) { + if (curTest != null) { + mockExcutionError += "Unexpected fireTestStarted(" + + description.getDisplayName() + "\n"; + } + if (!tDescs.contains(description)) { + mockExcutionError += "Unexpected fireTestStarted(" + + description.getDisplayName() + ")\n"; + } + curTest = description; + } + + @Override + public void fireTestFinished(Description description) { + if (curTest == null) { + mockExcutionError += "Unexpected fireTestFinished(" + + description.getDisplayName() + "\n"; + } + if (!curTest.equals(description)) { + mockExcutionError += "Unexpected fireTestFinished(" + + description.getDisplayName() + "); expect " + + curTest.getDisplayName() + "\n"; + } + curTest = null; + mockExecutionOK = true; + } + }; + + runner.run(notifierMock); + assertTrue(mockExcutionError, mockExcutionError.isEmpty()); + assertTrue(mockExecutionOK); + } + + public void testMissingSuiteMethodInitializationError() { + try { + AntUnitSuiteRunner runner = new AntUnitSuiteRunner( + JUnit4AntUnitRunnableWithoutSuiteMethod.class); + fail("InitializationError expected"); + } catch (InitializationError e) { + String msg = e.getCauses().get(0).getMessage(); + assertTrue("Unexpected error : " + msg, msg.contains("suite")); + } + } + + public void testNonStaticSuiteMethodInitializationError() { + try { + AntUnitSuiteRunner runner = new AntUnitSuiteRunner( + JUnit4AntUnitRunnableWithNonStaticSuite.class); + fail("InitializationError expected"); + } catch (InitializationError e) { + String msg = e.getCauses().get(0).getMessage(); + assertTrue("Unexpected error : " + msg, msg.contains("suite")); + assertTrue("Unexpected error : " + msg, msg.contains("static")); + } + } + + public static class JUnit4AntUnitRunnable { + public static AntUnitSuite suite() { + File f = new File("src/etc/testcases/antunit/junit.xml"); + return new AntUnitSuite(f, JUnit4AntUnitRunnable.class); + } + } + + public static class JUnit4AntUnitRunnableWithNonStaticSuite { + public AntUnitSuite suite() { + File f = new File("src/etc/testcases/antunit/junit.xml"); + return new AntUnitSuite(f, + JUnit4AntUnitRunnableWithNonStaticSuite.class); + } + } + + public static class JUnit4AntUnitRunnableWithoutSuiteMethod { + } + +}