1 /*
   2  * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.IOException;
  25 import java.io.PrintWriter;
  26 import java.lang.reflect.InvocationTargetException;
  27 import java.lang.reflect.Method;
  28 import java.lang.reflect.Modifier;
  29 import java.util.ArrayList;
  30 import java.util.Map;
  31 import java.util.LinkedHashMap;
  32 import java.util.List;
  33 
  34 /**
  35  * IntlTest is a base class for tests that can be run conveniently from
  36  * the command line as well as under the Java test harness.
  37  * <p>
  38  * Sub-classes implement a set of public void methods named "Test*" or
  39  * "test*" with no arguments. Each of these methods performs some
  40  * test. Test methods should indicate errors by calling either err() or
  41  * errln().  This will increment the errorCount field and may optionally
  42  * print a message to the log.  Debugging information may also be added to
  43  * the log via the log and logln methods.  These methods will add their
  44  * arguments to the log only if the test is being run in verbose mode.
  45  */
  46 public abstract class IntlTest {
  47 
  48     //------------------------------------------------------------------------
  49     // Everything below here is boilerplate code that makes it possible
  50     // to add a new test by simply adding a method to an existing class.
  51     //------------------------------------------------------------------------
  52 
  53     protected IntlTest() {
  54         // Populate testMethods with all the test methods.
  55         Method[] methods = getClass().getDeclaredMethods();
  56         for (Method method : methods) {
  57             if (Modifier.isPublic(method.getModifiers())
  58                 && method.getReturnType() == void.class
  59                 && method.getParameterCount() == 0) {
  60                 String name = method.getName();
  61                 if (name.length() > 4) {
  62                     if (name.startsWith("Test") || name.startsWith("test")) {
  63                         testMethods.put(name, method);
  64                     }
  65                 }
  66             }
  67         }
  68     }
  69 
  70     protected void run(String[] args) throws Exception
  71     {
  72         // Set up the log and reference streams.  We use PrintWriters in order to
  73         // take advantage of character conversion.  The JavaEsc converter will
  74         // convert Unicode outside the ASCII range to Java's \\uxxxx notation.
  75         log = new PrintWriter(System.out, true);
  76 
  77         // Parse the test arguments.  They can be either the flag
  78         // "-verbose" or names of test methods. Create a list of
  79         // tests to be run.
  80         List<Method> testsToRun = new ArrayList<>(args.length);
  81         for (String arg : args) {
  82             switch (arg) {
  83             case "-verbose":
  84                 verbose = true;
  85                 break;
  86             case "-prompt":
  87                 prompt = true;
  88                 break;
  89             case "-nothrow":
  90                 nothrow = true;
  91                 break;
  92             default:
  93                 Method m = testMethods.get(arg);
  94                 if (m == null) {
  95                     System.out.println("Method " + arg + ": not found");
  96                     usage();
  97                     return;
  98                 }
  99                 testsToRun.add(m);
 100                 break;
 101             }
 102         }
 103 
 104         // If no test method names were given explicitly, run them all.
 105         if (testsToRun.isEmpty()) {
 106             testsToRun.addAll(testMethods.values());
 107         }
 108 
 109         System.out.println(getClass().getName() + " {");
 110         indentLevel++;
 111 
 112         // Run the list of tests given in the test arguments
 113         for (Method testMethod : testsToRun) {
 114             int oldCount = errorCount;
 115 
 116             writeTestName(testMethod.getName());
 117 
 118             try {
 119                 testMethod.invoke(this, new Object[0]);
 120             } catch (IllegalAccessException e) {
 121                 errln("Can't acces test method " + testMethod.getName());
 122             } catch (InvocationTargetException e) {
 123                 errln("Uncaught exception thrown in test method "
 124                         + testMethod.getName());
 125                 e.getTargetException().printStackTrace(this.log);
 126             }
 127             writeTestResult(errorCount - oldCount);
 128         }
 129         indentLevel--;
 130         writeTestResult(errorCount);
 131 
 132         if (prompt) {
 133             System.out.println("Hit RETURN to exit...");
 134             try {
 135                 System.in.read();
 136             } catch (IOException e) {
 137                 System.out.println("Exception: " + e.toString() + e.getMessage());
 138             }
 139         }
 140         if (nothrow) {
 141             System.exit(errorCount);
 142         }
 143     }
 144 
 145     /**
 146      * Adds the given message to the log if we are in verbose mode.
 147      */
 148     protected void log(String message) {
 149         logImpl(message, false);
 150     }
 151 
 152     protected void logln(String message) {
 153         logImpl(message, true);
 154     }
 155 
 156     protected void logln() {
 157         logImpl(null, true);
 158     }
 159 
 160     private void logImpl(String message, boolean newline) {
 161         if (verbose) {
 162             if (message != null) {
 163                 indent(indentLevel + 1);
 164                 log.print(message);
 165             }
 166             if (newline) {
 167                 log.println();
 168             }
 169         }
 170     }
 171 
 172     protected void err(String message) {
 173         errImpl(message, false);
 174     }
 175 
 176     protected void errln(String message) {
 177         errImpl(message, true);
 178     }
 179 
 180     private void errImpl(String message, boolean newline) {
 181         errorCount++;
 182         indent(indentLevel + 1);
 183         log.print(message);
 184         if (newline) {
 185             log.println();
 186         }
 187         log.flush();
 188 
 189         if (!nothrow) {
 190             throw new RuntimeException(message);
 191         }
 192     }
 193 
 194     protected int getErrorCount() {
 195         return errorCount;
 196     }
 197 
 198     protected void writeTestName(String testName) {
 199         indent(indentLevel);
 200         log.print(testName);
 201         log.flush();
 202         needLineFeed = true;
 203     }
 204 
 205     protected void writeTestResult(int count) {
 206         if (!needLineFeed) {
 207             indent(indentLevel);
 208             log.print("}");
 209         }
 210         needLineFeed = false;
 211 
 212         if (count != 0) {
 213             log.println(" FAILED");
 214         } else {
 215             log.println(" Passed");
 216         }
 217     }
 218 
 219     /*
 220      * Returns a spece-delimited hex String.
 221      */
 222     protected static String toHexString(String s) {
 223         StringBuilder sb = new StringBuilder(" ");
 224 
 225         for (int i = 0; i < s.length(); i++) {
 226             sb.append(Integer.toHexString(s.charAt(i)));
 227             sb.append(' ');
 228         }
 229 
 230         return sb.toString();
 231     }
 232 
 233     private void indent(int distance) {
 234         if (needLineFeed) {
 235             log.println(" {");
 236             needLineFeed = false;
 237         }
 238         log.print(SPACES.substring(0, distance * 2));
 239     }
 240 
 241     /**
 242      * Print a usage message for this test class.
 243      */
 244     void usage() {
 245         System.out.println(getClass().getName() +
 246                             ": [-verbose] [-nothrow] [-prompt] [test names]");
 247 
 248         System.out.println("  Available test names:");
 249         for (String methodName : testMethods.keySet()) {
 250             System.out.println("\t" + methodName);
 251         }
 252     }
 253 
 254     private boolean     prompt;
 255     private boolean     nothrow;
 256     protected boolean   verbose;
 257 
 258     private PrintWriter log;
 259     private int         indentLevel;
 260     private boolean     needLineFeed;
 261     private int         errorCount;
 262 
 263     private final Map<String, Method> testMethods = new LinkedHashMap<>();
 264 
 265     private static final String SPACES = "                                          ";
 266 }