All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jqwik.engine.discovery.HierarchicalJavaResolver Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
package net.jqwik.engine.discovery;

import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;
import java.util.logging.*;

import org.junit.platform.engine.*;

import net.jqwik.engine.descriptor.*;
import net.jqwik.engine.discovery.predicates.*;

import static java.lang.String.*;
import static java.util.stream.Collectors.*;
import static org.junit.platform.commons.support.HierarchyTraversalMode.*;
import static org.junit.platform.commons.support.ReflectionSupport.*;

class HierarchicalJavaResolver {

	private static final Logger LOG = Logger.getLogger(HierarchicalJavaResolver.class.getName());

	private final static IsContainerAGroup isContainerAGroup = new IsContainerAGroup();
	private final static IsDiscoverableTestMethod isDiscoverableTestMethod = new IsDiscoverableTestMethod();

	private final TestDescriptor engineDescriptor;
	private final Set resolvers;

	HierarchicalJavaResolver(TestDescriptor engineDescriptor, Set resolvers) {
		this.engineDescriptor = engineDescriptor;
		this.resolvers = resolvers;
	}

	void resolveClass(Class testClass) {
		Set resolvedDescriptors = resolveContainerWithParents(testClass);
		resolvedDescriptors.forEach(this::resolveChildren);

		if (resolvedDescriptors.isEmpty()) {
			LOG.info(() -> format("Received request to resolve class '%s' as test container but could not fulfill it", testClass.getName()));
		}
	}

	void resolveMethod(Class testClass, Method testMethod) {
		Set potentialParents = resolveContainerWithParents(testClass);
		Set resolvedDescriptors = resolveForAllParents(testMethod, potentialParents);

		if (resolvedDescriptors.isEmpty()) {
			LOG.info(() -> format("Received request to resolve method '%s' as test but could not fulfill it", testMethod.toGenericString()));
		}
	}

	private Set resolveContainerWithParents(Class testClass) {
		Set potentialParents = isContainerAGroup.test(testClass)
			? resolveContainerWithParents(testClass.getDeclaringClass()) : Collections.singleton(engineDescriptor);
		return resolveForAllParents(testClass, potentialParents);
	}

	void resolveUniqueId(UniqueId uniqueId) {
		List segments = new ArrayList<>(uniqueId.getSegments());
		segments.remove(0); // Ignore engine unique ID

		if (!resolveUniqueId(this.engineDescriptor, segments)) {
			// This is more severe than unresolvable methods or classes because only suitable IDs should get here anyway
			LOG.warning(() -> format("Received request to resolve unique id '%s' as test or test container but could not fulfill it", uniqueId));
		}
	}

	/**
	 * Return true if all segments of unique ID could be resolved
	 */
	private boolean resolveUniqueId(TestDescriptor parent, List remainingSegments) {
		if (remainingSegments.isEmpty()) {
			resolveChildren(parent);
			return true;
		}

		UniqueId.Segment head = remainingSegments.remove(0);
		for (ElementResolver resolver : resolvers) {
			Optional resolvedDescriptor = resolver.resolveUniqueId(head, parent);
			if (!resolvedDescriptor.isPresent())
				continue;

			Optional foundTestDescriptor = findTestDescriptorByUniqueId(resolvedDescriptor.get().getUniqueId());
			TestDescriptor descriptor = foundTestDescriptor.orElseGet(() -> {
				TestDescriptor newDescriptor = resolvedDescriptor.get();
				parent.addChild(newDescriptor);
				return newDescriptor;
			});
			return resolveUniqueId(descriptor, remainingSegments);
		}
		return false;
	}

	private Set resolveContainerWithChildren(Class containerClass, Set potentialParents) {
		Set resolvedDescriptors = resolveForAllParents(containerClass, potentialParents);
		resolvedDescriptors.forEach(this::resolveChildren);
		return resolvedDescriptors;
	}

	private Set resolveForAllParents(AnnotatedElement element, Set potentialParents) {
		Set resolvedDescriptors = new HashSet<>();
		potentialParents.forEach(parent -> resolvedDescriptors.addAll(resolve(element, parent)));
		return resolvedDescriptors;
	}

	private void resolveChildren(TestDescriptor descriptor) {
		if (descriptor instanceof ContainerClassDescriptor) {
			ContainerClassDescriptor containerClassDescriptor = (ContainerClassDescriptor) descriptor;
			Class containerClass = containerClassDescriptor.getContainerClass();
			resolveContainedMethods(descriptor, containerClass);

			resolveContainedGroups(containerClassDescriptor, containerClass);
		}
	}

	private void resolveContainedGroups(ContainerClassDescriptor containerClassDescriptor, Class containerClass) {
		Predicate> isGroup = new IsContainerAGroup();
		List> containedContainersCandidates = findNestedClasses(containerClass, isGroup);
		containedContainersCandidates
			.forEach(nestedClass -> resolveContainerWithChildren(nestedClass, Collections.singleton(containerClassDescriptor)));
	}

	private void resolveContainedMethods(TestDescriptor containerDescriptor, Class testClass) {
		List testMethodCandidates = findMethods(testClass, isDiscoverableTestMethod, TOP_DOWN);
		testMethodCandidates.forEach(method -> resolve(method, containerDescriptor));
	}

	private Set resolve(AnnotatedElement element, TestDescriptor parent) {
		return this.resolvers.stream() //
			.map(resolver -> tryToResolveWithResolver(element, parent, resolver)) //
			.filter(testDescriptors -> !testDescriptors.isEmpty()) //
			.flatMap(Collection::stream) //
			.collect(toSet());
	}

	private Set tryToResolveWithResolver(AnnotatedElement element, TestDescriptor parent, ElementResolver resolver) {

		Set resolvedDescriptors = resolver.resolveElement(element, parent);
		Set result = new LinkedHashSet<>();

		resolvedDescriptors.forEach(testDescriptor -> {
			Optional existingTestDescriptor = findTestDescriptorByUniqueId(testDescriptor.getUniqueId());
			if (existingTestDescriptor.isPresent()) {
				result.add(existingTestDescriptor.get());
			} else {
				parent.addChild(testDescriptor);
				result.add(testDescriptor);
			}
		});

		return result;
	}

	@SuppressWarnings("unchecked")
	private Optional findTestDescriptorByUniqueId(UniqueId uniqueId) {
		return (Optional) this.engineDescriptor.findByUniqueId(uniqueId);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy