/* * 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; } } }