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
This commit is contained in:
Stefan Bodewig 2010-04-30 04:25:04 +00:00
parent 01c387ae3f
commit 95b8daf2a7
10 changed files with 514 additions and 7 deletions

View File

@ -38,6 +38,11 @@
</properties>
<release version="1.2" date="not-released">
<action type="add" issue="49168">
It is now possible to pass references from the build file
containing the &lt:antunit&gt; task to the build files under
test.
</action>
<action type="add">
Add Junit3 and Junit4 adapters allowing to launch AntUnit script from JUnit runner
</action>

View File

@ -34,10 +34,18 @@
<first>David</first>
<last>Jackman</last>
</name>
<name>
<first>Gilles</first>
<last>Scokart</last>
</name>
<name>
<first>Jan</first>
<last>Mat&#232;rne</last>
</name>
<name>
<first>Jason</first>
<last>Trump</last>
</name>
<name>
<first>Kevin</first>
<last>Jackson</last>
@ -62,8 +70,4 @@
<first>Steve</first>
<last>Loughran</last>
</name>
<name>
<first>Gilles</first>
<last>Scokart</last>
</name>
</contributors>

View File

@ -171,6 +171,53 @@ public class LogCapturer {
href="http://ant.apache.org/manual/CoreTypes/propertyset.html">&lt;propertyset&gt;</a>s
can be used to pass properties to the build files under test.</p>
<h4>reference</h4>
<p>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.</p>
<table border="1" cellpadding="2" cellspacing="0">
<tr>
<td valign="top"><b>Attribute</b></td>
<td valign="top"><b>Description</b></td>
<td align="center" valign="top"><b>Required</b></td>
</tr>
<tr>
<td valign="top">refid</td>
<td valign="top">The reference ID to inherit</td>
<td align="center" valign="middle" rowspan="2">Exactly one of these</td>
</tr>
<tr>
<td valign="top">regex</td>
<td valign="top">A regular expression identifying a group of reference IDs to inherit.</td>
</tr>
<tr>
<td valign="top">torefid</td>
<td valign="top">The ID of the reference in the test project. </td>
<td align="center" valign="middle" width="25%">No. If this reference appears inside
a <a href="#referenceset">&lt;referenceset&gt;</a> with a nested mapper element, the mapper
will be used to determine the target reference ID. Otherwise, defaults to the source
reference ID.</td>
</tr>
</table>
<a name="referenceset"/><h4>referenceset</h4>
<p>References can be grouped inside a &lt;referenceset&gt; element for clarity. Additionally, &lt;referenceset&gt;
elements may contain exactly one nested <a href="http://ant.apache.org/manual/CoreTypes/mapper.html">&lt;mapper&gt;</a>
element, which defines how source reference IDs are translated into target reference IDs. For example,
<pre>
&lt;au:antunit&gt;
&lt;referenceset&gt;
&lt;reference refid="first.ref"/&gt;
&lt;reference refid="second.ref"/&gt;
&lt;mapper type="regexp" from="(.*)\.ref" to="mapped.ref.\1"/&gt;
&lt;/referenceset&gt;
&lt;/au:antunit&gt;
</pre>
</p>
<h3>Examples</h3>
<p>This build file snippet (from src/etc/testcases/antunit/base.xml)</p>

View File

@ -86,7 +86,56 @@ under the License.
<au:plainlistener />
</au:antunit>
</target>
<target name="testReference">
<path id="inherited.ref" path="/some:/path:/value"/>
<path id="uninherited.ref" path="some:other:value"/>
<au:antunit>
<file file="antunit/reference.xml" />
<au:plainlistener />
<reference refid="inherited.ref"/>
</au:antunit>
</target>
<target name="testReferenceRegex">
<path id="first.ref" path="/first"/>
<path id="second.ref" path="/second"/>
<path id="third.missing" path="ignored"/>
<au:antunit>
<file file="antunit/reference-regex.xml" />
<au:plainlistener />
<reference regex=".*\.ref"/>
</au:antunit>
</target>
<target name="testReferenceSet">
<path id="first.ref" path="/first"/>
<path id="second.ref" path="/second"/>
<path id="third.ref" path="/third"/>
<au:antunit>
<file file="antunit/reference-set.xml" />
<au:plainlistener />
<referenceSet>
<reference refid="first.ref"/>
<reference refid="third.ref"/>
</referenceSet>
</au:antunit>
</target>
<target name="testReferenceMapper">
<path id="first.ref" path="/first"/>
<path id="second.ref" path="/second"/>
<au:antunit>
<file file="antunit/reference-mapper.xml" />
<au:plainlistener />
<referenceSet>
<reference refid="first.ref"/>
<reference refid="second.ref"/>
<mapper type="regexp" from="(.*)\.ref" to="\1.mapped"/>
</referenceSet>
</au:antunit>
</target>
<property name="reportsdir" location="../../../build/reports"/>
<target name="antunit-dir">
<mkdir dir="${reportsdir}"/>

View File

@ -0,0 +1,37 @@
<?xml version="1.0"?>
<!--
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.
-->
<project name="reference-mapper-test" basedir="." default="all"
xmlns:au="antlib:org.apache.ant.antunit">
<target name="all">
<fail>Not a self-contained build file</fail>
</target>
<target name="testReferenceMapper">
<property name="ref.one" refid="first.mapped"/>
<au:assertEquals expected="${file.separator}first" actual="${ref.one}" />
<property name="ref.two" refid="second.mapped"/>
<au:assertEquals expected="${file.separator}second" actual="${ref.two}" />
</target>
</project>

View File

@ -0,0 +1,42 @@
<?xml version="1.0"?>
<!--
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.
-->
<project name="reference-regex-test" basedir="." default="all"
xmlns:au="antlib:org.apache.ant.antunit">
<target name="all">
<fail>Not a self-contained build file</fail>
</target>
<target name="testReferenceRegex">
<property name="ref.one" refid="first.ref"/>
<au:assertEquals expected="${file.separator}first" actual="${ref.one}" />
<property name="ref.two" refid="second.ref"/>
<au:assertEquals expected="${file.separator}second" actual="${ref.two}" />
<condition property="uninherited.is.set" value="true">
<isreference refid="third.missing"/>
</condition>
<fail message="Uninherited reference should not be visible" if="uninherited.is.set"/>
</target>
</project>

View File

@ -0,0 +1,42 @@
<?xml version="1.0"?>
<!--
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.
-->
<project name="reference-set-test" basedir="." default="all"
xmlns:au="antlib:org.apache.ant.antunit">
<target name="all">
<fail>Not a self-contained build file</fail>
</target>
<target name="testReferenceSet">
<property name="ref.one" refid="first.ref"/>
<au:assertEquals expected="${file.separator}first" actual="${ref.one}" />
<property name="ref.three" refid="third.ref"/>
<au:assertEquals expected="${file.separator}third" actual="${ref.three}" />
<condition property="uninherited.is.set" value="true">
<isreference refid="second.ref"/>
</condition>
<fail message="Uninherited reference should not be visible" if="uninherited.is.set"/>
</target>
</project>

View File

@ -0,0 +1,39 @@
<?xml version="1.0"?>
<!--
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.
-->
<project name="reference-test" basedir="." default="all"
xmlns:au="antlib:org.apache.ant.antunit">
<target name="all">
<fail>Not a self-contained build file</fail>
</target>
<target name="testReference">
<property name="ref.string" refid="inherited.ref"/>
<au:assertEquals expected="${file.separator}some${path.separator}${file.separator}path${path.separator}${file.separator}value" actual="${ref.string}" />
<condition property="uninherited.is.set" value="true">
<isreference refid="uninherited.ref"/>
</condition>
<fail message="Uninherited reference should not be visible" if="uninherited.is.set"/>
</target>
</project>

View File

@ -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.
*
* <p>If we cannot clone it, copy the referenced object itself and
* keep our fingers crossed.</p>
* @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 <code>dest</code> any reference IDs in
* <code>src</code> 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.
*/

View File

@ -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");