From c272ff09dae73a061402a453ac77e2c5c7fd7d1c Mon Sep 17 00:00:00 2001 From: Falko Modler Date: Sat, 15 Jan 2022 17:54:15 +0100 Subject: [PATCH] Make sure (basic) logging is set up before each test class --- .../main/resources/junit-platform.properties | 3 +- .../test/junit/BasicLoggingEnabler.java | 54 ++++++++++++++++ .../QuarkusTestProfileAwareClassOrderer.java | 61 ++++++++++++++----- .../org.junit.jupiter.api.extension.Extension | 1 + ...arkusTestProfileAwareClassOrdererTest.java | 18 ++++++ 5 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 test-framework/junit5/src/main/java/io/quarkus/test/junit/BasicLoggingEnabler.java create mode 100644 test-framework/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension diff --git a/test-framework/junit5-properties/src/main/resources/junit-platform.properties b/test-framework/junit5-properties/src/main/resources/junit-platform.properties index 0823c6b870646..cdac134076ffb 100644 --- a/test-framework/junit5-properties/src/main/resources/junit-platform.properties +++ b/test-framework/junit5-properties/src/main/resources/junit-platform.properties @@ -1 +1,2 @@ -junit.jupiter.testclass.order.default=io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer \ No newline at end of file +junit.jupiter.extensions.autodetection.enabled=true +junit.jupiter.testclass.order.default=io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/BasicLoggingEnabler.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/BasicLoggingEnabler.java new file mode 100644 index 0000000000000..855de40c638b5 --- /dev/null +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/BasicLoggingEnabler.java @@ -0,0 +1,54 @@ +package io.quarkus.test.junit; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import io.quarkus.bootstrap.logging.InitialConfigurator; + +/** + * A (global) JUnit callback that enables/sets up basic logging if logging has not already been set up. + *

+ * This is useful for getting log output from non-Quarkus tests (if executed separately or before the first Quarkus test), + * but also for getting instant log output from {@code QuarkusTestResourceLifecycleManagers} etc. + *

+ * This callback can be disabled via {@link #CFGKEY_ENABLED} in {@code junit-platform.properties} or via system property. + */ +public class BasicLoggingEnabler implements BeforeAllCallback { + + private static final String CFGKEY_ENABLED = "junit.quarkus.enable-basic-logging"; + private static Boolean enabled; + + // to speed things up a little, eager async loading of the config that will be looked up in LoggingSetupRecorder + // downside: doesn't obey CFGKEY_ENABLED, but that should be bearable + static { + // e.g. continuous testing has everything set up already (DELAYED_HANDLER is active) + if (!InitialConfigurator.DELAYED_HANDLER.isActivated()) { + new Thread(() -> ConfigProvider.getConfig()).start(); + } + } + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + if (enabled == null) { + enabled = context.getConfigurationParameter(CFGKEY_ENABLED).map(Boolean::valueOf).orElse(Boolean.TRUE); + } + if (!enabled || InitialConfigurator.DELAYED_HANDLER.isActivated()) { + return; + } + try { + IntegrationTestUtil.activateLogging(); + } finally { + // release the config that was retrieved by above call so that tests that try to register their own config + // don't fail with: + // "IllegalStateException: SRCFG00017: Configuration already registered for the given class loader" + // also, a possible recreation of basically the same config for a later test class will consume far less time + var configProviderResolver = ConfigProviderResolver.instance(); + var config = configProviderResolver.getConfig(); + if (config != null) { // probably never null, but be safe + configProviderResolver.releaseConfig(config); + } + } + } +} diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java index 5d9c451f8908a..1fbc22514dc43 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java @@ -55,10 +55,22 @@ public class QuarkusTestProfileAwareClassOrderer implements ClassOrderer { protected static final String DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES = "45_"; protected static final String DEFAULT_ORDER_PREFIX_NON_QUARKUS_TEST = "60_"; - static final String CFGKEY_ORDER_PREFIX_QUARKUS_TEST = "quarkus.test.orderer.prefix.quarkus-test"; - static final String CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE = "quarkus.test.orderer.prefix.quarkus-test-with-profile"; + static final String CFGKEY_ORDER_PREFIX_QUARKUS_TEST = "junit.quarkus.orderer.prefix.quarkus-test"; + @Deprecated + static final String _CFGKEY_ORDER_PREFIX_QUARKUS_TEST = "quarkus.test.orderer.prefix.quarkus-test"; + + static final String CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE = "junit.quarkus.orderer.prefix.quarkus-test-with-profile"; + @Deprecated + static final String _CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE = "quarkus.test.orderer.prefix.quarkus-test-with-profile"; + static final String CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES = "quarkus.test.orderer.prefix.quarkus-test-with-restricted-resource"; + @Deprecated + static final String _CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES = "junit.quarkus.orderer.prefix.quarkus-test-with-restricted-resource"; + static final String CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST = "quarkus.test.orderer.prefix.non-quarkus-test"; + @Deprecated + static final String _CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST = "junit.quarkus.orderer.prefix.non-quarkus-test"; + static final String CFGKEY_SECONDARY_ORDERER = "quarkus.test.orderer.secondary-orderer"; @Override @@ -67,18 +79,26 @@ public void orderClasses(ClassOrdererContext context) { if (context.getClassDescriptors().size() <= 1 || context.getClassDescriptors().get(0).isAnnotated(Nested.class)) { return; } - var prefixQuarkusTest = context - .getConfigurationParameter(CFGKEY_ORDER_PREFIX_QUARKUS_TEST) - .orElse(DEFAULT_ORDER_PREFIX_QUARKUS_TEST); - var prefixQuarkusTestWithProfile = context - .getConfigurationParameter(CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE) - .orElse(DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE); - var prefixQuarkusTestWithRestrictedResource = context - .getConfigurationParameter(CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES) - .orElse(DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES); - var prefixNonQuarkusTest = context - .getConfigurationParameter(CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST) - .orElse(DEFAULT_ORDER_PREFIX_NON_QUARKUS_TEST); + var prefixQuarkusTest = getConfigParam( + CFGKEY_ORDER_PREFIX_QUARKUS_TEST, + _CFGKEY_ORDER_PREFIX_QUARKUS_TEST, + DEFAULT_ORDER_PREFIX_QUARKUS_TEST, + context); + var prefixQuarkusTestWithProfile = getConfigParam( + CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE, + _CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE, + DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE, + context); + var prefixQuarkusTestWithRestrictedResource = getConfigParam( + CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES, + _CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES, + DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES, + context); + var prefixNonQuarkusTest = getConfigParam( + CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST, + _CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST, + DEFAULT_ORDER_PREFIX_NON_QUARKUS_TEST, + context); // first pass: run secondary orderer first (!), which is easier than running it per "grouping" buildSecondaryOrderer(context).orderClasses(context); @@ -111,6 +131,19 @@ public void orderClasses(ClassOrdererContext context) { })); } + private String getConfigParam(String key, String deprecatedKey, String fallbackValue, ClassOrdererContext context) { + return context.getConfigurationParameter(key) + .orElseGet(() -> { + Optional value = context.getConfigurationParameter(deprecatedKey); + if (value.isPresent()) { + System.out.printf("Config key %s is deprecated, please use %s instead.%n", deprecatedKey, key); + return value.orElseThrow(); + } else { + return fallbackValue; + } + }); + } + private ClassOrderer buildSecondaryOrderer(ClassOrdererContext context) { return context.getConfigurationParameter(CFGKEY_SECONDARY_ORDERER) .map(fqcn -> { diff --git a/test-framework/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/test-framework/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 0000000000000..3d466fc6d2db5 --- /dev/null +++ b/test-framework/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +io.quarkus.test.junit.BasicLoggingEnabler diff --git a/test-framework/junit5/src/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java b/test-framework/junit5/src/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java index 8c6303f1d7b83..d20f14e8132e4 100644 --- a/test-framework/junit5/src/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java +++ b/test-framework/junit5/src/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java @@ -2,6 +2,7 @@ import static io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer.CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST; import static io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer.CFGKEY_SECONDARY_ORDERER; +import static io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer._CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; @@ -104,6 +105,23 @@ void configuredPrefix() { assertThat(input).containsExactly(nonQuarkusTestDesc, quarkusTestDesc); } + @Test + @Deprecated + void configuredPrefix_deprecated() { + ClassDescriptor quarkusTestDesc = quarkusDescriptorMock(Test01.class, null); + ClassDescriptor nonQuarkusTestDesc = descriptorMock(Test03.class); + List input = Arrays.asList(quarkusTestDesc, nonQuarkusTestDesc); + doReturn(input).when(contextMock).getClassDescriptors(); + + when(contextMock.getConfigurationParameter(anyString())).thenReturn(Optional.empty()); // for strict stubbing + // prioritize unit tests + when(contextMock.getConfigurationParameter(_CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST)).thenReturn(Optional.of("01_")); + + underTest.orderClasses(contextMock); + + assertThat(input).containsExactly(nonQuarkusTestDesc, quarkusTestDesc); + } + @Test void secondaryOrderer() { ClassDescriptor quarkusTest1Desc = quarkusDescriptorMock(Test01.class, null);