/*
* Copyright 2010 Proofpoint, Inc.
*
* 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 io.airlift.testing;
/**
* Derived from http://code.google.com/p/kawala
*
* Licensed under Apache License, Version 2.0
*/
import com.google.common.collect.ComparisonChain;
import io.airlift.testing.EquivalenceTester.ElementCheckFailure;
import io.airlift.testing.EquivalenceTester.PairCheckFailure;
import org.testng.annotations.Test;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static io.airlift.testing.Assertions.assertEqualsIgnoreOrder;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.COMPARE_CLASS_CAST_EXCEPTION;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.COMPARE_EQUAL;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.COMPARE_EQUAL_TO_NULL;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.COMPARE_NOT_EQUAL;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.COMPARE_NOT_REFLEXIVE;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.EQUAL;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.EQUAL_NULL_EXCEPTION;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.EQUAL_TO_NULL;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.EQUAL_TO_UNRELATED_CLASS;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.EQUAL_TO_UNRELATED_CLASS_CLASS_CAST_EXCEPTION;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.HASH_CODE_NOT_SAME;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.NOT_EQUAL;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.NOT_GREATER_THAN;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.NOT_LESS_THAN;
import static io.airlift.testing.EquivalenceTester.EquivalenceFailureType.NOT_REFLEXIVE;
import static io.airlift.testing.EquivalenceTester.comparisonTester;
import static io.airlift.testing.EquivalenceTester.equivalenceTester;
import static java.util.Objects.requireNonNull;
import static org.testng.Assert.assertEquals;
import static org.testng.FileAssert.fail;
public class TestEquivalenceTester
{
@Test
public void testCheckFailure()
{
Object o1 = new Object();
Object o2 = new Object();
assertEquals(new ElementCheckFailure(EQUAL, 0, 0, o1), new ElementCheckFailure(EQUAL, 0, 0, o1));
assertEquals(new PairCheckFailure(EQUAL, 0, 0, o1, 1, 0, o2), new PairCheckFailure(EQUAL, 0, 0, o1, 1, 0, o2));
}
@Test
public void notEqual()
{
try {
equivalenceTester()
.addEquivalentGroup("foo")
.addEquivalentGroup("foo")
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertEqualsIgnoreOrder(
e.getFailures(),
newArrayList(
new PairCheckFailure(EQUAL, 0, 0, "foo", 1, 0, "foo"),
new PairCheckFailure(EQUAL, 1, 0, "foo", 0, 0, "foo"),
new PairCheckFailure(COMPARE_EQUAL, 0, 0, "foo", 1, 0, "foo"),
new PairCheckFailure(COMPARE_EQUAL, 1, 0, "foo", 0, 0, "foo")
)
);
}
}
@Test
public void notReflexive()
{
NotReflexive notReflexive = new NotReflexive();
try {
equivalenceTester()
.addEquivalentGroup(notReflexive)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e, new ElementCheckFailure(NOT_REFLEXIVE, 0, 0, notReflexive));
}
}
static class NotReflexive
{
public boolean equals(Object that)
{
return that != null && that instanceof NotReflexive && this != that;
}
}
@Test
public void comparableNotReflexive()
{
ComparableNotReflexive comparableNotReflexive = new ComparableNotReflexive();
try {
equivalenceTester()
.addEquivalentGroup(comparableNotReflexive)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e, new ElementCheckFailure(COMPARE_NOT_REFLEXIVE, 0, 0, comparableNotReflexive));
}
}
static class ComparableNotReflexive implements Comparable<ComparableNotReflexive>
{
@Override
public int compareTo(ComparableNotReflexive that)
{
requireNonNull(that, "that is null");
return this == that ? 1 : -1;
}
}
@Test
public void notSymmetric()
{
NotSymmetric o1 = new NotSymmetric(1);
NotSymmetric o2 = new NotSymmetric(2);
NotSymmetric o3 = new NotSymmetric(3);
try {
equivalenceTester()
.addEquivalentGroup(o1, o3, o2)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(NOT_EQUAL, 0, 0, o1, 0, 1, o3),
new PairCheckFailure(NOT_EQUAL, 0, 0, o1, 0, 2, o2),
new PairCheckFailure(NOT_EQUAL, 0, 2, o2, 0, 1, o3));
}
}
static class NotSymmetric
{
private int id;
NotSymmetric(int id)
{
this.id = id;
}
public boolean equals(Object that)
{
return that != null && that instanceof NotSymmetric && id >= ((NotSymmetric) that).id;
}
@Override
public int hashCode()
{
return 0;
}
}
@Test
public void comparableNotSymmetric()
{
ComparableNotSymmetric o1 = new ComparableNotSymmetric(1);
ComparableNotSymmetric o2 = new ComparableNotSymmetric(2);
ComparableNotSymmetric o3 = new ComparableNotSymmetric(3);
try {
equivalenceTester()
.addEquivalentGroup(o1, o3, o2)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(COMPARE_NOT_EQUAL, 0, 0, o1, 0, 1, o3),
new PairCheckFailure(COMPARE_NOT_EQUAL, 0, 0, o1, 0, 2, o2),
new PairCheckFailure(COMPARE_NOT_EQUAL, 0, 2, o2, 0, 1, o3));
}
}
static class ComparableNotSymmetric implements Comparable<ComparableNotSymmetric>
{
private int id;
ComparableNotSymmetric(int id)
{
this.id = id;
}
@Override
public int compareTo(ComparableNotSymmetric that)
{
requireNonNull(that, "that is null");
if (id >= that.id) {
return 0;
}
return -1;
}
public boolean equals(Object that)
{
return that != null && that instanceof ComparableNotSymmetric;
}
@Override
public int hashCode()
{
return 0;
}
}
@Test
public void equalsNull()
{
EqualsNull equalsNull = new EqualsNull();
try {
equivalenceTester()
.addEquivalentGroup(equalsNull)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e, new ElementCheckFailure(EQUAL_TO_NULL, 0, 0, equalsNull));
}
}
static class EqualsNull
{
@SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
public boolean equals(Object that)
{
return that == null || this == that;
}
}
@Test
public void equalsNullThrowsException()
{
EqualsNullThrowsException equalsNullThrowsException = new EqualsNullThrowsException();
try {
equivalenceTester()
.addEquivalentGroup(equalsNullThrowsException)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e, new ElementCheckFailure(EQUAL_NULL_EXCEPTION, 0, 0, equalsNullThrowsException));
}
}
static class EqualsNullThrowsException
{
@SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
public boolean equals(Object that)
{
return this.hashCode() == that.hashCode();
}
}
@Test
public void equalsUnrelatedClass()
{
EqualsUnrelatedClass equalsUnrelatedClass = new EqualsUnrelatedClass();
try {
equivalenceTester()
.addEquivalentGroup(equalsUnrelatedClass)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e, new ElementCheckFailure(EQUAL_TO_UNRELATED_CLASS, 0, 0, equalsUnrelatedClass));
}
}
static class EqualsUnrelatedClass
{
@SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
public boolean equals(Object that)
{
return that != null;
}
}
@Test
public void equalsUnrelatedClassThrowsException()
{
EqualsOtherClassThrowsException equalsOtherClassThrowsException = new EqualsOtherClassThrowsException();
try {
equivalenceTester()
.addEquivalentGroup(equalsOtherClassThrowsException)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e, new ElementCheckFailure(EQUAL_TO_UNRELATED_CLASS_CLASS_CAST_EXCEPTION, 0, 0, equalsOtherClassThrowsException));
}
}
static class EqualsOtherClassThrowsException
{
@SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
public boolean equals(Object that)
{
return that != null && ((EqualsOtherClassThrowsException) that).hashCode() == this.hashCode();
}
}
@Test
public void comparableAndNotComparable()
{
NotComparable notComparable = new NotComparable();
try {
equivalenceTester()
.addEquivalentGroup(notComparable, "Hello")
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(COMPARE_CLASS_CAST_EXCEPTION, 0, 1, "Hello", 0, 0, notComparable),
new PairCheckFailure(NOT_EQUAL, 0, 0, notComparable, 0, 1, "Hello"),
new PairCheckFailure(NOT_EQUAL, 0, 1, "Hello", 0, 0, notComparable),
new PairCheckFailure(HASH_CODE_NOT_SAME, 0, 0, notComparable, 0, 1, "Hello")
);
}
try {
equivalenceTester()
.addEquivalentGroup("Hello", notComparable)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(COMPARE_CLASS_CAST_EXCEPTION, 0, 0, "Hello", 0, 1, notComparable),
new PairCheckFailure(NOT_EQUAL, 0, 0, "Hello", 0, 1, notComparable),
new PairCheckFailure(NOT_EQUAL, 0, 1, notComparable, 0, 0, "Hello"),
new PairCheckFailure(HASH_CODE_NOT_SAME, 0, 0, "Hello", 0, 1, notComparable)
);
}
try {
equivalenceTester()
.addEquivalentGroup(notComparable)
.addEquivalentGroup("Hello")
.check();
fail("EquivalenceTester should have throw an EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(COMPARE_CLASS_CAST_EXCEPTION, 1, 0, "Hello", 0, 0, notComparable)
);
}
try {
equivalenceTester()
.addEquivalentGroup("Hello")
.addEquivalentGroup(notComparable)
.check();
fail("EquivalenceTester should have throw an EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(COMPARE_CLASS_CAST_EXCEPTION, 0, 0, "Hello", 1, 0, notComparable)
);
}
}
static class NotComparable
{
}
@Test
public void compareToAgainstNull()
{
ComparableThatDoesNotThrowNPE comparableThatDoesNotThrowNPE = new ComparableThatDoesNotThrowNPE(1);
try {
equivalenceTester()
.addEquivalentGroup(comparableThatDoesNotThrowNPE)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new ElementCheckFailure(COMPARE_EQUAL_TO_NULL, 0, 0, comparableThatDoesNotThrowNPE)
);
}
}
@Test
public void testCheckCompare()
{
comparisonTester()
.addLesserGroup(-1)
.addGreaterGroup(0)
.addGreaterGroup(1)
.check();
comparisonTester()
.addLesserGroup("alice")
.addGreaterGroup("bob")
.addGreaterGroup("charlie")
.check();
comparisonTester()
.addLesserGroup(-1, -1, -1)
.addGreaterGroup(0, 0)
.addGreaterGroup(1)
.check();
comparisonTester()
.addLesserGroup("alice")
.addGreaterGroup("bob", "bob")
.addGreaterGroup("charlie", "charlie", "charlie")
.check();
}
@Test
public void testComparisonOrder()
{
try {
comparisonTester()
.addLesserGroup(1)
.addGreaterGroup(0)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(NOT_LESS_THAN, 0, 0, 1, 1, 0, 0),
new PairCheckFailure(NOT_GREATER_THAN, 1, 0, 0, 0, 0, 1)
);
}
try {
comparisonTester()
.addLesserGroup("bob")
.addGreaterGroup("alice")
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(NOT_LESS_THAN, 0, 0, "bob", 1, 0, "alice"),
new PairCheckFailure(NOT_GREATER_THAN, 1, 0, "alice", 0, 0, "bob")
);
}
try {
comparisonTester()
.addLesserGroup(1)
.addGreaterGroup(0, 0)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(NOT_LESS_THAN, 0, 0, 1, 1, 0, 0),
new PairCheckFailure(NOT_GREATER_THAN, 1, 0, 0, 0, 0, 1),
new PairCheckFailure(NOT_LESS_THAN, 0, 0, 1, 1, 1, 0),
new PairCheckFailure(NOT_GREATER_THAN, 1, 1, 0, 0, 0, 1)
);
}
try {
comparisonTester()
.addLesserGroup("bob")
.addGreaterGroup("alice", "alice")
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(NOT_LESS_THAN, 0, 0, "bob", 1, 0, "alice"),
new PairCheckFailure(NOT_GREATER_THAN, 1, 0, "alice", 0, 0, "bob"),
new PairCheckFailure(NOT_LESS_THAN, 0, 0, "bob", 1, 1, "alice"),
new PairCheckFailure(NOT_GREATER_THAN, 1, 1, "alice", 0, 0, "bob")
);
}
}
@Test
public void testComparisonNotEquals()
{
try {
comparisonTester()
.addLesserGroup(0)
.addGreaterGroup(1, 2)
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(NOT_EQUAL, 1, 0, 1, 1, 1, 2),
new PairCheckFailure(NOT_EQUAL, 1, 1, 2, 1, 0, 1),
new PairCheckFailure(COMPARE_NOT_EQUAL, 1, 0, 1, 1, 1, 2),
new PairCheckFailure(COMPARE_NOT_EQUAL, 1, 1, 2, 1, 0, 1),
new PairCheckFailure(HASH_CODE_NOT_SAME, 1, 0, 1, 1, 1, 2)
);
}
try {
comparisonTester()
.addLesserGroup("alice")
.addGreaterGroup("bob", "charlie")
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(NOT_EQUAL, 1, 0, "bob", 1, 1, "charlie"),
new PairCheckFailure(NOT_EQUAL, 1, 1, "charlie", 1, 0, "bob"),
new PairCheckFailure(COMPARE_NOT_EQUAL, 1, 0, "bob", 1, 1, "charlie"),
new PairCheckFailure(COMPARE_NOT_EQUAL, 1, 1, "charlie", 1, 0, "bob"),
new PairCheckFailure(HASH_CODE_NOT_SAME, 1, 0, "bob", 1, 1, "charlie")
);
}
}
@Test
@SuppressWarnings({"unchecked", "rawtypes", "OverlyStrongTypeCast"})
public void testNotComparableComparison()
{
try {
comparisonTester()
.addLesserGroup((List) newArrayList(5)) // cast to List in order to remove type safety of returned generic
.addGreaterGroup("string")
.check();
fail("Expected EquivalenceAssertionError");
}
catch (EquivalenceAssertionError e) {
assertExpectedFailures(e,
new PairCheckFailure(COMPARE_CLASS_CAST_EXCEPTION, 0, 0, 5, 1, 0, "string"),
new PairCheckFailure(COMPARE_CLASS_CAST_EXCEPTION, 1, 0, "string", 0, 0, 5)
);
}
}
static class ComparableThatDoesNotThrowNPE
implements Comparable<ComparableThatDoesNotThrowNPE>
{
private final int value;
public ComparableThatDoesNotThrowNPE(int value)
{
this.value = value;
}
@Override
public int compareTo(ComparableThatDoesNotThrowNPE o)
{
if (o == null) {
return 1;
}
return ComparisonChain.start().compare(value, o.value).result();
}
@SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
public boolean equals(Object that)
{
return that != null && that instanceof ComparableThatDoesNotThrowNPE && value == ((ComparableThatDoesNotThrowNPE) that).value;
}
@Override
public int hashCode()
{
return value;
}
}
private void assertExpectedFailures(EquivalenceAssertionError e, ElementCheckFailure... expected)
{
assertEqualsIgnoreOrder(e.getFailures(), newArrayList(expected));
}
}