From 95b8daf2a75c1a28184a1b3ea8ae5e9b20c776cd Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Fri, 30 Apr 2010 04:25:04 +0000 Subject: [PATCH] allow passing references from antunit task to tests. PR 49168. Submitted by Jason Trump git-svn-id: https://svn.apache.org/repos/asf/ant/antlibs/antunit/trunk@939555 13f79535-47bb-0310-9956-ffa450edef68 --- changes.xml | 5 + contributors.xml | 12 +- docs/antunit.html | 47 ++++ src/etc/testcases/antunit.xml | 49 ++++ .../testcases/antunit/reference-mapper.xml | 37 +++ src/etc/testcases/antunit/reference-regex.xml | 42 ++++ src/etc/testcases/antunit/reference-set.xml | 42 ++++ src/etc/testcases/antunit/reference.xml | 39 +++ src/main/org/apache/ant/antunit/AntUnit.java | 232 +++++++++++++++++- .../org/apache/ant/antunit/AntUnitTest.java | 16 ++ 10 files changed, 514 insertions(+), 7 deletions(-) create mode 100644 src/etc/testcases/antunit/reference-mapper.xml create mode 100644 src/etc/testcases/antunit/reference-regex.xml create mode 100644 src/etc/testcases/antunit/reference-set.xml create mode 100644 src/etc/testcases/antunit/reference.xml diff --git a/changes.xml b/changes.xml index 7381cc7..a2e66ef 100644 --- a/changes.xml +++ b/changes.xml @@ -38,6 +38,11 @@ + + It is now possible to pass references from the build file + containing the <:antunit> task to the build files under + test. + Add Junit3 and Junit4 adapters allowing to launch AntUnit script from JUnit runner diff --git a/contributors.xml b/contributors.xml index 4d2993b..b4a4966 100644 --- a/contributors.xml +++ b/contributors.xml @@ -34,10 +34,18 @@ David Jackman + + Gilles + Scokart + Jan Matèrne + + Jason + Trump + Kevin Jackson @@ -62,8 +70,4 @@ Steve Loughran - - Gilles - Scokart - diff --git a/docs/antunit.html b/docs/antunit.html index fd0b146..86ba51b 100644 --- a/docs/antunit.html +++ b/docs/antunit.html @@ -171,6 +171,53 @@ public class LogCapturer { href="http://ant.apache.org/manual/CoreTypes/propertyset.html"><propertyset>s can be used to pass properties to the build files under test.

+

reference

+ +

One or more references may be passed to the antunit script. For example, paths and filesets + can be defined once in the outer build script and passed through to multiple antunit scripts.

+ + + + + + + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
refidThe reference ID to inheritExactly one of these
regexA regular expression identifying a group of reference IDs to inherit.
torefidThe ID of the reference in the test project. No. If this reference appears inside + a <referenceset> with a nested mapper element, the mapper + will be used to determine the target reference ID. Otherwise, defaults to the source + reference ID.
+ +

referenceset

+ +

References can be grouped inside a <referenceset> element for clarity. Additionally, <referenceset> + elements may contain exactly one nested <mapper> + element, which defines how source reference IDs are translated into target reference IDs. For example, + +

+  <au:antunit>
+    <referenceset>
+      <reference refid="first.ref"/>
+      <reference refid="second.ref"/>
+      <mapper type="regexp" from="(.*)\.ref" to="mapped.ref.\1"/>
+    </referenceset>
+  </au:antunit>
+
+

+

Examples

This build file snippet (from src/etc/testcases/antunit/base.xml)

diff --git a/src/etc/testcases/antunit.xml b/src/etc/testcases/antunit.xml index 3b49356..3090ccb 100644 --- a/src/etc/testcases/antunit.xml +++ b/src/etc/testcases/antunit.xml @@ -86,7 +86,56 @@ under the License. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/etc/testcases/antunit/reference-mapper.xml b/src/etc/testcases/antunit/reference-mapper.xml new file mode 100644 index 0000000..269dc74 --- /dev/null +++ b/src/etc/testcases/antunit/reference-mapper.xml @@ -0,0 +1,37 @@ + + + + + + + + Not a self-contained build file + + + + + + + + + + + \ No newline at end of file diff --git a/src/etc/testcases/antunit/reference-regex.xml b/src/etc/testcases/antunit/reference-regex.xml new file mode 100644 index 0000000..498d720 --- /dev/null +++ b/src/etc/testcases/antunit/reference-regex.xml @@ -0,0 +1,42 @@ + + + + + + + + Not a self-contained build file + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/etc/testcases/antunit/reference-set.xml b/src/etc/testcases/antunit/reference-set.xml new file mode 100644 index 0000000..5d7644f --- /dev/null +++ b/src/etc/testcases/antunit/reference-set.xml @@ -0,0 +1,42 @@ + + + + + + + + Not a self-contained build file + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/etc/testcases/antunit/reference.xml b/src/etc/testcases/antunit/reference.xml new file mode 100644 index 0000000..98e9199 --- /dev/null +++ b/src/etc/testcases/antunit/reference.xml @@ -0,0 +1,39 @@ + + + + + + + + Not a self-contained build file + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/org/apache/ant/antunit/AntUnit.java b/src/main/org/apache/ant/antunit/AntUnit.java index 5bcadda..fa652dd 100644 --- a/src/main/org/apache/ant/antunit/AntUnit.java +++ b/src/main/org/apache/ant/antunit/AntUnit.java @@ -22,7 +22,11 @@ package org.apache.ant.antunit; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -32,12 +36,18 @@ import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildListener; import org.apache.tools.ant.MagicNames; import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectComponent; import org.apache.tools.ant.ProjectHelper; import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Ant; +import org.apache.tools.ant.types.Mapper; import org.apache.tools.ant.types.PropertySet; import org.apache.tools.ant.types.ResourceCollection; import org.apache.tools.ant.types.resources.FileResource; import org.apache.tools.ant.types.resources.Union; +import org.apache.tools.ant.util.FileNameMapper; +import org.apache.tools.ant.util.regexp.RegexpMatcher; +import org.apache.tools.ant.util.regexp.RegexpMatcherFactory; /** * Run every target whose name starts with "test" in a set of build files. @@ -73,7 +83,7 @@ public class AntUnit extends Task { */ private Union buildFiles; - + private AntUnitExecutionNotifier notifier = new AntUnitExecutionNotifier() { public void fireEndTest(String targetName) { @@ -92,7 +102,7 @@ public class AntUnit extends Task { AntUnit.this.fireStartTest(targetName); } }; - + /** * The object responsible for the execution of the unit test. * scriptRunner is invoked to executes the targets and keep the @@ -111,6 +121,11 @@ public class AntUnit extends Task { */ private ArrayList propertySets = new ArrayList(); + /** + * Holds references to be inherited by the test project + */ + private ArrayList referenceSets = new ArrayList(); + /** * has a failure occured? */ @@ -159,6 +174,24 @@ public class AntUnit extends Task { propertySets.add(ps); } + /** + * Add a set of inherited references. + */ + public ReferenceSet createReferenceSet() { + ReferenceSet set = new ReferenceSet(); + set.setProject(getProject()); + referenceSets.add(set); + return set; + } + + /** + * Add an inherited reference + */ + public void addReference(Reference reference) { + //wrap in a singleton reference set. + createReferenceSet().addReference(reference); + } + /** * Set the name of a property to set if an error or failure occurs. * @param s the name of the error property. @@ -227,7 +260,7 @@ public class AntUnit extends Task { ProjectFactory prjFactory = new ProjectFactory() { public Project createProject() { return createProjectForFile(f); - } + } }; try { scriptRunner = new AntUnitScriptRunner(prjFactory); @@ -311,6 +344,7 @@ public class AntUnit extends Task { p.initProperties(); p.setInputHandler(getProject().getInputHandler()); getProject().initSubProject(p); + //pass through inherited properties for (Iterator outer = propertySets.iterator(); outer.hasNext(); ) { PropertySet set = (PropertySet) outer.next(); Map props = set.getProperties(); @@ -328,6 +362,16 @@ public class AntUnit extends Task { } } } + + //pass through inherited references. this code is borrowed + //with significant modification from taskdefs.Ant in Ant core. + //unfortunately the only way we can share the code directly + //would be to extend Ant (which might not be a bad idea?) + for (int i = 0; i < referenceSets.size(); ++i) { + ReferenceSet set = (ReferenceSet) referenceSets.get(i); + set.copyReferencesInto(p); + } + p.setUserProperty(MagicNames.ANT_FILE, f.getAbsolutePath()); attachListeners(f, p); @@ -406,6 +450,188 @@ public class AntUnit extends Task { } } + /** + * Defines a collection of inherited {@link Reference references}, + * with an optional nested {@link Mapper} that maps them to new + * reference IDs in the target project. + */ + public static class ReferenceSet extends ProjectComponent { + /** + * references inherited from parent project by antunit scripts + */ + private ArrayList references = new ArrayList(); + /** + * maps source reference ID to target reference ID + */ + private Mapper mapper; + + public void addReference(Reference reference) { + references.add(reference); + } + + public Mapper createMapper() { + if (mapper == null) { + return mapper = new Mapper(getProject()); + } else { + throw new BuildException("Only one mapper element is allowed" + + " per referenceSet", getLocation()); + } + } + + /** configure a single mapper to translate reference IDs */ + public void setMapper(String typeName, String from, String to) { + Mapper mapper = createMapper(); + Mapper.MapperType type = new Mapper.MapperType(); + type.setValue(typeName); + + mapper.setType(type); + mapper.setFrom(from); + mapper.setTo(to); + } + + /** + * Copy all identified references into the target project, + * applying any name mapping required by a nested mapper + * element. + */ + public void copyReferencesInto(Project newProject) { + FileNameMapper mapper = this.mapper == null + ? null : this.mapper.getImplementation(); + HashSet matches = new HashSet(); + Hashtable src = getProject().getReferences(); + + for (Iterator it = references.iterator(); it.hasNext(); ) { + Reference ref = (Reference) it.next(); + + matches.clear(); + ref.addMatchingReferences(src, matches); + + for (Iterator ids = matches.iterator(); ids.hasNext(); ) { + String refid = (String) ids.next(); + String toRefid = ref.getToRefid(); + + //transform the refid with the mapper if necessary + if (mapper != null && toRefid == null) { + String[] mapped = mapper.mapFileName(refid); + if (mapped != null) { + toRefid = mapped[0]; + } + } + if (toRefid == null) { + toRefid = refid; + } + + //clone the reference into the new project + copyReference(refid, newProject, toRefid); + } + } + } + + /** + * Try to clone and reconfigure the object referenced by + * oldkey in the parent project and add it to the new project + * with the key newkey. This protects the parent project from + * modification by the child project. + * + *

If we cannot clone it, copy the referenced object itself and + * keep our fingers crossed.

+ * @param oldKey the reference id in the current project. + * @param newKey the reference id in the new project. + */ + private void copyReference(String oldKey, Project newProject, + String newKey) { + Object orig = getProject().getReference(oldKey); + if (orig == null) { + log("No object referenced by " + oldKey + ". Can't copy to " + + newKey, + Project.MSG_WARN); + return; + } + + Class c = orig.getClass(); + Object copy = orig; + try { + Method cloneM = c.getMethod("clone", new Class[0]); + if (cloneM != null) { + copy = cloneM.invoke(orig, new Object[0]); + log("Adding clone of reference " + oldKey, + Project.MSG_DEBUG); + } + } catch (Exception e) { + // not Clonable + } + + + if (copy instanceof ProjectComponent) { + ((ProjectComponent) copy).setProject(newProject); + } else { + try { + Method setProjectM = + c.getMethod("setProject", new Class[] {Project.class}); + if (setProjectM != null) { + setProjectM.invoke(copy, new Object[] {newProject}); + } + } catch (NoSuchMethodException e) { + // ignore this if the class being referenced does not have + // a set project method. + } catch (Exception e2) { + String msg = "Error setting new project instance for " + + "reference with id " + oldKey; + throw new BuildException(msg, e2, getLocation()); + } + } + newProject.addReference(newKey, copy); + } + + + } + + public static class Reference extends Ant.Reference { + + private String regex; + private RegexpMatcher matcher; + + public String getRegex() { + return regex; + } + + /** + * Set a regular expression to match references. + * @param regex + */ + public void setRegex(String regex) { + this.regex = regex; + RegexpMatcherFactory matchMaker = new RegexpMatcherFactory(); + matcher = matchMaker.newRegexpMatcher(); + matcher.setPattern(regex); + } + + /** + * Add to dest any reference IDs in + * src matching this reference descriptor + * @param src table of references to check + * @param dest set of reference IDs matching this reference pattern + */ + public void addMatchingReferences(Hashtable src, Collection dest) { + String id = getRefId(); + if (id != null) { + if (src.containsKey(id)) { + dest.add(id); + } + } else if (matcher != null) { + for (Iterator it = src.keySet().iterator(); it.hasNext(); ) { + String refid = (String)it.next(); + if (matcher.matches(refid)) { + dest.add(refid); + } + } + } else { + throw new BuildException("either the refid or regex attribute " + + "is required for reference elements"); + } + } + } + /** * Adapts AntUnitListener to BuildListener. */ diff --git a/src/tests/junit/org/apache/ant/antunit/AntUnitTest.java b/src/tests/junit/org/apache/ant/antunit/AntUnitTest.java index dedf9ba..11d5b6f 100644 --- a/src/tests/junit/org/apache/ant/antunit/AntUnitTest.java +++ b/src/tests/junit/org/apache/ant/antunit/AntUnitTest.java @@ -107,7 +107,23 @@ public class AntUnitTest extends BuildFileTest { System.setErr(savedErr); } } + + public void testReference() { + executeTarget("testReference"); + } + public void testReferenceSet() { + executeTarget("testReferenceSet"); + } + + public void testReferenceRegex() { + executeTarget("testReferenceRegex"); + } + + public void testReferenceMapper() { + executeTarget("testReferenceMapper"); + } + public static class HelloWorld { public static void main(String[] args) { System.out.println("HelloWorld");