Skip to content

Commit

Permalink
[312] Add ParameterResolver which allows Arquillian to provide parame…
Browse files Browse the repository at this point in the history
…ters. The parameters are resolved from TestEnrichers.

Signed-off-by: James R. Perkins <[email protected]>
  • Loading branch information
jamezp committed Aug 9, 2024
1 parent f86028a commit d5402a1
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

Expand Down Expand Up @@ -65,7 +64,6 @@ public void checkUrl() {
}

@Test
@Disabled("https://github.com/arquillian/arquillian-core/issues/312")
public void checkParameterUrl(@ArquillianResource final URL url) {
Assertions.assertNotNull(url, "The URL should have been injected");
Assertions.assertEquals(TestEnvironment.protocol(), url.getProtocol());
Expand All @@ -83,7 +81,6 @@ public void checkUri() {
}

@Test
@Disabled("https://github.com/arquillian/arquillian-core/issues/312")
public void checkParameterUri(@ArquillianResource final URI uri) {
Assertions.assertNotNull(uri, "The URI should have been injected");
checkHost(uri.getHost());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,29 @@

package org.jboss.arquillian.integration.test.resource.injection;

import java.net.URL;
import java.nio.file.Path;

import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.integration.test.common.TestEnvironment;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

/**
* @author <a href="mailto:[email protected]">James R. Perkins</a>
*/
@RunAsClient
public class ClientArquillianResourceTest extends AbstractArquillianResourceTest {

@Test
public void checkMultipleParameters(@ArquillianResource final URL url, @TempDir final Path tempDir) {
Assertions.assertNotNull(url, "The URL should have been injected");
Assertions.assertEquals(TestEnvironment.protocol(), url.getProtocol());
checkHost(url.getHost());
Assertions.assertEquals(TestEnvironment.port(), url.getPort());
Assertions.assertEquals("/" + DEPLOYMENT_NAME + "/", url.getPath());
Assertions.assertNotNull(tempDir, "The temp dir should have been injected");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@

package org.jboss.arquillian.integration.test.resource.injection;

import java.net.URL;
import java.nio.file.Path;
import javax.naming.Context;
import javax.naming.InitialContext;

import org.jboss.arquillian.integration.test.common.TestEnvironment;
import org.jboss.arquillian.junit5.annotations.Vetoed;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

/**
* @author <a href="mailto:[email protected]">James R. Perkins</a>
Expand All @@ -46,7 +50,6 @@ public void checkContext() throws Exception {
}

@Test
@Disabled("https://github.com/arquillian/arquillian-core/issues/312")
public void checkContextParameter(@ArquillianResource final Context context) throws Exception {
Assertions.assertNotNull(context, "The Context should have been injected");
final Object bm = context.lookup("java:comp/BeanManager");
Expand All @@ -61,10 +64,19 @@ public void checkInitialContext() throws Exception {
}

@Test
@Disabled("https://github.com/arquillian/arquillian-core/issues/312")
public void checkInitialContextParameter(@ArquillianResource final InitialContext initialContext) throws Exception {
Assertions.assertNotNull(initialContext, "The InitialContext should have been injected");
final Object bm = initialContext.lookup("java:comp/BeanManager");
Assertions.assertNotNull(bm);
}

@Test
public void checkMultipleParameters(@ArquillianResource final URL url, @Vetoed @TempDir final Path tempDir) {
Assertions.assertNotNull(url, "The URL should have been injected");
Assertions.assertEquals(TestEnvironment.protocol(), url.getProtocol());
checkHost(url.getHost());
Assertions.assertEquals(TestEnvironment.port(), url.getPort());
Assertions.assertEquals("/" + DEPLOYMENT_NAME + "/", url.getPath());
Assertions.assertNotNull(tempDir, "The temp dir should have been injected");
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jboss.arquillian.junit5.container;


import org.jboss.arquillian.container.test.spi.RemoteLoadableExtension;
import org.jboss.arquillian.junit5.ArquillianExtension;
import org.jboss.arquillian.container.test.spi.TestRunner;
import org.jboss.arquillian.container.test.spi.client.deployment.CachedAuxilliaryArchiveAppender;
Expand All @@ -22,6 +23,8 @@ protected Archive<?> buildArchive() {
.addAsServiceProvider(
TestRunner.class,
JUnitJupiterTestRunner.class)
.addAsServiceProvider(TestEngine.class, JupiterTestEngine.class);
.addAsServiceProvider(TestEngine.class, JupiterTestEngine.class)
// The remote extension for in-container tests
.addAsServiceProvider(RemoteLoadableExtension.class, JUnitJupiterRemoteExtension.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2024 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.arquillian.junit5.container;

import org.jboss.arquillian.container.test.spi.RemoteLoadableExtension;
import org.jboss.arquillian.core.spi.LoadableExtension;
import org.jboss.arquillian.junit5.MethodParameterObserver;

/**
* The remote extension for JUnit 5 in-container tests.
*
* @author <a href="mailto:[email protected]">James R. Perkins</a>
*/
public class JUnitJupiterRemoteExtension implements RemoteLoadableExtension {

@Override
public void register(LoadableExtension.ExtensionBuilder builder) {
builder.observer(MethodParameterObserver.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@
import java.util.Optional;
import java.util.function.Predicate;

import org.jboss.arquillian.junit5.annotations.Vetoed;
import org.jboss.arquillian.junit5.extension.RunModeEvent;
import org.jboss.arquillian.test.spi.LifecycleMethodExecutor;
import org.jboss.arquillian.test.spi.TestMethodExecutor;
import org.jboss.arquillian.test.spi.TestResult;
import org.jboss.arquillian.test.spi.TestRunnerAdaptor;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
import org.junit.platform.commons.JUnitException;
Expand All @@ -23,7 +28,7 @@
import static org.jboss.arquillian.junit5.ContextStore.getContextStore;
import static org.jboss.arquillian.junit5.JUnitJupiterTestClassLifecycleManager.getManager;

public class ArquillianExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, InvocationInterceptor, TestExecutionExceptionHandler {
public class ArquillianExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, InvocationInterceptor, TestExecutionExceptionHandler, ParameterResolver {
public static final String RUNNING_INSIDE_ARQUILLIAN = "insideArquillian";

private static final String CHAIN_EXCEPTION_MESSAGE_PREFIX = "Chain of InvocationInterceptors never called invocation";
Expand All @@ -46,18 +51,29 @@ public void afterAll(ExtensionContext context) throws Exception {

@Override
public void beforeEach(ExtensionContext context) throws Exception {
getManager(context).getAdaptor().before(
context.getRequiredTestInstance(),
context.getRequiredTestMethod(),
LifecycleMethodExecutor.NO_OP);
// Get the adapter, test instance and method
final TestRunnerAdaptor adapter = getManager(context)
.getAdaptor();
final Object instance = context.getRequiredTestInstance();
final Method method = context.getRequiredTestMethod();
// Create a new parameter holder
final MethodParameters holder = ContextStore.getContextStore(context).createMethodParameters();
adapter.before(
instance,
method,
() -> adapter.fireCustomLifecycle(new MethodParameterInjectionEvent(instance, method, holder)));
}

@Override
public void afterEach(ExtensionContext context) throws Exception {
getManager(context).getAdaptor().after(
try {
getManager(context).getAdaptor().after(
context.getRequiredTestInstance(),
context.getRequiredTestMethod(),
LifecycleMethodExecutor.NO_OP);
} finally {
ContextStore.getContextStore(context).removeMethodParameters();
}
}

@Override
Expand Down Expand Up @@ -190,4 +206,55 @@ private boolean isRunAsClient(ExtensionContext extensionContext) throws Exceptio
manager.getAdaptor().fireCustomLifecycle(runModeEvent);
return runModeEvent.isRunAsClient();
}

@Override
public boolean supportsParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException {
if (parameterContext.isAnnotated(Vetoed.class)) {
return false;
}
try {
if (isRunAsClient(extensionContext) || IS_INSIDE_ARQUILLIAN.test(extensionContext)) {
// Get the parameter holder
final MethodParameters holder = ContextStore.getContextStore(extensionContext).getMethodParameters();
if (holder == null) {
throw createParameterResolutionException(parameterContext, null);
}
return holder.get(parameterContext.getIndex()) != null;
}
// We always return here as we are an in-container test, but technically running on the client side. This
// happens as JUnit needs to invoke the test, then Arquillian runs it in the container. This does, however,
// create issues when you want to inject a parameter not provided by Arquillian. For example @TempDir Path.
// To make other provided parameters work, the @Vetoed annotation can be used to indicate the Arquillian
// should not attempt to provide the annotation.
return true;
} catch (Exception e) {
throw createParameterResolutionException(parameterContext, e);
}
}

@Override
public Object resolveParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException {
// This should never happen, but return null just in case
if (parameterContext.isAnnotated(Vetoed.class)) {
return null;
}
try {
// Get the parameter holder
final MethodParameters holder = ContextStore.getContextStore(extensionContext).getMethodParameters();
if (holder == null) {
throw createParameterResolutionException(parameterContext, null);
}
return holder.get(parameterContext.getIndex());
} catch (Exception e) {
throw createParameterResolutionException(parameterContext, e);
}
}

private static ParameterResolutionException createParameterResolutionException(final ParameterContext parameterContext, final Throwable cause) {
final String msg = String.format("Failed to resolve parameter %s", parameterContext.getParameter().getName());
if (cause == null) {
return new ParameterResolutionException(msg);
}
return new ParameterResolutionException(msg, cause);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package org.jboss.arquillian.junit5;

import org.junit.jupiter.api.extension.ExtensionContext;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Optional;

import org.junit.jupiter.api.extension.ExtensionContext;

class ContextStore {
private static final String NAMESPACE_KEY = "arquillianNamespace";

private static final String INTERCEPTED_TEMPLATE_NAMESPACE_KEY = "interceptedTestTemplates";

private static final String RESULT_NAMESPACE_KEY = "results";

private static final String PARAMETER_NAMESPACE_KEY = "methodParameters";

private final ExtensionContext context;

private ContextStore(ExtensionContext context) {
Expand Down Expand Up @@ -60,4 +62,34 @@ Optional<Throwable> getResult(String uniqueId) {
final ExtensionContext.Store resultStore = getResultStore();
return Optional.ofNullable(resultStore.getOrDefault(uniqueId, Throwable.class, null));
}

/**
* Creates a new method parameter holder and stores it in the current context.
*
* @return the method parameters holder
*/
MethodParameters createMethodParameters() {
final MethodParameters methodParameters = new MethodParameters();
context.getStore(ExtensionContext.Namespace.create(NAMESPACE_KEY, PARAMETER_NAMESPACE_KEY))
.put(PARAMETER_NAMESPACE_KEY, methodParameters);
return methodParameters;
}

/**
* Gets the method parameters holder.
*
* @return the method parameters holder or {@code null} if one was not created
*/
MethodParameters getMethodParameters() {
return context.getStore(ExtensionContext.Namespace.create(NAMESPACE_KEY, PARAMETER_NAMESPACE_KEY))
.get(PARAMETER_NAMESPACE_KEY, MethodParameters.class);
}

/**
* Removes the method parameters holder.
*/
void removeMethodParameters() {
context.getStore(ExtensionContext.Namespace.create(NAMESPACE_KEY, PARAMETER_NAMESPACE_KEY))
.remove(PARAMETER_NAMESPACE_KEY);
}
}
Loading

0 comments on commit d5402a1

Please sign in to comment.