001/* 002 * Copyright (C) 2011 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.testing; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020import static com.google.common.collect.Lists.asList; 021import static junit.framework.Assert.assertEquals; 022import static junit.framework.Assert.assertTrue; 023 024import com.google.common.annotations.GwtCompatible; 025import com.google.common.base.Equivalence; 026import com.google.common.collect.ImmutableList; 027import com.google.common.testing.RelationshipTester.Item; 028import com.google.errorprone.annotations.CanIgnoreReturnValue; 029import java.util.ArrayList; 030import java.util.List; 031import org.jspecify.annotations.NullMarked; 032 033/** 034 * Tester for {@link Equivalence} relationships between groups of objects. 035 * 036 * <p>To use, create a new {@link EquivalenceTester} and add equivalence groups where each group 037 * contains objects that are supposed to be equal to each other. Objects of different groups are 038 * expected to be unequal. For example: 039 * 040 * {@snippet : 041 * EquivalenceTester.of(someStringEquivalence) 042 * .addEquivalenceGroup("hello", "h" + "ello") 043 * .addEquivalenceGroup("world", "wor" + "ld") 044 * .test(); 045 * } 046 * 047 * <p>Note that testing {@link Object#equals(Object)} is more simply done using the {@link 048 * EqualsTester}. It includes an extra test against an instance of an arbitrary class without having 049 * to explicitly add another equivalence group. 050 * 051 * @author Gregory Kick 052 * @since 10.0 053 */ 054@GwtCompatible 055@NullMarked 056public final class EquivalenceTester<T> { 057 private static final int REPETITIONS = 3; 058 059 private final Equivalence<? super T> equivalence; 060 private final RelationshipTester<T> delegate; 061 private final List<T> items = new ArrayList<>(); 062 063 private EquivalenceTester(Equivalence<? super T> equivalence) { 064 this.equivalence = checkNotNull(equivalence); 065 this.delegate = 066 new RelationshipTester<>( 067 equivalence, "equivalent", "hash", /* itemReporter= */ Item::toString); 068 } 069 070 public static <T> EquivalenceTester<T> of(Equivalence<? super T> equivalence) { 071 return new EquivalenceTester<>(equivalence); 072 } 073 074 /** 075 * Adds a group of objects that are supposed to be equivalent to each other and not equivalent to 076 * objects in any other equivalence group added to this tester. 077 */ 078 @CanIgnoreReturnValue 079 public EquivalenceTester<T> addEquivalenceGroup(T first, T... rest) { 080 addEquivalenceGroup(asList(first, rest)); 081 return this; 082 } 083 084 /** 085 * Adds a group of objects that are supposed to be equivalent to each other and not equivalent to 086 * objects in any other equivalence group added to this tester. 087 */ 088 @CanIgnoreReturnValue 089 public EquivalenceTester<T> addEquivalenceGroup(Iterable<T> group) { 090 delegate.addRelatedGroup(group); 091 items.addAll(ImmutableList.copyOf(group)); 092 return this; 093 } 094 095 /** Run tests on equivalence methods, throwing a failure on an invalid test */ 096 @CanIgnoreReturnValue 097 public EquivalenceTester<T> test() { 098 for (int run = 0; run < REPETITIONS; run++) { 099 testItems(); 100 delegate.test(); 101 } 102 return this; 103 } 104 105 private void testItems() { 106 for (T item : items) { 107 /* 108 * TODO(cpovirk): consider no longer running these equivalent() tests on every Equivalence, 109 * since the Equivalence base type now implements this logic itself 110 */ 111 assertTrue(item + " must be inequivalent to null", !equivalence.equivalent(item, null)); 112 assertTrue("null must be inequivalent to " + item, !equivalence.equivalent(null, item)); 113 assertTrue(item + " must be equivalent to itself", equivalence.equivalent(item, item)); 114 assertEquals( 115 "the hash of " + item + " must be consistent", 116 equivalence.hash(item), 117 equivalence.hash(item)); 118 } 119 } 120}