View Javadoc

1   /*
2    * Copyright 2008-2012 smartics, Kronseder & Reiner GmbH
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  package de.smartics.util.test.theories;
17  
18  import static org.hamcrest.CoreMatchers.equalTo;
19  import static org.hamcrest.CoreMatchers.is;
20  import static org.hamcrest.CoreMatchers.not;
21  import static org.junit.Assert.assertThat;
22  import static org.junit.Assume.assumeThat;
23  
24  import org.junit.experimental.theories.Theories;
25  import org.junit.experimental.theories.Theory;
26  import org.junit.runner.RunWith;
27  
28  /* CHECKSTYLE:OFF */
29  /**
30   * Tests common object theories. This includes
31   * <ol>
32   * <li>{@link Object#equals(Object)}</li>
33   * <li>{@link Object#hashCode()}</li>
34   * <li>{@link Object#toString()}</li>
35   * </ol>
36   *
37   * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a>
38   * @version $Revision:591 $
39   */
40  @RunWith(Theories.class)
41  public abstract class ObjectTheories // NOPMD
42  /* CHECKSTYLE:ON */
43  { // NOPMD
44    // ********************************* Fields *********************************
45  
46    // --- constants ------------------------------------------------------------
47  
48    /**
49     * The default value for the number of iterations for the
50     * {@link #equalsIsConsistent(Object, Object) consistency check}. May be
51     * specified by subclasses by overriding
52     * {@link #getConsistencyIterationCount()}.
53     * <p>
54     * The value of this constant is {@value}.
55     * </p>
56     */
57    public static final int DEFAULT_COUNT_CONSISTENCY_CHECK = 7;
58  
59    /**
60     * The default value to be returned by
61     * {@link #checkForDifferentTypesInEquals()}.
62     * <p>
63     * The value of this constant is {@value}.
64     * </p>
65     */
66    public static final boolean DEFAULT_RUN_EQUALS_THEORY_ON_DIFFERENT_TYPES =
67        true;
68  
69    /**
70     * The default value to be returned by {@link #checkForUnequalHashCodes()}.
71     * <p>
72     * The value of this constant is {@value}.
73     * </p>
74     */
75    public static final boolean DEFAULT_RUN_HASH_CODE_THEORY_ON_UNEQUAL_INSTANCES =
76        false;
77  
78    // --- members --------------------------------------------------------------
79  
80    // ****************************** Inner Classes *****************************
81  
82    /**
83     * Type to instantiate for tests.
84     */
85    private static final class OtherType
86    {
87    }
88  
89    // ********************************* Methods ********************************
90  
91    // --- prepare --------------------------------------------------------------
92  
93    // --- helper ---------------------------------------------------------------
94  
95    private static void assumeThatValueIsNotNull(final Object uut)
96    {
97      assumeThat(uut, is(not(equalTo(null))));
98    }
99  
100   // CHECKSTYLE:OFF
101   /**
102    * Determines the default number of iterations to go through for the
103    * consistency checks.
104    *
105    * @return {@value #DEFAULT_COUNT_CONSISTENCY_CHECK} per default.
106    * @see #getEqualsConsistencyIterationCount()
107    * @see #getHashCodeConsistencyIterationCount()
108    */
109   protected int getConsistencyIterationCount()
110   {
111     return DEFAULT_COUNT_CONSISTENCY_CHECK;
112   }
113 
114   /**
115    * Determines the number of iterations to go through for the
116    * {@link #equalsIsConsistent(Object, Object) equals consistency check}.
117    *
118    * @return result of {@link #getConsistencyIterationCount()} per default.
119    */
120   protected int getEqualsConsistencyIterationCount()
121   {
122     return getConsistencyIterationCount();
123   }
124 
125   /**
126    * Determines the number of iterations to go through for the
127    * {@link #hashCodeIsConsistent(Object) hash code consistency check}.
128    *
129    * @return result of {@link #getConsistencyIterationCount()} per default.
130    */
131   protected int getHashCodeConsistencyIterationCount()
132   {
133     return getConsistencyIterationCount();
134   }
135 
136   /**
137    * Determines if a theory to check that equals fails on different types should
138    * be checked.
139    *
140    * @return <code>true</code> if the theory should be executed,
141    *         <code>false</code> otherwise. Per default returns
142    *         {@value #DEFAULT_RUN_EQUALS_THEORY_ON_DIFFERENT_TYPES}
143    */
144   protected boolean checkForDifferentTypesInEquals()
145   {
146     return DEFAULT_RUN_EQUALS_THEORY_ON_DIFFERENT_TYPES;
147   }
148 
149   /**
150    * Determines if a theory to check for unequal hash code values for unequal
151    * instances should be checked.
152    *
153    * @return <code>true</code> if the theory should be executed,
154    *         <code>false</code> otherwise. Per default returns
155    *         {@value #DEFAULT_RUN_HASH_CODE_THEORY_ON_UNEQUAL_INSTANCES}
156    */
157   protected boolean checkForUnequalHashCodes()
158   {
159     return DEFAULT_RUN_HASH_CODE_THEORY_ON_UNEQUAL_INSTANCES;
160   }
161 
162   // CHECKSTYLE:ON
163 
164   // --- tests ----------------------------------------------------------------
165 
166   // ... equals ...............................................................
167 
168   /**
169    * Checks the reflexive property of the {@link Object#equals(Object)} method.
170    * <p>
171    * <blockquote>
172    * <p>
173    * It is <i>reflexive</i>: for any non-null reference value <code>x</code>,
174    * <code>x.equals(x)</code> should return <code>true</code>.
175    * </p>
176    * </blockquote>
177    *
178    * @param uut the unit under test.
179    * @see Object#equals(Object)
180    */
181   @Theory
182   public final void equalsIsReflexive(final Object uut)
183   {
184     assumeThatValueIsNotNull(uut);
185     assertThat(uut.equals(uut), is(true));
186   }
187 
188   /**
189    * Checks the symmetric property of the {@link Object#equals(Object)} method.
190    * <p>
191    * <blockquote>
192    * <p>
193    * It is <i>symmetric</i>: for any non-null reference values <code>x</code>
194    * and <code>y</code>, <code>x.equals(y)</code> should return
195    * <code>true</code> if and only if <code>y.equals(x)</code> returns
196    * <code>true</code>.
197    * </p>
198    * </blockquote>
199    *
200    * @param uutX the unit under test to test for symmetry.
201    * @param uutY the unit under test to test for symmetry.
202    * @see Object#equals(Object)
203    */
204   @Theory
205   public final void equalsIsSymmetric(final Object uutX, final Object uutY)
206   {
207     assumeThatValueIsNotNull(uutX);
208     assumeThatValueIsNotNull(uutY);
209 
210     assumeThat(uutY.equals(uutX), is(true));
211     assertThat(uutX.equals(uutY), is(true));
212   }
213 
214   /**
215    * Checks the transitive property of the {@link Object#equals(Object)} method.
216    * <p>
217    * <blockquote>
218    * <p>
219    * It is <i>transitive</i>: for any non-null reference values <code>x</code>,
220    * <code>y</code>, and <code>z</code>, if <code>x.equals(y)</code> returns
221    * <code>true</code> and <code>y.equals(z)</code> returns <code>true</code>,
222    * then <code>x.equals(z)</code> should return <code>true</code>.
223    * </p>
224    * </blockquote>
225    *
226    * @param uutX the unit under test to test for transitivity.
227    * @param uutY the unit under test to test for transitivity.
228    * @param uutZ the unit under test to test for transitivity.
229    * @see Object#equals(Object)
230    */
231   @Theory
232   public final void equalsIsTransitive(final Object uutX, final Object uutY,
233       final Object uutZ)
234   {
235     assumeThatValueIsNotNull(uutX);
236     assumeThatValueIsNotNull(uutY);
237     assumeThatValueIsNotNull(uutZ);
238     assumeThat(uutX.equals(uutY) && uutY.equals(uutZ), is(true));
239 
240     assertThat(uutZ.equals(uutX), is(true));
241   }
242 
243   /**
244    * Checks the consistent property of the {@link Object#equals(Object)} method.
245    * <p>
246    * <blockquote>
247    * <p>
248    * It is <i>consistent</i>: for any non-null reference values <code>x</code>
249    * and <code>y</code>, multiple invocations of <tt>x.equals(y)</tt>
250    * consistently return <code>true</code> or consistently return
251    * <code>false</code>, provided no information used in <code>equals</code>
252    * comparisons on the objects is modified.
253    * </p>
254    * </blockquote>
255    *
256    * @param uutX the unit under test to test for consistency.
257    * @param uutY the unit under test to test for consistency.
258    * @see Object#equals(Object)
259    */
260   @Theory
261   public final void equalsIsConsistent(final Object uutX, final Object uutY)
262   {
263     assumeThatValueIsNotNull(uutX);
264 
265     final boolean result = uutX.equals(uutY);
266     for (int i = getEqualsConsistencyIterationCount(); i > 0; i--)
267     {
268       assertThat(uutX.equals(uutY), is(result));
269     }
270   }
271 
272   /**
273    * Checks the not-null property of the {@link Object#equals(Object)} method.
274    * <p>
275    * <blockquote>
276    * <p>
277    * For any non-null reference value <code>x</code>,
278    * <code>x.equals(null)</code> should return <code>false</code>.
279    * </p>
280    * </blockquote>
281    *
282    * @param uut the unit under test.
283    * @see Object#equals(Object)
284    */
285   @Theory
286   public final void equalsReturnFalseOnNull(final Object uut)
287   {
288     assumeThatValueIsNotNull(uut);
289 
290     assertThat(uut.equals(null), is(false)); // NOPMD
291   }
292 
293   /**
294    * Checks the consistent property of the {@link Object#equals(Object)} method
295    * regarding comparison with elements of other type.
296    *
297    * @param uut the unit under test.
298    * @see Object#equals(Object)
299    */
300   @Theory
301   public final void equalsReturnFalseOnInstanceOfOtherType(final Object uut)
302   {
303     if (checkForDifferentTypesInEquals())
304     {
305       assumeThatValueIsNotNull(uut);
306 
307       final Object instanceOfOtherType = new OtherType();
308       assertThat(uut.equals(instanceOfOtherType), is(false)); // NOPMD
309     }
310   }
311 
312   // ... hashCode .............................................................
313 
314   /**
315    * Checks the consistency property of the {@link Object#hashCode()} method.
316    * <p>
317    * <blockquote>
318    * <p>
319    * Whenever it is invoked on the same object more than once during an
320    * execution of a Java application, the <tt>hashCode</tt> method must
321    * consistently return the same integer, provided no information used in
322    * <tt>equals</tt> comparisons on the object is modified. This integer need
323    * not remain consistent from one execution of an application to another
324    * execution of the same application.
325    * </p>
326    * </blockquote>
327    *
328    * @param uut the unit under test.
329    * @see Object#hashCode()
330    */
331   @Theory
332   public final void hashCodeIsConsistent(final Object uut)
333   {
334     assumeThatValueIsNotNull(uut);
335 
336     final int hashCode = uut.hashCode();
337     for (int i = getHashCodeConsistencyIterationCount(); i > 0; i--)
338     {
339       assertThat(uut.hashCode(), is(hashCode));
340     }
341   }
342 
343   /**
344    * Checks the consistency-with-equals property of the
345    * {@link Object#hashCode()} method.
346    * <p>
347    * <blockquote>
348    * <p>
349    * If two objects are equal according to the <tt>equals(Object)</tt> method,
350    * then calling the <code>hashCode</code> method on each of the two objects
351    * must produce the same integer result.
352    * </p>
353    * </blockquote>
354    *
355    * @param uutX the unit under test to test for consistency-with-equals.
356    * @param uutY the unit under test to test for consistency-with-equals.
357    * @see Object#hashCode()
358    */
359   @Theory
360   public final void hashCodeIsConsistentWithEquals(final Object uutX,
361       final Object uutY)
362   {
363     assumeThatValueIsNotNull(uutX);
364     assumeThat(uutX.equals(uutY), is(true)); // implicitly uutY is not 'null'.
365 
366     assertThat(uutX.hashCode(), is(equalTo(uutY.hashCode())));
367   }
368 
369   /**
370    * Checks the guideline for optimum performance of the
371    * {@link Object#hashCode()} method.
372    * <p>
373    * <blockquote>
374    * <p>
375    * It is <em>not</em> required that if two objects are unequal according to
376    * the {@link java.lang.Object#equals(java.lang.Object)} method, then calling
377    * the <tt>hashCode</tt> method on each of the two objects must produce
378    * distinct integer results. However, the programmer should be aware that
379    * producing distinct integer results for unequal objects may improve the
380    * performance of hashtables.
381    * </p>
382    * </blockquote>
383    *
384    * @param uutX the unit under test.
385    * @param uutY the unit under test.
386    * @see Object#hashCode()
387    */
388   @Theory
389   public final void hashCodeProducesUnequalHashCodesForUnequalInstances(
390       final Object uutX, final Object uutY)
391   {
392     if (checkForUnequalHashCodes())
393     {
394       assumeThatValueIsNotNull(uutX);
395       assumeThatValueIsNotNull(uutY);
396       assumeThat(uutX.equals(uutY), is(false));
397 
398       assertThat(uutX.hashCode(), is(not(equalTo(uutY.hashCode()))));
399     }
400   }
401 
402   // ... toString .............................................................
403 
404   /**
405    * This theory simply checks that calling the {@link Object#toString()} method
406    * of the unit under test (UUT) does not fail with raising an exception.
407    * <p>
408    * The theory is only executed on UUTs that are not <code>null</code>.
409    * </p>
410    *
411    * @param uut the unit under test.
412    */
413   @Theory
414   public final void toStringRunsWithoutFailure(final Object uut)
415   {
416     assumeThatValueIsNotNull(uut);
417     final String string = uut.toString();
418     assertThat(string, is(not(equalTo(null))));
419   }
420 }