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