diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a4af6a3f..14bb563cb 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] +### Fixed + +- StackOverflowError when a class has a field of a sealed type whose only permitted subtype has a reference to the original class. ([Issue 920](https://github.com/jqno/equalsverifier/issues/920)) + ## [3.15.6] - 2024-01-09 ### Fixed diff --git a/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessorScramblingTest.java b/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessorScramblingTest.java index 49ff6d2a9..ce1af09c6 100644 --- a/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessorScramblingTest.java +++ b/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessorScramblingTest.java @@ -1,27 +1,22 @@ package nl.jqno.equalsverifier.internal.reflection; import static nl.jqno.equalsverifier.internal.prefabvalues.factories.Factories.values; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import nl.jqno.equalsverifier.internal.exceptions.EqualsVerifierInternalBugException; -import nl.jqno.equalsverifier.internal.prefabvalues.FactoryCache; -import nl.jqno.equalsverifier.internal.prefabvalues.JavaApiPrefabValues; -import nl.jqno.equalsverifier.internal.prefabvalues.PrefabValues; -import nl.jqno.equalsverifier.internal.prefabvalues.TypeTag; +import nl.jqno.equalsverifier.internal.prefabvalues.*; import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class RecordObjectAccessorScramblingTest { + private static final LinkedHashSet EMPTY_TYPE_STACK = new LinkedHashSet<>(); private FactoryCache factoryCache; private PrefabValues prefabValues; @@ -137,7 +132,7 @@ private Object fieldValue(ObjectAccessor accessor, String fieldName) } private ObjectAccessor doScramble(Object object) { - return create(object).scramble(prefabValues, TypeTag.NULL); + return create(object).scramble(prefabValues, TypeTag.NULL, EMPTY_TYPE_STACK); } record Point(int x, int y) {} diff --git a/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessorTest.java b/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessorTest.java index 3e3948b54..beb9eb68e 100644 --- a/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessorTest.java +++ b/equalsverifier-16/src/test/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessorTest.java @@ -6,6 +6,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.util.LinkedHashSet; import java.util.Objects; import nl.jqno.equalsverifier.internal.exceptions.ReflectionException; import nl.jqno.equalsverifier.internal.prefabvalues.JavaApiPrefabValues; @@ -17,6 +18,7 @@ public class RecordObjectAccessorTest { + private static final LinkedHashSet EMPTY_TYPE_STACK = new LinkedHashSet<>(); private Object recordInstance; @BeforeEach @@ -72,7 +74,7 @@ public void fail_whenConstructorThrowsOnSomethingElse() { PrefabValues pv = new PrefabValues(JavaApiPrefabValues.build()); ExpectedException - .when(() -> accessorFor(instance).scramble(pv, TypeTag.NULL)) + .when(() -> accessorFor(instance).scramble(pv, TypeTag.NULL, EMPTY_TYPE_STACK)) .assertThrows(ReflectionException.class) .assertMessageContains("Record:", "failed to run constructor", "prefab values"); } diff --git a/equalsverifier-17/src/test/java/nl/jqno/equalsverifier/integration/extended_contract/SealedTypesRecursionTest.java b/equalsverifier-17/src/test/java/nl/jqno/equalsverifier/integration/extended_contract/SealedTypesRecursionTest.java new file mode 100644 index 000000000..e1adf31da --- /dev/null +++ b/equalsverifier-17/src/test/java/nl/jqno/equalsverifier/integration/extended_contract/SealedTypesRecursionTest.java @@ -0,0 +1,124 @@ +package nl.jqno.equalsverifier.integration.extended_contract; + +import java.util.Objects; +import nl.jqno.equalsverifier.EqualsVerifier; +import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; +import org.junit.jupiter.api.Test; + +class SealedTypesRecursionTest { + + @Test + public void dontThrowStackOverflowError_whenOnlyPermittedSubclassInSealedInterfaceRefersBackToContainer() { + // A container with a field of a sealed interface. + // The sealed interface permits only 1 type, which refers back to the container. + ExpectedException + .when(() -> EqualsVerifier.forClass(SealedContainer.class).verify()) + .assertFailure() + .assertMessageContains( + "Recursive datastructure", + "Add prefab values for one of the following types", + "SealedContainer", + "SealedInterface" + ); + } + + @Test + public void dontThrowStackOverflowError_whenOnlyPermittedRecordInSealedInterfaceRefersBackToContainer() { + // A container with a field of a sealed interface. + // The sealed interface permits only 1 type, which is a record that refers back to the container. + ExpectedException + .when(() -> EqualsVerifier.forClass(SealedRecordContainer.class).verify()) + .assertFailure() + .assertMessageContains( + "Recursive datastructure", + "Add prefab values for one of the following types", + "SealedRecordContainer", + "SealedRecordInterface" + ); + } + + static final class SealedContainer { + + public final SealedInterface sealed; + + public SealedContainer(SealedInterface sealed) { + this.sealed = sealed; + } + + @Override + public int hashCode() { + return Objects.hash(sealed); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof SealedContainer)) { + return false; + } + SealedContainer other = (SealedContainer) obj; + return Objects.equals(sealed, other.sealed); + } + } + + sealed interface SealedInterface permits OnlyPermittedImplementation {} + + static final class OnlyPermittedImplementation implements SealedInterface { + + public final SealedContainer container; + + public OnlyPermittedImplementation(SealedContainer container) { + this.container = container; + } + + @Override + public int hashCode() { + return Objects.hash(container); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof OnlyPermittedImplementation)) { + return false; + } + OnlyPermittedImplementation other = (OnlyPermittedImplementation) obj; + return Objects.equals(container, other.container); + } + } + + static final class SealedRecordContainer { + + public final SealedRecordInterface sealed; + + public SealedRecordContainer(SealedRecordInterface sealed) { + this.sealed = sealed; + } + + @Override + public int hashCode() { + return Objects.hash(sealed); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof SealedRecordContainer)) { + return false; + } + SealedRecordContainer other = (SealedRecordContainer) obj; + return Objects.equals(sealed, other.sealed); + } + } + + sealed interface SealedRecordInterface permits OnlyPermittedRecordImplementation {} + + static final record OnlyPermittedRecordImplementation(SealedRecordContainer container) + implements SealedRecordInterface {} +} diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/prefabvalues/PrefabValues.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/prefabvalues/PrefabValues.java index e75cac3ea..799e7fd0d 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/prefabvalues/PrefabValues.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/prefabvalues/PrefabValues.java @@ -78,7 +78,19 @@ public T giveRedCopy(TypeTag tag) { * @return A tuple of two different values of the given type. */ public Tuple giveTuple(TypeTag tag) { - realizeCacheFor(tag, emptyStack()); + return giveTuple(tag, new LinkedHashSet<>()); + } + + /** + * Returns a tuple of two different prefabricated values of the specified type. + * + * @param The returned tuple will have this generic type. + * @param tag A description of the desired type, including generic parameters. + * @param typeStack Keeps track of recursion in the type. + * @return A tuple of two different values of the given type. + */ + public Tuple giveTuple(TypeTag tag, LinkedHashSet typeStack) { + realizeCacheFor(tag, typeStack); return cache.getTuple(tag); } @@ -92,6 +104,20 @@ public Tuple giveTuple(TypeTag tag) { * @return A value that is different from {@code value}. */ public T giveOther(TypeTag tag, T value) { + return giveOther(tag, value, new LinkedHashSet<>()); + } + + /** + * Returns a prefabricated value of the specified type, that is different from the specified + * value. + * + * @param The type of the value. + * @param tag A description of the desired type, including generic parameters. + * @param value A value that is different from the value that will be returned. + * @param typeStack Keeps track of recursion in the type. + * @return A value that is different from {@code value}. + */ + public T giveOther(TypeTag tag, T value, LinkedHashSet typeStack) { Class type = tag.getType(); if ( value != null && @@ -101,7 +127,7 @@ public T giveOther(TypeTag tag, T value) { throw new ReflectionException("TypeTag does not match value."); } - Tuple tuple = giveTuple(tag); + Tuple tuple = giveTuple(tag, typeStack); if (tuple.getRed() == null) { return null; } @@ -123,10 +149,6 @@ private boolean arraysAreDeeplyEqual(Object x, Object y) { return Arrays.deepEquals(new Object[] { x }, new Object[] { y }); } - private LinkedHashSet emptyStack() { - return new LinkedHashSet<>(); - } - /** * Makes sure that values for the specified type are present in the cache, but doesn't return * them. diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/prefabvalues/factories/FallbackFactory.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/prefabvalues/factories/FallbackFactory.java index a49f3728f..5ee82eed2 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/prefabvalues/factories/FallbackFactory.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/prefabvalues/factories/FallbackFactory.java @@ -37,7 +37,7 @@ public Tuple createValues( } traverseFields(tag, prefabValues, clone); - return giveInstances(tag, prefabValues); + return giveInstances(tag, prefabValues, clone); } private Tuple giveEnumInstances(TypeTag tag) { @@ -90,11 +90,15 @@ private void traverseFields( } } - private Tuple giveInstances(TypeTag tag, PrefabValues prefabValues) { + private Tuple giveInstances( + TypeTag tag, + PrefabValues prefabValues, + LinkedHashSet typeStack + ) { ClassAccessor accessor = ClassAccessor.of(tag.getType(), prefabValues); - T red = accessor.getRedObject(tag); - T blue = accessor.getBlueObject(tag); - T redCopy = accessor.getRedObject(tag); + T red = accessor.getRedObject(tag, typeStack); + T blue = accessor.getBlueObject(tag, typeStack); + T redCopy = accessor.getRedObject(tag, typeStack); return new Tuple<>(red, blue, redCopy); } } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/ClassAccessor.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/ClassAccessor.java index d78a11179..247645456 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/ClassAccessor.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/ClassAccessor.java @@ -4,6 +4,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.LinkedHashSet; import java.util.Set; import java.util.function.Predicate; import nl.jqno.equalsverifier.internal.prefabvalues.PrefabValues; @@ -185,6 +186,19 @@ public T getRedObject(TypeTag enclosingType) { return getRedAccessor(enclosingType).get(); } + /** + * Returns an instance of T that is not equal to the instance of T returned by {@link + * #getBlueObject(TypeTag)}. + * + * @param enclosingType Describes the type that contains this object as a field, to determine + * any generic parameters it may contain. + * @param typeStack Keeps track of recursion in the type. + * @return An instance of T. + */ + public T getRedObject(TypeTag enclosingType, LinkedHashSet typeStack) { + return getRedAccessor(enclosingType, typeStack).get(); + } + /** * Returns an {@link ObjectAccessor} for {@link #getRedObject(TypeTag)}. * @@ -193,7 +207,22 @@ public T getRedObject(TypeTag enclosingType) { * @return An {@link ObjectAccessor} for {@link #getRedObject(TypeTag)}. */ public ObjectAccessor getRedAccessor(TypeTag enclosingType) { - return buildObjectAccessor().scramble(prefabValues, enclosingType); + return getRedAccessor(enclosingType, new LinkedHashSet<>()); + } + + /** + * Returns an {@link ObjectAccessor} for {@link #getRedObject(TypeTag)}. + * + * @param enclosingType Describes the type that contains this object as a field, to determine + * any generic parameters it may contain. + * @param typeStack Keeps track of recursion in the type. + * @return An {@link ObjectAccessor} for {@link #getRedObject(TypeTag)}. + */ + public ObjectAccessor getRedAccessor( + TypeTag enclosingType, + LinkedHashSet typeStack + ) { + return buildObjectAccessor().scramble(prefabValues, enclosingType, typeStack); } /** @@ -208,6 +237,19 @@ public T getBlueObject(TypeTag enclosingType) { return getBlueAccessor(enclosingType).get(); } + /** + * Returns an instance of T that is not equal to the instance of T returned by {@link + * #getRedObject(TypeTag)}. + * + * @param enclosingType Describes the type that contains this object as a field, to determine + * any generic parameters it may contain. + * @param typeStack Keeps track of recursion in the type. + * @return An instance of T. + */ + public T getBlueObject(TypeTag enclosingType, LinkedHashSet typeStack) { + return getBlueAccessor(enclosingType, typeStack).get(); + } + /** * Returns an {@link ObjectAccessor} for {@link #getBlueObject(TypeTag)}. * @@ -216,9 +258,24 @@ public T getBlueObject(TypeTag enclosingType) { * @return An {@link ObjectAccessor} for {@link #getBlueObject(TypeTag)}. */ public ObjectAccessor getBlueAccessor(TypeTag enclosingType) { + return getBlueAccessor(enclosingType, new LinkedHashSet<>()); + } + + /** + * Returns an {@link ObjectAccessor} for {@link #getBlueObject(TypeTag)}. + * + * @param enclosingType Describes the type that contains this object as a field, to determine + * any generic parameters it may contain. + * @param typeStack Keeps track of recursion in the type. + * @return An {@link ObjectAccessor} for {@link #getBlueObject(TypeTag)}. + */ + public ObjectAccessor getBlueAccessor( + TypeTag enclosingType, + LinkedHashSet typeStack + ) { return buildObjectAccessor() - .scramble(prefabValues, enclosingType) - .scramble(prefabValues, enclosingType); + .scramble(prefabValues, enclosingType, typeStack) + .scramble(prefabValues, enclosingType, typeStack); } /** diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldModifier.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldModifier.java index ffb20997d..51ba705c9 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldModifier.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldModifier.java @@ -3,6 +3,7 @@ import static nl.jqno.equalsverifier.internal.util.Rethrow.rethrow; import java.lang.reflect.Field; +import java.util.LinkedHashSet; import nl.jqno.equalsverifier.internal.exceptions.ReflectionException; import nl.jqno.equalsverifier.internal.prefabvalues.PrefabValues; import nl.jqno.equalsverifier.internal.prefabvalues.TypeTag; @@ -92,9 +93,30 @@ public void copyTo(Object to) { * @throws ReflectionException If the operation fails. */ public void changeField(PrefabValues prefabValues, TypeTag enclosingType) { + changeField(prefabValues, enclosingType, new LinkedHashSet<>()); + } + + /** + * Changes the field's value to something else. The new value will never be null. Other than + * that, the precise value is undefined. + * + *

Ignores static fields and fields that can't be modified reflectively. + * + * @param prefabValues If the field is of a type contained within prefabValues, the new value + * will be taken from it. + * @param enclosingType A tag for the type that contains the field. Needed to determine a + * generic type, if it has one.. + * @param typeStack Keeps track of recursion in the type. + * @throws ReflectionException If the operation fails. + */ + public void changeField( + PrefabValues prefabValues, + TypeTag enclosingType, + LinkedHashSet typeStack + ) { FieldChanger fm = () -> { TypeTag tag = TypeTag.of(field, enclosingType); - Object newValue = prefabValues.giveOther(tag, field.get(object)); + Object newValue = prefabValues.giveOther(tag, field.get(object), typeStack); field.set(object, newValue); }; change(fm, false); diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/InPlaceObjectAccessor.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/InPlaceObjectAccessor.java index baae1c349..63ce34a7e 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/InPlaceObjectAccessor.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/InPlaceObjectAccessor.java @@ -1,6 +1,7 @@ package nl.jqno.equalsverifier.internal.reflection; import java.lang.reflect.Field; +import java.util.LinkedHashSet; import java.util.function.Function; import java.util.function.Predicate; import nl.jqno.equalsverifier.internal.exceptions.ModuleException; @@ -49,24 +50,34 @@ private S copyInto(S copy) { /** {@inheritDoc} */ @Override - public ObjectAccessor scramble(PrefabValues prefabValues, TypeTag enclosingType) { - return scrambleInternal(prefabValues, enclosingType, FieldIterable::of); + public ObjectAccessor scramble( + PrefabValues prefabValues, + TypeTag enclosingType, + LinkedHashSet typeStack + ) { + return scrambleInternal(prefabValues, enclosingType, typeStack, FieldIterable::of); } /** {@inheritDoc} */ @Override public ObjectAccessor shallowScramble(PrefabValues prefabValues, TypeTag enclosingType) { - return scrambleInternal(prefabValues, enclosingType, FieldIterable::ofIgnoringSuper); + return scrambleInternal( + prefabValues, + enclosingType, + new LinkedHashSet<>(), + FieldIterable::ofIgnoringSuper + ); } private ObjectAccessor scrambleInternal( PrefabValues prefabValues, TypeTag enclosingType, + LinkedHashSet typeStack, Function, FieldIterable> it ) { for (Field field : it.apply(type())) { try { - fieldModifierFor(field).changeField(prefabValues, enclosingType); + fieldModifierFor(field).changeField(prefabValues, enclosingType, typeStack); } catch (ModuleException e) { handleInaccessibleObjectException(e.getCause(), field); } catch (RuntimeException e) { diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/ObjectAccessor.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/ObjectAccessor.java index 3ba603b0d..e3ec543fa 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/ObjectAccessor.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/ObjectAccessor.java @@ -1,6 +1,7 @@ package nl.jqno.equalsverifier.internal.reflection; import java.lang.reflect.Field; +import java.util.LinkedHashSet; import java.util.function.Predicate; import nl.jqno.equalsverifier.internal.prefabvalues.PrefabValues; import nl.jqno.equalsverifier.internal.prefabvalues.TypeTag; @@ -126,9 +127,14 @@ public T getField(Field field) { * @param prefabValues Prefabricated values to take values from. * @param enclosingType Describes the type that contains this object as a field, to determine * any generic parameters it may contain. + * @param typeStack Keeps track of recursion in the type. * @return An accessor to the scrambled object. */ - public abstract ObjectAccessor scramble(PrefabValues prefabValues, TypeTag enclosingType); + public abstract ObjectAccessor scramble( + PrefabValues prefabValues, + TypeTag enclosingType, + LinkedHashSet typeStack + ); /** * Modifies all fields of the wrapped object that are declared in T, but not those inherited diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessor.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessor.java index 57c1f8bb0..600586fad 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessor.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/RecordObjectAccessor.java @@ -4,6 +4,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.util.LinkedHashSet; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; @@ -57,11 +58,15 @@ public T copyIntoAnonymousSubclass() { /** {@inheritDoc} */ @Override - public ObjectAccessor scramble(PrefabValues prefabValues, TypeTag enclosingType) { + public ObjectAccessor scramble( + PrefabValues prefabValues, + TypeTag enclosingType, + LinkedHashSet typeStack + ) { return makeAccessor(f -> { Object value = getField(f); TypeTag tag = TypeTag.of(f, enclosingType); - return prefabValues.giveOther(tag, value); + return prefabValues.giveOther(tag, value, typeStack); }); } diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/InPlaceObjectAccessorScramblingTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/InPlaceObjectAccessorScramblingTest.java index ebeb78f77..67a3487f4 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/InPlaceObjectAccessorScramblingTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/internal/reflection/InPlaceObjectAccessorScramblingTest.java @@ -1,19 +1,14 @@ package nl.jqno.equalsverifier.internal.reflection; import static nl.jqno.equalsverifier.internal.prefabvalues.factories.Factories.values; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import java.text.AttributedString; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import nl.jqno.equalsverifier.internal.exceptions.ModuleException; -import nl.jqno.equalsverifier.internal.prefabvalues.FactoryCache; -import nl.jqno.equalsverifier.internal.prefabvalues.JavaApiPrefabValues; -import nl.jqno.equalsverifier.internal.prefabvalues.PrefabValues; -import nl.jqno.equalsverifier.internal.prefabvalues.TypeTag; +import nl.jqno.equalsverifier.internal.prefabvalues.*; import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException; import nl.jqno.equalsverifier.testhelpers.types.Point; import nl.jqno.equalsverifier.testhelpers.types.Point3D; @@ -25,6 +20,7 @@ public class InPlaceObjectAccessorScramblingTest { + private static final LinkedHashSet EMPTY_TYPE_STACK = new LinkedHashSet<>(); private PrefabValues prefabValues; @BeforeEach @@ -165,7 +161,7 @@ private T copy(T object) { } private ObjectAccessor doScramble(Object object) { - return create(object).scramble(prefabValues, TypeTag.NULL); + return create(object).scramble(prefabValues, TypeTag.NULL, EMPTY_TYPE_STACK); } static final class StringContainer {