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

org.junit.jupiter.engine.execution.InvocationInterceptorChain Maven / Gradle / Ivy

There is a newer version: 5.11.3
Show newest version
/*
 * Copyright 2015-2022 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
 *
 * https://www.eclipse.org/legal/epl-v20.html
 */

package org.junit.jupiter.engine.execution;

import static java.util.stream.Collectors.joining;
import static org.apiguardian.api.API.Status.INTERNAL;

import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apiguardian.api.API;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;
import org.junit.jupiter.engine.extension.ExtensionRegistry;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.commons.logging.Logger;
import org.junit.platform.commons.logging.LoggerFactory;
import org.junit.platform.commons.util.ExceptionUtils;

@API(status = INTERNAL, since = "5.5")
public class InvocationInterceptorChain {

	public  T invoke(Invocation invocation, ExtensionRegistry extensionRegistry, InterceptorCall call) {
		List interceptors = extensionRegistry.getExtensions(InvocationInterceptor.class);
		if (interceptors.isEmpty()) {
			return proceed(invocation);
		}
		return chainAndInvoke(invocation, call, interceptors);
	}

	private  T chainAndInvoke(Invocation invocation, InterceptorCall call,
			List interceptors) {

		ValidatingInvocation validatingInvocation = new ValidatingInvocation<>(invocation, interceptors);
		Invocation chainedInvocation = chainInterceptors(validatingInvocation, call, interceptors);
		T result = proceed(chainedInvocation);
		validatingInvocation.verifyInvokedAtLeastOnce();
		return result;
	}

	private  Invocation chainInterceptors(Invocation invocation, InterceptorCall call,
			List interceptors) {

		Invocation result = invocation;
		ListIterator iterator = interceptors.listIterator(interceptors.size());
		while (iterator.hasPrevious()) {
			InvocationInterceptor interceptor = iterator.previous();
			result = new InterceptedInvocation<>(result, call, interceptor);
		}
		return result;
	}

	private  T proceed(Invocation invocation) {
		try {
			return invocation.proceed();
		}
		catch (Throwable t) {
			throw ExceptionUtils.throwAsUncheckedException(t);
		}
	}

	@FunctionalInterface
	public interface InterceptorCall {

		T apply(InvocationInterceptor interceptor, Invocation invocation) throws Throwable;

		static InterceptorCall ofVoid(VoidInterceptorCall call) {
			return ((interceptorChain, invocation) -> {
				call.apply(interceptorChain, invocation);
				return null;
			});
		}

	}

	@FunctionalInterface
	public interface VoidInterceptorCall {

		void apply(InvocationInterceptor interceptor, Invocation invocation) throws Throwable;

	}

	private static class InterceptedInvocation implements Invocation {

		private final Invocation invocation;
		private final InterceptorCall call;
		private final InvocationInterceptor interceptor;

		InterceptedInvocation(Invocation invocation, InterceptorCall call, InvocationInterceptor interceptor) {
			this.invocation = invocation;
			this.call = call;
			this.interceptor = interceptor;
		}

		@Override
		public T proceed() throws Throwable {
			return call.apply(interceptor, invocation);
		}

		@Override
		public void skip() {
			invocation.skip();
		}
	}

	private static class ValidatingInvocation implements Invocation {

		private static final Logger logger = LoggerFactory.getLogger(ValidatingInvocation.class);

		private final AtomicBoolean invokedOrSkipped = new AtomicBoolean();
		private final Invocation delegate;
		private final List interceptors;

		ValidatingInvocation(Invocation delegate, List interceptors) {
			this.delegate = delegate;
			this.interceptors = interceptors;
		}

		@Override
		public T proceed() throws Throwable {
			markInvokedOrSkipped();
			return delegate.proceed();
		}

		@Override
		public void skip() {
			logger.debug(() -> "The invocation is skipped");
			markInvokedOrSkipped();
			delegate.skip();
		}

		private void markInvokedOrSkipped() {
			if (!invokedOrSkipped.compareAndSet(false, true)) {
				fail("Chain of InvocationInterceptors called invocation multiple times instead of just once");
			}
		}

		void verifyInvokedAtLeastOnce() {
			if (!invokedOrSkipped.get()) {
				fail("Chain of InvocationInterceptors never called invocation");
			}
		}

		private void fail(String prefix) {
			String commaSeparatedInterceptorClasses = interceptors.stream().map(Object::getClass).map(
				Class::getName).collect(joining(", "));
			throw new JUnitException(prefix + ": " + commaSeparatedInterceptorClasses);
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy