1 /* 2 * $Id$ 3 * 4 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest.lib; 28 29 import java.io.PrintWriter; 30 import java.lang.reflect.InvocationTargetException; 31 import java.lang.reflect.Method; 32 import java.util.Enumeration; 33 import java.util.Hashtable; 34 import java.util.Map; 35 import java.util.Vector; 36 import com.sun.javatest.Status; 37 import com.sun.javatest.Test; 38 39 /** 40 * A handler for the set of test cases in a test. 41 * Test cases are those methods with no args that return a 42 * {@link com.sun.javatest.Status status}. 43 * Test cases can be explicitly selected into or excluded from the 44 * set. 45 */ 46 public class TestCases { 47 /** 48 * Exception used to report internal errors. 49 */ 50 public static class Fault extends Exception { 51 /** 52 * Construct a new Fault object that signals failure 53 * with a corresponding message. 54 * 55 * @param s the string containing a comment 56 */ 57 public Fault(String s) { 58 super(s); 59 } 60 } 61 62 /** 63 * Create an object to handle the test cases of the given test. 64 * @param t The test containing the test cases. 65 * @param log An optional stream to which to write log messages. 66 * Use null if messages are not desired. 67 */ 68 public TestCases(Test t, PrintWriter log) { 69 test = t; 70 this.log = log; 71 testClass = t.getClass(); 72 } 73 74 /** 75 * Explicitly select a set of test cases by name. Subsequent calls 76 * are cumulative; if no selections are made, the default is all 77 * test cases are selected. Excluded tests will be excluded from the 78 * selection; the order of select and exclude calls does not matter. 79 * @param testCaseNames a comma-separated list of test cases names. 80 * Each name must identify a method in the test object, that takes 81 * no arguments and returns a {@link com.sun.javatest.Status status}. 82 * @throws TestCases.Fault if any of the test case names are invalid. 83 */ 84 public void select(String testCaseNames) throws Fault { 85 select(split(testCaseNames)); 86 } 87 88 89 /** 90 * Explicitly select a set of test cases by name. Subsequent calls 91 * are cumulative; if no selections are made, the default is all 92 * test cases are selected. Excluded tests will be excluded from the 93 * selection; the order of select and exclude calls does not matter. 94 * @param testCaseNames an array of test cases names. 95 * Each name must identify a method in the test object, that takes 96 * no arguments and returns a {@link com.sun.javatest.Status status}. 97 * @throws TestCases.Fault if any of the test case names are invalid. 98 */ 99 public void select(String[] testCaseNames) throws Fault { 100 for (int i = 0; i < testCaseNames.length; i++) { 101 String t = testCaseNames[i]; 102 selectedCases.put(t, getTestCase(t)); 103 } 104 } 105 106 107 /** 108 * Explicitly exclude a set of test cases by name. Subsequent calls 109 * are cumulative; by default, no test cases are excluded. 110 * @param testCaseNames a comma-separated list of test cases names. 111 * Each name must identify a method in the test object, that takes 112 * no arguments and returns a {@link com.sun.javatest.Status status}. 113 * @throws TestCases.Fault if any of the test case names are invalid. 114 */ 115 public void exclude(String testCaseNames) throws Fault { 116 exclude(split(testCaseNames)); 117 } 118 119 120 /** 121 * Explicitly exclude a set of test cases by name. Subsequent calls 122 * are cumulative; by default, no test cases are excluded. 123 * @param testCaseNames an array of test cases names. 124 * Each name must identify a method in the test object, that takes 125 * no arguments and returns a {@link com.sun.javatest.Status status}. 126 * @throws TestCases.Fault if any of the test case names are invalid. 127 */ 128 public void exclude(String[] testCaseNames) throws Fault { 129 for (int i = 0; i < testCaseNames.length; i++) { 130 String t = testCaseNames[i]; 131 excludedCases.put(t, getTestCase(t)); 132 } 133 } 134 135 136 /** 137 * Return an enumeration of the selected test cases, based on the 138 * select and exclude calls that have been made, if any. 139 * @return An enumeration of the test cases. 140 */ 141 public Enumeration<Method> enumerate() { 142 Vector<Method> v = new Vector<>(); 143 if (selectedCases.isEmpty()) { 144 Method[] methods = testClass.getMethods(); 145 for (int i = 0; i < methods.length; i++) { 146 Method m = methods[i]; 147 if (excludedCases.get(m.getName()) == null) { 148 Class<?>[] paramTypes = m.getParameterTypes(); 149 Class<?> returnType = m.getReturnType(); 150 if ((paramTypes.length == 0) && Status.class.isAssignableFrom(returnType)) 151 v.addElement(m); 152 } 153 } 154 } 155 else { 156 for (Method m : selectedCases.values()) { 157 if (excludedCases.get(m.getName()) == null) 158 v.addElement(m); 159 } 160 } 161 return v.elements(); 162 } 163 164 165 /** 166 * Invoke each of the selected test cases, based upon the select and exclude 167 * calls that have been made, if any. 168 * If the test object provides a public method 169 * {@link com.sun.javatest.Status}invokeTestCase({@link java.lang.reflect.Method}) 170 * that method will be called to invoke the test cases; otherwise, the test 171 * cases will be invoked directly. 172 * It is an error if no test cases are selected, (or if they have all been excluded.) 173 * @return the combined result of executing all the test cases. 174 */ 175 public Status invokeTestCases() { 176 // see if test object provides Status invokeTestCase(Method m) 177 Method invoker; 178 try { 179 invoker = testClass.getMethod("invokeTestCase", new Class<?>[] {Method.class}); 180 if (!Status.class.isAssignableFrom(invoker.getReturnType())) 181 invoker = null; 182 } 183 catch (NoSuchMethodException e) { 184 invoker = null; 185 } 186 187 MultiStatus ms = new MultiStatus(log); 188 for (Enumeration<Method> e = enumerate(); e.hasMoreElements(); ) { 189 Method m = (e.nextElement()); 190 Status s; 191 try { 192 if (invoker != null) 193 s = (Status)invoker.invoke(test, new Object[] {m}); 194 else 195 s = (Status)m.invoke(test, noArgs); 196 } 197 catch (IllegalAccessException ex) { 198 s = Status.failed("Could not access test case: " + m.getName()); 199 } 200 catch (InvocationTargetException ex) { 201 printStackTrace(ex.getTargetException()); 202 s = Status.failed("Exception from test case: " + 203 ex.getTargetException().toString()); 204 } 205 catch (ThreadDeath t) { 206 printStackTrace(t); 207 throw t; 208 } 209 catch (Throwable t) { 210 printStackTrace(t); 211 s = Status.failed("Unexpected Throwable: " + t); 212 } 213 214 ms.add(m.getName(), s); 215 } 216 if (ms.getTestCount() == 0) 217 return Status.passed("Test passed by default: no test cases executed."); 218 else 219 return ms.getStatus(); 220 } 221 222 /** 223 * Print a stack trace for an exception to the log. 224 * @param t The Throwable for which to print the trace 225 */ 226 protected void printStackTrace(Throwable t) { 227 if (log != null) 228 t.printStackTrace(log); 229 } 230 231 /** 232 * Look up a test case in the test object. 233 * @param name the name of the test case; it must identify a method 234 * Status name() 235 * @return the selected method 236 * @throws Fault if the name does not identify an appropriate method. 237 */ 238 private Method getTestCase(String name) throws Fault { 239 try { 240 Method m = testClass.getMethod(name, noArgTypes); 241 if (!Status.class.isAssignableFrom(m.getReturnType())) 242 throw new Fault("Method for test case '" + name + "' has wrong return type" ); 243 return m; 244 } 245 catch (NoSuchMethodException e) { 246 throw new Fault("Could not find test case: " + name); 247 } 248 catch (SecurityException e) { 249 throw new Fault(e.toString()); 250 } 251 } 252 253 private String[] split(String s) { 254 Vector<String> v = new Vector<>(); 255 int start = 0; 256 for (int i = s.indexOf(','); i != -1; i = s.indexOf(',', start)) { 257 v.addElement(s.substring(start, i)); 258 start = i + 1; 259 } 260 if (start != s.length()) 261 v.addElement(s.substring(start)); 262 String[] ss = new String[v.size()]; 263 v.copyInto(ss); 264 return ss; 265 } 266 267 private Object test; 268 private Class<?> testClass; 269 private Map<String, Method> selectedCases = new Hashtable<>(); 270 private Map<String, Method> excludedCases = new Hashtable<>(); 271 private PrintWriter log; 272 273 private static final Object[] noArgs = { }; 274 private static final Class<?>[] noArgTypes = { }; 275 }