You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ant.apache.org by jh...@apache.org on 2007/12/20 14:41:26 UTC
svn commit: r605925 - in /ant/sandbox/parallelexecutor: ./ src/ src/main/
src/main/org/ src/main/org/apache/ src/main/org/apache/ant/
src/main/org/apache/ant/parallelexecutor/ src/test/ src/test/org/
src/test/org/apache/ src/test/org/apache/ant/ src/te...
Author: jhm
Date: Thu Dec 20 05:41:22 2007
New Revision: 605925
URL: http://svn.apache.org/viewvc?rev=605925&view=rev
Log:
Add current working version of the parallel executor for backup and discussion.
Current state:
* (seems to) work from command line
* JUnit test failes
Added:
ant/sandbox/parallelexecutor/
ant/sandbox/parallelexecutor/build.xml
ant/sandbox/parallelexecutor/src/
ant/sandbox/parallelexecutor/src/main/
ant/sandbox/parallelexecutor/src/main/org/
ant/sandbox/parallelexecutor/src/main/org/apache/
ant/sandbox/parallelexecutor/src/main/org/apache/ant/
ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/
ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/ParallelExecutor.java
ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/TargetContainer.java
ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/TargetContainerStatus.java
ant/sandbox/parallelexecutor/src/test/
ant/sandbox/parallelexecutor/src/test/org/
ant/sandbox/parallelexecutor/src/test/org/apache/
ant/sandbox/parallelexecutor/src/test/org/apache/ant/
ant/sandbox/parallelexecutor/src/test/org/apache/ant/parallelexecutor/
ant/sandbox/parallelexecutor/src/test/org/apache/ant/parallelexecutor/ParallelExecutorTest.java
ant/sandbox/parallelexecutor/src/testdata/
ant/sandbox/parallelexecutor/src/testdata/double-deps.xml
ant/sandbox/parallelexecutor/src/testdata/failures.xml
ant/sandbox/parallelexecutor/src/testdata/multiple-deps.xml
ant/sandbox/parallelexecutor/src/testdata/simple-dep.xml
ant/sandbox/parallelexecutor/src/testdata/simple.xml
Added: ant/sandbox/parallelexecutor/build.xml
URL: http://svn.apache.org/viewvc/ant/sandbox/parallelexecutor/build.xml?rev=605925&view=auto
==============================================================================
--- ant/sandbox/parallelexecutor/build.xml (added)
+++ ant/sandbox/parallelexecutor/build.xml Thu Dec 20 05:41:22 2007
@@ -0,0 +1,70 @@
+<!--
+ 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="ant-parallelexecutor" default="build">
+ <property name="javac.source" value="1.6"/>
+ <property name="javac.target" value="1.6"/>
+
+ <property name="build.dir" value="build"/>
+ <property name="src.dir" value="src"/>
+ <property name="version" value="1.0"/>
+
+ <property name="main.src.dir" value="${src.dir}/main"/>
+ <property name="junit.src.dir" value="${src.dir}/test"/>
+ <property name="data.src.dir" value="${src.dir}/testdata"/>
+
+ <property name="main.classes.dir" value="${build.dir}/main"/>
+ <property name="junit.classes.dir" value="${build.dir}/test"/>
+ <property name="data.classes.dir" value="${build.dir}/testdata"/>
+
+
+ <target name="clean">
+ <delete dir="${build.dir}"/>
+ </target>
+
+
+ <target name="compile">
+ <mkdir dir="${main.classes.dir}" />
+ <javac srcdir="${main.src.dir}"
+ destdir="${main.classes.dir}"
+ source="${javac.source}"
+ target="${javac.target}"
+ debug="true"
+ />
+ </target>
+
+
+ <target name="jar" depends="compile">
+ <jar destfile="${build.dir}/${ant.project.name}.jar">
+ <fileset dir="${main.classes.dir}"/>
+ <manifest>
+ <attribute name="Built-By" value="${user.name}"/>
+ <attribute name="Extension-name" value="org.apache.ant.parallelexecutor"/>
+ <attribute name="Specification-Title" value="Apache Ant"/>
+ <attribute name="Specification-Version" value="${version}"/>
+ <attribute name="Specification-Vendor" value="Apache Software Foundation"/>
+ <attribute name="Implementation-Title" value="org.apache.ant.parallelexecutor"/>
+ <attribute name="Implementation-Version" value="${version}"/>
+ <attribute name="Implementation-Vendor" value="Apache Software Foundation"/>
+ </manifest>
+ </jar>
+ </target>
+
+
+ <target name="build" depends="jar" />
+
+
+</project>
\ No newline at end of file
Added: ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/ParallelExecutor.java
URL: http://svn.apache.org/viewvc/ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/ParallelExecutor.java?rev=605925&view=auto
==============================================================================
--- ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/ParallelExecutor.java (added)
+++ ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/ParallelExecutor.java Thu Dec 20 05:41:22 2007
@@ -0,0 +1,255 @@
+/*
+ * 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.parallelexecutor;
+
+import static org.apache.ant.parallelexecutor.TargetContainerStatus.WAITING;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Executor;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Target;
+
+/**
+ * <p>This executors parallelizes the exution of Ant targets.
+ * Each target will run in its own thread. That thread will be started
+ * if all dependend targets are finished, the if/unless attributes are
+ * evaluated and no dependend target failed. When a TargetContainer finishes
+ * it calls this Executors <tt>targetFinished</tt> so that the Executor could
+ * restart all the waiting threads.</p>
+ * <p>This executor is used via Ants magic property <tt>ant.executor.class</tt></p>
+ * <pre>
+ * ant
+ * -Dant.executor.class=org.apache.ant.parallelexecutor.ParallelExecutor
+ * -lib pathToJarContainingTheExecutor
+ * </pre>
+ */
+public class ParallelExecutor implements Executor {
+
+ /**
+ * Default value for waiting for shutting down the ExecutorService.
+ * @see ExecutorService#awaitTermination(long, TimeUnit)
+ * @see #EXECUTOR_SERVICE_SHUTDOWN_TIME_UNIT
+ */
+ public static final long EXECUTOR_SERVICE_SHUTDOWN_TIME_VALUE = 10;
+
+ /**
+ * TimeUnit for the shutdown time.
+ */
+ public static final TimeUnit EXECUTOR_SERVICE_SHUTDOWN_TIME_UNIT = TimeUnit.MINUTES;
+
+
+ /**
+ * Targets which should run, wrapped by TargetContainers for threading and monitoring.
+ */
+ private Set<TargetContainer> targetsToProcess;
+
+
+ /**
+ * ExecutorService for Thread-creation.
+ */
+ private ExecutorService executorService;
+
+
+ /**
+ * Entry-point defined in the Executor interface.
+ * Initializes this Executor and starts the threads.
+ * @param project Ant-project which hold the targets
+ * @param targetNames target names as specified on the command line to execute
+ * @see org.apache.tools.ant.Executor#executeTargets(org.apache.tools.ant.Project, java.lang.String[])
+ */
+ @Override
+ public void executeTargets(Project project, String[] targetNames) throws BuildException {
+ targetsToProcess = getTargetsToProcess(project, targetNames);
+ executorService = java.util.concurrent.Executors.newCachedThreadPool();
+ startWaitingContainers();
+ while (unfinishedTargets()) {
+ sleep(50);
+ }
+ sleep(100);
+ buildFinished();
+ }
+
+
+ /**
+ * Lets the current thread sleep for a given time.
+ * @param timeMS sleep time in milliseconds
+ */
+ protected void sleep(long timeMS) {
+ try {
+ Thread.sleep(timeMS);
+ } catch (InterruptedException e) {
+ // no-op
+ }
+ }
+
+
+ /**
+ * Initializes the list of TargetContainers with all targets which should be started.
+ * @param project project containing the targets
+ * @param targetNames list of the targets to start
+ * @return list of TargetContainers for these targets
+ */
+ private Set<TargetContainer> getTargetsToProcess(Project project, String[] targetNames) {
+ Set<TargetContainer> rv = new HashSet<TargetContainer>();
+ // iterate over all required targets - including dependent targets
+ for (Object targetName : project.topoSort(targetNames, project.getTargets(), false)) {
+ // must be a String - Objects are not equal while iterating
+ String key = targetName.toString();
+ Target target = (Target)project.getTargets().get(key);
+ TargetContainer container = new TargetContainer(target, this);
+ rv.add(container);
+ }
+ return rv;
+ }
+
+
+ /**
+ * not used
+ * @see org.apache.tools.ant.Executor#getSubProjectExecutor()
+ */
+ public Executor getSubProjectExecutor() {
+ return null;
+ }
+
+
+ /**
+ * Starts all waiting TargetContainers.
+ * @return <tt>true</tt> if one or more containers were be started, <tt>false</tt> otherwise.
+ */
+ private boolean startWaitingContainers() {
+ boolean hasStartedAContainer = false;
+ for (TargetContainer container : targetsToProcess) {
+ if (container.getCurrentStatus() == WAITING) {
+ container.start();
+ executorService.execute(container);
+ hasStartedAContainer = true;
+ }
+ }
+ return hasStartedAContainer;
+ }
+
+
+ /**
+ * Call-back method for finishing TargetContainers.
+ *
+ * @param container
+ * TargetContainer which finished.
+ */
+ public void targetFinished(TargetContainer container) {
+ if (unfinishedTargets()) {
+ startWaitingContainers();
+ }
+ }
+
+
+ /**
+ * Checks if there are targets which haven't finished.
+ * @return result of the check
+ */
+ protected synchronized boolean unfinishedTargets() {
+ for (TargetContainer container : targetsToProcess) {
+ if (!container.getCurrentStatus().hasFinished()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Collects all the results from the different targets.
+ */
+ private void buildFinished() {
+ // we have already checked that all started thread have completed
+ executorService.shutdown();
+
+ // Checks the targets for BuildExceptions and their state.
+ List<BuildException> thrownExceptions = new ArrayList<BuildException>();
+ for (TargetContainer container : targetsToProcess) {
+ if (container.getBuildExeption()!=null) {
+ thrownExceptions.add(container.getBuildExeption());
+ }
+ }
+
+ // throw BuildExceptions if needed
+ throwCaughtExceptions(thrownExceptions);
+ }
+
+
+ /**
+ * Checks the given list of BuildExceptions and <ol>
+ * <li>throws a composite BuildException with the information provided by that list</li>
+ * <li>throws the BuildException if the list does contain only one exception</li>
+ * <li>does not throw any Exception if the list is empty</li>
+ * </ul>
+ * @param thrownExceptions list of caught exceptions
+ */
+ private void throwCaughtExceptions(List<BuildException> thrownExceptions) {
+ if (thrownExceptions.isEmpty()) {
+ return;
+ }
+ if (thrownExceptions.size() == 1) {
+ throw thrownExceptions.get(0);
+ }
+ // Collect all BEs into one new
+ StringBuilder sb = new StringBuilder();
+ sb.append("Multiple BuildExceptions occured:")
+ .append(System.getProperty("line.separator"));
+ for (BuildException be : thrownExceptions) {
+ sb.append("\t")
+ .append(be.getLocation())
+ .append(" : ")
+ .append(be.getMessage())
+ .append(System.getProperty("line.separator"));
+ }
+ throw new BuildException(sb.toString());
+ }
+
+
+ /**
+ * Returns the current status for a given target.
+ * @param depName name of the target
+ * @return status of that target
+ */
+ public TargetContainerStatus getStatus(String depName) {
+ return getContainer(depName).getCurrentStatus();
+ }
+
+
+ /**
+ * Returns a TargetContainer by its name.
+ * @param targetName name of the target to look for
+ * @return the target container wrapping that target
+ */
+ private TargetContainer getContainer(String targetName) {
+ for (TargetContainer container : targetsToProcess) {
+ if (container.getName().equals(targetName)) {
+ return container;
+ }
+ }
+ return null;
+ }
+
+}
Added: ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/TargetContainer.java
URL: http://svn.apache.org/viewvc/ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/TargetContainer.java?rev=605925&view=auto
==============================================================================
--- ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/TargetContainer.java (added)
+++ ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/TargetContainer.java Thu Dec 20 05:41:22 2007
@@ -0,0 +1,183 @@
+/*
+ * 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.parallelexecutor;
+
+import static org.apache.ant.parallelexecutor.TargetContainerStatus.CHECKING;
+import static org.apache.ant.parallelexecutor.TargetContainerStatus.FAILED;
+import static org.apache.ant.parallelexecutor.TargetContainerStatus.PREREQUISITE_FAILED;
+import static org.apache.ant.parallelexecutor.TargetContainerStatus.RUNNING;
+import static org.apache.ant.parallelexecutor.TargetContainerStatus.STARTING;
+import static org.apache.ant.parallelexecutor.TargetContainerStatus.SUCCESSED;
+import static org.apache.ant.parallelexecutor.TargetContainerStatus.WAITING;
+
+import java.util.Enumeration;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Target;
+
+
+/**
+ * The TargetContainer wrapps a target for the use in a multithreaded environment.
+ * It provides the "management methods" needed by the ParallelExecutor.
+ */
+public class TargetContainer implements Runnable {
+
+ /** Current status of the target. */
+ private TargetContainerStatus currentStatus = WAITING;
+
+ /** Wrapped target. */
+ private Target target;
+
+ /** Caught exception if the target throws one. */
+ private BuildException caughtBuildExeption;
+
+ /** The calling ParallelExecutor to inform on finished works. */
+ private ParallelExecutor caller;
+
+
+ /**
+ * Constructor.
+ * @param target target to wrap
+ * @param caller calling exectuor for call back
+ */
+ public TargetContainer(Target target, ParallelExecutor caller) {
+ this.target = target;
+ this.caller = caller;
+ setCurrentStatus(WAITING);
+ }
+
+
+ /**
+ * Gets the current status of the TargetContainer.
+ * @return status
+ */
+ public TargetContainerStatus getCurrentStatus() {
+ return currentStatus;
+ }
+
+
+ /**
+ * Sets the current status of the TargetContainer.
+ * @param currentStatus new status
+ */
+ private void setCurrentStatus(TargetContainerStatus currentStatus) {
+ synchronized (this.currentStatus) {
+ this.currentStatus = currentStatus;
+ }
+ }
+
+
+ /**
+ * Called by the {@link ParallelExecutor} if the target should try
+ * to start.
+ * @see java.lang.Runnable#run()
+ */
+ @Override
+ public void run() {
+ setCurrentStatus(CHECKING);
+ TargetContainerStatus statusDepends = checkDependentTargets();
+ if (statusDepends == SUCCESSED) {
+ if (evaluateIf() && !evaluateUnless()) {
+ setCurrentStatus(RUNNING);
+ try {
+ target.execute();
+ setCurrentStatus(SUCCESSED);
+ } catch (BuildException be) {
+ // Don't handle the be, just store it for handling by the executor.
+ caughtBuildExeption = be;
+ setCurrentStatus(FAILED);
+ }
+ } else {
+ // 'if' or 'unless' attributes failed
+ setCurrentStatus(PREREQUISITE_FAILED);
+ }
+ } else {
+ // Finishing the run method stops the thread. Status here is WAITING or PREFAILED
+ setCurrentStatus(statusDepends);
+ }
+ caller.targetFinished(this);
+ }
+
+
+ /**
+ * Checks the result of the dependend targets.
+ * @return <tt>SUCCESSED</tt> if <b>all</b> targets finished successfully,
+ * <tt>PREREQUISITE_FAILED</tt> if one or more failed or
+ * <tt>WAITING</tt> otherwise.
+ */
+ @SuppressWarnings("unchecked")
+ private TargetContainerStatus checkDependentTargets() {
+ for (Enumeration deps = target.getDependencies(); deps.hasMoreElements();) {
+ TargetContainerStatus status = caller.getStatus((String) deps.nextElement());
+ if (status == FAILED || status == PREREQUISITE_FAILED) {
+ return PREREQUISITE_FAILED;
+ }
+ if (status != SUCCESSED) {
+ return WAITING;
+ }
+ }
+ return SUCCESSED;
+ }
+
+
+ /**
+ * Checks if the property specified as <tt>unless</tt> is set.
+ * @return <tt>true</tt> if set, <tt>false</tt> otherwise
+ */
+ private boolean evaluateUnless() {
+ return target.getProject().getProperty(target.getUnless()) != null;
+ }
+
+
+ /**
+ * Checks if the property specified as <tt>if</tt> is set.
+ * @return <tt>true</tt> if set, <tt>false</tt> otherwise
+ */
+ private boolean evaluateIf() {
+ String ifName = target.getIf();
+ return ifName == null || target.getProject().getProperty(ifName) != null;
+ }
+
+
+ /**
+ * Gets the name of the target.
+ * @return target name
+ * @see Target#getName()
+ */
+ public String getName() {
+ return target.getName();
+ }
+
+
+ /**
+ * Returns the caught exception thrown by the target.
+ * @return the exception or <tt>null</tt>
+ */
+ public BuildException getBuildExeption() {
+ return caughtBuildExeption;
+ }
+
+
+ /**
+ * Marks this container as starting.
+ */
+ public void start(){
+ setCurrentStatus(STARTING);
+ }
+
+}
Added: ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/TargetContainerStatus.java
URL: http://svn.apache.org/viewvc/ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/TargetContainerStatus.java?rev=605925&view=auto
==============================================================================
--- ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/TargetContainerStatus.java (added)
+++ ant/sandbox/parallelexecutor/src/main/org/apache/ant/parallelexecutor/TargetContainerStatus.java Thu Dec 20 05:41:22 2007
@@ -0,0 +1,58 @@
+/*
+ * 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.parallelexecutor;
+
+/**
+ * TargetContainer can be in the here defined states.
+ * The transistion between them is:<pre>
+ * init --> WAITING
+ * WAITING --> STARTING
+ * STARTING --> CHECKING
+ * CHECKING --> RUNNING, PREREQUISITE_FAILED
+ * RUNNING --> SUCCESSED, FAILED
+ * SUCCESSED --> end
+ * FAILED --> end
+ * PREREQUISITE_FAILED --> end
+ * </pre>
+ */
+public enum TargetContainerStatus {
+ /* Waiting for starting by the Executor. */
+ WAITING,
+ /* This state is directly set by the executor for marking as 'not-waiting'. */
+ STARTING,
+ /* Checking prerequisites (depends, if, unless). */
+ CHECKING,
+ /* Target is currently running. */
+ RUNNING,
+ /* Target has finished without any error. */
+ SUCCESSED,
+ /* Target has thrown a BuildException. */
+ FAILED,
+ /* Target could not run because a prerequisite has not succeeded. */
+ PREREQUISITE_FAILED;
+
+
+ /**
+ * Utility method for easier access to a set of states.
+ * <pre>Finished = [SUCCESSED|FAILED|PREREQUISITE_FAILED]</pre>
+ * @return computed state
+ */
+ public boolean hasFinished() {
+ return this == SUCCESSED || this == FAILED || this == PREREQUISITE_FAILED;
+ }
+}
Added: ant/sandbox/parallelexecutor/src/test/org/apache/ant/parallelexecutor/ParallelExecutorTest.java
URL: http://svn.apache.org/viewvc/ant/sandbox/parallelexecutor/src/test/org/apache/ant/parallelexecutor/ParallelExecutorTest.java?rev=605925&view=auto
==============================================================================
--- ant/sandbox/parallelexecutor/src/test/org/apache/ant/parallelexecutor/ParallelExecutorTest.java (added)
+++ ant/sandbox/parallelexecutor/src/test/org/apache/ant/parallelexecutor/ParallelExecutorTest.java Thu Dec 20 05:41:22 2007
@@ -0,0 +1,142 @@
+/*
+ * 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.parallelexecutor;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+import junit.framework.TestCase;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+
+/**
+ * Test class for the ParallelExecutor.
+ */
+public class ParallelExecutorTest extends TestCase {
+
+ /**
+ * Runs the project and returns the order of the executed targets.
+ * @param pathToBuildfile relative path to the buildfile
+ * @param arguments additional arguments to pass to Ant
+ * @return the configured Ant project
+ */
+ @SuppressWarnings("unchecked")
+ protected List<String> runProject(String pathToBuildfile, String targets) {
+ Vector targetNames = new Vector();
+ for (String target : targets.split(" ")) {
+ targetNames.add(target);
+ }
+ Project project = new Project();
+ project.init();
+ File antFile = new File(System.getProperty("root"), pathToBuildfile);
+ project.setUserProperty("ant.file" , antFile.getAbsolutePath());
+ project.setUserProperty("ant.executor.class", "org.apache.ant.parallelexecutor.ParallelExecutor");
+ BuildOrderListener orderListener = new BuildOrderListener();
+ project.addBuildListener(orderListener);
+ ProjectHelper.configureProject(project, antFile);
+ project.executeTargets(targetNames);
+ return orderListener.getOrder();
+ }
+
+
+ /**
+ * BuildListener which collects the order of executed targets.
+ */
+ class BuildOrderListener implements BuildListener {
+ List<String> order = new ArrayList<String>();
+ public List<String> getOrder() {
+ return order;
+ }
+ public void targetStarted(BuildEvent event) {
+ order.add(event.getTarget().getName());
+ }
+ // methods we dont need here
+ public void buildFinished(BuildEvent event) {}
+ public void buildStarted(BuildEvent event) {}
+ public void messageLogged(BuildEvent event) {}
+ public void targetFinished(BuildEvent event) {}
+ public void taskFinished(BuildEvent event) {}
+ public void taskStarted(BuildEvent event) {}
+ }
+
+
+ /**
+ * Converts a string-list into a string for easier comparison without
+ * any separation string.
+ * @param list input
+ * @return converted list
+ */
+ protected String list2String(List<String> list) {
+ StringBuilder sb = new StringBuilder();
+ for (String string : list) {
+ sb.append(string);
+ }
+ return sb.toString();
+ }
+
+
+ public void testOneTarget() {
+ String result = list2String(runProject("src/testdata/simple.xml", "A"));
+ assertEquals("A", result);
+ }
+
+ public void testDependsOn1Target() {
+ String result = list2String(runProject("src/testdata/simple-dep.xml", "A"));
+ assertEquals("BA", result);
+ }
+
+ public void testDependsOn2Targets() {
+ String result = list2String(runProject("src/testdata/double-deps.xml", "A"));
+ assertTrue("Wrong order: " + result, result.equals("BCA") || result.equals("CBA") );
+ }
+
+ public void testDependsOn2and1Targets() {
+ String result = list2String(runProject("src/testdata/multiple-deps.xml", "A"));
+ assertTrue("Wrong order: " + result, result.equals("DBCA") || result.equals("DCBA") );
+ }
+
+ public void testFailingTarget1() {
+ String result = list2String(runProject("src/testdata/failures.xml", "A"));
+ assertEquals("A", result);
+ //TODO: check that A throws a BuildException
+ }
+
+ public void testFailingTarget2() {
+ String result = list2String(runProject("src/testdata/failures.xml", "B"));
+ assertEquals("C", result);
+ //TODO: check that C throws a BuildException
+ }
+
+ public void testFailingTarget3() {
+ String result = list2String(runProject("src/testdata/failures.xml", "D"));
+ assertEquals("ED", result);
+ //TODO: check that D throws a BuildException
+ }
+
+ public void testFailingTarget4() {
+ String result = list2String(runProject("src/testdata/failures.xml", "F"));
+ assertTrue("Wrong order: " + result, result.equals("GH") || result.equals("HG") );
+ //TODO: check that G and H throw a BuildException
+ }
+
+}
Added: ant/sandbox/parallelexecutor/src/testdata/double-deps.xml
URL: http://svn.apache.org/viewvc/ant/sandbox/parallelexecutor/src/testdata/double-deps.xml?rev=605925&view=auto
==============================================================================
--- ant/sandbox/parallelexecutor/src/testdata/double-deps.xml (added)
+++ ant/sandbox/parallelexecutor/src/testdata/double-deps.xml Thu Dec 20 05:41:22 2007
@@ -0,0 +1,5 @@
+<project>
+ <target name="A" depends="B,C"/>
+ <target name="B"/>
+ <target name="C"/>
+</project>
\ No newline at end of file
Added: ant/sandbox/parallelexecutor/src/testdata/failures.xml
URL: http://svn.apache.org/viewvc/ant/sandbox/parallelexecutor/src/testdata/failures.xml?rev=605925&view=auto
==============================================================================
--- ant/sandbox/parallelexecutor/src/testdata/failures.xml (added)
+++ ant/sandbox/parallelexecutor/src/testdata/failures.xml Thu Dec 20 05:41:22 2007
@@ -0,0 +1,36 @@
+<project>
+
+ <target name="A">
+ <echo>A</echo>
+ <fail message="A failed"/>
+ </target>
+
+ <target name="B" depends="C">
+ <echo>B</echo>
+ </target>
+ <target name="C">
+ <echo>C</echo>
+ <faill message="C failed"/>
+ </target>
+
+ <target name="D" depends="E">
+ <echo>D</echo>
+ <faill message="D failed"/>
+ </target>
+ <target name="E">
+ <echo>E</echo>
+ </target>
+
+ <target name="F" depends="G,H">
+ <echo>F</echo>
+ </target>
+ <target name="G">
+ <echo>G</echo>
+ <faill message="G failed"/>
+ </target>
+ <target name="H">
+ <echo>H</echo>
+ <faill message="H failed"/>
+ </target>
+
+</project>
\ No newline at end of file
Added: ant/sandbox/parallelexecutor/src/testdata/multiple-deps.xml
URL: http://svn.apache.org/viewvc/ant/sandbox/parallelexecutor/src/testdata/multiple-deps.xml?rev=605925&view=auto
==============================================================================
--- ant/sandbox/parallelexecutor/src/testdata/multiple-deps.xml (added)
+++ ant/sandbox/parallelexecutor/src/testdata/multiple-deps.xml Thu Dec 20 05:41:22 2007
@@ -0,0 +1,6 @@
+<project>
+ <target name="A" depends="B,C"/>
+ <target name="B" depends="D"/>
+ <target name="C" depends="D"/>
+ <target name="D"/>
+</project>
\ No newline at end of file
Added: ant/sandbox/parallelexecutor/src/testdata/simple-dep.xml
URL: http://svn.apache.org/viewvc/ant/sandbox/parallelexecutor/src/testdata/simple-dep.xml?rev=605925&view=auto
==============================================================================
--- ant/sandbox/parallelexecutor/src/testdata/simple-dep.xml (added)
+++ ant/sandbox/parallelexecutor/src/testdata/simple-dep.xml Thu Dec 20 05:41:22 2007
@@ -0,0 +1,4 @@
+<project>
+ <target name="A" depends="B"/>
+ <target name="B"/>
+</project>
\ No newline at end of file
Added: ant/sandbox/parallelexecutor/src/testdata/simple.xml
URL: http://svn.apache.org/viewvc/ant/sandbox/parallelexecutor/src/testdata/simple.xml?rev=605925&view=auto
==============================================================================
--- ant/sandbox/parallelexecutor/src/testdata/simple.xml (added)
+++ ant/sandbox/parallelexecutor/src/testdata/simple.xml Thu Dec 20 05:41:22 2007
@@ -0,0 +1,4 @@
+<project>
+ <target name="A"/>
+ <target name="B"/>
+</project>
\ No newline at end of file