View Javadoc
1   package net.trajano.commons.testing;
2   
3   import java.util.concurrent.Callable;
4   
5   import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
6   
7   /**
8    * This provides utility methods that ensure the {@link #equals(Object)} and
9    * {@link #hashCode()} are implemented correctly.
10   *
11   * @author Archimedes Trajano
12   */
13  public final class EqualsTestUtil {
14  
15      /**
16       * Builds two objects the same way and checks if they are equal.
17       *
18       * @param <T>
19       *            type
20       * @param objectBuilder
21       *            object builder
22       */
23      public static <T> void assertEqualsImplementedCorrectly(final Callable<T> objectBuilder) {
24  
25          assertEqualsImplementedCorrectly(objectBuilder, objectBuilder);
26      }
27  
28      /**
29       * Builds two objects and ensures that they are equal.
30       *
31       * @param <T>
32       *            type
33       * @param objectBuilder1
34       *            first object builder
35       * @param objectBuilder2
36       *            second object builder
37       */
38      public static <T> void assertEqualsImplementedCorrectly(final Callable<T> objectBuilder1,
39              final Callable<T> objectBuilder2) {
40  
41          try {
42              final T o1 = objectBuilder1.call();
43              final T o2 = objectBuilder2.call();
44              assertEqualsImplementedCorrectly(o1, o2);
45          } catch (final Exception e) {
46              throw new AssertionError(e);
47          }
48      }
49  
50      /**
51       * Take two objects and ensure their are implemented correctly. Warnings are
52       * suppressed as this block of code will do things that normal developers
53       * are not supposed to do, but are needed to ensure that
54       * {@link #equals(Object)} is implemented correctly.
55       * <p>
56       * The generic check is not put in to allow testing with different classes
57       * </p>
58       *
59       * @param o1
60       *            first object
61       * @param o2
62       *            second object
63       */
64      @SuppressWarnings("all")
65      @SuppressFBWarnings()
66      public static void assertEqualsImplementedCorrectly(final Object o1,
67              final Object o2) {
68  
69          // symmetric
70          if (!o1.equals(o2)) {
71              throw new AssertionError("Expected " + o1 + " ==  " + o2);
72          }
73          if (!o2.equals(o1)) {
74              throw new AssertionError("Expected " + o2 + " ==  " + o1);
75          }
76  
77          // this == object tests
78          if (!o1.equals(o1)) {
79              throw new AssertionError("Expected " + o1 + " ==  " + o1);
80          }
81          if (!o2.equals(o2)) {
82              throw new AssertionError("Expected " + o2 + " ==  " + o2);
83          }
84  
85          // Different class checks
86          if (o1.equals(new EqualsTestUtil())) {
87              throw new AssertionError("Type of " + o1 + " does not match expected class");
88          }
89          if (o2.equals(new EqualsTestUtil())) {
90              throw new AssertionError("Type of " + o2 + " does not match expected class");
91          }
92  
93          // Null tests done poorly but will at least trigger the right paths.
94  
95          // CHECKSTYLE:OFF
96          if (o1.equals(null)) {
97              throw new AssertionError("Did not expect " + o1 + " == null");
98          }
99          if (o2.equals(null)) {
100             throw new AssertionError("Did not expect " + o2 + " == null");
101         }
102         // CHECKSTYLE:ON
103 
104         // hash code validity
105         if (o1.hashCode() != o2.hashCode()) {
106             throw new AssertionError(String.format("Expected hash code of %s (%d) == %s (%d)", o1, o1.hashCode(), o2, o2.hashCode()));
107         }
108     }
109 
110     /**
111      * Take a single object and ensure its equality is implemented correctly.
112      *
113      * @param <T>
114      *            type
115      * @param o
116      *            object
117      */
118     public static <T> void assertEqualsImplementedCorrectly(final T o) {
119 
120         assertEqualsImplementedCorrectly(o, o);
121     }
122 
123     /**
124      * Prevent instantiation of utility class.
125      */
126     private EqualsTestUtil() {
127 
128     }
129 }