From 8c8eac3ec3552563a65c45fc31e4d933aaf81bd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 6 Sep 2024 12:21:15 +0200 Subject: [PATCH] Add .debugBytecode() and .traceCategories() to QuarkusUnitTest To easy debugging of such tests. --- .../PublicFieldAccessInheritanceTest.java | 32 ++------- .../java/io/quarkus/test/QuarkusUnitTest.java | 69 +++++++++++++++++++ 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/applicationfieldaccess/PublicFieldAccessInheritanceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/applicationfieldaccess/PublicFieldAccessInheritanceTest.java index 030061fe276bb8..8497a301c518d6 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/applicationfieldaccess/PublicFieldAccessInheritanceTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/applicationfieldaccess/PublicFieldAccessInheritanceTest.java @@ -2,9 +2,6 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.util.Map; -import java.util.UUID; - import jakarta.inject.Inject; import jakarta.persistence.Entity; import jakarta.persistence.EntityManager; @@ -30,36 +27,19 @@ */ public class PublicFieldAccessInheritanceTest { - // FIXME Temporary debug options for https://github.com/quarkusio/quarkus/issues/42479 - // Needs to be set very early (e.g. as system properties) in order to affect the build; - // see https://quarkusio.zulipchat.com/#narrow/stream/187038-dev/topic/Build.20logs - private static final Map DEBUG_PROPERTIES = Map.of( - "quarkus.hibernate-orm.log.sql", "true", - "quarkus.hibernate-orm.log.bind-parameters", "true", - "quarkus.log.category.\"org.hibernate\".min-level", "TRACE", - "quarkus.log.category.\"org.hibernate\".level", "TRACE", - "quarkus.log.category.\"io.quarkus.hibernate\".min-level", "TRACE", - "quarkus.log.category.\"io.quarkus.hibernate\".level", "TRACE", - "quarkus.log.category.\"io.quarkus.panache\".min-level", "TRACE", - "quarkus.log.category.\"io.quarkus.panache\".level", "TRACE", - "quarkus.debug.transformed-classes-dir", "target/debug/${testRunId}/transformed-classes", - "quarkus.debug.generated-classes-dir", "target/debug/${testRunId}/generated-classes"); - @RegisterExtension static QuarkusUnitTest runner = new QuarkusUnitTest() - .setBeforeAllCustomizer(() -> { - // Used to differentiate reruns of flaky tests in Maven - var testRunId = PublicFieldAccessInheritanceTest.class + "/" + UUID.randomUUID(); - System.out.println("Test run ID: " + testRunId); - DEBUG_PROPERTIES.forEach((key, value) -> System.setProperty(key, value.replace("${testRunId}", testRunId))); - }) - .setAfterAllCustomizer(() -> DEBUG_PROPERTIES.keySet().forEach(System::clearProperty)) .withApplicationRoot((jar) -> jar .addClass(MyMappedSuperclass.class) .addClass(MyAbstractEntity.class) .addClass(MyConcreteEntity.class) .addClass(FieldAccessEnhancedDelegate.class)) - .withConfigurationResource("application.properties"); + .withConfigurationResource("application.properties") + // FIXME Temporary debug options for https://github.com/quarkusio/quarkus/issues/42479 + .overrideConfigKey("quarkus.hibernate-orm.log.sql", "true") + .overrideConfigKey("quarkus.hibernate-orm.log.bind-parameters", "true") + .debugBytecode(true) + .traceCategories("org.hibernate", "io.quarkus.hibernate", "io.quarkus.panache"); @Inject EntityManager em; diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java index 3e0b9bb4d57470..59190ef5b4f370 100644 --- a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java @@ -27,6 +27,7 @@ import java.util.ServiceLoader; import java.util.Timer; import java.util.TimerTask; +import java.util.UUID; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; @@ -137,6 +138,10 @@ public class QuarkusUnitTest private List> bootstrapCustomizers = new ArrayList<>(); + private boolean debugBytecode = false; + private List traceCategories = new ArrayList<>(); + private Map systemPropertiesToRestore = new HashMap<>(); + public QuarkusUnitTest setExpectedException(Class expectedException) { return setExpectedException(expectedException, false); } @@ -514,6 +519,26 @@ public void beforeAll(ExtensionContext extensionContext) throws Exception { if (beforeAllCustomizer != null) { beforeAllCustomizer.run(); } + if (debugBytecode) { + // Use a unique ID to avoid overriding dumps between test classes (and re-execution of flaky tests). + var testRunId = extensionContext.getRequiredTestClass().getName() + "/" + UUID.randomUUID(); + System.out.println("[QuarkusUnitTest] Debug dumps enabled. Test run ID: " + testRunId); + // This needs to be set as system properties; see BootstrapDebug.java. + // Note these paths are considered standard and may be taken advantage of in Quarkus CI (to collect dumps). + overrideSystemProperty("quarkus.debug.transformed-classes-dir", + "target/debug/" + testRunId + "/transformed-classes"); + overrideSystemProperty("quarkus.debug.generated-classes-dir", "target/debug/" + testRunId + "/generated-classes"); + overrideSystemProperty("quarkus.debug.generated-sources-dir", "target/debug/" + testRunId + "/generated-sources"); + } + if (traceCategories != null) { + System.out.println("[QuarkusUnitTest] Trace logs enabled for the following categories: " + traceCategories); + // This needs to be set very early (e.g. as system properties) in order to affect the build too; + // see https://quarkusio.zulipchat.com/#narrow/stream/187038-dev/topic/Build.20logs + for (String traceCategory : traceCategories) { + overrideSystemProperty("quarkus.log.category.\"" + traceCategory + "\".min-level", "TRACE"); + overrideSystemProperty("quarkus.log.category.\"" + traceCategory + "\".level", "TRACE"); + } + } originalClassLoader = Thread.currentThread().getContextClassLoader(); originalHandlers = rootLogger.getHandlers(); rootLogger.addHandler(inMemoryLogHandler); @@ -718,6 +743,13 @@ private Throwable unwrapException(Throwable cause) { return cause; } + private void overrideSystemProperty(String key, String value) { + // IMPORTANT: Not logging the value in case it's a secret. + System.out.println("[QuarkusUnitTest] Overriding system property '" + key + "'"); + systemPropertiesToRestore.putIfAbsent(key, System.getProperty(key)); + System.setProperty(key, value); + } + @Override public void afterAll(ExtensionContext extensionContext) throws Exception { actualTestClass = null; @@ -764,6 +796,13 @@ public void afterAll(ExtensionContext extensionContext) throws Exception { if (afterAllCustomizer != null) { afterAllCustomizer.run(); } + systemPropertiesToRestore.forEach((key, previousValue) -> { + if (previousValue == null) { + System.clearProperty(key); + } else { + System.setProperty(key, previousValue); + } + }); ClearCache.clearCaches(); TestConfigUtil.cleanUp(); } @@ -850,6 +889,36 @@ public QuarkusUnitTest overrideRuntimeConfigKey(final String propertyKey, final return this; } + /** + * Controls bytecode-related debug dumping. + *

+ * When enabled, each Quarkus startup will have configuration properties + * such as {@code quarkus.debug.generated-classes-dir} set + * so that generated code gets dumped in {@code target/debug}, + * within a unique subdirectory for each test execution. + *

+ * Look at the logs of a particular test to identify the corresponding dump directory. + * + * @param debugBytecode {@code true} if debug should be enabled + * @return {@code this}, for method chaining. + */ + public QuarkusUnitTest debugBytecode(boolean debugBytecode) { + this.debugBytecode = debugBytecode; + return this; + } + + /** + * Enables trace logs for the given categories, + * during both build and runtime. + * + * @param categories The categories for which to enable trace logging. + * @return {@code this}, for method chaining. + */ + public QuarkusUnitTest traceCategories(String... categories) { + Collections.addAll(this.traceCategories, categories); + return this; + } + @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {