changeset 866:f42fc832db86

Add missing (ignored) lib/ files. Add test report and classes dirs to ignore. 2008-05-19 Mark Wielaard <mark@klomp.org> * test/jtreg/com/sun/javatest/lib/*.java: Added. * .hgignore: Add test/hotspot, test/jdk, test/langtools and test/jtreg/classes
author Mark Wielaard <mark@klomp.org>
date Mon, 19 May 2008 10:33:03 +0200
parents bf4662205a02
children 47be7e06b551
files .hgignore ChangeLog test/jtreg/com/sun/javatest/lib/APIScript.java test/jtreg/com/sun/javatest/lib/Deprecated.java test/jtreg/com/sun/javatest/lib/ExecStdTestOtherJVMCmd.java test/jtreg/com/sun/javatest/lib/ExecStdTestSameJVMCmd.java test/jtreg/com/sun/javatest/lib/JavaCompileCommand.java test/jtreg/com/sun/javatest/lib/KeywordScript.java test/jtreg/com/sun/javatest/lib/MultiStatus.java test/jtreg/com/sun/javatest/lib/MultiTest.java test/jtreg/com/sun/javatest/lib/ProcessCommand.java test/jtreg/com/sun/javatest/lib/ReportScript.java test/jtreg/com/sun/javatest/lib/StdTestScript.java test/jtreg/com/sun/javatest/lib/TestCases.java
diffstat 14 files changed, 2539 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Mon May 19 08:16:22 2008 +0200
+++ b/.hgignore	Mon May 19 10:33:03 2008 +0200
@@ -22,6 +22,10 @@
 platform_zero
 jvm.cfg
 ergo.c
+test/hotspot
+test/jdk
+test/langtools
+test/jtreg/classes
 rt/com/sun/jdi/AbsentInformationException.java
 rt/com/sun/jdi/Accessible.java
 rt/com/sun/jdi/ArrayReference.java
--- a/ChangeLog	Mon May 19 08:16:22 2008 +0200
+++ b/ChangeLog	Mon May 19 10:33:03 2008 +0200
@@ -1,3 +1,9 @@
+2008-05-19  Mark Wielaard  <mark@klomp.org>
+
+	* test/jtreg/com/sun/javatest/lib/*.java: Added.
+	* .hgignore: Add test/hotspot, test/jdk, test/langtools and
+	test/jtreg/classes
+
 2008-05-19  Mark Wielaard  <mark@klomp.org>
 
 	* test/jtreg/*: New jtreg and jtharness sources.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/APIScript.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,114 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import java.io.File;
+import java.io.PrintWriter;
+
+import com.sun.javatest.Script;
+import com.sun.javatest.Status;
+import com.sun.javatest.TestEnvironment;
+import com.sun.javatest.TestDescription;
+
+/**
+ * A Script designed to compile/execute a test.
+ */
+public class APIScript extends Script
+{
+    /// XXX this code really needs to be re-visited!
+    
+    /**
+     * The method that interprets the tags provided in the test description and
+     * performs actions accordingly.
+     *
+     * @param args Any arguments that the APIScript may use.  Currently
+     *             there are none (value ignored).
+     * @param td   The current TestDescription.
+     * @param env  The test environment giving the details of how to run the
+     *             test.
+     * @return     The result of running the script on the given test
+     *             description.
+     */
+    public Status run(String [] args, TestDescription td, TestEnvironment env) {
+
+        PrintWriter trOut = getTestResult().getTestCommentWriter();
+
+	Status status = decodeArgs(args);
+	if (status != null)
+	    return status;
+	
+	// XXX This isn't everything.  We need to make sure that this is a
+	// XXX reasonable subset of JCKScript.  Do we want to handle all options 
+	// XXX available there?  How about the keywords?
+
+	// compile
+	File [] srcs = td.getSourceFiles();
+	Status compileStatus;
+	if (precompileClassDir == null) {
+	    trOut.println("Unconditionally compiling all sources");
+	    compileStatus = compileTogether(TEST_COMPILE, srcs);
+	} else {
+	    trOut.println("Compiling sources only if necessary");
+	    compileStatus = compileIfNecessary(TEST_COMPILE, srcs, precompileClassDir);
+	}
+
+	if (!compileStatus.isPassed())
+	    return compileStatus;
+
+	// execute
+	String executeClass  = td.getParameter("executeClass");
+	String executeArgs   = td.getParameter("executeArgs");
+	Status executeStatus = execute(TEST_EXECUTE, executeClass, executeArgs);
+
+	return executeStatus;
+    } // run()
+
+    //----------private methods-------------------------------------------------
+
+    private Status decodeArgs(String [] args) {
+	// decode args
+	for (int i = 0; i < args.length; i++) {
+	    if (args[i].equals("-precompileClassDir") && (i+1 < args.length))
+		precompileClassDir = args[++i];
+	    else
+		return Status.failed(UNRECOGNIZED_ARG + args[i]);
+	}
+
+	return null;
+    } // init()
+
+    //----------member variables------------------------------------------------
+
+    private static final String TEST_COMPILE = "testCompile";
+    private static final String TEST_EXECUTE = "testExecute";
+
+    // special option to use compileIfNecessary
+    private String precompileClassDir;
+
+    private static final String
+	UNRECOGNIZED_ARG      = "Unrecognized argument for script: ";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/Deprecated.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,52 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+/**
+ * This class should take the hit for all deprecated methods used in this
+ * package.  
+ */
+class Deprecated
+{
+    /**
+     * This method is for use in place of calls to the deprecated constructors
+     * for PrintStream().  It is necessary to keep using PrintStreams for
+     * java.lang.System.setOut(), java.lang.System.setErr(), or calls to things
+     * in sun.tools that have no alternative entry points that do not use
+     * PrintStreams.
+     *
+     * @param out  The output stream to which values and objects will be
+     *             printed.
+     * @return     A PrintStream.
+     */
+    static PrintStream createPrintStream(OutputStream out) {
+	return new PrintStream(out);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/ExecStdTestOtherJVMCmd.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,77 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import com.sun.javatest.Status;
+
+/**
+ * This is a modification of <code>ProcessCommand</code> suitable
+ * for executing standard tests in a separate JVM.  When run in a
+ * separate process, these tests report their exit status by calling
+ * <code>Status.exit()</code>.  
+ **/
+public class ExecStdTestOtherJVMCmd extends ProcessCommand
+{
+
+    /**
+     *	Generate a status for the command, based upon the command's exit code
+     *  and a status that may have been passed from the command by using 
+     *  <code>status.exit()</code>.
+     *
+     *  @param exitCode		The exit code from the command that was executed.
+     *  @param logStatus	If the command that was executed was a test program
+     *				and exited by calling <code>status.exit()</code>,
+     *				then logStatus will be set to `status'.  Otherwise,
+     *				it will be null.  The value of the status is passed
+     *				from the command by writing it as the last line to
+     *				stdout before exiting the process.   If it is not
+     *				received as the last line, the value will be lost.
+     *  @return			If <code>logStatus</code> is not null, it will
+     *				be used as the result; this will normally correspond
+     *				to the status for which the test called 
+     *				<code>status.exit();</code>. 
+     *				<p> If <code>logStatus</code> is null, that means
+     *				that for some reason the test did not successfully
+     *				call <code>status.exit()</code> and the test is
+     *				deemed to have failed. If the exit code is zero,
+     *				a likely possibility is that the test raised an
+     *				exception which caused the JVM to dump the stack
+     *				and exit. In this case, the result is
+     *				<code>Status.failed("exit without status, exception assumed")</code>.
+     *				In other cases, the result is simply
+     *				<code>Status.failed("exit code" + exitCode)</code>.
+     *			
+     **/
+    protected Status getStatus(int exitCode, Status logStatus) {
+	if (logStatus != null)
+	    return logStatus;
+	else if (exitCode == 0)
+	    return Status.failed("exit without status, exception assumed");
+	else
+	    return Status.failed("exit code " + exitCode);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/ExecStdTestSameJVMCmd.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,155 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import java.io.File;
+import java.io.PrintWriter;
+import com.sun.javatest.Command;
+import com.sun.javatest.Status;
+import com.sun.javatest.Test;
+import com.sun.javatest.util.DirectoryClassLoader;
+
+
+/**
+ * ExecStdTestSameJVMCmd executes a standard test (one that implements
+ * the Test interface) in the same Java Virtual Machine as the caller.
+ *
+ * It can use either a private class loader or the system class loader.
+ * A private class loader will be created if the -loadDir option is given;
+ * otherwise the system class loader will be used.  A private class
+ * loader minimises the interference between tests, but you may be
+ * restricted from using private class loaders if you are running the
+ * harness inside a web browser.
+ *
+ * <p> If the the <code>-repeat</code> option is provided, then the test will be
+ * run multiple times in the same JVM.  <code>Status.error()</code> will be
+ * returned (and the remainder of the iterations will not be performed) if any
+ * repetition of the test returns an error, or if the status return type changes
+ * between iterations.  The returned status after each iteration will be
+ * included in the log. If this option is not given, the test will be run once.
+ *
+ * @see com.sun.javatest.lib.ExecStdTestOtherJVMCmd
+ */
+public class ExecStdTestSameJVMCmd extends Command
+{
+    /**
+     * The method that that does the work of the command.
+     * @param args	[-loadDir <em>dir</em>] [-saveProps] <em>executeClass</em> <em>executeArgs</em>
+     * @param log	A stream to which to report messages and errors
+     * @param ref	A stream to which to write reference output
+     * @return		The result of the command
+     */
+    public Status run(String[] args, PrintWriter log, PrintWriter ref) {
+	int repeat = 1;
+	String className = null;
+	String[] executeArgs = { };
+	ClassLoader loader = getClassLoader();
+
+	int i = 0;
+
+	for (; i < args.length && args[i].startsWith("-"); i++) {
+	    if ("-loadDir".equals(args[i]) && i+1 < args.length) {
+		// -loadDir is optional; if given, a new class loader will be created
+		// to load the class to execute;  if not given, the system class loader
+		// will be used.
+		loader = new DirectoryClassLoader(new File(args[++i]));
+	    } else if ("-repeat".equals(args[i]) && i+1 < args.length) {
+		// -repeat is optional; if given, the test will be run that
+		// number of times (in the same JVM)
+		try {
+		    if ((repeat = Integer.parseInt(args[++i])) < 1)
+			return Status.error("Unexpected number of repetitions: " + repeat);
+		}
+		catch (NumberFormatException e) {
+		    return Status.error("Unrecognized number of repetitions: " + repeat);
+		}
+	    }
+	}
+
+	// Next must come the executeClass
+	if (i < args.length) {
+	    className = args[i];
+	    i++;
+	} else
+	    return Status.failed("No executeClass specified");
+
+	// Finally, any optional args
+	if (i < args.length) {
+	    executeArgs = new String[args.length - i];
+	    System.arraycopy(args, i, executeArgs, 0, executeArgs.length);
+	}
+
+	Status status = null;
+	try {
+	    Class c;
+	    if (loader == null)
+		c = Class.forName(className);
+	    else
+		c = loader.loadClass(className);
+
+	    Status prevStatus = null;
+	    for (int j = 0; j < repeat; j++) {
+		if (repeat > 1)
+		    log.println("iteration: " + (j+1));
+
+		Test t = (Test) (c.newInstance());
+		status = t.run(executeArgs, log, ref);
+
+		if (repeat > 1)
+		    log.println("   " + status);
+
+		if ((prevStatus != null) && status.getType() != prevStatus.getType())
+		    status = Status.error("Return status type changed at repetition: " + (j+1));
+
+		if (status.isError())
+		    return status;
+		else
+		    prevStatus = status;
+	    }
+	}
+        catch (ClassCastException e) {
+	    status = Status.failed("Can't load test: required interface not found");
+	}
+	catch (ClassNotFoundException e) {
+	    status = Status.failed("Can't load test: " + e);
+	}
+	catch (InstantiationException e) {
+	    status = Status.failed("Can't instantiate test: " + e);
+	}
+	catch (IllegalAccessException e) {
+	    status = Status.failed("Illegal access to test: " + e);
+	}
+	catch (VerifyError e) {
+	    return Status.failed("Class verification error while trying to load test class `" + className + "': " + e);
+	}
+	catch (LinkageError e) {
+	    return Status.failed("Class linking error while trying to load test class `" + className + "': " + e);
+	}
+	return status;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/JavaCompileCommand.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,317 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.InvocationTargetException;
+import com.sun.javatest.Command;
+import com.sun.javatest.Status;
+import com.sun.javatest.util.PathClassLoader;
+import com.sun.javatest.util.WriterStream;
+
+/**
+ * Invoke a Java compiler via reflection.
+ * The compiler is assumed to have a constructor and compile method
+ * matching the following signature:
+ *<pre>
+ * public class COMPILER {
+ *     public COMPILER(java.io.OutputStream out, String compilerName);
+ *     public boolean compile(String[] args);
+ * }
+ *</pre>
+ * or
+ *<pre>
+ * public class COMPILER {
+ *     public COMPILER();
+ *     public int compile(String[] args);
+ * }
+ *</pre>
+ * or
+ * <pre>
+ * public class COMPILER {
+ *    public static int compile(String[] args, PrintWriter out);
+ * }
+ * </pre>
+ * This means the command is suitable for (but not limited to) the
+ * compiler javac suplied with JDK. (Note that this uses an internal
+ * API of javac which is not documented and is not guaranteed to exist
+ * in any specific release of JDK.)
+ */
+public class JavaCompileCommand extends Command
+{
+    /**
+     * A stand-alone entry point for this command. An instance of this
+     * command is created, and its <code>run</code> method invoked,
+     * passing in the command line args and <code>System.out</code> and
+     * <code>System.err</code> as the two streams.
+     * @param args command line arguments for this command.
+     * @see #run
+     */
+    public static void main(String[] args) {
+	PrintWriter out = new PrintWriter(System.out);
+	PrintWriter err = new PrintWriter(System.err);
+	Status s;
+	try {
+	    JavaCompileCommand c = new JavaCompileCommand();
+	    s = c.run(args, out, err);
+	}
+	finally {
+	    out.flush();
+	    err.flush();
+	}
+	s.exit();
+    }
+
+
+    /**
+     * Invoke a specified compiler, or the default, javac.
+     * If the first word in the <code>args</code> array is "-compiler"
+     * the second is interpreted as the class name for the compiler to be 
+     * invoked, optionally preceded by a name for the compiler, separated
+     * from the class name by a colon.  If no -compiler is specified,
+     * the default is `javac:com.sun.tools.javac.Main'. If -compiler is specified
+     * but no compiler name is given before the class name, the default name
+     * will be `java ' followed by the classname. For example, `-compiler Main'
+     * will result in the class name being `Main' and the compiler name being
+     * `java Main'. After determining the class and compiler name,
+     * an instance of the compiler class will be created, passing it a stream
+     * using the <code>ref</code> parameter, and the name of the compiler.
+     * Then the `compile' method will be invoked, passing it the remaining
+     * values of the `args' parameter.  If the compile method returns true,
+     * the result will be a status of `passed'; if it returns `false', the
+     * result will be `failed'. If any problems arise, the result will be
+     * a status of `error'.
+     * @param args An optional specification for the compiler to be invoked,
+     *		followed by arguments for the compiler's compile method.
+     * @param log  Not used.
+     * @param ref  Passed to the compiler that is invoked.
+     * @return `passed' if the compilation is successful; `failed' if the
+     * 		compiler is invoked and errors are found in the file(s)
+     *		being compiler; or `error' if some more serios problem arose
+     *		that prevented the compiler performing its task.
+     */
+    public Status run(String[] args, PrintWriter log, PrintWriter ref) {
+
+	if (args.length == 0)
+	    return Status.error("No args supplied");
+
+	String compilerClassName = null;
+	String compilerName = null;
+	String classpath = null;
+	String[] options = null;
+
+	// If we find a '-' in the args, what comes before it are 
+	// options for this class and what comes after it are args 
+	// for the compiler class. If don't find a '-', there are no
+	// options for this class, and everything is handed off to
+	// the compiler class
+
+	for (int i = 0; i < args.length; i++) {
+	    if (args[i].equals("-")) {
+		options = new String[i];
+		System.arraycopy(args, 0, options, 0, options.length);
+		args = shift(args, i+1);
+		break;
+	    }
+	}
+	
+	if (options != null) {
+	    for (int i = 0; i < options.length; i++) {
+		if (options[i].equals("-compiler")) {
+		    if (i + 1 == options.length)
+			return Status.error("No compiler specified after -compiler option");
+		    
+		    String s = options[++i];
+		    int colon = s.indexOf(":");
+		    if (colon == -1) {
+			compilerClassName = s;
+			compilerName = "java " + s;
+		    }
+		    else {
+			compilerClassName = s.substring(colon + 1);
+			compilerName = s.substring(0, colon);
+		    }
+		}
+		else if (options[i].equals("-cp") || options[i].equals("-classpath")) {
+		    if (i + 1 == options.length)
+			return Status.error("No path specified after -cp or -classpath option");
+		    classpath = options[++i];
+		}
+		else if (options[i].equals("-verbose"))
+		    verbose = true;
+		else
+		    return Status.error("Unrecognized option: " + options[i]);
+	    }
+	}
+
+	this.log = log;
+
+	try {
+
+	    ClassLoader loader;
+	    if (classpath == null)
+		loader = null;
+	    else
+		loader = new PathClassLoader(classpath);
+
+	    Class compilerClass;
+	    if (compilerClassName != null) {
+		compilerClass = getClass(loader, compilerClassName);
+		if (compilerClass == null)
+		    return Status.error("Cannot find compiler: " + compilerClassName);
+	    }
+	    else {
+		compilerName = "javac";
+		compilerClass = getClass(loader, "com.sun.tools.javac.Main");  // JDK1.3+
+		if (compilerClass == null)
+		    compilerClass = getClass(loader, "sun.tools.javac.Main");  // JDK1.1-2
+		if (compilerClass == null)
+		    return Status.error("Cannot find compiler");
+	    }
+
+	    loader = null;
+
+	    Object[] compileMethodArgs;
+	    Method compileMethod = getMethod(compilerClass, "compile", // JDK1.4+
+					     new Class[] { String[].class, PrintWriter.class });
+	    if (compileMethod != null) 
+		compileMethodArgs = new Object[] { args, ref };
+	    else {
+		compileMethod = getMethod(compilerClass, "compile",   // JDK1.1-3
+					  new Class[] { String[].class });
+		if (compileMethod != null)
+		    compileMethodArgs = new Object[] { args };
+		else
+		    return Status.error("Cannot find compile method for " + compilerClass.getName());
+	    }
+
+	    Object compiler;
+	    if (Modifier.isStatic(compileMethod.getModifiers()))
+		compiler =  null;
+	    else {
+		Object[] constrArgs;
+		Constructor constr = getConstructor(compilerClass, // JDK1.1-2
+						    new Class[] { OutputStream.class, String.class });
+		if (constr != null) 
+		    constrArgs = new Object[] { new WriterStream(ref), compilerName };
+		else {
+		    constr = getConstructor(compilerClass, new Class[0]); // JDK1.3
+		    if (constr != null)
+			constrArgs = new Object[0];
+		    else
+			return Status.error("Cannot find suitable constructor for " + compilerClass.getName());
+		}
+		try {
+		    compiler = constr.newInstance(constrArgs);
+		}
+		catch (Throwable t) {
+		    t.printStackTrace(log);
+		    return Status.error("Cannot instantiate compiler");
+		}
+	    }
+
+	    Object result;
+	    try {
+		result = compileMethod.invoke(compiler, compileMethodArgs);
+	    }
+	    catch (Throwable t) {
+		t.printStackTrace(log);
+		return Status.error("Error invoking compiler");
+	    }
+
+	    // result might be a boolean (old javac) or an int (new javac)
+	    if (result instanceof Boolean) {
+		boolean ok = ((Boolean)result).booleanValue();
+		return (ok ? passed : failed);
+	    }
+	    else if (result instanceof Integer) {
+		int rc = ((Integer)result).intValue();
+		return (rc == 0 ? passed : failed);
+	    }
+	    else 
+		return Status.error("Unexpected return value from compiler: " + result);
+	}
+	finally {
+	    log.flush();
+	    ref.flush();
+	}    
+    }
+
+    private Class getClass(ClassLoader loader, String name) {
+	try { 
+	    return (loader == null ? Class.forName(name) : loader.loadClass(name));
+	}
+	catch (ClassNotFoundException e) {
+	    return null;
+	}
+    }
+
+    private Constructor getConstructor(Class c, Class[] argTypes) {
+	try {
+	    return c.getConstructor(argTypes);
+	}
+	catch (NoSuchMethodException e) {
+	    return null;
+	}
+	catch (Throwable t) {
+	    if (verbose)
+		t.printStackTrace(log);
+	    return null;
+	}
+    }
+
+    private Method getMethod(Class c, String name, Class[] argTypes) {
+	try {
+	    return c.getMethod(name, argTypes);
+	}
+	catch (NoSuchMethodException e) {
+	    return null;
+	}
+	catch (Throwable t) {
+	    if (verbose)
+		t.printStackTrace(log);
+	    return null;
+	}
+    }
+
+    private static String[] shift(String[] args, int n) {
+	String[] newArgs = new String[args.length - n];
+	System.arraycopy(args, n, newArgs, 0, newArgs.length);
+	return newArgs;
+    }
+
+    public static boolean defaultVerbose = Boolean.getBoolean("javatest.JavaCompileCommand.verbose");
+    private boolean verbose = defaultVerbose;
+    private PrintWriter log;
+
+    private static final Status passed = Status.passed("Compilation successful");
+    private static final Status failed = Status.failed("Compilation failed");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/KeywordScript.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,235 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import com.sun.javatest.Script;
+import com.sun.javatest.Status;
+import com.sun.javatest.TestResult;
+import com.sun.javatest.TestDescription;
+import com.sun.javatest.TestEnvironment;
+import com.sun.javatest.util.StringArray;
+
+/**
+ * Default script, which delegates to one of a number of scripts defined in
+ * environment entries, according to the keywords on the test description.
+ */
+public class KeywordScript extends Script 
+{
+    /** 
+     * Run the script, using the parameters set up by the standard initialization
+     * methods.
+     */
+    public void run() { 
+
+        PrintWriter trOut = getTestResult().getTestCommentWriter();
+        TestDescription td = getTestDescription();
+
+	for (int i = 0; i < scriptArgs.length; i++) {
+	    if (scriptArgs[i].equals("-debug"))
+		debug = true;
+	    else {
+                setStatus(Status.error("bad args for script: " + scriptArgs[i]));
+		return;
+            }   // else
+	}   // for
+
+	String prefix = "script.";
+	Set testKeys = td.getKeywordTable();
+	Vector choices = new Vector(); // the set of choices
+	Vector matches = new Vector(); // the set of matches
+	int wordsMatchingInMatches = 0;// the number of words matching
+
+    findMatch:
+	for (Iterator iter = env.keys().iterator(); iter.hasNext(); ) {
+	    String key = (String) (iter.next());
+
+	    // if the key does not begin with the `script.' prefix, ignore key
+	    if (!key.startsWith(prefix)) 
+		continue;
+
+	    if (debug)
+		trOut.println("CHECKING " + key);
+
+	    String keyList = key.substring(prefix.length()).replace('_', ' ').toLowerCase();
+	    String[] keys = StringArray.split(keyList);
+
+	    choices.addElement(keyList);
+
+	    if (debug)
+		trOut.println("keys: " + StringArray.join(keys));
+
+	    // if there are no words after the `script.' prefix,
+	    // or if it has fewer words than the best match so far, ignore key
+	    if (keys == null || keys.length < wordsMatchingInMatches) 
+		continue;
+
+	    for (int i = 0; i < keys.length; i++) {
+		// if key has a word that is not for the test, ignore key
+		if (!testKeys.contains(keys[i])) {
+
+		    if (debug)
+			trOut.println("discarding, because of " + keys[i]);
+
+		    continue findMatch;
+		}
+	    }
+
+	    // see if key is better than best so far
+	    if (keys.length > wordsMatchingInMatches) {
+		// update best so far
+
+		if (debug)
+		    trOut.println("new best match, " + keys.length + " keys");
+
+		matches = new Vector();
+		wordsMatchingInMatches = keys.length;
+	    } 
+	    
+	    // this key deserves note
+	    matches.addElement(key);
+	}   // for
+
+	// check we have a unique script selected
+	String name = env.getName();
+	String envName = (name.length() == 0 ? 
+			  "The anonymous environment" : 
+			  "Environment `" + env.getName() + "'");
+	if (matches.size() == 0) {
+	    if (choices.size() == 0) {
+		String s = envName + " has no `script' entries";
+		trOut.println(s);
+		setStatus(Status.error(s));
+		return;
+	    }
+	    else {
+		String s = envName + " has no suitable `script' entry";
+		trOut.println(s);
+		trOut.println("The keyword combinations for scripts in this environment are: ");
+		for (int i = 0; i < choices.size(); i++) {
+		    trOut.println((String)choices.elementAt(i));
+                }   // for
+
+		setStatus(Status.error(s));
+		return;
+	    }   // inner else
+	} else if (matches.size() > 1) {
+	    String s = envName + " has ambiguous `script' entries";
+	    trOut.println(s);
+	    for (int i = 0; i < matches.size(); i++) {
+		trOut.println(i + ": " + matches.elementAt(i));
+            }   // for
+
+	    setStatus(Status.error(s));
+	    return;
+	}   // else if
+
+	String bestScript = (String)matches.elementAt(0);
+	//trOut.report.println("BEST " + bestScript);
+
+        try {
+	    String[] command = env.lookup(bestScript);
+	    if (command.length == 0) {
+		String s = "INTERNAL ERROR: failed to lookup key: " + bestScript;
+		trOut.println(s);
+		setStatus(Status.error(s));
+		return;
+	    }
+
+	    trOut.println("test: " + td.getRootRelativeURL());
+	    trOut.println("script: " + this.getClass().getName() + " " +
+			  StringArray.join(scriptArgs));
+	    
+	    String[] msgs = {
+		"Based on these keywords:    " + 
+		bestScript.substring(prefix.length()).replace('_', ' ').toLowerCase(),
+		"this script has now been selected: " + "   " +
+		StringArray.join(command) };
+	    printStrArr(trOut, msgs);
+	    
+	    try {
+		Class c = Class.forName(command[0]);
+
+		Script script = (Script)c.newInstance();
+		String[] scriptArgs = new String[command.length - 1];
+		System.arraycopy(command, 1, scriptArgs, 0, scriptArgs.length);
+		initDelegate(script, scriptArgs);
+
+		script.run();
+	    }
+	    catch (ClassNotFoundException ex) {
+		setStatus(Status.error("Can't find class `" +
+                                     command[0] + "' for `" + env.getName() + "'"));
+	    } 
+	    catch (IllegalAccessException ex) {
+		setStatus(Status.error("Illegal access to class `" +
+                                     command[0] + "' for `" + env.getName() + "'"));
+	    } 
+	    catch (InstantiationException ex) {
+		setStatus(Status.error("Can't instantiate class`" +
+                                     command[0] + "' for `" + env.getName() + "'"));
+	    }
+	} 
+	catch (TestEnvironment.Fault ex) {
+	    setStatus(Status.error("environment `" +
+                                      env.getName() +
+                                      "' has bad `script' entry for `" +
+                                      bestScript +"'"));
+	}
+    }
+
+    public Status run(String[] args, TestDescription td, TestEnvironment env) {
+        throw new Error("Method not applicable.");
+    }
+
+    private static void printStrArr(PrintWriter pw, String[] data) {
+        if(data == null) return;
+
+        for(int i = 0; i < data.length; i++) {
+            pw.println(data[i]);
+        }
+    }
+
+    private void setStatus(Status s) {
+	TestResult tr = getTestResult();
+	tr.setEnvironment(env);
+	tr.setStatus(s);
+        try {
+            tr.writeResults(workDir, backupPolicy);
+        }
+	catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static boolean debug = false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/MultiStatus.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,246 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import java.io.PrintWriter;
+import com.sun.javatest.Status;
+
+/**
+ * When executing multiple test cases in the same test class, it is usually 
+ * easier for each test case to return a Status object representing whether that 
+ * individual test case passed or failed.  However, combining those individual
+ * Status objects into one Status object to represent the overall Status of the
+ * tests executed can be difficult.  This test library is designed to solve the 
+ * problem of generating an aggregate or overall Status from multiple Status 
+ * objects.  The static method overallStatus is designed to take an array of 
+ * Status objects from individual test cases and generate a Status object that 
+ * correctly reflects the overall status of all the individual the test 
+ * cases executed.
+ *
+ * <P>The rule for how MultiStatus calculates the overall Status of an array of 
+ * Status objects is based on the following precedence:
+ * <BLOCKQUOTE>
+ * If any of the test cases return a Status.FAILED, then the overall status is 
+ * Status.FAILED.<BR>
+ * If all test cases return Status.PASSED, then the overall status is 
+ * Status.PASSED.<BR>
+ * If at least one test case returns either a null Status or some other Status, 
+ * the overall status is Status.FAILED.
+ * </BLOCKQUOTE>
+ *
+ * <P>For an example of how to use this library see the UmbrellaTest library or the 
+ * JCK test case: <TT>tests/api/java_lang/Double/SerializeTests.html</TT>.
+ */
+public class MultiStatus {
+
+    /**
+     * Create a MultiStatus object to accumulate individual Status objects.
+     */
+    public MultiStatus() {
+    }
+
+    /**
+     * Create a MultiStatus object to accumulate individual Status objects.
+     * @param out    A stream to which the report the outcome of the tests.
+     *			 If the stream is null, no reporting is done.
+     */
+    public MultiStatus(PrintWriter out) {
+	this.out = out;
+    }
+
+    /**
+     * Get the number of individual test results that have been added.
+     * @return the number of individual results that have been added.
+     */
+    public int getTestCount() {
+	return iTestCases;
+    }
+
+    /** 
+     * Add another test result into the set for consideration.
+     *
+     * @param testID A name for this test case.  Should not be null.
+     * @param status The outcome of this test case
+     */
+    public void add(String testID, Status status) {
+	if (out != null) {
+	    out.println(testID + ": " + status);
+	}
+	
+	++iTestCases;
+	
+	if (status != null) {
+	    int t = status.getType();
+
+	    switch (t) {
+	    case Status.PASSED:
+		++iPassed;
+		break;
+		
+	    case Status.FAILED:
+		++iFail;
+		break;
+		
+	    case Status.ERROR:
+		++iError;
+		break;
+		
+	    default:
+		++iBad;
+		break;
+	    }
+
+	    if (t != Status.PASSED && firstTestCase.length() == 0)
+		firstTestCase = testID;
+	}
+    }
+    
+    /**
+     * Get the aggregate outcome of all the outcomes passed to "add".
+     * @return the aggregate outcome
+     */
+    public Status getStatus() {
+
+	// If stream not null, flush it
+	if( out != null ) {
+	    out.flush();
+	}
+
+	String summary;
+	if (iTestCases == 0)
+	    summary = "No tests cases found (or all test cases excluded.)";
+	else {
+	    summary = "test cases: " + iTestCases;
+	    if (iPassed > 0) {
+		if (iPassed == iTestCases)
+		    summary += "; all passed";
+		else
+		    summary += "; passed: " + iPassed;
+	    }
+
+	    if (iFail > 0) {
+		if (iFail == iTestCases)
+		    summary += "; all failed";
+		else
+		    summary += "; failed: " + iFail;
+	    }
+
+	    if (iError > 0) {
+		if (iError == iTestCases)
+		    summary += "; all had an error";
+		else
+		    summary += "; error: " + iError;
+	    }
+		
+	    if (iBad > 0) {
+		if (iBad == iTestCases)
+		    summary += "; all bad";
+		else
+		    summary += "; bad status: " + iBad;
+	    }
+	}
+
+	/* Return a status object that reflects the aggregate of the various test cases. */
+
+        /* At least one test case was bad */
+	if (iBad > 0) {
+	    return Status.error(summary + "; first bad test case result found: " + firstTestCase);
+	}
+	/* At least one test case was bad */
+	else if (iError > 0) {
+	    return Status.error(summary + "; first test case with error: " + firstTestCase);
+	}
+        /* At least one test case failed */
+	else if (iFail > 0) {
+	    return Status.failed(summary + "; first test case failure: " + firstTestCase);
+	}
+	/* All test cases passed */
+	else {
+	    return Status.passed(summary);
+	}
+    }
+
+
+    /**
+     * Generates a Status object that reflects an array of Status objects.
+     * Uses the algorithm above to generate an overall status from an array of 
+     * Status objects.  This method prints out the individual Status values from 
+     * each test case to the PrintWriter supplied.  If the PrintWriter is null,
+     * no output is generated.
+     *
+     * @param testIDs an array of names used to identify the individual test cases.
+     * @param status an array of Status objects giving the outcomes of the individual test cases.
+     * @param out a PrintWriter that can be used to output the individual test case
+     *            status values. If null, no output is generated.	
+     * @return the aggregate status of the array of Status objects.
+     */
+    public static Status overallStatus(String testIDs[], Status status[], PrintWriter out) {
+
+	/* Check the number of tests against the number of statuses */
+	if( testIDs.length != status.length ) {
+	    return Status.failed( "mismatched array sizes; test cases: " + testIDs.length +
+				  " statuses: " + status.length );
+	}
+
+        /* Loop through status objects, check types,
+         * increment appropriate counters, and identify the
+         * first test that should be listed in the aggregate status.
+         */
+	
+	MultiStatus ms = new MultiStatus(out);
+        for( int i = 0; i < status.length; ++i ) {
+	    ms.add(testIDs[i], status[i]);
+	}
+
+	return ms.getStatus();
+    }
+
+
+    /**
+     * Generates a Status object that reflects an array of Status objects.
+     * Uses the algorithm above to generate an overall status from an array of 
+     * Status objects.  This method does not output any information
+     *
+     * @param testIDs an array of names used to identify the individual test cases.
+     * @param status an array of Status objects giving the outcomes of the individual test cases.
+     * @return overall status of the specified array of Status objects.
+     */
+    public static Status overallStatus(String testIDs[], Status status[]) {
+	return MultiStatus.overallStatus(testIDs, status, null);
+    }
+
+    // These values accumulate the aggregate outcome, without requiring
+    // the individual outcomes be stored
+    private int iTestCases = 0;
+    private int iPassed = 0;
+    private int iFail = 0;
+    private int iError = 0;
+    private int iBad = 0;
+    private String firstTestCase = "";
+
+    private PrintWriter out = null;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/MultiTest.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,314 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import com.sun.javatest.lib.TestCases;
+import com.sun.javatest.Status;
+import com.sun.javatest.Test;
+
+/**
+ * Base class for tests with multiple sub test cases.
+ * This base class implements the standard com.sun.javatest.Test 
+ * features so that you can provide the additional test cases without concern about
+ * the boilerplate needed to execute individual test case methods. 
+ *
+ * <P>You must add individual test case methods to your derived test class
+ * to create a useful test class. Each test case method must take no 
+ * arguments.  If you need to pass an argument into a test method, you should 
+ * design a wrapper test case to calculate the argument values and then call 
+ * the test method with the correct arguments.  The test case methods must 
+ * implement this interface: 
+ * <blockquote>
+ * <strong><code>public Status methodName( )</code></strong>
+ * </blockquote>
+ *
+ * @see com.sun.javatest.Test
+ * @see com.sun.javatest.lib.TestCases
+ */
+public class MultiTest implements Test
+{
+    /**
+     * This exception is thrown when a problem occurs initializing the test.
+     * It may also be used to indicate that the test is not applicable in the
+     * current circumstances and should not be run.
+     */
+    public static class SetupException extends Exception {
+	/**
+	 * Construct a new SetupException object that signals failure
+	 * with a corresponding message.
+	 * 
+	 * @param s the string containing a comment
+	 */
+	public SetupException(String s) {
+	    super(s);
+	}
+	
+	/**
+	 * Creates a SetupException object which indicates that 
+	 * this test is not applicable. The cases when it is needed 
+	 * are rare, so please think twice whether you really need it.
+	 * 
+	 * @param msg a detail string, explaining why the test is "not applicable".
+	 * @return an exception object that indicates the test should not be run
+	 *   because it is not applicable.
+	 */
+	public static SetupException notApplicable(String msg) {
+	    SetupException e = new SetupException("Test not applicable: " + msg);
+	    e.passed = true;
+	    return e;
+	}
+	
+	/**
+	 * Determines whether this SetupException signals failure or not.
+	 * @return true if and only if the test is not applicable and should be
+	 * deemed to have "passed, by default".
+	 * 
+	 */
+	public boolean isPassed() {
+	    return passed;
+	    
+	}
+
+	/**
+	 * Indicate whether this exception was the result of calling {@link #notApplicable}.
+	 * @serial
+	 */
+	private boolean passed = false;
+    }
+
+
+    /** 
+     * Run the test cases contained in this object. The test cases are determined
+     * and invoked via reflection. The set of test cases can be specified with
+     * -select case1,case2,case3... and/or restricted with -exclude case1,case2,case3...
+     *
+     * @see #decodeAllArgs
+     * @see #init
+     *
+     * @param args Execute arguments passed in from either the 
+     *             command line or the execution harness.
+     * @param log Output stream for general messages from the tests.
+     * @param ref Output stream for reference output from the tests.
+     * @return Overall status of running all of the test cases.
+     */
+    public Status run(String[] args, PrintWriter log, PrintWriter ref) {
+	this.log = log;
+	this.ref = ref;
+	testCases = new TestCases(this, log);
+
+	Status initStatus = init(args);
+	if (testNotApplicable 
+	    || (initStatus != null && initStatus.getType( ) != Status.PASSED)) {
+	    return initStatus;
+	}
+
+	return testCases.invokeTestCases();
+    }
+
+    /** 
+     * Run the test cases contained in this object
+     *
+     * This method is a convenience wrapper around the primary run method
+     * which takes PrintWriters: this variant takes PrintStreams and wraps
+     * them into PrintWriters.
+     *
+     * @see #decodeAllArgs
+     * @see #init
+     *
+     * @param argv Execute arguments passed in from either the 
+     *             command line or the execution harness.
+     * @param log Output stream for general messages from the tests.
+     * @param ref Output stream for reference output from the tests.
+     * @return Overall status of running all of the test cases.
+     */
+    public final Status run(String[] argv, PrintStream log, PrintStream ref) {
+	PrintWriter pwLog = new PrintWriter(new OutputStreamWriter(log));
+	PrintWriter pwRef = new PrintWriter(new OutputStreamWriter(ref));
+	try {
+	    return run(argv, pwLog, pwRef);
+	}
+	finally {
+	    pwLog.flush();
+	    pwRef.flush();
+	}
+    }
+
+
+    /** 
+     * 
+     * Initialize the test from the given arguments. The arguments will
+     * be passed to <code>decodeAllArgs</code>, and then <code>init()</code>
+     * will be called.
+     *
+     * @param args The arguments for the test, passed to <code>decodeArgs</code>.
+     * @return null if initialization is successful, or a status indicating why
+     * initialization was not successful.
+     * 
+     * @see #decodeAllArgs
+     * @see #decodeArg
+     * @see #init()
+     *
+     * @deprecated Use <code>decodeArg(String)</code> and <code>init()</code> instead.
+     */
+    protected Status init(String[] args) { 
+	try {
+	    decodeAllArgs(args);
+	    init();
+	    return null;
+	}
+	catch (SetupException e) {
+	    testNotApplicable = true;
+	    return (e.isPassed() 
+		    ? Status.passed(e.getMessage()) 
+		    : Status.failed(e.getMessage()) );
+	}
+    }
+
+    /** 
+     * A setup method called after argument decoding is complete,
+     * and before the test cases are executed. By default, it does
+     * nothing; it may be overridden to provide additional behavior. 
+     *
+     * @throws MultiTest.SetupException if processing should not continue.
+     * This may be due to some inconsistency in the arguments,
+     * or if it is determined the test should not execute for
+     * some reason.
+     */
+    protected void init() throws SetupException { }
+
+    /** 
+     * Parses the arguments passed to the test.
+     *
+     * This method embodies the main loop for all of the test's arguments. 
+     * It calls <code>decodeArg</code> for successive arguments in the 
+     * argument array.
+     *
+     * @param args arguments passed to the test.
+     *
+     * @throws MultiTest.SetupException raised when an invalid parameter is passed, 
+     * or another error occurred.
+     *
+     * @see #decodeArg 
+     */
+    protected final void decodeAllArgs(String args[]) throws SetupException {
+	int i = 0;
+	while (i < args.length) {
+	    int elementsConsumed = decodeArg(args, i);
+	    if (elementsConsumed == 0 ) {
+		// The argument was not recognized.
+		throw new SetupException("Could not recognize argument: " + args[i]);
+	    }
+	    i += elementsConsumed;
+	}
+    }
+
+    /**
+     * Decode the next argument in the argument array.  This will typically be
+     * overridden by subtypes that wish to decode additional arguments. If an
+     * overriding method does not recognize an argument, it should return
+     * <code>super.decodeArg(args, index)</code> to give supertypes a change
+     * to decode the argument as well.
+     * 
+     * @param args The array containing all the arguments
+     * @param index The position of the next argument to be decoded.
+     * @return the number of elements in the array were "consumed" by this call.
+     *
+     * @throws MultiTest.SetupException is there is a problem decoding the
+     *   argument. 
+     */
+    protected int decodeArg(String[] args, int index) throws SetupException {
+	try {
+	    if (args[index].equals("-select") && index + 1 < args.length) {
+		testCases.select(args[index + 1]);
+		return 2;
+	    }
+	    else if (args[index].equals("-exclude") && index + 1 < args.length) {
+		testCases.exclude(args[index + 1]);
+		return 2;
+	    }
+	    // support -TestCaseID for historical compatibility
+	    else if (args[index].equals("-TestCaseID")) {
+		if (index + 1 < args.length && args[index + 1].equals("ALL")) {
+		    // ignore -TestCaseID ALL, since it is the default
+		    return 2;
+		}
+		else {
+		    int i;
+		    for (i = index + 1; i < args.length && !args[i].startsWith("-"); i++) 
+			testCases.select(args[i]);
+		    return (i - index);
+		}
+		
+	    }
+	    else if (args[index].equals("-autoFlush")) {
+                ref = new PrintWriter(ref, true);
+                log = new PrintWriter(ref, true);
+                return 1;
+            }
+	    else 
+		return 0;
+	}
+	catch (TestCases.Fault e) {
+	    throw new SetupException(e.getMessage());
+	}
+    }
+
+    /**
+     * Default way to invoke a specified test case.
+     * @param m The method to be invoked.
+     * @return The result of invoking the specified test case.
+     * @throws IllegalAccessException if there was a problem accessing the specified method
+     * @throws InvocationTargetException if the specified method threw an exception when 
+     * it was invoked.
+     */
+    protected Status invokeTestCase(Method m) 
+		throws IllegalAccessException, InvocationTargetException {
+	Object[] testArgs = { };
+	return (Status) (m.invoke(this, testArgs));
+    }
+
+    // the set of test cases to be executed
+    private TestCases testCases;
+
+    // may be set if SetupException is thrown during decodeArgs() or init
+    private boolean testNotApplicable;
+
+    /** 
+     * Output to be logged to result file. 
+     */
+    protected PrintWriter ref;
+
+    /** 
+     * Output to be logged to result file.
+     */
+    protected PrintWriter log;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/ProcessCommand.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,462 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.util.Hashtable;
+
+
+import com.sun.javatest.Command;
+import com.sun.javatest.Status;
+import com.sun.javatest.util.StringArray;
+
+/**
+ * A Command to execute an arbitrary OS command.
+ **/
+public class ProcessCommand extends Command
+{
+    /**
+     * A stand-alone entry point for this command. An instance of this
+     * command is created, and its <code>run</code> method invoked,
+     * passing in the command line args and <code>System.out</code> and
+     * <code>System.err</code> as the two streams.
+     * @param args command line arguments for this command.
+     * @see #run
+     */
+    public static void main(String[] args) {
+	PrintWriter log = new PrintWriter(new OutputStreamWriter(System.err));
+	PrintWriter ref = new PrintWriter(new OutputStreamWriter(System.out));
+	Status s;
+	try {
+	    Command cmd = new ProcessCommand();
+	    s = cmd.run(args, log, ref);
+	}
+	finally {
+	    log.flush();
+	    ref.flush();
+	}
+	s.exit();
+    }
+
+    /**
+     * Set a status to be returned for a specific exit code, overwriting any
+     * previous setting for this exit code. If the default status has not yet 
+     * been initialized, it is set to Status.error("unrecognized exit code").
+     *
+     * @param exitCode The process exit code for which to assign a status.
+     * @param status The status to associate with the exit code.
+     */
+    public void setStatusForExit(int exitCode, Status status) {
+	if (statusTable == null) {
+	    statusTable = new Hashtable();
+	    if (defaultStatus == null)
+		defaultStatus = Status.error("unrecognized exit code");
+	}
+	statusTable.put(new Integer(exitCode), status);
+    }
+
+    /**
+     * Set the default status to be returned for all exit codes. 
+     * This will not affect any values for specific exit codes that 
+     * may have been set with setStatusForExit. If this method is
+     * not called, the default value will be Status.failed (for
+     * backwards compatability) unless setStatusForExit has been
+     * called, which sets the default value to Status.error.  
+     *
+     * @param status The default status to use when a specific status
+     * has not been set for a particular process exit code.
+     */ 
+    public void setDefaultStatus(Status status) {
+	if (statusTable == null) 
+	    statusTable = new Hashtable();
+	defaultStatus = status;
+    }
+
+    /**
+     * Set the directory in which to execute the process.
+     * Use null to indicate the default directory.
+     * @param dir the directory in which to execute the process.
+     * @see #getExecDir
+     */
+    public void setExecDir(File dir) {
+	execDir = dir;
+    }
+
+    /**
+     * Get the directory in which to execute the process,
+     * or null if none set.
+     * @return the directory in which to execute the process.
+     * @see #setExecDir
+     */
+    public File getExecDir() {
+	return execDir;
+    } 
+
+    /**
+     * Internal routine to assist argument decoding prior to calling
+     * setStatusForExit or setDefaultStatus
+     */
+    private void setStatus(String exitSpec, Status status) {
+	// for now, we just support "default" and <integer>
+	// in principle we could support ranges and lists too
+	if (exitSpec.equals("default"))
+	    setDefaultStatus(status);
+	else 
+	    setStatusForExit(Integer.parseInt(exitSpec), status);
+    }
+
+    /**
+     * Run the given command.
+     * @param args	An array of strings composed of
+     * 			<em>command-options</em>,
+     *			<em>environment-variables</em>,
+     *			<em>command</em>,
+     *			<em>command-arguments</em>.
+     *			<br>
+     *
+     *			The <em>command-options</em> are an optional
+     *			set of options, each beginning with `-', to be
+     *			used by this object.
+     *			The options are
+     *			<dt>
+     *			<dt>-v   
+     *		       	<dd>	verbose mode
+     *			<dt>-pass|-fail|-error <i>exit-code</i> <i>string</i>
+     *			<dd>	set the status to be returned for the given 
+     *				exit code to one of 
+     *				Status.passed/Status.failed/Status.error.
+     *				<i>exit-code</i> can be either an integer or "default".
+     *				<i>string</i> the message string provided in the
+     *				status object.
+     *                  <dt>-execDir <i>execDir</i>
+     *			<dd>    set the directory in which to execute the command.
+     *			</dl>
+     *			<br>
+     *			
+     *			The <em>environment-variables</em> are an
+     *			optional list of environment variable to be supplied
+     *			to the command. They should be in the form
+     *			<em>NAME</em><code>=</code><em>VALUE</em>.
+     *			<br>
+     *
+     *			The <em>command</em> identifies the command to
+     *			be executed. This name will be platform specific.
+     *			<br>
+     *
+     *			The <em>command-arguments</em> are an optional
+     *			list of strings to be passed to the command to be
+     *			executed.
+     * @param log	A stream for logging output.
+     * @param ref	A stream for reference output, if the test requires it.
+     *			If not, it can be used as an additional logging stream.
+     *
+     * @return 		The result of the method is obtained by calling
+     *			<code>getStatus</code> after the command completes.
+     *			The default behaviour is to use the explicit or default
+     *			status given in the arguments, or via the API. If none
+     *			have been set up, then the following values are used:
+     *			<code>Status.passed("exit code 0")</code>
+     *			if the command exited with exit status 0, or
+     *			<code>Status.failed("exit code " + exitCode)</code>
+     *			otherwise.  <code>getStatus</code> may be overridden
+     *			to provide different behavior.
+     *
+     * @see #getStatus
+     *
+     **/
+    public Status run(String[] args, PrintWriter log, PrintWriter ref) {
+	// analyse options
+	int i = 0;
+	for (; i < args.length && args[i].startsWith("-"); i++) {
+	    if (args[i].equals("-v"))
+	    	verbose = true;
+	    else if (args[i].equals("-execDir") && i+1 < args.length) {
+		execDir = new File(args[++i]);
+	    }
+	    else if (args[i].equals("-pass") && i+2 < args.length) {
+		setStatus(args[++i], Status.passed(args[++i]));
+	    }
+	    else if (args[i].equals("-fail") && i+2 < args.length) {
+		setStatus(args[++i], Status.failed(args[++i]));
+	    }
+	    else if (args[i].equals("-error") && i+2 < args.length) {
+		setStatus(args[++i], Status.error(args[++i]));
+	    }
+	    else if (args[i].equals("-end")) { 
+		// -end is supported for the improbable event someone wants an 
+		// env var or command beginning with -
+		i++;  // because the for-loop won't get a chance to do it
+		break;
+	    }
+	    else
+		return Status.error("Unrecognized option: " + args[i]);
+	}
+
+	// get environment variables for command
+	int cmdEnvStart = i;
+	while (i < args.length && (args[i].indexOf('=') != -1))
+	    i++;
+	String[] cmdEnv = new String[i - cmdEnvStart];
+	System.arraycopy(args, cmdEnvStart, cmdEnv, 0, cmdEnv.length);
+
+	// get command name
+	if (i == args.length)
+	    return Status.error("no command specified for " + getClass().getName());
+
+	String[] cmd = new String[args.length - i];
+	System.arraycopy(args, i, cmd, 0, cmd.length);
+
+	return exec(cmd, cmdEnv, log, ref);
+    }
+
+    /**
+     * Exceute a command, bypassing the standard argument decoding of 'run'.
+     * @param cmd       The command to be executed
+     * @param cmdEnv    The environment to be passed to the command
+     * @param log	A stream for logging output.
+     * @param ref	A stream for reference output, if the test requires it.
+     *			If not, it can be used as an additional logging stream.
+     * @return 		The result of the method is obtained by calling
+     *			<code>getStatus</code> after the command completes.
+     * @see #run
+     * @see #getStatus
+     */
+    public Status exec(String[] cmd, String[] cmdEnv, PrintWriter log, PrintWriter ref) {
+	Process p = null;
+	Status s = null;
+	try {
+	    // The following is a workaround for a JDK problem ... if the cmdEnv
+	    // is empty, JDK assumes this means to inherit the parent environment.
+	    // (There is a separate call which more reasonably means that.)
+	    // So, to prevent the parent process' environment being inherited
+	    // we set the command environment to a dummy entry which will hopefully
+	    // not cause any problems for either the Runtime machinery or the
+	    // child process.
+	    if (cmdEnv != null && cmdEnv.length == 0) {
+		String[] envWithDummyEntry = {/*empty*/"="/*empty*/};
+		cmdEnv=envWithDummyEntry;
+	    }
+
+	    if (verbose) {
+		log.println("Command is: " + StringArray.join(cmd));
+		if (cmdEnv == null) {
+		    log.println("Command environment is inherited from parent process");
+		}
+		else if (cmdEnv.length == 0) {
+		    log.println("Command environment is empty");
+		}
+		else {
+		    log.println("Command environment is:");
+		    for (int i = 0; i < cmdEnv.length; i++)
+			log.println(cmdEnv[i]);
+		}
+		if (execDir != null)
+		    log.println("Execution directory is " + execDir);
+	    }
+
+	    Runtime r = Runtime.getRuntime();
+	    p = (execDir == null ? r.exec(cmd, cmdEnv) : r.exec(cmd, cmdEnv, execDir));
+
+	    Reader in = new InputStreamReader(p.getInputStream()); // output stream from process
+	    StreamCopier refConnector = new StreamCopier(in, ref);
+	    refConnector.start();
+	    Reader err = new InputStreamReader(p.getErrorStream());
+	    StreamCopier logConnector = new StreamCopier(err, log);
+	    logConnector.start();
+
+	    OutputStream out = p.getOutputStream();  // input stream to process
+	    if (out != null)
+		out.close();
+
+	    // wait for the stream copiers to complete (which may be interrupted by the
+	    // timeout thread
+	    refConnector.waitUntilDone();
+	    logConnector.waitUntilDone();
+
+	    // wait for the process to complete;
+	    // WARNING: in JDK1.0.2 this does not appear to be interruptible, which is
+	    // why we waited for the stream copiers to complete first ... because they are
+	    // interruptible.
+	    int exitCode = p.waitFor();
+	    //if (verbose > 0)
+	    //	log.report("command exited, exit=" + exitCode);
+
+	    in.close();
+	    err.close();
+
+	    return getStatus(exitCode, logConnector.exitStatus());
+	}
+	catch (InterruptedException e) {
+	    if (p != null)
+		p.destroy();
+	    String msg = "Program `" + cmd[0] + "' interrupted! (timed out?)";
+	    s = (useFailedOnException ? Status.failed(msg) : Status.error(msg));
+	}
+	catch (IOException e) {
+	    String msg = "Error invoking program `" + cmd[0] + "': " + e;
+	    s = (useFailedOnException ? Status.failed(msg) : Status.error(msg));
+	}
+	return s;
+    }
+
+    /**
+     * Generate a status for the command, based upon the command's exit code
+     * and a status that may have been passed from the command by using
+     * <code>status.exit()</code>.
+     *
+     * @param exitCode		The exit code from the command that was executed.
+     * @param logStatus		If the command that was executed was a test program
+     *				and exited by calling <code>status.exit()</code>,
+     *				then logStatus will be set to `status'.  Otherwise,
+     *				it will be null.  The value of the status is passed
+     *				from the command by writing it as the last line to
+     *				stdout before exiting the process.   If it is not
+     *				received as the last line, the value will be lost.
+     * @return		Unless overridden, the default is
+     *			<code>Status.passed("exit code 0")</code>
+     *			if the command exited with exit code 0, or
+     *			<code>Status.failed("exit code " + exitCode)</code>
+     *			otherwise.
+     **/
+    protected Status getStatus(int exitCode, Status logStatus) {
+	if (logStatus != null)
+	    return logStatus;
+	else if (statusTable != null) {
+	    Status s = (Status)(statusTable.get(new Integer(exitCode)));
+	    return (s == null ? defaultStatus.augment("exit code: " + exitCode) : s);
+	}
+	else if (exitCode == 0)
+	    return Status.passed("exit code 0");
+	else
+	    return Status.failed("exit code " + exitCode);
+    }
+
+    private boolean verbose;
+
+
+    /**
+     * A thread to copy an input stream to an output stream
+     */
+    class StreamCopier extends Thread
+    {
+	/**
+	 * Create one.
+	 * @param from 	the stream to copy from
+	 * @param out	the log to copy to
+	 */
+	StreamCopier(Reader from, PrintWriter to) {
+	    super(Thread.currentThread().getName() + "_StreamCopier_" + (serial++));
+	    in = new BufferedReader(from);
+	    out = to;
+	    lastStatusLine = null;
+	}
+
+	/**
+	 * Set the thread going.
+	 */
+	public void run() {
+	    //System.out.println("Copying stream");
+	    try {
+		String line;
+		while ((line = in.readLine()) != null) {
+		    out.println(line);
+		    // take care lastLine doesn't get set to null at EOF
+		    // and ignore trailing newlines
+		    if (line.startsWith(Status.EXIT_PREFIX))
+			lastStatusLine = line;
+		}
+	    }
+	    catch (IOException e) {
+	    }
+	    //System.out.println("Stream copied");
+	    synchronized (this) {
+		done = true;
+		notifyAll();
+	    }
+	}
+
+	public synchronized boolean isDone() {
+	    return done;
+	}
+
+	/**
+	 * Blocks until the copy is complete, or until the thread is interrupted
+	 */
+	public synchronized void waitUntilDone() throws InterruptedException {
+	    boolean interrupted = false;
+
+	    // poll interrupted flag, while waiting for copy to complete
+	    while (!(interrupted = Thread.interrupted()) && !done)
+		wait(1000);
+
+	    //if (interrupted)
+	    //    System.out.println("TESTSCRIPT DETECTS interrupted() " + Thread.currentThread().getName());
+	    //else
+	    //    System.out.println("TESTSCRIPT waitUntilDone OK " + Thread.currentThread().getName());
+
+	    // workaround; if the exception hasn't been thrown already, do it now
+	    if (interrupted) {
+		//System.out.println("Stream copier: throwing InterruptedException");
+		throw new InterruptedException();
+	    }
+	}
+
+	/**
+	 * Return the status information from the child process if it returned
+	 * any on the log stream, otherwise return null.
+	 */
+	public Status exitStatus() {
+	    if (lastStatusLine == null)
+		return null;
+	    else
+		return Status.parse(lastStatusLine.substring(Status.EXIT_PREFIX.length()));
+	}
+
+
+	private BufferedReader in;
+	private PrintWriter out;
+	private String lastStatusLine;
+	private boolean done;
+
+    }
+
+    private static boolean useFailedOnException = 
+	Boolean.getBoolean("javatest.processCommand.useFailedOnException");
+	
+    private static int serial;
+    private Hashtable statusTable;
+    private Status defaultStatus;
+    private File execDir;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/ReportScript.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,59 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import com.sun.javatest.Script;
+import com.sun.javatest.Status;
+import com.sun.javatest.TestDescription;
+import com.sun.javatest.TestEnvironment;
+import com.sun.javatest.TestResult;
+
+/**
+ * A special script which reads the result file which is presumed to exist in
+ * the work directory as the result of a prior run.  This will allow the
+ * creation of a TestResult object from a old test run.
+ */
+public class ReportScript extends Script 
+{
+    public final void run() {
+	TestResult tr = getTestResult();
+	try {
+	    tr.reloadFromWorkDir(workDir);
+	}
+	catch (TestResult.Fault ignore) {
+	}
+    }
+
+    /** This method should not be called; for this class, its identity
+     * is significant, not its implementation.
+     *
+     * @throws Error if called
+     */
+    public Status run(String[] args, TestDescription td, TestEnvironment env) {
+	throw new Error("should not be called");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/StdTestScript.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,223 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import java.io.File;
+import com.sun.javatest.Script;
+import com.sun.javatest.Status;
+import com.sun.javatest.TestDescription;
+import com.sun.javatest.TestEnvironment;
+import com.sun.javatest.util.StringArray;
+
+/**
+ * A Script to compile/execute a standard test.
+ */
+public class StdTestScript extends Script
+{
+    public Status run(String[] args, TestDescription td, TestEnvironment env) {
+	try {
+	    String[] m = env.lookup("script.mode");
+	    if (m != null && m.length == 1)
+		setMode(m[0]);
+	}
+	catch (TestEnvironment.Fault e) {
+	    return Status.failed("error determining script mode: " + e.getMessage());
+	}
+
+	boolean compile = false;
+	boolean execute = false;
+	boolean expectFail = false;
+
+	for (int i = 0; i < args.length; i++) {
+	    String arg = args[i];
+	    
+	    if (arg.equals("-certify")) {
+		compile = false;
+		execute = true;
+	    }
+	    else if (arg.equals("-precompile")) {
+		compile = true;
+		execute = false;
+	    }
+	    else if (arg.equals("-developer")) {
+		compile = true;
+		execute = true;
+	    }
+	    else if (arg.equals("-compile")) {
+		compile = true;
+	    }
+	    else if (arg.equals("-execute")) {
+		execute = true;
+	    }
+	    else if (arg.equals("-expectFail")) {
+		expectFail = true;
+	    }
+	    else
+		return Status.failed("bad arg for script: `" + arg + "'");
+	}
+
+	if (compile == false && execute == false) {
+	    // not set in args, so set from mode
+	    compile = (mode == DEVELOPER || mode == PRECOMPILE);
+	    execute = (mode == DEVELOPER || mode == CERTIFY);
+	}
+
+	if (compile) {
+	    String srcsParameter = td.getParameter("sources");
+	    if (srcsParameter == null)
+		// check "source" for backwards compatibility
+		srcsParameter = td.getParameter("source");
+	    String[] srcs = StringArray.split(srcsParameter);
+	    File[] files = new File[srcs.length];
+	    File tdDir  = td.getDir();
+	    for (int i = 0; i < files.length; i++) 
+		files[i] = new File(tdDir, srcs[i].replace('/', File.separatorChar));
+
+	    Status compileStatus = compileTogether(files);
+
+	    // if we're not going to execute the test, this is the end of the task
+	    if (!execute)  {
+		if (expectFail) {
+		    // backwards compatibility for negative compiler tests,
+		    // for which we expect the compilation to fail,
+		    // so verify that it did, and return accordingly
+		    if (compileStatus.getType() == Status.FAILED) 
+			return pass_compFailExp.augment(compileStatus);
+		    else
+			return fail_compSuccUnexp.augment(compileStatus);
+		} else
+		    // normal exit for compile-only tests
+		    return compileStatus;
+	    } 
+
+	    // if we want to execute the test, but the compilation failed, we can't go on
+	    if (compileStatus.isFailed())
+		return fail_compFailUnexp.augment(compileStatus);
+	}
+
+	if (execute) {
+	    String executeClass = td.getParameter("executeClass");
+	    if (executeClass == null)
+		return error_noExecuteClass;
+
+	    Status executeStatus = execute(executeClass, td.getParameter("executeArgs"));
+
+	    if (expectFail) {
+		// backwards compatibility for negative execution tests,
+		// for which we expect the execution to fail,
+		// so verify that it did, and return accordingly
+		if (executeStatus.getType() == Status.FAILED) 
+		    return pass_execFailExp.augment(executeStatus);
+		else
+		    return fail_execSuccUnexp.augment(executeStatus);
+	    } else
+		// normal exit for (compile and) execute tests
+		return executeStatus;
+	}
+
+	return error_noActionSpecified;
+    }
+
+    /**
+     * Get the execution mode for this script. The default mode is CERTIFY.
+     * @return an integer signifying the execution mode for this script
+     * @see #setMode
+     * @see #UNKNOWN
+     * @see #CERTIFY
+     * @see #PRECOMPILE
+     * @see #DEVELOPER
+     */
+    public int getMode() {
+	return mode;
+    }
+
+    /**
+     * Set the execution mode for this script. 
+     * @param mode an integer signifying the execution mode for this script
+     * @see #getMode
+     * @see #UNKNOWN
+     * @see #CERTIFY
+     * @see #PRECOMPILE
+     * @see #DEVELOPER
+     */
+    public void setMode(int mode) {
+	switch (mode) {
+	case CERTIFY:
+	case PRECOMPILE:
+	case DEVELOPER:
+	    this.mode = mode;
+	    break;
+
+	default:
+	    throw new IllegalArgumentException();
+	}
+    }
+
+    private void setMode(String mode) {
+	setMode(parseMode(mode));
+    }
+    
+    private static int parseMode(String m) {
+	if (m == null || m.equals("certify"))
+	    return CERTIFY;
+	else if (m.equals("precompile"))
+	    return PRECOMPILE;
+	else if (m.equals("developer"))
+	    return DEVELOPER;
+	else 
+	    return UNKNOWN;
+    }
+
+    private static int getDefaultMode() {
+	return parseMode(System.getProperty("javatest.stdTestScript.defaultMode"));
+    }
+
+    /**
+     * An integer signifying that the execution mode is unknown.
+     */
+    public static final int UNKNOWN = 0;
+
+    /**
+     * An integer signifying that the execution mode is to perform
+     * a certification run, executing precompiled classes.
+     */
+    public static final int CERTIFY = 1;
+
+    /**
+     * An integer signifying that the execution mode is to precompile
+     * but not otherwise execute the tests.
+     */
+    public static final int PRECOMPILE = 2;
+
+    /**
+     * An integer signifying that the execution mode is to compile
+     * and execute the tests.
+     */
+    public static final int DEVELOPER = 3;
+
+    private int mode = getDefaultMode();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/lib/TestCases.java	Mon May 19 10:33:03 2008 +0200
@@ -0,0 +1,275 @@
+/*
+ * $Id$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.javatest.lib;
+
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import com.sun.javatest.Status;
+import com.sun.javatest.Test;
+
+/**
+ * A handler for the set of test cases in a test.
+ * Test cases are those methods with no args that return a 
+ * {@link com.sun.javatest.Status status}.
+ * Test cases can be explicitly selected into or excluded from the 
+ * set.
+ */
+public class TestCases {
+    /**
+     * Exception used to report internal errors.
+     */
+    public static class Fault extends Exception {
+	/**
+	 * Construct a new Fault object that signals failure
+	 * with a corresponding message.
+	 * 
+	 * @param s the string containing a comment
+	 */
+	public Fault(String s) {
+	    super(s);
+	}
+    }
+
+    /**
+     * Create an object to handle the test cases of the given test.
+     * @param t The test containing the test cases.
+     * @param log An optional stream to which to write log messages.
+     *   Use null if messages are not desired.
+     */
+    public TestCases(Test t, PrintWriter log) {
+	test = t;
+	this.log = log;
+	testClass = t.getClass();
+    }
+
+    /**
+     * Explicitly select a set of test cases by name. Subsequent calls
+     * are cumulative; if no selections are made, the default is all
+     * test cases are selected. Excluded tests will be excluded from the
+     * selection; the order of select and exclude calls does not matter.
+     * @param testCaseNames a comma-separated list of test cases names.
+     * Each name must identify a method in the test object, that takes
+     * no arguments and returns a {@link com.sun.javatest.Status status}.
+     * @throws TestCases.Fault if any of the test case names are invalid.
+     */
+    public void select(String testCaseNames) throws Fault {
+	select(split(testCaseNames));
+    }
+
+
+    /**
+     * Explicitly select a set of test cases by name. Subsequent calls
+     * are cumulative; if no selections are made, the default is all
+     * test cases are selected. Excluded tests will be excluded from the
+     * selection; the order of select and exclude calls does not matter.
+     * @param testCaseNames an array of test cases names.
+     * Each name must identify a method in the test object, that takes
+     * no arguments and returns a {@link com.sun.javatest.Status status}.
+     * @throws TestCases.Fault if any of the test case names are invalid.
+     */
+    public void select(String[] testCaseNames) throws Fault  {
+	for (int i = 0; i < testCaseNames.length; i++) {
+	    String t = testCaseNames[i];
+	    selectedCases.put(t, getTestCase(t));
+	}
+    }
+
+
+    /**
+     * Explicitly exclude a set of test cases by name. Subsequent calls
+     * are cumulative; by default, no test cases are excluded.
+     * @param testCaseNames a comma-separated list of test cases names.
+     * Each name must identify a method in the test object, that takes
+     * no arguments and returns a {@link com.sun.javatest.Status status}.
+     * @throws TestCases.Fault if any of the test case names are invalid.
+     */
+    public void exclude(String testCaseNames) throws Fault  {
+	exclude(split(testCaseNames));
+    }
+
+
+    /**
+     * Explicitly exclude a set of test cases by name. Subsequent calls
+     * are cumulative; by default, no test cases are excluded.
+     * @param testCaseNames an array of test cases names.
+     * Each name must identify a method in the test object, that takes
+     * no arguments and returns a {@link com.sun.javatest.Status status}.
+     * @throws TestCases.Fault if any of the test case names are invalid.
+     */
+    public void exclude(String[] testCaseNames) throws Fault  {
+	for (int i = 0; i < testCaseNames.length; i++) {
+	    String t = testCaseNames[i];
+	    excludedCases.put(t, getTestCase(t));
+	}
+    }
+
+
+    /**
+     * Return an enumeration of the selected test cases, based on the
+     * select and exclude calls that have been made, if any.
+     * @return An enumeration of the test cases.
+     */
+    public Enumeration enumerate() {
+	Vector v = new Vector();
+	if (selectedCases.isEmpty()) {
+	    Method[] methods = testClass.getMethods();
+	    for (int i = 0; i < methods.length; i++) {
+		Method m = methods[i];
+		if (excludedCases.get(m.getName()) == null) {
+		    Class[] paramTypes = m.getParameterTypes();
+		    Class returnType = m.getReturnType();
+		    if ((paramTypes.length == 0) && Status.class.isAssignableFrom(returnType))
+			v.addElement(m);
+		}
+	    }
+	}
+	else {
+	    for (Enumeration e = selectedCases.elements(); e.hasMoreElements(); ) {
+		Method m = (Method)(e.nextElement());
+		if (excludedCases.get(m.getName()) == null)
+		    v.addElement(m);
+	    }
+	}
+	return v.elements();	
+    }
+
+    
+    /**
+     * Invoke each of the selected test cases, based upon the select and exclude
+     * calls that have been made, if any.
+     * If the test object provides a public method 
+     * {@link com.sun.javatest.Status}invokeTestCase({@link java.lang.reflect.Method})
+     * that method will be called to invoke the test cases; otherwise, the test
+     * cases will be invoked directly.
+     * It is an error if no test cases are selected, (or if they have all been excluded.)
+     * @return the combined result of executing all the test cases.
+     */
+    public Status invokeTestCases() {
+	// see if test object provides  Status invokeTestCase(Method m)
+	Method invoker;
+	try {
+	    invoker = testClass.getMethod("invokeTestCase", new Class[] {Method.class});
+	    if (!Status.class.isAssignableFrom(invoker.getReturnType()))
+		invoker = null;
+	}
+	catch (NoSuchMethodException e) {
+	    invoker = null;
+	}
+
+	MultiStatus ms = new MultiStatus(log);
+	for (Enumeration e = enumerate(); e.hasMoreElements(); ) {
+	    Method m = (Method)(e.nextElement());
+	    Status s;
+	    try {
+		if (invoker != null)
+		    s = (Status)invoker.invoke(test, new Object[] {m});
+		else
+		    s = (Status)m.invoke(test, noArgs);
+	    }
+	    catch (IllegalAccessException ex) {
+		s = Status.failed("Could not access test case: " + m.getName());
+	    }
+	    catch (InvocationTargetException ex) {
+		printStackTrace(ex.getTargetException());
+		s = Status.failed("Exception from test case: " +
+				       ex.getTargetException().toString());
+	    }
+	    catch (ThreadDeath t) {
+		printStackTrace(t);
+		throw t;
+	    }
+	    catch (Throwable t) {
+		printStackTrace(t);
+		s = Status.failed("Unexpected Throwable: " + t);
+	    }
+
+	    ms.add(m.getName(), s);
+	}
+	if (ms.getTestCount() == 0)
+	    return Status.passed("Test passed by default: no test cases executed.");
+	else
+	    return ms.getStatus();
+    }
+
+    /**
+     * Print a stack trace for an exception to the log.
+     * @param t The Throwable for which to print the trace
+     */
+    protected void printStackTrace(Throwable t) {
+	if (log != null) 
+	    t.printStackTrace(log);
+    }
+
+    /**
+     * Look up a test case in the test object.
+     * @param name the name of the test case; it must identify a method
+     *		Status name()
+     * @returns the selected method
+     * @throws Fault if the name does not identify an appropriate method.
+     */
+    private Method getTestCase(String name) throws Fault {
+	try {
+	    Method m = testClass.getMethod(name, noArgTypes);
+	    if (!Status.class.isAssignableFrom(m.getReturnType()))
+		throw new Fault("Method for test case '" + name + "' has wrong return type" ); 
+	    return m;
+	} 
+	catch (NoSuchMethodException e) {
+	    throw new Fault("Could not find test case: " + name);
+	} 
+	catch (SecurityException e) {
+	    throw new Fault(e.toString());
+	}
+    }
+
+    private String[] split(String s) {
+	Vector v = new Vector();
+	int start = 0;
+	for (int i = s.indexOf(','); i != -1; i = s.indexOf(',', start)) {
+	    v.addElement(s.substring(start, i));
+	    start = i + 1;
+	}
+	if (start != s.length())
+	    v.addElement(s.substring(start));
+	String[] ss = new String[v.size()];
+	v.copyInto(ss);
+	return ss;
+    }
+
+    private Object test;
+    private Class testClass;
+    private Hashtable selectedCases = new Hashtable();
+    private Hashtable excludedCases = new Hashtable();
+    private PrintWriter log;
+
+    private static final Object[] noArgs = { };
+    private static final Class[] noArgTypes = { };
+}