Last active
November 7, 2017 11:37
-
-
Save famod/a423cadbe976401e02002b9103d1d2d5 to your computer and use it in GitHub Desktop.
Arquillian ReuseDeployment extension
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2017 GitHub user famod. | |
* | |
* 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 some.pckg; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target(ElementType.TYPE) | |
public @interface ReuseDeployment { | |
Class<?> value(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2017 GitHub user famod. | |
* | |
* 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 some.pckg; | |
import java.util.HashMap; | |
import java.util.Map; | |
import java.util.Optional; | |
import java.util.logging.Level; | |
import java.util.logging.Logger; | |
import org.jboss.arquillian.container.spi.client.deployment.DeploymentScenario; | |
import org.jboss.arquillian.container.spi.event.DeployManagedDeployments; | |
import org.jboss.arquillian.container.spi.event.UnDeployManagedDeployments; | |
import org.jboss.arquillian.container.spi.event.container.BeforeStop; | |
import org.jboss.arquillian.container.test.impl.client.deployment.event.GenerateDeployment; | |
import org.jboss.arquillian.core.api.Event; | |
import org.jboss.arquillian.core.api.InstanceProducer; | |
import org.jboss.arquillian.core.api.annotation.Inject; | |
import org.jboss.arquillian.core.api.annotation.Observes; | |
import org.jboss.arquillian.core.spi.EventContext; | |
import org.jboss.arquillian.core.spi.LoadableExtension; | |
import org.jboss.arquillian.test.spi.TestClass; | |
import org.jboss.arquillian.test.spi.annotation.ClassScoped; | |
import org.jboss.arquillian.test.spi.annotation.SuiteScoped; | |
import org.jboss.arquillian.test.spi.event.suite.AfterSuite; | |
/** | |
* @author famod | |
*/ | |
public class ReuseDeploymentExtension implements LoadableExtension { | |
private static final Logger LOG = Logger.getLogger(ReuseDeploymentExtension.class.getName()); | |
@Override | |
public void register(ExtensionBuilder builder) { | |
builder.observer(ReuseDeploymentHandler.class); | |
} | |
private static class ReuseDeploymentHandler { | |
private Class<?> activeReusableDeploymentClass; | |
private final Map<Class<?>, DeploymentScenario> generatedReusableDeploymentScenarios = new HashMap<>(); | |
private boolean blockDeploy; | |
private boolean blockUnDeploy; | |
@Inject | |
private Event<GenerateDeployment> generateDeploymentEvent; | |
@Inject | |
private Event<UnDeployManagedDeployments> unDeployManagedDeploymentsEvent; | |
@Inject | |
@ClassScoped | |
private InstanceProducer<DeploymentScenario> deploymentScenarioProducer; | |
@Inject | |
@SuiteScoped | |
private InstanceProducer<DeploymentScenario> suiteScopedDeploymentScenarioProducer; | |
public void generateDeployment(@Observes EventContext<GenerateDeployment> eventCtx) { | |
// just proceed if event has been fired from this method (see further down) | |
if (eventCtx.getEvent() instanceof GenerateReusableDeployment) { | |
eventCtx.proceed(); | |
return; | |
} | |
blockDeploy = false; | |
blockUnDeploy = false; | |
// evaluate @ReuseDeployment on test class (if present) | |
final TestClass testClass = eventCtx.getEvent().getTestClass(); | |
final Class<?> deploymentClassToReuse = Optional.ofNullable(testClass.getAnnotation(ReuseDeployment.class)) | |
.map(ReuseDeployment::value) | |
.orElse(null); | |
// skip deploy/undeploy if the active reusable deployment class is the same as the one required by the test class | |
if (deploymentClassToReuse != null && deploymentClassToReuse == activeReusableDeploymentClass) { | |
LOG.log(Level.FINE, "Reusing deployed Deployment for {0}: {1}", new Object[] { testClass.getName(), activeReusableDeploymentClass }); | |
deploymentScenarioProducer.set(generatedReusableDeploymentScenarios.get(deploymentClassToReuse)); | |
blockDeploy = true; | |
blockUnDeploy = true; | |
return; | |
} | |
// undeploy deployed reusable deployment because test class requires another reusable or a non-reusable deployment | |
if (activeReusableDeploymentClass != null) { | |
LOG.log(Level.FINE, "Undeploying for {0}: {1}", new Object[] { testClass.getName(), activeReusableDeploymentClass }); | |
deploymentScenarioProducer.set(generatedReusableDeploymentScenarios.get(activeReusableDeploymentClass)); | |
unDeployManagedDeploymentsEvent.fire(new UnDeployManagedDeployments()); | |
} | |
// just proceed without any futher special handling in case the test class requires a non-reusable deployment | |
if (deploymentClassToReuse == null) { | |
LOG.log(Level.FINE, "Performing default deployment procedure for {0}", testClass.getName()); | |
eventCtx.proceed(); | |
return; | |
} | |
// test class requires reusable deployment which is no deployed and may not even have been generated | |
final DeploymentScenario deploymentScenario = generatedReusableDeploymentScenarios.get(deploymentClassToReuse); | |
if (deploymentScenario != null) { | |
LOG.log(Level.FINE, "Reusing generated Deployment for {0}: {1}", new Object[] { testClass.getName(), deploymentClassToReuse }); | |
deploymentScenarioProducer.set(deploymentScenario); | |
} else { | |
LOG.log(Level.FINE, "Generating reusable deployment for {0}: {1}", new Object[] { testClass.getName(), deploymentClassToReuse }); | |
generateDeploymentEvent.fire(new GenerateReusableDeployment(new TestClass(deploymentClassToReuse))); | |
generatedReusableDeploymentScenarios.put(deploymentClassToReuse, deploymentScenarioProducer.get()); | |
} | |
activeReusableDeploymentClass = deploymentClassToReuse; | |
blockUnDeploy = true; | |
} | |
public void deploy(@Observes EventContext<DeployManagedDeployments> eventCtx) { | |
if (blockDeploy) { | |
LOG.log(Level.FINE, "Blocking deployment, activeReusableDeploymentClass: {0}", | |
Optional.ofNullable(activeReusableDeploymentClass).map(Class::getName).orElse("-")); | |
} else { | |
eventCtx.proceed(); | |
} | |
} | |
public void undeploy(@Observes EventContext<UnDeployManagedDeployments> eventCtx) { | |
if (blockUnDeploy) { | |
LOG.log(Level.FINE, "Blocking undeployment, activeReusableDeploymentClass: {0}", | |
Optional.ofNullable(activeReusableDeploymentClass).map(Class::getName).orElse("-")); | |
} else { | |
eventCtx.proceed(); | |
activeReusableDeploymentClass = null; | |
} | |
} | |
public void undeploy(@Observes BeforeStop event) { | |
if (activeReusableDeploymentClass != null) { | |
LOG.log(Level.FINE, "Undeploying reusable deployment before stopping container: {0}", activeReusableDeploymentClass.getName()); | |
blockUnDeploy = false; | |
suiteScopedDeploymentScenarioProducer.set(generatedReusableDeploymentScenarios.get(activeReusableDeploymentClass)); | |
unDeployManagedDeploymentsEvent.fire(new UnDeployManagedDeployments()); | |
} | |
} | |
public void cleanUp(@Observes(precedence = Integer.MIN_VALUE) AfterSuite event) { | |
generatedReusableDeploymentScenarios.clear(); | |
} | |
private static class GenerateReusableDeployment extends GenerateDeployment { | |
public GenerateReusableDeployment(TestClass testClass) { | |
super(testClass); | |
} | |
} | |
} | |
} |
And if you have lets say 1k tests how do you force them to first use some deployment, then use another etc..
Not switch deployment every test...
@ingwarsw This is a problem indeed, which could be solved by sorting the test classes. Unfortunately this not trivial with JUnit4 and/or Maven Surefire.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is an alternate approach to https://github.com/ingwarsw/arquillian-suite-extension/blob/master/src/main/java/org/eu/ingwar/tools/arquillian/extension/suite/ArquillianSuiteExtension.java which supports multiple "reusable" deployments via
@ReuseDeployment(MyCommonDeployment.class)
and does not "force" all Arquillian tests into one "deployment suite".Each generated/deployed reusable
DeploymentScenario
is stored in a map and is restored as soon as another test class references that scenario/deployment class. A reusable deployment is undeployed if a test class does not reference it (assuming non-coexistence between different deployments).