Skip to content

Commit

Permalink
Generator functions are now also searched for in outer groups.
Browse files Browse the repository at this point in the history
  • Loading branch information
jlink committed Oct 14, 2017
1 parent 21bd0a4 commit 80055dd
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 20 deletions.
4 changes: 2 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ repositories {
ext.junitPlatformVersion = '1.0.0'
ext.junitJupiterVersion = '5.0.0'
ext.jqwikVersion = '0.5.0'
ext.jqwikVersion = '0.5.1'
junitPlatform {
filters {
Expand Down Expand Up @@ -105,7 +105,7 @@ Add the following repository to your `pom.xml` file:
<dependency>
<groupId>com.github.jlink</groupId>
<artifactId>jqwik</artifactId>
<version>0.5.0</version>
<version>0.5.1</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package net.jqwik.execution;

import net.jqwik.api.*;
import net.jqwik.descriptor.*;
import net.jqwik.execution.providers.*;
import net.jqwik.properties.*;
import org.junit.platform.commons.support.*;
import static net.jqwik.execution.providers.DefaultArbitraryProviders.register;
import static net.jqwik.support.JqwikReflectionSupport.*;
import static org.junit.platform.commons.support.ReflectionSupport.invokeMethod;

import java.lang.annotation.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;

import static net.jqwik.execution.providers.DefaultArbitraryProviders.*;
import static org.junit.platform.commons.support.ReflectionSupport.*;
import org.junit.platform.commons.support.*;

import net.jqwik.api.*;
import net.jqwik.descriptor.PropertyMethodDescriptor;
import net.jqwik.execution.providers.*;
import net.jqwik.properties.Arbitrary;

public class PropertyMethodArbitraryResolver implements ArbitraryResolver {

Expand Down Expand Up @@ -82,7 +84,7 @@ private Arbitrary<?> forType(GenericType genericType, String generatorName, Anno
private Arbitrary<?> createForType(GenericType genericType, String generatorName, Annotation[] annotations) {
Optional<Method> optionalCreator = findArbitraryCreatorByName(genericType, generatorName);
if (optionalCreator.isPresent()) {
return (Arbitrary<?>) invokeMethod(optionalCreator.get(), testInstance);
return (Arbitrary<?>) invokeMethodPotentiallyOuter(optionalCreator.get(), testInstance);
}

Arbitrary<?> defaultArbitrary = findDefaultArbitrary(genericType, generatorName, annotations);
Expand All @@ -98,7 +100,10 @@ private Arbitrary<?> createForType(GenericType genericType, String generatorName

private Optional<Method> findArbitraryCreatorByName(GenericType genericType, String generatorToFind) {
if (generatorToFind.isEmpty()) return Optional.empty();
List<Method> creators = ReflectionSupport.findMethods(descriptor.getContainerClass(), isCreatorForType(genericType), HierarchyTraversalMode.BOTTOM_UP);
List<Method> creators = findMethodsPotentiallyOuter( //
descriptor.getContainerClass(), //
isCreatorForType(genericType), //
HierarchyTraversalMode.BOTTOM_UP);
return creators.stream().filter(generatorMethod -> {
Generate generateAnnotation = generatorMethod.getDeclaredAnnotation(Generate.class);
String generatorName = generateAnnotation.value();
Expand All @@ -112,14 +117,15 @@ private Optional<Method> findArbitraryCreatorByName(GenericType genericType, Str
private Arbitrary<?> findFirstFitArbitrary(GenericType genericType) {
Optional<Method> optionalCreator = findArbitraryCreator(genericType);
if (optionalCreator.isPresent()) {
return (Arbitrary<?>) invokeMethod(optionalCreator.get(), testInstance);
return (Arbitrary<?>) invokeMethodPotentiallyOuter(optionalCreator.get(), testInstance);
} else {
return null;
}
}

private Optional<Method> findArbitraryCreator(GenericType genericType) {
List<Method> creators = ReflectionSupport.findMethods(descriptor.getContainerClass(), isCreatorForType(genericType), HierarchyTraversalMode.BOTTOM_UP);
List<Method> creators = findMethodsPotentiallyOuter(descriptor.getContainerClass(), isCreatorForType(genericType),
HierarchyTraversalMode.BOTTOM_UP);
if (creators.size() > 1) {
throw new AmbiguousArbitraryException(genericType, creators);
}
Expand Down
49 changes: 43 additions & 6 deletions src/main/java/net/jqwik/support/JqwikReflectionSupport.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package net.jqwik.support;

import net.jqwik.discovery.predicates.*;
import org.junit.platform.commons.support.*;
import org.junit.platform.commons.util.*;

import java.lang.reflect.*;
import java.nio.file.*;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.*;
import java.util.function.Predicate;
import java.util.stream.Stream;

import org.junit.platform.commons.support.*;
import org.junit.platform.commons.util.ReflectionUtils;

import net.jqwik.discovery.predicates.IsTopLevelClass;

public class JqwikReflectionSupport {

Expand Down Expand Up @@ -59,6 +61,41 @@ public static <T> T newInstanceWithDefaultConstructor(Class<T> clazz) {
}
}

/**
* Find all {@linkplain Method methods} as in ReflectionSupport.findMethods(..) but also use outer classes to look for
* methods.
*/
public static List<Method> findMethodsPotentiallyOuter(Class<?> clazz, Predicate<Method> predicate,
HierarchyTraversalMode traversalMode) {

List<Method> foundMethods = new ArrayList<>();
foundMethods.addAll(ReflectionSupport.findMethods(clazz, predicate, traversalMode));
Class<?> searchClass = clazz;
while (searchClass.getDeclaringClass() != null) {
searchClass = searchClass.getDeclaringClass();
foundMethods.addAll(ReflectionSupport.findMethods(searchClass, predicate, traversalMode));
}
return foundMethods;
}

/**
* Invoke the supplied {@linkplain Method method} as in ReflectionSupport.invokeMethod(..) but potentially use the outer instance if the
* method belongs to the outer instance of an object.
*/
public static Object invokeMethodPotentiallyOuter(Method method, Object target, Object... args) {
if (method.getDeclaringClass().isAssignableFrom(target.getClass())) {
return ReflectionSupport.invokeMethod(method, target, args);
} else {
if (target.getClass().getDeclaringClass() != null) {
Optional<Object> newTarget = getOuterInstance(target);
if (newTarget.isPresent()) {
return invokeMethodPotentiallyOuter(method, newTarget.get(), args);
}
}
throw new IllegalArgumentException(String.format("Method [%s] cannot be invoked on target [%s].", method, target));
}
}

public static Set<Path> getAllClasspathRootDirectories() {
return ReflectionUtils.getAllClasspathRootDirectories();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,30 @@ void findStringGeneratorByMethodName() throws Exception {
assertThat(actual).isInstanceOf(String.class);
}

@Example
void findGeneratorByMethodNameOutsideGroup() throws Exception {
PropertyMethodArbitraryResolver provider = getProvider(WithNamedProviders.NestedWithNamedProviders.class, "nestedStringByMethodName", String.class);
Parameter parameter = getParameter(WithNamedProviders.NestedWithNamedProviders.class, "nestedStringByMethodName");
Object actual = generateObject(provider, parameter);
assertThat(actual).isInstanceOf(String.class);
}

@Example
void findGeneratorByNameOutsideGroup() throws Exception {
PropertyMethodArbitraryResolver provider = getProvider(WithNamedProviders.NestedWithNamedProviders.class, "nestedString", String.class);
Parameter parameter = getParameter(WithNamedProviders.NestedWithNamedProviders.class, "nestedString");
Object actual = generateObject(provider, parameter);
assertThat(actual).isInstanceOf(String.class);
}

@Example
void findFirstFitIfNoNameIsGivenInOutsideGroup() throws Exception {
PropertyMethodArbitraryResolver provider = getProvider(WithNamedProviders.NestedWithNamedProviders.class, "nestedThing", Thing.class);
Parameter parameter = getParameter(WithNamedProviders.NestedWithNamedProviders.class, "nestedThing");
Object actual = generateObject(provider, parameter);
assertThat(actual).isInstanceOf(Thing.class);
}

@Example
void namedStringGeneratorNotFound() throws Exception {
PropertyMethodArbitraryResolver provider = getProvider(WithNamedProviders.class, "otherString", String.class);
Expand Down Expand Up @@ -368,6 +392,24 @@ Arbitrary<Thing> aThing() {
return Arbitraries.of(new Thing());
}

@Group
class NestedWithNamedProviders {
@Property
boolean nestedStringByMethodName(@ForAll("byMethodName") String aString) {
return true;
}

@Property
boolean nestedString(@ForAll("aString") String aString) {
return true;
}

@Property
boolean nestedThing(@ForAll Thing aThing) {
return true;
}

}
}

}
Expand Down

0 comments on commit 80055dd

Please sign in to comment.