458 lines
13 KiB
Java
458 lines
13 KiB
Java
/*
|
|
* 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 java.util.Set;
|
|
import junit.framework.AssertionFailedError;
|
|
import junit.framework.TestCase;
|
|
|
|
/**
|
|
* Unit tests for {@link EqualsTester}.
|
|
*
|
|
* @author Jim McMaster
|
|
*/
|
|
@GwtCompatible
|
|
@SuppressWarnings("MissingTestCall")
|
|
public class EqualsTesterTest extends TestCase {
|
|
private ValidTestObject reference;
|
|
private EqualsTester equalsTester;
|
|
private ValidTestObject equalObject1;
|
|
private ValidTestObject equalObject2;
|
|
private ValidTestObject notEqualObject1;
|
|
|
|
@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);
|
|
}
|
|
|
|
/** 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 Object#equals 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 Object#equals 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 not be Object#equals 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 not be Object#equals 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 + " [group 1, item 1]");
|
|
assertErrorMessage(e, notEqualObject1 + " [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 Object#hashCode ("
|
|
+ a.hashCode()
|
|
+ ") of "
|
|
+ a
|
|
+ " [group 1, item 1] must be equal to the Object#hashCode ("
|
|
+ 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 Object#equals 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 Object#equals 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 Object#equals 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 not be Object#equals 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();
|
|
}
|
|
|
|
public void testEqualityBasedOnToString() {
|
|
try {
|
|
new EqualsTester().addEqualityGroup(new EqualsBasedOnToString("foo")).testEquals();
|
|
fail();
|
|
} catch (AssertionFailedError e) {
|
|
assertTrue(e.getMessage().contains("toString representation"));
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
@SuppressWarnings("EqualsHashCode")
|
|
@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 reflexivity. 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) {
|
|
return o != null;
|
|
}
|
|
|
|
@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;
|
|
}
|
|
}
|
|
|
|
private static final class EqualsBasedOnToString {
|
|
private final String s;
|
|
|
|
private EqualsBasedOnToString(String s) {
|
|
this.s = s;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
return obj != null && obj.toString().equals(toString());
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return s.hashCode();
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return s;
|
|
}
|
|
}
|
|
}
|