001/* 002 * Copyright (C) 2012 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.testing; 018 019import static com.google.common.base.Predicates.and; 020import static com.google.common.base.Predicates.not; 021import static com.google.common.testing.AbstractPackageSanityTests.Chopper.suffix; 022 023import com.google.common.annotations.GwtIncompatible; 024import com.google.common.annotations.J2ktIncompatible; 025import com.google.common.annotations.VisibleForTesting; 026import com.google.common.base.Optional; 027import com.google.common.base.Predicate; 028import com.google.common.collect.HashMultimap; 029import com.google.common.collect.ImmutableList; 030import com.google.common.collect.Iterables; 031import com.google.common.collect.Lists; 032import com.google.common.collect.Maps; 033import com.google.common.collect.Multimap; 034import com.google.common.collect.Sets; 035import com.google.common.reflect.ClassPath; 036import com.google.common.testing.NullPointerTester.Visibility; 037import com.google.j2objc.annotations.J2ObjCIncompatible; 038import java.io.IOException; 039import java.io.Serializable; 040import java.util.LinkedHashSet; 041import java.util.List; 042import java.util.Locale; 043import java.util.TreeMap; 044import java.util.logging.Level; 045import java.util.logging.Logger; 046import junit.framework.TestCase; 047import org.junit.Test; 048 049/** 050 * Automatically runs sanity checks against top level classes in the same package of the test that 051 * extends {@code AbstractPackageSanityTests}. Currently sanity checks include {@link 052 * NullPointerTester}, {@link EqualsTester} and {@link SerializableTester}. For example: 053 * 054 * <pre> 055 * public class PackageSanityTests extends AbstractPackageSanityTests {} 056 * </pre> 057 * 058 * <p>Note that only top-level classes with either a non-private constructor or a non-private static 059 * factory method to construct instances can have their instance methods checked. For example: 060 * 061 * <pre> 062 * public class Address { 063 * private final String city; 064 * private final String state; 065 * private final String zipcode; 066 * 067 * public Address(String city, String state, String zipcode) {...} 068 * 069 * {@literal @Override} public boolean equals(Object obj) {...} 070 * {@literal @Override} public int hashCode() {...} 071 * ... 072 * } 073 * </pre> 074 * 075 * <p>No cascading checks are performed against the return values of methods unless the method is a 076 * static factory method. Neither are semantics of mutation methods such as {@code 077 * someList.add(obj)} checked. For more detailed discussion of supported and unsupported cases, see 078 * {@link #testEquals}, {@link #testNulls} and {@link #testSerializable}. 079 * 080 * <p>For testing against the returned instances from a static factory class, such as 081 * 082 * <pre> 083 * interface Book {...} 084 * public class Books { 085 * public static Book hardcover(String title) {...} 086 * public static Book paperback(String title) {...} 087 * } 088 * </pre> 089 * 090 * <p>please use {@link ClassSanityTester#forAllPublicStaticMethods}. 091 * 092 * <p>If not all classes on the classpath should be covered, {@link #ignoreClasses} can be used to 093 * exclude certain classes. As a special case, classes with an underscore in the name (like {@code 094 * AutoValue_Foo}) can be excluded using <code>ignoreClasses({@link #UNDERSCORE_IN_NAME})</code>. 095 * 096 * <p>{@link #setDefault} allows subclasses to specify default values for types. 097 * 098 * <p>This class incurs IO because it scans the classpath and reads classpath resources. 099 * 100 * @author Ben Yu 101 * @since 14.0 102 */ 103// TODO: Switch to JUnit 4 and use @Parameterized and @BeforeClass 104// Note: @Test annotations are deliberate, as some subclasses specify @RunWith(JUnit4). 105@GwtIncompatible 106@J2ktIncompatible 107@J2ObjCIncompatible // com.google.common.reflect.ClassPath 108public abstract class AbstractPackageSanityTests extends TestCase { 109 110 /** 111 * A predicate that matches classes with an underscore in the class name. This can be used with 112 * {@link #ignoreClasses} to exclude generated classes, such as the {@code AutoValue_Foo} classes 113 * generated by <a href="https://github.com/google/auto/tree/master/value">AutoValue</a>. 114 * 115 * @since 19.0 116 */ 117 public static final Predicate<Class<?>> UNDERSCORE_IN_NAME = 118 (Class<?> c) -> c.getSimpleName().contains("_"); 119 120 /* The names of the expected method that tests null checks. */ 121 private static final ImmutableList<String> NULL_TEST_METHOD_NAMES = 122 ImmutableList.of( 123 "testNulls", "testNull", 124 "testNullPointers", "testNullPointer", 125 "testNullPointerExceptions", "testNullPointerException"); 126 127 /* The names of the expected method that tests serializable. */ 128 private static final ImmutableList<String> SERIALIZABLE_TEST_METHOD_NAMES = 129 ImmutableList.of( 130 "testSerializable", "testSerialization", 131 "testEqualsAndSerializable", "testEqualsAndSerialization"); 132 133 /* The names of the expected method that tests equals. */ 134 private static final ImmutableList<String> EQUALS_TEST_METHOD_NAMES = 135 ImmutableList.of( 136 "testEquals", 137 "testEqualsAndHashCode", 138 "testEqualsAndSerializable", 139 "testEqualsAndSerialization", 140 "testEquality"); 141 142 private static final Chopper TEST_SUFFIX = 143 suffix("Test").or(suffix("Tests")).or(suffix("TestCase")).or(suffix("TestSuite")); 144 145 private final Logger logger = Logger.getLogger(getClass().getName()); 146 private final ClassSanityTester tester = new ClassSanityTester(); 147 private Visibility visibility = Visibility.PACKAGE; 148 private Predicate<Class<?>> classFilter = 149 (Class<?> cls) -> visibility.isVisible(cls.getModifiers()); 150 151 /** 152 * Restricts the sanity tests for public API only. By default, package-private API are also 153 * covered. 154 */ 155 protected final void publicApiOnly() { 156 visibility = Visibility.PUBLIC; 157 } 158 159 /** 160 * Tests all top-level {@link Serializable} classes in the package. For a serializable Class 161 * {@code C}: 162 * 163 * <ul> 164 * <li>If {@code C} explicitly implements {@link Object#equals}, the deserialized instance will 165 * be checked to be equal to the instance before serialization. 166 * <li>If {@code C} doesn't explicitly implement {@code equals} but instead inherits it from a 167 * superclass, no equality check is done on the deserialized instance because it's not clear 168 * whether the author intended for the class to be a value type. 169 * <li>If a constructor or factory method takes a parameter whose type is interface, a dynamic 170 * proxy will be passed to the method. It's possible that the method body expects an 171 * instance method of the passed-in proxy to be of a certain value yet the proxy isn't aware 172 * of the assumption, in which case the equality check before and after serialization will 173 * fail. 174 * <li>If the constructor or factory method takes a parameter that {@link 175 * AbstractPackageSanityTests} doesn't know how to construct, the test will fail. 176 * <li>If there is no visible constructor or visible static factory method declared by {@code 177 * C}, {@code C} is skipped for serialization test, even if it implements {@link 178 * Serializable}. 179 * <li>Serialization test is not performed on method return values unless the method is a 180 * visible static factory method whose return type is {@code C} or {@code C}'s subtype. 181 * </ul> 182 * 183 * <p>In all cases, if {@code C} needs custom logic for testing serialization, you can add an 184 * explicit {@code testSerializable()} test in the corresponding {@code CTest} class, and {@code 185 * C} will be excluded from automated serialization test performed by this method. 186 */ 187 @Test 188 public void testSerializable() throws Exception { 189 // TODO: when we use @BeforeClass, we can pay the cost of class path scanning only once. 190 for (Class<?> classToTest : 191 findClassesToTest(loadClassesInPackage(), SERIALIZABLE_TEST_METHOD_NAMES)) { 192 if (Serializable.class.isAssignableFrom(classToTest)) { 193 try { 194 Object instance = tester.instantiate(classToTest); 195 if (instance != null) { 196 if (isEqualsDefined(classToTest)) { 197 SerializableTester.reserializeAndAssert(instance); 198 } else { 199 SerializableTester.reserialize(instance); 200 } 201 } 202 } catch (Throwable e) { 203 throw sanityError(classToTest, SERIALIZABLE_TEST_METHOD_NAMES, "serializable test", e); 204 } 205 } 206 } 207 } 208 209 /** 210 * Performs {@link NullPointerTester} checks for all top-level classes in the package. For a class 211 * {@code C} 212 * 213 * <ul> 214 * <li>All visible static methods are checked such that passing null for any parameter that's 215 * not annotated nullable (according to the rules of {@link NullPointerTester}) should throw 216 * {@link NullPointerException}. 217 * <li>If there is any visible constructor or visible static factory method declared by the 218 * class, all visible instance methods will be checked too using the instance created by 219 * invoking the constructor or static factory method. 220 * <li>If the constructor or factory method used to construct instance takes a parameter that 221 * {@link AbstractPackageSanityTests} doesn't know how to construct, the test will fail. 222 * <li>If there is no visible constructor or visible static factory method declared by {@code 223 * C}, instance methods are skipped for nulls test. 224 * <li>Nulls test is not performed on method return values unless the method is a visible static 225 * factory method whose return type is {@code C} or {@code C}'s subtype. 226 * </ul> 227 * 228 * <p>In all cases, if {@code C} needs custom logic for testing nulls, you can add an explicit 229 * {@code testNulls()} test in the corresponding {@code CTest} class, and {@code C} will be 230 * excluded from the automated null tests performed by this method. 231 */ 232 @Test 233 public void testNulls() throws Exception { 234 for (Class<?> classToTest : findClassesToTest(loadClassesInPackage(), NULL_TEST_METHOD_NAMES)) { 235 if (classToTest.getSimpleName().equals("ReflectionFreeAssertThrows")) { 236 /* 237 * These classes handle null properly but throw IllegalArgumentException for the default 238 * Class argument that this test uses. Normally we'd fix that by declaring a 239 * ReflectionFreeAssertThrowsTest with a testNulls method, but that's annoying to have to do 240 * for a package-private utility class. So we skip the class entirely instead. 241 */ 242 continue; 243 } 244 try { 245 tester.doTestNulls(classToTest, visibility); 246 } catch (Throwable e) { 247 throw sanityError(classToTest, NULL_TEST_METHOD_NAMES, "nulls test", e); 248 } 249 } 250 } 251 252 /** 253 * Tests {@code equals()} and {@code hashCode()} implementations for every top-level class in the 254 * package, that explicitly implements {@link Object#equals}. For a class {@code C}: 255 * 256 * <ul> 257 * <li>The visible constructor or visible static factory method with the most parameters is used 258 * to construct the sample instances. In case of tie, the candidate constructors or 259 * factories are tried one after another until one can be used to construct sample 260 * instances. 261 * <li>For the constructor or static factory method used to construct instances, it's checked 262 * that when equal parameters are passed, the result instance should also be equal; and vice 263 * versa. 264 * <li>Inequality check is not performed against state mutation methods such as {@link 265 * List#add}, or functional update methods such as {@link 266 * com.google.common.base.Joiner#skipNulls}. 267 * <li>If the constructor or factory method used to construct instance takes a parameter that 268 * {@link AbstractPackageSanityTests} doesn't know how to construct, the test will fail. 269 * <li>If there is no visible constructor or visible static factory method declared by {@code 270 * C}, {@code C} is skipped for equality test. 271 * <li>Equality test is not performed on method return values unless the method is a visible 272 * static factory method whose return type is {@code C} or {@code C}'s subtype. 273 * </ul> 274 * 275 * <p>In all cases, if {@code C} needs custom logic for testing {@code equals()}, you can add an 276 * explicit {@code testEquals()} test in the corresponding {@code CTest} class, and {@code C} will 277 * be excluded from the automated {@code equals} test performed by this method. 278 */ 279 @Test 280 public void testEquals() throws Exception { 281 for (Class<?> classToTest : 282 findClassesToTest(loadClassesInPackage(), EQUALS_TEST_METHOD_NAMES)) { 283 if (!classToTest.isEnum() && isEqualsDefined(classToTest)) { 284 try { 285 tester.doTestEquals(classToTest); 286 } catch (Throwable e) { 287 throw sanityError(classToTest, EQUALS_TEST_METHOD_NAMES, "equals test", e); 288 } 289 } 290 } 291 } 292 293 /** 294 * Sets the default value for {@code type}, when dummy value for a parameter of the same type 295 * needs to be created in order to invoke a method or constructor. The default value isn't used in 296 * testing {@link Object#equals} because more than one sample instances are needed for testing 297 * inequality. 298 */ 299 protected final <T> void setDefault(Class<T> type, T value) { 300 tester.setDefault(type, value); 301 } 302 303 /** 304 * Sets two distinct values for {@code type}. These values can be used for both null pointer 305 * testing and equals testing. 306 * 307 * @since 17.0 308 */ 309 protected final <T> void setDistinctValues(Class<T> type, T value1, T value2) { 310 tester.setDistinctValues(type, value1, value2); 311 } 312 313 /** Specifies that classes that satisfy the given predicate aren't tested for sanity. */ 314 protected final void ignoreClasses(Predicate<? super Class<?>> condition) { 315 this.classFilter = and(this.classFilter, not(condition)); 316 } 317 318 private static AssertionError sanityError( 319 Class<?> cls, List<String> explicitTestNames, String description, Throwable e) { 320 String message = 321 String.format( 322 Locale.ROOT, 323 "Error in automated %s of %s\n" 324 + "If the class is better tested explicitly, you can add %s() to %sTest", 325 description, 326 cls, 327 explicitTestNames.get(0), 328 cls.getName()); 329 return new AssertionError(message, e); 330 } 331 332 /** 333 * Finds the classes not ending with a test suffix and not covered by an explicit test whose name 334 * is {@code explicitTestNames}. 335 */ 336 @VisibleForTesting 337 List<Class<?>> findClassesToTest( 338 Iterable<? extends Class<?>> classes, Iterable<String> explicitTestNames) { 339 // "a.b.Foo" -> a.b.Foo.class 340 TreeMap<String, Class<?>> classMap = Maps.newTreeMap(); 341 for (Class<?> cls : classes) { 342 classMap.put(cls.getName(), cls); 343 } 344 // Foo.class -> [FooTest.class, FooTests.class, FooTestSuite.class, ...] 345 Multimap<Class<?>, Class<?>> testClasses = HashMultimap.create(); 346 LinkedHashSet<Class<?>> candidateClasses = Sets.newLinkedHashSet(); 347 for (Class<?> cls : classes) { 348 Optional<String> testedClassName = TEST_SUFFIX.chop(cls.getName()); 349 if (testedClassName.isPresent()) { 350 Class<?> testedClass = classMap.get(testedClassName.get()); 351 if (testedClass != null) { 352 testClasses.put(testedClass, cls); 353 } 354 } else { 355 candidateClasses.add(cls); 356 } 357 } 358 List<Class<?>> result = Lists.newArrayList(); 359 NEXT_CANDIDATE: 360 for (Class<?> candidate : Iterables.filter(candidateClasses, classFilter)) { 361 for (Class<?> testClass : testClasses.get(candidate)) { 362 if (hasTest(testClass, explicitTestNames)) { 363 // covered by explicit test 364 continue NEXT_CANDIDATE; 365 } 366 } 367 result.add(candidate); 368 } 369 return result; 370 } 371 372 private List<Class<?>> loadClassesInPackage() throws IOException { 373 List<Class<?>> classes = Lists.newArrayList(); 374 String packageName = getClass().getPackage().getName(); 375 for (ClassPath.ClassInfo classInfo : 376 ClassPath.from(getClass().getClassLoader()).getTopLevelClasses(packageName)) { 377 Class<?> cls; 378 try { 379 cls = classInfo.load(); 380 } catch (NoClassDefFoundError e) { 381 // In case there were linking problems, this is probably not a class we care to test anyway. 382 logger.log(Level.SEVERE, "Cannot load class " + classInfo + ", skipping...", e); 383 continue; 384 } 385 if (!cls.isInterface()) { 386 classes.add(cls); 387 } 388 } 389 return classes; 390 } 391 392 private static boolean hasTest(Class<?> testClass, Iterable<String> testNames) { 393 for (String testName : testNames) { 394 try { 395 testClass.getMethod(testName); 396 return true; 397 } catch (NoSuchMethodException e) { 398 continue; 399 } 400 } 401 return false; 402 } 403 404 private static boolean isEqualsDefined(Class<?> cls) { 405 try { 406 return !cls.getDeclaredMethod("equals", Object.class).isSynthetic(); 407 } catch (NoSuchMethodException e) { 408 return false; 409 } 410 } 411 412 abstract static class Chopper { 413 414 final Chopper or(Chopper you) { 415 Chopper i = this; 416 return new Chopper() { 417 @Override 418 Optional<String> chop(String str) { 419 return i.chop(str).or(you.chop(str)); 420 } 421 }; 422 } 423 424 abstract Optional<String> chop(String str); 425 426 static Chopper suffix(String suffix) { 427 return new Chopper() { 428 @Override 429 Optional<String> chop(String str) { 430 if (str.endsWith(suffix)) { 431 return Optional.of(str.substring(0, str.length() - suffix.length())); 432 } else { 433 return Optional.absent(); 434 } 435 } 436 }; 437 } 438 } 439}