From 1c946c8487412eb567a3dfea23cee1c26f04a96f Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Sat, 5 Oct 2024 10:08:33 +0200 Subject: [PATCH 01/15] Let ValueProvider return an Optional --- .../checkers/AbstractDelegationChecker.java | 3 +- .../MapEntryHashCodeRequirementChecker.java | 2 ++ .../fieldchecks/JpaLazyGetterFieldCheck.java | 5 +-- .../fieldchecks/ReflexivityFieldCheck.java | 3 +- .../fieldchecks/StringFieldCheck.java | 7 +++- .../internal/exceptions/NoValueException.java | 20 +++++++++++ .../instantiation/SubjectCreator.java | 6 +++- .../instantiation/ValueProvider.java | 6 ++-- .../instantiation/VintageValueProvider.java | 4 +-- .../exceptions/NoValueExceptionTest.java | 16 +++++++++ .../instantiation/SubjectCreatorTest.java | 34 +++++++++++++++---- .../VintageValueProviderTest.java | 11 +++--- 12 files changed, 95 insertions(+), 22 deletions(-) create mode 100644 equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/exceptions/NoValueException.java create mode 100644 equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/exceptions/NoValueExceptionTest.java diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/AbstractDelegationChecker.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/AbstractDelegationChecker.java index ca64fabb1..c3842314f 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/AbstractDelegationChecker.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/AbstractDelegationChecker.java @@ -4,6 +4,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.lang.reflect.Field; +import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.ClassProbe; import nl.jqno.equalsverifier.internal.reflection.FieldIterable; import nl.jqno.equalsverifier.internal.reflection.Tuple; @@ -74,7 +75,7 @@ private void checkAbstractDelegationInFields() { private Tuple safelyGetTuple(TypeTag tag) { try { - return valueProvider.provide(tag); + return valueProvider.provide(tag).orElseThrow(() -> new NoValueException(tag)); } catch (Exception ignored) { // If it fails for some reason, any reason, just return null so we can skip the test. return null; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/MapEntryHashCodeRequirementChecker.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/MapEntryHashCodeRequirementChecker.java index 3ae750f99..e686aba8b 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/MapEntryHashCodeRequirementChecker.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/MapEntryHashCodeRequirementChecker.java @@ -4,6 +4,7 @@ import java.util.Map; import java.util.Objects; +import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider; import nl.jqno.equalsverifier.internal.util.Configuration; import nl.jqno.equalsverifier.internal.util.Context; @@ -24,6 +25,7 @@ public void check() { if (Map.Entry.class.isAssignableFrom(config.getType())) { Map.Entry e = valueProvider .>provide(config.getTypeTag()) + .orElseThrow(() -> new NoValueException(config.getTypeTag())) .getRed(); int expectedHashCode = Objects.hashCode(e.getKey()) ^ Objects.hashCode(e.getValue()); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/JpaLazyGetterFieldCheck.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/JpaLazyGetterFieldCheck.java index 9727744bf..2c021ba54 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/JpaLazyGetterFieldCheck.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/JpaLazyGetterFieldCheck.java @@ -9,6 +9,7 @@ import java.util.function.Function; import nl.jqno.equalsverifier.Warning; import nl.jqno.equalsverifier.internal.exceptions.EqualsVerifierInternalBugException; +import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.ClassProbe; import nl.jqno.equalsverifier.internal.reflection.FieldProbe; import nl.jqno.equalsverifier.internal.reflection.Instantiator; @@ -67,8 +68,8 @@ public void execute(FieldProbe fieldProbe) { classProbe.hasMethod(getterName) ); - Class sub = throwingGetterCreator(getterName); - Tuple tuple = valueProvider.provide(new TypeTag(sub)); + TypeTag sub = new TypeTag(throwingGetterCreator(getterName)); + Tuple tuple = valueProvider.provide(sub).orElseThrow(() -> new NoValueException(sub)); T red1 = tuple.getRed(); T red2 = tuple.getRedCopy(); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java index 3d775008a..31501c691 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java @@ -7,6 +7,7 @@ import java.util.EnumSet; import java.util.Set; import nl.jqno.equalsverifier.Warning; +import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.*; import nl.jqno.equalsverifier.internal.reflection.annotations.AnnotationCache; import nl.jqno.equalsverifier.internal.reflection.annotations.SupportedAnnotations; @@ -82,7 +83,7 @@ private void checkValueReflexivity(FieldProbe probe) { TypeTag tag = TypeTag.of(field, typeTag); Tuple tuple = prefabbedFields.contains(fieldName) ? fieldCache.get(fieldName) - : valueProvider.provide(tag); + : valueProvider.provide(tag).orElseThrow(() -> new NoValueException(tag)); Object left = subjectCreator.withFieldSetTo(field, tuple.getRed()); Object right = subjectCreator.withFieldSetTo(field, tuple.getRedCopy()); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/StringFieldCheck.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/StringFieldCheck.java index e05e50ad5..0362fa154 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/StringFieldCheck.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/StringFieldCheck.java @@ -3,6 +3,7 @@ import static nl.jqno.equalsverifier.internal.util.Assert.fail; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.exceptions.ReflectionException; import nl.jqno.equalsverifier.internal.reflection.FieldProbe; import nl.jqno.equalsverifier.internal.reflection.TypeTag; @@ -40,7 +41,11 @@ public StringFieldCheck( ) public void execute(FieldProbe fieldProbe) { if (String.class.equals(fieldProbe.getType()) && !fieldProbe.isStatic()) { - String red = valueProvider.provide(new TypeTag(String.class)).getRed(); + TypeTag string = new TypeTag(String.class); + String red = valueProvider + .provide(string) + .orElseThrow(() -> new NoValueException(string)) + .getRed(); final T reference; final T copy; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/exceptions/NoValueException.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/exceptions/NoValueException.java new file mode 100644 index 000000000..182a71572 --- /dev/null +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/exceptions/NoValueException.java @@ -0,0 +1,20 @@ +package nl.jqno.equalsverifier.internal.exceptions; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import nl.jqno.equalsverifier.internal.reflection.TypeTag; + +@SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "EqualsVerifier doesn't serialize.") +public class NoValueException extends MessagingException { + + private final TypeTag tag; + + public NoValueException(TypeTag tag) { + super(); + this.tag = tag; + } + + @Override + public String getDescription() { + return "Could not find a value for " + tag + ". Please add prefab values for this type."; + } +} diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java index 86c3a7cb2..7eb5ac81a 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; import nl.jqno.equalsverifier.internal.exceptions.ModuleException; +import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.*; import nl.jqno.equalsverifier.internal.util.Configuration; import nl.jqno.equalsverifier.internal.util.Rethrow; @@ -244,7 +245,10 @@ private Tuple valuesFor(Field f) { return fieldCache.get(fieldName); } try { - Tuple tuple = valueProvider.provide(TypeTag.of(f, typeTag)); + TypeTag fieldTag = TypeTag.of(f, typeTag); + Tuple tuple = valueProvider + .provide(fieldTag) + .orElseThrow(() -> new NoValueException(fieldTag)); fieldCache.put(fieldName, tuple); return tuple; } catch (ModuleException e) { diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ValueProvider.java index eef125f5c..e2a73f95c 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ValueProvider.java @@ -1,5 +1,6 @@ package nl.jqno.equalsverifier.internal.reflection.instantiation; +import java.util.Optional; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; @@ -18,7 +19,8 @@ public interface ValueProvider { * * @param The returned tuple will have this generic type. * @param tag A description of the desired type, including generic parameters. - * @return A tuple of two different values of the given type. + * @return A tuple of two different values of the given type, or an empty Optional if none + * could be found. */ - Tuple provide(TypeTag tag); + Optional> provide(TypeTag tag); } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java index 4af2fbd1b..c92154520 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java @@ -42,8 +42,8 @@ public VintageValueProvider(FactoryCache factoryCache, Objenesis objenesis) { /** {@inheritDoc} */ @Override - public Tuple provide(TypeTag tag) { - return Rethrow.rethrow(() -> giveTuple(tag)); + public Optional> provide(TypeTag tag) { + return Rethrow.rethrow(() -> Optional.of(giveTuple(tag))); } /** diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/exceptions/NoValueExceptionTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/exceptions/NoValueExceptionTest.java new file mode 100644 index 000000000..58259cc49 --- /dev/null +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/exceptions/NoValueExceptionTest.java @@ -0,0 +1,16 @@ +package nl.jqno.equalsverifier.internal.exceptions; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import org.junit.jupiter.api.Test; + +public class NoValueExceptionTest { + + @Test + public void description() { + TypeTag tag = new TypeTag(String.class); + NoValueException e = new NoValueException(tag); + assertTrue(e.getDescription().contains("String")); + } +} diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java index d9e9ba8cd..e62ebfd13 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java @@ -5,9 +5,12 @@ import java.lang.reflect.Field; import java.util.Objects; +import java.util.Optional; +import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.FieldCache; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; import nl.jqno.equalsverifier.internal.util.Configuration; import nl.jqno.equalsverifier.internal.util.ConfigurationHelper; import org.junit.jupiter.api.BeforeEach; @@ -49,9 +52,6 @@ public void setup() throws NoSuchFieldException { fieldS = SomeClass.class.getDeclaredField("s"); } - @Test - public void sanity() {} - @Test public void plain() { expected = new SomeClass(I_RED, I_RED, S_RED); @@ -194,16 +194,36 @@ public void copyIntoSubclass() { assertEquals(SomeSub.class, actual.getClass()); } + @Test + public void noValueFound() { + sut = new SubjectCreator<>(config, new NoValueProvider(), fieldCache, objenesis); + + ExpectedException + .when(() -> sut.plain()) + .assertThrows(NoValueException.class) + .assertDescriptionContains("int"); + + assertEquals(expected, actual); + } + static class SubjectCreatorTestValueProvider implements ValueProvider { - public Tuple provide(TypeTag tag) { + public Optional> provide(TypeTag tag) { if (int.class.equals(tag.getType())) { - return Tuple.of(I_RED, I_BLUE, I_RED); + return Optional.of(Tuple.of(I_RED, I_BLUE, I_RED)); } if (String.class.equals(tag.getType())) { - return Tuple.of(S_RED, S_BLUE, new String(S_RED)); + return Optional.of(Tuple.of(S_RED, S_BLUE, new String(S_RED))); } - throw new IllegalStateException(); + return Optional.empty(); + } + } + + static class NoValueProvider implements ValueProvider { + + @Override + public Optional> provide(TypeTag tag) { + return Optional.empty(); } } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java index 2fa15353e..03cde5c19 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java @@ -7,9 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.fail; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; +import java.util.*; import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; @@ -48,8 +46,11 @@ public void sanityTestFactoryIncreasesStringLength() { @Test public void provide() { - Tuple actual = vp.provide(POINT_TAG); - assertEquals(Tuple.of(new Point(42, 42), new Point(1337, 1337), new Point(42, 42)), actual); + Optional> actual = vp.provide(POINT_TAG); + assertEquals( + Tuple.of(new Point(42, 42), new Point(1337, 1337), new Point(42, 42)), + actual.get() + ); } @Test From a72cd9a1433ad5cff1886f1e411960027acc13dd Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Sat, 5 Oct 2024 19:51:51 +0200 Subject: [PATCH 02/15] Adds ChainedValueProvider --- .../instantiation/ChainedValueProvider.java | 34 ++++++++ .../instantiation/VintageValueProvider.java | 2 +- .../equalsverifier/internal/util/Context.java | 7 +- .../ChainedValueProviderTest.java | 84 +++++++++++++++++++ 4 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java create mode 100644 equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java new file mode 100644 index 000000000..bef1c40d7 --- /dev/null +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java @@ -0,0 +1,34 @@ +package nl.jqno.equalsverifier.internal.reflection.instantiation; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import nl.jqno.equalsverifier.internal.reflection.Tuple; +import nl.jqno.equalsverifier.internal.reflection.TypeTag; + +/** + * Provider of prefabricated instances of classes, delegating to other ValueProviders in sequence. + */ +public class ChainedValueProvider implements ValueProvider { + + private final List providers; + + /** Constructor. + * + * @param providers ValueProviders to delegate to when providing a value. + */ + public ChainedValueProvider(ValueProvider... providers) { + this.providers = Arrays.asList(providers); + } + + /** {@inheritDoc} */ + @Override + public Optional> provide(TypeTag tag) { + return providers + .stream() + .map(vp -> vp.provide(tag)) + .filter(Optional::isPresent) + .findFirst() + .orElse(Optional.empty()); + } +} diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java index c92154520..c886d6c4e 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java @@ -14,7 +14,7 @@ import org.objenesis.Objenesis; /** - * Creator of prefabricated instances of classes, using a "vintage" strategy for doing so. + * Provider of prefabricated instances of classes, using a "vintage" strategy for doing so. * * Vintage in this case means that it employs the creation strategy that EqualsVerifier has been * using since its inception. This strategy is quite hacky and messy, and other strategies might diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java index e70f1620d..97f6d5429 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java @@ -2,9 +2,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import nl.jqno.equalsverifier.internal.reflection.*; -import nl.jqno.equalsverifier.internal.reflection.instantiation.SubjectCreator; -import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider; -import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.reflection.instantiation.*; import org.objenesis.Objenesis; public final class Context { @@ -33,7 +31,8 @@ public Context( this.fieldCache = fieldCache; FactoryCache cache = JavaApiPrefabValues.build().merge(factoryCache); - this.valueProvider = new VintageValueProvider(cache, objenesis); + ValueProvider vintage = new VintageValueProvider(cache, objenesis); + this.valueProvider = new ChainedValueProvider(vintage); this.subjectCreator = new SubjectCreator<>(configuration, valueProvider, fieldCache, objenesis); } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java new file mode 100644 index 000000000..a809b8821 --- /dev/null +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java @@ -0,0 +1,84 @@ +package nl.jqno.equalsverifier.internal.reflection.instantiation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Optional; +import nl.jqno.equalsverifier.internal.reflection.Tuple; +import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import org.junit.jupiter.api.Test; + +public class ChainedValueProviderTest { + + private static final TypeTag INT = new TypeTag(int.class); + + private final SingleTypeValueProvider intProvider = new SingleTypeValueProvider<>( + int.class, + 1, + 2, + 1 + ); + private final SingleTypeValueProvider stringProvider = new SingleTypeValueProvider<>( + String.class, + "a", + "b", + "a" + ); + + private ChainedValueProvider sut; + + @Test + public void returnsValueIfMatch() { + sut = new ChainedValueProvider(intProvider); + assertEquals(1, sut.provide(INT).get().getRed()); + } + + @Test + public void returnsEmptyIfNoMatch() { + sut = new ChainedValueProvider(stringProvider); + assertEquals(Optional.empty(), sut.provide(INT)); + } + + @Test + public void skipsNonMatchingValue() { + sut = new ChainedValueProvider(stringProvider, intProvider); + assertEquals(1, sut.provide(INT).get().getRed()); + assertEquals(1, stringProvider.called); + assertEquals(1, intProvider.called); + } + + @Test + public void returnsValueFromFirstMatch() { + SingleTypeValueProvider anotherIntProvider = new SingleTypeValueProvider( + int.class, + 1, + 2, + 1 + ); + sut = new ChainedValueProvider(intProvider, anotherIntProvider); + assertEquals(1, sut.provide(INT).get().getRed()); + assertEquals(1, intProvider.called); + assertEquals(0, anotherIntProvider.called); + } + + static class SingleTypeValueProvider implements ValueProvider { + + private final Class type; + private final Tuple values; + private int called = 0; + + public SingleTypeValueProvider(Class type, X red, X blue, X redCopy) { + this.type = type; + this.values = Tuple.of(red, blue, redCopy); + } + + @Override + @SuppressWarnings("unchecked") + public Optional> provide(TypeTag tag) { + called++; + if (tag.getType().equals(type)) { + return Optional.of((Tuple) values); + } + return Optional.empty(); + } + } +} From f4f44384e953b39e4d8feb096cf20c4938c30b42 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Sun, 6 Oct 2024 19:40:03 +0200 Subject: [PATCH 03/15] Adds label parameter to ValueProvider and does some refactoring --- .../checkers/AbstractDelegationChecker.java | 3 +-- .../MapEntryHashCodeRequirementChecker.java | 2 -- .../fieldchecks/JpaLazyGetterFieldCheck.java | 9 ++------ .../fieldchecks/ReflexivityFieldCheck.java | 3 +-- .../fieldchecks/StringFieldCheck.java | 6 +---- .../instantiation/ChainedValueProvider.java | 4 ++-- .../instantiation/SubjectCreator.java | 2 +- .../instantiation/ValueProvider.java | 22 +++++++++++++++++-- .../instantiation/VintageValueProvider.java | 2 +- .../ChainedValueProviderTest.java | 21 +++++++++++++----- .../instantiation/SubjectCreatorTest.java | 4 ++-- .../VintageValueProviderTest.java | 8 ++----- 12 files changed, 49 insertions(+), 37 deletions(-) diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/AbstractDelegationChecker.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/AbstractDelegationChecker.java index c3842314f..ca64fabb1 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/AbstractDelegationChecker.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/AbstractDelegationChecker.java @@ -4,7 +4,6 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.lang.reflect.Field; -import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.ClassProbe; import nl.jqno.equalsverifier.internal.reflection.FieldIterable; import nl.jqno.equalsverifier.internal.reflection.Tuple; @@ -75,7 +74,7 @@ private void checkAbstractDelegationInFields() { private Tuple safelyGetTuple(TypeTag tag) { try { - return valueProvider.provide(tag).orElseThrow(() -> new NoValueException(tag)); + return valueProvider.provide(tag); } catch (Exception ignored) { // If it fails for some reason, any reason, just return null so we can skip the test. return null; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/MapEntryHashCodeRequirementChecker.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/MapEntryHashCodeRequirementChecker.java index e686aba8b..3ae750f99 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/MapEntryHashCodeRequirementChecker.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/MapEntryHashCodeRequirementChecker.java @@ -4,7 +4,6 @@ import java.util.Map; import java.util.Objects; -import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider; import nl.jqno.equalsverifier.internal.util.Configuration; import nl.jqno.equalsverifier.internal.util.Context; @@ -25,7 +24,6 @@ public void check() { if (Map.Entry.class.isAssignableFrom(config.getType())) { Map.Entry e = valueProvider .>provide(config.getTypeTag()) - .orElseThrow(() -> new NoValueException(config.getTypeTag())) .getRed(); int expectedHashCode = Objects.hashCode(e.getKey()) ^ Objects.hashCode(e.getValue()); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/JpaLazyGetterFieldCheck.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/JpaLazyGetterFieldCheck.java index 2c021ba54..5069c598c 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/JpaLazyGetterFieldCheck.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/JpaLazyGetterFieldCheck.java @@ -9,12 +9,7 @@ import java.util.function.Function; import nl.jqno.equalsverifier.Warning; import nl.jqno.equalsverifier.internal.exceptions.EqualsVerifierInternalBugException; -import nl.jqno.equalsverifier.internal.exceptions.NoValueException; -import nl.jqno.equalsverifier.internal.reflection.ClassProbe; -import nl.jqno.equalsverifier.internal.reflection.FieldProbe; -import nl.jqno.equalsverifier.internal.reflection.Instantiator; -import nl.jqno.equalsverifier.internal.reflection.Tuple; -import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import nl.jqno.equalsverifier.internal.reflection.*; import nl.jqno.equalsverifier.internal.reflection.annotations.AnnotationCache; import nl.jqno.equalsverifier.internal.reflection.annotations.SupportedAnnotations; import nl.jqno.equalsverifier.internal.reflection.instantiation.SubjectCreator; @@ -69,7 +64,7 @@ public void execute(FieldProbe fieldProbe) { ); TypeTag sub = new TypeTag(throwingGetterCreator(getterName)); - Tuple tuple = valueProvider.provide(sub).orElseThrow(() -> new NoValueException(sub)); + Tuple tuple = valueProvider.provide(sub); T red1 = tuple.getRed(); T red2 = tuple.getRedCopy(); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java index 31501c691..3d775008a 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java @@ -7,7 +7,6 @@ import java.util.EnumSet; import java.util.Set; import nl.jqno.equalsverifier.Warning; -import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.*; import nl.jqno.equalsverifier.internal.reflection.annotations.AnnotationCache; import nl.jqno.equalsverifier.internal.reflection.annotations.SupportedAnnotations; @@ -83,7 +82,7 @@ private void checkValueReflexivity(FieldProbe probe) { TypeTag tag = TypeTag.of(field, typeTag); Tuple tuple = prefabbedFields.contains(fieldName) ? fieldCache.get(fieldName) - : valueProvider.provide(tag).orElseThrow(() -> new NoValueException(tag)); + : valueProvider.provide(tag); Object left = subjectCreator.withFieldSetTo(field, tuple.getRed()); Object right = subjectCreator.withFieldSetTo(field, tuple.getRedCopy()); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/StringFieldCheck.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/StringFieldCheck.java index 0362fa154..19fb9c094 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/StringFieldCheck.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/StringFieldCheck.java @@ -3,7 +3,6 @@ import static nl.jqno.equalsverifier.internal.util.Assert.fail; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.exceptions.ReflectionException; import nl.jqno.equalsverifier.internal.reflection.FieldProbe; import nl.jqno.equalsverifier.internal.reflection.TypeTag; @@ -42,10 +41,7 @@ public StringFieldCheck( public void execute(FieldProbe fieldProbe) { if (String.class.equals(fieldProbe.getType()) && !fieldProbe.isStatic()) { TypeTag string = new TypeTag(String.class); - String red = valueProvider - .provide(string) - .orElseThrow(() -> new NoValueException(string)) - .getRed(); + String red = valueProvider.provide(string).getRed(); final T reference; final T copy; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java index bef1c40d7..f02b17dc1 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java @@ -23,10 +23,10 @@ public ChainedValueProvider(ValueProvider... providers) { /** {@inheritDoc} */ @Override - public Optional> provide(TypeTag tag) { + public Optional> provide(TypeTag tag, String label) { return providers .stream() - .map(vp -> vp.provide(tag)) + .map(vp -> vp.provide(tag, label)) .filter(Optional::isPresent) .findFirst() .orElse(Optional.empty()); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java index 7eb5ac81a..d1477654a 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java @@ -247,7 +247,7 @@ private Tuple valuesFor(Field f) { try { TypeTag fieldTag = TypeTag.of(f, typeTag); Tuple tuple = valueProvider - .provide(fieldTag) + .provide(fieldTag, fieldName) .orElseThrow(() -> new NoValueException(fieldTag)); fieldCache.put(fieldName, tuple); return tuple; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ValueProvider.java index e2a73f95c..97fcb24f9 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ValueProvider.java @@ -1,6 +1,7 @@ package nl.jqno.equalsverifier.internal.reflection.instantiation; import java.util.Optional; +import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; @@ -15,12 +16,29 @@ */ public interface ValueProvider { /** - * Returns a tuple of two different prefabricated values of the specified type. + * Returns a tuple of prefabricated values of the specified type, or, if none exists, returns + * an empty Optional. * * @param The returned tuple will have this generic type. * @param tag A description of the desired type, including generic parameters. + * @param label Returns only the value assigned to the given label, or if label is null, + * returns the value that's not assigned to any label. * @return A tuple of two different values of the given type, or an empty Optional if none * could be found. */ - Optional> provide(TypeTag tag); + Optional> provide(TypeTag tag, String label); + + /** + * Returns a tuple of prefabricated values of the specified type, or, if none exists, throws a + * NoValueException. + * + * @param The returned tuple will have this generic type. + * @param tag A description of the desired type, including generic parameters. + * @return A tuple of two different values of the given type, or an empty Optional if none + * could be found. + * @throws NoValueException if no value could be found for the given tag. + */ + default Tuple provide(TypeTag tag) { + return this.provide(tag, null).orElseThrow(() -> new NoValueException(tag)); + } } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java index c886d6c4e..eaac61bd7 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java @@ -42,7 +42,7 @@ public VintageValueProvider(FactoryCache factoryCache, Objenesis objenesis) { /** {@inheritDoc} */ @Override - public Optional> provide(TypeTag tag) { + public Optional> provide(TypeTag tag, String label) { return Rethrow.rethrow(() -> Optional.of(giveTuple(tag))); } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java index a809b8821..cb8461f93 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java @@ -3,8 +3,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Optional; +import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; import org.junit.jupiter.api.Test; public class ChainedValueProviderTest { @@ -29,19 +31,28 @@ public class ChainedValueProviderTest { @Test public void returnsValueIfMatch() { sut = new ChainedValueProvider(intProvider); - assertEquals(1, sut.provide(INT).get().getRed()); + assertEquals(1, sut.provide(INT).getRed()); } @Test public void returnsEmptyIfNoMatch() { sut = new ChainedValueProvider(stringProvider); - assertEquals(Optional.empty(), sut.provide(INT)); + assertEquals(Optional.empty(), sut.provide(INT, null)); + } + + @Test + public void throwsExceptionIfNoMatch() { + sut = new ChainedValueProvider(stringProvider); + ExpectedException + .when(() -> sut.provide(INT)) + .assertThrows(NoValueException.class) + .assertDescriptionContains("Could not find a value for int"); } @Test public void skipsNonMatchingValue() { sut = new ChainedValueProvider(stringProvider, intProvider); - assertEquals(1, sut.provide(INT).get().getRed()); + assertEquals(1, sut.provide(INT).getRed()); assertEquals(1, stringProvider.called); assertEquals(1, intProvider.called); } @@ -55,7 +66,7 @@ public void returnsValueFromFirstMatch() { 1 ); sut = new ChainedValueProvider(intProvider, anotherIntProvider); - assertEquals(1, sut.provide(INT).get().getRed()); + assertEquals(1, sut.provide(INT).getRed()); assertEquals(1, intProvider.called); assertEquals(0, anotherIntProvider.called); } @@ -73,7 +84,7 @@ public SingleTypeValueProvider(Class type, X red, X blue, X redCopy) { @Override @SuppressWarnings("unchecked") - public Optional> provide(TypeTag tag) { + public Optional> provide(TypeTag tag, String label) { called++; if (tag.getType().equals(type)) { return Optional.of((Tuple) values); diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java index e62ebfd13..13f633f2c 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java @@ -208,7 +208,7 @@ public void noValueFound() { static class SubjectCreatorTestValueProvider implements ValueProvider { - public Optional> provide(TypeTag tag) { + public Optional> provide(TypeTag tag, String label) { if (int.class.equals(tag.getType())) { return Optional.of(Tuple.of(I_RED, I_BLUE, I_RED)); } @@ -222,7 +222,7 @@ public Optional> provide(TypeTag tag) { static class NoValueProvider implements ValueProvider { @Override - public Optional> provide(TypeTag tag) { + public Optional> provide(TypeTag tag, String label) { return Optional.empty(); } } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java index 03cde5c19..d90f85db9 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java @@ -46,11 +46,8 @@ public void sanityTestFactoryIncreasesStringLength() { @Test public void provide() { - Optional> actual = vp.provide(POINT_TAG); - assertEquals( - Tuple.of(new Point(42, 42), new Point(1337, 1337), new Point(42, 42)), - actual.get() - ); + Tuple actual = vp.provide(POINT_TAG); + assertEquals(Tuple.of(new Point(42, 42), new Point(1337, 1337), new Point(42, 42)), actual); } @Test @@ -248,7 +245,6 @@ private static final class StaticContainer { int regularInt = 3; } - @SuppressWarnings("unused") public static class Lazy { public static final Lazy X = new Lazy(1); From 18f71790e7e5f875bc3e1a1c761beb6204a344b7 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Mon, 7 Oct 2024 20:11:34 +0200 Subject: [PATCH 04/15] Adds PrefabValueProvider --- .../instantiation/PrefabValueProvider.java | 64 +++++++++++++++++++ .../PrefabValueProviderTest.java | 53 +++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java create mode 100644 equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java new file mode 100644 index 000000000..e15e75a27 --- /dev/null +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java @@ -0,0 +1,64 @@ +package nl.jqno.equalsverifier.internal.reflection.instantiation; + +import java.util.*; +import nl.jqno.equalsverifier.internal.reflection.Tuple; +import nl.jqno.equalsverifier.internal.reflection.TypeTag; + +/** + * Provider of prefabricated instances of classes that have been provided by the user. + */ +public class PrefabValueProvider implements ValueProvider { + + private final Map> cache = new HashMap<>(); + + /** {@inheritDoc} */ + @Override + @SuppressWarnings("unchecked") + public Optional> provide(TypeTag tag, String label) { + Key key = new Key(tag, label); + return Optional.ofNullable((Tuple) cache.get(key)); + } + + /** + * Registers a prefab value. + * + * @param tag The class of the prefabricated values. + * @param label The label that the prefabricated value is linked to, or null if the value is + * not assigned to any label. + * @param red An instance of {@code T}. + * @param blue Another instance of {@code T}, not equal to {@code red}. + * @param redCopy An instance of {@code T}, equal to {@code red} but preferably not the same + * instance. + * @param The type of the instances. + */ + public void register(TypeTag tag, String label, T red, T blue, T redCopy) { + Key key = new Key(tag, label); + Tuple value = Tuple.of(red, blue, redCopy); + cache.put(key, value); + } + + static final class Key { + + private final TypeTag tag; + private final String label; + + private Key(TypeTag tag, String label) { + this.tag = tag; + this.label = label; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Key)) { + return false; + } + Key other = (Key) obj; + return Objects.equals(tag, other.tag) && Objects.equals(label, other.label); + } + + @Override + public int hashCode() { + return Objects.hash(tag, label); + } + } +} diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java new file mode 100644 index 000000000..3bae911af --- /dev/null +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java @@ -0,0 +1,53 @@ +package nl.jqno.equalsverifier.internal.reflection.instantiation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Optional; +import nl.jqno.equalsverifier.EqualsVerifier; +import nl.jqno.equalsverifier.internal.reflection.Tuple; +import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import org.junit.jupiter.api.Test; + +public class PrefabValueProviderTest { + + private static final TypeTag INT = new TypeTag(int.class); + + private PrefabValueProvider sut = new PrefabValueProvider(); + + @Test + public void aRegisteredValueCanBeFound() { + sut.register(INT, null, 3, 2, 3); + assertEquals(Tuple.of(3, 2, 3), sut.provide(INT, null).get()); + } + + @Test + public void aValueRegisteredWithALabelCanBeFoundUnderThatLabel() { + sut.register(INT, "label", 3, 2, 3); + assertEquals(Tuple.of(3, 2, 3), sut.provide(INT, "label").get()); + } + + @Test + public void aValueRegisteredWithALabelCanNotBeFoundWithoutThatLabel() { + sut.register(INT, "label", 3, 2, 3); + assertEquals(Optional.empty(), sut.provide(INT, null)); + } + + @Test + public void aValueRegisteredWithoutALabelCanNotBeFoundWithALabel() { + sut.register(INT, null, 3, 2, 3); + assertEquals(Optional.empty(), sut.provide(INT, "label")); + } + + @Test + public void anUnregisteredValueCanNotBeFound() { + assertEquals(Optional.empty(), sut.provide(INT, null)); + } + + @Test + public void keyEqualsAndHashCode() { + EqualsVerifier + .forClass(PrefabValueProvider.Key.class) + .withPrefabValues(TypeTag.class, new TypeTag(Integer.class), new TypeTag(String.class)) + .verify(); + } +} From 682e220a2e7180511c7f8f454b2df64073a41ed9 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Fri, 11 Oct 2024 11:23:03 +0200 Subject: [PATCH 05/15] Introduces PrefabValueProvider --- .../ConfiguredEqualsVerifier.java | 22 +++++++- .../api/SingleTypeEqualsVerifierApi.java | 26 +++++++--- .../fieldchecks/ReflexivityFieldCheck.java | 9 ++-- .../internal/exceptions/NoValueException.java | 13 ++++- .../instantiation/PrefabValueProvider.java | 51 +++++++++++++++---- .../instantiation/SubjectCreator.java | 4 +- .../equalsverifier/internal/util/Context.java | 15 +++--- .../internal/util/PrefabValuesApi.java | 24 +++++---- .../operational/RecursionTest.java | 5 +- .../exceptions/NoValueExceptionTest.java | 14 ++++- .../PrefabValueProviderTest.java | 32 +++++++++--- .../instantiation/SubjectCreatorTest.java | 11 +--- 12 files changed, 159 insertions(+), 67 deletions(-) diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java index 5574cfb43..8d5b0bd04 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java @@ -11,6 +11,7 @@ import nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi; import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.PackageScanner; +import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; import nl.jqno.equalsverifier.internal.util.ListBuilders; import nl.jqno.equalsverifier.internal.util.PrefabValuesApi; import nl.jqno.equalsverifier.internal.util.Validations; @@ -21,24 +22,33 @@ public final class ConfiguredEqualsVerifier implements EqualsVerifierApi { private final EnumSet warningsToSuppress; private final FactoryCache factoryCache; + private final PrefabValueProvider prefabValueProvider; private boolean usingGetClass; private Function fieldnameToGetter; private final Objenesis objenesis = new ObjenesisStd(); /** Constructor. */ public ConfiguredEqualsVerifier() { - this(EnumSet.noneOf(Warning.class), new FactoryCache(), false, null); + this( + EnumSet.noneOf(Warning.class), + new FactoryCache(), + new PrefabValueProvider(), + false, + null + ); } /** Private constructor. For internal use only. */ private ConfiguredEqualsVerifier( EnumSet warningsToSuppress, FactoryCache factoryCache, + PrefabValueProvider prefabValueProvider, boolean usingGetClass, Function fieldnameToGetter ) { this.warningsToSuppress = warningsToSuppress; this.factoryCache = factoryCache; + this.prefabValueProvider = prefabValueProvider; this.usingGetClass = usingGetClass; this.fieldnameToGetter = fieldnameToGetter; } @@ -52,6 +62,7 @@ public ConfiguredEqualsVerifier copy() { return new ConfiguredEqualsVerifier( EnumSet.copyOf(warningsToSuppress), new FactoryCache().merge(factoryCache), + prefabValueProvider, usingGetClass, fieldnameToGetter ); @@ -67,7 +78,14 @@ public ConfiguredEqualsVerifier suppress(Warning... warnings) { /** {@inheritDoc} */ @Override public ConfiguredEqualsVerifier withPrefabValues(Class otherType, S red, S blue) { - PrefabValuesApi.addPrefabValues(factoryCache, objenesis, otherType, red, blue); + PrefabValuesApi.addPrefabValues( + prefabValueProvider, + factoryCache, + objenesis, + otherType, + red, + blue + ); return this; } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java index c09b94d68..1f62f5b7a 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java @@ -10,7 +10,7 @@ import nl.jqno.equalsverifier.internal.checkers.*; import nl.jqno.equalsverifier.internal.exceptions.MessagingException; import nl.jqno.equalsverifier.internal.reflection.FactoryCache; -import nl.jqno.equalsverifier.internal.reflection.FieldCache; +import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; import nl.jqno.equalsverifier.internal.util.*; import nl.jqno.equalsverifier.internal.util.Formatter; import org.objenesis.Objenesis; @@ -31,7 +31,7 @@ public class SingleTypeEqualsVerifierApi implements EqualsVerifierApi { private boolean hasRedefinedSuperclass = false; private Class redefinedSubclass = null; private FactoryCache factoryCache = new FactoryCache(); - private FieldCache fieldCache = new FieldCache(); + private PrefabValueProvider prefabValueProvider = new PrefabValueProvider(); private CachedHashCodeInitializer cachedHashCodeInitializer = CachedHashCodeInitializer.passthrough(); private Function fieldnameToGetter = null; @@ -121,7 +121,14 @@ public SingleTypeEqualsVerifierApi suppress(Warning... warnings) { /** {@inheritDoc} */ @Override public SingleTypeEqualsVerifierApi withPrefabValues(Class otherType, S red, S blue) { - PrefabValuesApi.addPrefabValues(factoryCache, objenesis, otherType, red, blue); + PrefabValuesApi.addPrefabValues( + prefabValueProvider, + factoryCache, + objenesis, + otherType, + red, + blue + ); return this; } @@ -143,7 +150,14 @@ public SingleTypeEqualsVerifierApi withPrefabValuesForField( S red, S blue ) { - PrefabValuesApi.addPrefabValuesForField(fieldCache, objenesis, type, fieldName, red, blue); + PrefabValuesApi.addPrefabValuesForField( + prefabValueProvider, + objenesis, + type, + fieldName, + red, + blue + ); withNonnullFields(fieldName); return this; } @@ -431,7 +445,7 @@ private void performVerification() { Validations.validateClassCanBeVerified(type); Configuration config = buildConfig(); - Context context = new Context<>(config, factoryCache, fieldCache, objenesis); + Context context = new Context<>(config, factoryCache, prefabValueProvider, objenesis); Validations.validateProcessedAnnotations( type, config.getAnnotationCache(), @@ -450,7 +464,7 @@ private Configuration buildConfig() { allExcludedFields, allIncludedFields, nonnullFields, - fieldCache.getFieldNames(), + prefabValueProvider.getFieldNames(), cachedHashCodeInitializer, hasRedefinedSuperclass, redefinedSubclass, diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java index 3d775008a..fd2d98f06 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/checkers/fieldchecks/ReflexivityFieldCheck.java @@ -7,6 +7,7 @@ import java.util.EnumSet; import java.util.Set; import nl.jqno.equalsverifier.Warning; +import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.*; import nl.jqno.equalsverifier.internal.reflection.annotations.AnnotationCache; import nl.jqno.equalsverifier.internal.reflection.annotations.SupportedAnnotations; @@ -25,7 +26,6 @@ public class ReflexivityFieldCheck implements FieldCheck { private final Set nonnullFields; private final Set prefabbedFields; private final AnnotationCache annotationCache; - private final FieldCache fieldCache; public ReflexivityFieldCheck(Context context) { this.subjectCreator = context.getSubjectCreator(); @@ -37,7 +37,6 @@ public ReflexivityFieldCheck(Context context) { this.nonnullFields = config.getNonnullFields(); this.prefabbedFields = config.getPrefabbedFields(); this.annotationCache = config.getAnnotationCache(); - this.fieldCache = context.getFieldCache(); } @Override @@ -80,9 +79,9 @@ private void checkValueReflexivity(FieldProbe probe) { Field field = probe.getField(); String fieldName = field.getName(); TypeTag tag = TypeTag.of(field, typeTag); - Tuple tuple = prefabbedFields.contains(fieldName) - ? fieldCache.get(fieldName) - : valueProvider.provide(tag); + Tuple tuple = valueProvider + .provide(tag, fieldName) + .orElseThrow(() -> new NoValueException(tag, fieldName)); Object left = subjectCreator.withFieldSetTo(field, tuple.getRed()); Object right = subjectCreator.withFieldSetTo(field, tuple.getRedCopy()); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/exceptions/NoValueException.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/exceptions/NoValueException.java index 182a71572..dcb3be89e 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/exceptions/NoValueException.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/exceptions/NoValueException.java @@ -7,14 +7,25 @@ public class NoValueException extends MessagingException { private final TypeTag tag; + private final String label; public NoValueException(TypeTag tag) { + this(tag, null); + } + + public NoValueException(TypeTag tag, String label) { super(); this.tag = tag; + this.label = label; } @Override public String getDescription() { - return "Could not find a value for " + tag + ". Please add prefab values for this type."; + return ( + "Could not find a value for " + + tag + + (label == null ? "" : " and label " + label) + + ". Please add prefab values for this type." + ); } } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java index e15e75a27..ee775d932 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java @@ -1,8 +1,11 @@ package nl.jqno.equalsverifier.internal.reflection.instantiation; import java.util.*; +import java.util.function.Supplier; +import java.util.stream.Collectors; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import nl.jqno.equalsverifier.internal.util.PrimitiveMappers; /** * Provider of prefabricated instances of classes that have been provided by the user. @@ -13,16 +16,31 @@ public class PrefabValueProvider implements ValueProvider { /** {@inheritDoc} */ @Override - @SuppressWarnings("unchecked") public Optional> provide(TypeTag tag, String label) { - Key key = new Key(tag, label); - return Optional.ofNullable((Tuple) cache.get(key)); + Class type = tag.getType(); + Tuple result = attempt(type, label); + Class boxed = PrimitiveMappers.PRIMITIVE_OBJECT_MAPPER.get(tag.getType()); + if (result == null && boxed != null) { + result = attempt(boxed, label); + } + if (result == null) { + result = attempt(type, null); + } + if (result == null && boxed != null) { + result = attempt(boxed, null); + } + return Optional.ofNullable(result); + } + + @SuppressWarnings("unchecked") + private Tuple attempt(Class type, String label) { + return (Tuple) cache.get(new Key(type, label)); } /** * Registers a prefab value. * - * @param tag The class of the prefabricated values. + * @param type The class of the prefabricated values. * @param label The label that the prefabricated value is linked to, or null if the value is * not assigned to any label. * @param red An instance of {@code T}. @@ -31,19 +49,23 @@ public Optional> provide(TypeTag tag, String label) { * instance. * @param The type of the instances. */ - public void register(TypeTag tag, String label, T red, T blue, T redCopy) { - Key key = new Key(tag, label); + public void register(Class type, String label, T red, T blue, T redCopy) { + Key key = new Key(type, label); Tuple value = Tuple.of(red, blue, redCopy); cache.put(key, value); } + public Set getFieldNames() { + return cache.keySet().stream().map(k -> k.label).collect(Collectors.toSet()); + } + static final class Key { - private final TypeTag tag; + private final Class type; private final String label; - private Key(TypeTag tag, String label) { - this.tag = tag; + private Key(Class type, String label) { + this.type = type; this.label = label; } @@ -53,12 +75,19 @@ public boolean equals(Object obj) { return false; } Key other = (Key) obj; - return Objects.equals(tag, other.tag) && Objects.equals(label, other.label); + return Objects.equals(type, other.type) && Objects.equals(label, other.label); } @Override public int hashCode() { - return Objects.hash(tag, label); + return Objects.hash(type, label); + } + + @Override + public String toString() { + return "Key: [" + type + "/" + label + "]"; } } + + static interface Value extends Supplier> {} } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java index d1477654a..356b433f3 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java @@ -31,14 +31,12 @@ public class SubjectCreator { * * @param config A configuration object. * @param valueProvider To provide values for the fields of the subject. - * @param fieldCache Prepared values for the fields of the subject. * @param objenesis Needed by InstanceCreator to instantiate non-record classes. */ @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "A cache is inherently mutable") public SubjectCreator( Configuration config, ValueProvider valueProvider, - FieldCache fieldCache, Objenesis objenesis ) { this.typeTag = config.getTypeTag(); @@ -46,7 +44,7 @@ public SubjectCreator( this.config = config; this.valueProvider = valueProvider; this.classProbe = new ClassProbe<>(type); - this.fieldCache = fieldCache; + this.fieldCache = new FieldCache(); this.objenesis = objenesis; this.instanceCreator = new InstanceCreator<>(classProbe, objenesis); } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java index 97f6d5429..f3f362ed7 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java @@ -10,7 +10,7 @@ public final class Context { private final Class type; private final Configuration configuration; private final ClassProbe classProbe; - private final FieldCache fieldCache; + private final PrefabValueProvider prefabValueProvider; private final SubjectCreator subjectCreator; private final ValueProvider valueProvider; @@ -22,19 +22,18 @@ public final class Context { public Context( Configuration configuration, FactoryCache factoryCache, - FieldCache fieldCache, + PrefabValueProvider prefabValueProvider, Objenesis objenesis ) { this.type = configuration.getType(); this.configuration = configuration; this.classProbe = new ClassProbe<>(configuration.getType()); - this.fieldCache = fieldCache; + this.prefabValueProvider = prefabValueProvider; FactoryCache cache = JavaApiPrefabValues.build().merge(factoryCache); ValueProvider vintage = new VintageValueProvider(cache, objenesis); - this.valueProvider = new ChainedValueProvider(vintage); - this.subjectCreator = - new SubjectCreator<>(configuration, valueProvider, fieldCache, objenesis); + this.valueProvider = new ChainedValueProvider(prefabValueProvider, vintage); + this.subjectCreator = new SubjectCreator<>(configuration, valueProvider, objenesis); } public Class getType() { @@ -50,8 +49,8 @@ public ClassProbe getClassProbe() { } @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "A cache is inherently mutable") - public FieldCache getFieldCache() { - return fieldCache; + public PrefabValueProvider getPrefabValueProvider() { + return prefabValueProvider; } @SuppressFBWarnings( diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java index 8bc5c9c0c..9b5a43c00 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java @@ -5,9 +5,8 @@ import nl.jqno.equalsverifier.Func.Func1; import nl.jqno.equalsverifier.Func.Func2; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; -import nl.jqno.equalsverifier.internal.reflection.FieldCache; -import nl.jqno.equalsverifier.internal.reflection.Tuple; +import nl.jqno.equalsverifier.internal.reflection.*; +import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; import nl.jqno.equalsverifier.internal.reflection.vintage.ObjectAccessor; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.PrefabValueFactory; import org.objenesis.Objenesis; @@ -17,6 +16,7 @@ public final class PrefabValuesApi { private PrefabValuesApi() {} public static void addPrefabValues( + PrefabValueProvider provider, FactoryCache factoryCache, Objenesis objenesis, Class otherType, @@ -26,13 +26,16 @@ public static void addPrefabValues( Validations.validateRedAndBluePrefabValues(otherType, red, blue); if (red.getClass().isArray()) { + provider.register(otherType, null, red, blue, red); factoryCache.put(otherType, values(red, blue, red)); } else { try { T redCopy = ObjectAccessor.of(red).copy(objenesis); + provider.register(otherType, null, red, blue, redCopy); factoryCache.put(otherType, values(red, blue, redCopy)); } catch (RuntimeException ignored) { /* specifically, on Java 9+: InacessibleObjectException */ + provider.register(otherType, null, red, blue, red); factoryCache.put(otherType, values(red, blue, red)); } } @@ -40,25 +43,26 @@ public static void addPrefabValues( @SuppressWarnings("unchecked") public static void addPrefabValuesForField( - FieldCache fieldCache, + PrefabValueProvider provider, Objenesis objenesis, - Class type, + Class enclosingType, String fieldName, T red, T blue ) { Validations.validateRedAndBluePrefabValues((Class) red.getClass(), red, blue); - Validations.validateFieldTypeMatches(type, fieldName, red.getClass()); + Validations.validateFieldTypeMatches(enclosingType, fieldName, red.getClass()); - if (red.getClass().isArray()) { - fieldCache.put(fieldName, new Tuple<>(red, blue, red)); + Class type = red.getClass(); + if (type.isArray()) { + provider.register(type, fieldName, red, blue, red); } else { try { T redCopy = ObjectAccessor.of(red).copy(objenesis); - fieldCache.put(fieldName, new Tuple<>(red, blue, redCopy)); + provider.register(type, fieldName, red, blue, redCopy); } catch (RuntimeException ignored) { /* specifically, on Java 9+: InacessibleObjectException */ - fieldCache.put(fieldName, new Tuple<>(red, blue, red)); + provider.register(type, fieldName, red, blue, red); } } } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/RecursionTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/RecursionTest.java index 952b32f0f..3b0a0990d 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/RecursionTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/RecursionTest.java @@ -4,10 +4,7 @@ import static nl.jqno.equalsverifier.internal.testhelpers.Util.defaultHashCode; import com.google.common.collect.ImmutableList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; +import java.util.*; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/exceptions/NoValueExceptionTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/exceptions/NoValueExceptionTest.java index 58259cc49..5eec5c2d8 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/exceptions/NoValueExceptionTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/exceptions/NoValueExceptionTest.java @@ -1,5 +1,6 @@ package nl.jqno.equalsverifier.internal.exceptions; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import nl.jqno.equalsverifier.internal.reflection.TypeTag; @@ -8,9 +9,18 @@ public class NoValueExceptionTest { @Test - public void description() { + public void descriptionWithoutLabel() { TypeTag tag = new TypeTag(String.class); - NoValueException e = new NoValueException(tag); + NoValueException e = new NoValueException(tag, null); assertTrue(e.getDescription().contains("String")); + assertFalse(e.getDescription().contains("label")); + } + + @Test + public void descriptionWithLabel() { + TypeTag tag = new TypeTag(String.class); + NoValueException e = new NoValueException(tag, "lbl"); + assertTrue(e.getDescription().contains("String")); + assertTrue(e.getDescription().contains("label lbl")); } } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java index 3bae911af..ce4b31204 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java @@ -11,31 +11,51 @@ public class PrefabValueProviderTest { private static final TypeTag INT = new TypeTag(int.class); + private static final TypeTag INTEGER = new TypeTag(Integer.class); private PrefabValueProvider sut = new PrefabValueProvider(); @Test public void aRegisteredValueCanBeFound() { - sut.register(INT, null, 3, 2, 3); + sut.register(INT.getType(), null, 3, 2, 3); assertEquals(Tuple.of(3, 2, 3), sut.provide(INT, null).get()); } @Test public void aValueRegisteredWithALabelCanBeFoundUnderThatLabel() { - sut.register(INT, "label", 3, 2, 3); + sut.register(INT.getType(), "label", 3, 2, 3); assertEquals(Tuple.of(3, 2, 3), sut.provide(INT, "label").get()); } @Test public void aValueRegisteredWithALabelCanNotBeFoundWithoutThatLabel() { - sut.register(INT, "label", 3, 2, 3); + sut.register(INT.getType(), "label", 3, 2, 3); assertEquals(Optional.empty(), sut.provide(INT, null)); } @Test - public void aValueRegisteredWithoutALabelCanNotBeFoundWithALabel() { - sut.register(INT, null, 3, 2, 3); - assertEquals(Optional.empty(), sut.provide(INT, "label")); + public void aQueryWithLabelFallsBackToRegisteredValueWithoutLabel() { + sut.register(INT.getType(), null, 3, 2, 3); + assertEquals(Tuple.of(3, 2, 3), sut.provide(INT, "label").get()); + } + + @Test + public void aQueryWithLabelPrefersRegisteredValueWithThatLabel() { + sut.register(INT.getType(), null, 3, 2, 3); + sut.register(INT.getType(), "label", 4, 3, 4); + assertEquals(Tuple.of(4, 3, 4), sut.provide(INT, "label").get()); + } + + @Test + public void itFallsBackToBoxedTypeWithoutLabel() { + sut.register(INTEGER.getType(), null, 3, 2, 3); + assertEquals(Tuple.of(3, 2, 3), sut.provide(INT, null).get()); + } + + @Test + public void itFallsBackToBoxedTypeWithLabel() { + sut.register(INTEGER.getType(), "label", 3, 2, 3); + assertEquals(Tuple.of(3, 2, 3), sut.provide(INT, "label").get()); } @Test diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java index 13f633f2c..ee51ae639 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreatorTest.java @@ -7,7 +7,6 @@ import java.util.Objects; import java.util.Optional; import nl.jqno.equalsverifier.internal.exceptions.NoValueException; -import nl.jqno.equalsverifier.internal.reflection.FieldCache; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; @@ -29,14 +28,8 @@ public class SubjectCreatorTest { SomeClass.class ); private ValueProvider valueProvider = new SubjectCreatorTestValueProvider(); - private FieldCache fieldCache = new FieldCache(); private Objenesis objenesis = new ObjenesisStd(); - private SubjectCreator sut = new SubjectCreator<>( - config, - valueProvider, - fieldCache, - objenesis - ); + private SubjectCreator sut = new SubjectCreator<>(config, valueProvider, objenesis); private Field fieldX; private Field fieldI; @@ -196,7 +189,7 @@ public void copyIntoSubclass() { @Test public void noValueFound() { - sut = new SubjectCreator<>(config, new NoValueProvider(), fieldCache, objenesis); + sut = new SubjectCreator<>(config, new NoValueProvider(), objenesis); ExpectedException .when(() -> sut.plain()) From 735996e97b0fa5367a9cdbe707ae7d18cbbdd58e Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Sat, 12 Oct 2024 20:14:44 +0200 Subject: [PATCH 06/15] Lets VintageValueProvider use other ValueProviders --- .../RecordObjectAccessorScramblingTest.java | 4 +++- .../vintage/RecordObjectAccessorTest.java | 7 ++++++- .../instantiation/ChainedValueProvider.java | 6 +++--- .../instantiation/VintageValueProvider.java | 14 ++++++++++++- .../testhelpers/EmptyValueProvider.java | 16 ++++++++++++++ .../equalsverifier/internal/util/Context.java | 19 ++++++++++++++--- .../ChainedValueProviderTest.java | 21 +++++++++++++------ .../VintageValueProviderCreatorTest.java | 16 +++++++++----- .../VintageValueProviderTest.java | 15 +++++++------ .../reflection/vintage/ClassAccessorTest.java | 7 +++++-- .../InPlaceObjectAccessorScramblingTest.java | 4 +++- .../factories/FallbackFactoryTest.java | 4 +++- .../factories/MapFactoryTest.java | 8 ++++++- .../factories/SimpleGenericFactoryTest.java | 8 ++++++- .../JavaFxFactoryProviderTest.java | 8 ++++++- 15 files changed, 124 insertions(+), 33 deletions(-) create mode 100644 equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/testhelpers/EmptyValueProvider.java diff --git a/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorScramblingTest.java b/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorScramblingTest.java index 830186672..d0cb07925 100644 --- a/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorScramblingTest.java +++ b/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorScramblingTest.java @@ -10,6 +10,7 @@ import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.objenesis.ObjenesisStd; @@ -23,7 +24,8 @@ public class RecordObjectAccessorScramblingTest { @BeforeEach public void setup() throws Exception { factoryCache = JavaApiPrefabValues.build(); - valueProvider = new VintageValueProvider(factoryCache, new ObjenesisStd()); + valueProvider = + new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, new ObjenesisStd()); } @Test diff --git a/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorTest.java b/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorTest.java index dbcbb904f..9c3b3cb7f 100644 --- a/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorTest.java +++ b/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorTest.java @@ -11,6 +11,7 @@ import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -74,7 +75,11 @@ public void fail_whenConstructorThrowsOnSomethingElse() { .of(OtherThrowingConstructorRecord.class, objenesis) .instantiate(); - VintageValueProvider vp = new VintageValueProvider(JavaApiPrefabValues.build(), objenesis); + VintageValueProvider vp = new VintageValueProvider( + EmptyValueProvider.INSTANCE, + JavaApiPrefabValues.build(), + objenesis + ); ExpectedException .when(() -> accessorFor(instance).scramble(vp, TypeTag.NULL, EMPTY_TYPE_STACK)) .assertThrows(ReflectionException.class) diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java index f02b17dc1..e7857fa12 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java @@ -1,6 +1,6 @@ package nl.jqno.equalsverifier.internal.reflection.instantiation; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import nl.jqno.equalsverifier.internal.reflection.Tuple; @@ -17,8 +17,8 @@ public class ChainedValueProvider implements ValueProvider { * * @param providers ValueProviders to delegate to when providing a value. */ - public ChainedValueProvider(ValueProvider... providers) { - this.providers = Arrays.asList(providers); + public ChainedValueProvider(List providers) { + this.providers = new ArrayList<>(providers); } /** {@inheritDoc} */ diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java index eaac61bd7..67ed4e0fa 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java @@ -25,17 +25,24 @@ public class VintageValueProvider implements ValueProvider { // I'd like to remove this, but that affects recursion detection it a way I can't yet explain private final Map> valueCache = new HashMap<>(); + private final ValueProvider valueProvider; private final FactoryCache factoryCache; private final PrefabValueFactory fallbackFactory; /** * Constructor. * + * @param valueProvider Will be used to look up values before they are created. * @param factoryCache The factories that can be used to create values. * @param objenesis To instantiate non-record classes. */ @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "A cache is inherently mutable.") - public VintageValueProvider(FactoryCache factoryCache, Objenesis objenesis) { + public VintageValueProvider( + ValueProvider valueProvider, + FactoryCache factoryCache, + Objenesis objenesis + ) { + this.valueProvider = valueProvider; this.factoryCache = factoryCache; this.fallbackFactory = new FallbackFactory<>(objenesis); } @@ -167,6 +174,11 @@ private Tuple createTuple(TypeTag tag, LinkedHashSet typeStack) throw new RecursionException(typeStack); } + Optional> provided = valueProvider.provide(tag, null); + if (provided.isPresent()) { + return provided.get(); + } + Class type = tag.getType(); if (factoryCache.contains(type)) { PrefabValueFactory factory = factoryCache.get(type); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/testhelpers/EmptyValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/testhelpers/EmptyValueProvider.java new file mode 100644 index 000000000..418e3af9d --- /dev/null +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/testhelpers/EmptyValueProvider.java @@ -0,0 +1,16 @@ +package nl.jqno.equalsverifier.internal.testhelpers; + +import java.util.Optional; +import nl.jqno.equalsverifier.internal.reflection.Tuple; +import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider; + +public class EmptyValueProvider implements ValueProvider { + + public static final ValueProvider INSTANCE = new EmptyValueProvider(); + + @Override + public Optional> provide(TypeTag tag, String label) { + return Optional.empty(); + } +} diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java index f3f362ed7..045312728 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java @@ -1,7 +1,11 @@ package nl.jqno.equalsverifier.internal.util; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import nl.jqno.equalsverifier.internal.reflection.*; +import java.util.ArrayList; +import java.util.List; +import nl.jqno.equalsverifier.internal.reflection.ClassProbe; +import nl.jqno.equalsverifier.internal.reflection.FactoryCache; +import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; import nl.jqno.equalsverifier.internal.reflection.instantiation.*; import org.objenesis.Objenesis; @@ -30,9 +34,18 @@ public Context( this.classProbe = new ClassProbe<>(configuration.getType()); this.prefabValueProvider = prefabValueProvider; + List providers = new ArrayList<>(); + providers.add(prefabValueProvider); FactoryCache cache = JavaApiPrefabValues.build().merge(factoryCache); - ValueProvider vintage = new VintageValueProvider(cache, objenesis); - this.valueProvider = new ChainedValueProvider(prefabValueProvider, vintage); + ValueProvider vintage = new VintageValueProvider( + new ChainedValueProvider(providers), + cache, + objenesis + ); + + providers.add(vintage); + + this.valueProvider = new ChainedValueProvider(providers); this.subjectCreator = new SubjectCreator<>(configuration, valueProvider, objenesis); } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java index cb8461f93..da063cb89 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.Optional; +import java.util.*; import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; @@ -30,19 +30,19 @@ public class ChainedValueProviderTest { @Test public void returnsValueIfMatch() { - sut = new ChainedValueProvider(intProvider); + sut = new ChainedValueProvider(Arrays.asList(intProvider)); assertEquals(1, sut.provide(INT).getRed()); } @Test public void returnsEmptyIfNoMatch() { - sut = new ChainedValueProvider(stringProvider); + sut = new ChainedValueProvider(Arrays.asList(stringProvider)); assertEquals(Optional.empty(), sut.provide(INT, null)); } @Test public void throwsExceptionIfNoMatch() { - sut = new ChainedValueProvider(stringProvider); + sut = new ChainedValueProvider(Arrays.asList(stringProvider)); ExpectedException .when(() -> sut.provide(INT)) .assertThrows(NoValueException.class) @@ -51,7 +51,7 @@ public void throwsExceptionIfNoMatch() { @Test public void skipsNonMatchingValue() { - sut = new ChainedValueProvider(stringProvider, intProvider); + sut = new ChainedValueProvider(Arrays.asList(stringProvider, intProvider)); assertEquals(1, sut.provide(INT).getRed()); assertEquals(1, stringProvider.called); assertEquals(1, intProvider.called); @@ -65,12 +65,21 @@ public void returnsValueFromFirstMatch() { 2, 1 ); - sut = new ChainedValueProvider(intProvider, anotherIntProvider); + sut = new ChainedValueProvider(Arrays.asList(intProvider, anotherIntProvider)); assertEquals(1, sut.provide(INT).getRed()); assertEquals(1, intProvider.called); assertEquals(0, anotherIntProvider.called); } + @Test + public void makesDefensiveCopy() { + List providers = new ArrayList<>(); + providers.add(stringProvider); + sut = new ChainedValueProvider(providers); + providers.add(intProvider); + assertEquals(Optional.empty(), sut.provide(INT, null)); + } + static class SingleTypeValueProvider implements ValueProvider { private final Class type; diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderCreatorTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderCreatorTest.java index 45bba76bc..11a5df39c 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderCreatorTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderCreatorTest.java @@ -6,6 +6,7 @@ import nl.jqno.equalsverifier.internal.exceptions.RecursionException; import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; import nl.jqno.equalsverifier.testhelpers.FactoryCacheFactory; import nl.jqno.equalsverifier.testhelpers.types.Point; @@ -37,7 +38,8 @@ public class VintageValueProviderCreatorTest { public void setup() { objenesis = new ObjenesisStd(); factoryCache = FactoryCacheFactory.withPrimitiveFactories(); - valueProvider = new VintageValueProvider(factoryCache, objenesis); + valueProvider = + new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); } @Test @@ -77,7 +79,8 @@ public void createEmptyEnum() { @Test public void oneStepRecursiveType() { factoryCache.put(Node.class, values(new Node(), new Node(), new Node())); - valueProvider = new VintageValueProvider(factoryCache, objenesis); + valueProvider = + new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); valueProvider.giveRed(NODE_TAG); } @@ -94,7 +97,8 @@ public void oneStepRecursiveArrayType() { NodeArray.class, values(new NodeArray(), new NodeArray(), new NodeArray()) ); - valueProvider = new VintageValueProvider(factoryCache, objenesis); + valueProvider = + new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); valueProvider.giveRed(NODE_ARRAY_TAG); } @@ -111,7 +115,8 @@ public void addTwoStepRecursiveType() { TwoStepNodeB.class, values(new TwoStepNodeB(), new TwoStepNodeB(), new TwoStepNodeB()) ); - valueProvider = new VintageValueProvider(factoryCache, objenesis); + valueProvider = + new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); valueProvider.giveRed(TWOSTEP_NODE_A_TAG); } @@ -128,7 +133,8 @@ public void twoStepRecursiveArrayType() { TwoStepNodeArrayB.class, values(new TwoStepNodeArrayB(), new TwoStepNodeArrayB(), new TwoStepNodeArrayB()) ); - valueProvider = new VintageValueProvider(factoryCache, objenesis); + valueProvider = + new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); valueProvider.giveRed(TWOSTEP_NODE_ARRAY_A_TAG); } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java index d90f85db9..b8a43308a 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java @@ -7,11 +7,14 @@ import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.fail; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.PrefabValueFactory; +import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.testhelpers.types.Point; import nl.jqno.equalsverifier.testhelpers.types.ThrowingInitializer; import org.junit.jupiter.api.BeforeEach; @@ -33,7 +36,7 @@ public class VintageValueProviderTest { public void setUp() { factoryCache.put(String.class, new AppendingStringTestFactory()); factoryCache.put(int.class, values(42, 1337, 42)); - vp = new VintageValueProvider(factoryCache, objenesis); + vp = new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); } @Test @@ -114,7 +117,7 @@ public void fallbackDoesNotAffectStaticFields() { @Test public void stringListIsSeparateFromIntegerList() { factoryCache.put(List.class, new ListTestFactory()); - vp = new VintageValueProvider(factoryCache, objenesis); + vp = new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); List strings = vp.giveRed(new TypeTag(List.class, STRING_TAG)); List ints = vp.giveRed(new TypeTag(List.class, INT_TAG)); @@ -131,7 +134,7 @@ public void addingNullDoesntBreakAnything() { @Test public void addingATypeTwiceOverrulesTheExistingOne() { factoryCache.put(int.class, values(-1, -2, -1)); - vp = new VintageValueProvider(factoryCache, objenesis); + vp = new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); assertEquals(-1, (int) vp.giveRed(INT_TAG)); assertEquals(-2, (int) vp.giveBlue(INT_TAG)); } @@ -140,7 +143,7 @@ public void addingATypeTwiceOverrulesTheExistingOne() { public void addLazyFactoryWorks() { TypeTag lazyTag = new TypeTag(Lazy.class); factoryCache.put(Lazy.class.getName(), values(Lazy.X, Lazy.Y, Lazy.X)); - vp = new VintageValueProvider(factoryCache, objenesis); + vp = new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); assertEquals(Lazy.X, vp.giveRed(lazyTag)); assertEquals(Lazy.Y, vp.giveBlue(lazyTag)); assertEquals(Lazy.X, vp.giveRedCopy(lazyTag)); @@ -156,7 +159,7 @@ public void addLazyFactoryIsLazy() { (t, p, ts) -> Tuple.of(ThrowingInitializer.X, ThrowingInitializer.Y, ThrowingInitializer.X) ); - vp = new VintageValueProvider(factoryCache, objenesis); + vp = new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); // Should throw, because `giveRed` does instantiate objects: try { diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/ClassAccessorTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/ClassAccessorTest.java index 3fbfcb6cb..349cd5c78 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/ClassAccessorTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/ClassAccessorTest.java @@ -10,6 +10,7 @@ import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.testhelpers.types.PointContainer; import nl.jqno.equalsverifier.testhelpers.types.RecursiveTypeHelper.TwoStepNodeA; import nl.jqno.equalsverifier.testhelpers.types.RecursiveTypeHelper.TwoStepNodeB; @@ -32,7 +33,8 @@ public void setup() { empty = new LinkedHashSet<>(); objenesis = new ObjenesisStd(); factoryCache = JavaApiPrefabValues.build(); - valueProvider = new VintageValueProvider(factoryCache, objenesis); + valueProvider = + new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); pointContainerAccessor = ClassAccessor.of(PointContainer.class, valueProvider, objenesis); } @@ -123,7 +125,8 @@ public void instantiateRecursiveTypeUsingPrefabValue() { TwoStepNodeB.class, values(new TwoStepNodeB(), new TwoStepNodeB(), new TwoStepNodeB()) ); - valueProvider = new VintageValueProvider(factoryCache, objenesis); + valueProvider = + new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); ClassAccessor .of(TwoStepNodeA.class, valueProvider, objenesis) .getRedObject(TypeTag.NULL, empty); diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/InPlaceObjectAccessorScramblingTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/InPlaceObjectAccessorScramblingTest.java index a6775ab59..3c04a23ac 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/InPlaceObjectAccessorScramblingTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/InPlaceObjectAccessorScramblingTest.java @@ -12,6 +12,7 @@ import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; import nl.jqno.equalsverifier.testhelpers.types.Point; import nl.jqno.equalsverifier.testhelpers.types.Point3D; @@ -34,7 +35,8 @@ public void setup() { FactoryCache factoryCache = JavaApiPrefabValues.build(); factoryCache.put(Point.class, values(new Point(1, 2), new Point(2, 3), new Point(1, 2))); objenesis = new ObjenesisStd(); - valueProviderTest = new VintageValueProvider(factoryCache, objenesis); + valueProviderTest = + new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); } @Test diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/FallbackFactoryTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/FallbackFactoryTest.java index ff91709c4..af2874c49 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/FallbackFactoryTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/FallbackFactoryTest.java @@ -12,6 +12,7 @@ import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; import nl.jqno.equalsverifier.testhelpers.types.RecursiveTypeHelper.Node; import nl.jqno.equalsverifier.testhelpers.types.RecursiveTypeHelper.NodeArray; @@ -36,7 +37,8 @@ public void setUp() { factory = new FallbackFactory<>(objenesis); FactoryCache factoryCache = new FactoryCache(); factoryCache.put(int.class, values(42, 1337, 42)); - valueProvider = new VintageValueProvider(factoryCache, objenesis); + valueProvider = + new VintageValueProvider(EmptyValueProvider.INSTANCE, factoryCache, objenesis); typeStack = new LinkedHashSet<>(); } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/MapFactoryTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/MapFactoryTest.java index afd23e9d7..f9e4a1bf0 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/MapFactoryTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/MapFactoryTest.java @@ -9,6 +9,7 @@ import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.testhelpers.types.TypeHelper.OneElementEnum; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -49,7 +50,12 @@ public class MapFactoryTest { @BeforeEach public void setUp() { - valueProvider = new VintageValueProvider(JavaApiPrefabValues.build(), new ObjenesisStd()); + valueProvider = + new VintageValueProvider( + EmptyValueProvider.INSTANCE, + JavaApiPrefabValues.build(), + new ObjenesisStd() + ); red = valueProvider.giveRed(STRING_TYPETAG); blue = valueProvider.giveBlue(STRING_TYPETAG); redObject = valueProvider.giveRed(OBJECT_TYPETAG); diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/SimpleGenericFactoryTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/SimpleGenericFactoryTest.java index ff5f32d6e..4928a789f 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/SimpleGenericFactoryTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/SimpleGenericFactoryTest.java @@ -8,6 +8,7 @@ import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.testhelpers.types.Pair; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -51,7 +52,12 @@ public class SimpleGenericFactoryTest { @BeforeEach public void setUp() { - valueProvider = new VintageValueProvider(JavaApiPrefabValues.build(), new ObjenesisStd()); + valueProvider = + new VintageValueProvider( + EmptyValueProvider.INSTANCE, + JavaApiPrefabValues.build(), + new ObjenesisStd() + ); redString = valueProvider.giveRed(STRING_TYPETAG); blueString = valueProvider.giveBlue(STRING_TYPETAG); redInt = valueProvider.giveRed(INTEGER_TYPETAG); diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaFxFactoryProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaFxFactoryProviderTest.java index 06681caeb..b5ded91fa 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaFxFactoryProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaFxFactoryProviderTest.java @@ -11,6 +11,7 @@ import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.PrefabValueFactory; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factoryproviders.JavaFxFactoryProvider.PropertyFactory; +import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.testhelpers.types.Point; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -23,7 +24,12 @@ public class JavaFxFactoryProviderTest { @BeforeEach public void setUp() { - valueProvider = new VintageValueProvider(JavaApiPrefabValues.build(), new ObjenesisStd()); + valueProvider = + new VintageValueProvider( + EmptyValueProvider.INSTANCE, + JavaApiPrefabValues.build(), + new ObjenesisStd() + ); } @Test From 863313f2cd5a05f112ff88b051f320cc1b8d04e9 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Tue, 15 Oct 2024 19:55:05 +0200 Subject: [PATCH 07/15] Removes unused getter and field --- .../java/nl/jqno/equalsverifier/internal/util/Context.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java index 045312728..94a147828 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java @@ -14,7 +14,6 @@ public final class Context { private final Class type; private final Configuration configuration; private final ClassProbe classProbe; - private final PrefabValueProvider prefabValueProvider; private final SubjectCreator subjectCreator; private final ValueProvider valueProvider; @@ -32,7 +31,6 @@ public Context( this.type = configuration.getType(); this.configuration = configuration; this.classProbe = new ClassProbe<>(configuration.getType()); - this.prefabValueProvider = prefabValueProvider; List providers = new ArrayList<>(); providers.add(prefabValueProvider); @@ -61,11 +59,6 @@ public ClassProbe getClassProbe() { return classProbe; } - @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "A cache is inherently mutable") - public PrefabValueProvider getPrefabValueProvider() { - return prefabValueProvider; - } - @SuppressFBWarnings( value = "EI_EXPOSE_REP", justification = "VintageValueProvider can use a mutable cache." From 89d940f63ddbab0de4481f3f6723e398661b0c57 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Wed, 16 Oct 2024 10:35:57 +0200 Subject: [PATCH 08/15] Small refactoring in ChainedValueProvider to providers available at the right time --- .../instantiation/ChainedValueProvider.java | 21 +++++++++---- .../equalsverifier/internal/util/Context.java | 30 +++++++++++-------- .../ChainedValueProviderTest.java | 29 +++++++++++------- 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java index e7857fa12..f2c32e770 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import nl.jqno.equalsverifier.internal.exceptions.EqualsVerifierInternalBugException; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; @@ -11,14 +12,24 @@ */ public class ChainedValueProvider implements ValueProvider { - private final List providers; + private boolean locked = false; + private final List providers = new ArrayList<>(); - /** Constructor. + /** + * Adds providers to the chain, so they can be delegated to when providing a value. * - * @param providers ValueProviders to delegate to when providing a value. + * @param valueProviders ValueProviders to add to the chain. */ - public ChainedValueProvider(List providers) { - this.providers = new ArrayList<>(providers); + public void register(ValueProvider... valueProviders) { + if (locked) { + throw new EqualsVerifierInternalBugException( + "Provider is locked; can't add any new ones." + ); + } + for (ValueProvider p : valueProviders) { + providers.add(p); + } + locked = true; } /** {@inheritDoc} */ diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java index 94a147828..32fa2be9b 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java @@ -1,8 +1,6 @@ package nl.jqno.equalsverifier.internal.util; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import java.util.ArrayList; -import java.util.List; import nl.jqno.equalsverifier.internal.reflection.ClassProbe; import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; @@ -15,8 +13,8 @@ public final class Context { private final Configuration configuration; private final ClassProbe classProbe; - private final SubjectCreator subjectCreator; private final ValueProvider valueProvider; + private final SubjectCreator subjectCreator; @SuppressFBWarnings( value = "EI_EXPOSE_REP2", @@ -32,19 +30,25 @@ public Context( this.configuration = configuration; this.classProbe = new ClassProbe<>(configuration.getType()); - List providers = new ArrayList<>(); - providers.add(prefabValueProvider); + this.valueProvider = configureValueProviders(factoryCache, prefabValueProvider, objenesis); + this.subjectCreator = new SubjectCreator<>(configuration, valueProvider, objenesis); + } + + private static ValueProvider configureValueProviders( + FactoryCache factoryCache, + PrefabValueProvider prefabValueProvider, + Objenesis objenesis + ) { + ChainedValueProvider mainChain = new ChainedValueProvider(); + ChainedValueProvider vintageChain = new ChainedValueProvider(); + FactoryCache cache = JavaApiPrefabValues.build().merge(factoryCache); - ValueProvider vintage = new VintageValueProvider( - new ChainedValueProvider(providers), - cache, - objenesis - ); + ValueProvider vintage = new VintageValueProvider(vintageChain, cache, objenesis); - providers.add(vintage); + mainChain.register(prefabValueProvider, vintage); + vintageChain.register(prefabValueProvider); - this.valueProvider = new ChainedValueProvider(providers); - this.subjectCreator = new SubjectCreator<>(configuration, valueProvider, objenesis); + return mainChain; } public Class getType() { diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java index da063cb89..ffed6dcf5 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.*; +import nl.jqno.equalsverifier.internal.exceptions.EqualsVerifierInternalBugException; import nl.jqno.equalsverifier.internal.exceptions.NoValueException; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; @@ -26,23 +27,23 @@ public class ChainedValueProviderTest { "a" ); - private ChainedValueProvider sut; + private ChainedValueProvider sut = new ChainedValueProvider(); @Test public void returnsValueIfMatch() { - sut = new ChainedValueProvider(Arrays.asList(intProvider)); + sut.register(intProvider); assertEquals(1, sut.provide(INT).getRed()); } @Test public void returnsEmptyIfNoMatch() { - sut = new ChainedValueProvider(Arrays.asList(stringProvider)); + sut.register(stringProvider); assertEquals(Optional.empty(), sut.provide(INT, null)); } @Test public void throwsExceptionIfNoMatch() { - sut = new ChainedValueProvider(Arrays.asList(stringProvider)); + sut.register(stringProvider); ExpectedException .when(() -> sut.provide(INT)) .assertThrows(NoValueException.class) @@ -51,7 +52,7 @@ public void throwsExceptionIfNoMatch() { @Test public void skipsNonMatchingValue() { - sut = new ChainedValueProvider(Arrays.asList(stringProvider, intProvider)); + sut.register(stringProvider, intProvider); assertEquals(1, sut.provide(INT).getRed()); assertEquals(1, stringProvider.called); assertEquals(1, intProvider.called); @@ -65,7 +66,7 @@ public void returnsValueFromFirstMatch() { 2, 1 ); - sut = new ChainedValueProvider(Arrays.asList(intProvider, anotherIntProvider)); + sut.register(intProvider, anotherIntProvider); assertEquals(1, sut.provide(INT).getRed()); assertEquals(1, intProvider.called); assertEquals(0, anotherIntProvider.called); @@ -73,13 +74,21 @@ public void returnsValueFromFirstMatch() { @Test public void makesDefensiveCopy() { - List providers = new ArrayList<>(); - providers.add(stringProvider); - sut = new ChainedValueProvider(providers); - providers.add(intProvider); + ValueProvider[] providers = { stringProvider }; + sut.register(providers); + providers[0] = intProvider; assertEquals(Optional.empty(), sut.provide(INT, null)); } + @Test + public void locksAfterFirstAssignment() { + sut.register(intProvider); + ExpectedException + .when(() -> sut.register(stringProvider)) + .assertThrows(EqualsVerifierInternalBugException.class) + .assertMessageContains("Provider is locked"); + } + static class SingleTypeValueProvider implements ValueProvider { private final Class type; From 7f12e10ab6faa09b1c8c8b50af7ad5afc0605001 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Wed, 16 Oct 2024 19:35:58 +0200 Subject: [PATCH 09/15] Improves type-safety of PrefabValueProvider --- .../reflection/instantiation/PrefabValueProvider.java | 2 +- .../jqno/equalsverifier/internal/util/PrefabValuesApi.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java index ee775d932..8c91546f9 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java @@ -49,7 +49,7 @@ private Tuple attempt(Class type, String label) { * instance. * @param The type of the instances. */ - public void register(Class type, String label, T red, T blue, T redCopy) { + public void register(Class type, String label, T red, T blue, T redCopy) { Key key = new Key(type, label); Tuple value = Tuple.of(red, blue, redCopy); cache.put(key, value); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java index 9b5a43c00..f0587c0eb 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java @@ -50,10 +50,11 @@ public static void addPrefabValuesForField( T red, T blue ) { - Validations.validateRedAndBluePrefabValues((Class) red.getClass(), red, blue); + Class type = (Class) red.getClass(); + + Validations.validateRedAndBluePrefabValues(type, red, blue); Validations.validateFieldTypeMatches(enclosingType, fieldName, red.getClass()); - Class type = red.getClass(); if (type.isArray()) { provider.register(type, fieldName, red, blue, red); } else { From 94e8e2561bb06bab83a93319e8bbec1b7cff9977 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Fri, 18 Oct 2024 11:19:30 +0200 Subject: [PATCH 10/15] Adds GenericPrefabValueProvider --- .../ConfiguredEqualsVerifier.java | 14 +- .../api/SingleTypeEqualsVerifierApi.java | 29 +++- .../GenericPrefabValueProvider.java | 119 ++++++++++++++ .../reflection/instantiation/Key.java | 33 ++++ .../instantiation/PrefabValueProvider.java | 55 +++---- .../equalsverifier/internal/util/Context.java | 15 +- .../internal/util/PrefabValuesApi.java | 25 +-- .../internal/util/Validations.java | 7 +- .../ConfiguredEqualsVerifierMultipleTest.java | 21 +++ .../ConfiguredEqualsVerifierSingleTest.java | 21 +++ .../GenericPrefabValueProviderTest.java | 153 ++++++++++++++++++ .../reflection/instantiation/KeyTest.java | 26 +++ .../PrefabValueProviderTest.java | 13 +- 13 files changed, 460 insertions(+), 71 deletions(-) create mode 100644 equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProvider.java create mode 100644 equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/Key.java create mode 100644 equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProviderTest.java create mode 100644 equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/KeyTest.java diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java index 8d5b0bd04..86188efd5 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java @@ -11,6 +11,7 @@ import nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi; import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.PackageScanner; +import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider; import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; import nl.jqno.equalsverifier.internal.util.ListBuilders; import nl.jqno.equalsverifier.internal.util.PrefabValuesApi; @@ -23,6 +24,7 @@ public final class ConfiguredEqualsVerifier implements EqualsVerifierApi { private final EnumSet warningsToSuppress; private final FactoryCache factoryCache; private final PrefabValueProvider prefabValueProvider; + private final GenericPrefabValueProvider genericPrefabValueProvider; private boolean usingGetClass; private Function fieldnameToGetter; private final Objenesis objenesis = new ObjenesisStd(); @@ -33,6 +35,7 @@ public ConfiguredEqualsVerifier() { EnumSet.noneOf(Warning.class), new FactoryCache(), new PrefabValueProvider(), + new GenericPrefabValueProvider(), false, null ); @@ -43,12 +46,14 @@ private ConfiguredEqualsVerifier( EnumSet warningsToSuppress, FactoryCache factoryCache, PrefabValueProvider prefabValueProvider, + GenericPrefabValueProvider genericPrefabValueProvider, boolean usingGetClass, Function fieldnameToGetter ) { this.warningsToSuppress = warningsToSuppress; this.factoryCache = factoryCache; this.prefabValueProvider = prefabValueProvider; + this.genericPrefabValueProvider = genericPrefabValueProvider; this.usingGetClass = usingGetClass; this.fieldnameToGetter = fieldnameToGetter; } @@ -62,7 +67,8 @@ public ConfiguredEqualsVerifier copy() { return new ConfiguredEqualsVerifier( EnumSet.copyOf(warningsToSuppress), new FactoryCache().merge(factoryCache), - prefabValueProvider, + prefabValueProvider.copy(), + genericPrefabValueProvider.copy(), usingGetClass, fieldnameToGetter ); @@ -95,7 +101,7 @@ public ConfiguredEqualsVerifier withGenericPrefabValues( Class otherType, Func1 factory ) { - PrefabValuesApi.addGenericPrefabValues(factoryCache, otherType, factory); + PrefabValuesApi.addGenericPrefabValues(genericPrefabValueProvider, otherType, factory); return this; } @@ -105,7 +111,7 @@ public ConfiguredEqualsVerifier withGenericPrefabValues( Class otherType, Func2 factory ) { - PrefabValuesApi.addGenericPrefabValues(factoryCache, otherType, factory); + PrefabValuesApi.addGenericPrefabValues(genericPrefabValueProvider, otherType, factory); return this; } @@ -146,6 +152,8 @@ public SingleTypeEqualsVerifierApi forClass(Class type) { type, EnumSet.copyOf(warningsToSuppress), factoryCache, + prefabValueProvider.copy(), + genericPrefabValueProvider.copy(), objenesis, usingGetClass, fieldnameToGetter diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java index 1f62f5b7a..0fe4f3226 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java @@ -1,5 +1,6 @@ package nl.jqno.equalsverifier.api; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.*; import java.util.function.Function; import nl.jqno.equalsverifier.EqualsVerifier; @@ -10,6 +11,7 @@ import nl.jqno.equalsverifier.internal.checkers.*; import nl.jqno.equalsverifier.internal.exceptions.MessagingException; import nl.jqno.equalsverifier.internal.reflection.FactoryCache; +import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider; import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; import nl.jqno.equalsverifier.internal.util.*; import nl.jqno.equalsverifier.internal.util.Formatter; @@ -32,6 +34,8 @@ public class SingleTypeEqualsVerifierApi implements EqualsVerifierApi { private Class redefinedSubclass = null; private FactoryCache factoryCache = new FactoryCache(); private PrefabValueProvider prefabValueProvider = new PrefabValueProvider(); + private GenericPrefabValueProvider genericPrefabValueProvider = + new GenericPrefabValueProvider(); private CachedHashCodeInitializer cachedHashCodeInitializer = CachedHashCodeInitializer.passthrough(); private Function fieldnameToGetter = null; @@ -70,15 +74,24 @@ public SingleTypeEqualsVerifierApi(Class type, Objenesis objenesis) { * @param type The class for which the {@code equals} method should be tested. * @param warningsToSuppress A list of warnings to suppress in {@code EqualsVerifier}. * @param factoryCache Factories that can be used to create values. + * @param prefabValueProvider ValueProvider that records prefab values. + * @param genericPrefabValueProvider ValueProvider that records generic prefab values. * @param objenesis To instantiate non-record classes. * @param usingGetClass Whether {@code getClass} is used in the implementation of the {@code * equals} method, instead of an {@code instanceof} check. * @param converter A function that converts from field name to getter name. */ + @SuppressFBWarnings( + value = "EI_EXPOSE_REP2", + justification = "GenericPrefabValueProvider has a mutable element" + ) + // CHECKSTYLE OFF: ParameterNumber public SingleTypeEqualsVerifierApi( Class type, EnumSet warningsToSuppress, FactoryCache factoryCache, + PrefabValueProvider prefabValueProvider, + GenericPrefabValueProvider genericPrefabValueProvider, Objenesis objenesis, boolean usingGetClass, Function converter @@ -86,10 +99,14 @@ public SingleTypeEqualsVerifierApi( this(type, objenesis); this.warningsToSuppress = EnumSet.copyOf(warningsToSuppress); this.factoryCache = this.factoryCache.merge(factoryCache); + this.prefabValueProvider = prefabValueProvider; + this.genericPrefabValueProvider = genericPrefabValueProvider; this.usingGetClass = usingGetClass; this.fieldnameToGetter = converter; } + // CHECKSTYLE ON: ParameterNumber + /** * Constructor, only to be called by {@link RelaxedEqualsVerifierApi#andUnequalExamples(Object, * Object[])}. @@ -168,7 +185,7 @@ public SingleTypeEqualsVerifierApi withGenericPrefabValues( Class otherType, Func1 factory ) { - PrefabValuesApi.addGenericPrefabValues(factoryCache, otherType, factory); + PrefabValuesApi.addGenericPrefabValues(genericPrefabValueProvider, otherType, factory); return this; } @@ -178,7 +195,7 @@ public SingleTypeEqualsVerifierApi withGenericPrefabValues( Class otherType, Func2 factory ) { - PrefabValuesApi.addGenericPrefabValues(factoryCache, otherType, factory); + PrefabValuesApi.addGenericPrefabValues(genericPrefabValueProvider, otherType, factory); return this; } @@ -445,7 +462,13 @@ private void performVerification() { Validations.validateClassCanBeVerified(type); Configuration config = buildConfig(); - Context context = new Context<>(config, factoryCache, prefabValueProvider, objenesis); + Context context = new Context<>( + config, + factoryCache, + prefabValueProvider, + genericPrefabValueProvider, + objenesis + ); Validations.validateProcessedAnnotations( type, config.getAnnotationCache(), diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProvider.java new file mode 100644 index 000000000..b7f29f0db --- /dev/null +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProvider.java @@ -0,0 +1,119 @@ +package nl.jqno.equalsverifier.internal.reflection.instantiation; + +import java.util.*; +import nl.jqno.equalsverifier.Func; +import nl.jqno.equalsverifier.Func.Func1; +import nl.jqno.equalsverifier.Func.Func2; +import nl.jqno.equalsverifier.internal.exceptions.EqualsVerifierInternalBugException; +import nl.jqno.equalsverifier.internal.reflection.Tuple; +import nl.jqno.equalsverifier.internal.reflection.TypeTag; + +public class GenericPrefabValueProvider implements ValueProvider { + + private final Map> cache = new HashMap<>(); + private ValueProvider provider; + + /** Constructor. */ + public GenericPrefabValueProvider() {} + + /** + * Private copy constructor. + * + * @param other The {@link GenericPrefabValueProvider} to copy. + */ + private GenericPrefabValueProvider(GenericPrefabValueProvider other) { + this(); + cache.putAll(other.cache); + setProvider(other.provider); + } + + /** + * Copies the cache of this GenericPrefabValueProvider into a new instance. + * + * @return A copy of this GenericPrefabValueProvider. + */ + public GenericPrefabValueProvider copy() { + return new GenericPrefabValueProvider(this); + } + + /** + * Registers a prefab value with a single generic type argument. + * + * @param type The class of the prefabricated values. + * @param label The label that the prefabricated value is linked to, or null if the value is + * not assigned to any label. + * @param factory A factory that can produce instances for {@code type}. + * @param The type of the instances. + */ + public void register(Class type, String label, Func1 factory) { + Key key = new Key(type, label); + cache.put(key, factory); + } + + /** + * Registers a prefab value with two generic type arguments. + * + * @param type The class of the prefabricated values. + * @param label The label that the prefabricated value is linked to, or null if the value is + * not assigned to any label. + * @param factory A factory that can produce instances for {@code type}. + * @param The type of the instances. + */ + public void register(Class type, String label, Func2 factory) { + Key key = new Key(type, label); + cache.put(key, factory); + } + + /** + * Configures the valueProvider to be used for recursive value lookups. + * + * @param valueProvider The valueProvider to use. + */ + public void setProvider(ValueProvider valueProvider) { + this.provider = valueProvider; + } + + /** {@inheritDoc} */ + @Override + public Optional> provide(TypeTag tag, String label) { + Class type = tag.getType(); + Optional> maybeFactory = findFactory(type, label); + if (!maybeFactory.isPresent()) { + return Optional.empty(); + } + return findValue(tag, maybeFactory.get()); + } + + @SuppressWarnings("unchecked") + private Optional> findFactory(Class type, String label) { + Func result = (Func) cache.get(new Key(type, label)); + if (result == null) { + result = (Func) cache.get(new Key(type, null)); + } + return Optional.ofNullable(result); + } + + private Optional> findValue(TypeTag tag, Func factory) { + if (provider == null) { + throw new EqualsVerifierInternalBugException( + "valueProvider not initialized in GenericPrefabValueProvider" + ); + } + List redValues = new ArrayList<>(); + List blueValues = new ArrayList<>(); + List redCopyValues = new ArrayList<>(); + + for (TypeTag generic : tag.getGenericTypes()) { + Tuple tuple = provider.provide(generic); + redValues.add(tuple.getRed()); + blueValues.add(tuple.getBlue()); + redCopyValues.add(tuple.getRedCopy()); + } + + T red = factory.apply(redValues); + T blue = factory.apply(blueValues); + T redCopy = factory.apply(redCopyValues); + + return Optional.of(Tuple.of(red, blue, redCopy)); + } +} diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/Key.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/Key.java new file mode 100644 index 000000000..064da2922 --- /dev/null +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/Key.java @@ -0,0 +1,33 @@ +package nl.jqno.equalsverifier.internal.reflection.instantiation; + +import java.util.Objects; + +final class Key { + + private final Class type; + final String label; + + Key(Class type, String label) { + this.type = type; + this.label = label; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Key)) { + return false; + } + Key other = (Key) obj; + return Objects.equals(type, other.type) && Objects.equals(label, other.label); + } + + @Override + public int hashCode() { + return Objects.hash(type, label); + } + + @Override + public String toString() { + return "Key: [" + type + "/" + label + "]"; + } +} diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java index 8c91546f9..3d28ce559 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java @@ -1,7 +1,6 @@ package nl.jqno.equalsverifier.internal.reflection.instantiation; import java.util.*; -import java.util.function.Supplier; import java.util.stream.Collectors; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; @@ -14,6 +13,28 @@ public class PrefabValueProvider implements ValueProvider { private final Map> cache = new HashMap<>(); + /** Constructor. */ + public PrefabValueProvider() {} + + /** + * Private copy constructor. + * + * @param other The {@link GenericPrefabValueProvider} to copy. + */ + private PrefabValueProvider(PrefabValueProvider other) { + this(); + cache.putAll(other.cache); + } + + /** + * Copies the cache of this PrefabValueProvider into a new instance. + * + * @return A copy of this PrefabValueProvider. + */ + public PrefabValueProvider copy() { + return new PrefabValueProvider(this); + } + /** {@inheritDoc} */ @Override public Optional> provide(TypeTag tag, String label) { @@ -58,36 +79,4 @@ public void register(Class type, String label, T red, T blue, T redCopy) public Set getFieldNames() { return cache.keySet().stream().map(k -> k.label).collect(Collectors.toSet()); } - - static final class Key { - - private final Class type; - private final String label; - - private Key(Class type, String label) { - this.type = type; - this.label = label; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Key)) { - return false; - } - Key other = (Key) obj; - return Objects.equals(type, other.type) && Objects.equals(label, other.label); - } - - @Override - public int hashCode() { - return Objects.hash(type, label); - } - - @Override - public String toString() { - return "Key: [" + type + "/" + label + "]"; - } - } - - static interface Value extends Supplier> {} } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java index 32fa2be9b..4ef6c17c7 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java @@ -24,19 +24,27 @@ public Context( Configuration configuration, FactoryCache factoryCache, PrefabValueProvider prefabValueProvider, + GenericPrefabValueProvider genericPrefabValueProvider, Objenesis objenesis ) { this.type = configuration.getType(); this.configuration = configuration; this.classProbe = new ClassProbe<>(configuration.getType()); - this.valueProvider = configureValueProviders(factoryCache, prefabValueProvider, objenesis); + this.valueProvider = + configureValueProviders( + factoryCache, + prefabValueProvider, + genericPrefabValueProvider, + objenesis + ); this.subjectCreator = new SubjectCreator<>(configuration, valueProvider, objenesis); } private static ValueProvider configureValueProviders( FactoryCache factoryCache, PrefabValueProvider prefabValueProvider, + GenericPrefabValueProvider genericPrefabValueProvider, Objenesis objenesis ) { ChainedValueProvider mainChain = new ChainedValueProvider(); @@ -45,8 +53,9 @@ private static ValueProvider configureValueProviders( FactoryCache cache = JavaApiPrefabValues.build().merge(factoryCache); ValueProvider vintage = new VintageValueProvider(vintageChain, cache, objenesis); - mainChain.register(prefabValueProvider, vintage); - vintageChain.register(prefabValueProvider); + mainChain.register(prefabValueProvider, genericPrefabValueProvider, vintage); + vintageChain.register(prefabValueProvider, genericPrefabValueProvider); + genericPrefabValueProvider.setProvider(mainChain); return mainChain; } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java index f0587c0eb..9d595dceb 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java @@ -1,14 +1,13 @@ package nl.jqno.equalsverifier.internal.util; -import static nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.Factories.simple; import static nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.Factories.values; import nl.jqno.equalsverifier.Func.Func1; import nl.jqno.equalsverifier.Func.Func2; -import nl.jqno.equalsverifier.internal.reflection.*; +import nl.jqno.equalsverifier.internal.reflection.FactoryCache; +import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider; import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; import nl.jqno.equalsverifier.internal.reflection.vintage.ObjectAccessor; -import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.PrefabValueFactory; import org.objenesis.Objenesis; public final class PrefabValuesApi { @@ -69,30 +68,22 @@ public static void addPrefabValuesForField( } public static void addGenericPrefabValues( - FactoryCache factoryCache, + GenericPrefabValueProvider provider, Class otherType, Func1 factory ) { Validations.validateNotNull(factory, "factory is null."); - addGenericPrefabValueFactory(factoryCache, otherType, simple(factory, null), 1); + Validations.validateGenericPrefabValues(otherType, 1); + provider.register(otherType, null, factory); } public static void addGenericPrefabValues( - FactoryCache factoryCache, + GenericPrefabValueProvider provider, Class otherType, Func2 factory ) { Validations.validateNotNull(factory, "factory is null."); - addGenericPrefabValueFactory(factoryCache, otherType, simple(factory, null), 2); - } - - private static void addGenericPrefabValueFactory( - FactoryCache factoryCache, - Class otherType, - PrefabValueFactory factory, - int arity - ) { - Validations.validateGenericPrefabValues(otherType, factory, arity); - factoryCache.put(otherType, factory); + Validations.validateGenericPrefabValues(otherType, 2); + provider.register(otherType, null, factory); } } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Validations.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Validations.java index 0adb4f04d..2b0ed19e9 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Validations.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Validations.java @@ -12,7 +12,6 @@ import nl.jqno.equalsverifier.internal.reflection.FieldIterable; import nl.jqno.equalsverifier.internal.reflection.annotations.AnnotationCache; import nl.jqno.equalsverifier.internal.reflection.annotations.SupportedAnnotations; -import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.PrefabValueFactory; public final class Validations { @@ -118,11 +117,7 @@ public static void validateFieldTypeMatches( } } - public static void validateGenericPrefabValues( - Class type, - PrefabValueFactory factory, - int arity - ) { + public static void validateGenericPrefabValues(Class type, int arity) { validateNotNull(type, "type is null."); int n = type.getTypeParameters().length; diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/ConfiguredEqualsVerifierMultipleTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/ConfiguredEqualsVerifierMultipleTest.java index 94ce5f16d..8600efe44 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/ConfiguredEqualsVerifierMultipleTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/ConfiguredEqualsVerifierMultipleTest.java @@ -156,6 +156,27 @@ public void individuallySuppressedWarningsAreNotAddedGlobally() { public void individuallyAddedPrefabValuesAreNotAddedGlobally() { ConfiguredEqualsVerifier ev = EqualsVerifier.configure(); + // should succeed + ev + .forClasses(RecursiveTypeContainer.class, A.class) + .withPrefabValues( + RecursiveType.class, + new RecursiveType(null), + new RecursiveType(new RecursiveType(null)) + ) + .verify(); + + // PrefabValues are not added to configuration, so should fail + ExpectedException + .when(() -> ev.forClasses(RecursiveTypeContainer.class, A.class).verify()) + .assertFailure() + .assertMessageContains("Recursive datastructure"); + } + + @Test + public void individuallyAddedGenericPrefabValuesAreNotAddedGlobally() { + ConfiguredEqualsVerifier ev = EqualsVerifier.configure(); + // should succeed ev .forClasses(SingleGenericContainerContainer.class, A.class) diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/ConfiguredEqualsVerifierSingleTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/ConfiguredEqualsVerifierSingleTest.java index 16cd8f8a9..7ace3f1ae 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/ConfiguredEqualsVerifierSingleTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/ConfiguredEqualsVerifierSingleTest.java @@ -117,6 +117,27 @@ public void individuallySuppressedWarningsAreNotAddedGlobally() { public void individuallyAddedPrefabValuesAreNotAddedGlobally() { ConfiguredEqualsVerifier ev = EqualsVerifier.configure(); + // should succeed + ev + .forClass(RecursiveTypeContainer.class) + .withPrefabValues( + RecursiveType.class, + new RecursiveType(null), + new RecursiveType(new RecursiveType(null)) + ) + .verify(); + + // PrefabValues are not added to configuration, so should fail + ExpectedException + .when(() -> ev.forClass(RecursiveTypeContainer.class).verify()) + .assertFailure() + .assertMessageContains("Recursive datastructure"); + } + + @Test + public void individuallyAddedGenericPrefabValuesAreNotAddedGlobally() { + ConfiguredEqualsVerifier ev = EqualsVerifier.configure(); + // should succeed ev .forClass(SingleGenericContainerContainer.class) diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProviderTest.java new file mode 100644 index 000000000..3c82ebe64 --- /dev/null +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProviderTest.java @@ -0,0 +1,153 @@ +package nl.jqno.equalsverifier.internal.reflection.instantiation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.*; +import nl.jqno.equalsverifier.internal.reflection.Tuple; +import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class GenericPrefabValueProviderTest { + + private static final TypeTag INTEGER = new TypeTag(Integer.class); + private static final TypeTag STRING = new TypeTag(String.class); + private static final TypeTag LIST = new TypeTag(List.class, new TypeTag(String.class)); + private static final TypeTag MAP = new TypeTag(Map.class, INTEGER, STRING); + + private GenericPrefabValueProvider sut = new GenericPrefabValueProvider(); + + @BeforeEach + public void setup() { + PrefabValueProvider p = new PrefabValueProvider(); + p.register(INTEGER.getType(), null, 3, 2, 3); + p.register(STRING.getType(), null, "a", "b", "a"); + sut.setProvider(p); + } + + @Test + public void generic1_aRegisteredValueCanBeFound() { + sut.register(LIST.getType(), null, (String s) -> list(s)); + assertEquals(Tuple.of(list("a"), list("b"), list("a")), sut.provide(LIST, null).get()); + } + + @Test + public void generic2_aRegisteredValueCanBeFound() { + sut.register(MAP.getType(), null, (Integer k, String v) -> map(k, v)); + assertEquals(Tuple.of(map(3, "a"), map(2, "b"), map(3, "a")), sut.provide(MAP, null).get()); + } + + @Test + public void generic1_aValueRegisteredWithALabelCanBeFoundUnderThatLabel() { + sut.register(LIST.getType(), "label", (String s) -> list(s)); + assertEquals(Tuple.of(list("a"), list("b"), list("a")), sut.provide(LIST, "label").get()); + } + + @Test + public void generic2_aValueRegisteredWithALabelCanBeFoundUnderThatLabel() { + sut.register(MAP.getType(), "label", (Integer k, String v) -> map(k, v)); + assertEquals( + Tuple.of(map(3, "a"), map(2, "b"), map(3, "a")), + sut.provide(MAP, "label").get() + ); + } + + @Test + public void generic1_aValueRegisteredWithALabelCanNotBeFoundWithoutThatLabel() { + sut.register(LIST.getType(), "label", (String s) -> list(s)); + assertEquals(Optional.empty(), sut.provide(LIST, null)); + } + + @Test + public void generic2_aValueRegisteredWithALabelCanNotBeFoundWithoutThatLabel() { + sut.register(MAP.getType(), "label", (Integer k, String v) -> map(k, v)); + assertEquals(Optional.empty(), sut.provide(MAP, null)); + } + + @Test + public void generic1_aQueryWithLabelFallsBackToRegisteredValueWithoutLabel() { + sut.register(LIST.getType(), null, (String s) -> list(s)); + assertEquals(Tuple.of(list("a"), list("b"), list("a")), sut.provide(LIST, "label").get()); + } + + @Test + public void generic2_aQueryWithLabelFallsBackToRegisteredValueWithoutLabel() { + sut.register(MAP.getType(), null, (Integer k, String v) -> map(k, v)); + assertEquals( + Tuple.of(map(3, "a"), map(2, "b"), map(3, "a")), + sut.provide(MAP, "label").get() + ); + } + + @Test + public void generic1_aQueryWithLabelPrefersRegisteredValueWithThatLabel() { + sut.register(LIST.getType(), null, (String s) -> list(s)); + sut.register(LIST.getType(), "label", (String s) -> list(s + "x")); + assertEquals( + Tuple.of(list("ax"), list("bx"), list("ax")), + sut.provide(LIST, "label").get() + ); + } + + @Test + public void generic2_aQueryWithLabelPrefersRegisteredValueWithThatLabel() { + sut.register(MAP.getType(), null, (Integer k, String v) -> map(k - 1, v + "x")); + sut.register(MAP.getType(), "label", (Integer k, String v) -> map(k + 1, v + "y")); + assertEquals( + Tuple.of(map(4, "ay"), map(3, "by"), map(4, "ay")), + sut.provide(MAP, "label").get() + ); + } + + @Test + public void generic1_anUnregisteredValueCanNotBeFound() { + assertEquals(Optional.empty(), sut.provide(LIST, null)); + } + + @Test + public void generic2_anUnregisteredValueCanNotBeFound() { + assertEquals(Optional.empty(), sut.provide(MAP, null)); + } + + @Test + public void generic1_copy() { + sut.register(LIST.getType(), "original", (String s) -> list(s + "x")); + GenericPrefabValueProvider anotherSut = sut.copy(); + sut.register(LIST.getType(), "invisibleToOther", (String s) -> list(s + "y")); + + assertEquals( + Tuple.of(list("ax"), list("bx"), list("ax")), + anotherSut.provide(LIST, "original").get() + ); + assertEquals(Optional.empty(), anotherSut.provide(LIST, "invisibleToOther")); + } + + @Test + public void generic2_copy() { + sut.register(MAP.getType(), "original", (Integer k, String v) -> map(k - 1, v + "x")); + GenericPrefabValueProvider anotherSut = sut.copy(); + sut.register( + MAP.getType(), + "invisibleToOther", + (Integer k, String v) -> map(k + 1, v + "y") + ); + + assertEquals( + Tuple.of(map(2, "ax"), map(1, "bx"), map(2, "ax")), + anotherSut.provide(MAP, "original").get() + ); + assertEquals(Optional.empty(), anotherSut.provide(MAP, "invisibleToOther")); + } + + private List list(String s) { + List result = new ArrayList<>(); + result.add(s); + return result; + } + + private Map map(Integer k, String v) { + Map result = new HashMap<>(); + result.put(k, v); + return result; + } +} diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/KeyTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/KeyTest.java new file mode 100644 index 000000000..63587aa79 --- /dev/null +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/KeyTest.java @@ -0,0 +1,26 @@ +package nl.jqno.equalsverifier.internal.reflection.instantiation; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import nl.jqno.equalsverifier.EqualsVerifier; +import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import org.junit.jupiter.api.Test; + +public class KeyTest { + + @Test + public void keyEqualsAndHashCode() { + EqualsVerifier + .forClass(Key.class) + .withPrefabValues(TypeTag.class, new TypeTag(Integer.class), new TypeTag(String.class)) + .verify(); + } + + @Test + public void testToString() { + Key k = new Key(String.class, "label"); + String actual = k.toString(); + assertTrue(actual.contains("String")); + assertTrue(actual.contains("label")); + } +} diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java index ce4b31204..2449d63e7 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java @@ -3,7 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Optional; -import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import org.junit.jupiter.api.Test; @@ -64,10 +63,12 @@ public void anUnregisteredValueCanNotBeFound() { } @Test - public void keyEqualsAndHashCode() { - EqualsVerifier - .forClass(PrefabValueProvider.Key.class) - .withPrefabValues(TypeTag.class, new TypeTag(Integer.class), new TypeTag(String.class)) - .verify(); + public void copy() { + sut.register(INT.getType(), "original", 1, 2, 1); + PrefabValueProvider anotherSut = sut.copy(); + sut.register(INT.getType(), "invisibleToOther", 3, 4, 3); + + assertEquals(Tuple.of(1, 2, 1), anotherSut.provide(INT, "original").get()); + assertEquals(Optional.empty(), anotherSut.provide(INT, "invisibleToOther")); } } From 28b68d24dd4a47d65dadabae952523ccc77191e2 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Fri, 18 Oct 2024 11:27:30 +0200 Subject: [PATCH 11/15] Moves FactoryCache one level of abstraction deeper --- .../RecordObjectAccessorScramblingTest.java | 1 - .../ConfiguredEqualsVerifier.java | 16 +--------------- .../api/SingleTypeEqualsVerifierApi.java | 18 +----------------- .../reflection/JavaApiPrefabValues.java | 1 + .../instantiation/VintageValueProvider.java | 2 +- .../reflection/{ => vintage}/FactoryCache.java | 2 +- .../factories/ExternalFactory.java | 2 +- .../factoryproviders/AwtFactoryProvider.java | 2 +- .../factoryproviders/FactoryProvider.java | 2 +- .../factoryproviders/GuavaFactoryProvider.java | 2 +- .../JavaFxFactoryProvider.java | 1 + .../factoryproviders/JavaxFactoryProvider.java | 2 +- .../factoryproviders/JodaFactoryProvider.java | 2 +- .../factoryproviders/RmiFactoryProvider.java | 2 +- .../equalsverifier/internal/util/Context.java | 9 ++++----- .../internal/util/PrefabValuesApi.java | 7 ------- .../architecture/ArchitectureTest.java | 4 ---- .../VintageValueProviderCreatorTest.java | 4 ++-- .../VintageValueProviderTest.java | 2 +- .../reflection/vintage/ClassAccessorTest.java | 1 - .../vintage}/FactoryCacheFactory.java | 4 +--- .../{ => vintage}/FactoryCacheTest.java | 2 +- .../InPlaceObjectAccessorScramblingTest.java | 1 - .../factories/FallbackFactoryTest.java | 2 +- 24 files changed, 23 insertions(+), 68 deletions(-) rename equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/{ => vintage}/FactoryCache.java (98%) rename equalsverifier-core/src/test/java/nl/jqno/equalsverifier/{testhelpers => internal/reflection/vintage}/FactoryCacheFactory.java (88%) rename equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/{ => vintage}/FactoryCacheTest.java (97%) diff --git a/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorScramblingTest.java b/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorScramblingTest.java index d0cb07925..b5ccc6824 100644 --- a/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorScramblingTest.java +++ b/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/RecordObjectAccessorScramblingTest.java @@ -6,7 +6,6 @@ import java.lang.reflect.Constructor; import java.util.LinkedHashSet; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java index 86188efd5..be1417b25 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java @@ -9,7 +9,6 @@ import nl.jqno.equalsverifier.api.EqualsVerifierApi; import nl.jqno.equalsverifier.api.MultipleTypeEqualsVerifierApi; import nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.PackageScanner; import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider; import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; @@ -22,7 +21,6 @@ public final class ConfiguredEqualsVerifier implements EqualsVerifierApi { private final EnumSet warningsToSuppress; - private final FactoryCache factoryCache; private final PrefabValueProvider prefabValueProvider; private final GenericPrefabValueProvider genericPrefabValueProvider; private boolean usingGetClass; @@ -33,7 +31,6 @@ public final class ConfiguredEqualsVerifier implements EqualsVerifierApi { public ConfiguredEqualsVerifier() { this( EnumSet.noneOf(Warning.class), - new FactoryCache(), new PrefabValueProvider(), new GenericPrefabValueProvider(), false, @@ -44,14 +41,12 @@ public ConfiguredEqualsVerifier() { /** Private constructor. For internal use only. */ private ConfiguredEqualsVerifier( EnumSet warningsToSuppress, - FactoryCache factoryCache, PrefabValueProvider prefabValueProvider, GenericPrefabValueProvider genericPrefabValueProvider, boolean usingGetClass, Function fieldnameToGetter ) { this.warningsToSuppress = warningsToSuppress; - this.factoryCache = factoryCache; this.prefabValueProvider = prefabValueProvider; this.genericPrefabValueProvider = genericPrefabValueProvider; this.usingGetClass = usingGetClass; @@ -66,7 +61,6 @@ private ConfiguredEqualsVerifier( public ConfiguredEqualsVerifier copy() { return new ConfiguredEqualsVerifier( EnumSet.copyOf(warningsToSuppress), - new FactoryCache().merge(factoryCache), prefabValueProvider.copy(), genericPrefabValueProvider.copy(), usingGetClass, @@ -84,14 +78,7 @@ public ConfiguredEqualsVerifier suppress(Warning... warnings) { /** {@inheritDoc} */ @Override public ConfiguredEqualsVerifier withPrefabValues(Class otherType, S red, S blue) { - PrefabValuesApi.addPrefabValues( - prefabValueProvider, - factoryCache, - objenesis, - otherType, - red, - blue - ); + PrefabValuesApi.addPrefabValues(prefabValueProvider, objenesis, otherType, red, blue); return this; } @@ -151,7 +138,6 @@ public SingleTypeEqualsVerifierApi forClass(Class type) { return new SingleTypeEqualsVerifierApi<>( type, EnumSet.copyOf(warningsToSuppress), - factoryCache, prefabValueProvider.copy(), genericPrefabValueProvider.copy(), objenesis, diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java index 0fe4f3226..8d36e0492 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java @@ -10,7 +10,6 @@ import nl.jqno.equalsverifier.Warning; import nl.jqno.equalsverifier.internal.checkers.*; import nl.jqno.equalsverifier.internal.exceptions.MessagingException; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider; import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; import nl.jqno.equalsverifier.internal.util.*; @@ -32,7 +31,6 @@ public class SingleTypeEqualsVerifierApi implements EqualsVerifierApi { private boolean usingGetClass = false; private boolean hasRedefinedSuperclass = false; private Class redefinedSubclass = null; - private FactoryCache factoryCache = new FactoryCache(); private PrefabValueProvider prefabValueProvider = new PrefabValueProvider(); private GenericPrefabValueProvider genericPrefabValueProvider = new GenericPrefabValueProvider(); @@ -73,7 +71,6 @@ public SingleTypeEqualsVerifierApi(Class type, Objenesis objenesis) { * * @param type The class for which the {@code equals} method should be tested. * @param warningsToSuppress A list of warnings to suppress in {@code EqualsVerifier}. - * @param factoryCache Factories that can be used to create values. * @param prefabValueProvider ValueProvider that records prefab values. * @param genericPrefabValueProvider ValueProvider that records generic prefab values. * @param objenesis To instantiate non-record classes. @@ -85,11 +82,9 @@ public SingleTypeEqualsVerifierApi(Class type, Objenesis objenesis) { value = "EI_EXPOSE_REP2", justification = "GenericPrefabValueProvider has a mutable element" ) - // CHECKSTYLE OFF: ParameterNumber public SingleTypeEqualsVerifierApi( Class type, EnumSet warningsToSuppress, - FactoryCache factoryCache, PrefabValueProvider prefabValueProvider, GenericPrefabValueProvider genericPrefabValueProvider, Objenesis objenesis, @@ -98,15 +93,12 @@ public SingleTypeEqualsVerifierApi( ) { this(type, objenesis); this.warningsToSuppress = EnumSet.copyOf(warningsToSuppress); - this.factoryCache = this.factoryCache.merge(factoryCache); this.prefabValueProvider = prefabValueProvider; this.genericPrefabValueProvider = genericPrefabValueProvider; this.usingGetClass = usingGetClass; this.fieldnameToGetter = converter; } - // CHECKSTYLE ON: ParameterNumber - /** * Constructor, only to be called by {@link RelaxedEqualsVerifierApi#andUnequalExamples(Object, * Object[])}. @@ -138,14 +130,7 @@ public SingleTypeEqualsVerifierApi suppress(Warning... warnings) { /** {@inheritDoc} */ @Override public SingleTypeEqualsVerifierApi withPrefabValues(Class otherType, S red, S blue) { - PrefabValuesApi.addPrefabValues( - prefabValueProvider, - factoryCache, - objenesis, - otherType, - red, - blue - ); + PrefabValuesApi.addPrefabValues(prefabValueProvider, objenesis, otherType, red, blue); return this; } @@ -464,7 +449,6 @@ private void performVerification() { Configuration config = buildConfig(); Context context = new Context<>( config, - factoryCache, prefabValueProvider, genericPrefabValueProvider, objenesis diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/JavaApiPrefabValues.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/JavaApiPrefabValues.java index e1db1d3e3..cadf7fcd3 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/JavaApiPrefabValues.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/JavaApiPrefabValues.java @@ -29,6 +29,7 @@ import java.util.function.Supplier; import java.util.regex.Pattern; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.EnumMapFactory; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.EnumSetFactory; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.ExternalFactory; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java index 67ed4e0fa..3b6740e16 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java @@ -4,9 +4,9 @@ import java.util.*; import nl.jqno.equalsverifier.internal.exceptions.RecursionException; import nl.jqno.equalsverifier.internal.exceptions.ReflectionException; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.FallbackFactory; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.PrefabValueFactory; import nl.jqno.equalsverifier.internal.util.PrimitiveMappers; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FactoryCache.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCache.java similarity index 98% rename from equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FactoryCache.java rename to equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCache.java index 1d77eabdd..acc82c54c 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FactoryCache.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCache.java @@ -1,4 +1,4 @@ -package nl.jqno.equalsverifier.internal.reflection; +package nl.jqno.equalsverifier.internal.reflection.vintage; import java.util.HashMap; import java.util.Map; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/ExternalFactory.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/ExternalFactory.java index 0bea3340b..d458ee0a7 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/ExternalFactory.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/ExternalFactory.java @@ -5,10 +5,10 @@ import java.util.LinkedHashSet; import nl.jqno.equalsverifier.internal.reflection.ConditionalInstantiator; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factoryproviders.FactoryProvider; public class ExternalFactory implements PrefabValueFactory { diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/AwtFactoryProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/AwtFactoryProvider.java index 2312d3b5a..9499349f6 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/AwtFactoryProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/AwtFactoryProvider.java @@ -7,7 +7,7 @@ import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_Profile; import java.awt.image.BufferedImage; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; public final class AwtFactoryProvider implements FactoryProvider { diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/FactoryProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/FactoryProvider.java index c81cda452..9da2eef3b 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/FactoryProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/FactoryProvider.java @@ -1,6 +1,6 @@ package nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factoryproviders; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; public interface FactoryProvider { FactoryCache getFactoryCache(); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/GuavaFactoryProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/GuavaFactoryProvider.java index 4416de38c..cf571ca2f 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/GuavaFactoryProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/GuavaFactoryProvider.java @@ -6,10 +6,10 @@ import com.google.common.reflect.TypeToken; import java.util.*; import java.util.function.Supplier; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.AbstractGenericFactory; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.EnumMapFactory; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.EnumSetFactory; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaFxFactoryProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaFxFactoryProvider.java index aa2b6a3ca..9d6496d4c 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaFxFactoryProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaFxFactoryProvider.java @@ -9,6 +9,7 @@ import java.util.function.Function; import nl.jqno.equalsverifier.internal.reflection.*; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.AbstractGenericFactory; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.PrefabValueFactory; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaxFactoryProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaxFactoryProvider.java index 386bb67ae..b968c0bc6 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaxFactoryProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JavaxFactoryProvider.java @@ -4,7 +4,7 @@ import javax.naming.Reference; import javax.swing.tree.DefaultMutableTreeNode; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; public final class JavaxFactoryProvider implements FactoryProvider { diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JodaFactoryProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JodaFactoryProvider.java index da40b542d..33eb24584 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JodaFactoryProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/JodaFactoryProvider.java @@ -2,7 +2,7 @@ import static nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.Factories.values; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; import org.joda.time.*; import org.joda.time.chrono.GregorianChronology; import org.joda.time.chrono.ISOChronology; diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/RmiFactoryProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/RmiFactoryProvider.java index edb25d411..72e45f836 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/RmiFactoryProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factoryproviders/RmiFactoryProvider.java @@ -4,7 +4,7 @@ import java.rmi.dgc.VMID; import java.rmi.server.UID; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; public final class RmiFactoryProvider implements FactoryProvider { diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java index 4ef6c17c7..e5d51ad06 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java @@ -2,9 +2,9 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import nl.jqno.equalsverifier.internal.reflection.ClassProbe; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; import nl.jqno.equalsverifier.internal.reflection.instantiation.*; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; import org.objenesis.Objenesis; public final class Context { @@ -22,7 +22,6 @@ public final class Context { ) public Context( Configuration configuration, - FactoryCache factoryCache, PrefabValueProvider prefabValueProvider, GenericPrefabValueProvider genericPrefabValueProvider, Objenesis objenesis @@ -31,9 +30,10 @@ public Context( this.configuration = configuration; this.classProbe = new ClassProbe<>(configuration.getType()); + FactoryCache cache = JavaApiPrefabValues.build(); this.valueProvider = configureValueProviders( - factoryCache, + cache, prefabValueProvider, genericPrefabValueProvider, objenesis @@ -50,8 +50,7 @@ private static ValueProvider configureValueProviders( ChainedValueProvider mainChain = new ChainedValueProvider(); ChainedValueProvider vintageChain = new ChainedValueProvider(); - FactoryCache cache = JavaApiPrefabValues.build().merge(factoryCache); - ValueProvider vintage = new VintageValueProvider(vintageChain, cache, objenesis); + ValueProvider vintage = new VintageValueProvider(vintageChain, factoryCache, objenesis); mainChain.register(prefabValueProvider, genericPrefabValueProvider, vintage); vintageChain.register(prefabValueProvider, genericPrefabValueProvider); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java index 9d595dceb..7765ba8e4 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java @@ -1,10 +1,7 @@ package nl.jqno.equalsverifier.internal.util; -import static nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.Factories.values; - import nl.jqno.equalsverifier.Func.Func1; import nl.jqno.equalsverifier.Func.Func2; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider; import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; import nl.jqno.equalsverifier.internal.reflection.vintage.ObjectAccessor; @@ -16,7 +13,6 @@ private PrefabValuesApi() {} public static void addPrefabValues( PrefabValueProvider provider, - FactoryCache factoryCache, Objenesis objenesis, Class otherType, T red, @@ -26,16 +22,13 @@ public static void addPrefabValues( if (red.getClass().isArray()) { provider.register(otherType, null, red, blue, red); - factoryCache.put(otherType, values(red, blue, red)); } else { try { T redCopy = ObjectAccessor.of(red).copy(objenesis); provider.register(otherType, null, red, blue, redCopy); - factoryCache.put(otherType, values(red, blue, redCopy)); } catch (RuntimeException ignored) { /* specifically, on Java 9+: InacessibleObjectException */ provider.register(otherType, null, red, blue, red); - factoryCache.put(otherType, values(red, blue, red)); } } } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/architecture/ArchitectureTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/architecture/ArchitectureTest.java index b7461f4e9..42be625a1 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/architecture/ArchitectureTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/architecture/ArchitectureTest.java @@ -5,7 +5,6 @@ import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.ArchTest; import com.tngtech.archunit.lang.ArchRule; -import nl.jqno.equalsverifier.internal.reflection.FactoryCacheTest; import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProviderCreatorTest; @@ -15,7 +14,6 @@ import nl.jqno.equalsverifier.internal.reflection.vintage.ObjectAccessor; import nl.jqno.equalsverifier.internal.util.Context; import nl.jqno.equalsverifier.internal.util.PrefabValuesApi; -import nl.jqno.equalsverifier.testhelpers.FactoryCacheFactory; @AnalyzeClasses(packages = "nl.jqno.equalsverifier") public final class ArchitectureTest { @@ -29,8 +27,6 @@ public final class ArchitectureTest { PrefabValuesApi.class, JavaApiPrefabValues.class, // 👇 Test classes - FactoryCacheFactory.class, - FactoryCacheTest.class, VintageValueProviderTest.class, VintageValueProviderCreatorTest.class ) diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderCreatorTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderCreatorTest.java index 11a5df39c..607c3b20f 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderCreatorTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderCreatorTest.java @@ -4,11 +4,11 @@ import static org.junit.jupiter.api.Assertions.*; import nl.jqno.equalsverifier.internal.exceptions.RecursionException; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCacheFactory; import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; -import nl.jqno.equalsverifier.testhelpers.FactoryCacheFactory; import nl.jqno.equalsverifier.testhelpers.types.Point; import nl.jqno.equalsverifier.testhelpers.types.RecursiveTypeHelper.*; import nl.jqno.equalsverifier.testhelpers.types.TypeHelper.EmptyEnum; diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java index b8a43308a..f2995be16 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProviderTest.java @@ -10,9 +10,9 @@ import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.PrefabValueFactory; import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.testhelpers.types.Point; diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/ClassAccessorTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/ClassAccessorTest.java index 349cd5c78..f2323dbdb 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/ClassAccessorTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/ClassAccessorTest.java @@ -6,7 +6,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.LinkedHashSet; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/testhelpers/FactoryCacheFactory.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCacheFactory.java similarity index 88% rename from equalsverifier-core/src/test/java/nl/jqno/equalsverifier/testhelpers/FactoryCacheFactory.java rename to equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCacheFactory.java index 9f9871c6e..be7c8020f 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/testhelpers/FactoryCacheFactory.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCacheFactory.java @@ -1,9 +1,7 @@ -package nl.jqno.equalsverifier.testhelpers; +package nl.jqno.equalsverifier.internal.reflection.vintage; import static nl.jqno.equalsverifier.internal.reflection.vintage.prefabvalues.factories.Factories.values; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; - public final class FactoryCacheFactory { private FactoryCacheFactory() {} diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/FactoryCacheTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCacheTest.java similarity index 97% rename from equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/FactoryCacheTest.java rename to equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCacheTest.java index bbe20d280..89692154a 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/FactoryCacheTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCacheTest.java @@ -1,4 +1,4 @@ -package nl.jqno.equalsverifier.internal.reflection; +package nl.jqno.equalsverifier.internal.reflection.vintage; import static org.junit.jupiter.api.Assertions.*; diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/InPlaceObjectAccessorScramblingTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/InPlaceObjectAccessorScramblingTest.java index 3c04a23ac..ed642c6b3 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/InPlaceObjectAccessorScramblingTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/InPlaceObjectAccessorScramblingTest.java @@ -8,7 +8,6 @@ import java.util.LinkedHashSet; import java.util.List; import nl.jqno.equalsverifier.internal.exceptions.ModuleException; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/FallbackFactoryTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/FallbackFactoryTest.java index af2874c49..9a2982b17 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/FallbackFactoryTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/vintage/prefabvalues/factories/FallbackFactoryTest.java @@ -8,10 +8,10 @@ import java.util.LinkedHashSet; import nl.jqno.equalsverifier.internal.exceptions.RecursionException; -import nl.jqno.equalsverifier.internal.reflection.FactoryCache; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider; +import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; import nl.jqno.equalsverifier.internal.testhelpers.EmptyValueProvider; import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; import nl.jqno.equalsverifier.testhelpers.types.RecursiveTypeHelper.Node; From c5e52a9f887672f69e8cef817d9510e539b5cb43 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Fri, 18 Oct 2024 20:12:49 +0200 Subject: [PATCH 12/15] Factors FieldCache away into PrefabValueProvider --- .../internal/reflection/FieldCache.java | 60 ------------------ .../instantiation/ChainedValueProvider.java | 18 +++++- .../instantiation/PrefabValueProvider.java | 22 ++++++- .../instantiation/SubjectCreator.java | 9 +-- .../equalsverifier/internal/util/Context.java | 8 +-- .../internal/reflection/FieldCacheTest.java | 61 ------------------- .../ChainedValueProviderTest.java | 17 +++++- .../PrefabValueProviderTest.java | 26 ++++++++ 8 files changed, 84 insertions(+), 137 deletions(-) delete mode 100644 equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldCache.java delete mode 100644 equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/FieldCacheTest.java diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldCache.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldCache.java deleted file mode 100644 index 455a786e5..000000000 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldCache.java +++ /dev/null @@ -1,60 +0,0 @@ -package nl.jqno.equalsverifier.internal.reflection; - -import java.util.*; -import nl.jqno.equalsverifier.internal.reflection.instantiation.SubjectCreator; - -/** Contains a cache for values connected to specific fields, for {@link SubjectCreator}. */ -public class FieldCache { - - /** - * We store Strings instead of Fields, to make it easier to interact with when we don't - * actually have a reference to a Field. - */ - private final Map> cache = new HashMap<>(); - - /** - * Adds the given factory to the cache and associates it with the given type. - * - * @param The type of the values. - * @param fieldName The name of the field to associate with the values. - * @param tuple The tuple that contains the values. - */ - public void put(String fieldName, Tuple tuple) { - if (fieldName != null) { - cache.put(fieldName, tuple); - } - } - - /** - * Retrieves the values from the cache for the given field. - * - *

What happens when there are no values, is undefined. Always call {@link #contains(String)} - * first. - * - * @param The returned values will have this as generic type. - * @param fieldName The name of the field for which values are needed. - * @return A tuple of values for the given type, or {@code null} if none is available. - */ - @SuppressWarnings("unchecked") - public Tuple get(String fieldName) { - if (fieldName == null) { - return null; - } - return (Tuple) cache.get(fieldName); - } - - /** - * @param fieldName The name of the field for which values are needed. - * @return Whether values are available for the given field. - */ - public boolean contains(String fieldName) { - return cache.containsKey(fieldName); - } - - /** - * @return The fields preset in the cache. - */ - public Set getFieldNames() { - return new HashSet<>(cache.keySet()); - } -} diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java index f2c32e770..34983f293 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProvider.java @@ -13,8 +13,20 @@ public class ChainedValueProvider implements ValueProvider { private boolean locked = false; + private final PrefabValueProvider prefabValueProvider; private final List providers = new ArrayList<>(); + /** + * Constructor. + * + * @param prefabValueProvider The prefabValueProvider is always the first in the chain, + * and it will act as a cache for values that were found by other ValueProviders. + */ + public ChainedValueProvider(PrefabValueProvider prefabValueProvider) { + this.prefabValueProvider = prefabValueProvider; + providers.add(prefabValueProvider); + } + /** * Adds providers to the chain, so they can be delegated to when providing a value. * @@ -40,6 +52,10 @@ public Optional> provide(TypeTag tag, String label) { .map(vp -> vp.provide(tag, label)) .filter(Optional::isPresent) .findFirst() - .orElse(Optional.empty()); + .orElse(Optional.empty()) + .map(tuple -> { + prefabValueProvider.register(tag.getType(), label, tuple); + return tuple; + }); } } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java index 3d28ce559..eb6d4ec11 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProvider.java @@ -71,11 +71,29 @@ private Tuple attempt(Class type, String label) { * @param The type of the instances. */ public void register(Class type, String label, T red, T blue, T redCopy) { + Tuple tuple = Tuple.of(red, blue, redCopy); + register(type, label, tuple); + } + + /** + * Registers a prefab value. + * + * @param type The class of the prefabricated values. + * @param label The label that the prefabricated value is linked to, or null if the value is + * not assigned to any label. + * @param tuple A tuple of instances of {@code T}. + * @param The type of the instances. + */ + public void register(Class type, String label, Tuple tuple) { Key key = new Key(type, label); - Tuple value = Tuple.of(red, blue, redCopy); - cache.put(key, value); + cache.put(key, tuple); } + /** + * Returns the names of the fields for which a prefab value is currently known. + * + * @return The names of the fields for which a prefab value is currently known. + */ public Set getFieldNames() { return cache.keySet().stream().map(k -> k.label).collect(Collectors.toSet()); } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java index 356b433f3..979682f1a 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/SubjectCreator.java @@ -22,7 +22,6 @@ public class SubjectCreator { private final Configuration config; private final ValueProvider valueProvider; private final ClassProbe classProbe; - private final FieldCache fieldCache; private final Objenesis objenesis; private final InstanceCreator instanceCreator; @@ -44,7 +43,6 @@ public SubjectCreator( this.config = config; this.valueProvider = valueProvider; this.classProbe = new ClassProbe<>(type); - this.fieldCache = new FieldCache(); this.objenesis = objenesis; this.instanceCreator = new InstanceCreator<>(classProbe, objenesis); } @@ -238,16 +236,11 @@ private FieldIterable nonSuperFields() { } private Tuple valuesFor(Field f) { - String fieldName = f.getName(); - if (fieldCache.contains(fieldName)) { - return fieldCache.get(fieldName); - } try { TypeTag fieldTag = TypeTag.of(f, typeTag); Tuple tuple = valueProvider - .provide(fieldTag, fieldName) + .provide(fieldTag, f.getName()) .orElseThrow(() -> new NoValueException(fieldTag)); - fieldCache.put(fieldName, tuple); return tuple; } catch (ModuleException e) { throw new ModuleException( diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java index e5d51ad06..5aae2d1cc 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java @@ -47,13 +47,13 @@ private static ValueProvider configureValueProviders( GenericPrefabValueProvider genericPrefabValueProvider, Objenesis objenesis ) { - ChainedValueProvider mainChain = new ChainedValueProvider(); - ChainedValueProvider vintageChain = new ChainedValueProvider(); + ChainedValueProvider mainChain = new ChainedValueProvider(prefabValueProvider); + ChainedValueProvider vintageChain = new ChainedValueProvider(prefabValueProvider); ValueProvider vintage = new VintageValueProvider(vintageChain, factoryCache, objenesis); - mainChain.register(prefabValueProvider, genericPrefabValueProvider, vintage); - vintageChain.register(prefabValueProvider, genericPrefabValueProvider); + mainChain.register(genericPrefabValueProvider, vintage); + vintageChain.register(genericPrefabValueProvider); genericPrefabValueProvider.setProvider(mainChain); return mainChain; diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/FieldCacheTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/FieldCacheTest.java deleted file mode 100644 index 5d09df19a..000000000 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/FieldCacheTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package nl.jqno.equalsverifier.internal.reflection; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import org.junit.jupiter.api.Test; - -public class FieldCacheTest { - - private String stringField = "string"; - private final Tuple stringValues = new Tuple<>("red", "blue", "red"); - - private String intField = "int"; - private final Tuple intValues = new Tuple<>(1, 2, 1); - - private FieldCache cache = new FieldCache(); - - @Test - public void putAndGetTuple() { - cache.put(stringField, stringValues); - assertEquals(stringValues, cache.get(stringField)); - } - - @Test - public void putTwiceAndGetBoth() { - cache.put(stringField, stringValues); - cache.put(intField, intValues); - - assertEquals(intValues, cache.get(intField)); - assertEquals(stringValues, cache.get(stringField)); - } - - @Test - public void putNullAndGetNothingBack() { - cache.put(null, stringValues); - assertNull(cache.get(null)); - } - - @Test - public void contains() { - cache.put(stringField, stringValues); - assertTrue(cache.contains(stringField)); - } - - @Test - public void doesntContain() { - assertFalse(cache.contains(stringField)); - } - - @Test - public void getFieldNames() { - assertEquals(Collections.emptySet(), cache.getFieldNames()); - - cache.put(stringField, stringValues); - Set expected = new HashSet<>(); - expected.add(stringField); - assertEquals(expected, cache.getFieldNames()); - } -} diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java index ffed6dcf5..9e31caae9 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/ChainedValueProviderTest.java @@ -27,7 +27,8 @@ public class ChainedValueProviderTest { "a" ); - private ChainedValueProvider sut = new ChainedValueProvider(); + private final PrefabValueProvider prefab = new PrefabValueProvider(); + private final ChainedValueProvider sut = new ChainedValueProvider(prefab); @Test public void returnsValueIfMatch() { @@ -89,6 +90,20 @@ public void locksAfterFirstAssignment() { .assertMessageContains("Provider is locked"); } + @Test + public void cachesWithoutLabelInPrefabValueProvider() { + sut.register(intProvider); + sut.provide(INT); + assertEquals(1, prefab.provide(INT).getRed()); + } + + @Test + public void cachesWithLabelInPrefabValueProvider() { + sut.register(intProvider); + sut.provide(INT, "label"); + assertEquals(1, prefab.provide(INT, "label").get().getRed()); + } + static class SingleTypeValueProvider implements ValueProvider { private final Class type; diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java index 2449d63e7..f3947dd30 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/PrefabValueProviderTest.java @@ -2,7 +2,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; import org.junit.jupiter.api.Test; @@ -20,6 +22,12 @@ public void aRegisteredValueCanBeFound() { assertEquals(Tuple.of(3, 2, 3), sut.provide(INT, null).get()); } + @Test + public void aValueRegisteredAsATupleCanBeFound() { + sut.register(INT.getType(), null, Tuple.of(3, 2, 3)); + assertEquals(Tuple.of(3, 2, 3), sut.provide(INT, null).get()); + } + @Test public void aValueRegisteredWithALabelCanBeFoundUnderThatLabel() { sut.register(INT.getType(), "label", 3, 2, 3); @@ -62,6 +70,16 @@ public void anUnregisteredValueCanNotBeFound() { assertEquals(Optional.empty(), sut.provide(INT, null)); } + @Test + public void getFieldNames() { + sut.register(INT.getType(), "field1", 3, 2, 3); + sut.register(INT.getType(), "field2", 3, 2, 3); + assertEquals(set("field1", "field2"), sut.getFieldNames()); + + sut.register(INT.getType(), "field3", 3, 2, 3); + assertEquals(set("field1", "field2", "field3"), sut.getFieldNames()); + } + @Test public void copy() { sut.register(INT.getType(), "original", 1, 2, 1); @@ -71,4 +89,12 @@ public void copy() { assertEquals(Tuple.of(1, 2, 1), anotherSut.provide(INT, "original").get()); assertEquals(Optional.empty(), anotherSut.provide(INT, "invisibleToOther")); } + + private Set set(String... strings) { + Set result = new HashSet<>(); + for (String s : strings) { + result.add(s); + } + return result; + } } From d32989db9e2b55f9bed94a91febbc821acc4e693 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Tue, 22 Oct 2024 11:51:24 +0200 Subject: [PATCH 13/15] Refactors GenericPrefabValueProvider --- .../ConfiguredEqualsVerifier.java | 18 +-- .../api/SingleTypeEqualsVerifierApi.java | 22 +-- .../GenericPrefabValueProvider.java | 101 ++++++------- .../equalsverifier/internal/util/Context.java | 18 +-- .../internal/util/PrefabValuesApi.java | 10 +- .../GenericPrefabValueProviderTest.java | 138 +++++++----------- 6 files changed, 134 insertions(+), 173 deletions(-) diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java index be1417b25..8109ba10d 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/ConfiguredEqualsVerifier.java @@ -10,7 +10,7 @@ import nl.jqno.equalsverifier.api.MultipleTypeEqualsVerifierApi; import nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi; import nl.jqno.equalsverifier.internal.reflection.PackageScanner; -import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider; +import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider.GenericFactories; import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; import nl.jqno.equalsverifier.internal.util.ListBuilders; import nl.jqno.equalsverifier.internal.util.PrefabValuesApi; @@ -22,7 +22,7 @@ public final class ConfiguredEqualsVerifier implements EqualsVerifierApi { private final EnumSet warningsToSuppress; private final PrefabValueProvider prefabValueProvider; - private final GenericPrefabValueProvider genericPrefabValueProvider; + private final GenericFactories genericFactories; private boolean usingGetClass; private Function fieldnameToGetter; private final Objenesis objenesis = new ObjenesisStd(); @@ -32,7 +32,7 @@ public ConfiguredEqualsVerifier() { this( EnumSet.noneOf(Warning.class), new PrefabValueProvider(), - new GenericPrefabValueProvider(), + new GenericFactories(), false, null ); @@ -42,13 +42,13 @@ public ConfiguredEqualsVerifier() { private ConfiguredEqualsVerifier( EnumSet warningsToSuppress, PrefabValueProvider prefabValueProvider, - GenericPrefabValueProvider genericPrefabValueProvider, + GenericFactories genericFactories, boolean usingGetClass, Function fieldnameToGetter ) { this.warningsToSuppress = warningsToSuppress; this.prefabValueProvider = prefabValueProvider; - this.genericPrefabValueProvider = genericPrefabValueProvider; + this.genericFactories = genericFactories; this.usingGetClass = usingGetClass; this.fieldnameToGetter = fieldnameToGetter; } @@ -62,7 +62,7 @@ public ConfiguredEqualsVerifier copy() { return new ConfiguredEqualsVerifier( EnumSet.copyOf(warningsToSuppress), prefabValueProvider.copy(), - genericPrefabValueProvider.copy(), + genericFactories.copy(), usingGetClass, fieldnameToGetter ); @@ -88,7 +88,7 @@ public ConfiguredEqualsVerifier withGenericPrefabValues( Class otherType, Func1 factory ) { - PrefabValuesApi.addGenericPrefabValues(genericPrefabValueProvider, otherType, factory); + PrefabValuesApi.addGenericPrefabValues(genericFactories, otherType, factory); return this; } @@ -98,7 +98,7 @@ public ConfiguredEqualsVerifier withGenericPrefabValues( Class otherType, Func2 factory ) { - PrefabValuesApi.addGenericPrefabValues(genericPrefabValueProvider, otherType, factory); + PrefabValuesApi.addGenericPrefabValues(genericFactories, otherType, factory); return this; } @@ -139,7 +139,7 @@ public SingleTypeEqualsVerifierApi forClass(Class type) { type, EnumSet.copyOf(warningsToSuppress), prefabValueProvider.copy(), - genericPrefabValueProvider.copy(), + genericFactories.copy(), objenesis, usingGetClass, fieldnameToGetter diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java index 8d36e0492..b91e4a22a 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java @@ -1,6 +1,5 @@ package nl.jqno.equalsverifier.api; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.*; import java.util.function.Function; import nl.jqno.equalsverifier.EqualsVerifier; @@ -10,7 +9,7 @@ import nl.jqno.equalsverifier.Warning; import nl.jqno.equalsverifier.internal.checkers.*; import nl.jqno.equalsverifier.internal.exceptions.MessagingException; -import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider; +import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider.GenericFactories; import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; import nl.jqno.equalsverifier.internal.util.*; import nl.jqno.equalsverifier.internal.util.Formatter; @@ -32,8 +31,7 @@ public class SingleTypeEqualsVerifierApi implements EqualsVerifierApi { private boolean hasRedefinedSuperclass = false; private Class redefinedSubclass = null; private PrefabValueProvider prefabValueProvider = new PrefabValueProvider(); - private GenericPrefabValueProvider genericPrefabValueProvider = - new GenericPrefabValueProvider(); + private GenericFactories genericFactories = new GenericFactories(); private CachedHashCodeInitializer cachedHashCodeInitializer = CachedHashCodeInitializer.passthrough(); private Function fieldnameToGetter = null; @@ -72,21 +70,17 @@ public SingleTypeEqualsVerifierApi(Class type, Objenesis objenesis) { * @param type The class for which the {@code equals} method should be tested. * @param warningsToSuppress A list of warnings to suppress in {@code EqualsVerifier}. * @param prefabValueProvider ValueProvider that records prefab values. - * @param genericPrefabValueProvider ValueProvider that records generic prefab values. + * @param genericFactories ValueProvider that records generic prefab values. * @param objenesis To instantiate non-record classes. * @param usingGetClass Whether {@code getClass} is used in the implementation of the {@code * equals} method, instead of an {@code instanceof} check. * @param converter A function that converts from field name to getter name. */ - @SuppressFBWarnings( - value = "EI_EXPOSE_REP2", - justification = "GenericPrefabValueProvider has a mutable element" - ) public SingleTypeEqualsVerifierApi( Class type, EnumSet warningsToSuppress, PrefabValueProvider prefabValueProvider, - GenericPrefabValueProvider genericPrefabValueProvider, + GenericFactories genericFactories, Objenesis objenesis, boolean usingGetClass, Function converter @@ -94,7 +88,7 @@ public SingleTypeEqualsVerifierApi( this(type, objenesis); this.warningsToSuppress = EnumSet.copyOf(warningsToSuppress); this.prefabValueProvider = prefabValueProvider; - this.genericPrefabValueProvider = genericPrefabValueProvider; + this.genericFactories = genericFactories; this.usingGetClass = usingGetClass; this.fieldnameToGetter = converter; } @@ -170,7 +164,7 @@ public SingleTypeEqualsVerifierApi withGenericPrefabValues( Class otherType, Func1 factory ) { - PrefabValuesApi.addGenericPrefabValues(genericPrefabValueProvider, otherType, factory); + PrefabValuesApi.addGenericPrefabValues(genericFactories, otherType, factory); return this; } @@ -180,7 +174,7 @@ public SingleTypeEqualsVerifierApi withGenericPrefabValues( Class otherType, Func2 factory ) { - PrefabValuesApi.addGenericPrefabValues(genericPrefabValueProvider, otherType, factory); + PrefabValuesApi.addGenericPrefabValues(genericFactories, otherType, factory); return this; } @@ -450,7 +444,7 @@ private void performVerification() { Context context = new Context<>( config, prefabValueProvider, - genericPrefabValueProvider, + genericFactories, objenesis ); Validations.validateProcessedAnnotations( diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProvider.java index b7f29f0db..c786a641a 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProvider.java @@ -7,70 +7,71 @@ import nl.jqno.equalsverifier.internal.exceptions.EqualsVerifierInternalBugException; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import nl.jqno.equalsverifier.internal.util.Context; +/** + * Provider of instances of generic types; factories of which have been provided by the user. + * + * Needs to be constructed by means of a GenericFactories instance, because of dependencies that + * would otherwise be cyclic, in the {@link Context} class. + */ public class GenericPrefabValueProvider implements ValueProvider { - private final Map> cache = new HashMap<>(); - private ValueProvider provider; - - /** Constructor. */ - public GenericPrefabValueProvider() {} + private final Map> cache; + private final ValueProvider provider; /** - * Private copy constructor. + * Private constructor. * - * @param other The {@link GenericPrefabValueProvider} to copy. + * @param factories The {@link GenericFactories} to use the cache from. + * @param provider A ValueProvider to use to provide instances for generic type parameters. */ - private GenericPrefabValueProvider(GenericPrefabValueProvider other) { - this(); - cache.putAll(other.cache); - setProvider(other.provider); + public GenericPrefabValueProvider(GenericFactories factories, ValueProvider provider) { + this.cache = factories.cache; + this.provider = provider; } /** - * Copies the cache of this GenericPrefabValueProvider into a new instance. - * - * @return A copy of this GenericPrefabValueProvider. + * Container for a cache that will be assigned to the GenericPrefabValueProvider when it is constructed. */ - public GenericPrefabValueProvider copy() { - return new GenericPrefabValueProvider(this); - } + public static class GenericFactories { - /** - * Registers a prefab value with a single generic type argument. - * - * @param type The class of the prefabricated values. - * @param label The label that the prefabricated value is linked to, or null if the value is - * not assigned to any label. - * @param factory A factory that can produce instances for {@code type}. - * @param The type of the instances. - */ - public void register(Class type, String label, Func1 factory) { - Key key = new Key(type, label); - cache.put(key, factory); - } + private final Map> cache = new HashMap<>(); - /** - * Registers a prefab value with two generic type arguments. - * - * @param type The class of the prefabricated values. - * @param label The label that the prefabricated value is linked to, or null if the value is - * not assigned to any label. - * @param factory A factory that can produce instances for {@code type}. - * @param The type of the instances. - */ - public void register(Class type, String label, Func2 factory) { - Key key = new Key(type, label); - cache.put(key, factory); - } + /** + * Registers a prefab value with a single generic type argument. + * + * @param type The class of the prefabricated values. + * @param factory A factory that can produce instances for {@code type}. + * @param The type of the instances. + */ + public void register(Class type, Func1 factory) { + Key key = new Key(type, null); + cache.put(key, factory); + } - /** - * Configures the valueProvider to be used for recursive value lookups. - * - * @param valueProvider The valueProvider to use. - */ - public void setProvider(ValueProvider valueProvider) { - this.provider = valueProvider; + /** + * Registers a prefab value with two generic type arguments. + * + * @param type The class of the prefabricated values. + * @param factory A factory that can produce instances for {@code type}. + * @param The type of the instances. + */ + public void register(Class type, Func2 factory) { + Key key = new Key(type, null); + cache.put(key, factory); + } + + /** + * Copies the cache of this GenericFactories into a new instance. + * + * @return A copy of this GenericFactories. + */ + public GenericFactories copy() { + GenericFactories copy = new GenericFactories(); + copy.cache.putAll(this.cache); + return copy; + } } /** {@inheritDoc} */ diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java index 5aae2d1cc..9b5237da9 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/Context.java @@ -4,6 +4,7 @@ import nl.jqno.equalsverifier.internal.reflection.ClassProbe; import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues; import nl.jqno.equalsverifier.internal.reflection.instantiation.*; +import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider.GenericFactories; import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache; import org.objenesis.Objenesis; @@ -23,7 +24,7 @@ public final class Context { public Context( Configuration configuration, PrefabValueProvider prefabValueProvider, - GenericPrefabValueProvider genericPrefabValueProvider, + GenericFactories genericFactories, Objenesis objenesis ) { this.type = configuration.getType(); @@ -32,29 +33,24 @@ public Context( FactoryCache cache = JavaApiPrefabValues.build(); this.valueProvider = - configureValueProviders( - cache, - prefabValueProvider, - genericPrefabValueProvider, - objenesis - ); + configureValueProviders(cache, prefabValueProvider, genericFactories, objenesis); this.subjectCreator = new SubjectCreator<>(configuration, valueProvider, objenesis); } private static ValueProvider configureValueProviders( FactoryCache factoryCache, PrefabValueProvider prefabValueProvider, - GenericPrefabValueProvider genericPrefabValueProvider, + GenericFactories genericFactories, Objenesis objenesis ) { ChainedValueProvider mainChain = new ChainedValueProvider(prefabValueProvider); ChainedValueProvider vintageChain = new ChainedValueProvider(prefabValueProvider); ValueProvider vintage = new VintageValueProvider(vintageChain, factoryCache, objenesis); + ValueProvider genericPrefab = new GenericPrefabValueProvider(genericFactories, mainChain); - mainChain.register(genericPrefabValueProvider, vintage); - vintageChain.register(genericPrefabValueProvider); - genericPrefabValueProvider.setProvider(mainChain); + mainChain.register(genericPrefab, vintage); + vintageChain.register(genericPrefab); return mainChain; } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java index 7765ba8e4..429d660aa 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java @@ -2,7 +2,7 @@ import nl.jqno.equalsverifier.Func.Func1; import nl.jqno.equalsverifier.Func.Func2; -import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider; +import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider.GenericFactories; import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider; import nl.jqno.equalsverifier.internal.reflection.vintage.ObjectAccessor; import org.objenesis.Objenesis; @@ -61,22 +61,22 @@ public static void addPrefabValuesForField( } public static void addGenericPrefabValues( - GenericPrefabValueProvider provider, + GenericFactories factories, Class otherType, Func1 factory ) { Validations.validateNotNull(factory, "factory is null."); Validations.validateGenericPrefabValues(otherType, 1); - provider.register(otherType, null, factory); + factories.register(otherType, factory); } public static void addGenericPrefabValues( - GenericPrefabValueProvider provider, + GenericFactories factories, Class otherType, Func2 factory ) { Validations.validateNotNull(factory, "factory is null."); Validations.validateGenericPrefabValues(otherType, 2); - provider.register(otherType, null, factory); + factories.register(otherType, factory); } } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProviderTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProviderTest.java index 3c82ebe64..b1a6a1c2f 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProviderTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/instantiation/GenericPrefabValueProviderTest.java @@ -5,6 +5,7 @@ import java.util.*; import nl.jqno.equalsverifier.internal.reflection.Tuple; import nl.jqno.equalsverifier.internal.reflection.TypeTag; +import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider.GenericFactories; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -13,130 +14,89 @@ public class GenericPrefabValueProviderTest { private static final TypeTag INTEGER = new TypeTag(Integer.class); private static final TypeTag STRING = new TypeTag(String.class); private static final TypeTag LIST = new TypeTag(List.class, new TypeTag(String.class)); + private static final TypeTag SET = new TypeTag(Set.class, new TypeTag(String.class)); private static final TypeTag MAP = new TypeTag(Map.class, INTEGER, STRING); + private static final TypeTag ENTRY = new TypeTag(Map.Entry.class, INTEGER, STRING); - private GenericPrefabValueProvider sut = new GenericPrefabValueProvider(); + private PrefabValueProvider prefab = new PrefabValueProvider(); + private GenericFactories factories = new GenericFactories(); + private GenericPrefabValueProvider sut; @BeforeEach public void setup() { - PrefabValueProvider p = new PrefabValueProvider(); - p.register(INTEGER.getType(), null, 3, 2, 3); - p.register(STRING.getType(), null, "a", "b", "a"); - sut.setProvider(p); + prefab.register(INTEGER.getType(), null, 3, 2, 3); + prefab.register(STRING.getType(), null, "a", "b", "a"); } @Test public void generic1_aRegisteredValueCanBeFound() { - sut.register(LIST.getType(), null, (String s) -> list(s)); + factories.register(LIST.getType(), (String s) -> list(s)); + construct(); assertEquals(Tuple.of(list("a"), list("b"), list("a")), sut.provide(LIST, null).get()); } @Test public void generic2_aRegisteredValueCanBeFound() { - sut.register(MAP.getType(), null, (Integer k, String v) -> map(k, v)); + factories.register(MAP.getType(), (Integer k, String v) -> map(k, v)); + construct(); assertEquals(Tuple.of(map(3, "a"), map(2, "b"), map(3, "a")), sut.provide(MAP, null).get()); } - @Test - public void generic1_aValueRegisteredWithALabelCanBeFoundUnderThatLabel() { - sut.register(LIST.getType(), "label", (String s) -> list(s)); - assertEquals(Tuple.of(list("a"), list("b"), list("a")), sut.provide(LIST, "label").get()); - } - - @Test - public void generic2_aValueRegisteredWithALabelCanBeFoundUnderThatLabel() { - sut.register(MAP.getType(), "label", (Integer k, String v) -> map(k, v)); - assertEquals( - Tuple.of(map(3, "a"), map(2, "b"), map(3, "a")), - sut.provide(MAP, "label").get() - ); - } - - @Test - public void generic1_aValueRegisteredWithALabelCanNotBeFoundWithoutThatLabel() { - sut.register(LIST.getType(), "label", (String s) -> list(s)); - assertEquals(Optional.empty(), sut.provide(LIST, null)); - } - - @Test - public void generic2_aValueRegisteredWithALabelCanNotBeFoundWithoutThatLabel() { - sut.register(MAP.getType(), "label", (Integer k, String v) -> map(k, v)); - assertEquals(Optional.empty(), sut.provide(MAP, null)); - } - - @Test - public void generic1_aQueryWithLabelFallsBackToRegisteredValueWithoutLabel() { - sut.register(LIST.getType(), null, (String s) -> list(s)); - assertEquals(Tuple.of(list("a"), list("b"), list("a")), sut.provide(LIST, "label").get()); - } - - @Test - public void generic2_aQueryWithLabelFallsBackToRegisteredValueWithoutLabel() { - sut.register(MAP.getType(), null, (Integer k, String v) -> map(k, v)); - assertEquals( - Tuple.of(map(3, "a"), map(2, "b"), map(3, "a")), - sut.provide(MAP, "label").get() - ); - } - - @Test - public void generic1_aQueryWithLabelPrefersRegisteredValueWithThatLabel() { - sut.register(LIST.getType(), null, (String s) -> list(s)); - sut.register(LIST.getType(), "label", (String s) -> list(s + "x")); - assertEquals( - Tuple.of(list("ax"), list("bx"), list("ax")), - sut.provide(LIST, "label").get() - ); - } - - @Test - public void generic2_aQueryWithLabelPrefersRegisteredValueWithThatLabel() { - sut.register(MAP.getType(), null, (Integer k, String v) -> map(k - 1, v + "x")); - sut.register(MAP.getType(), "label", (Integer k, String v) -> map(k + 1, v + "y")); - assertEquals( - Tuple.of(map(4, "ay"), map(3, "by"), map(4, "ay")), - sut.provide(MAP, "label").get() - ); - } - @Test public void generic1_anUnregisteredValueCanNotBeFound() { + construct(); assertEquals(Optional.empty(), sut.provide(LIST, null)); } @Test public void generic2_anUnregisteredValueCanNotBeFound() { + construct(); assertEquals(Optional.empty(), sut.provide(MAP, null)); } @Test public void generic1_copy() { - sut.register(LIST.getType(), "original", (String s) -> list(s + "x")); - GenericPrefabValueProvider anotherSut = sut.copy(); - sut.register(LIST.getType(), "invisibleToOther", (String s) -> list(s + "y")); + // CHECKSTYLE OFF: VariableDeclarationUsageDistance + factories.register(LIST.getType(), (String s) -> list(s + "x")); + GenericFactories otherFactories = factories.copy(); + factories.register(SET.getType(), (String s) -> set(s + "y")); - assertEquals( - Tuple.of(list("ax"), list("bx"), list("ax")), - anotherSut.provide(LIST, "original").get() + construct(); + assertEquals(Tuple.of(set("ay"), set("by"), set("ay")), sut.provide(SET)); + + GenericPrefabValueProvider anotherSut = new GenericPrefabValueProvider( + otherFactories, + prefab ); - assertEquals(Optional.empty(), anotherSut.provide(LIST, "invisibleToOther")); + assertEquals(Tuple.of(list("ax"), list("bx"), list("ax")), anotherSut.provide(LIST)); + assertEquals(Optional.empty(), anotherSut.provide(SET, null)); + // CHECKSTYLE ON: VariableDeclarationUsageDistance } @Test public void generic2_copy() { - sut.register(MAP.getType(), "original", (Integer k, String v) -> map(k - 1, v + "x")); - GenericPrefabValueProvider anotherSut = sut.copy(); - sut.register( - MAP.getType(), - "invisibleToOther", - (Integer k, String v) -> map(k + 1, v + "y") - ); + // CHECKSTYLE OFF: VariableDeclarationUsageDistance + factories.register(MAP.getType(), (Integer k, String v) -> map(k - 1, v + "x")); + GenericFactories otherFactories = factories.copy(); + factories.register(ENTRY.getType(), (Integer k, String v) -> entry(k + 1, v + "y")); + + construct(); + assertEquals(Tuple.of(entry(4, "ay"), entry(3, "by"), entry(4, "ay")), sut.provide(ENTRY)); + GenericPrefabValueProvider anotherSut = new GenericPrefabValueProvider( + otherFactories, + prefab + ); assertEquals( Tuple.of(map(2, "ax"), map(1, "bx"), map(2, "ax")), anotherSut.provide(MAP, "original").get() ); - assertEquals(Optional.empty(), anotherSut.provide(MAP, "invisibleToOther")); + assertEquals(Optional.empty(), anotherSut.provide(ENTRY, null)); + // CHECKSTYLE ON: VariableDeclarationUsageDistance + } + + private void construct() { + sut = new GenericPrefabValueProvider(factories, prefab); } private List list(String s) { @@ -145,9 +105,19 @@ private List list(String s) { return result; } + private Set set(String s) { + Set result = new HashSet<>(); + result.add(s); + return result; + } + private Map map(Integer k, String v) { Map result = new HashMap<>(); result.put(k, v); return result; } + + private Map.Entry entry(Integer k, String v) { + return new AbstractMap.SimpleEntry<>(k, v); + } } From 32262c5107fbcbb13e5e626d6c35e4a5e79220f6 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Tue, 22 Oct 2024 20:05:32 +0200 Subject: [PATCH 14/15] Fixes a PITest mutant --- .../WithPrefabValuesForFieldTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/WithPrefabValuesForFieldTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/WithPrefabValuesForFieldTest.java index 0d39f7c59..dad621f2d 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/WithPrefabValuesForFieldTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/operational/WithPrefabValuesForFieldTest.java @@ -2,6 +2,7 @@ import java.time.LocalDate; import java.time.Month; +import java.util.Arrays; import java.util.Objects; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; @@ -157,6 +158,14 @@ public void dontThrow_whenAddingPrefabValuesFromAnotherModuleAndThereforeARedCop .verify(); } + @Test + public void succeed_whenPrefabForArrayIsOverridden() { + EqualsVerifier + .forClass(ThrowingArrayContainer.class) + .withPrefabValuesForField("field", new int[] { 1, 2, 3 }, new int[] { 4, 5, 6 }) + .verify(); + } + static final class SinglePrecondition { private final FinalPoint point; @@ -239,4 +248,30 @@ public int hashCode() { return Objects.hash(date); } } + + static final class ThrowingArrayContainer { + + private final int[] field; + + public ThrowingArrayContainer(int[] field) { + this.field = field; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ThrowingArrayContainer)) { + return false; + } + if (field != null && field.length == 1) { + throw new IllegalStateException("Don't use a built-in prefab value!"); + } + ThrowingArrayContainer other = (ThrowingArrayContainer) obj; + return Arrays.equals(field, other.field); + } + + @Override + public int hashCode() { + return Arrays.hashCode(field); + } + } } From e41d7ffc8eec9f10e4cc7064e535dfcf614153ff Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Tue, 22 Oct 2024 20:11:21 +0200 Subject: [PATCH 15/15] Updates CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8493aaa1f..d5035b759 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- The internal instantiation logic has been further refactored, to be more robust and extensible for future enhancements. + ## [3.17.1] - 2024-10-02 ### Fixed