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.
+
+
+
+ | Attribute |
+ Description |
+ Required |
+
+
+ | refid |
+ The reference ID to inherit |
+ Exactly one of these |
+
+
+ | regex |
+ A regular expression identifying a group of reference IDs to inherit. |
+
+
+ | torefid |
+ The 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");