1010import java .nio .file .Files ;
1111import java .nio .file .Path ;
1212import java .nio .file .Paths ;
13+ import java .util .ArrayList ;
1314import java .util .Collection ;
1415import java .util .HashMap ;
1516import java .util .HashSet ;
3738import io .quarkus .bootstrap .workspace .SourceDir ;
3839import io .quarkus .bootstrap .workspace .WorkspaceModule ;
3940import io .quarkus .commons .classloading .ClassLoaderHelper ;
41+ import io .quarkus .maven .dependency .DependencyFlags ;
4042import io .quarkus .paths .PathList ;
4143import io .quarkus .runtime .LaunchMode ;
4244import 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