1 /*
   2  * Copyright (c) 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 package toolbox;
  25 
  26 import java.io.PrintStream;
  27 import java.lang.annotation.Annotation;
  28 import java.lang.annotation.Retention;
  29 import java.lang.annotation.RetentionPolicy;
  30 import java.lang.reflect.InvocationTargetException;
  31 import java.lang.reflect.Method;
  32 import java.util.function.Function;
  33 
  34 /**
  35  * Utility class to manage and execute sub-tests within a test.
  36  *
  37  * This class does the following:
  38  * <ul>
  39  * <li>  invokes those test methods annotated with @Test
  40  * <li>  keeps track of successful and failed tests
  41  * <li>  throws an Exception if any test fails.
  42  * <li>  provides a test summary at the end of the run.
  43  * </ul>
  44 
  45  * Tests must extend this class, annotate the test methods
  46  * with @Test and call one of the runTests method.
  47  */
  48 public abstract class TestRunner {
  49    /** Marker annotation for test cases. */
  50     @Retention(RetentionPolicy.RUNTIME)
  51     public @interface Test { }
  52 
  53     int testCount = 0;
  54     int errorCount = 0;
  55 
  56     public String testName = null;
  57 
  58     protected PrintStream out;
  59 
  60     /**
  61      * Constructs the Object.
  62      * @param out the PrintStream to print output to.
  63      */
  64     public TestRunner(PrintStream out) {
  65         this.out = out;
  66     }
  67 
  68     /**
  69      * Invoke all methods annotated with @Test.
  70      * @throws java.lang.Exception if any errors occur
  71      */
  72     protected void runTests() throws Exception {
  73         runTests(f -> new Object[0]);
  74     }
  75 
  76     /**
  77      * Invoke all methods annotated with @Test.
  78      * @param f a lambda expression to specify arguments for the test method
  79      * @throws java.lang.Exception if any errors occur
  80      */
  81     protected void runTests(Function<Method, Object[]> f) throws Exception {
  82         for (Method m : getClass().getDeclaredMethods()) {
  83             Annotation a = m.getAnnotation(Test.class);
  84             if (a != null) {
  85                 testName = m.getName();
  86                 try {
  87                     testCount++;
  88                     out.println("test: " + testName);
  89                     m.invoke(this, f.apply(m));
  90                 } catch (InvocationTargetException e) {
  91                     errorCount++;
  92                     Throwable cause = e.getCause();
  93                     out.println("Exception: " + e.getCause());
  94                     cause.printStackTrace(out);
  95                 }
  96                 out.println();
  97             }
  98         }
  99 
 100         if (testCount == 0) {
 101             throw new Error("no tests found");
 102         }
 103 
 104         StringBuilder summary = new StringBuilder();
 105         if (testCount != 1) {
 106             summary.append(testCount).append(" tests");
 107         }
 108         if (errorCount > 0) {
 109             if (summary.length() > 0) {
 110                 summary.append(", ");
 111             }
 112             summary.append(errorCount).append(" errors");
 113         }
 114         out.println(summary);
 115         if (errorCount > 0) {
 116             throw new Exception(errorCount + " errors found");
 117         }
 118     }
 119 
 120     protected void error(String message) {
 121         out.println("Error: " + message);
 122         errorCount++;
 123     }
 124 }