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>

Reply via email to