Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type arguments are missing in some situations #492

Closed
lmartelli opened this issue Jun 19, 2023 · 8 comments
Closed

Type arguments are missing in some situations #492

lmartelli opened this issue Jun 19, 2023 · 8 comments
Labels

Comments

@lmartelli
Copy link

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.

@lmartelli lmartelli changed the title Type arguments are mùissing in some situations Type arguments are missing in some situations Jun 19, 2023
@jlink
Copy link
Collaborator

jlink commented Jun 20, 2023

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.

@lmartelli
Copy link
Author

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>
[email protected](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());
            }
        }
    }
}

@vlsi
Copy link
Contributor

vlsi commented Jun 20, 2023

Judging by the amount of generics, https://github.com/harawata/typeparameterresolver might be relevant

@lmartelli
Copy link
Author

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.

@jlink
Copy link
Collaborator

jlink commented Jun 21, 2023

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.

@jlink
Copy link
Collaborator

jlink commented Jul 16, 2023

Next on my list for 1.8.0

jlink added a commit that referenced this issue Jul 19, 2023
jlink added a commit that referenced this issue Jul 19, 2023
@jlink
Copy link
Collaborator

jlink commented Jul 19, 2023

Should be fixed.
@lmartelli You can try out 1.8.0-SNAPCHAR

@jlink
Copy link
Collaborator

jlink commented Jul 21, 2023

Reopen if it does not work for you.

@jlink jlink closed this as completed Jul 21, 2023
@jlink jlink removed the in progress label Jul 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants