Skip to content

Instantly share code, notes, and snippets.

@famod
Last active November 7, 2017 11:37
Show Gist options
  • Save famod/a423cadbe976401e02002b9103d1d2d5 to your computer and use it in GitHub Desktop.
Save famod/a423cadbe976401e02002b9103d1d2d5 to your computer and use it in GitHub Desktop.
Arquillian ReuseDeployment extension
/*
* 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();
}
/*
* 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);
}
}
}
}
@famod
Copy link
Author

famod commented Nov 7, 2017

@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