Skip to content

Commit

Permalink
[312] Initial attempt to allow Arquillian to inject method parameters. (
Browse files Browse the repository at this point in the history
#608)

* [312] Add ParameterResolver which allows Arquillian to provide parameters. The parameters are resolved from TestEnrichers.

Signed-off-by: James R. Perkins <jperkins@redhat.com>

* [312] Skip the Context and InitialContext parameter injection for Payara. There is an issue with where/when the InitialContext producer is initialized for JUnit 5 ParameterResolvers.

Signed-off-by: James R. Perkins <jperkins@redhat.com>

---------

Signed-off-by: James R. Perkins <jperkins@redhat.com>
  • Loading branch information
jamezp authored Aug 13, 2024
1 parent cd9e802 commit ff8a556
Show file tree
Hide file tree
Showing 12 changed files with 370 additions and 16 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:jperkins@redhat.com">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.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.condition.DisabledIfSystemProperty;
import org.junit.jupiter.api.io.TempDir;

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

@Test
@Disabled("https://github.com/arquillian/arquillian-core/issues/312")
@DisabledIfSystemProperty(named = "javax.naming.Context.parameter", matches = "skip")
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 +65,20 @@ public void checkInitialContext() throws Exception {
}

@Test
@Disabled("https://github.com/arquillian/arquillian-core/issues/312")
@DisabledIfSystemProperty(named = "javax.naming.Context.parameter", matches = "skip")
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, @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");
}
}
2 changes: 2 additions & 0 deletions integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@
<groupId>fish.payara.arquillian</groupId>
<artifactId>arquillian-payara-server-managed</artifactId>
<version>${version.fish.payara.arquillian}</version>
<scope>test</scope>
</dependency>
</dependencies>

Expand Down Expand Up @@ -292,6 +293,7 @@
<systemPropertyVariables>
<payara.home>${payara.home}</payara.home>
<arq.host>localhost</arq.host>
<javax.naming.Context.parameter>skip</javax.naming.Context.parameter>
</systemPropertyVariables>
</configuration>
</plugin>
Expand Down
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:jperkins@redhat.com">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 @@ -9,12 +9,16 @@
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,12 +27,13 @@
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";

private static final Predicate<ExtensionContext> IS_INSIDE_ARQUILLIAN = (context -> Boolean.parseBoolean(context.getConfigurationParameter(RUNNING_INSIDE_ARQUILLIAN).orElse("false")));
private static final Predicate<ExtensionContext> IS_INSIDE_ARQUILLIAN = (context -> Boolean.parseBoolean(context.getConfigurationParameter(RUNNING_INSIDE_ARQUILLIAN)
.orElse("false")));

@Override
public void beforeAll(ExtensionContext context) throws Exception {
Expand All @@ -46,18 +51,31 @@ 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 methodParameters = ContextStore.getContextStore(context).createMethodParameters();
// Fired to set the MethodParameters on the producer
adapter.fireCustomLifecycle(new MethodParameterProducerEvent(instance, method, methodParameters));
adapter.before(
instance,
method,
LifecycleMethodExecutor.NO_OP);
}

@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 +208,40 @@ 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 {
try {
// Get the parameter holder
final MethodParameters holder = ContextStore.getContextStore(extensionContext).getMethodParameters();
if (holder == null) {
throw createParameterResolutionException(parameterContext, null);
}
return holder.get(parameterContext.getIndex()) != null;
} catch (Exception e) {
throw createParameterResolutionException(parameterContext, e);
}
}

@Override
public Object resolveParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException {
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 ff8a556

Please sign in to comment.