| /* |
| ******************************************************************************* |
| * Copyright (C) 1996-2000, International Business Machines Corporation and * |
| * others. All Rights Reserved. * |
| ******************************************************************************* |
| * |
| * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/dev/test/TestFmwk.java,v $ |
| * $Date: 2002/06/05 23:00:58 $ |
| * $Revision: 1.30 $ |
| * |
| ***************************************************************************************** |
| */ |
| package com.ibm.icu.dev.test; |
| |
| import java.lang.reflect.*; |
| import java.util.Hashtable; |
| import java.util.Enumeration; |
| import java.util.Vector; |
| import java.util.Comparator; |
| import java.io.*; |
| import java.text.*; |
| import com.ibm.icu.text.UTF16; |
| import com.ibm.icu.impl.Utility; |
| import com.ibm.icu.text.UnicodeSet; |
| |
| |
| /** |
| * TestFmwk is a base class for tests that can be run conveniently from |
| * the command line as well as under the Java test harness. |
| * <p> |
| * Sub-classes implement a set of methods named Test<something>. Each |
| * of these methods performs some test. Test methods should indicate |
| * errors by calling either err or errln. This will increment the |
| * errorCount field and may optionally print a message to the log. |
| * Debugging information may also be added to the log via the log |
| * and logln methods. These methods will add their arguments to the |
| * log only if the test is being run in verbose mode. |
| */ |
| |
| public class TestFmwk implements TestLog { |
| |
| /** |
| * Puts a copyright in the .class file |
| */ |
| private static final String copyrightNotice |
| = "Copyright \u00a91997-1998 IBM Corp. All rights reserved."; |
| |
| //------------------------------------------------------------------------ |
| // Everything below here is boilerplate code that makes it possible |
| // to add a new test by simply adding a function to an existing class |
| //------------------------------------------------------------------------ |
| |
| protected TestFmwk() { |
| // Create a hashtable containing all the test methods. |
| testMethods = new Hashtable(); |
| Method[] methods = getClass().getDeclaredMethods(); |
| for( int i=0; i<methods.length; i++ ) { |
| if( methods[i].getName().startsWith("Test") |
| || methods[i].getName().startsWith("test")) { |
| testMethods.put( methods[i].getName(), methods[i] ); |
| } |
| } |
| } |
| |
| private void _run() throws Exception { |
| writeTestName(getClass().getName()); |
| params.indentLevel++; |
| int oldClassCount = params.errorCount; |
| int oldClassInvalidCount = params.invalidCount; |
| |
| if (validate()) { |
| |
| Enumeration methodsToRun; |
| |
| if (testsToRun != null && testsToRun.size() >= 1) { |
| methodsToRun = testsToRun.elements(); |
| } else { |
| methodsToRun = testMethods.elements(); |
| } |
| |
| methodsToRun = new SortedEnumeration(methodsToRun, |
| new Comparator() { |
| public int compare(Object a, Object b) { |
| return ((Method)a).getName().compareToIgnoreCase( |
| ((Method)b).getName()); |
| } |
| public boolean equals(Object o) { |
| return false; |
| } |
| }); |
| |
| // Run the list of tests given in the test arguments |
| final Object[] NO_ARGS = new Object[0]; |
| while (methodsToRun.hasMoreElements()) { |
| int oldCount = params.errorCount; |
| int oldInvalidCount = params.invalidCount; |
| |
| Method testMethod = (Method)methodsToRun.nextElement(); |
| String testName = testMethod.getName(); |
| |
| writeTestName(testName); |
| |
| if (validateMethod(testName)) { |
| try { |
| testMethod.invoke(this, NO_ARGS); |
| } catch( IllegalAccessException e ) { |
| errln("Can't access test method " + testName); |
| } catch( InvocationTargetException e ) { |
| errln("Uncaught exception \""+e+"\" thrown in test method " |
| + testName); |
| e.getTargetException().printStackTrace(this.params.log); |
| } |
| } else { |
| params.invalidCount++; |
| } |
| writeTestResult(params.errorCount - oldCount, params.invalidCount - oldInvalidCount); |
| } |
| } else { |
| params.invalidCount++; |
| } |
| params.indentLevel--; |
| |
| writeTestResult(params.errorCount - oldClassCount, params.invalidCount - oldClassInvalidCount); |
| } |
| |
| public void run(String[] args) throws Exception { |
| if (params == null) params = new TestParams(); |
| |
| // Parse the test arguments. They can be either the flag |
| // "-verbose" or names of test methods. Create a list of |
| // tests to be run. |
| boolean printUsage = false; |
| testsToRun = new Vector(args.length); |
| for (int i = 0; i < args.length; i++) { |
| if (args[i].equals("-verbose") || args[i].equals("-v")) { |
| params.verbose = true; |
| } |
| else if (args[i].equals("-prompt")) { |
| params.prompt = true; |
| } else if (args[i].equals("-nothrow")) { |
| params.nothrow = true; |
| } else if (args[i].equals("-describe")) { |
| params.describe = true; |
| } else if (args[i].startsWith("-e")) { |
| params.inclusion = (args[i].length() == 2) ? 5 : Integer.parseInt(args[i].substring(2)); |
| } else if (args[i].toLowerCase().startsWith("-filter:")) { |
| params.filter = args[i].substring(8); |
| } else { |
| Object m = testMethods.get(args[i]); |
| if (m != null) { |
| testsToRun.addElement(m); |
| } else { |
| printUsage = true; |
| } |
| } |
| } |
| if (printUsage) { |
| usage(); |
| return; |
| } |
| |
| _run(); |
| |
| if (params.prompt) { |
| System.out.println("Hit RETURN to exit..."); |
| try { |
| System.in.read(); |
| } catch (IOException e) { |
| System.out.println("Exception: " + e.toString() + e.getMessage()); |
| } |
| } |
| if (params.nothrow) { |
| System.exit(params.errorCount); |
| } |
| } |
| |
| /** |
| * Return true if we can run this test (allows test to inspect jvm, environment, params before running) |
| */ |
| protected boolean validate() { |
| return true; |
| } |
| |
| protected String getDescription() { |
| return null; |
| } |
| |
| protected boolean validateMethod(String name) { |
| return true; |
| } |
| |
| protected String getMethodDescription(String name) { |
| return null; |
| } |
| |
| protected void run(TestFmwk childTest) throws Exception { |
| run(new TestFmwk[] { childTest }); |
| } |
| |
| protected void run(TestFmwk[] tests) throws Exception { |
| for (int i=0; i<tests.length; ++i) { |
| tests[i].params = this.params; |
| params.indentLevel++; |
| tests[i]._run(); |
| params.indentLevel--; |
| } |
| } |
| |
| protected boolean isVerbose() { |
| return params.verbose; |
| } |
| |
| /** |
| * 0 = fewest tests, 5 is normal build, 10 is most tests |
| */ |
| public int getInclusion() { |
| return params.inclusion; |
| } |
| |
| public boolean isQuick() { |
| return params.inclusion == 0; |
| } |
| |
| public String getFilter() { |
| return params.filter; |
| } |
| |
| /** |
| * Adds given string to the log if we are in verbose mode. |
| */ |
| public void log( String message ) { |
| log(message, true, false); |
| } |
| |
| public void logln( String message ) { |
| log(message + System.getProperty("line.separator"), true, false); |
| } |
| |
| /** |
| * Add a given string to the log. |
| * @param message text to add |
| * @param pass if true and if in verbose mode, or if false, then add |
| * the text; otherwise suppress it |
| * @param incrementCount if pass if false and incrementCount is true, |
| * then increment the failure count; if pass is true, then this param |
| * is ignored |
| */ |
| public void log( String message, boolean pass, boolean incrementCount ) { |
| if (!pass && incrementCount) { |
| params.errorCount++; |
| } |
| |
| if (!pass || params.verbose) { |
| indent(params.indentLevel + 1); |
| params.log.print( message ); |
| params.log.flush(); |
| } |
| |
| if (!pass && !params.nothrow) { |
| throw new RuntimeException(message); |
| } |
| } |
| |
| public void logln( String message, boolean pass, boolean incrementCount ) { |
| log(message + System.getProperty("line.separator"), pass, incrementCount); |
| } |
| |
| /** |
| * Convenience overloads |
| */ |
| public void log( String message, boolean pass ) { |
| log(message, pass, true); |
| } |
| |
| public void logln( String message, boolean pass ) { |
| logln(message, pass, true); |
| } |
| |
| /** |
| * Report an error |
| */ |
| public void err( String message ) { |
| log(message, false, true); |
| } |
| |
| public void errln( String message ) { |
| logln(message, false, true); |
| } |
| |
| protected int getErrorCount() { |
| return params.errorCount; |
| } |
| |
| protected void writeTestName(String testName) { |
| indent(params.indentLevel); |
| params.log.print(testName); |
| params.log.flush(); |
| params.needLineFeed = true; |
| } |
| |
| protected void writeTestResult(int failCount, int invalidCount) { |
| if (!params.needLineFeed) { |
| indent(params.indentLevel); |
| params.log.print("}"); |
| } |
| params.needLineFeed = false; |
| |
| if (failCount != 0) { |
| params.log.println(" FAILED (" + failCount + " failures" + |
| ((invalidCount != 0) ? |
| ", " + invalidCount + " tests skipped)" : |
| ")")); |
| } else if (invalidCount != 0) { |
| params.log.println(" Qualified (" + invalidCount + " tests skipped)"); |
| } else { |
| params.log.println(" Passed"); |
| } |
| } |
| |
| private final void indent(int distance) { |
| if (params.needLineFeed) { |
| params.log.println(" {"); |
| params.needLineFeed = false; |
| } |
| params.log.print(spaces.substring(0, distance * 2)); |
| } |
| |
| /** |
| * Print a usage message for this test class. |
| */ |
| void usage() { |
| System.out.println(getClass().getName() + |
| ": [-verbose] [-nothrow] [-prompt] [-describe] [test names]"); |
| |
| Enumeration methodNames = new SortedEnumeration(testMethods.keys(), |
| new Comparator() { |
| public int compare(Object a, Object b) { |
| return ((String)a).compareToIgnoreCase( |
| ((String)b)); |
| } |
| public boolean equals(Object o) { |
| return false; |
| } |
| }); |
| boolean valid = params.describe && validate(); |
| if (valid) { |
| String testDescription = getDescription(); |
| if (testDescription != null) { |
| System.out.println("-- " + testDescription); |
| } |
| } |
| System.out.println("test names:"); |
| while( methodNames.hasMoreElements() ) { |
| String methodName = (String)methodNames.nextElement(); |
| System.out.print("\t" + methodName ); |
| if (valid) { |
| String methodDescription = getMethodDescription(methodName); |
| if (methodDescription != null) { |
| System.out.print(" -- " + methodDescription); |
| } |
| } |
| System.out.println(); |
| } |
| } |
| |
| public static String hex(char ch) { |
| StringBuffer result = new StringBuffer(); |
| String foo = Integer.toString(ch,16).toUpperCase(); |
| for (int i = foo.length(); i < 4; ++i) { |
| result.append('0'); |
| } |
| return result + foo; |
| } |
| |
| public static String hex(int ch) { |
| StringBuffer result = new StringBuffer(); |
| String foo = Integer.toString(ch,16).toUpperCase(); |
| for (int i = foo.length(); i < 4; ++i) { |
| result.append('0'); |
| } |
| return result + foo; |
| } |
| |
| public static String hex(String s) { |
| StringBuffer result = new StringBuffer(); |
| for (int i = 0; i < s.length(); ++i) { |
| if (i != 0) result.append(','); |
| result.append(hex(s.charAt(i))); |
| } |
| return result.toString(); |
| } |
| |
| public static String hex(StringBuffer s) { |
| return hex(s.toString()); |
| } |
| |
| private static class ASCIIWriter extends PrintWriter { |
| private Writer w; |
| private StringBuffer buffer = new StringBuffer(); |
| |
| // Characters that we think are printable but that escapeUnprintable |
| // doesn't |
| private static final UnicodeSet S = |
| new UnicodeSet("[\\u0009\\u000A\\u000D]"); |
| |
| public ASCIIWriter(Writer w, boolean autoFlush) { |
| super(w, autoFlush); |
| } |
| |
| public ASCIIWriter(OutputStream os, boolean autoFlush) { |
| super(os, autoFlush); |
| } |
| |
| public void write(int c) { |
| synchronized(lock) { |
| buffer.setLength(0); |
| if (!S.contains(c) && Utility.escapeUnprintable(buffer, c)) { |
| super.write(buffer.toString()); |
| } else { |
| super.write(c); |
| } |
| } |
| } |
| |
| public void write(char[] buf, int off, int len) { |
| synchronized (lock) { |
| buffer.setLength(0); |
| int limit = off + len; |
| while (off < limit) { |
| int c = UTF16.charAt(buf, 0, buf.length, off); |
| off += UTF16.getCharCount(c); |
| if (!S.contains(c) && Utility.escapeUnprintable(buffer, c)) { |
| super.write(buffer.toString()); |
| buffer.setLength(0); |
| } else { |
| super.write(c); |
| } |
| } |
| } |
| } |
| |
| public void write(String s, int off, int len) { |
| write(s.substring(off, off + len).toCharArray(), 0, len); |
| } |
| } |
| |
| private static class TestParams { |
| public boolean prompt = false; |
| public boolean nothrow = false; |
| public boolean verbose = false; |
| public boolean describe = false; |
| public int inclusion = 0; |
| public String filter = null; |
| |
| public PrintWriter log = new ASCIIWriter(System.out, true); |
| public int indentLevel = 0; |
| public boolean needLineFeed = false; |
| public int errorCount = 0; |
| public int invalidCount = 0; |
| } |
| |
| private static class SortedEnumeration implements Enumeration { |
| private Object[] sorted; |
| private int pos; |
| |
| public SortedEnumeration(Enumeration unsorted, Comparator c) { |
| Vector v = new Vector(); |
| while (unsorted.hasMoreElements()) { |
| v.addElement(unsorted.nextElement()); |
| } |
| sorted = new Object[v.size()]; |
| v.copyInto(sorted); |
| java.util.Arrays.sort(sorted, c); |
| pos = 0; |
| } |
| |
| public boolean hasMoreElements() { |
| return pos < sorted.length; |
| } |
| |
| public Object nextElement() { |
| return pos < sorted.length ? sorted[pos++] : null; |
| } |
| } |
| |
| private TestParams params = null; |
| private Hashtable testMethods; |
| private Vector testsToRun; |
| private final String spaces = " "; |
| } |