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 }