From 9b7a8fee95806309ee30043223bac13c41fc4ed1 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Tue, 8 Aug 2023 14:34:16 +0200 Subject: [PATCH] QuarkusComponentTest: another fix for continuous testing integration - it seems that classloading in the continuous testing environment has changed recently; this commit should fix the problem - also add a test so that we can catch similar problems in the CI --- integration-tests/devmode/pom.xml | 5 +++ .../ComponentContinuousTestingTest.java | 41 +++++++++++++++++++ .../quarkus/test/component/ComponentFoo.java | 17 ++++++++ .../quarkus/test/component/ComponentUT.java | 21 ++++++++++ .../QuarkusComponentTestExtension.java | 28 ++++++++----- 5 files changed, 102 insertions(+), 10 deletions(-) create mode 100644 integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentContinuousTestingTest.java create mode 100644 integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentFoo.java create mode 100644 integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentUT.java diff --git a/integration-tests/devmode/pom.xml b/integration-tests/devmode/pom.xml index f149a3b7510d3..61ff9b3365fc3 100644 --- a/integration-tests/devmode/pom.xml +++ b/integration-tests/devmode/pom.xml @@ -84,6 +84,11 @@ quarkus-junit5-internal test + + io.quarkus + quarkus-junit5-component + test + io.rest-assured rest-assured diff --git a/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentContinuousTestingTest.java b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentContinuousTestingTest.java new file mode 100644 index 0000000000000..7bbc7ccb4698a --- /dev/null +++ b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentContinuousTestingTest.java @@ -0,0 +1,41 @@ +package io.quarkus.test.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; +import io.quarkus.test.QuarkusDevModeTest; + +public class ComponentContinuousTestingTest { + + @RegisterExtension + static final QuarkusDevModeTest config = new QuarkusDevModeTest() + .withApplicationRoot( + root -> root.addClass(ComponentFoo.class).add(new StringAsset(ContinuousTestingTestUtils.appProperties()), + "application.properties")) + .setTestArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClass(ComponentUT.class)); + + @Test + public void test() { + ContinuousTestingTestUtils utils = new ContinuousTestingTestUtils(); + TestStatus ts = utils.waitForNextCompletion(); + assertEquals(0L, ts.getTestsFailed()); + assertEquals(1L, ts.getTestsPassed()); + assertEquals(0L, ts.getTestsSkipped()); + + config.modifySourceFile(ComponentFoo.class, s -> s.replace("return bar;", "return bar + bar;")); + + ts = utils.waitForNextCompletion(); + assertEquals(1L, ts.getTestsFailed()); + assertEquals(0L, ts.getTestsPassed()); + assertEquals(0L, ts.getTestsSkipped()); + } + +} diff --git a/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentFoo.java b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentFoo.java new file mode 100644 index 0000000000000..5b12c6a524fe3 --- /dev/null +++ b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentFoo.java @@ -0,0 +1,17 @@ +package io.quarkus.test.component; + +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +@Singleton +public class ComponentFoo { + + @ConfigProperty(name = "bar", defaultValue = "baz") + String bar; + + String ping() { + return bar; + } + +} diff --git a/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentUT.java b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentUT.java new file mode 100644 index 0000000000000..d6a6e4302ed5d --- /dev/null +++ b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentUT.java @@ -0,0 +1,21 @@ +package io.quarkus.test.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; + +@QuarkusComponentTest +@TestConfigProperty(key = "bar", value = "qux") +public class ComponentUT { + + @Inject + ComponentFoo foo; + + @Test + public void test() { + assertEquals("qux", foo.ping()); + } + +} diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java index a5eaebcb5c164..00960605b882a 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java @@ -505,6 +505,9 @@ private ClassLoader initArcContainer(ExtensionContext extensionContext, Collecti throw new IllegalStateException("Failed to create index", e); } + ClassLoader testClassClassLoader = testClass.getClassLoader(); + // The test class is loaded by the QuarkusClassLoader in continuous testing environment + boolean isContinuousTesting = testClassClassLoader instanceof QuarkusClassLoader; ClassLoader oldTccl = Thread.currentThread().getContextClassLoader(); IndexView computingIndex = BeanArchives.buildComputingBeanArchiveIndex(oldTccl, @@ -541,11 +544,13 @@ private ClassLoader initArcContainer(ExtensionContext extensionContext, Collecti // We need collect all generated resources so that we can remove them after the test // NOTE: previously we kept the generated framework classes (to speedup subsequent test runs) but that breaks the existing @QuarkusTests - Set generatedResources = new HashSet<>(); + Set generatedResources; + // E.g. target/generated-arc-sources/org/acme/ComponentsProvider File componentsProviderFile = getComponentsProviderFile(testClass); - if (testClass.getClassLoader() instanceof QuarkusClassLoader) { - //continuous testing environment + + if (isContinuousTesting) { + generatedResources = Set.of(); Map classes = new HashMap<>(); builder.setOutput(new ResourceOutput() { @Override @@ -566,11 +571,12 @@ public void writeResource(Resource resource) throws IOException { } break; default: - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Unsupported resource type: " + resource.getType()); } } }); } else { + generatedResources = new HashSet<>(); File testOutputDirectory = getTestOutputDirectory(testClass); builder.setOutput(new ResourceOutput() { @Override @@ -590,7 +596,7 @@ public void writeResource(Resource resource) throws IOException { } break; default: - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Unsupported resource type: " + resource.getType()); } } }); @@ -748,7 +754,9 @@ public void accept(BytecodeTransformer transformer) { } // Use a custom ClassLoader to load the generated ComponentsProvider file - QuarkusComponentTestClassLoader testClassLoader = new QuarkusComponentTestClassLoader(oldTccl, + // In continuous testing the CL that loaded the test class must be used as the parent CL + QuarkusComponentTestClassLoader testClassLoader = new QuarkusComponentTestClassLoader( + isContinuousTesting ? testClassClassLoader : oldTccl, componentsProviderFile, null); Thread.currentThread().setContextClassLoader(testClassLoader); @@ -1077,18 +1085,18 @@ private File getComponentsProviderFile(Class testClass) { File targetDir = new File("target"); if (targetDir.canWrite()) { // maven build - generatedSourcesDirectory = new File("target/generated-arc-sources"); + generatedSourcesDirectory = new File(targetDir, "generated-arc-sources"); } else { File buildDir = new File("build"); if (buildDir.canWrite()) { // gradle build - generatedSourcesDirectory = new File("build/generated-arc-sources"); + generatedSourcesDirectory = new File(buildDir, "generated-arc-sources"); } else { generatedSourcesDirectory = new File("quarkus-component-test/generated-arc-sources"); } } - return new File(generatedSourcesDirectory, - nameToPath(testClass.getPackage().getName()) + File.pathSeparator + ComponentsProvider.class.getSimpleName()); + return new File(new File(generatedSourcesDirectory, nameToPath(testClass.getPackage().getName())), + ComponentsProvider.class.getSimpleName()); } }