Type arguments are missing in some situations
lmartelli opened this issue · comments
I have an ArbitraryProvider
implementation use in a context of a hierarchy of abstract inner classes of an abstract test class like this (I will provide for a complete minimal "working" example, but this should give you a rough picture) :
public class AbstractCrudControllerTest {
public abstract class EndpointWithParamsTest<PARAMS> extends SecuredEndpointTest {
protected final Function<PARAMS, ResultActions> endpoint;
protected final Authority requiredAuthority;
public EndpointWithParamsTest(Authority requiredAuthority, Function<PARAMS, ResultActions> endpoint) {
this.requiredAuthority = requiredAuthority;
this.endpoint = endpoint;
}
@Property
void without_authority_returns_forbidden(@ForAll Authority authority, @ForAll PARAMS params) {
givenAuthority(authority).isNot(requiredAuthority);
endpoint.apply(params).andExpect(status().isForbidden());
}
}
public abstract class FilteredAndPagedEndpointTest<FILTERS> extends EndpointWithParamsTest<FilteredAndPagedParams<FILTERS>> {
public FilteredAndPagedEndpointTest(Authority requiredAuthority) {
super(requiredAuthority, AbstractCrudControllerTest.this::getPage);
}
@Property
void returns_requested_page(@ForAll List<T> expectedEntities, @ForAll FilteredAndPagedParams<FILTERS> params) {
can_get_page(params, expectedEntities);
}
}
}
@Domain(MyDomain.class)
class ConfigurationEntrepotControllerTest extends AbstractCrudControllerTest {
@Group
class GetConfigurations extends FilteredAndPagedEndpointTest<ConfigurationEntrepot.Filters> {
public GetConfigurations() {
super(VOIR_CONFIGURATION_ENTREPOT);
}
}
@Domain(DomainContext.Global.class)
public class MyDomain extends DomainContextBase {
public class FilteredAndPagedParamsArbitraryProvider implements ArbitraryProvider {
@Override
public boolean canProvideFor(TypeUsage targetType) {
return targetType.isOfType(FilteredAndPagedParams.class);
}
@Override
public Set<Arbitrary<?>> provideFor(TypeUsage targetType, SubtypeProvider subtypeProvider) {
TypeUsage innerType = targetType.getTypeArguments().get(0);
var innerTypeProviders = subtypeProvider.apply(innerType);
if (innerTypeProviders.isEmpty())
throw new RuntimeException("Cannot find any Arbitrary provider for " + innerType.getType().getTypeName());
return seq(innerTypeProviders)
.map(arbitrary ->
combine(pageRequests(), arbitrary)
.as(FilteredAndPagedParams::new))
.collect(Collectors.toSet());
}
}
}
When without_authority_returns_forbidden()
is run for Group GetConfigurations
, provideFor()
fails with java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
because the type arguments are missing.
Quite a complicated setup you have there ;-) Still trying to wrap my head around it.
What is the actual value of targetType
in the failing case?
And yes, a reproducible example would help me figure out the problem more easily.
Quite a complicated setup you have there ;-)
Agreed !
Still trying to wrap my head around it.
What is the actual value of
targetType
in the failing case?And yes, a reproducible example would help me figure out the problem more easily.
I've created a simple test project here : https://github.com/lmartelli/jqwik-issue-492
It's all in one file (see below).
Inner2
group runs fine with value set to various types.
But Inner3
fails :
provideFor net.jqwik.issue_492.BugTest$GenericType<java.lang.String>
targetType=@net.jqwik.api.ForAll(value="", supplier=net.jqwik.api.ArbitrarySupplier$NONE.class) GenericType
timestamp = 2023-06-20T21:44:35.438978394, Inner3:genericProperty =
java.lang.IndexOutOfBoundsException:
Index 0 out of bounds for length 0
package net.jqwik.issue_492;
import net.jqwik.api.*;
import net.jqwik.api.domains.Domain;
import net.jqwik.api.domains.DomainContext;
import net.jqwik.api.domains.DomainContextBase;
import net.jqwik.api.providers.ArbitraryProvider;
import net.jqwik.api.providers.TypeUsage;
import java.util.Set;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
@Domain(BugTest.ConfigurationDomain.class)
@PropertyDefaults(tries = 10)
public class BugTest {
public abstract class Inner1<PARAMS> {
@Property
void genericProperty(@ForAll PARAMS params) {
System.out.println("params = " + params);
assertThat(params).isNotNull();
}
}
@Group
public class Inner2<T> extends Inner1<GenericType<T>> {
}
public record GenericType<T>(T value) {
}
@Group
class Inner3 extends Inner2<String> {
}
@Domain(DomainContext.Global.class)
static class ConfigurationDomain extends DomainContextBase {
public class GenericTypeArbitraryProvider implements ArbitraryProvider {
@Override
public boolean canProvideFor(TypeUsage targetType) {
return targetType.isOfType(BugTest.GenericType.class);
}
@Override
public Set<Arbitrary<?>> provideFor(TypeUsage targetType, SubtypeProvider subtypeProvider) {
System.out.println("provideFor " + targetType.getType().getTypeName());
System.out.println("targetType=" + targetType);
TypeUsage innerType = targetType.getTypeArguments().get(0);
System.out.println("TypeArgument = " + innerType.getType());
var innerTypeProviders = subtypeProvider.apply(innerType);
if (innerTypeProviders.isEmpty())
throw new RuntimeException("Cannot find any Arbitrary provider for " + innerType.getType().getTypeName());
return innerTypeProviders.stream()
.map(arbitrary -> arbitrary.map(BugTest.GenericType::new))
.collect(Collectors.toSet());
}
}
}
}
Judging by the amount of generics, https://github.com/harawata/typeparameterresolver might be relevant
Judging by the amount of generics, https://github.com/harawata/typeparameterresolver might be relevant
I've just tried it on my use case (lmartelli/jqwik-issue-492@de206e3) but it does not seem to provide more information.
Thanks @lmartelli for the reproducing example. It's really helpfully and it strongly suggests a bug in jqwik's type resolution to me. I'll put the bug as first thing on my list for 1.7.5 since 1.7.4 will have to be released soonish in order to get the micronaut extension on the road.
Next on my list for 1.8.0
Should be fixed.
@lmartelli You can try out 1.8.0-SNAPCHAR
Reopen if it does not work for you.