org.junit.jupiter.engine.execution.InvocationInterceptorChain Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of junit-jupiter-engine Show documentation
Show all versions of junit-jupiter-engine Show documentation
Module "junit-jupiter-engine" of JUnit 5.
/*
* Copyright 2015-2021 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