blob: e2ec18aa330d2bf3a273c702742bb133082aad1a [file] [log] [blame]
/*
* 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;
}
}
}