diff --git a/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionMultiDeploymentTest.java b/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionMultiDeploymentTest.java new file mode 100644 index 000000000..13cc96758 --- /dev/null +++ b/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionMultiDeploymentTest.java @@ -0,0 +1,111 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2025 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.integration.test.manual; + +import java.net.URI; +import java.net.URL; + +import org.jboss.arquillian.container.test.api.ContainerController; +import org.jboss.arquillian.container.test.api.Deployer; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.OperateOnDeployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.integration.test.common.app.EchoResource; +import org.jboss.arquillian.integration.test.common.app.RestActivator; +import org.jboss.arquillian.junit5.container.annotation.ArquillianTest; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * @author James R. Perkins + */ +@ArquillianTest +@RunAsClient +public class ManualModeInjectionMultiDeploymentTest { + private static final String CONTAINER_NAME = "default"; + static final String DEPLOYMENT_NAME_1 = "manual-mode-default"; + static final String DEPLOYMENT_NAME_2 = "manual-mode-secondary"; + + @ArquillianResource + private static ContainerController controller; + + @ArquillianResource + private static Deployer deployer; + + @Deployment(name = DEPLOYMENT_NAME_1, managed = false) + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class, DEPLOYMENT_NAME_1 + ".war") + .addClasses(RestActivator.class, EchoResource.class) + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @Deployment(name = DEPLOYMENT_NAME_2, managed = false) + public static WebArchive createDeploymentSecondary() { + return ShrinkWrap.create(WebArchive.class, DEPLOYMENT_NAME_2 + ".war") + .addClasses(RestActivator.class, EchoResource.class) + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @BeforeEach + public void startAndDeploy() { + controller.start(CONTAINER_NAME); + deployer.deploy(DEPLOYMENT_NAME_1); + deployer.deploy(DEPLOYMENT_NAME_2); + } + + @AfterEach + public void stop() { + if (controller.isStarted(CONTAINER_NAME)) { + deployer.undeploy(DEPLOYMENT_NAME_1); + deployer.undeploy(DEPLOYMENT_NAME_2); + controller.stop(CONTAINER_NAME); + } + } + + @Test + public void checkUri(@OperateOnDeployment(DEPLOYMENT_NAME_1) @ArquillianResource final URI uri) { + Assertions.assertNotNull(uri); + Assertions.assertTrue(uri.toString().contains("/" + DEPLOYMENT_NAME_1)); + } + + @Test + public void checkUrl(@OperateOnDeployment(DEPLOYMENT_NAME_1) @ArquillianResource final URL url) { + Assertions.assertNotNull(url); + Assertions.assertTrue(url.toString().contains("/" + DEPLOYMENT_NAME_1)); + } + + @Test + public void checkSecondaryUri(@OperateOnDeployment(DEPLOYMENT_NAME_2) @ArquillianResource final URI uri) { + Assertions.assertNotNull(uri); + Assertions.assertTrue(uri.toString().contains("/" + DEPLOYMENT_NAME_2)); + } + + @Test + public void checkSecondaryUrl(@OperateOnDeployment(DEPLOYMENT_NAME_2) @ArquillianResource final URL url) { + Assertions.assertNotNull(url); + Assertions.assertTrue(url.toString().contains("/" + DEPLOYMENT_NAME_2)); + } +} diff --git a/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionTest.java b/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionTest.java new file mode 100644 index 000000000..3343612cf --- /dev/null +++ b/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionTest.java @@ -0,0 +1,88 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2025 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.integration.test.manual; + +import java.net.URI; +import java.net.URL; + +import org.jboss.arquillian.container.test.api.ContainerController; +import org.jboss.arquillian.container.test.api.Deployer; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.integration.test.common.app.EchoResource; +import org.jboss.arquillian.integration.test.common.app.RestActivator; +import org.jboss.arquillian.junit5.container.annotation.ArquillianTest; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * @author James R. Perkins + */ +@ArquillianTest +@RunAsClient +public class ManualModeInjectionTest { + private static final String CONTAINER_NAME = "default"; + static final String DEPLOYMENT_NAME = "manual-mode"; + + @ArquillianResource + private static ContainerController controller; + + @ArquillianResource + private static Deployer deployer; + + @Deployment(name = DEPLOYMENT_NAME, managed = false) + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class, DEPLOYMENT_NAME + ".war") + .addClasses(RestActivator.class, EchoResource.class) + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @BeforeEach + public void startAndDeploy() { + controller.start(CONTAINER_NAME); + deployer.deploy(DEPLOYMENT_NAME); + } + + @AfterEach + public void stop() { + if (controller.isStarted(CONTAINER_NAME)) { + deployer.undeploy(DEPLOYMENT_NAME); + controller.stop(CONTAINER_NAME); + } + } + + @Test + public void checkUri(@ArquillianResource final URI uri) { + Assertions.assertNotNull(uri); + Assertions.assertTrue(uri.toString().contains("/" + DEPLOYMENT_NAME)); + } + + @Test + public void checkUrl(@ArquillianResource final URL url) { + Assertions.assertNotNull(url); + Assertions.assertTrue(url.toString().contains("/" + DEPLOYMENT_NAME)); + } +} diff --git a/junit5/core/pom.xml b/junit5/core/pom.xml index 28448112c..9b2683f45 100644 --- a/junit5/core/pom.xml +++ b/junit5/core/pom.xml @@ -26,6 +26,11 @@ arquillian-test-spi ${project.version} + + org.jboss.arquillian.container + arquillian-container-spi + ${project.version} + org.junit.jupiter diff --git a/junit5/core/src/main/java/org/jboss/arquillian/junit5/ArquillianExtension.java b/junit5/core/src/main/java/org/jboss/arquillian/junit5/ArquillianExtension.java index 9da2c3828..caecff3e8 100644 --- a/junit5/core/src/main/java/org/jboss/arquillian/junit5/ArquillianExtension.java +++ b/junit5/core/src/main/java/org/jboss/arquillian/junit5/ArquillianExtension.java @@ -14,6 +14,7 @@ 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.BeforeTestExecutionCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.InvocationInterceptor; import org.junit.jupiter.api.extension.ParameterContext; @@ -27,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, ParameterResolver { +public class ArquillianExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, 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"; @@ -78,6 +79,16 @@ public void afterEach(ExtensionContext context) throws Exception { } } + @Override + public void beforeTestExecution(final ExtensionContext context) throws Exception { + // Get the adapter, test instance and method + final TestRunnerAdaptor adapter = getManager(context) + .getAdaptor(); + final Object instance = context.getRequiredTestInstance(); + final Method method = context.getRequiredTestMethod(); + adapter.fireCustomLifecycle(new BeforeTestExecutionEvent(instance, method)); + } + @Override public void interceptTestTemplateMethod(Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { if (IS_INSIDE_ARQUILLIAN.test(extensionContext)) { diff --git a/junit5/core/src/main/java/org/jboss/arquillian/junit5/BeforeTestExecutionEvent.java b/junit5/core/src/main/java/org/jboss/arquillian/junit5/BeforeTestExecutionEvent.java new file mode 100644 index 000000000..e7f94224b --- /dev/null +++ b/junit5/core/src/main/java/org/jboss/arquillian/junit5/BeforeTestExecutionEvent.java @@ -0,0 +1,44 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2025 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; + +import java.lang.reflect.Method; + +import org.jboss.arquillian.test.spi.event.suite.TestLifecycleEvent; + +/** + * An event triggered before each test is invoked. + * + * @author James R. Perkins + */ +public class BeforeTestExecutionEvent extends TestLifecycleEvent { + /** + * Creates a new event. + * + * @param testInstance The test case instance + * @param testMethod The test method + * + * @throws IllegalArgumentException if testInstance is null + * @throws IllegalArgumentException if testMethod is null + */ + BeforeTestExecutionEvent(final Object testInstance, final Method testMethod) { + super(testInstance, testMethod); + } +} diff --git a/junit5/core/src/main/java/org/jboss/arquillian/junit5/MethodParameterObserver.java b/junit5/core/src/main/java/org/jboss/arquillian/junit5/MethodParameterObserver.java index e9d015921..a61d345f0 100644 --- a/junit5/core/src/main/java/org/jboss/arquillian/junit5/MethodParameterObserver.java +++ b/junit5/core/src/main/java/org/jboss/arquillian/junit5/MethodParameterObserver.java @@ -21,7 +21,11 @@ import java.lang.reflect.Method; import java.util.Collection; +import java.util.stream.Collectors; +import org.jboss.arquillian.container.spi.client.deployment.Deployment; +import org.jboss.arquillian.container.spi.client.deployment.DeploymentScenario; +import org.jboss.arquillian.container.spi.context.DeploymentContext; import org.jboss.arquillian.core.api.Event; import org.jboss.arquillian.core.api.Instance; import org.jboss.arquillian.core.api.InstanceProducer; @@ -33,7 +37,6 @@ import org.jboss.arquillian.test.spi.event.enrichment.AfterEnrichment; import org.jboss.arquillian.test.spi.event.enrichment.BeforeEnrichment; import org.jboss.arquillian.test.spi.event.enrichment.EnrichmentEvent; -import org.jboss.arquillian.test.spi.event.suite.Before; /** * The observer used to process method parameters provided by Arquillian. @@ -52,26 +55,39 @@ public class MethodParameterObserver { @TestScoped private InstanceProducer methodParametersProducer; + @Inject + private Instance deploymentContext; + + @Inject + private Instance deploymentScenario; + /** * Updates the stored {@link MethodParameters} for method parameters which can be provided by Arquillian. * * @param event the fired event */ - public void injectParameters(@Observes final Before event) { - final Object testInstance = event.getTestInstance(); - final Method testMethod = event.getTestMethod(); - enrichmentEvent.fire(new BeforeEnrichment(testInstance, testMethod)); - final MethodParameters methodParameters = methodParametersProducer.get(); - final Collection testEnrichers = serviceLoader.get().all(TestEnricher.class); - for (TestEnricher enricher : testEnrichers) { - final Object[] values = enricher.resolve(testMethod); - for (int i = 0; i < values.length; i++) { - if (values[i] != null) { - methodParameters.add(i, values[i]); + public void injectParameters(@Observes final BeforeTestExecutionEvent event) { + final boolean contextActivated = activateDeployments(); + try { + final Object testInstance = event.getTestInstance(); + final Method testMethod = event.getTestMethod(); + enrichmentEvent.fire(new BeforeEnrichment(testInstance, testMethod)); + final MethodParameters methodParameters = methodParametersProducer.get(); + final Collection testEnrichers = serviceLoader.get().all(TestEnricher.class); + for (TestEnricher enricher : testEnrichers) { + final Object[] values = enricher.resolve(testMethod); + for (int i = 0; i < values.length; i++) { + if (values[i] != null) { + methodParameters.add(i, values[i]); + } } } + enrichmentEvent.fire(new AfterEnrichment(testEnrichers, testMethod)); + } finally { + if (contextActivated) { + deploymentContext.get().deactivate(); + } } - enrichmentEvent.fire(new AfterEnrichment(testEnrichers, testMethod)); } /** @@ -82,4 +98,21 @@ public void injectParameters(@Observes final Before event) { public void injectParameters(@Observes MethodParameterProducerEvent event) { methodParametersProducer.set(event.getTestParameterHolder()); } + + private boolean activateDeployments() { + final DeploymentContext context = deploymentContext.get(); + // If the deployment context is not available or already active, we don't need to activate the deployment context + if (context == null || context.isActive()) { + return false; + } + final Collection activeDeployments = deploymentScenario.get().deployments().stream() + .filter(Deployment::isDeployed) + .collect(Collectors.toList()); + // If there are multiple deployments, don't activate any of them as an @OperatesOnDeployment should be used + if (activeDeployments.size() != 1) { + return false; + } + activeDeployments.forEach(context::activate); + return true; + } }