Skip to content

Commit

Permalink
fix: do not cache finished specimen #102
Browse files Browse the repository at this point in the history
This will allow us to create different objects so we can populate
sets.
  • Loading branch information
akutschera committed Aug 22, 2024
1 parent b77fc43 commit 2fb1442
Show file tree
Hide file tree
Showing 17 changed files with 126 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ public T create(final CustomizationContext customizationContext, Annotation[] an
Map.<String, ISpecimen<?>>of().getOrDefault(
field.getGenericType().getTypeName(),
specimenFactory.build(SpecimenType.fromClass(field.getGenericType()))).create(new CustomizationContext(List.of(), Map.of(), false), field.getAnnotations()))));
return result;
return context.remove(type);
} catch(SpecimenException ignored) {
return context.cached(type, instanceFactory.manufacture(type, customizationContext));
return instanceFactory.manufacture(type, customizationContext);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ public T create(final CustomizationContext customizationContext, Annotation[] an

IntStream.range(0, length).boxed().forEach(i -> Array.set(result, i, specimenFactory.build(SpecimenType.fromClass(type.getComponentType())).create(customizationContext, new Annotation[0])));

return result;
return context.remove(type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public T create(final CustomizationContext customizationContext, Annotation[] an
.filter(x -> specimen != null)
.forEach(x -> collection.add(specimen.create(customizationContext, new Annotation[0])));

return (T) collection;
return context.remove(type);
}

private <G extends Enum> T createEnumSet(CustomizationContext customizationContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ public T create(CustomizationContext customizationContext, Annotation[] annotati
return context.cached(type);
}

return context.cached(type, shuffledStream(derivedTypes)
return shuffledStream(derivedTypes)
.map(derivedType -> specimenFactory.build(derivedType))
.flatMap(derivedSpecimen -> tryCreate(derivedSpecimen, customizationContext, annotations).stream())
.findFirst()
.orElseGet(() -> proxy(customizationContext)));
.orElseGet(() -> proxy(customizationContext));
}

private <R extends T> R proxy(CustomizationContext customizationContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ public T create(CustomizationContext customizationContext, Annotation[] annotati
}

if (type.isInterface()) {
return (T) context.cached(type, instanceFactory.proxy(type, specimens));
return (T) instanceFactory.proxy(type, specimens);
}

if (customizationContext.useRandomConstructor()) {
return context.cached(type, instanceFactory.construct(type, customizationContext));
return instanceFactory.construct(type, customizationContext);
}

return populate(customizationContext);
Expand All @@ -85,9 +85,10 @@ private T populate(CustomizationContext customizationContext) {
field.getGenericType().getTypeName(),
specimenFactory.build(SpecimenType.fromClass(field.getType()))).create(new CustomizationContext(List.of(), Map.of(), false), new Annotation[0]))));
} catch (SpecimenException ex) {
return context.overwrite(type, instanceFactory.construct(type, customizationContext));
context.remove(type);
return instanceFactory.construct(type, customizationContext);
}
return result;
return context.remove(type);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,7 @@ public InterfaceSpecimen(final SpecimenType<T> type, final Context context, fina

@Override
public T create(final CustomizationContext customizationContext, Annotation[] annotations) {
if (context.isCached(type)) {
return context.cached(type);
}

return (T) context.cached(type, instanceFactory.proxy(type));
return (T) instanceFactory.proxy(type);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public T create(final CustomizationContext customizationContext, Annotation[] an
.filter(x -> keySpecimen != null && valueSpecimen != null)
.forEach(x -> map.put(keySpecimen.create(customizationContext, new Annotation[0]), valueSpecimen.create(customizationContext, new Annotation[0])));

return (T) map;
return context.remove(type);
}

private Map<K, V> createFromConcreteType(final SpecimenType<T> type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public T create(CustomizationContext customizationContext, Annotation[] annotati
}

if (customizationContext.useRandomConstructor()) {
return context.cached(type, instanceFactory.construct(type, customizationContext));
return instanceFactory.construct(type, customizationContext);
}

return populate(customizationContext);
Expand All @@ -72,6 +72,6 @@ private T populate(CustomizationContext customizationContext) {
} catch (SpecimenException ex) {
return context.overwrite(type, instanceFactory.construct(type, customizationContext));
}
return result;
return context.remove(type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,15 @@ void argumentsCanBeCustomized() {

@Test
@DisplayName("constructor arguments are cached")
void constructorArgumentsAreCached() {
void constructorArgumentsAreNotCached() {
var sut = new InstanceFactory(new SpecimenFactory(new Context(Configuration.configure())));

var customizationContext = new CustomizationContext(List.of(), Map.of(), true);
TestObject first = sut.construct(fromClass(TestObject.class), customizationContext);
TestObject second = sut.construct(fromClass(TestObject.class), customizationContext);

assertThat(first.getIntegers()).usingRecursiveComparison().isEqualTo(second.getIntegers());
assertThat(first.getStrings()).usingRecursiveComparison().isEqualTo(second.getStrings());
assertThat(first.getIntegers()).usingRecursiveComparison().isNotEqualTo(second.getIntegers());
assertThat(first.getStrings()).usingRecursiveComparison().isNotEqualTo(second.getStrings());
assertThat(first.getValue()).as("primitives are never cached").isNotEqualTo(second.getValue());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,23 +108,27 @@ void voidAbstractMethodsWillJustReturnVoid() {

@Test
void createAbstractClassWithoutConstructor() {
var sut = new AbstractSpecimen<Charset>(SpecimenType.fromClass(Charset.class), context, specimenFactory);
SpecimenType<Charset> specimenType = SpecimenType.fromClass(Charset.class);
var sut = new AbstractSpecimen<>(specimenType, context, specimenFactory);
assertThat(context.isCached(specimenType)).isFalse();

var actual = sut.create(noContext(), new Annotation[0]);

assertThat(actual).isInstanceOf(Charset.class);
assertThat(context.isCached(specimenType)).isFalse();
}

@Test
void resultIsCached() {
void resultIsNotCached() {

var original = new AbstractSpecimen<TestAbstractClass>(SpecimenType.fromClass(TestAbstractClass.class), context, specimenFactory).create(noContext(), new Annotation[0]);
var cached = new AbstractSpecimen<TestAbstractClass>(SpecimenType.fromClass(TestAbstractClass.class), context, specimenFactory).create(noContext(), new Annotation[0]);
var second = new AbstractSpecimen<TestAbstractClass>(SpecimenType.fromClass(TestAbstractClass.class), context, specimenFactory).create(noContext(), new Annotation[0]);

assertThat(original).isInstanceOf(TestAbstractClass.class);
assertThat(original).isSameAs(cached);
assertThat(original.toString()).isEqualTo(cached.toString());
assertThat(original.getString()).isSameAs(cached.getString());
assertThat(original).isNotEqualTo(second);
assertThat(original.hashCode()).isNotEqualTo(second.hashCode());
assertThat(original.toString()).isNotEqualTo(second.toString());
assertThat(original.getString()).isNotEqualTo(second.getString());
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.junit.jupiter.api.Test;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Map;

import static com.github.nylle.javafixture.CustomizationContext.noContext;
Expand Down Expand Up @@ -83,8 +84,18 @@ void canHandleCircularReferences() {
assertThat(actual).isInstanceOf(AccountManager[].class);
assertThat(actual.length).isEqualTo(2);
assertThat(actual[0]).isInstanceOf(AccountManager.class);
assertThat(actual[0]).isSameAs(actual[1]);
assertThat(actual[0]).isNotEqualTo(actual[1]);
assertThat(actual[0].getOtherAccountManagers()).isInstanceOf(AccountManager[].class);
assertThat(actual[0].getOtherAccountManagers()).isSameAs(actual);
}

@Test
void createdArraysAreNotCached() {
var sut = new ArraySpecimen<AccountManager[]>(SpecimenType.fromClass(AccountManager[].class), context, specimenFactory);

var actual = sut.create(noContext(), new Annotation[0]);
var second = sut.create(noContext(), new Annotation[0]);

assertThat(Arrays.asList(actual)).doesNotContainAnyElementsOf(Arrays.asList(second));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,12 @@ void createTreeSetFromSortedSetInterface() {

@Test
void createHashSetFromSetInterface() {
var sut = new CollectionSpecimen<>(new SpecimenType<Set<String>>() {}, context, specimenFactory);
var sut = new CollectionSpecimen<>(new SpecimenType<Set<Object>>() {}, context, specimenFactory);

var actual = sut.create(noContext(), new Annotation[0]);

assertThat(actual).isInstanceOf(HashSet.class);
assertThat(actual.stream().allMatch(x -> x.getClass().equals(String.class))).isTrue();
assertThat(actual.stream().allMatch(x -> x.getClass().equals(Object.class))).isTrue();
assertThat(actual.size()).isEqualTo(2);
}

Expand Down Expand Up @@ -295,16 +295,16 @@ void createLinkedList() {
}

@Test
void resultIsCached() {
void resultIsNotCached() {

var original = new CollectionSpecimen<>(new SpecimenType<List<String>>() {}, context, specimenFactory).create(noContext(), new Annotation[0]);
var cached = new CollectionSpecimen<>(new SpecimenType<List<String>>() {}, context, specimenFactory).create(noContext(), new Annotation[0]);
var second = new CollectionSpecimen<>(new SpecimenType<List<String>>() {}, context, specimenFactory).create(noContext(), new Annotation[0]);

assertThat(original).isInstanceOf(List.class);
assertThat(original.size()).isEqualTo(2);
assertThat(original).isSameAs(cached);
assertThat(original.get(0)).isEqualTo(cached.get(0));
assertThat(original.get(1)).isEqualTo(cached.get(1));
assertThat(original.size()).as("collection size should be two because of this test's context").isEqualTo(2);
assertThat(original).isNotEqualTo(second);
assertThat(original.get(0)).isNotEqualTo(second.get(0));
assertThat(original.get(1)).isNotEqualTo(second.get(1));
}

@Test
Expand All @@ -323,7 +323,7 @@ void nestedLists() {
}

@Test
void nonPrimitiveElementsAreSameInstance() {
void nonPrimitiveElementsAreNotCached() {

var sut = new CollectionSpecimen<>(new SpecimenType<List<TestObject>>() {}, context, specimenFactory);

Expand All @@ -333,7 +333,7 @@ void nonPrimitiveElementsAreSameInstance() {
assertThat(actual.size()).isEqualTo(2);
assertThat(actual.get(0)).isExactlyInstanceOf(TestObject.class);
assertThat(actual.get(1)).isExactlyInstanceOf(TestObject.class);
assertThat(actual.get(0)).isSameAs(actual.get(1));
assertThat(actual.get(0)).isNotEqualTo(actual.get(1));

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,16 @@ void typeMustNotBeMap() {
class WhenCallingCreate {

@Test
@DisplayName("the result is cached")
void resultIsCached() {
@DisplayName("the result is not cached")
void resultIsNotCached() {
var original = new InterfaceSpecimen<InterfaceWithoutImplementation>(SpecimenType.fromClass(InterfaceWithoutImplementation.class), context, specimenFactory).create(noContext(), new Annotation[0]);
var cached = new InterfaceSpecimen<InterfaceWithoutImplementation>(SpecimenType.fromClass(InterfaceWithoutImplementation.class), context, specimenFactory).create(noContext(), new Annotation[0]);
var second = new InterfaceSpecimen<InterfaceWithoutImplementation>(SpecimenType.fromClass(InterfaceWithoutImplementation.class), context, specimenFactory).create(noContext(), new Annotation[0]);

assertThat(original)
.isInstanceOf(InterfaceWithoutImplementation.class)
.isSameAs(cached);
assertThat(original.toString()).isEqualTo(cached.toString());
assertThat(original.getTestObject()).isSameAs(cached.getTestObject());
.isNotEqualTo(second);
assertThat(original.toString()).isNotEqualTo(second.toString());
assertThat(original.getTestObject()).isNotEqualTo(second.getTestObject());
}

@Nested
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
import com.github.nylle.javafixture.SpecimenException;
import com.github.nylle.javafixture.SpecimenFactory;
import com.github.nylle.javafixture.SpecimenType;
import com.github.nylle.javafixture.annotations.testcases.TestCase;
import com.github.nylle.javafixture.annotations.testcases.TestWithCases;
import com.github.nylle.javafixture.testobjects.TestObject;
import com.github.nylle.javafixture.testobjects.TestObjectGeneric;
import com.github.nylle.javafixture.testobjects.inheritance.GenericChild;
import com.github.nylle.javafixture.testobjects.withconstructor.TestObjectWithConstructedField;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
Expand Down Expand Up @@ -108,6 +111,15 @@ void subSpecimenAreProperlyCached() {
assertThat(result.getU()).isInstanceOf(Optional.class);
}

@Test
void constructedGenericsAreNotCached() {
var first = new GenericSpecimen<>(new SpecimenType<Optional<Integer>>() {}, context, specimenFactory).create(noContext(), new Annotation[0]);
var second = new GenericSpecimen<>(new SpecimenType<Optional<Integer>>() {}, context, specimenFactory).create(noContext(), new Annotation[0]);

assertThat(first).isNotEqualTo(second);

}

@Test
void cannotSetNonExistingField() {
var sut = new GenericSpecimen<>(new SpecimenType<TestObjectGeneric<String, Integer>>() {}, context, specimenFactory);
Expand Down Expand Up @@ -147,6 +159,42 @@ void customFieldIsOnlyUsedInTopLevelObject() {
assertThat(actual.getTestObject().getIntegers()).isNotEmpty();
}

@Test
void createdObjectsAreNotCached() {
var sut = new GenericSpecimen<>(new SpecimenType<WithTestObject<Integer>>() {}, context, specimenFactory);
var actual = sut.create(new CustomizationContext(List.of(), Map.of(), false), new Annotation[0]);
var second = sut.create(new CustomizationContext(List.of(), Map.of(), false), new Annotation[0]);

assertThat(actual).isNotEqualTo(second);
}

@DisplayName("objects are not cached, neither constructed nor populated")
@TestWithCases
@TestCase(bool1 = true)
@TestCase(bool1 = false)
void constructedObjectsAreNotCached(boolean useConstructor) {
var sut = new GenericSpecimen<>(new SpecimenType<WithTestObject<TestObjectWithConstructedField>>() {}, context, specimenFactory);

var actual = sut.create(new CustomizationContext(List.of(), Map.of(), useConstructor), new Annotation[0]);
var second = sut.create(new CustomizationContext(List.of(), Map.of(), useConstructor), new Annotation[0]);

assertThat(actual).isNotEqualTo(second);
}

@DisplayName("when generic type is an interface, we will fixture the non-default methods")
@Test
void interfacesAreFixtured() {
var sut = new GenericSpecimen<>(new SpecimenType<Comparable<Integer>>() {}, context, specimenFactory);

var actual = sut.create(new CustomizationContext(List.of(), Map.of(), false), new Annotation[0]);
var second = sut.create(new CustomizationContext(List.of(), Map.of(), false), new Annotation[0]);

assertThat(actual.compareTo(0))
.as("two different fixtured objects should have two different fixtured results")
.isNotEqualTo(second.compareTo(0));
assertThat(actual).isNotEqualTo(second);
}

@Nested
@DisplayName("when specimen has superclass")
class WhenInheritance {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ void createInterface() {
}

@Test
void resultIsCached() {
void resultIsNotCached() {

var original = new InterfaceSpecimen<InterfaceWithoutImplementation>(SpecimenType.fromClass(InterfaceWithoutImplementation.class), context, specimenFactory).create(noContext(), new Annotation[0]);
var cached = new InterfaceSpecimen<InterfaceWithoutImplementation>(SpecimenType.fromClass(InterfaceWithoutImplementation.class), context, specimenFactory).create(noContext(), new Annotation[0]);
var second = new InterfaceSpecimen<InterfaceWithoutImplementation>(SpecimenType.fromClass(InterfaceWithoutImplementation.class), context, specimenFactory).create(noContext(), new Annotation[0]);

assertThat(original).isInstanceOf(InterfaceWithoutImplementation.class);
assertThat(original).isSameAs(cached);
assertThat(original.toString()).isEqualTo(cached.toString());
assertThat(original.getTestObject()).isSameAs(cached.getTestObject());
assertThat(original).isNotEqualTo(second);
assertThat(original.toString()).isNotEqualTo(second.toString());
assertThat(original.getTestObject()).isNotEqualTo(second.getTestObject());
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -183,16 +183,15 @@ void createEnumMap() {
}

@Test
void resultIsCached() {
void resultIsNotCached() {

var original = new MapSpecimen<>(new SpecimenType<Map<String, Integer>>() {}, context, specimenFactory).create(noContext(), new Annotation[0]);
var cached = new MapSpecimen<>(new SpecimenType<Map<String, Integer>>() {}, context, specimenFactory).create(noContext(), new Annotation[0]);
var second = new MapSpecimen<>(new SpecimenType<Map<String, Integer>>() {}, context, specimenFactory).create(noContext(), new Annotation[0]);

assertThat(original).isInstanceOf(Map.class);
assertThat(original.size()).isEqualTo(2);
assertThat(original).isSameAs(cached);
assertThat(original.get(0)).isEqualTo(cached.get(0));
assertThat(original.get(1)).isEqualTo(cached.get(1));
assertThat(original).isNotEqualTo(second);
assertThat(original.values()).doesNotContainAnyElementsOf(second.values());
}

@Test
Expand Down
Loading

0 comments on commit 2fb1442

Please sign in to comment.