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 }