| /* |
| * Copyright (C) 2007 The Guava Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.common.testing; |
| |
| import com.google.common.annotations.GwtCompatible; |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Sets; |
| |
| import junit.framework.AssertionFailedError; |
| import junit.framework.TestCase; |
| |
| import java.util.Set; |
| |
| /** |
| * Unit tests for {@link EqualsTester}. |
| * |
| * @author Jim McMaster |
| */ |
| @GwtCompatible |
| public class EqualsTesterTest extends TestCase { |
| private ValidTestObject reference; |
| private EqualsTester equalsTester; |
| private ValidTestObject equalObject1; |
| private ValidTestObject equalObject2; |
| private ValidTestObject notEqualObject1; |
| private ValidTestObject notEqualObject2; |
| |
| @Override |
| public void setUp() throws Exception { |
| super.setUp(); |
| reference = new ValidTestObject(1, 2); |
| equalsTester = new EqualsTester(); |
| equalObject1 = new ValidTestObject(1, 2); |
| equalObject2 = new ValidTestObject(1, 2); |
| notEqualObject1 = new ValidTestObject(0, 2); |
| notEqualObject2 = new ValidTestObject(1, 0); |
| } |
| |
| /** |
| * Test null reference yields error |
| */ |
| public void testAddNullReference() { |
| try { |
| equalsTester.addEqualityGroup((Object) null); |
| fail("Should fail on null reference"); |
| } catch (NullPointerException e) {} |
| } |
| |
| /** |
| * Test equalObjects after adding multiple instances at once with a null |
| */ |
| public void testAddTwoEqualObjectsAtOnceWithNull() { |
| try { |
| equalsTester.addEqualityGroup(reference, equalObject1, null); |
| fail("Should fail on null equal object"); |
| } catch (NullPointerException e) {} |
| } |
| |
| /** |
| * Test adding null equal object yields error |
| */ |
| public void testAddNullEqualObject() { |
| try { |
| equalsTester.addEqualityGroup(reference, (Object[]) null); |
| fail("Should fail on null equal object"); |
| } catch (NullPointerException e) {} |
| } |
| |
| /** |
| * Test adding objects only by addEqualityGroup, with no reference object |
| * specified in the constructor. |
| */ |
| public void testAddEqualObjectWithOArgConstructor() { |
| equalsTester.addEqualityGroup(equalObject1, notEqualObject1); |
| try { |
| equalsTester.testEquals(); |
| } catch (AssertionFailedError e) { |
| assertErrorMessage( |
| e, |
| equalObject1 + " [group 1, item 1] must be equal to " |
| + notEqualObject1 + " [group 1, item 2]"); |
| return; |
| } |
| fail("Should get not equal to equal object error"); |
| } |
| |
| /** |
| * Test EqualsTester with no equals or not equals objects. This checks |
| * proper handling of null, incompatible class and reflexive tests |
| */ |
| public void testTestEqualsEmptyLists() { |
| equalsTester.addEqualityGroup(reference); |
| equalsTester.testEquals(); |
| } |
| |
| /** |
| * Test EqualsTester after populating equalObjects. This checks proper |
| * handling of equality and verifies hashCode for valid objects |
| */ |
| public void testTestEqualsEqualsObjects() { |
| equalsTester.addEqualityGroup(reference, equalObject1, equalObject2); |
| equalsTester.testEquals(); |
| } |
| |
| /** |
| * Test proper handling of case where an object is not equal to itself |
| */ |
| public void testNonreflexiveEquals() { |
| Object obj = new NonReflexiveObject(); |
| equalsTester.addEqualityGroup(obj); |
| try { |
| equalsTester.testEquals(); |
| } catch (AssertionFailedError e) { |
| assertErrorMessage( |
| e, obj + " must be equal to itself"); |
| return; |
| } |
| fail("Should get non-reflexive error"); |
| } |
| |
| /** |
| * Test proper handling where an object tests equal to null |
| */ |
| public void testInvalidEqualsNull() { |
| Object obj = new InvalidEqualsNullObject(); |
| equalsTester.addEqualityGroup(obj); |
| try { |
| equalsTester.testEquals(); |
| } catch (AssertionFailedError e) { |
| assertErrorMessage( |
| e, obj + " must be unequal to null"); |
| return; |
| } |
| fail("Should get equal to null error"); |
| } |
| |
| /** |
| * Test proper handling where an object incorrectly tests for an |
| * incompatible class |
| */ |
| public void testInvalidEqualsIncompatibleClass() { |
| Object obj = new InvalidEqualsIncompatibleClassObject(); |
| equalsTester.addEqualityGroup(obj); |
| try { |
| equalsTester.testEquals(); |
| } catch (AssertionFailedError e) { |
| assertErrorMessage( |
| e, |
| obj |
| + " must be unequal to an arbitrary object of another class"); |
| return; |
| } |
| fail("Should get equal to incompatible class error"); |
| } |
| |
| /** |
| * Test proper handling where an object is not equal to one the user has |
| * said should be equal |
| */ |
| public void testInvalidNotEqualsEqualObject() { |
| equalsTester.addEqualityGroup(reference, notEqualObject1); |
| try { |
| equalsTester.testEquals(); |
| } catch (AssertionFailedError e) { |
| assertErrorMessage(e, reference.toString() + " [group 1, item 1]"); |
| assertErrorMessage(e, notEqualObject1.toString() + " [group 1, item 2]"); |
| return; |
| } |
| fail("Should get not equal to equal object error"); |
| } |
| |
| /** |
| * Test for an invalid hashCode method, i.e., one that returns different |
| * value for objects that are equal according to the equals method |
| */ |
| public void testInvalidHashCode() { |
| Object a = new InvalidHashCodeObject(1, 2); |
| Object b = new InvalidHashCodeObject(1, 2); |
| equalsTester.addEqualityGroup(a, b); |
| try { |
| equalsTester.testEquals(); |
| } catch (AssertionFailedError e) { |
| assertErrorMessage( |
| e, "the hash (" + a.hashCode() + ") of " + a |
| + " [group 1, item 1] must be equal to the hash (" + b.hashCode() + ") of " + b); |
| return; |
| } |
| fail("Should get invalid hashCode error"); |
| } |
| |
| public void testNullEqualityGroup() { |
| EqualsTester tester = new EqualsTester(); |
| try { |
| tester.addEqualityGroup((Object[]) null); |
| fail(); |
| } catch (NullPointerException e) {} |
| } |
| |
| public void testNullObjectInEqualityGroup() { |
| EqualsTester tester = new EqualsTester(); |
| try { |
| tester.addEqualityGroup(1, null, 3); |
| fail(); |
| } catch (NullPointerException e) { |
| assertErrorMessage(e, "at index 1"); |
| } |
| } |
| |
| public void testSymmetryBroken() { |
| EqualsTester tester = new EqualsTester() |
| .addEqualityGroup(named("foo").addPeers("bar"), named("bar")); |
| try { |
| tester.testEquals(); |
| } catch (AssertionFailedError e) { |
| assertErrorMessage( |
| e, |
| "bar [group 1, item 2] must be equal to foo [group 1, item 1]"); |
| return; |
| } |
| fail("should failed because symmetry is broken"); |
| } |
| |
| public void testTransitivityBrokenInEqualityGroup() { |
| EqualsTester tester = new EqualsTester() |
| .addEqualityGroup( |
| named("foo").addPeers("bar", "baz"), |
| named("bar").addPeers("foo"), |
| named("baz").addPeers("foo")); |
| try { |
| tester.testEquals(); |
| } catch (AssertionFailedError e) { |
| assertErrorMessage( |
| e, |
| "bar [group 1, item 2] must be equal to baz [group 1, item 3]"); |
| return; |
| } |
| fail("should failed because transitivity is broken"); |
| } |
| |
| public void testUnequalObjectsInEqualityGroup() { |
| EqualsTester tester = new EqualsTester() |
| .addEqualityGroup(named("foo"), named("bar")); |
| try { |
| tester.testEquals(); |
| } catch (AssertionFailedError e) { |
| assertErrorMessage( |
| e, |
| "foo [group 1, item 1] must be equal to bar [group 1, item 2]"); |
| return; |
| } |
| fail("should failed because of unequal objects in the same equality group"); |
| } |
| |
| public void testTransitivityBrokenAcrossEqualityGroups() { |
| EqualsTester tester = new EqualsTester() |
| .addEqualityGroup( |
| named("foo").addPeers("bar"), |
| named("bar").addPeers("foo", "x")) |
| .addEqualityGroup( |
| named("baz").addPeers("x"), |
| named("x").addPeers("baz", "bar")); |
| try { |
| tester.testEquals(); |
| } catch (AssertionFailedError e) { |
| assertErrorMessage( |
| e, |
| "bar [group 1, item 2] must be unequal to x [group 2, item 2]"); |
| return; |
| } |
| fail("should failed because transitivity is broken"); |
| } |
| |
| public void testEqualityGroups() { |
| new EqualsTester() |
| .addEqualityGroup( |
| named("foo").addPeers("bar"), named("bar").addPeers("foo")) |
| .addEqualityGroup(named("baz"), named("baz")) |
| .testEquals(); |
| } |
| |
| private static void assertErrorMessage(Throwable e, String message) { |
| // TODO(kevinb): use a Truth assertion here |
| if (!e.getMessage().contains(message)) { |
| fail("expected <" + e.getMessage() + "> to contain <" + message + ">"); |
| } |
| } |
| |
| /** |
| * Test class with valid equals and hashCode methods. Testers created |
| * with instances of this class should always pass. |
| */ |
| private static class ValidTestObject { |
| private int aspect1; |
| private int aspect2; |
| |
| ValidTestObject(int aspect1, int aspect2) { |
| this.aspect1 = aspect1; |
| this.aspect2 = aspect2; |
| } |
| |
| @Override public boolean equals(Object o) { |
| if (!(o instanceof ValidTestObject)) { |
| return false; |
| } |
| ValidTestObject other = (ValidTestObject) o; |
| if (aspect1 != other.aspect1) { |
| return false; |
| } |
| if (aspect2 != other.aspect2) { |
| return false; |
| } |
| return true; |
| } |
| |
| @Override public int hashCode() { |
| int result = 17; |
| result = 37 * result + aspect1; |
| result = 37 * result + aspect2; |
| return result; |
| } |
| } |
| |
| /** Test class with invalid hashCode method. */ |
| private static class InvalidHashCodeObject { |
| private int aspect1; |
| private int aspect2; |
| |
| InvalidHashCodeObject(int aspect1, int aspect2) { |
| this.aspect1 = aspect1; |
| this.aspect2 = aspect2; |
| } |
| |
| @Override public boolean equals(Object o) { |
| if (!(o instanceof InvalidHashCodeObject)) { |
| return false; |
| } |
| InvalidHashCodeObject other = (InvalidHashCodeObject) o; |
| if (aspect1 != other.aspect1) { |
| return false; |
| } |
| if (aspect2 != other.aspect2) { |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| /** Test class that violates reflexitivity. It is not equal to itself */ |
| private static class NonReflexiveObject{ |
| |
| @Override public boolean equals(Object o) { |
| return false; |
| } |
| |
| @Override public int hashCode() { |
| return super.hashCode(); |
| } |
| } |
| |
| /** Test class that returns true if the test object is null */ |
| private static class InvalidEqualsNullObject{ |
| |
| @Override public boolean equals(Object o) { |
| return o == this || o == null; |
| } |
| |
| @Override public int hashCode() { |
| return 0; |
| } |
| } |
| |
| /** |
| * Test class that returns true even if the test object is of the wrong class |
| */ |
| private static class InvalidEqualsIncompatibleClassObject{ |
| |
| @Override public boolean equals(Object o) { |
| if (o == null) { |
| return false; |
| } |
| return true; |
| } |
| |
| @Override public int hashCode() { |
| return 0; |
| } |
| } |
| |
| private static NamedObject named(String name) { |
| return new NamedObject(name); |
| } |
| |
| private static class NamedObject { |
| private final Set<String> peerNames = Sets.newHashSet(); |
| |
| private final String name; |
| |
| NamedObject(String name) { |
| this.name = Preconditions.checkNotNull(name); |
| } |
| |
| NamedObject addPeers(String... names) { |
| peerNames.addAll(ImmutableList.copyOf(names)); |
| return this; |
| } |
| |
| @Override public boolean equals(Object obj) { |
| if (obj instanceof NamedObject) { |
| NamedObject that = (NamedObject) obj; |
| return name.equals(that.name) || peerNames.contains(that.name); |
| } |
| return false; |
| } |
| |
| @Override public int hashCode() { |
| return 0; |
| } |
| |
| @Override public String toString() { |
| return name; |
| } |
| } |
| } |