/*
 * Copyright 2015-2019 the original author or authors.
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License v2.0 which
 * accompanies this distribution and is available at
 *
 * http://www.eclipse.org/legal/epl-v20.html
 */

package org.junit.jupiter.engine.descriptor;

import static java.util.stream.Collectors.toList;
import static org.junit.platform.commons.util.AnnotationUtils.findAnnotatedFields;
import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation;
import static org.junit.platform.commons.util.AnnotationUtils.findRepeatableAnnotations;
import static org.junit.platform.commons.util.ReflectionUtils.isPrivate;
import static org.junit.platform.commons.util.ReflectionUtils.isStatic;
import static org.junit.platform.commons.util.ReflectionUtils.tryToReadFieldValue;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;

import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.engine.extension.ExtensionRegistry;
import org.junit.platform.commons.util.Preconditions;

Collection of utilities for working with extensions and the extension registry.
See Also:
Since:5.1
/** * Collection of utilities for working with extensions and the extension registry. * * @since 5.1 * @see ExtensionRegistry * @see ExtendWith * @see RegisterExtension */
final class ExtensionUtils { private static final Predicate<Field> isStaticExtension = new IsStaticExtensionField(); private static final Predicate<Field> isNonStaticExtension = new IsNonStaticExtensionField(); private ExtensionUtils() { /* no-op */ }
Populate a new ExtensionRegistry from extension types declared via @ExtendWith on the supplied AnnotatedElement.
Params:
  • parentRegistry – the parent extension registry to set in the newly created registry; never null
  • annotatedElement – the annotated element on which to search for declarations of @ExtendWith; never null
Returns:the new extension registry; never null
Since:5.0
/** * Populate a new {@link ExtensionRegistry} from extension types declared via * {@link ExtendWith @ExtendWith} on the supplied {@link AnnotatedElement}. * * @param parentRegistry the parent extension registry to set in the newly * created registry; never {@code null} * @param annotatedElement the annotated element on which to search for * declarations of {@code @ExtendWith}; never {@code null} * * @return the new extension registry; never {@code null} * @since 5.0 */
static ExtensionRegistry populateNewExtensionRegistryFromExtendWithAnnotation(ExtensionRegistry parentRegistry, AnnotatedElement annotatedElement) { Preconditions.notNull(annotatedElement, "AnnotatedElement must not be null"); Preconditions.notNull(parentRegistry, "Parent ExtensionRegistry must not be null"); // @formatter:off List<Class<? extends Extension>> extensionTypes = findRepeatableAnnotations(annotatedElement, ExtendWith.class).stream() .map(ExtendWith::value) .flatMap(Arrays::stream) .collect(toList()); // @formatter:on return ExtensionRegistry.createRegistryFrom(parentRegistry, extensionTypes); }
Register extensions in the supplied registry from fields in the supplied class that are annotated with @RegisterExtension.

The extensions will be sorted according to @Order semantics prior to registration.

Params:
  • registry – the registry in which to register the extensions; never null
  • clazz – the class or interface in which to find the fields; never null
  • instance – the instance of the supplied class; may be null when searching for static fields in the class
/** * Register extensions in the supplied registry from fields in the supplied * class that are annotated with {@link RegisterExtension @RegisterExtension}. * * <p>The extensions will be sorted according to {@link Order @Order} semantics * prior to registration. * * @param registry the registry in which to register the extensions; never {@code null} * @param clazz the class or interface in which to find the fields; never {@code null} * @param instance the instance of the supplied class; may be {@code null} * when searching for {@code static} fields in the class */
static void registerExtensionsFromFields(ExtensionRegistry registry, Class<?> clazz, Object instance) { Preconditions.notNull(clazz, "Class must not be null"); Preconditions.notNull(registry, "ExtensionRegistry must not be null"); Predicate<Field> predicate = (instance == null) ? isStaticExtension : isNonStaticExtension; // Ensure that the list is modifiable, since findAnnotatedFields() returns an unmodifiable list. List<Field> fields = new ArrayList<>(findAnnotatedFields(clazz, RegisterExtension.class, predicate)); // Sort fields based on @Order. fields.sort(orderComparator); fields.forEach(field -> { tryToReadFieldValue(field, instance).ifSuccess(value -> { Preconditions.notNull(value, () -> String.format( "Failed to register extension via @RegisterExtension field [%s]: field must not be null when evaluated.", field)); Extension extension = (Extension) value; registry.registerExtension(extension, field); }); }); }
Since:5.4
/** * @since 5.4 */
private static final Comparator<Field> orderComparator = // (field1, field2) -> Integer.compare(getOrder(field1), getOrder(field2));
Since:5.4
/** * @since 5.4 */
private static int getOrder(Field field) { return findAnnotation(field, Order.class).map(Order::value).orElse(Integer.MAX_VALUE); } static class IsNonStaticExtensionField implements Predicate<Field> { @Override public boolean test(Field field) { // Please do not collapse the following into a single statement. if (isStatic(field)) { return false; } if (isPrivate(field)) { return false; } if (!Extension.class.isAssignableFrom(field.getType())) { return false; } return true; } } static class IsStaticExtensionField implements Predicate<Field> { @Override public boolean test(Field field) { // Please do not collapse the following into a single statement. if (!isStatic(field)) { return false; } if (isPrivate(field)) { return false; } if (!Extension.class.isAssignableFrom(field.getType())) { return false; } return true; } } }