1 /* 2 * Copyright (C) 2007 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 com.google.common.annotations.GwtCompatible; 20 import com.google.common.base.Preconditions; 21 import com.google.common.collect.ImmutableList; 22 import com.google.common.collect.Sets; 23 24 import junit.framework.AssertionFailedError; 25 import junit.framework.TestCase; 26 27 import java.util.Set; 28 29 /** 30 * Unit tests for {@link EqualsTester}. 31 * 32 * @author Jim McMaster 33 */ 34 @GwtCompatible 35 public class EqualsTesterTest extends TestCase { 36 private ValidTestObject reference; 37 private EqualsTester equalsTester; 38 private ValidTestObject equalObject1; 39 private ValidTestObject equalObject2; 40 private ValidTestObject notEqualObject1; 41 42 @Override setUp()43 public void setUp() throws Exception { 44 super.setUp(); 45 reference = new ValidTestObject(1, 2); 46 equalsTester = new EqualsTester(); 47 equalObject1 = new ValidTestObject(1, 2); 48 equalObject2 = new ValidTestObject(1, 2); 49 notEqualObject1 = new ValidTestObject(0, 2); 50 } 51 52 /** 53 * Test null reference yields error 54 */ testAddNullReference()55 public void testAddNullReference() { 56 try { 57 equalsTester.addEqualityGroup((Object) null); 58 fail("Should fail on null reference"); 59 } catch (NullPointerException e) {} 60 } 61 62 /** 63 * Test equalObjects after adding multiple instances at once with a null 64 */ testAddTwoEqualObjectsAtOnceWithNull()65 public void testAddTwoEqualObjectsAtOnceWithNull() { 66 try { 67 equalsTester.addEqualityGroup(reference, equalObject1, null); 68 fail("Should fail on null equal object"); 69 } catch (NullPointerException e) {} 70 } 71 72 /** 73 * Test adding null equal object yields error 74 */ testAddNullEqualObject()75 public void testAddNullEqualObject() { 76 try { 77 equalsTester.addEqualityGroup(reference, (Object[]) null); 78 fail("Should fail on null equal object"); 79 } catch (NullPointerException e) {} 80 } 81 82 /** 83 * Test adding objects only by addEqualityGroup, with no reference object 84 * specified in the constructor. 85 */ testAddEqualObjectWithOArgConstructor()86 public void testAddEqualObjectWithOArgConstructor() { 87 equalsTester.addEqualityGroup(equalObject1, notEqualObject1); 88 try { 89 equalsTester.testEquals(); 90 } catch (AssertionFailedError e) { 91 assertErrorMessage( 92 e, 93 equalObject1 + " [group 1, item 1] must be Object#equals to " 94 + notEqualObject1 + " [group 1, item 2]"); 95 return; 96 } 97 fail("Should get not equal to equal object error"); 98 } 99 100 /** 101 * Test EqualsTester with no equals or not equals objects. This checks 102 * proper handling of null, incompatible class and reflexive tests 103 */ testTestEqualsEmptyLists()104 public void testTestEqualsEmptyLists() { 105 equalsTester.addEqualityGroup(reference); 106 equalsTester.testEquals(); 107 } 108 109 /** 110 * Test EqualsTester after populating equalObjects. This checks proper 111 * handling of equality and verifies hashCode for valid objects 112 */ testTestEqualsEqualsObjects()113 public void testTestEqualsEqualsObjects() { 114 equalsTester.addEqualityGroup(reference, equalObject1, equalObject2); 115 equalsTester.testEquals(); 116 } 117 118 /** 119 * Test proper handling of case where an object is not equal to itself 120 */ testNonreflexiveEquals()121 public void testNonreflexiveEquals() { 122 Object obj = new NonReflexiveObject(); 123 equalsTester.addEqualityGroup(obj); 124 try { 125 equalsTester.testEquals(); 126 } catch (AssertionFailedError e) { 127 assertErrorMessage( 128 e, obj + " must be Object#equals to itself"); 129 return; 130 } 131 fail("Should get non-reflexive error"); 132 } 133 134 /** 135 * Test proper handling where an object tests equal to null 136 */ testInvalidEqualsNull()137 public void testInvalidEqualsNull() { 138 Object obj = new InvalidEqualsNullObject(); 139 equalsTester.addEqualityGroup(obj); 140 try { 141 equalsTester.testEquals(); 142 } catch (AssertionFailedError e) { 143 assertErrorMessage( 144 e, obj + " must not be Object#equals to null"); 145 return; 146 } 147 fail("Should get equal to null error"); 148 } 149 150 /** 151 * Test proper handling where an object incorrectly tests for an 152 * incompatible class 153 */ testInvalidEqualsIncompatibleClass()154 public void testInvalidEqualsIncompatibleClass() { 155 Object obj = new InvalidEqualsIncompatibleClassObject(); 156 equalsTester.addEqualityGroup(obj); 157 try { 158 equalsTester.testEquals(); 159 } catch (AssertionFailedError e) { 160 assertErrorMessage( 161 e, 162 obj 163 + " must not be Object#equals to an arbitrary object of another class"); 164 return; 165 } 166 fail("Should get equal to incompatible class error"); 167 } 168 169 /** 170 * Test proper handling where an object is not equal to one the user has 171 * said should be equal 172 */ testInvalidNotEqualsEqualObject()173 public void testInvalidNotEqualsEqualObject() { 174 equalsTester.addEqualityGroup(reference, notEqualObject1); 175 try { 176 equalsTester.testEquals(); 177 } catch (AssertionFailedError e) { 178 assertErrorMessage(e, reference + " [group 1, item 1]"); 179 assertErrorMessage(e, notEqualObject1 + " [group 1, item 2]"); 180 return; 181 } 182 fail("Should get not equal to equal object error"); 183 } 184 185 /** 186 * Test for an invalid hashCode method, i.e., one that returns different 187 * value for objects that are equal according to the equals method 188 */ testInvalidHashCode()189 public void testInvalidHashCode() { 190 Object a = new InvalidHashCodeObject(1, 2); 191 Object b = new InvalidHashCodeObject(1, 2); 192 equalsTester.addEqualityGroup(a, b); 193 try { 194 equalsTester.testEquals(); 195 } catch (AssertionFailedError e) { 196 assertErrorMessage( 197 e, "the Object#hashCode (" + a.hashCode() + ") of " + a 198 + " [group 1, item 1] must be equal to the Object#hashCode (" 199 + b.hashCode() + ") of " + b); 200 return; 201 } 202 fail("Should get invalid hashCode error"); 203 } 204 testNullEqualityGroup()205 public void testNullEqualityGroup() { 206 EqualsTester tester = new EqualsTester(); 207 try { 208 tester.addEqualityGroup((Object[]) null); 209 fail(); 210 } catch (NullPointerException e) {} 211 } 212 testNullObjectInEqualityGroup()213 public void testNullObjectInEqualityGroup() { 214 EqualsTester tester = new EqualsTester(); 215 try { 216 tester.addEqualityGroup(1, null, 3); 217 fail(); 218 } catch (NullPointerException e) { 219 assertErrorMessage(e, "at index 1"); 220 } 221 } 222 testSymmetryBroken()223 public void testSymmetryBroken() { 224 EqualsTester tester = new EqualsTester() 225 .addEqualityGroup(named("foo").addPeers("bar"), named("bar")); 226 try { 227 tester.testEquals(); 228 } catch (AssertionFailedError e) { 229 assertErrorMessage( 230 e, 231 "bar [group 1, item 2] must be Object#equals to foo [group 1, item 1]"); 232 return; 233 } 234 fail("should failed because symmetry is broken"); 235 } 236 testTransitivityBrokenInEqualityGroup()237 public void testTransitivityBrokenInEqualityGroup() { 238 EqualsTester tester = new EqualsTester() 239 .addEqualityGroup( 240 named("foo").addPeers("bar", "baz"), 241 named("bar").addPeers("foo"), 242 named("baz").addPeers("foo")); 243 try { 244 tester.testEquals(); 245 } catch (AssertionFailedError e) { 246 assertErrorMessage( 247 e, 248 "bar [group 1, item 2] must be Object#equals to baz [group 1, item 3]"); 249 return; 250 } 251 fail("should failed because transitivity is broken"); 252 } 253 testUnequalObjectsInEqualityGroup()254 public void testUnequalObjectsInEqualityGroup() { 255 EqualsTester tester = new EqualsTester() 256 .addEqualityGroup(named("foo"), named("bar")); 257 try { 258 tester.testEquals(); 259 } catch (AssertionFailedError e) { 260 assertErrorMessage( 261 e, 262 "foo [group 1, item 1] must be Object#equals to bar [group 1, item 2]"); 263 return; 264 } 265 fail("should failed because of unequal objects in the same equality group"); 266 } 267 testTransitivityBrokenAcrossEqualityGroups()268 public void testTransitivityBrokenAcrossEqualityGroups() { 269 EqualsTester tester = new EqualsTester() 270 .addEqualityGroup( 271 named("foo").addPeers("bar"), 272 named("bar").addPeers("foo", "x")) 273 .addEqualityGroup( 274 named("baz").addPeers("x"), 275 named("x").addPeers("baz", "bar")); 276 try { 277 tester.testEquals(); 278 } catch (AssertionFailedError e) { 279 assertErrorMessage( 280 e, 281 "bar [group 1, item 2] must not be Object#equals to x [group 2, item 2]"); 282 return; 283 } 284 fail("should failed because transitivity is broken"); 285 } 286 testEqualityGroups()287 public void testEqualityGroups() { 288 new EqualsTester() 289 .addEqualityGroup( 290 named("foo").addPeers("bar"), named("bar").addPeers("foo")) 291 .addEqualityGroup(named("baz"), named("baz")) 292 .testEquals(); 293 } 294 assertErrorMessage(Throwable e, String message)295 private static void assertErrorMessage(Throwable e, String message) { 296 // TODO(kevinb): use a Truth assertion here 297 if (!e.getMessage().contains(message)) { 298 fail("expected <" + e.getMessage() + "> to contain <" + message + ">"); 299 } 300 } 301 302 /** 303 * Test class with valid equals and hashCode methods. Testers created 304 * with instances of this class should always pass. 305 */ 306 private static class ValidTestObject { 307 private int aspect1; 308 private int aspect2; 309 ValidTestObject(int aspect1, int aspect2)310 ValidTestObject(int aspect1, int aspect2) { 311 this.aspect1 = aspect1; 312 this.aspect2 = aspect2; 313 } 314 equals(Object o)315 @Override public boolean equals(Object o) { 316 if (!(o instanceof ValidTestObject)) { 317 return false; 318 } 319 ValidTestObject other = (ValidTestObject) o; 320 if (aspect1 != other.aspect1) { 321 return false; 322 } 323 if (aspect2 != other.aspect2) { 324 return false; 325 } 326 return true; 327 } 328 hashCode()329 @Override public int hashCode() { 330 int result = 17; 331 result = 37 * result + aspect1; 332 result = 37 * result + aspect2; 333 return result; 334 } 335 } 336 337 /** Test class with invalid hashCode method. */ 338 private static class InvalidHashCodeObject { 339 private int aspect1; 340 private int aspect2; 341 InvalidHashCodeObject(int aspect1, int aspect2)342 InvalidHashCodeObject(int aspect1, int aspect2) { 343 this.aspect1 = aspect1; 344 this.aspect2 = aspect2; 345 } 346 equals(Object o)347 @Override public boolean equals(Object o) { 348 if (!(o instanceof InvalidHashCodeObject)) { 349 return false; 350 } 351 InvalidHashCodeObject other = (InvalidHashCodeObject) o; 352 if (aspect1 != other.aspect1) { 353 return false; 354 } 355 if (aspect2 != other.aspect2) { 356 return false; 357 } 358 return true; 359 } 360 } 361 362 /** Test class that violates reflexitivity. It is not equal to itself */ 363 private static class NonReflexiveObject { 364 equals(Object o)365 @Override public boolean equals(Object o) { 366 return false; 367 } 368 hashCode()369 @Override public int hashCode() { 370 return super.hashCode(); 371 } 372 } 373 374 /** Test class that returns true if the test object is null */ 375 private static class InvalidEqualsNullObject { 376 equals(Object o)377 @Override public boolean equals(Object o) { 378 return o == this || o == null; 379 } 380 hashCode()381 @Override public int hashCode() { 382 return 0; 383 } 384 } 385 386 /** 387 * Test class that returns true even if the test object is of the wrong class 388 */ 389 private static class InvalidEqualsIncompatibleClassObject { 390 equals(Object o)391 @Override public boolean equals(Object o) { 392 if (o == null) { 393 return false; 394 } 395 return true; 396 } 397 hashCode()398 @Override public int hashCode() { 399 return 0; 400 } 401 } 402 named(String name)403 private static NamedObject named(String name) { 404 return new NamedObject(name); 405 } 406 407 private static class NamedObject { 408 private final Set<String> peerNames = Sets.newHashSet(); 409 410 private final String name; 411 NamedObject(String name)412 NamedObject(String name) { 413 this.name = Preconditions.checkNotNull(name); 414 } 415 addPeers(String... names)416 NamedObject addPeers(String... names) { 417 peerNames.addAll(ImmutableList.copyOf(names)); 418 return this; 419 } 420 equals(Object obj)421 @Override public boolean equals(Object obj) { 422 if (obj instanceof NamedObject) { 423 NamedObject that = (NamedObject) obj; 424 return name.equals(that.name) || peerNames.contains(that.name); 425 } 426 return false; 427 } 428 hashCode()429 @Override public int hashCode() { 430 return 0; 431 } 432 toString()433 @Override public String toString() { 434 return name; 435 } 436 } 437 } 438