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