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 junit.framework.Assert.assertEquals; 021import static junit.framework.Assert.assertTrue; 022 023import com.google.common.annotations.GwtCompatible; 024import com.google.common.base.Equivalence; 025import com.google.common.collect.ImmutableList; 026import com.google.common.collect.Lists; 027import com.google.common.testing.RelationshipTester.ItemReporter; 028import com.google.errorprone.annotations.CanIgnoreReturnValue; 029import java.util.List; 030import org.jspecify.annotations.NullMarked; 031 032/** 033 * Tester for {@link Equivalence} relationships between groups of objects. 034 * 035 * <p>To use, create a new {@link EquivalenceTester} and add equivalence groups where each group 036 * contains objects that are supposed to be equal to each other. Objects of different groups are 037 * expected to be unequal. For example: 038 * 039 * <pre>{@code 040 * EquivalenceTester.of(someStringEquivalence) 041 * .addEquivalenceGroup("hello", "h" + "ello") 042 * .addEquivalenceGroup("world", "wor" + "ld") 043 * .test(); 044 * }</pre> 045 * 046 * <p>Note that testing {@link Object#equals(Object)} is more simply done using the {@link 047 * EqualsTester}. It includes an extra test against an instance of an arbitrary class without having 048 * to explicitly add another equivalence group. 049 * 050 * @author Gregory Kick 051 * @since 10.0 052 */ 053@GwtCompatible 054@NullMarked 055public final class EquivalenceTester<T> { 056 private static final int REPETITIONS = 3; 057 058 private final Equivalence<? super T> equivalence; 059 private final RelationshipTester<T> delegate; 060 private final List<T> items = Lists.newArrayList(); 061 062 private EquivalenceTester(Equivalence<? super T> equivalence) { 063 this.equivalence = checkNotNull(equivalence); 064 this.delegate = new RelationshipTester<>(equivalence, "equivalent", "hash", new ItemReporter()); 065 } 066 067 public static <T> EquivalenceTester<T> of(Equivalence<? super T> equivalence) { 068 return new EquivalenceTester<>(equivalence); 069 } 070 071 /** 072 * Adds a group of objects that are supposed to be equivalent to each other and not equivalent to 073 * objects in any other equivalence group added to this tester. 074 */ 075 @CanIgnoreReturnValue 076 public EquivalenceTester<T> addEquivalenceGroup(T first, T... rest) { 077 addEquivalenceGroup(Lists.asList(first, rest)); 078 return this; 079 } 080 081 @CanIgnoreReturnValue 082 public EquivalenceTester<T> addEquivalenceGroup(Iterable<T> group) { 083 delegate.addRelatedGroup(group); 084 items.addAll(ImmutableList.copyOf(group)); 085 return this; 086 } 087 088 /** Run tests on equivalence methods, throwing a failure on an invalid test */ 089 @CanIgnoreReturnValue 090 public EquivalenceTester<T> test() { 091 for (int run = 0; run < REPETITIONS; run++) { 092 testItems(); 093 delegate.test(); 094 } 095 return this; 096 } 097 098 private void testItems() { 099 for (T item : items) { 100 /* 101 * TODO(cpovirk): consider no longer running these equivalent() tests on every Equivalence, 102 * since the Equivalence base type now implements this logic itself 103 */ 104 assertTrue(item + " must be inequivalent to null", !equivalence.equivalent(item, null)); 105 assertTrue("null must be inequivalent to " + item, !equivalence.equivalent(null, item)); 106 assertTrue(item + " must be equivalent to itself", equivalence.equivalent(item, item)); 107 assertEquals( 108 "the hash of " + item + " must be consistent", 109 equivalence.hash(item), 110 equivalence.hash(item)); 111 } 112 } 113}