From d582dc205e59b661464fa62fe409258eb6eb29d8 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 26 May 2023 12:47:43 +0200 Subject: [PATCH 1/8] ArC: fix type discovery for classes added by Build Compatible Extensions The Build Compatible Extension API specifies that classes added through `ScannedClasses` during `@Discovery` are always discovered types and therefore always beans. This requires a change in the type discovery implementation in the Quarkus ArC extension: all classes added through `ScannedClasses` are subject to an annotation transformation that adds a bean defining annotation (`@AdditionalBean`). --- .../arc/deployment/BeanArchiveProcessor.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java index 162daf43e3aaf..14972848123b0 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java @@ -22,10 +22,12 @@ import org.jboss.jandex.Indexer; import org.jboss.logging.Logger; +import io.quarkus.arc.processor.AnnotationsTransformer; import io.quarkus.arc.processor.BeanArchives; import io.quarkus.arc.processor.BeanDefiningAnnotation; import io.quarkus.arc.processor.BeanDeployment; import io.quarkus.arc.processor.DotNames; +import io.quarkus.arc.runtime.AdditionalBean; import io.quarkus.deployment.ApplicationArchive; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -51,7 +53,8 @@ public BeanArchiveIndexBuildItem build(ArcConfig config, ApplicationArchivesBuil CustomScopeAnnotationsBuildItem customScopes, List excludeDependencyBuildItems, List beanArchivePredicates, List knownCompatibleBeanArchives, - BuildCompatibleExtensionsBuildItem buildCompatibleExtensions) + BuildCompatibleExtensionsBuildItem buildCompatibleExtensions, + BuildProducer annotationsTransformations) throws Exception { // First build an index from application archives @@ -69,6 +72,20 @@ public BeanArchiveIndexBuildItem build(ArcConfig config, ApplicationArchivesBuil Set additionalBeansFromExtensions = new HashSet<>(); buildCompatibleExtensions.entrypoint.runDiscovery(applicationIndex, additionalBeansFromExtensions); additionalBeanClasses.addAll(additionalBeansFromExtensions); + annotationsTransformations.produce(new AnnotationsTransformerBuildItem(new AnnotationsTransformer() { + @Override + public boolean appliesTo(Kind kind) { + return kind == Kind.CLASS; + } + + @Override + public void transform(TransformationContext ctx) { + if (additionalBeansFromExtensions.contains(ctx.getTarget().asClass().name().toString())) { + // make all the `@Discovery`-registered classes beans + ctx.transform().add(AdditionalBean.class).done(); + } + } + })); // Build the index for additional beans and generated bean classes Set additionalIndex = new HashSet<>(); From aab03ecfba3894cb02db6406087729d2eb2988b1 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 26 May 2023 12:52:48 +0200 Subject: [PATCH 2/8] ArC: fix bean archive discovery in strict mode The Quarkus ArC extension assumes that the root application archive is always a bean archive. That doesn't necessarily have to be true per the CDI specification, but is useful in practice. Therefore, it needs to be fixed only in strict mode, which is what this commit does. By default, the root application archive is always a bean archive. In strict mode, the root application archive goes through the same scanning process as other application archives. --- .../arc/deployment/BeanArchiveProcessor.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java index 14972848123b0..9c5b7300d4b88 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java @@ -152,9 +152,16 @@ private IndexView buildApplicationIndex(ArcConfig config, ApplicationArchivesBui beanDefiningAnnotations.add(DotNames.QUALIFIER); beanDefiningAnnotations.add(DotNames.INTERCEPTOR_BINDING); + boolean rootIsAlwaysBeanArchive = !config.strictCompatibility; + Collection candidateArchives = applicationArchivesBuildItem.getApplicationArchives(); + if (!rootIsAlwaysBeanArchive) { + candidateArchives = new ArrayList<>(candidateArchives); + candidateArchives.add(applicationArchivesBuildItem.getRootArchive()); + } + List indexes = new ArrayList<>(); - for (ApplicationArchive archive : applicationArchivesBuildItem.getApplicationArchives()) { + for (ApplicationArchive archive : candidateArchives) { if (isApplicationArchiveExcluded(config, excludeDependencyBuildItems, archive)) { continue; } @@ -168,7 +175,9 @@ private IndexView buildApplicationIndex(ArcConfig config, ApplicationArchivesBui indexes.add(index); } } - indexes.add(applicationArchivesBuildItem.getRootArchive().getIndex()); + if (rootIsAlwaysBeanArchive) { + indexes.add(applicationArchivesBuildItem.getRootArchive().getIndex()); + } return CompositeIndex.create(indexes); } From 9d514e6cd1ecae5affd750e8e0669cd794ceb212 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 26 May 2023 12:56:26 +0200 Subject: [PATCH 3/8] ArC: fix discovering bean archives that only contain scopes The Quarkus ArC extension used to discover bean archives that only contain qualifiers or interceptor bindings. This commit fixes that to also discover bean archives that only contain scopes. --- .../java/io/quarkus/arc/deployment/BeanArchiveProcessor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java index 9c5b7300d4b88..c03e0d3280d86 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java @@ -148,7 +148,9 @@ private IndexView buildApplicationIndex(ArcConfig config, ApplicationArchivesBui .map(bda -> new BeanDefiningAnnotation(bda.getName(), bda.getDefaultScope())) .collect(Collectors.toList()), stereotypes); beanDefiningAnnotations.addAll(customScopes.getCustomScopeNames()); - // Also include archives that are not bean archives but contain qualifiers or interceptor bindings + // Also include archives that are not bean archives but contain scopes, qualifiers or interceptor bindings + beanDefiningAnnotations.add(DotNames.SCOPE); + beanDefiningAnnotations.add(DotNames.NORMAL_SCOPE); beanDefiningAnnotations.add(DotNames.QUALIFIER); beanDefiningAnnotations.add(DotNames.INTERCEPTOR_BINDING); From 89ccdb1a901549b5e5c42ea1907b310bcdbe7c98 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Tue, 30 May 2023 09:10:46 +0200 Subject: [PATCH 4/8] TCKs: remove useless Arquillian extension from MP Context Propagation TCK This extension was used to activate/deactivate the CDI request context before/after each test. The Quarkus Arquillian adapter contains the exact same extension, so the one in MP Context Propagation TCK is useless. --- .../ArquillianBeforeAfterEnricher.java | 49 ------------------- .../QuarkusArquillianLifecycleExtension.java | 12 ----- ...boss.arquillian.core.spi.LoadableExtension | 1 - 3 files changed, 62 deletions(-) delete mode 100644 tcks/microprofile-context-propagation/src/main/java/io/quarkus/arquillian/ArquillianBeforeAfterEnricher.java delete mode 100644 tcks/microprofile-context-propagation/src/main/java/io/quarkus/arquillian/QuarkusArquillianLifecycleExtension.java delete mode 100644 tcks/microprofile-context-propagation/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension diff --git a/tcks/microprofile-context-propagation/src/main/java/io/quarkus/arquillian/ArquillianBeforeAfterEnricher.java b/tcks/microprofile-context-propagation/src/main/java/io/quarkus/arquillian/ArquillianBeforeAfterEnricher.java deleted file mode 100644 index a9a2949286bbe..0000000000000 --- a/tcks/microprofile-context-propagation/src/main/java/io/quarkus/arquillian/ArquillianBeforeAfterEnricher.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.quarkus.arquillian; - -import org.jboss.arquillian.container.spi.context.annotation.DeploymentScoped; -import org.jboss.arquillian.core.api.InstanceProducer; -import org.jboss.arquillian.core.api.annotation.Inject; -import org.jboss.arquillian.core.api.annotation.Observes; - -import io.quarkus.arc.Arc; - -/** - * Activates request context before test runs and shuts it down afterwards - */ -public class ArquillianBeforeAfterEnricher { - - private static final String ERROR_MSG = "Arc container is not running, cannot activate CDI contexts!"; - - @Inject - @DeploymentScoped - private InstanceProducer appClassloader; - - public void on(@Observes(precedence = -100) org.jboss.arquillian.test.spi.event.suite.Before event) throws Throwable { - //we are outside the runtime class loader, so we don't have direct access to the container - Class arcClz = appClassloader.get().loadClass(Arc.class.getName()); - Object container = arcClz.getMethod("container").invoke(null); - if (container != null) { - boolean running = (boolean) container.getClass().getMethod("isRunning").invoke(container); - if (running) { - Object context = container.getClass().getMethod("requestContext").invoke(container); - context.getClass().getMethod("activate").invoke(context); - } else { - throw new IllegalStateException(ERROR_MSG); - } - } - } - - public void on(@Observes(precedence = 100) org.jboss.arquillian.test.spi.event.suite.After event) throws Throwable { - Class arcClz = appClassloader.get().loadClass(Arc.class.getName()); - Object container = arcClz.getMethod("container").invoke(null); - if (container != null) { - boolean running = (boolean) container.getClass().getMethod("isRunning").invoke(container); - if (running) { - Object context = container.getClass().getMethod("requestContext").invoke(container); - context.getClass().getMethod("terminate").invoke(context); - } else { - throw new IllegalStateException(ERROR_MSG); - } - } - } -} diff --git a/tcks/microprofile-context-propagation/src/main/java/io/quarkus/arquillian/QuarkusArquillianLifecycleExtension.java b/tcks/microprofile-context-propagation/src/main/java/io/quarkus/arquillian/QuarkusArquillianLifecycleExtension.java deleted file mode 100644 index cf3362278045e..0000000000000 --- a/tcks/microprofile-context-propagation/src/main/java/io/quarkus/arquillian/QuarkusArquillianLifecycleExtension.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.quarkus.arquillian; - -import org.jboss.arquillian.core.spi.LoadableExtension; - -public class QuarkusArquillianLifecycleExtension implements LoadableExtension { - - @Override - public void register(ExtensionBuilder builder) { - // for MP CP TCK only, we want to register this observer which activated CDI contexts - builder.observer(ArquillianBeforeAfterEnricher.class); - } -} diff --git a/tcks/microprofile-context-propagation/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension b/tcks/microprofile-context-propagation/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension deleted file mode 100644 index f0a66d5d92b63..0000000000000 --- a/tcks/microprofile-context-propagation/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension +++ /dev/null @@ -1 +0,0 @@ -io.quarkus.arquillian.QuarkusArquillianLifecycleExtension \ No newline at end of file From 4a68d1fe71f3552de0e5c515592e8dda6c0de8a3 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 26 May 2023 13:00:25 +0200 Subject: [PATCH 5/8] Arquillian: add workaround for memory leak Arquillian holds onto deployment-scoped (and container-scoped) objects until its end of life. That allows undeploying and redeploying without losing contextual data, but is also technically a memory leak. In case the deployment-scoped objects are large (which is the case in Quarkus, see `RunningQuarkusApplication`), this memory leak becomes very visible very soon. The Quarkus Arquillian adapter is only used to run TCKs for Jakarta and MicroProfile specifications, which don't need this capability. Also, the `RunningQuarkusApplication` is unusable after it is closed, so it makes no sense to keep it around. Hence, `QuarkusDeployableContainer` no longer stores the `RunningQuarkusApplication` into the deployment context directly; instead, it stores a holder object (`QuarkusDeployment`) that can safely be cleaned up after an Arquillian deployment is undeployed. That makes the Quarkus application unreachable and GC-able. Arquillian will keep piling up empty objects, which is still a memory leak, but one that doesn't hurt as much. This commit also fixes an ordering issue. Previously, the Arquillian adapter used to delete the Quarkus application directory _before_ shutting down the application, which could lead to errors. This commit switches the order: the app is shut down first and only then is it deleted. --- .../ClassLoaderExceptionTransformer.java | 4 +- .../quarkus/arquillian/InjectionEnricher.java | 11 ++- .../QuarkusBeforeAfterLifecycle.java | 8 +- .../QuarkusDeployableContainer.java | 80 +++++++++---------- .../quarkus/arquillian/QuarkusDeployment.java | 39 +++++++++ .../quarkus/arquillian/QuarkusProtocol.java | 4 +- .../arquillian/RequestContextLifecycle.java | 14 ++-- 7 files changed, 101 insertions(+), 59 deletions(-) create mode 100644 test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusDeployment.java diff --git a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/ClassLoaderExceptionTransformer.java b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/ClassLoaderExceptionTransformer.java index f9934b6b8db40..bd00146bebc55 100644 --- a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/ClassLoaderExceptionTransformer.java +++ b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/ClassLoaderExceptionTransformer.java @@ -16,7 +16,7 @@ public class ClassLoaderExceptionTransformer { @Inject @DeploymentScoped - Instance classLoaderInstance; + Instance deployment; @Inject Instance testResultInstance; @@ -29,7 +29,7 @@ public void transform(@Observes(precedence = -1000) Test event) { try { if (res.getClass().getClassLoader() != null && res.getClass().getClassLoader() != getClass().getClassLoader()) { - if (res.getClass() == classLoaderInstance.get().loadClass(res.getClass().getName())) { + if (res.getClass() == deployment.get().getAppClassLoader().loadClass(res.getClass().getName())) { ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(out); oo.writeObject(res); diff --git a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/InjectionEnricher.java b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/InjectionEnricher.java index 79c816d4c59f6..3044d4e6b6624 100644 --- a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/InjectionEnricher.java +++ b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/InjectionEnricher.java @@ -15,6 +15,7 @@ import jakarta.enterprise.inject.spi.BeanManager; import org.jboss.arquillian.container.spi.context.annotation.DeploymentScoped; +import org.jboss.arquillian.core.api.Instance; import org.jboss.arquillian.core.api.InstanceProducer; import org.jboss.arquillian.core.api.annotation.Inject; import org.jboss.arquillian.test.spi.TestEnricher; @@ -37,7 +38,7 @@ public class InjectionEnricher implements TestEnricher { @Inject @DeploymentScoped - private InstanceProducer appClassloader; + private Instance deployment; @Override public void enrich(Object testCase) { @@ -50,7 +51,9 @@ public Object[] resolve(Method method) { ClassLoader old = Thread.currentThread().getContextClassLoader(); try { CreationContextHolder holder = getCreationalContext(); - ClassLoader cl = appClassloader.get() != null ? appClassloader.get() : getClass().getClassLoader(); + ClassLoader cl = deployment.get() != null && deployment.get().hasAppClassLoader() + ? deployment.get().getAppClassLoader() + : getClass().getClassLoader(); Thread.currentThread().setContextClassLoader(cl); Class c = cl.loadClass(IsolatedEnricher.class.getName()); BiFunction function = (BiFunction) c @@ -67,7 +70,9 @@ public Object[] resolve(Method method) { private CreationContextHolder getCreationalContext() { try { - ClassLoader cl = appClassloader.get() != null ? appClassloader.get() : getClass().getClassLoader(); + ClassLoader cl = deployment.get() != null && deployment.get().hasAppClassLoader() + ? deployment.get().getAppClassLoader() + : getClass().getClassLoader(); Class c = cl.loadClass(IsolatedCreationContextCreator.class.getName()); Supplier> supplier = (Supplier>) c .getDeclaredConstructor().newInstance(); diff --git a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusBeforeAfterLifecycle.java b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusBeforeAfterLifecycle.java index 2a6343ff69bfc..647f8c5cd150d 100644 --- a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusBeforeAfterLifecycle.java +++ b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusBeforeAfterLifecycle.java @@ -5,7 +5,7 @@ import org.jboss.arquillian.container.spi.context.annotation.DeploymentScoped; import org.jboss.arquillian.container.test.api.RunAsClient; -import org.jboss.arquillian.core.api.InstanceProducer; +import org.jboss.arquillian.core.api.Instance; import org.jboss.arquillian.core.api.annotation.Inject; import org.jboss.arquillian.core.api.annotation.Observes; @@ -25,7 +25,7 @@ public class QuarkusBeforeAfterLifecycle { @Inject @DeploymentScoped - private InstanceProducer appClassloader; + private Instance deployment; public void on(@Observes(precedence = DEFAULT_PRECEDENCE) org.jboss.arquillian.test.spi.event.suite.Before event) throws Throwable { @@ -97,7 +97,9 @@ private void invokeCallbacks(String methodName, String junitOrTestNgCallbackClas throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException { ClassLoader old = Thread.currentThread().getContextClassLoader(); - ClassLoader cl = appClassloader.get() != null ? appClassloader.get() : old; + ClassLoader cl = deployment.get() != null && deployment.get().hasAppClassLoader() + ? deployment.get().getAppClassLoader() + : old; try { Thread.currentThread().setContextClassLoader(cl); diff --git a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusDeployableContainer.java b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusDeployableContainer.java index 2c5f3ebbb5aeb..4f41a5dd06c35 100644 --- a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusDeployableContainer.java +++ b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusDeployableContainer.java @@ -59,16 +59,12 @@ public class QuarkusDeployableContainer implements DeployableContainer runningApp; + private InstanceProducer deployment; @Inject @DeploymentScoped private InstanceProducer deploymentLocation; - @Inject - @DeploymentScoped - private InstanceProducer appClassloader; - @Inject private Instance testClass; @@ -191,8 +187,7 @@ public void execute(BuildContext context) { AugmentAction augmentAction = new AugmentActionImpl(curatedApplication, customizers); StartupAction app = augmentAction.createInitialRuntimeApplication(); RunningQuarkusApplication runningQuarkusApplication = app.run(); - appClassloader.set(runningQuarkusApplication.getClassLoader()); - runningApp.set(runningQuarkusApplication); + deployment.set(new QuarkusDeployment(runningQuarkusApplication)); Thread.currentThread().setContextClassLoader(runningQuarkusApplication.getClassLoader()); // Instantiate the real test instance testInstance = TestInstantiator.instantiateTest(testJavaClass, runningQuarkusApplication.getClassLoader()); @@ -241,7 +236,7 @@ public void execute(BuildContext context) { ProtocolMetaData metadata = new ProtocolMetaData(); //TODO: fix this - String testUri = TestHTTPResourceManager.getUri(runningApp.get()); + String testUri = TestHTTPResourceManager.getUri(deployment.get().getRunningApp()); System.setProperty("test.url", testUri); URI uri = URI.create(testUri); @@ -255,42 +250,9 @@ public void execute(BuildContext context) { @Override public void undeploy(Archive archive) throws DeploymentException { try { - RunningQuarkusApplication runner = runningApp.get(); - if (runner != null) { - Thread.currentThread().setContextClassLoader(runningApp.get().getClassLoader()); - } - testInstance = null; - Path location = deploymentLocation.get(); - if (location != null) { - try { - Files.walkFileTree(location, new FileVisitor() { - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Files.delete(file); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - Files.delete(dir); - return FileVisitResult.CONTINUE; - } - }); - } catch (IOException e) { - LOGGER.warn("Unable to delete the deployment dir: " + location, e); - } - } + RunningQuarkusApplication runner = deployment.get().getRunningApp(); if (runner != null) { + Thread.currentThread().setContextClassLoader(runner.getClassLoader()); try { runner.close(); } catch (Exception e) { @@ -300,6 +262,38 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx } finally { Thread.currentThread().setContextClassLoader(old); } + deployment.get().cleanup(); + + Path location = deploymentLocation.get(); + if (location != null) { + try { + Files.walkFileTree(location, new FileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + LOGGER.warn("Unable to delete the deployment dir: " + location, e); + } + } } @Override diff --git a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusDeployment.java b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusDeployment.java new file mode 100644 index 0000000000000..7b267da97d774 --- /dev/null +++ b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusDeployment.java @@ -0,0 +1,39 @@ +package io.quarkus.arquillian; + +import io.quarkus.bootstrap.app.RunningQuarkusApplication; + +/** + * This class is a workaround for Arquillian holding onto deployment-scoped (and container-scoped) + * objects until its end of life. That allows undeploying and redeploying without losing contextual + * data, but is also technically a memory leak. If the deployment-scoped objects are large (such as + * the {@code RunningQuarkusApplication}), this memory leak becomes very visible very soon. This + * Quarkus Arquillian adapter is only used to run TCKs for Jakarta and MicroProfile specifications, + * which don't need this capability. Also, the {@code RunningQuarkusApplication} is unusable after + * it is closed, so it makes no sense to keep it around. Hence, {@link QuarkusDeployableContainer} + * may safely {@link #cleanup()} this object after a deployment is undeployed, making the Quarkus + * application unreachable and GC-able. Arquillian will keep piling up empty objects, which is still + * a memory leak, but one that doesn't hurt as much. + */ +class QuarkusDeployment { + private RunningQuarkusApplication runningApp; + + QuarkusDeployment(RunningQuarkusApplication runningApp) { + this.runningApp = runningApp; + } + + RunningQuarkusApplication getRunningApp() { + return runningApp; + } + + ClassLoader getAppClassLoader() { + return runningApp.getClassLoader(); + } + + boolean hasAppClassLoader() { + return runningApp != null && runningApp.getClassLoader() != null; + } + + void cleanup() { + runningApp = null; + } +} diff --git a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusProtocol.java b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusProtocol.java index afd7b8381156c..17d238b8cc89b 100644 --- a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusProtocol.java +++ b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/QuarkusProtocol.java @@ -59,7 +59,7 @@ static class QuarkusMethodExecutor implements ContainerMethodExecutor { Instance testResult; @Inject - Instance classLoaderInstance; + Instance deployment; @Override public TestResult invoke(TestMethodExecutor testMethodExecutor) { @@ -70,7 +70,7 @@ public TestResult invoke(TestMethodExecutor testMethodExecutor) { public void invoke(Object... parameters) throws Throwable { ClassLoader loader = Thread.currentThread().getContextClassLoader(); try { - Thread.currentThread().setContextClassLoader(classLoaderInstance.get()); + Thread.currentThread().setContextClassLoader(deployment.get().getAppClassLoader()); Object actualTestInstance = QuarkusDeployableContainer.testInstance; Method actualMethod = null; diff --git a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/RequestContextLifecycle.java b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/RequestContextLifecycle.java index a3ebcea8ff28d..49f99b2604e14 100644 --- a/test-framework/arquillian/src/main/java/io/quarkus/arquillian/RequestContextLifecycle.java +++ b/test-framework/arquillian/src/main/java/io/quarkus/arquillian/RequestContextLifecycle.java @@ -1,7 +1,7 @@ package io.quarkus.arquillian; import org.jboss.arquillian.container.spi.context.annotation.DeploymentScoped; -import org.jboss.arquillian.core.api.InstanceProducer; +import org.jboss.arquillian.core.api.Instance; import org.jboss.arquillian.core.api.annotation.Inject; import org.jboss.arquillian.core.api.annotation.Observes; import org.jboss.arquillian.test.spi.event.suite.After; @@ -21,12 +21,13 @@ public class RequestContextLifecycle { @Inject @DeploymentScoped - private InstanceProducer appClassloader; + private Instance deployment; public void on(@Observes(precedence = DEFAULT_PRECEDENCE) Before event) throws Throwable { //we are outside the runtime class loader, so we don't have direct access to the container - ClassLoader classLoader = appClassloader.get(); - if (classLoader != null) { + QuarkusDeployment deployment = this.deployment.get(); + if (deployment != null && deployment.hasAppClassLoader()) { + ClassLoader classLoader = deployment.getAppClassLoader(); Class arcClz = classLoader.loadClass(Arc.class.getName()); Object container = arcClz.getMethod("container").invoke(null); if (container != null) { @@ -42,8 +43,9 @@ public void on(@Observes(precedence = DEFAULT_PRECEDENCE) Before event) throws T public void on(@Observes(precedence = DEFAULT_PRECEDENCE) After event) throws Throwable { //we are outside the runtime class loader, so we don't have direct access to the container - ClassLoader classLoader = appClassloader.get(); - if (classLoader != null) { + QuarkusDeployment deployment = this.deployment.get(); + if (deployment != null && deployment.hasAppClassLoader()) { + ClassLoader classLoader = deployment.getAppClassLoader(); Class arcClz = classLoader.loadClass(Arc.class.getName()); Object container = arcClz.getMethod("container").invoke(null); if (container != null) { From a6afaa4d49f3d812cee7267963c979ec604f9509 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 26 May 2023 13:07:02 +0200 Subject: [PATCH 6/8] Testing: improve instantiating test classes through CDI This commit moves the code that looks up a test class from CDI into a dedicated delegate class. This class is loaded from the Quarkus CL and so the code can be written directly instead of reflectively. Additionally, the CDI lookup can now correctly instantiate a generic test class. Such bean cannot be looked up from CDI directly, because its set of bean types doesn't contain a `Class` object (it contains a `ParameterizedType` instead). This commit uses a naive approach for this (iterate through all beans), which is fine, because it's very uncommon. --- .../arquillian/ArcDeployableContainer.java | 12 +++-- .../quarkus/test/common/TestInstantiator.java | 46 ++++++++++++------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/independent-projects/arc/tcks/arquillian/src/main/java/io/quarkus/arc/arquillian/ArcDeployableContainer.java b/independent-projects/arc/tcks/arquillian/src/main/java/io/quarkus/arc/arquillian/ArcDeployableContainer.java index 25a3ee26e9fc1..d011791f2282f 100644 --- a/independent-projects/arc/tcks/arquillian/src/main/java/io/quarkus/arc/arquillian/ArcDeployableContainer.java +++ b/independent-projects/arc/tcks/arquillian/src/main/java/io/quarkus/arc/arquillian/ArcDeployableContainer.java @@ -106,11 +106,13 @@ private Object findTest(ArcContainer arc, Class testClass) { return instance.get(); } - // fallback for generic test classes, whose set of bean types does not contain a `Class` - // but a `ParameterizedType` instead - for (InstanceHandle handle : arc.listAll(Object.class)) { - if (testClass.equals(handle.getBean().getBeanClass())) { - return handle.get(); + if (testClass.getTypeParameters().length > 0) { + // fallback for generic test classes, whose set of bean types does not contain a `Class` + // but a `ParameterizedType` instead + for (InstanceHandle handle : arc.listAll(Object.class)) { + if (testClass.equals(handle.getBean().getBeanClass())) { + return handle.get(); + } } } diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/TestInstantiator.java b/test-framework/common/src/main/java/io/quarkus/test/common/TestInstantiator.java index 59215a6250827..9351c1fbd86b7 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/TestInstantiator.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/TestInstantiator.java @@ -1,9 +1,11 @@ package io.quarkus.test.common; -import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.spi.CDI; + import org.jboss.logging.Logger; public class TestInstantiator { @@ -15,21 +17,10 @@ public static Object instantiateTest(Class testClass, ClassLoader classLoader try { Class actualTestClass = Class.forName(testClass.getName(), true, Thread.currentThread().getContextClassLoader()); - Class cdi = Thread.currentThread().getContextClassLoader().loadClass("jakarta.enterprise.inject.spi.CDI"); - Object instance = cdi.getMethod("current").invoke(null); - Method selectMethod = cdi.getMethod("select", Class.class, Annotation[].class); - Object cdiInstance = selectMethod.invoke(instance, actualTestClass, new Annotation[0]); - return selectMethod.getReturnType().getMethod("get").invoke(cdiInstance); - // BeanManager bm = CDI.current().getBeanManager(); - // Set> beans = bm.getBeans(testClass); - // Set> nonSubClasses = new HashSet<>(); - // for (Bean i : beans) { - // if (i.getBeanClass() == testClass) { - // nonSubClasses.add(i); - // } - // } - // Bean bean = bm.resolve(nonSubClasses); - // return bm.getReference(bean, testClass, bm.createCreationalContext(bean)); + Class delegate = Thread.currentThread().getContextClassLoader() + .loadClass("io.quarkus.test.common.TestInstantiator$Delegate"); + Method instantiate = delegate.getMethod("instantiate", Class.class); + return instantiate.invoke(null, actualTestClass); } catch (Exception e) { log.warn("Failed to initialize test as a CDI bean, falling back to direct initialization", e); try { @@ -41,4 +32,27 @@ public static Object instantiateTest(Class testClass, ClassLoader classLoader } } } + + // this class shall be loaded by the Quarkus CL + public static class Delegate { + public static Object instantiate(Class clazz) { + CDI cdi = CDI.current(); + Instance instance = cdi.select(clazz); + if (instance.isResolvable()) { + return instance.get(); + } + + if (clazz.getTypeParameters().length > 0) { + // fallback for generic test classes, whose set of bean types + // does not contain a `Class` but a `ParameterizedType` instead + for (Instance.Handle handle : cdi.select(Object.class).handles()) { + if (clazz.equals(handle.getBean().getBeanClass())) { + return handle.get(); + } + } + } + + throw new IllegalStateException("No bean: " + clazz); + } + } } From be2d247028028e5b83dfa347b2387e3d7f8071ac Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Mon, 29 May 2023 11:43:09 +0200 Subject: [PATCH 7/8] ArC: update to CDI TCK 4.0.10 --- independent-projects/arc/pom.xml | 2 +- .../src/test/resources/testng.xml | 79 +------------------ 2 files changed, 4 insertions(+), 77 deletions(-) diff --git a/independent-projects/arc/pom.xml b/independent-projects/arc/pom.xml index 720a01b64e47c..b21c8b33c0bdc 100644 --- a/independent-projects/arc/pom.xml +++ b/independent-projects/arc/pom.xml @@ -60,7 +60,7 @@ 1.7.0.Final 2.0.1 - 4.0.9 + 4.0.10 4.13.2 3.11.0 diff --git a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml b/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml index 87e459298438e..04d01134aff6c 100644 --- a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml +++ b/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml @@ -20,87 +20,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 109066f8068a6799ba42840a38483c285eec184c Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Mon, 29 May 2023 11:45:29 +0200 Subject: [PATCH 8/8] Add Jakarta TCKs - AtInject - CDI (Lite) - CDI Lang Model --- .../arc/tcks/cdi-tck-runner/pom.xml | 1 + .../resources/META-INF/cdi-tck.properties | 1 - tcks/jakarta-atinject/pom.xml | 88 +++++++++++++++++ .../tck/atinject/AtInjectTckExtension.java | 53 ++++++++++ .../io/quarkus/tck/atinject/AtInjectTest.java | 61 ++++++++++++ .../java/io/quarkus/tck/atinject/Spare.java | 11 +++ tcks/jakarta-cdi-lang-model/pom.xml | 80 +++++++++++++++ .../cdi/lang/model/LangModelTckExtension.java | 23 +++++ .../tck/cdi/lang/model/LangModelTest.java | 31 ++++++ tcks/jakarta-cdi/pom.xml | 99 +++++++++++++++++++ .../jakarta-cdi/src/test/resources/testng.xml | 33 +++++++ tcks/pom.xml | 3 + 12 files changed, 483 insertions(+), 1 deletion(-) delete mode 100644 independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/META-INF/cdi-tck.properties create mode 100644 tcks/jakarta-atinject/pom.xml create mode 100644 tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/AtInjectTckExtension.java create mode 100644 tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/AtInjectTest.java create mode 100644 tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/Spare.java create mode 100644 tcks/jakarta-cdi-lang-model/pom.xml create mode 100644 tcks/jakarta-cdi-lang-model/src/test/java/io/quarkus/tck/cdi/lang/model/LangModelTckExtension.java create mode 100644 tcks/jakarta-cdi-lang-model/src/test/java/io/quarkus/tck/cdi/lang/model/LangModelTest.java create mode 100644 tcks/jakarta-cdi/pom.xml create mode 100644 tcks/jakarta-cdi/src/test/resources/testng.xml diff --git a/independent-projects/arc/tcks/cdi-tck-runner/pom.xml b/independent-projects/arc/tcks/cdi-tck-runner/pom.xml index 9cf87fd791c06..ccbc8f951ca31 100644 --- a/independent-projects/arc/tcks/cdi-tck-runner/pom.xml +++ b/independent-projects/arc/tcks/cdi-tck-runner/pom.xml @@ -74,6 +74,7 @@ src/test/resources/testng.xml + true ${project.build.directory}/porting-pkg false diff --git a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/META-INF/cdi-tck.properties b/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/META-INF/cdi-tck.properties deleted file mode 100644 index c38de38628e08..0000000000000 --- a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/META-INF/cdi-tck.properties +++ /dev/null @@ -1 +0,0 @@ -org.jboss.cdi.tck.cdiLiteMode=true diff --git a/tcks/jakarta-atinject/pom.xml b/tcks/jakarta-atinject/pom.xml new file mode 100644 index 0000000000000..ae1bf0a694d24 --- /dev/null +++ b/tcks/jakarta-atinject/pom.xml @@ -0,0 +1,88 @@ + + + quarkus-tck-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-tck-jakarta-atinjecft + Quarkus - TCK - Jakarta AtInject + + + 2.0.1 + 4.13.2 + + + + + io.quarkus + quarkus-arquillian + + + io.quarkus + quarkus-arc + + + + jakarta.inject + jakarta.inject-tck + ${atinject-tck.version} + + + jakarta.inject + jakarta.inject-api + + + junit + junit + + + + + junit + junit + ${junit4.version} + + + org.jboss.arquillian.junit + arquillian-junit-container + + + + + io.quarkus + quarkus-arc-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + false + true + false + + false + + + + + + diff --git a/tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/AtInjectTckExtension.java b/tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/AtInjectTckExtension.java new file mode 100644 index 0000000000000..d0c7753c8983b --- /dev/null +++ b/tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/AtInjectTckExtension.java @@ -0,0 +1,53 @@ +package io.quarkus.tck.atinject; + +import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension; +import jakarta.enterprise.inject.build.compatible.spi.ClassConfig; +import jakarta.enterprise.inject.build.compatible.spi.Discovery; +import jakarta.enterprise.inject.build.compatible.spi.Enhancement; +import jakarta.enterprise.inject.build.compatible.spi.MetaAnnotations; +import jakarta.enterprise.inject.build.compatible.spi.ScannedClasses; +import jakarta.enterprise.inject.literal.NamedLiteral; + +import org.atinject.tck.auto.Convertible; +import org.atinject.tck.auto.Drivers; +import org.atinject.tck.auto.DriversSeat; +import org.atinject.tck.auto.FuelTank; +import org.atinject.tck.auto.Seat; +import org.atinject.tck.auto.Tire; +import org.atinject.tck.auto.V8Engine; +import org.atinject.tck.auto.accessories.Cupholder; +import org.atinject.tck.auto.accessories.SpareTire; + +public class AtInjectTckExtension implements BuildCompatibleExtension { + @Discovery + public void discovery(ScannedClasses scan, MetaAnnotations meta) { + scan.add(Convertible.class.getName()); + scan.add(DriversSeat.class.getName()); + scan.add(FuelTank.class.getName()); + scan.add(Seat.class.getName()); + scan.add(Tire.class.getName()); + scan.add(V8Engine.class.getName()); + + scan.add(Cupholder.class.getName()); + scan.add(SpareTire.class.getName()); + } + + @Enhancement(types = Convertible.class) + public void convertible(ClassConfig clazz) { + clazz.fields() + .stream() + .filter(it -> "spareTire".equals(it.info().name())) + .forEach(it -> it.addAnnotation(Spare.class)); + } + + @Enhancement(types = DriversSeat.class) + public void driversSeat(ClassConfig clazz) { + clazz.addAnnotation(Drivers.class); + } + + @Enhancement(types = SpareTire.class) + public void spareTire(ClassConfig clazz) { + clazz.addAnnotation(NamedLiteral.of("spare")) + .addAnnotation(Spare.class); + } +} diff --git a/tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/AtInjectTest.java b/tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/AtInjectTest.java new file mode 100644 index 0000000000000..5f9753f116646 --- /dev/null +++ b/tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/AtInjectTest.java @@ -0,0 +1,61 @@ +package io.quarkus.tck.atinject; + +import static org.junit.Assert.assertTrue; + +import java.util.Enumeration; + +import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension; + +import org.atinject.tck.Tck; +import org.atinject.tck.auto.Car; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +import io.quarkus.arc.Arc; + +@RunWith(Arquillian.class) +public class AtInjectTest { + @Deployment + public static Archive deployment() { + return ShrinkWrap.create(JavaArchive.class) + .addPackages(true, Tck.class.getPackage()) + .addClasses(AtInjectTest.class, AtInjectTckExtension.class, Spare.class) + .addAsServiceProvider(BuildCompatibleExtension.class, AtInjectTckExtension.class) + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @Test + public void test() { + Car instance = Arc.container().instance(Car.class).get(); + + junit.framework.Test test = Tck.testsFor(instance, /* supportsStatic */ false, /* supportsPrivate */ true); + junit.framework.TestResult result = new junit.framework.TestResult(); + test.run(result); + + // this is ugly and doesn't report failures properly, but I don't see a better way + if (!result.wasSuccessful()) { + int failuresCount = 0; + Enumeration failures = result.failures(); + while (failures.hasMoreElements()) { + System.out.println(failures.nextElement()); + failuresCount++; + } + + int errorsCount = 0; + Enumeration errors = result.errors(); + while (errors.hasMoreElements()) { + System.out.println(errors.nextElement()); + errorsCount++; + } + System.out.println("Total " + failuresCount + " failures and " + errorsCount + " errors"); + } + + assertTrue(result.wasSuccessful()); + } +} diff --git a/tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/Spare.java b/tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/Spare.java new file mode 100644 index 0000000000000..188f152fb441d --- /dev/null +++ b/tcks/jakarta-atinject/src/test/java/io/quarkus/tck/atinject/Spare.java @@ -0,0 +1,11 @@ +package io.quarkus.tck.atinject; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import jakarta.inject.Qualifier; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface Spare { +} diff --git a/tcks/jakarta-cdi-lang-model/pom.xml b/tcks/jakarta-cdi-lang-model/pom.xml new file mode 100644 index 0000000000000..ace4cae236926 --- /dev/null +++ b/tcks/jakarta-cdi-lang-model/pom.xml @@ -0,0 +1,80 @@ + + + quarkus-tck-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-tck-jakarta-cdi-lang-model + Quarkus - TCK - Jakarta CDI Lang Model + + + 4.0.10 + 4.13.2 + + + + + io.quarkus + quarkus-arquillian + + + io.quarkus + quarkus-arc + + + + jakarta.enterprise + cdi-tck-lang-model + ${cdi-tck.version} + + + junit + junit + ${junit4.version} + + + org.jboss.arquillian.junit + arquillian-junit-container + + + + + io.quarkus + quarkus-arc-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + false + true + false + + + false + + + + + + diff --git a/tcks/jakarta-cdi-lang-model/src/test/java/io/quarkus/tck/cdi/lang/model/LangModelTckExtension.java b/tcks/jakarta-cdi-lang-model/src/test/java/io/quarkus/tck/cdi/lang/model/LangModelTckExtension.java new file mode 100644 index 0000000000000..5861c91ec6742 --- /dev/null +++ b/tcks/jakarta-cdi-lang-model/src/test/java/io/quarkus/tck/cdi/lang/model/LangModelTckExtension.java @@ -0,0 +1,23 @@ +package io.quarkus.tck.cdi.lang.model; + +import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension; +import jakarta.enterprise.inject.build.compatible.spi.Discovery; +import jakarta.enterprise.inject.build.compatible.spi.Enhancement; +import jakarta.enterprise.inject.build.compatible.spi.ScannedClasses; +import jakarta.enterprise.lang.model.declarations.ClassInfo; + +import org.jboss.cdi.lang.model.tck.LangModelVerifier; + +public class LangModelTckExtension implements BuildCompatibleExtension { + @Discovery + public void addClass(ScannedClasses scan) { + // `LangModelVerifier` has no bean defining annotation + // and isn't discovered in annotated discovery + scan.add(LangModelVerifier.class.getName()); + } + + @Enhancement(types = LangModelVerifier.class) + public void run(ClassInfo clazz) { + LangModelVerifier.verify(clazz); + } +} diff --git a/tcks/jakarta-cdi-lang-model/src/test/java/io/quarkus/tck/cdi/lang/model/LangModelTest.java b/tcks/jakarta-cdi-lang-model/src/test/java/io/quarkus/tck/cdi/lang/model/LangModelTest.java new file mode 100644 index 0000000000000..14c8c8a831079 --- /dev/null +++ b/tcks/jakarta-cdi-lang-model/src/test/java/io/quarkus/tck/cdi/lang/model/LangModelTest.java @@ -0,0 +1,31 @@ +package io.quarkus.tck.cdi.lang.model; + +import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.cdi.lang.model.tck.LangModelVerifier; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(Arquillian.class) +public class LangModelTest { + @Deployment + public static Archive deployment() { + return ShrinkWrap.create(JavaArchive.class) + .addPackage(LangModelVerifier.class.getPackage()) + .addClasses(LangModelTest.class, LangModelTckExtension.class) + .addAsServiceProvider(BuildCompatibleExtension.class, LangModelTckExtension.class) + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @Test + public void test() { + // the test itself runs in LangModelTckExtension + // and if it fails, deployment fails + } +} diff --git a/tcks/jakarta-cdi/pom.xml b/tcks/jakarta-cdi/pom.xml new file mode 100644 index 0000000000000..db576b37d02c9 --- /dev/null +++ b/tcks/jakarta-cdi/pom.xml @@ -0,0 +1,99 @@ + + + quarkus-tck-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-tck-jakarta-cdi + Quarkus - TCK - Jakarta CDI + + + 4.0.10 + + + + + io.quarkus + quarkus-arquillian + + + io.quarkus + quarkus-arc + + + io.quarkus.arc + arc-cdi-tck-porting-pkg + ${project.version} + + + jakarta.enterprise + cdi-tck-core-impl + ${cdi-tck.version} + + + + + io.quarkus + quarkus-arc-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-porting-pkg + generate-test-resources + + copy-dependencies + + + io.quarkus.arc + arc-cdi-tck-porting-pkg + ${project.build.directory}/porting-pkg + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + true + ${project.build.directory}/porting-pkg + false + true + false + + + jakarta.enterprise:cdi-tck-core-impl + + + src/test/resources/testng.xml + + false + + + + + + diff --git a/tcks/jakarta-cdi/src/test/resources/testng.xml b/tcks/jakarta-cdi/src/test/resources/testng.xml new file mode 100644 index 0000000000000..04d01134aff6c --- /dev/null +++ b/tcks/jakarta-cdi/src/test/resources/testng.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tcks/pom.xml b/tcks/pom.xml index 37c78b0af8dc2..cab353810baae 100644 --- a/tcks/pom.xml +++ b/tcks/pom.xml @@ -97,6 +97,9 @@ + jakarta-atinject + jakarta-cdi + jakarta-cdi-lang-model microprofile-config microprofile-context-propagation microprofile-fault-tolerance