001/**
002 * Copyright (C) 2006-2024 Talend Inc. - www.talend.com
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.talend.sdk.component.junit.base.junit5;
017
018import java.lang.annotation.Annotation;
019import java.util.stream.Stream;
020
021import org.junit.jupiter.api.extension.ExtensionContext;
022import org.junit.jupiter.api.extension.ParameterContext;
023import org.junit.jupiter.api.extension.ParameterResolutionException;
024import org.junit.jupiter.api.extension.ParameterResolver;
025import org.junit.jupiter.api.extension.TestInstancePostProcessor;
026
027/**
028 * Helper which ensure the same code is used for the field injections and parameter injections.
029 */
030public interface JUnit5InjectionSupport extends ParameterResolver, TestInstancePostProcessor {
031
032    Class<? extends Annotation> injectionMarker();
033
034    default boolean supports(final Class<?> type) {
035        return type.isInstance(this);
036    }
037
038    default Object findInstance(final ExtensionContext extensionContext, final Class<?> type, final Annotation marker) {
039        return findInstance(extensionContext, type);
040    }
041
042    default Object findInstance(final ExtensionContext extensionContext, final Class<?> type) {
043        return this;
044    }
045
046    @Override
047    default boolean supportsParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext)
048            throws ParameterResolutionException {
049        return supports(parameterContext.getParameter().getType());
050    }
051
052    @Override
053    default Object resolveParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext)
054            throws ParameterResolutionException {
055        return findInstance(extensionContext, parameterContext.getParameter().getType());
056    }
057
058    @Override
059    default void postProcessTestInstance(final Object testInstance, final ExtensionContext context) {
060        Class<?> testClass = context.getRequiredTestClass();
061        while (testClass != Object.class) {
062            Stream
063                    .of(testClass.getDeclaredFields())
064                    .filter(c -> c.isAnnotationPresent(injectionMarker()))
065                    .forEach(f -> {
066                        if (!supports(f.getType())) {
067                            throw new IllegalArgumentException("@" + injectionMarker() + " not supported on " + f);
068                        }
069                        if (!f.isAccessible()) {
070                            f.setAccessible(true);
071                        }
072                        try {
073                            f.set(testInstance, findInstance(context, f.getType(), f.getAnnotation(injectionMarker())));
074                        } catch (final IllegalAccessException e) {
075                            throw new IllegalStateException(e);
076                        }
077                    });
078            testClass = testClass.getSuperclass();
079        }
080    }
081}