Skip to content

Commit 75a51dc

Browse files
committed
WIP
1 parent fb89d7f commit 75a51dc

File tree

5 files changed

+103
-261
lines changed

5 files changed

+103
-261
lines changed

test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java

Lines changed: 9 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import static io.quarkus.test.common.PathTestHelper.getTestClassesLocation;
44

55
import java.io.IOException;
6-
import java.lang.reflect.InvocationTargetException;
76
import java.nio.file.Path;
87
import java.util.ArrayDeque;
98
import java.util.Collection;
@@ -14,9 +13,6 @@
1413
import java.util.Optional;
1514
import java.util.ServiceConfigurationError;
1615
import java.util.Set;
17-
import java.util.stream.Collectors;
18-
19-
import jakarta.enterprise.inject.Alternative;
2016

2117
import org.eclipse.microprofile.config.ConfigProvider;
2218
import org.junit.jupiter.api.Nested;
@@ -25,16 +21,13 @@
2521
import org.junit.jupiter.api.extension.ExtensionContext;
2622

2723
import io.quarkus.bootstrap.BootstrapException;
28-
import io.quarkus.bootstrap.app.AugmentAction;
2924
import io.quarkus.bootstrap.app.CuratedApplication;
3025
import io.quarkus.bootstrap.app.RunningQuarkusApplication;
3126
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
3227
import io.quarkus.bootstrap.resolver.AppModelResolverException;
3328
import io.quarkus.bootstrap.runner.Timing;
3429
import io.quarkus.deployment.dev.testing.TestConfig;
3530
import io.quarkus.logging.Log;
36-
import io.quarkus.runtime.LaunchMode;
37-
import io.quarkus.test.common.RestorableSystemProperties;
3831
import io.smallrye.config.SmallRyeConfig;
3932

4033
public class AbstractJvmQuarkusTestExtension extends AbstractQuarkusTestWithContextExtension
@@ -56,13 +49,14 @@ public class AbstractJvmQuarkusTestExtension extends AbstractQuarkusTestWithCont
5649
protected static Class<?> currentJUnitTestClass;
5750

5851
// TODO only used by QuarkusMainTest, fix that class and delete this
59-
protected PrepareResult createAugmentor(ExtensionContext context, Class<? extends QuarkusTestProfile> profile,
52+
protected QuarkusTestPrepareResult createAugmentor(ExtensionContext context, Class<? extends QuarkusTestProfile> profile,
6053
Collection<Runnable> shutdownTasks) throws Exception {
6154

6255
originalCl = Thread.currentThread().getContextClassLoader();
56+
quarkusTestProfile = profile;
6357
final Class<?> requiredTestClass = context.getRequiredTestClass();
6458

65-
CuratedApplication curatedApplication = getCuratedApplication(requiredTestClass, context, shutdownTasks);
59+
CuratedApplication curatedApplication = getCuratedApplication(requiredTestClass, context.getDisplayName());
6660

6761
// TODO need to handle the gradle case - can we put it in that method?
6862
Path testClassLocation = getTestClassesLocation(requiredTestClass, curatedApplication);
@@ -82,56 +76,21 @@ protected PrepareResult createAugmentor(ExtensionContext context, Class<? extend
8276

8377
// clear the test.url system property as the value leaks into the run when using different profiles
8478
System.clearProperty("test.url");
85-
Map<String, String> additional = new HashMap<>();
8679

87-
QuarkusTestProfile profileInstance = getQuarkusTestProfile(profile, shutdownTasks, additional);
80+
QuarkusTestProfile profileInstance = AppMakerHelper.getQuarkusTestProfile(profile, shutdownTasks);
8881

8982
if (profile != null) {
9083
props.put(TEST_PROFILE, profile.getName());
9184
}
92-
quarkusTestProfile = profile;
93-
return new PrepareResult(curatedApplication
85+
return new QuarkusTestPrepareResult(curatedApplication
9486
.createAugmentor(TestBuildChainFunction.class.getName(), props), profileInstance,
95-
curatedApplication, testClassLocation);
87+
curatedApplication);
9688
}
9789

98-
protected CuratedApplication getCuratedApplication(Class<?> requiredTestClass, ExtensionContext context,
99-
Collection<Runnable> shutdownTasks) throws BootstrapException, AppModelResolverException, IOException {
90+
protected CuratedApplication getCuratedApplication(Class<?> requiredTestClass, String displayName)
91+
throws AppModelResolverException, IOException, BootstrapException {
10092
// TODO make this abstract, push this implementation down to QuarkusTestExtension, since that is the only place it will work
101-
CuratedApplication curatedApplication = ((QuarkusClassLoader) requiredTestClass.getClassLoader())
102-
.getCuratedApplication();
103-
return curatedApplication;
104-
}
105-
106-
protected static QuarkusTestProfile getQuarkusTestProfile(Class<? extends QuarkusTestProfile> profile,
107-
Collection<Runnable> shutdownTasks, Map<String, String> additional)
108-
throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
109-
QuarkusTestProfile profileInstance = null;
110-
if (profile != null) {
111-
profileInstance = profile.getConstructor().newInstance();
112-
// TODO the stuff below here is unique to this class - TODO what does this comment even mean?
113-
additional.putAll(profileInstance.getConfigOverrides());
114-
if (!profileInstance.getEnabledAlternatives().isEmpty()) {
115-
additional.put("quarkus.arc.selected-alternatives", profileInstance.getEnabledAlternatives().stream()
116-
.peek((c) -> {
117-
if (!c.isAnnotationPresent(Alternative.class)) {
118-
throw new RuntimeException(
119-
"Enabled alternative " + c + " is not annotated with @Alternative");
120-
}
121-
})
122-
.map(Class::getName).collect(Collectors.joining(",")));
123-
}
124-
if (profileInstance.disableApplicationLifecycleObservers()) {
125-
additional.put("quarkus.arc.test.disable-application-lifecycle-observers", "true");
126-
}
127-
if (profileInstance.getConfigProfile() != null) {
128-
additional.put(LaunchMode.TEST.getProfileKey(), profileInstance.getConfigProfile());
129-
}
130-
//we just use system properties for now
131-
//it's a lot simpler
132-
shutdownTasks.add(RestorableSystemProperties.setProperties(additional)::close);
133-
}
134-
return profileInstance;
93+
return ((QuarkusClassLoader) requiredTestClass.getClassLoader()).getCuratedApplication();
13594
}
13695

13796
// TODO is it nicer to pass in the test class, or invoke the getter twice?
@@ -298,19 +257,4 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con
298257
return ConditionEvaluationResult.disabled("Test '" + context.getRequiredTestClass()
299258
+ "' disabled because 'quarkus.profile.test.tags' don't match the tags of '" + testProfile + "'");
300259
}
301-
302-
protected static class PrepareResult {
303-
protected final AugmentAction augmentAction;
304-
protected final QuarkusTestProfile profileInstance;
305-
protected final CuratedApplication curatedApplication;
306-
protected final Path testClassLocation;
307-
308-
public PrepareResult(AugmentAction augmentAction, QuarkusTestProfile profileInstance,
309-
CuratedApplication curatedApplication, Path testClassLocation) {
310-
this.augmentAction = augmentAction;
311-
this.profileInstance = profileInstance;
312-
this.curatedApplication = curatedApplication;
313-
this.testClassLocation = testClassLocation;
314-
}
315-
}
316260
}

test-framework/junit5/src/main/java/io/quarkus/test/junit/AppMakerHelper.java

Lines changed: 75 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.nio.file.Files;
1111
import java.nio.file.Path;
1212
import java.nio.file.Paths;
13+
import java.util.ArrayList;
1314
import java.util.Collection;
1415
import java.util.HashMap;
1516
import java.util.HashSet;
@@ -37,6 +38,7 @@
3738
import io.quarkus.bootstrap.workspace.SourceDir;
3839
import io.quarkus.bootstrap.workspace.WorkspaceModule;
3940
import io.quarkus.commons.classloading.ClassLoaderHelper;
41+
import io.quarkus.maven.dependency.DependencyFlags;
4042
import io.quarkus.paths.PathList;
4143
import io.quarkus.runtime.LaunchMode;
4244
import io.quarkus.test.common.PathTestHelper;
@@ -57,27 +59,14 @@ public class AppMakerHelper {
5759
private static List<Object> testMethodInvokers;
5860
private Runnable configCleanup;
5961

60-
public static class PrepareResult {
61-
protected final AugmentAction augmentAction;
62-
public final QuarkusTestProfile profileInstance;
63-
protected final CuratedApplication curatedApplication;
64-
65-
public PrepareResult(AugmentAction augmentAction, QuarkusTestProfile profileInstance,
66-
CuratedApplication curatedApplication) {
67-
68-
this.augmentAction = augmentAction;
69-
this.profileInstance = profileInstance;
70-
this.curatedApplication = curatedApplication;
71-
}
72-
}
73-
7462
public static ApplicationModel getGradleAppModelForIDE(Path projectRoot) throws IOException, AppModelResolverException {
7563
return System.getProperty(BootstrapConstants.SERIALIZED_TEST_APP_MODEL) == null
7664
? BuildToolHelper.enableGradleAppModelForTest(projectRoot)
7765
: null;
7866
}
7967

80-
private PrepareResult createAugmentor(final Class<?> requiredTestClass, String displayName, boolean isContinuousTesting,
68+
private QuarkusTestPrepareResult createAugmentor(final Class<?> requiredTestClass, String displayName,
69+
boolean isContinuousTesting,
8170
CuratedApplication curatedApplication,
8271
Class<? extends QuarkusTestProfile> profile,
8372
Collection<Runnable> shutdownTasks) throws AppModelResolverException, BootstrapException, IOException,
@@ -86,65 +75,75 @@ private PrepareResult createAugmentor(final Class<?> requiredTestClass, String d
8675
if (curatedApplication == null) {
8776
curatedApplication = makeCuratedApplication(requiredTestClass, displayName, isContinuousTesting, shutdownTasks);
8877
}
78+
8979
Path testClassLocation = getTestClassesLocation(requiredTestClass, curatedApplication);
9080

9181
// clear the test.url system property as the value leaks into the run when using different profiles
9282
System.clearProperty("test.url");
93-
Map<String, String> additional = new HashMap<>();
94-
95-
QuarkusTestProfile profileInstance = null;
96-
if (profile != null) {
97-
98-
profileInstance = new ClassCoercingTestProfile(profile.getConstructor()
99-
.newInstance());
100-
// TODO we make this twice, also in abstractjvmextension can we streamline that?
101-
// TODO We can't get rid of the one here because config needs to be set before augmentation, but maybe we can get rid of it on the test side?
102-
additional.putAll(profileInstance.getConfigOverrides());
103-
if (!profileInstance.getEnabledAlternatives()
104-
.isEmpty()) {
105-
additional.put("quarkus.arc.selected-alternatives", profileInstance.getEnabledAlternatives()
106-
.stream()
107-
.peek((c) -> {
108-
try {
109-
// TODO is string comparison more efficient?
110-
if (!c.isAnnotationPresent((Class<? extends Annotation>) profile.getClassLoader()
111-
.loadClass(Alternative.class.getName()))) {
112-
throw new RuntimeException(
113-
"Enabled alternative " + c + " is not annotated with @Alternative");
114-
}
115-
} catch (ClassNotFoundException e) {
116-
throw new RuntimeException(e);
117-
}
118-
})
119-
.map(Class::getName)
120-
.collect(Collectors.joining(",")));
121-
}
122-
if (profileInstance.disableApplicationLifecycleObservers()) {
123-
additional.put("quarkus.arc.test.disable-application-lifecycle-observers", "true");
124-
}
125-
if (profileInstance.getConfigProfile() != null) {
126-
additional.put(LaunchMode.TEST.getProfileKey(), profileInstance.getConfigProfile());
127-
}
128-
129-
//we just use system properties for now
130-
//it's a lot simpler
131-
// TODO this is really ugly, set proper config on the app
132-
// Sadly, I don't think #42715 helps, because it kicks in after this code
133-
configCleanup = RestorableSystemProperties.setProperties(additional)::close;
134-
}
135-
136-
if (curatedApplication
137-
.getApplicationModel().getRuntimeDependencies().isEmpty()) {
138-
throw new RuntimeException(
139-
"The tests were run against a directory that does not contain a Quarkus project. Please ensure that the test is configured to use the proper working directory.");
140-
}
14183

14284
// TODO should we do this here, or when we prepare the curated application?
14385
// Or is it needed at all?
14486
Index testClassesIndex = TestClassIndexer.indexTestClasses(testClassLocation);
14587
// we need to write the Index to make it reusable from other parts of the testing infrastructure that run in different ClassLoaders
14688
TestClassIndexer.writeIndex(testClassesIndex, testClassLocation, requiredTestClass);
14789

90+
return getPrepareResult(requiredTestClass, curatedApplication, profile, testClassLocation);
91+
}
92+
93+
static QuarkusTestProfile getQuarkusTestProfile(Class<? extends QuarkusTestProfile> profile,
94+
Collection<Runnable> shutdownTasks)
95+
throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
96+
if (profile == null) {
97+
return null;
98+
}
99+
final QuarkusTestProfile profileInstance = new ClassCoercingTestProfile(profile.getConstructor()
100+
.newInstance());
101+
final Map<String, String> additional = new HashMap<>();
102+
// TODO we make this twice, also in abstractjvmextension can we streamline that?
103+
// TODO We can't get rid of the one here because config needs to be set before augmentation, but maybe we can get rid of it on the test side?
104+
additional.putAll(profileInstance.getConfigOverrides());
105+
if (!profileInstance.getEnabledAlternatives().isEmpty()) {
106+
additional.put("quarkus.arc.selected-alternatives", profileInstance.getEnabledAlternatives()
107+
.stream()
108+
.peek((c) -> {
109+
try {
110+
// TODO is string comparison more efficient?
111+
if (!c.isAnnotationPresent((Class<? extends Annotation>) profile.getClassLoader()
112+
.loadClass(Alternative.class.getName()))) {
113+
throw new RuntimeException(
114+
"Enabled alternative " + c + " is not annotated with @Alternative");
115+
}
116+
} catch (ClassNotFoundException e) {
117+
throw new RuntimeException(e);
118+
}
119+
})
120+
.map(Class::getName)
121+
.collect(Collectors.joining(",")));
122+
}
123+
if (profileInstance.disableApplicationLifecycleObservers()) {
124+
additional.put("quarkus.arc.test.disable-application-lifecycle-observers", "true");
125+
}
126+
if (profileInstance.getConfigProfile() != null) {
127+
additional.put(LaunchMode.TEST.getProfileKey(), profileInstance.getConfigProfile());
128+
}
129+
130+
//we just use system properties for now
131+
//it's a lot simpler
132+
// TODO this is really ugly, set proper config on the app
133+
// Sadly, I don't think #42715 helps, because it kicks in after this code
134+
shutdownTasks.add(RestorableSystemProperties.setProperties(additional)::close);
135+
return profileInstance;
136+
}
137+
138+
private QuarkusTestPrepareResult getPrepareResult(Class<?> requiredTestClass, CuratedApplication curatedApplication,
139+
Class<? extends QuarkusTestProfile> profile, Path testClassLocation)
140+
throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
141+
QuarkusTestProfile profileInstance = null;
142+
if (profile != null) {
143+
var shutdownTasks = new ArrayList<Runnable>(1);
144+
profileInstance = getQuarkusTestProfile(profile, shutdownTasks);
145+
configCleanup = shutdownTasks.get(0);
146+
}
148147
Timing.staticInitStarted(curatedApplication
149148
.getOrCreateBaseRuntimeClassLoader(),
150149
curatedApplication
@@ -156,12 +155,12 @@ private PrepareResult createAugmentor(final Class<?> requiredTestClass, String d
156155
if (profile != null) {
157156
props.put(TEST_PROFILE, profile.getName());
158157
}
159-
return new PrepareResult(curatedApplication
158+
return new QuarkusTestPrepareResult(curatedApplication
160159
.createAugmentor(TestBuildChainFunction.class.getName(), props), profileInstance,
161160
curatedApplication);
162161
}
163162

164-
public CuratedApplication makeCuratedApplication(Class<?> requiredTestClass, String displayName,
163+
public static CuratedApplication makeCuratedApplication(Class<?> requiredTestClass, String displayName,
165164
boolean isContinuousTesting,
166165
Collection<Runnable> shutdownTasks) throws IOException, AppModelResolverException, BootstrapException {
167166
final PathList.Builder rootBuilder = PathList.builder();
@@ -255,6 +254,12 @@ public CuratedApplication makeCuratedApplication(Class<?> requiredTestClass, Str
255254
.bootstrap();
256255
shutdownTasks.add(curatedApplication::close);
257256

257+
if (!curatedApplication.getApplicationModel().getDependencies(DependencyFlags.RUNTIME_CP).iterator().hasNext()) {
258+
throw new RuntimeException(
259+
"The tests were run against a directory that does not contain a Quarkus project. Please ensure that the test is configured to use the proper working directory.");
260+
261+
}
262+
258263
// TODO can we consolidate some of this with TestSupport? The code over there is
259264
// final QuarkusBootstrap.Builder bootstrapConfig = curatedApplication.getQuarkusBootstrap().clonedBuilder()
260265
// .setMode(QuarkusBootstrap.Mode.TEST)
@@ -276,14 +281,14 @@ public CuratedApplication makeCuratedApplication(Class<?> requiredTestClass, Str
276281
// Note that curated application cannot be re-used between restarts, so this application
277282
// should have been freshly created
278283
// TODO maybe don't even accept one? is that comment right?
279-
public StartupAction getStartupAction(Class testClass, CuratedApplication curatedApplication,
284+
public StartupAction getStartupAction(Class<?> testClass, CuratedApplication curatedApplication,
280285
boolean isContinuousTesting, Class profile) throws AppModelResolverException, BootstrapException, IOException,
281286
InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
282287

283-
Collection<Runnable> shutdownTasks = new HashSet();
284-
PrepareResult result = createAugmentor(testClass, "(QuarkusTest)", isContinuousTesting, curatedApplication, profile,
285-
shutdownTasks);
286-
AugmentAction augmentAction = result.augmentAction;
288+
Collection<Runnable> shutdownTasks = new HashSet<>();
289+
final AugmentAction augmentAction = createAugmentor(testClass, "(QuarkusTest)", isContinuousTesting, curatedApplication,
290+
profile,
291+
shutdownTasks).augmentAction();
287292

288293
try {
289294
StartupAction startupAction = augmentAction.createInitialRuntimeApplication();

0 commit comments

Comments
 (0)