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