Skip to content

Commit 1ab8b4a

Browse files
committed
refactor: check for valid candidate before picking
Since we pick a random class from the found implementations, we want to filter out unsuitable candidates before we pick one. Otherwise, we might pick one that can't be used while we skip other candidates which would have worked. This also resolves a potential NPE when creating a GenericSpecimen with mismatching type-parameters.
1 parent 1ebe9d2 commit 1ab8b4a

File tree

1 file changed

+9
-4
lines changed

1 file changed

+9
-4
lines changed

src/main/java/com/github/nylle/javafixture/SpecimenFactory.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ public <T> ISpecimen<T> build(final SpecimenType<T> type) {
9696
private <T> ISpecimen<T> implementationOrProxy(final SpecimenType<T> interfaceType) {
9797
try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan()) {
9898
var implementingClasses = scanResult.getClassesImplementing(interfaceType.asClass()).stream()
99-
.filter(x -> interfaceType.isParameterized() || isNotParametrized(x))
99+
.filter(x -> isNotParametrized(x) || interfaceType.isParameterized())
100+
.filter(x -> isNotParametrized(x) || typeParametersMatch(x, interfaceType))
100101
.collect(Collectors.toList());
101102

102103
if (implementingClasses.isEmpty()) {
@@ -121,17 +122,21 @@ private static boolean isNotParametrized(ClassInfo classInfo) {
121122
return classInfo.getTypeSignature() == null || classInfo.getTypeSignature().getTypeParameters().isEmpty();
122123
}
123124

125+
private static <T> boolean typeParametersMatch(ClassInfo implementingClass, SpecimenType<T> genericType) {
126+
return resolveTypeArguments(genericType, implementingClass).length >= implementingClass.getTypeSignature().getTypeParameters().size();
127+
}
128+
124129
private static <T> Type[] resolveTypeArguments(SpecimenType<T> genericType, ClassInfo implementingClass) {
125-
// throws NPE if implementing class has more type arguments than interface
126-
// this was not intended, but luckily causes a fallback to proxy, because we wouldn't be able to resolve the additional type anyway
127130
var typeParameters = IntStream.range(0, genericType.getGenericTypeArguments().length)
128131
.boxed()
129132
.collect(toMap(
130133
i -> genericType.getTypeParameterName(i),
131134
i -> SpecimenType.fromClass(genericType.getGenericTypeArgument(i))));
132135

133136
return implementingClass.getTypeSignature().getTypeParameters().stream()
134-
.map(x -> typeParameters.get(x.getName()).asClass())
137+
.map(x -> typeParameters.getOrDefault(x.getName(), null))
138+
.filter(x -> x != null)
139+
.map(x -> x.asClass())
135140
.toArray(size -> new Type[size]);
136141
}
137142
}

0 commit comments

Comments
 (0)