Index: src/ant/org/apache/hadoop/ant/GridUnitTask.java =================================================================== --- src/ant/org/apache/hadoop/ant/GridUnitTask.java (revision 0) +++ src/ant/org/apache/hadoop/ant/GridUnitTask.java (revision 0) @@ -0,0 +1,1090 @@ +package org.apache.hadoop.ant; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Vector; + +//import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.conf.Configured; +import org.apache.hadoop.fs.FsShell; + +//import org.apache.hadoop.util.ToolRunner; +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.ExecuteWatchdog; +import org.apache.tools.ant.taskdefs.LogOutputStream; +import org.apache.tools.ant.taskdefs.PumpStreamHandler; +import org.apache.tools.ant.taskdefs.optional.junit.BaseTest; +import org.apache.tools.ant.taskdefs.optional.junit.BatchTest; +import org.apache.tools.ant.taskdefs.optional.junit.Constants; +import org.apache.tools.ant.taskdefs.optional.junit.Enumerations; +import org.apache.tools.ant.taskdefs.optional.junit.FormatterElement; +import org.apache.tools.ant.taskdefs.optional.junit.JUnitTaskMirror; +import org.apache.tools.ant.taskdefs.optional.junit.JUnitTest; +import org.apache.tools.ant.taskdefs.optional.junit.JUnitTaskMirrorImpl; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Assertions; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.types.Environment; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Permissions; +import org.apache.tools.ant.types.PropertySet; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.LoaderUtils; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.Project; + +//import org.apache.tools.ant.taskdefs.optional.junit.JUnitTask; +import org.apache.tools.ant.taskdefs.optional.junit.SummaryJUnitResultFormatter; + +public class GridUnitTask extends Task { + + private static final String HALT_ON_ERROR = "haltOnError="; + private static final String HALT_ON_FAILURE = "haltOnFailure="; + private static final String FILTERTRACE = "filtertrace="; + private static final String SHOWOUTPUT = "showoutput="; + private static final String OUTPUT_TO_FORMATTERS = "outputtoformatters="; + private static final String FORMATTER = "formatter="; + private static final String LOGTESTLISTENEREVENTS = "logtestlistenerevents="; + private static final String TESTSFILE = "testsfile="; + private boolean useFile = true; + private String classname; + private File outFile; + private OutputStream out = System.out; + + CommandlineJava cmd = new CommandlineJava(); + + private static final String LINE_SEP = System.getProperty("line.separator"); + private static final String CLASSPATH = "CLASSPATH="; + private static final String JAVA_HOME = ""; + private CommandlineJava commandline; + private Vector tests = new Vector(); + private Vector batchTests = new Vector(); + private Vector formatters = new Vector(); + private File dir = null; + + private Integer timeout = null; + private boolean summary = false; + private boolean reloading = true; + private String summaryValue = ""; + private JUnitTaskMirror.JUnitTestRunnerMirror runner = null; + + private boolean newEnvironment = false; + private Environment env = new Environment(); + + private boolean includeAntRuntime = true; + private Path antRuntimeClasses = null; + + // Do we send output to System.out/.err in addition to the formatters? + private boolean showOutput = false; + + // Do we send output to the formatters ? + private boolean outputToFormatters = true; + + private File tmpDir; + private File dfspath; + private AntClassLoader classLoader = null; + private Permissions perm = null; + // private ForkMode forkMode = new ForkMode("perTest"); + + private boolean splitJunit = false; + private JUnitTaskMirror delegate; + private ClassLoader mirrorLoader; + + /** A boolean on whether to get the forked path for ant classes */ + private boolean forkedPathChecked = false; + + // Attributes for basetest + private boolean haltOnError = false; + private boolean haltOnFail = false; + private boolean filterTrace = true; + private boolean fork = false; + private String failureProperty; + private String errorProperty; + + private int numberOfRuns = 0; + private String inputfile = ""; + private String outputfile = ""; + private String jarFileName = null; + + private static final int STRING_BUFFER_SIZE = 128; + private static final FsShell shell = new FsShell(); + /** + * @since Ant 1.7 + */ + public static final String TESTLISTENER_PREFIX = "junit.framework.TestListener: "; + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + PrintWriter pw = new PrintWriter(new FileWriter("input.txt")); + + public CommandlineJava getCommandLine() { + + return cmd; + } + + // Number of test runs + public void setNumberOfRuns(int n) { + this.numberOfRuns = n; + } + + public void setInputFile(String inputf) { + this.inputfile = inputf; + } + + public void setOutputFile(String outputf) { + this.outputfile = outputf; + } + + public void setDfsPath(File dfsP) { + this.dfspath = dfsP; + } + + public void addSysproperty(Environment.Variable sysp) { + getCommandLine().addSysproperty(sysp); + } + + public void setJar(String jarFileName) { + this.jarFileName = jarFileName; + } + + /** + * If true, smartly filter the stack frames of JUnit errors and failures + * before reporting them. + * + *

+ * This property is applied on all BatchTest (batchtest) and JUnitTest (test) + * however it can possibly be overridden by their own properties. + *

+ * + * @param value + * false if it should not filter, otherwise true + * + * @since Ant 1.5 + */ + public void setFiltertrace(boolean value) { + this.filterTrace = value; + } + + /** + * If true, stop the build process when there is an error in a test. This + * property is applied on all BatchTest (batchtest) and JUnitTest (test) + * however it can possibly be overridden by their own properties. + * + * @param value + * true if it should halt, otherwise false + * + * @since Ant 1.2 + */ + public void setHaltonerror(boolean value) { + this.haltOnError = value; + } + + /** + * Property to set to "true" if there is a error in a test. + * + *

+ * This property is applied on all BatchTest (batchtest) and JUnitTest (test), + * however, it can possibly be overriden by their own properties. + *

+ * + * @param propertyName + * the name of the property to set in the event of an error. + * + * @since Ant 1.4 + */ + public void setErrorProperty(String propertyName) { + this.errorProperty = propertyName; + } + + /** + * If true, stop the build process if a test fails (errors are considered + * failures as well). This property is applied on all BatchTest (batchtest) + * and JUnitTest (test) however it can possibly be overridden by their own + * properties. + * + * @param value + * true if it should halt, otherwise false + * + * @since Ant 1.2 + */ + public void setHaltonfailure(boolean value) { + this.haltOnFail = value; + } + + /** + * Property to set to "true" if there is a failure in a test. + * + *

+ * This property is applied on all BatchTest (batchtest) and JUnitTest (test), + * however, it can possibly be overriden by their own properties. + *

+ * + * @param propertyName + * the name of the property to set in the event of an failure. + * + * @since Ant 1.4 + */ + public void setFailureProperty(String propertyName) { + this.failureProperty = propertyName; + } + + /** + * If true, JVM should be forked for each test. + * + *

+ * It avoids interference between testcases and possibly avoids hanging the + * build. this property is applied on all BatchTest (batchtest) and JUnitTest + * (test) however it can possibly be overridden by their own properties. + *

+ * + * @param value + * true if a JVM should be forked, otherwise + * false + * @see #setTimeout + * + * @since Ant 1.2 + */ + public void setFork(boolean value) { + this.fork = value; + } + + /** + * If true, print one-line statistics for each test, or "withOutAndErr" to + * also show standard output and error. + * + * Can take the values on, off, and withOutAndErr. + * + * @param value + * true to print a summary, withOutAndErr to + * include the test's output as well, false + * otherwise. + * @see SummaryJUnitResultFormatter + * + * @since Ant 1.2 + */ + public void setPrintsummary(SummaryAttribute value) { + summaryValue = value.getValue(); + summary = value.asBoolean(); + } + + /** + * Print summary enumeration values. + */ + public static class SummaryAttribute extends EnumeratedAttribute { + /** + * list the possible values + * + * @return array of allowed values + */ + public String[] getValues() { + return new String[] { "true", "yes", "false", "no", "on", "off", + "withOutAndErr" }; + } + + /** + * gives the boolean equivalent of the authorized values + * + * @return boolean equivalent of the value + */ + public boolean asBoolean() { + String v = getValue(); + return "true".equals(v) || "on".equals(v) || "yes".equals(v) + || "withOutAndErr".equals(v); + } + } + + /** + * Set the timeout value (in milliseconds). + * + *

+ * If the test is running for more than this value, the test will be canceled. + * (works only when in 'fork' mode). + *

+ * + * @param value + * the maximum time (in milliseconds) allowed before declaring the + * test as 'timed-out' + * @see #setFork(boolean) + * + * @since Ant 1.2 + */ + public void setTimeout(Integer value) { + timeout = value; + } + + /** + * Set the maximum memory to be used by all forked JVMs. + * + * @param max + * the value as defined by -mx or -Xmx in the + * java command line options. + * + * @since Ant 1.2 + */ + public void setMaxmemory(String max) { + getCommandLine().setMaxmemory(max); + } + + /** + * The command used to invoke the Java Virtual Machine, default is 'java'. The + * command is resolved by java.lang.Runtime.exec(). Ignored if fork is + * disabled. + * + * @param value + * the new VM to use instead of java + * @see #setFork(boolean) + * + * @since Ant 1.2 + */ + public void setJvm(String value) { + getCommandLine().setVm(value); + } + + /** + * Adds a JVM argument; ignored if not forking. + * + * @return create a new JVM argument so that any argument can be passed to the + * JVM. + * @see #setFork(boolean) + * + * @since Ant 1.2 + */ + public Commandline.Argument createJvmarg() { + return getCommandLine().createVmArgument(); + } + + /** + * The directory to invoke the VM in. Ignored if no JVM is forked. + * + * @param dir + * the directory to invoke the JVM from. + * @see #setFork(boolean) + * + * @since Ant 1.2 + */ + public void setDir(File dir) { + this.dir = dir; + } + + /** + * Adds a system property that tests can access. This might be useful to + * tranfer Ant properties to the testcases when JVM forking is not enabled. + * + * @param sysp + * new environment variable to add + * @since Ant 1.6 + */ + public void addConfiguredSysproperty(Environment.Variable sysp) { + // get a build exception if there is a missing key or value + // see bugzilla report 21684 + String testString = sysp.getContent(); + getProject().log("sysproperty added : " + testString, Project.MSG_DEBUG); + getCommandLine().addSysproperty(sysp); + } + + /** + * Adds a set of properties that will be used as system properties that tests + * can access. + * + * This might be useful to tranfer Ant properties to the testcases when JVM + * forking is not enabled. + * + * @param sysp + * set of properties to be added + * @since Ant 1.6 + */ + public void addSyspropertyset(PropertySet sysp) { + getCommandLine().addSyspropertyset(sysp); + } + + /** + * Adds path to classpath used for tests. + * + * @return reference to the classpath in the embedded java command line + * @since Ant 1.2 + */ + public Path createClasspath() { + return getCommandLine().createClasspath(getProject()).createPath(); + } + + /** + * Adds an environment variable; used when forking. + * + *

+ * Will be ignored if we are not forking a new VM. + *

+ * + * @param var + * environment variable to be added + * @since Ant 1.5 + */ + public void addEnv(Environment.Variable var) { + env.addVariable(var); + } + + /** + * If true, use a new environment when forked. + * + *

+ * Will be ignored if we are not forking a new VM. + *

+ * + * @param newenv + * boolean indicating if setting a new environment is wished + * @since Ant 1.5 + */ + public void setNewenvironment(boolean newenv) { + newEnvironment = newenv; + } + + /** + * Preset the attributes of the test before configuration in the build script. + * This allows attributes in the task be be defaults for the tests, + * but allows individual tests to override the defaults. + */ + private void preConfigure(BaseTest test) { + test.setFiltertrace(filterTrace); + test.setHaltonerror(haltOnError); + if (errorProperty != null) { + test.setErrorProperty(errorProperty); + } + test.setHaltonfailure(haltOnFail); + if (failureProperty != null) { + test.setFailureProperty(failureProperty); + } + test.setFork(fork); + } + + /** + * Add a new single testcase. + * + * @param test + * a new single testcase + * @see JUnitTest + * + * @since Ant 1.2 + */ + public void addTest(JUnitTest test) { + tests.addElement(test); + preConfigure(test); + } + + /** + * Adds a set of tests based on pattern matching. + * + * @return a new instance of a batch test. + * @see BatchTest + * + * @since Ant 1.2 + */ + public BatchTest createBatchTest() { + BatchTest test = new BatchTest(getProject()); + batchTests.addElement(test); + preConfigure(test); + return test; + } + + /** + * Add a new formatter to all tests of this task. + * + * @param fe + * formatter element + * @since Ant 1.2 + */ + public void addFormatter(FormatterElement fe) { + formatters.addElement(fe); + } + + /** + * If true, include ant.jar, optional.jar and junit.jar in the forked VM. + * + * @param b + * include ant run time yes or no + * @since Ant 1.5 + */ + public void setIncludeantruntime(boolean b) { + includeAntRuntime = b; + } + + /** + * If true, send any output generated by tests to Ant's logging system as well + * as to the formatters. By default only the formatters receive the output. + * + *

+ * Output will always be passed to the formatters and not by shown by default. + * This option should for example be set for tests that are interactive and + * prompt the user to do something. + *

+ * + * @param showOutput + * if true, send output to Ant's logging system too + * @since Ant 1.5 + */ + public void setShowOutput(boolean showOutput) { + this.showOutput = showOutput; + } + + /** + * If true, send any output generated by tests to the formatters. + * + * @param outputToFormatters + * if true, send output to formatters (Default is true). + * @since Ant 1.7.0 + */ + public void setOutputToFormatters(boolean outputToFormatters) { + this.outputToFormatters = outputToFormatters; + } + + /** + * Assertions to enable in this program (if fork=true) + * + * @since Ant 1.6 + * @param asserts + * assertion set + */ + public void addAssertions(Assertions asserts) { + if (getCommandLine().getAssertions() != null) { + throw new BuildException("Only one assertion declaration is allowed"); + } + getCommandLine().setAssertions(asserts); + } + + /** + * Sets the permissions for the application run inside the same JVM. + * + * @since Ant 1.6 + * @return . + */ + public Permissions createPermissions() { + if (perm == null) { + perm = new Permissions(); + } + return perm; + } + + /** + * If set, system properties will be copied to the cloned VM - as well as the + * bootclasspath unless you have explicitly specified a bootclaspath. + * + *

+ * Doesn't have any effect unless fork is true. + *

+ * + * @param cloneVm + * a boolean value. + * @since Ant 1.7 + */ + public void setCloneVm(boolean cloneVm) { + getCommandLine().setCloneVm(cloneVm); + } + + /** + * Creates a new JUnitRunner and enables fork of a new Java VM. + * + * @throws Exception + * under ??? circumstances + * @since Ant 1.2 + */ + public GridUnitTask() throws Exception { + getCommandLine().setClassname( + "org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner"); + } + + /** + * Where Ant should place temporary files. + * + * @param tmpDir + * location where temporary files should go to + * @since Ant 1.6 + */ + public void setTempdir(File tmpDir) { + if (tmpDir != null) { + if (!tmpDir.exists() || !tmpDir.isDirectory()) { + throw new BuildException(tmpDir.toString() + + " is not a valid temp directory"); + } + } + this.tmpDir = tmpDir; + } + + private final class SplitLoader extends AntClassLoader { + + public SplitLoader(ClassLoader parent, Path path) { + super(parent, getProject(), path, true); + } + + // forceLoadClass is not convenient here since it would not + // properly deal with inner classes of these classes. + protected synchronized Class loadClass(String classname, boolean resolve) + throws ClassNotFoundException { + Class theClass = findLoadedClass(classname); + if (theClass != null) { + return theClass; + } + if (isSplit(classname)) { + theClass = findClass(classname); + if (resolve) { + resolveClass(theClass); + } + return theClass; + } else { + return super.loadClass(classname, resolve); + } + } + + private final String[] splitClasses = { "BriefJUnitResultFormatter", + "JUnitResultFormatter", "JUnitTaskMirrorImpl", "JUnitTestRunner", + "JUnitVersionHelper", "OutErrSummaryJUnitResultFormatter", + "PlainJUnitResultFormatter", "SummaryJUnitResultFormatter", + "XMLJUnitResultFormatter", }; + + private boolean isSplit(String classname) { + String simplename = classname.substring(classname.lastIndexOf('.') + 1); + for (int i = 0; i < splitClasses.length; i++) { + if (simplename.equals(splitClasses[i]) + || simplename.startsWith(splitClasses[i] + '$')) { + return true; + } + } + return false; + } + + } + + /** + * Sets up the delegate that will actually run the tests. + * + *

+ * Will be invoked implicitly once the delegate is needed. + *

+ * + * @since Ant 1.7.1 + */ + + protected void setupJUnitDelegate() { + ClassLoader myLoader = GridUnitTask.class.getClassLoader(); + if (splitJunit) { + Path path = new Path(getProject()); + path.add(antRuntimeClasses); + Path extra = getCommandLine().getClasspath(); + if (extra != null) { + path.add(extra); + } + mirrorLoader = new SplitLoader(myLoader, path); + } else { + mirrorLoader = myLoader; + } + // delegate = createMirror(this, mirrorLoader); + } + + protected Collection executeOrQueue(Enumeration testList) { + Map testConfigurations = new HashMap(); + while (testList.hasMoreElements()) { + JUnitTest test = (JUnitTest) testList.nextElement(); + if (test.shouldRun(getProject())) { + execute(test); + } + } + return testConfigurations.values(); + } + + /** + * Runs the testcase. + * + * @throws BuildException + * in case of test failures or errors + * @since Ant 1.2 + */ + + public void execute() throws BuildException { + setupJUnitDelegate(); + List testLists = new ArrayList(); + testLists.addAll(executeOrQueue(getIndividualTests())); + try { + Iterator iter = testLists.iterator(); + while (iter.hasNext()) { + List l = (List) iter.next(); + if (l.size() == 1) { + execute((JUnitTest) l.get(0)); + } else { + execute(l); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + pw.close(); + + List args = new ArrayList(); + args.add(0, inputfile); + args.add(1, outputfile); + + for (int i = 0; i < args.size(); i++) { + + System.out.println(args.get(i)); + } + + HadoopUnit hu = new HadoopUnit(dir, dfspath, jarFileName); + + try { + String[] arguments = { inputfile, outputfile }; + hu.run(arguments); + } catch (Exception e) { // TODO Auto-generated catch + e.printStackTrace(); + } + + } + + /** + * Run the tests. + * + * @param arg + * one JunitTest + * @throws BuildException + * in case of test failures or errors + */ + protected void execute(JUnitTest arg) throws BuildException { + JUnitTest test = (JUnitTest) arg.clone(); + if (test.getTodir() == null) { + test.setTodir(getProject().resolveFile(".")); + } + + if (test.getOutfile() == null) { + test.setOutfile("TEST-" + test.getName()); + } + // execute the test and get the return code + TestResultHolder result = null; + // Destroys a process running for too long + ExecuteWatchdog watchdog = createWatchdog(); + result = executeAsForked(test, watchdog, null); + } + + protected void execute(List testList) throws BuildException { + JUnitTest test = null; + try { + Iterator iter = testList.iterator(); + while (iter.hasNext()) { + test = (JUnitTest) iter.next(); + } + // execute the test and get the return code + ExecuteWatchdog watchdog = createWatchdog(); + TestResultHolder result = executeAsForked(test, watchdog, null); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + /** + * Execute a testcase by forking a new JVM. The command will block until it + * finishes. To know if the process was destroyed or not or whether the forked + * Java VM exited abnormally, use the attributes of the returned holder + * object. + * + * @param test + * the testcase to execute. + * @param watchdog + * the watchdog in charge of cancelling the test if it exceeds a + * certain amount of time. Can be null, in this case the + * test could probably hang forever. + * @param casesFile + * list of test cases to execute. Can be null, in this + * case only one test is executed. + * @return the test results from the JVM itself. + * @throws BuildException + * in case of error creating a temporary property file, or if the + * junit process can not be forked + */ + private TestResultHolder executeAsForked(JUnitTest test, + ExecuteWatchdog watchdog, File casesFile) throws BuildException { + + if (perm != null) { + log("Permissions ignored when running in forked mode!", Project.MSG_WARN); + } + + CommandlineJava cmd; + try { + cmd = (CommandlineJava) (getCommandLine().clone()); + } catch (CloneNotSupportedException e) { + throw new BuildException("This shouldn't happen", e, getLocation()); + } + cmd + .setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner"); + if (casesFile == null) { + cmd.createArgument().setValue(test.getName()); + } else { + log("Running multiple tests in the same VM", Project.MSG_VERBOSE); + cmd.createArgument().setValue(TESTSFILE + casesFile); + } + + cmd.createArgument().setValue(FILTERTRACE + test.getFiltertrace()); + cmd.createArgument().setValue(HALT_ON_ERROR + test.getHaltonerror()); + cmd.createArgument().setValue(HALT_ON_FAILURE + test.getHaltonfailure()); + // checkIncludeAntRuntime(cmd); + // checkIncludeJavaPath(cmd); + + checkIncludeSummary(cmd); + + cmd.createArgument().setValue(SHOWOUTPUT + String.valueOf(showOutput)); + cmd.createArgument().setValue( + OUTPUT_TO_FORMATTERS + String.valueOf(outputToFormatters)); + cmd.createArgument().setValue(LOGTESTLISTENEREVENTS + "true"); // #31885 + + File vmWatcher = createTempPropertiesFile("junitvmwatcher"); + File propsFile = createTempPropertiesFile("junit"); + Hashtable p = getProject().getProperties(); + Properties props = new Properties(); + for (Enumeration e = p.keys(); e.hasMoreElements();) { + Object key = e.nextElement(); + props.put(key, p.get(key)); + } + try { + FileOutputStream outstream = new FileOutputStream(propsFile); + props.store(outstream, "Ant JUnitTask generated properties file"); + outstream.close(); + } catch (java.io.IOException e) { + propsFile.delete(); + throw new BuildException( + "Error creating temporary properties " + "file.", e, getLocation()); + } + TestResultHolder result = new TestResultHolder(); + + cmd.setVm("java"); + + log(cmd.describeCommand(), Project.MSG_VERBOSE); + + checkForkedPath(cmd); + + String str = cmd.toString(); + + // Number of times the cmd needs to be generated + if (numberOfRuns > 1) { + for (int i = 0; i < numberOfRuns; i++) { + pw.println(str); + } + } else + pw.println(str); + + return result; + } + + /** + * check for the parameter being "withoutanderr" in a locale-independent way. + * + * @param summaryOption + * the summary option -can be null + * @return true if the run should be withoutput and error + */ + + private boolean equalsWithOutAndErr(String summaryOption) { + return summaryOption != null + && "withoutanderr".equals(summaryOption.toLowerCase(Locale.ENGLISH)); + } + + private void checkIncludeSummary(CommandlineJava cmd) { + if (summary) { + String prefix = ""; + if (equalsWithOutAndErr(summaryValue)) { + prefix = "OutErr"; + } + cmd.createArgument().setValue( + FORMATTER + "org.apache.tools.ant.taskdefs.optional.junit." + prefix + + "SummaryJUnitResultFormatter"); + } + } + + /** + * Check the path for multiple different versions of ant. + * + * @param cmd + * command to execute + */ + private void checkForkedPath(CommandlineJava cmd) { + if (forkedPathChecked) { + return; + } + forkedPathChecked = true; + AntClassLoader loader = new AntClassLoader(getProject(), cmd + .createClasspath(getProject())); + String projectResourceName = LoaderUtils.classNameToResource(Project.class + .getName()); + URL previous = null; + try { + for (Enumeration e = loader.getResources(projectResourceName); e + .hasMoreElements();) { + URL current = (URL) e.nextElement(); + if (previous != null && !current.equals(previous)) { + log("WARNING: multiple versions of ant detected " + + "in path for junit " + LINE_SEP + " " + previous + + LINE_SEP + " and " + current, Project.MSG_WARN); + return; + } + previous = current; + } + } catch (Exception ex) { + // Ignore exception + } + } + + /** + * Create a temporary file to pass the properties to a new process. Will + * auto-delete on (graceful) exit. The file will be in the project basedir + * unless tmpDir declares something else. + * + * @param prefix + * @return created file + */ + + private File createTempPropertiesFile(String prefix) { + /* + * File propsFile = FILE_UTILS.createTempFile(prefix, ".properties", tmpDir != + * null ? tmpDir : getProject().getBaseDir(), true, true); + */ + + File propsFile = FILE_UTILS.createTempFile(prefix, ".properties", + tmpDir != null ? tmpDir : getProject().getBaseDir(), true); + + return propsFile; + } + + JUnitTaskMirror.JUnitResultFormatterMirror createFormatter() + throws BuildException { + return createFormatter(null); + } + + /** + * @since Ant 1.6 + */ + JUnitTaskMirror.JUnitResultFormatterMirror createFormatter(ClassLoader loader) + throws BuildException { + + if (classname == null) { + throw new BuildException("you must specify type or classname"); + } + // although this code appears to duplicate that of + // ClasspathUtils.newInstance, + // we cannot use that because this formatter may run in a forked + // process, + // without that class. + Class f = null; + try { + if (loader == null) { + f = Class.forName(classname); + } else { + f = Class.forName(classname, true, loader); + } + } catch (ClassNotFoundException e) { + throw new BuildException("Using loader " + loader + " on class " + + classname + ": " + e, e); + } catch (NoClassDefFoundError e) { + throw new BuildException("Using loader " + loader + " on class " + + classname + ": " + e, e); + } + + Object o = null; + try { + o = f.newInstance(); + } catch (InstantiationException e) { + throw new BuildException(e); + } catch (IllegalAccessException e) { + throw new BuildException(e); + } + + if (!(o instanceof JUnitTaskMirror.JUnitResultFormatterMirror)) { + throw new BuildException(classname + " is not a JUnitResultFormatter"); + } + JUnitTaskMirror.JUnitResultFormatterMirror r = (JUnitTaskMirror.JUnitResultFormatterMirror) o; + if (useFile && outFile != null) { + try { + out = new BufferedOutputStream(new FileOutputStream(outFile)); + } catch (java.io.IOException e) { + throw new BuildException("Unable to open file " + outFile, e); + } + } + r.setOutput(out); + return r; + } + + /** + * @return null if there is a timeout value, otherwise the watchdog + * instance. + * + * @throws BuildException + * under unspecified circumstances + * @since Ant 1.2 + */ + protected ExecuteWatchdog createWatchdog() throws BuildException { + if (timeout == null) { + return null; + } + return new ExecuteWatchdog((long) timeout.intValue()); + } + + /** + * Merge all individual tests from the batchtest with all individual tests and + * return an enumeration over all JUnitTest. + * + * @return enumeration over individual tests + * @since Ant 1.3 + */ + protected Enumeration getIndividualTests() { + final int count = batchTests.size(); + final Enumeration[] enums = new Enumeration[count + 1]; + for (int i = 0; i < count; i++) { + BatchTest batchtest = (BatchTest) batchTests.elementAt(i); + enums[i] = batchtest.elements(); + } + enums[enums.length - 1] = tests.elements(); + return Enumerations.fromCompound(enums); + } + + /** + * return an enumeration listing each test, then each batchtest + * + * @return enumeration + * @since Ant 1.3 + */ + protected Enumeration allTests() { + Enumeration[] enums = { tests.elements(), batchTests.elements() }; + return Enumerations.fromCompound(enums); + } + + /** + * A value class that contains the result of a test. + */ + protected class TestResultHolder { + // CheckStyle:VisibilityModifier OFF - bc + /** the exit code of the test. */ + public int exitCode = JUnitTaskMirror.JUnitTestRunnerMirror.ERRORS; + /** true if the test timed out */ + public boolean timedOut = false; + /** true if the test crashed */ + public boolean crashed = false; + // CheckStyle:VisibilityModifier ON + } +} Index: src/ant/org/apache/hadoop/ant/HadoopUnit.java =================================================================== --- src/ant/org/apache/hadoop/ant/HadoopUnit.java (revision 0) +++ src/ant/org/apache/hadoop/ant/HadoopUnit.java (revision 0) @@ -0,0 +1,271 @@ +/** + * 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.hadoop.ant; + +import java.io.*; +import java.net.URI; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.conf.Configured; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.LongWritable; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.io.Writable; +import org.apache.hadoop.mapred.FileInputFormat; +import org.apache.hadoop.mapred.FileOutputFormat; +import org.apache.hadoop.mapred.JobClient; +import org.apache.hadoop.mapred.JobConf; +import org.apache.hadoop.mapred.MapReduceBase; +import org.apache.hadoop.mapred.Mapper; +import org.apache.hadoop.mapred.OutputCollector; +import org.apache.hadoop.mapred.Reducer; +import org.apache.hadoop.mapred.Reporter; +import org.apache.hadoop.filecache.DistributedCache; +import org.apache.hadoop.mapred.*; +import org.apache.hadoop.mapred.lib.*; +import org.apache.hadoop.util.*; + +public class HadoopUnit extends Configured implements Tool { + private static File basedir; + private static File dfsPath; + private static String jarfile; + private static final String TIME_OUT_KEY = "time_out"; + private static final int TIME_OUT_VAL = 5; + private static final String JOB_NAME = "HadoopUnit"; + + static List arguments = new ArrayList(); + + HadoopUnit() { + + } + + HadoopUnit(File file, File dfspath, String jarfile) { + basedir = file; + this.dfsPath = dfspath; + this.jarfile = jarfile; + } + + public static class MapClass extends MapReduceBase implements + Mapper { + + private String originalPath = ""; + private String changedPath = ""; + + public void configure(JobConf job) { + Path[] pt = null; + + /* + try { + //pt = DistributedCache.getLocalCacheArchives(job); + } catch (IOException e) { + + e.printStackTrace(); + } + + //changedPath = pt[0].toString(); + */ + + } + + public void map(LongWritable key, Text value, + OutputCollector output, Reporter reporter) + throws IOException { + + String line = value.toString(); + String outputStr = ""; + String testName = ""; + String[] tname = line.split(" "); + for (int i = 0; i < tname.length; i++) { + if (tname[i].contains(".Test")) { + testName = tname[i]; + } + } + //line = line.replaceAll(basedir.toString(), changedPath); + try { + + String javaHome = System.getenv("JAVA_HOME"); + Process p = Runtime.getRuntime().exec(javaHome + "/bin/" + line); + InputStreamReader isr = new InputStreamReader(p.getInputStream()); + BufferedReader br = new BufferedReader(isr); + String thisLine; + while ((thisLine = br.readLine()) != null) { + outputStr += thisLine + "\n"; + } + } catch (IOException e) { + System.out.println(e); + } + + Pattern pattern = Pattern + .compile("Tests run: (\\d+), " + + "Failures: (\\d+), Errors: (\\d+), " + + "Time elapsed: (\\d+\\.\\d+) sec"); + Matcher matcher = pattern.matcher(outputStr); + + //matcher.find(); + Stats statistics = new Stats(); + + if (matcher.find()) { + + String testRuns = matcher.group(1); + int numRuns = Integer.parseInt(testRuns); + String strFailures = matcher.group(2); + int numFail = Integer.parseInt(strFailures); + String strErrors = matcher.group(3); + int errros = Integer.parseInt(strErrors); + String strTime = matcher.group(4); + double time = Double.parseDouble(strTime); + + statistics = new Stats(numRuns, numFail, errros, time); + } else + System.out.println("No results"); + + output.collect(new Text(testName), statistics); + + } + } + + public static class Stats implements Writable { + private int _numErrors; + private int _numFailures; + + private int _numTestRuns; + private double _Time; + + Stats() { + }; + + Stats(int numTestRuns, int numFailures, int numErrors, double time) { + + _numErrors = numErrors; + _numFailures = numFailures; + _numTestRuns = numTestRuns; + _Time = time; + + } + + public void write(DataOutput out) throws IOException { + + out.writeInt(_numErrors); + out.writeInt(_numFailures); + out.writeInt(_numTestRuns); + out.writeDouble(_Time); + } + + public void readFields(DataInput in) throws IOException { + _numErrors = in.readInt(); + _numFailures = in.readInt(); + _numTestRuns = in.readInt(); + _Time = in.readInt(); + + } + + public static Stats read(DataInput in) throws IOException { + Stats s = new Stats(); + s.readFields(in); + return s; + } + + public String toString() { + String result = ""; + + result = result + "\n" + "Test runs: " + _numTestRuns + "\n" + + "Number of Failures: " + _numFailures + "\n" + "Errors: " + + _numErrors + "\n" + "Time: " + _Time + "\n"; + return result; + } + } + + /** + * A reducer class that just emits combined output. + */ + public static class Reduce extends MapReduceBase implements + Reducer { + + public void reduce(Text key, Iterator values, + OutputCollector output, Reporter reporter) + throws IOException { + + Stats stat = (Stats) values.next(); + output.collect(key, stat); + + } + } + + static int printUsage() { + System.out + .println("HadoopUnit [-m ] [-r ] " + + " "); + ToolRunner.printGenericCommandUsage(System.out); + return -1; + } + + /** + * The main driver for HadoopUnit map/reduce program. + * Invoke this method to submit the map/reduce job. + * @throws IOException When there is communication problems with the + * job tracker. + */ + public int run(String[] args) throws Exception { + + //JobConf conf = new JobConf(getConf(), HadoopUnit.class); + JobConf conf = new JobConf(HadoopUnit.class); + conf.setJobName(JOB_NAME); + conf.setJar(basedir + "/build/" + jarfile); + // the keys are words (strings) + conf.setOutputKeyClass(Text.class); + conf.setInt(TIME_OUT_KEY, TIME_OUT_VAL); + conf.setOutputValueClass(Stats.class); + conf.setMapperClass(MapClass.class); + conf.setCombinerClass(Reduce.class); + conf.setReducerClass(Reduce.class); + conf.setInputFormat(NLineInputFormat.class); + conf.setOutputFormat(TextOutputFormat.class); + conf.setNumReduceTasks(1); + + //DistributedCache.addCacheArchive(new URI(dfsPath +"/trunk.jar"), conf); + + List other_args = new ArrayList(); + + for (int i = 0; i < args.length; ++i) { + try { + if ("-skip".equals(args[i])) { + DistributedCache.addCacheFile(new Path(args[++i]).toUri(), conf); + } else { + other_args.add(args[i]); + } + } catch (NumberFormatException except) { + System.out.println("ERROR: Integer expected instead of " + args[i]); + return printUsage(); + } catch (ArrayIndexOutOfBoundsException except) { + System.out.println("ERROR: Required parameter missing from " + + args[i - 1]); + return printUsage(); + } + + } + + FileInputFormat.setInputPaths(conf, new Path(other_args.get(0))); + FileOutputFormat.setOutputPath(conf, new Path(other_args.get(1))); + JobClient.runJob(conf); + return 0; + } + +} Index: build.xml =================================================================== --- build.xml (revision 711364) +++ build.xml (working copy) @@ -105,7 +105,10 @@ + + + @@ -1401,5 +1404,63 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tests failed! + +