Skip to content

Commit

Permalink
Allow the use of QuarkusTestResourceLifecycleManager with @testprofile
Browse files Browse the repository at this point in the history
  • Loading branch information
geoand committed Jun 30, 2020
1 parent a4028e3 commit b0d7c99
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.QuarkusTestProfile;
import io.quarkus.test.junit.TestProfile;
Expand All @@ -35,6 +37,12 @@ public void testPortTakesEffect() {
Assertions.assertEquals(7777, RestAssured.port);
}

@Test
public void testTestResourceState() {
// 155 means that the TestResource was started but hasn't yet stopped
Assertions.assertEquals(155, DummyTestResource.state.get());
}

public static class MyProfile implements QuarkusTestProfile {

@Override
Expand All @@ -46,5 +54,40 @@ public Map<String, String> getConfigOverrides() {
public Set<Class<?>> getEnabledAlternatives() {
return Collections.singleton(BonjourService.class);
}

@Override
public Map<Class<? extends QuarkusTestResourceLifecycleManager>, Map<String, String>> testResources() {
return Collections.singletonMap(DummyTestResource.class, Collections.singletonMap("num", "100"));
}
}

/**
* This only used to ensure that the TestResource has been handled correctly by the QuarkusTestExtension
*/
public static class DummyTestResource implements QuarkusTestResourceLifecycleManager {

public static final AtomicInteger state = new AtomicInteger(0);
public static final int START_DELTA = 55;

private Integer numArg;

@Override
public void init(Map<String, String> initArgs) {
numArg = Integer.parseInt(initArgs.get("num"));
state.set(numArg);
}

@Override
public Map<String, String> start() {
state.addAndGet(START_DELTA);
return Collections.emptyMap();
}

@Override
public void stop() {
if (state.get() != (numArg + START_DELTA)) {
throw new IllegalStateException("TestResource state was not properly handled");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ public class TestResourceManager implements Closeable {
private boolean started = false;

public TestResourceManager(Class<?> testClass) {
testResourceEntries = getTestResources(testClass);
this(testClass, Collections.emptyMap());
}

public TestResourceManager(Class<?> testClass,
Map<Class<? extends QuarkusTestResourceLifecycleManager>, Map<String, String>> additionalTestResources) {
testResourceEntries = getTestResources(testClass, additionalTestResources);
}

public void init() {
Expand Down Expand Up @@ -102,18 +107,19 @@ public void close() {
}

@SuppressWarnings("unchecked")
private List<TestResourceEntry> getTestResources(Class<?> testClass) {
private List<TestResourceEntry> getTestResources(Class<?> testClass,
Map<Class<? extends QuarkusTestResourceLifecycleManager>, Map<String, String>> additionalTestResources) {
IndexView index = TestClassIndexer.readIndex(testClass);

List<TestResourceEntry> testResourceEntries = new ArrayList<>();

// we need to keep track of duplicate entries to make sure we don't start the same resource
// multiple times even if there are multiple same @QuarkusTestResource annotations
Set<TestResourceClassEntry> alreadyAddedEntries = new HashSet<>();
Set<TestResourceClassEntry> uniqueEntries = new HashSet<>();
for (AnnotationInstance annotation : findQuarkusTestResourceInstances(index)) {
try {
Class<? extends QuarkusTestResourceLifecycleManager> testResourceClass = (Class<? extends QuarkusTestResourceLifecycleManager>) Class
.forName(annotation.value().asString(), true, Thread.currentThread().getContextClassLoader());
Class<? extends QuarkusTestResourceLifecycleManager> testResourceClass = loadTestResourceClassFromTCCL(
annotation.value().asString());

AnnotationValue argsAnnotationValue = annotation.value("initArgs");
Map<String, String> args;
Expand All @@ -128,15 +134,38 @@ private List<TestResourceEntry> getTestResources(Class<?> testClass) {
}

TestResourceClassEntry testResourceClassEntry = new TestResourceClassEntry(testResourceClass, args);
if (alreadyAddedEntries.contains(testResourceClassEntry)) {
if (uniqueEntries.contains(testResourceClassEntry)) {
continue;
}
uniqueEntries.add(testResourceClassEntry);
} catch (IllegalArgumentException | SecurityException e) {
throw new RuntimeException("Unable to instantiate the test resource " + annotation.value().asString());
}
}

for (Map.Entry<Class<? extends QuarkusTestResourceLifecycleManager>, Map<String, String>> entry : additionalTestResources
.entrySet()) {
String testResourceClassName = entry.getKey().getName();
try {
TestResourceClassEntry entryFromCorrectCL = new TestResourceClassEntry(
loadTestResourceClassFromTCCL(testResourceClassName), entry.getValue());
if (uniqueEntries.contains(entryFromCorrectCL)) {
continue;
}
alreadyAddedEntries.add(testResourceClassEntry);
uniqueEntries.add(entryFromCorrectCL);
} catch (IllegalArgumentException | SecurityException e) {
throw new RuntimeException("Unable to instantiate the test resource " + testResourceClassName);
}
}

for (TestResourceClassEntry entry : uniqueEntries) {
Class<? extends QuarkusTestResourceLifecycleManager> testResourceClass = entry.clazz;
Map<String, String> args = entry.args;
try {
testResourceEntries.add(new TestResourceEntry(testResourceClass.getConstructor().newInstance(), args));
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
throw new RuntimeException("Unable to instantiate the test resource " + annotation.value().asString());
throw new RuntimeException("Unable to instantiate the test resource " + testResourceClass.getName());
}
}

Expand All @@ -158,6 +187,16 @@ public int compare(TestResourceEntry o1, TestResourceEntry o2) {
return testResourceEntries;
}

@SuppressWarnings("unchecked")
private Class<? extends QuarkusTestResourceLifecycleManager> loadTestResourceClassFromTCCL(String className) {
try {
return (Class<? extends QuarkusTestResourceLifecycleManager>) Class
.forName(className, true, Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}

private Collection<AnnotationInstance> findQuarkusTestResourceInstances(IndexView index) {
Set<AnnotationInstance> testResourceAnnotations = new HashSet<>(index
.getAnnotations(DotName.createSimple(QuarkusTestResource.class.getName())));
Expand All @@ -168,29 +207,7 @@ private Collection<AnnotationInstance> findQuarkusTestResourceInstances(IndexVie
return testResourceAnnotations;
}

private static class TestResourceEntry {
private final QuarkusTestResourceLifecycleManager testResource;
private final Map<String, String> args;

public TestResourceEntry(QuarkusTestResourceLifecycleManager testResource) {
this(testResource, Collections.emptyMap());
}

public TestResourceEntry(QuarkusTestResourceLifecycleManager testResource, Map<String, String> args) {
this.testResource = testResource;
this.args = args;
}

public QuarkusTestResourceLifecycleManager getTestResource() {
return testResource;
}

public Map<String, String> getArgs() {
return args;
}
}

private static class TestResourceClassEntry {
public static class TestResourceClassEntry {
private Class<? extends QuarkusTestResourceLifecycleManager> clazz;
private Map<String, String> args;

Expand All @@ -216,4 +233,26 @@ public int hashCode() {
}
}

private static class TestResourceEntry {
private final QuarkusTestResourceLifecycleManager testResource;
private final Map<String, String> args;

public TestResourceEntry(QuarkusTestResourceLifecycleManager testResource) {
this(testResource, Collections.emptyMap());
}

public TestResourceEntry(QuarkusTestResourceLifecycleManager testResource, Map<String, String> args) {
this.testResource = testResource;
this.args = args;
}

public QuarkusTestResourceLifecycleManager getTestResource() {
return testResource;
}

public Map<String, String> getArgs() {
return args;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.nio.file.Paths;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -63,6 +64,7 @@
import io.quarkus.runtime.configuration.ProfileManager;
import io.quarkus.test.common.PathTestHelper;
import io.quarkus.test.common.PropertyTestUtil;
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
import io.quarkus.test.common.RestAssuredURLManager;
import io.quarkus.test.common.TestClassIndexer;
import io.quarkus.test.common.TestResourceManager;
Expand Down Expand Up @@ -132,8 +134,9 @@ private ExtensionState doJavaStart(ExtensionContext context, Class<? extends Qua
final QuarkusBootstrap.Builder runnerBuilder = QuarkusBootstrap.builder()
.setIsolateDeployment(true)
.setMode(QuarkusBootstrap.Mode.TEST);
QuarkusTestProfile profileInstance = null;
if (profile != null) {
QuarkusTestProfile profileInstance = profile.newInstance();
profileInstance = profile.newInstance();
Map<String, String> additional = new HashMap<>(profileInstance.getConfigOverrides());
if (!profileInstance.getEnabledAlternatives().isEmpty()) {
additional.put("quarkus.arc.selected-alternatives", profileInstance.getEnabledAlternatives().stream()
Expand Down Expand Up @@ -188,8 +191,8 @@ private ExtensionState doJavaStart(ExtensionContext context, Class<? extends Qua

//must be done after the TCCL has been set
testResourceManager = (Closeable) startupAction.getClassLoader().loadClass(TestResourceManager.class.getName())
.getConstructor(Class.class)
.newInstance(requiredTestClass);
.getConstructor(Class.class, Map.class)
.newInstance(requiredTestClass, getAdditionalTestResourceEntries(profileInstance));
testResourceManager.getClass().getMethod("init").invoke(testResourceManager);
testResourceManager.getClass().getMethod("start").invoke(testResourceManager);

Expand Down Expand Up @@ -253,6 +256,21 @@ public void run() {
}
}

private Map<Class<? extends QuarkusTestResourceLifecycleManager>, Map<String, String>> getAdditionalTestResourceEntries(
QuarkusTestProfile profileInstance) {
if (profileInstance == null) {
return Collections.emptyMap();
}

Map<Class<? extends QuarkusTestResourceLifecycleManager>, Map<String, String>> testResources = profileInstance
.testResources();
if (testResources.isEmpty()) {
return Collections.emptyMap();
}

return testResources;
}

// keep it super simple for now, but we might need multiple strategies in the future
private void populateDeepCloneField(StartupAction startupAction) {
deepClone = new XStreamDeepClone(startupAction.getClassLoader());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import java.util.Map;
import java.util.Set;

import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;

/**
* Defines a 'test profile'. Tests run under a test profile
* will have different configuration options to other tests.
Expand Down Expand Up @@ -39,4 +41,12 @@ default Set<Class<?>> getEnabledAlternatives() {
default String getConfigProfile() {
return null;
}

/**
* {@link QuarkusTestResourceLifecycleManager} classes (along with their init params) to be used from this
* specific test profile
*/
default Map<Class<? extends QuarkusTestResourceLifecycleManager>, Map<String, String>> testResources() {
return Collections.emptyMap();
}
}

0 comments on commit b0d7c99

Please sign in to comment.