Coverage Summary for Class: AbstractPackageSanityTests (com.google.common.testing)

Class Method, % Line, %
AbstractPackageSanityTests 78.6% (11/14) 75.2% (76/101)
AbstractPackageSanityTests$1 100% (2/2) 100% (2/2)
AbstractPackageSanityTests$2 100% (2/2) 100% (2/2)
AbstractPackageSanityTests$Chopper 100% (3/3) 100% (4/4)
AbstractPackageSanityTests$Chopper$1 100% (2/2) 100% (2/2)
AbstractPackageSanityTests$Chopper$2 100% (2/2) 100% (4/4)
Total 88% (22/25) 78.3% (90/115)


1 /* 2  * Copyright (C) 2012 The Guava Authors 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); 5  * you may not use this file except in compliance with the License. 6  * You may obtain a copy of the License at 7  * 8  * http://www.apache.org/licenses/LICENSE-2.0 9  * 10  * Unless required by applicable law or agreed to in writing, software 11  * distributed under the License is distributed on an "AS IS" BASIS, 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13  * See the License for the specific language governing permissions and 14  * limitations under the License. 15  */ 16  17 package com.google.common.testing; 18  19 import static com.google.common.base.Predicates.and; 20 import static com.google.common.base.Predicates.not; 21 import static com.google.common.testing.AbstractPackageSanityTests.Chopper.suffix; 22  23 import com.google.common.annotations.Beta; 24 import com.google.common.annotations.GwtIncompatible; 25 import com.google.common.annotations.VisibleForTesting; 26 import com.google.common.base.Optional; 27 import com.google.common.base.Predicate; 28 import com.google.common.collect.HashMultimap; 29 import com.google.common.collect.ImmutableList; 30 import com.google.common.collect.Iterables; 31 import com.google.common.collect.Lists; 32 import com.google.common.collect.Maps; 33 import com.google.common.collect.Multimap; 34 import com.google.common.collect.Sets; 35 import com.google.common.reflect.ClassPath; 36 import com.google.common.testing.NullPointerTester.Visibility; 37 import com.google.j2objc.annotations.J2ObjCIncompatible; 38 import java.io.IOException; 39 import java.io.Serializable; 40 import java.util.LinkedHashSet; 41 import java.util.List; 42 import java.util.Locale; 43 import java.util.TreeMap; 44 import java.util.logging.Level; 45 import java.util.logging.Logger; 46 import junit.framework.AssertionFailedError; 47 import junit.framework.TestCase; 48 import org.junit.Test; 49  50 /** 51  * Automatically runs sanity checks against top level classes in the same package of the test that 52  * extends {@code AbstractPackageSanityTests}. Currently sanity checks include {@link 53  * NullPointerTester}, {@link EqualsTester} and {@link SerializableTester}. For example: 54  * 55  * <pre> 56  * public class PackageSanityTests extends AbstractPackageSanityTests {} 57  * </pre> 58  * 59  * <p>Note that only top-level classes with either a non-private constructor or a non-private static 60  * factory method to construct instances can have their instance methods checked. For example: 61  * 62  * <pre> 63  * public class Address { 64  * private final String city; 65  * private final String state; 66  * private final String zipcode; 67  * 68  * public Address(String city, String state, String zipcode) {...} 69  * 70  * {@literal @Override} public boolean equals(Object obj) {...} 71  * {@literal @Override} public int hashCode() {...} 72  * ... 73  * } 74  * </pre> 75  * 76  * <p>No cascading checks are performed against the return values of methods unless the method is a 77  * static factory method. Neither are semantics of mutation methods such as {@code 78  * someList.add(obj)} checked. For more detailed discussion of supported and unsupported cases, see 79  * {@link #testEquals}, {@link #testNulls} and {@link #testSerializable}. 80  * 81  * <p>For testing against the returned instances from a static factory class, such as 82  * 83  * <pre> 84  * interface Book {...} 85  * public class Books { 86  * public static Book hardcover(String title) {...} 87  * public static Book paperback(String title) {...} 88  * } 89  * </pre> 90  * 91  * <p>please use {@link ClassSanityTester#forAllPublicStaticMethods}. 92  * 93  * <p>If not all classes on the classpath should be covered, {@link #ignoreClasses} can be used to 94  * exclude certain classes. As a special case, classes with an underscore in the name (like {@code 95  * AutoValue_Foo}) can be excluded using <code>ignoreClasses({@link #UNDERSCORE_IN_NAME})</code>. 96  * 97  * <p>{@link #setDefault} allows subclasses to specify default values for types. 98  * 99  * <p>This class incurs IO because it scans the classpath and reads classpath resources. 100  * 101  * @author Ben Yu 102  * @since 14.0 103  */ 104 @Beta 105 // TODO: Switch to JUnit 4 and use @Parameterized and @BeforeClass 106 // Note: @Test annotations are deliberate, as some subclasses specify @RunWith(JUnit4). 107 @GwtIncompatible 108 @J2ObjCIncompatible // com.google.common.reflect.ClassPath 109 public abstract class AbstractPackageSanityTests extends TestCase { 110  111  /** 112  * A predicate that matches classes with an underscore in the class name. This can be used with 113  * {@link #ignoreClasses} to exclude generated classes, such as the {@code AutoValue_Foo} classes 114  * generated by <a href="https://github.com/google/auto/tree/master/value">AutoValue</a>. 115  * 116  * @since 19.0 117  */ 118  public static final Predicate<Class<?>> UNDERSCORE_IN_NAME = 119  new Predicate<Class<?>>() { 120  @Override 121  public boolean apply(Class<?> c) { 122  return c.getSimpleName().contains("_"); 123  } 124  }; 125  126  /* The names of the expected method that tests null checks. */ 127  private static final ImmutableList<String> NULL_TEST_METHOD_NAMES = 128  ImmutableList.of( 129  "testNulls", "testNull", 130  "testNullPointers", "testNullPointer", 131  "testNullPointerExceptions", "testNullPointerException"); 132  133  /* The names of the expected method that tests serializable. */ 134  private static final ImmutableList<String> SERIALIZABLE_TEST_METHOD_NAMES = 135  ImmutableList.of( 136  "testSerializable", "testSerialization", 137  "testEqualsAndSerializable", "testEqualsAndSerialization"); 138  139  /* The names of the expected method that tests equals. */ 140  private static final ImmutableList<String> EQUALS_TEST_METHOD_NAMES = 141  ImmutableList.of( 142  "testEquals", 143  "testEqualsAndHashCode", 144  "testEqualsAndSerializable", 145  "testEqualsAndSerialization", 146  "testEquality"); 147  148  private static final Chopper TEST_SUFFIX = 149  suffix("Test").or(suffix("Tests")).or(suffix("TestCase")).or(suffix("TestSuite")); 150  151  private final Logger logger = Logger.getLogger(getClass().getName()); 152  private final ClassSanityTester tester = new ClassSanityTester(); 153  private Visibility visibility = Visibility.PACKAGE; 154  private Predicate<Class<?>> classFilter = 155  new Predicate<Class<?>>() { 156  @Override 157  public boolean apply(Class<?> cls) { 158  return visibility.isVisible(cls.getModifiers()); 159  } 160  }; 161  162  /** 163  * Restricts the sanity tests for public API only. By default, package-private API are also 164  * covered. 165  */ 166  protected final void publicApiOnly() { 167  visibility = Visibility.PUBLIC; 168  } 169  170  /** 171  * Tests all top-level {@link Serializable} classes in the package. For a serializable Class 172  * {@code C}: 173  * 174  * <ul> 175  * <li>If {@code C} explicitly implements {@link Object#equals}, the deserialized instance will 176  * be checked to be equal to the instance before serialization. 177  * <li>If {@code C} doesn't explicitly implement {@code equals} but instead inherits it from a 178  * superclass, no equality check is done on the deserialized instance because it's not clear 179  * whether the author intended for the class to be a value type. 180  * <li>If a constructor or factory method takes a parameter whose type is interface, a dynamic 181  * proxy will be passed to the method. It's possible that the method body expects an 182  * instance method of the passed-in proxy to be of a certain value yet the proxy isn't aware 183  * of the assumption, in which case the equality check before and after serialization will 184  * fail. 185  * <li>If the constructor or factory method takes a parameter that {@link 186  * AbstractPackageSanityTests} doesn't know how to construct, the test will fail. 187  * <li>If there is no visible constructor or visible static factory method declared by {@code 188  * C}, {@code C} is skipped for serialization test, even if it implements {@link 189  * Serializable}. 190  * <li>Serialization test is not performed on method return values unless the method is a 191  * visible static factory method whose return type is {@code C} or {@code C}'s subtype. 192  * </ul> 193  * 194  * <p>In all cases, if {@code C} needs custom logic for testing serialization, you can add an 195  * explicit {@code testSerializable()} test in the corresponding {@code CTest} class, and {@code 196  * C} will be excluded from automated serialization test performed by this method. 197  */ 198  @Test 199  public void testSerializable() throws Exception { 200  // TODO: when we use @BeforeClass, we can pay the cost of class path scanning only once. 201  for (Class<?> classToTest : 202  findClassesToTest(loadClassesInPackage(), SERIALIZABLE_TEST_METHOD_NAMES)) { 203  if (Serializable.class.isAssignableFrom(classToTest)) { 204  try { 205  Object instance = tester.instantiate(classToTest); 206  if (instance != null) { 207  if (isEqualsDefined(classToTest)) { 208  SerializableTester.reserializeAndAssert(instance); 209  } else { 210  SerializableTester.reserialize(instance); 211  } 212  } 213  } catch (Throwable e) { 214  throw sanityError(classToTest, SERIALIZABLE_TEST_METHOD_NAMES, "serializable test", e); 215  } 216  } 217  } 218  } 219  220  /** 221  * Performs {@link NullPointerTester} checks for all top-level classes in the package. For a class 222  * {@code C} 223  * 224  * <ul> 225  * <li>All visible static methods are checked such that passing null for any parameter that's 226  * not annotated nullable (according to the rules of {@link NullPointerTester}) should throw 227  * {@link NullPointerException}. 228  * <li>If there is any visible constructor or visible static factory method declared by the 229  * class, all visible instance methods will be checked too using the instance created by 230  * invoking the constructor or static factory method. 231  * <li>If the constructor or factory method used to construct instance takes a parameter that 232  * {@link AbstractPackageSanityTests} doesn't know how to construct, the test will fail. 233  * <li>If there is no visible constructor or visible static factory method declared by {@code 234  * C}, instance methods are skipped for nulls test. 235  * <li>Nulls test is not performed on method return values unless the method is a visible static 236  * factory method whose return type is {@code C} or {@code C}'s subtype. 237  * </ul> 238  * 239  * <p>In all cases, if {@code C} needs custom logic for testing nulls, you can add an explicit 240  * {@code testNulls()} test in the corresponding {@code CTest} class, and {@code C} will be 241  * excluded from the automated null tests performed by this method. 242  */ 243  @Test 244  public void testNulls() throws Exception { 245  for (Class<?> classToTest : findClassesToTest(loadClassesInPackage(), NULL_TEST_METHOD_NAMES)) { 246  try { 247  tester.doTestNulls(classToTest, visibility); 248  } catch (Throwable e) { 249  throw sanityError(classToTest, NULL_TEST_METHOD_NAMES, "nulls test", e); 250  } 251  } 252  } 253  254  /** 255  * Tests {@code equals()} and {@code hashCode()} implementations for every top-level class in the 256  * package, that explicitly implements {@link Object#equals}. For a class {@code C}: 257  * 258  * <ul> 259  * <li>The visible constructor or visible static factory method with the most parameters is used 260  * to construct the sample instances. In case of tie, the candidate constructors or 261  * factories are tried one after another until one can be used to construct sample 262  * instances. 263  * <li>For the constructor or static factory method used to construct instances, it's checked 264  * that when equal parameters are passed, the result instance should also be equal; and vice 265  * versa. 266  * <li>Inequality check is not performed against state mutation methods such as {@link 267  * List#add}, or functional update methods such as {@link 268  * com.google.common.base.Joiner#skipNulls}. 269  * <li>If the constructor or factory method used to construct instance takes a parameter that 270  * {@link AbstractPackageSanityTests} doesn't know how to construct, the test will fail. 271  * <li>If there is no visible constructor or visible static factory method declared by {@code 272  * C}, {@code C} is skipped for equality test. 273  * <li>Equality test is not performed on method return values unless the method is a visible 274  * static factory method whose return type is {@code C} or {@code C}'s subtype. 275  * </ul> 276  * 277  * <p>In all cases, if {@code C} needs custom logic for testing {@code equals()}, you can add an 278  * explicit {@code testEquals()} test in the corresponding {@code CTest} class, and {@code C} will 279  * be excluded from the automated {@code equals} test performed by this method. 280  */ 281  @Test 282  public void testEquals() throws Exception { 283  for (Class<?> classToTest : 284  findClassesToTest(loadClassesInPackage(), EQUALS_TEST_METHOD_NAMES)) { 285  if (!classToTest.isEnum() && isEqualsDefined(classToTest)) { 286  try { 287  tester.doTestEquals(classToTest); 288  } catch (Throwable e) { 289  throw sanityError(classToTest, EQUALS_TEST_METHOD_NAMES, "equals test", e); 290  } 291  } 292  } 293  } 294  295  /** 296  * Sets the default value for {@code type}, when dummy value for a parameter of the same type 297  * needs to be created in order to invoke a method or constructor. The default value isn't used in 298  * testing {@link Object#equals} because more than one sample instances are needed for testing 299  * inequality. 300  */ 301  protected final <T> void setDefault(Class<T> type, T value) { 302  tester.setDefault(type, value); 303  } 304  305  /** 306  * Sets two distinct values for {@code type}. These values can be used for both null pointer 307  * testing and equals testing. 308  * 309  * @since 17.0 310  */ 311  protected final <T> void setDistinctValues(Class<T> type, T value1, T value2) { 312  tester.setDistinctValues(type, value1, value2); 313  } 314  315  /** Specifies that classes that satisfy the given predicate aren't tested for sanity. */ 316  protected final void ignoreClasses(Predicate<? super Class<?>> condition) { 317  this.classFilter = and(this.classFilter, not(condition)); 318  } 319  320  private static AssertionFailedError sanityError( 321  Class<?> cls, List<String> explicitTestNames, String description, Throwable e) { 322  String message = 323  String.format( 324  Locale.ROOT, 325  "Error in automated %s of %s\n" 326  + "If the class is better tested explicitly, you can add %s() to %sTest", 327  description, 328  cls, 329  explicitTestNames.get(0), 330  cls.getName()); 331  AssertionFailedError error = new AssertionFailedError(message); 332  error.initCause(e); 333  return error; 334  } 335  336  /** 337  * Finds the classes not ending with a test suffix and not covered by an explicit test whose name 338  * is {@code explicitTestName}. 339  */ 340  @VisibleForTesting 341  List<Class<?>> findClassesToTest( 342  Iterable<? extends Class<?>> classes, Iterable<String> explicitTestNames) { 343  // "a.b.Foo" -> a.b.Foo.class 344  TreeMap<String, Class<?>> classMap = Maps.newTreeMap(); 345  for (Class<?> cls : classes) { 346  classMap.put(cls.getName(), cls); 347  } 348  // Foo.class -> [FooTest.class, FooTests.class, FooTestSuite.class, ...] 349  Multimap<Class<?>, Class<?>> testClasses = HashMultimap.create(); 350  LinkedHashSet<Class<?>> candidateClasses = Sets.newLinkedHashSet(); 351  for (Class<?> cls : classes) { 352  Optional<String> testedClassName = TEST_SUFFIX.chop(cls.getName()); 353  if (testedClassName.isPresent()) { 354  Class<?> testedClass = classMap.get(testedClassName.get()); 355  if (testedClass != null) { 356  testClasses.put(testedClass, cls); 357  } 358  } else { 359  candidateClasses.add(cls); 360  } 361  } 362  List<Class<?>> result = Lists.newArrayList(); 363  NEXT_CANDIDATE: 364  for (Class<?> candidate : Iterables.filter(candidateClasses, classFilter)) { 365  for (Class<?> testClass : testClasses.get(candidate)) { 366  if (hasTest(testClass, explicitTestNames)) { 367  // covered by explicit test 368  continue NEXT_CANDIDATE; 369  } 370  } 371  result.add(candidate); 372  } 373  return result; 374  } 375  376  private List<Class<?>> loadClassesInPackage() throws IOException { 377  List<Class<?>> classes = Lists.newArrayList(); 378  String packageName = getClass().getPackage().getName(); 379  for (ClassPath.ClassInfo classInfo : 380  ClassPath.from(getClass().getClassLoader()).getTopLevelClasses(packageName)) { 381  Class<?> cls; 382  try { 383  cls = classInfo.load(); 384  } catch (NoClassDefFoundError e) { 385  // In case there were linking problems, this is probably not a class we care to test anyway. 386  logger.log(Level.SEVERE, "Cannot load class " + classInfo + ", skipping...", e); 387  continue; 388  } 389  if (!cls.isInterface()) { 390  classes.add(cls); 391  } 392  } 393  return classes; 394  } 395  396  private static boolean hasTest(Class<?> testClass, Iterable<String> testNames) { 397  for (String testName : testNames) { 398  try { 399  testClass.getMethod(testName); 400  return true; 401  } catch (NoSuchMethodException e) { 402  continue; 403  } 404  } 405  return false; 406  } 407  408  private static boolean isEqualsDefined(Class<?> cls) { 409  try { 410  return !cls.getDeclaredMethod("equals", Object.class).isSynthetic(); 411  } catch (NoSuchMethodException e) { 412  return false; 413  } 414  } 415  416  abstract static class Chopper { 417  418  final Chopper or(final Chopper you) { 419  final Chopper i = this; 420  return new Chopper() { 421  @Override 422  Optional<String> chop(String str) { 423  return i.chop(str).or(you.chop(str)); 424  } 425  }; 426  } 427  428  abstract Optional<String> chop(String str); 429  430  static Chopper suffix(final String suffix) { 431  return new Chopper() { 432  @Override 433  Optional<String> chop(String str) { 434  if (str.endsWith(suffix)) { 435  return Optional.of(str.substring(0, str.length() - suffix.length())); 436  } else { 437  return Optional.absent(); 438  } 439  } 440  }; 441  } 442  } 443 }