1 /*
   2  * Copyright (c) 1999, 2020, 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 /*
  25  * (C) Copyright IBM Corp. 1999 - All Rights Reserved
  26  *
  27  * The original version of this source code and documentation is
  28  * copyrighted and owned by IBM. These materials are provided
  29  * under terms of a License Agreement between IBM and Sun.
  30  * This technology is protected by multiple US and International
  31  * patents. This notice and attribution to IBM may not be removed.
  32  */
  33 
  34 import java.lang.reflect.*;
  35 import java.util.Hashtable;
  36 import java.util.Enumeration;
  37 import java.util.Vector;
  38 import java.io.*;
  39 import java.text.*;
  40 
  41 /**
  42  * RBTestFmwk is a base class for tests that can be run conveniently from
  43  * the command line as well as under the Java test harness.
  44  * <p>
  45  * Sub-classes implement a set of methods named Test<something>. Each
  46  * of these methods performs some test. Test methods should indicate
  47  * errors by calling either err or errln.  This will increment the
  48  * errorCount field and may optionally print a message to the log.
  49  * Debugging information may also be added to the log via the log
  50  * and logln methods.  These methods will add their arguments to the
  51  * log only if the test is being run in verbose mode.
  52  */
  53 public class TestFmwk {
  54     //------------------------------------------------------------------------
  55     // Everything below here is boilerplate code that makes it possible
  56     // to add a new test by simply adding a function to an existing class
  57     //------------------------------------------------------------------------
  58 
  59     protected TestFmwk() {
  60         // Create a hashtable containing all the test methods.
  61         testMethods = new Hashtable();
  62         Method[] methods = getClass().getDeclaredMethods();
  63         for( int i=0; i<methods.length; i++ ) {
  64             if( methods[i].getName().startsWith("Test")
  65                     || methods[i].getName().startsWith("test") ) {
  66                 testMethods.put( methods[i].getName(), methods[i] );
  67             }
  68         }
  69     }
  70 
  71     protected void run(String[] args) throws Exception
  72     {
  73         System.out.println(getClass().getName() + " {");
  74         indentLevel++;
  75 
  76         // Set up the log and reference streams.  We use PrintWriters in order to
  77         // take advantage of character conversion.  The JavaEsc converter will
  78         // convert Unicode outside the ASCII range to Java's \\uxxxx notation.
  79         log = new PrintWriter(System.out,true);
  80 
  81         // Parse the test arguments.  They can be either the flag
  82         // "-verbose" or names of test methods. Create a list of
  83         // tests to be run.
  84         Vector testsToRun = new Vector( args.length );
  85         for( int i=0; i<args.length; i++ ) {
  86             if( args[i].equals("-verbose") ) {
  87                 verbose = true;
  88             }
  89             else if( args[i].equals("-prompt") ) {
  90                 prompt = true;
  91             } else if (args[i].equals("-nothrow")) {
  92                 nothrow = true;
  93             } else {
  94                 Object m = testMethods.get( args[i] );
  95                 if( m != null ) {
  96                     testsToRun.addElement( m );
  97                 }
  98                 else {
  99                     usage();
 100                     return;
 101                 }
 102             }
 103         }
 104 
 105         // If no test method names were given explicitly, run them all.
 106         if( testsToRun.size() == 0 ) {
 107             Enumeration methodNames = testMethods.elements();
 108             while( methodNames.hasMoreElements() ) {
 109                 testsToRun.addElement( methodNames.nextElement() );
 110             }
 111         }
 112 
 113         // Run the list of tests given in the test arguments
 114         for( int i=0; i<testsToRun.size(); i++ ) {
 115             int oldCount = errorCount;
 116 
 117             Method testMethod = (Method)testsToRun.elementAt(i);
 118             writeTestName(testMethod.getName());
 119 
 120             try {
 121                 testMethod.invoke(this, new Object[0]);
 122             }
 123             catch( IllegalAccessException e ) {
 124                 errln("Can't acces test method " + testMethod.getName());
 125             }
 126             catch( InvocationTargetException e ) {
 127                 errln("Uncaught exception thrown in test method "
 128                                + testMethod.getName());
 129                 e.getTargetException().printStackTrace(this.log);
 130             }
 131             writeTestResult(errorCount - oldCount);
 132         }
 133         indentLevel--;
 134         writeTestResult(errorCount);
 135 
 136         if (prompt) {
 137             System.out.println("Hit RETURN to exit...");
 138             try {
 139                 System.in.read();
 140             }
 141             catch (IOException e) {
 142                 System.out.println("Exception: " + e.toString() + e.getMessage());
 143             }
 144         }
 145         if (nothrow) {
 146             System.exit(errorCount);
 147         }
 148     }
 149 
 150     /**
 151      * Adds given string to the log if we are in verbose mode.
 152      */
 153     protected void log( String message ) {
 154         if( verbose ) {
 155             indent(indentLevel + 1);
 156             log.print( message );
 157             log.flush();
 158         }
 159     }
 160 
 161     protected void logln( String message ) {
 162         log(message + System.getProperty("line.separator"));
 163     }
 164 
 165     /**
 166      * Report an error
 167      */
 168     protected void err( String message ) {
 169         errorCount++;
 170         indent(indentLevel + 1);
 171         log.print( message );
 172         log.flush();
 173 
 174         if (!nothrow) {
 175             throw new RuntimeException(message);
 176         }
 177     }
 178 
 179     protected void errln( String message ) {
 180         err(message + System.getProperty("line.separator"));
 181     }
 182 
 183 
 184     protected void writeTestName(String testName) {
 185         indent(indentLevel);
 186         log.print(testName);
 187         log.flush();
 188         needLineFeed = true;
 189     }
 190 
 191     protected void writeTestResult(int count) {
 192         if (!needLineFeed) {
 193             indent(indentLevel);
 194             log.print("}");
 195         }
 196         needLineFeed = false;
 197 
 198         if (count != 0)
 199             log.println(" FAILED");
 200         else
 201             log.println(" Passed");
 202     }
 203 
 204     private final void indent(int distance) {
 205         if (needLineFeed) {
 206             log.println(" {");
 207             needLineFeed = false;
 208         }
 209         log.print(spaces.substring(0, distance * 2));
 210     }
 211 
 212     /**
 213      * Print a usage message for this test class.
 214      */
 215     void usage() {
 216         System.out.println(getClass().getName() +
 217                             ": [-verbose] [-nothrow] [-prompt] [test names]");
 218 
 219         System.out.println("test names:");
 220         Enumeration methodNames = testMethods.keys();
 221         while( methodNames.hasMoreElements() ) {
 222             System.out.println("\t" + methodNames.nextElement() );
 223         }
 224     }
 225 
 226     private boolean     prompt = false;
 227     private boolean     nothrow = false;
 228     protected boolean   verbose = false;
 229 
 230     private PrintWriter log;
 231     private int         indentLevel = 0;
 232     private boolean     needLineFeed = false;
 233     private int         errorCount = 0;
 234 
 235     private Hashtable testMethods;
 236     private final String spaces = "                                          ";
 237 }