001 package org.junit.runner; 002 003 import java.io.Serializable; 004 import java.lang.annotation.Annotation; 005 import java.util.ArrayList; 006 import java.util.Arrays; 007 import java.util.Collection; 008 import java.util.concurrent.ConcurrentLinkedQueue; 009 import java.util.regex.Matcher; 010 import java.util.regex.Pattern; 011 012 /** 013 * A <code>Description</code> describes a test which is to be run or has been run. <code>Descriptions</code> 014 * can be atomic (a single test) or compound (containing children tests). <code>Descriptions</code> are used 015 * to provide feedback about the tests that are about to run (for example, the tree view 016 * visible in many IDEs) or tests that have been run (for example, the failures view). 017 * <p> 018 * <code>Descriptions</code> are implemented as a single class rather than a Composite because 019 * they are entirely informational. They contain no logic aside from counting their tests. 020 * <p> 021 * In the past, we used the raw {@link junit.framework.TestCase}s and {@link junit.framework.TestSuite}s 022 * to display the tree of tests. This was no longer viable in JUnit 4 because atomic tests no longer have 023 * a superclass below {@link Object}. We needed a way to pass a class and name together. Description 024 * emerged from this. 025 * 026 * @see org.junit.runner.Request 027 * @see org.junit.runner.Runner 028 * @since 4.0 029 */ 030 public class Description implements Serializable { 031 private static final long serialVersionUID = 1L; 032 033 private static final Pattern METHOD_AND_CLASS_NAME_PATTERN = Pattern 034 .compile("([\\s\\S]*)\\((.*)\\)"); 035 036 /** 037 * Create a <code>Description</code> named <code>name</code>. 038 * Generally, you will add children to this <code>Description</code>. 039 * 040 * @param name the name of the <code>Description</code> 041 * @param annotations meta-data about the test, for downstream interpreters 042 * @return a <code>Description</code> named <code>name</code> 043 */ 044 public static Description createSuiteDescription(String name, Annotation... annotations) { 045 return new Description(null, name, annotations); 046 } 047 048 /** 049 * Create a <code>Description</code> named <code>name</code>. 050 * Generally, you will add children to this <code>Description</code>. 051 * 052 * @param name the name of the <code>Description</code> 053 * @param uniqueId an arbitrary object used to define uniqueness (in {@link #equals(Object)} 054 * @param annotations meta-data about the test, for downstream interpreters 055 * @return a <code>Description</code> named <code>name</code> 056 */ 057 public static Description createSuiteDescription(String name, Serializable uniqueId, Annotation... annotations) { 058 return new Description(null, name, uniqueId, annotations); 059 } 060 061 /** 062 * Create a <code>Description</code> of a single test named <code>name</code> in the 'class' named 063 * <code>className</code>. Generally, this will be a leaf <code>Description</code>. This method is a better choice 064 * than {@link #createTestDescription(Class, String, Annotation...)} for test runners whose test cases are not 065 * defined in an actual Java <code>Class</code>. 066 * 067 * @param className the class name of the test 068 * @param name the name of the test (a method name for test annotated with {@link org.junit.Test}) 069 * @param annotations meta-data about the test, for downstream interpreters 070 * @return a <code>Description</code> named <code>name</code> 071 */ 072 public static Description createTestDescription(String className, String name, Annotation... annotations) { 073 return new Description(null, formatDisplayName(name, className), annotations); 074 } 075 076 /** 077 * Create a <code>Description</code> of a single test named <code>name</code> in the class <code>clazz</code>. 078 * Generally, this will be a leaf <code>Description</code>. 079 * 080 * @param clazz the class of the test 081 * @param name the name of the test (a method name for test annotated with {@link org.junit.Test}) 082 * @param annotations meta-data about the test, for downstream interpreters 083 * @return a <code>Description</code> named <code>name</code> 084 */ 085 public static Description createTestDescription(Class<?> clazz, String name, Annotation... annotations) { 086 return new Description(clazz, formatDisplayName(name, clazz.getName()), annotations); 087 } 088 089 /** 090 * Create a <code>Description</code> of a single test named <code>name</code> in the class <code>clazz</code>. 091 * Generally, this will be a leaf <code>Description</code>. 092 * (This remains for binary compatibility with clients of JUnit 4.3) 093 * 094 * @param clazz the class of the test 095 * @param name the name of the test (a method name for test annotated with {@link org.junit.Test}) 096 * @return a <code>Description</code> named <code>name</code> 097 */ 098 public static Description createTestDescription(Class<?> clazz, String name) { 099 return new Description(clazz, formatDisplayName(name, clazz.getName())); 100 } 101 102 /** 103 * Create a <code>Description</code> of a single test named <code>name</code> in the class <code>clazz</code>. 104 * Generally, this will be a leaf <code>Description</code>. 105 * 106 * @param name the name of the test (a method name for test annotated with {@link org.junit.Test}) 107 * @return a <code>Description</code> named <code>name</code> 108 */ 109 public static Description createTestDescription(String className, String name, Serializable uniqueId) { 110 return new Description(null, formatDisplayName(name, className), uniqueId); 111 } 112 113 private static String formatDisplayName(String name, String className) { 114 return String.format("%s(%s)", name, className); 115 } 116 117 /** 118 * Create a <code>Description</code> named after <code>testClass</code> 119 * 120 * @param testClass A {@link Class} containing tests 121 * @return a <code>Description</code> of <code>testClass</code> 122 */ 123 public static Description createSuiteDescription(Class<?> testClass) { 124 return new Description(testClass, testClass.getName(), testClass.getAnnotations()); 125 } 126 127 /** 128 * Describes a Runner which runs no tests 129 */ 130 public static final Description EMPTY = new Description(null, "No Tests"); 131 132 /** 133 * Describes a step in the test-running mechanism that goes so wrong no 134 * other description can be used (for example, an exception thrown from a Runner's 135 * constructor 136 */ 137 public static final Description TEST_MECHANISM = new Description(null, "Test mechanism"); 138 139 private final Collection<Description> children = new ConcurrentLinkedQueue<Description>(); 140 private final String displayName; 141 private final Serializable uniqueId; 142 private final Annotation[] annotations; 143 private volatile /* write-once */ Class<?> testClass; 144 145 private Description(Class<?> clazz, String displayName, Annotation... annotations) { 146 this(clazz, displayName, displayName, annotations); 147 } 148 149 private Description(Class<?> testClass, String displayName, Serializable uniqueId, Annotation... annotations) { 150 if ((displayName == null) || (displayName.length() == 0)) { 151 throw new IllegalArgumentException( 152 "The display name must not be empty."); 153 } 154 if ((uniqueId == null)) { 155 throw new IllegalArgumentException( 156 "The unique id must not be null."); 157 } 158 this.testClass = testClass; 159 this.displayName = displayName; 160 this.uniqueId = uniqueId; 161 this.annotations = annotations; 162 } 163 164 /** 165 * @return a user-understandable label 166 */ 167 public String getDisplayName() { 168 return displayName; 169 } 170 171 /** 172 * Add <code>Description</code> as a child of the receiver. 173 * 174 * @param description the soon-to-be child. 175 */ 176 public void addChild(Description description) { 177 children.add(description); 178 } 179 180 /** 181 * Gets the copy of the children of this {@code Description}. 182 * Returns an empty list if there are no children. 183 */ 184 public ArrayList<Description> getChildren() { 185 return new ArrayList<Description>(children); 186 } 187 188 /** 189 * @return <code>true</code> if the receiver is a suite 190 */ 191 public boolean isSuite() { 192 return !isTest(); 193 } 194 195 /** 196 * @return <code>true</code> if the receiver is an atomic test 197 */ 198 public boolean isTest() { 199 return children.isEmpty(); 200 } 201 202 /** 203 * @return the total number of atomic tests in the receiver 204 */ 205 public int testCount() { 206 if (isTest()) { 207 return 1; 208 } 209 int result = 0; 210 for (Description child : children) { 211 result += child.testCount(); 212 } 213 return result; 214 } 215 216 @Override 217 public int hashCode() { 218 return uniqueId.hashCode(); 219 } 220 221 @Override 222 public boolean equals(Object obj) { 223 if (!(obj instanceof Description)) { 224 return false; 225 } 226 Description d = (Description) obj; 227 return uniqueId.equals(d.uniqueId); 228 } 229 230 @Override 231 public String toString() { 232 return getDisplayName(); 233 } 234 235 /** 236 * @return true if this is a description of a Runner that runs no tests 237 */ 238 public boolean isEmpty() { 239 return equals(EMPTY); 240 } 241 242 /** 243 * @return a copy of this description, with no children (on the assumption that some of the 244 * children will be added back) 245 */ 246 public Description childlessCopy() { 247 return new Description(testClass, displayName, annotations); 248 } 249 250 /** 251 * @return the annotation of type annotationType that is attached to this description node, 252 * or null if none exists 253 */ 254 public <T extends Annotation> T getAnnotation(Class<T> annotationType) { 255 for (Annotation each : annotations) { 256 if (each.annotationType().equals(annotationType)) { 257 return annotationType.cast(each); 258 } 259 } 260 return null; 261 } 262 263 /** 264 * @return all of the annotations attached to this description node 265 */ 266 public Collection<Annotation> getAnnotations() { 267 return Arrays.asList(annotations); 268 } 269 270 /** 271 * @return If this describes a method invocation, 272 * the class of the test instance. 273 */ 274 public Class<?> getTestClass() { 275 if (testClass != null) { 276 return testClass; 277 } 278 String name = getClassName(); 279 if (name == null) { 280 return null; 281 } 282 try { 283 testClass = Class.forName(name, false, getClass().getClassLoader()); 284 return testClass; 285 } catch (ClassNotFoundException e) { 286 return null; 287 } 288 } 289 290 /** 291 * @return If this describes a method invocation, 292 * the name of the class of the test instance 293 */ 294 public String getClassName() { 295 return testClass != null ? testClass.getName() : methodAndClassNamePatternGroupOrDefault(2, toString()); 296 } 297 298 /** 299 * @return If this describes a method invocation, 300 * the name of the method (or null if not) 301 */ 302 public String getMethodName() { 303 return methodAndClassNamePatternGroupOrDefault(1, null); 304 } 305 306 private String methodAndClassNamePatternGroupOrDefault(int group, 307 String defaultString) { 308 Matcher matcher = METHOD_AND_CLASS_NAME_PATTERN.matcher(toString()); 309 return matcher.matches() ? matcher.group(group) : defaultString; 310 } 311 }