This is an automated email from the ASF dual-hosted git repository. pkarwasz pushed a commit to branch parallel-tests in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 0ae3d8a08fb38a5b974da7341c1aa84f6d871eb0 Author: Piotr P. Karwasz <[email protected]> AuthorDate: Sun Jul 17 12:04:39 2022 +0200 Add JUnitContextSelector Adds a JUnitContextSelector to provide per-test logger contexts and enables it for JUnit 5 Core tests. --- log4j-api-test/pom.xml | 2 +- log4j-core-test/pom.xml | 43 +++++++++++- .../core/test/junit/JUnitContextSelector.java | 73 +++++++++++++++++++ .../core/test/junit/LoggerContextResolver.java | 82 +++++++++------------- .../log4j/core/test/junit/LoggerContextSource.java | 6 +- pom.xml | 2 +- 6 files changed, 153 insertions(+), 55 deletions(-) diff --git a/log4j-api-test/pom.xml b/log4j-api-test/pom.xml index de7d3667a6..4b7db049be 100644 --- a/log4j-api-test/pom.xml +++ b/log4j-api-test/pom.xml @@ -144,7 +144,7 @@ <junit.jupiter.execution.parallel.enabled>true</junit.jupiter.execution.parallel.enabled> <junit.jupiter.execution.parallel.mode.default>same_thread</junit.jupiter.execution.parallel.mode.default> <junit.jupiter.execution.parallel.mode.classes.default>concurrent</junit.jupiter.execution.parallel.mode.classes.default> - <!-- Enables the `ExtensionContextAnchor` on each test --> + <!-- Enables the `ExtensionContextAnchor` and `StatusLoggerExtention` on each test --> <junit.jupiter.extensions.autodetection.enabled>true</junit.jupiter.extensions.autodetection.enabled> <junit.jupiter.testclass.order.default>org.junit.jupiter.api.ClassOrderer$Random</junit.jupiter.testclass.order.default> <log4j2.junit.disableConsoleStatusListener>true</log4j2.junit.disableConsoleStatusListener> diff --git a/log4j-core-test/pom.xml b/log4j-core-test/pom.xml index 1166236bef..775d6e9927 100644 --- a/log4j-core-test/pom.xml +++ b/log4j-core-test/pom.xml @@ -334,11 +334,52 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> + <runOrder>random</runOrder> <systemPropertyVariables> <log4j2.is.webapp>false</log4j2.is.webapp> </systemPropertyVariables> - <runOrder>random</runOrder> </configuration> + <executions> + <execution> + <id>default-test</id> + <phase>test</phase> + <goals> + <goal>test</goal> + </goals> + <configuration> + <systemPropertyVariables> + <!-- Enables parallel tests: + <junit.jupiter.execution.parallel.config.strategy>dynamic</junit.jupiter.execution.parallel.config.strategy> + <junit.jupiter.execution.parallel.config.dynamic.factor>1</junit.jupiter.execution.parallel.config.dynamic.factor> + <junit.jupiter.execution.parallel.enabled>true</junit.jupiter.execution.parallel.enabled> + <junit.jupiter.execution.parallel.mode.default>same_thread</junit.jupiter.execution.parallel.mode.default> + <junit.jupiter.execution.parallel.mode.classes.default>concurrent</junit.jupiter.execution.parallel.mode.classes.default> + <junit.jupiter.testclass.order.default>org.junit.jupiter.api.ClassOrderer$Random</junit.jupiter.testclass.order.default> + --> + <!-- Enables the `ExtensionContextAnchor` and `StatusLoggerExtention` on each test --> + <junit.jupiter.extensions.autodetection.enabled>true</junit.jupiter.extensions.autodetection.enabled> + <log4j2.contextSelector>org.apache.logging.log4j.core.test.junit.JUnitContextSelector</log4j2.contextSelector> + <log4j2.StatusLogger.level>DEBUG</log4j2.StatusLogger.level> + <log4j2.junit.disableConsoleStatusListener>true</log4j2.junit.disableConsoleStatusListener> + </systemPropertyVariables> + <runOrder>random</runOrder> + <forkCount>1</forkCount> + <reuseForks>true</reuseForks> + <redirectTestOutputToFile>false</redirectTestOutputToFile> + <includeJUnit5Engines>junit-jupiter</includeJUnit5Engines> + </configuration> + </execution> + <execution> + <id>junit4-test</id> + <phase>test</phase> + <goals> + <goal>test</goal> + </goals> + <configuration> + <excludeJUnit5Engines>junit-jupiter</excludeJUnit5Engines> + </configuration> + </execution> + </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> diff --git a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/JUnitContextSelector.java b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/JUnitContextSelector.java new file mode 100644 index 0000000000..0a12753830 --- /dev/null +++ b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/JUnitContextSelector.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.log4j.core.test.junit; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.net.URI; +import java.util.List; + +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.LoggerContextAccessor; +import org.apache.logging.log4j.core.impl.ContextAnchor; +import org.apache.logging.log4j.core.selector.ContextSelector; +import org.apache.logging.log4j.test.junit.ExtensionContextAnchor; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class JUnitContextSelector implements ContextSelector { + + static LoggerContext getContext(ExtensionContext context, URI configLocation) { + LoggerContextAccessor accessor = ExtensionContextAnchor.getAttribute(LoggerContextAccessor.class, + LoggerContextAccessor.class, context); + assertNotNull(accessor, "Missing LoggerContext. Check if the @LoggerContextSource annotation is present."); + return accessor.getLoggerContext(); + } + + @Override + public LoggerContext getContext(String fqcn, ClassLoader loader, boolean currentContext) { + return getContext(fqcn, loader, currentContext, null); + } + + @Override + public LoggerContext getContext(String fqcn, ClassLoader loader, boolean currentContext, URI configLocation) { + if (currentContext) { + final LoggerContext context = ContextAnchor.THREAD_CONTEXT.get(); + if (context != null) { + return context; + } + } + final LoggerContext context = getContext(null, configLocation); + if (context.getConfigLocation() == null && configLocation != null) { + context.setConfigLocation(configLocation); + } + return context; + } + + @Override + public List<LoggerContext> getLoggerContexts() { + LoggerContextAccessor accessor = ExtensionContextAnchor.getAttribute(LoggerContextAccessor.class, + LoggerContextAccessor.class, null); + return accessor != null ? List.of(accessor.getLoggerContext()) : List.of(); + } + + @Override + public void removeContext(LoggerContext context) { + ExtensionContextAnchor.removeAttribute(LoggerContext.class, null); + } + +} diff --git a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/LoggerContextResolver.java b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/LoggerContextResolver.java index b1d6072122..fa4e0cb54f 100644 --- a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/LoggerContextResolver.java +++ b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/LoggerContextResolver.java @@ -17,35 +17,32 @@ package org.apache.logging.log4j.core.test.junit; -import org.apache.logging.log4j.LogManager; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.net.URI; +import java.util.concurrent.TimeUnit; + import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.LoggerContextAccessor; import org.apache.logging.log4j.core.config.ConfigurationFactory; -import org.apache.logging.log4j.core.impl.Log4jContextFactory; +import org.apache.logging.log4j.core.impl.ContextAnchor; import org.apache.logging.log4j.core.util.NetUtils; import org.apache.logging.log4j.plugins.di.DI; import org.apache.logging.log4j.plugins.di.Injector; +import org.apache.logging.log4j.test.junit.ExtensionContextAnchor; +import org.apache.logging.log4j.test.junit.TestPropertySource; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.support.TypeBasedParameterResolver; import org.junit.platform.commons.util.AnnotationUtils; -import java.net.URI; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - class LoggerContextResolver extends TypeBasedParameterResolver<LoggerContext> implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback { - private static final String FQCN = LoggerContextResolver.class.getName(); - private static final Namespace BASE_NAMESPACE = Namespace.create(LoggerContext.class); @Override public void beforeAll(ExtensionContext context) throws Exception { @@ -58,14 +55,13 @@ class LoggerContextResolver extends TypeBasedParameterResolver<LoggerContext> im public void beforeEach(ExtensionContext context) throws Exception { final Class<?> testClass = context.getRequiredTestClass(); if (AnnotationUtils.isAnnotated(testClass, LoggerContextSource.class)) { - final Store testClassStore = context.getStore(BASE_NAMESPACE.append(testClass)); - final LoggerContextAccessor accessor = testClassStore.get(LoggerContextAccessor.class, LoggerContextAccessor.class); + final LoggerContextAccessor accessor = ExtensionContextAnchor.getAttribute(LoggerContextAccessor.class, LoggerContextAccessor.class, context); if (accessor == null) { throw new IllegalStateException( "Specified @LoggerContextSource but no LoggerContext found for test class " + testClass.getCanonicalName()); } - if (testClassStore.get(ReconfigurationPolicy.class, ReconfigurationPolicy.class) == ReconfigurationPolicy.BEFORE_EACH) { + if (ExtensionContextAnchor.getAttribute(ReconfigurationPolicy.class, ReconfigurationPolicy.class, context) == ReconfigurationPolicy.BEFORE_EACH) { accessor.getLoggerContext().reconfigure(); } } @@ -82,9 +78,11 @@ class LoggerContextResolver extends TypeBasedParameterResolver<LoggerContext> im public void afterEach(ExtensionContext context) throws Exception { final Class<?> testClass = context.getRequiredTestClass(); if (AnnotationUtils.isAnnotated(testClass, LoggerContextSource.class)) { - final Store testClassStore = getTestStore(context); - if (testClassStore.get(ReconfigurationPolicy.class, ReconfigurationPolicy.class) == ReconfigurationPolicy.AFTER_EACH) { - testClassStore.get(LoggerContextAccessor.class, LoggerContextAccessor.class).getLoggerContext().reconfigure(); + final ReconfigurationPolicy policy = ExtensionContextAnchor.getAttribute(ReconfigurationPolicy.class, + ReconfigurationPolicy.class, context); + if (policy == ReconfigurationPolicy.AFTER_EACH) { + ExtensionContextAnchor.getAttribute(LoggerContextAccessor.class, LoggerContextAccessor.class, context) + .getLoggerContext().reconfigure(); } } } @@ -96,43 +94,38 @@ class LoggerContextResolver extends TypeBasedParameterResolver<LoggerContext> im } static LoggerContext getLoggerContext(ExtensionContext context) { - final Store store = getTestStore(context); - final LoggerContextAccessor accessor = store.get(LoggerContextAccessor.class, LoggerContextAccessor.class); + final LoggerContextAccessor accessor = ExtensionContextAnchor.getAttribute(LoggerContextAccessor.class, + LoggerContextAccessor.class, context); assertNotNull(accessor); return accessor.getLoggerContext(); } - private static Store getTestStore(final ExtensionContext context) { - return context.getStore(BASE_NAMESPACE.append(context.getRequiredTestClass())); - } - - private static LoggerContext setUpLoggerContext(final LoggerContextSource source, final ExtensionContext extensionContext) { + private static LoggerContext setUpLoggerContext(final LoggerContextSource source, + final ExtensionContext extensionContext) { final String displayName = extensionContext.getDisplayName(); - final Injector injector = extensionContext.getTestInstance().map(DI::createInjector).orElseGet(DI::createInjector); + final Injector injector = extensionContext.getTestInstance().map(DI::createInjector) + .orElseGet(DI::createInjector); injector.init(); - final Log4jContextFactory loggerContextFactory; - if (source.bootstrap()) { - loggerContextFactory = new Log4jContextFactory(injector); - LogManager.setFactory(loggerContextFactory); - } else { - loggerContextFactory = (Log4jContextFactory) LogManager.getFactory(); - } - final Class<?> testClass = extensionContext.getRequiredTestClass(); - final ClassLoader classLoader = testClass.getClassLoader(); - final Map.Entry<String, Object> injectorContext = Map.entry(Injector.class.getName(), injector); final String configLocation = source.value(); final URI configUri; if (source.v1config()) { - System.setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, configLocation); + TestPropertySource.createProperties(extensionContext) + .setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, configLocation); configUri = null; // handled by system property } else { configUri = configLocation.isEmpty() ? null : NetUtils.toURI(configLocation); } - final LoggerContext context = loggerContextFactory.getContext(FQCN, classLoader, injectorContext, false, configUri, displayName); - assertNotNull(context, () -> "No LoggerContext created for " + testClass + " and config file " + configLocation); - final Store store = getTestStore(extensionContext); - store.put(ReconfigurationPolicy.class, source.reconfigure()); - store.put(LoggerContextAccessor.class, new ContextHolder(context, source.timeout(), source.unit())); + final LoggerContext context = new LoggerContext(displayName, extensionContext, configUri, injector); + context.putObject(Injector.class.getName(), injector); + ExtensionContextAnchor.setAttribute(ReconfigurationPolicy.class, source.reconfigure(), extensionContext); + ExtensionContextAnchor.setAttribute(LoggerContextAccessor.class, + new ContextHolder(context, source.timeout(), source.unit()), extensionContext); + ContextAnchor.THREAD_CONTEXT.set(context); + try { + context.start(); + } finally { + ContextAnchor.THREAD_CONTEXT.remove(); + } return context; } @@ -154,12 +147,7 @@ class LoggerContextResolver extends TypeBasedParameterResolver<LoggerContext> im @Override public void close() throws Throwable { - try { - context.stop(shutdownTimeout, unit); - } finally { - System.clearProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL); - System.clearProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY); - } + context.stop(shutdownTimeout, unit); } } diff --git a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/LoggerContextSource.java b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/LoggerContextSource.java index ccdc502764..4806b67181 100644 --- a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/LoggerContextSource.java +++ b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/LoggerContextSource.java @@ -66,7 +66,7 @@ public @interface LoggerContextSource { /** * Specifies the name of the configuration file to use for the annotated test. */ - String value(); + String value() default ""; /** * Specifies when to {@linkplain LoggerContext#reconfigure() reconfigure} the logging system. @@ -88,8 +88,4 @@ public @interface LoggerContextSource { */ boolean v1config() default false; - /** - * Determines whether to bootstrap a fresh LoggerContextFactory. - */ - boolean bootstrap() default false; } diff --git a/pom.xml b/pom.xml index 62d3d54e9e..6ef01d5b8f 100644 --- a/pom.xml +++ b/pom.xml @@ -243,7 +243,7 @@ <!-- surefire.plugin.version 2.18 yields http://jira.codehaus.org/browse/SUREFIRE-1121, which is fixed in 2.18.1 --> <!-- surefire.plugin.version 2.19 yields https://issues.apache.org/jira/browse/SUREFIRE-1193. --> <!-- all versions after 2.13 yield https://issues.apache.org/jira/browse/SUREFIRE-720 --> - <surefire.plugin.version>3.0.0-M5</surefire.plugin.version> + <surefire.plugin.version>3.0.0-M7</surefire.plugin.version> <failsafe.plugin.version>2.22.2</failsafe.plugin.version> <checkstyle.plugin.version>3.0.0</checkstyle.plugin.version> <deploy.plugin.version>2.8.2</deploy.plugin.version>
