You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2020/09/07 19:14:22 UTC

[logging-log4j2] 02/03: Support shutdown timeout in JUnit 5 extension

This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 1fb8aaee0704cafbe074f9b5c180b4a87e2ae096
Author: Matt Sicker <bo...@gmail.com>
AuthorDate: Mon Sep 7 14:13:20 2020 -0500

    Support shutdown timeout in JUnit 5 extension
    
    This ports over the previous functionality to specify a custom shutdown timeout when stopping a LoggerContext in unit tests.
    
    Signed-off-by: Matt Sicker <bo...@gmail.com>
---
 .../logging/log4j/junit/LoggerContextResolver.java | 95 +++++++++++++++-------
 .../logging/log4j/junit/LoggerContextRule.java     |  3 +
 .../logging/log4j/junit/LoggerContextSource.java   | 11 +++
 3 files changed, 80 insertions(+), 29 deletions(-)

diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextResolver.java b/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextResolver.java
index fb0f367..3a92d3b 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextResolver.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextResolver.java
@@ -18,6 +18,7 @@
 package org.apache.logging.log4j.junit;
 
 import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.LoggerContextAccessor;
 import org.apache.logging.log4j.core.config.Configurator;
 import org.junit.jupiter.api.extension.AfterAllCallback;
 import org.junit.jupiter.api.extension.AfterEachCallback;
@@ -29,6 +30,7 @@ import org.junit.jupiter.api.extension.ParameterResolutionException;
 import org.junit.jupiter.api.extension.support.TypeBasedParameterResolver;
 
 import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
 
 class LoggerContextResolver extends TypeBasedParameterResolver<LoggerContext> implements BeforeAllCallback,
         AfterAllCallback, BeforeEachCallback, AfterEachCallback {
@@ -37,59 +39,64 @@ class LoggerContextResolver extends TypeBasedParameterResolver<LoggerContext> im
         final Class<?> testClass = context.getRequiredTestClass();
         final LoggerContextSource testSource = testClass.getAnnotation(LoggerContextSource.class);
         if (testSource != null) {
-            final LoggerContext loggerContext =
-                    Configurator.initialize(context.getDisplayName(), testClass.getClassLoader(), testSource.value());
-            getTestClassStore(context).put(LoggerContext.class, loggerContext);
+            final LoggerContextConfig config = new LoggerContextConfig(testSource, context);
+            getTestClassStore(context).put(LoggerContext.class, config);
         }
     }
 
     @Override
     public void afterAll(ExtensionContext context) throws Exception {
-        final LoggerContext loggerContext = getTestClassStore(context).get(LoggerContext.class, LoggerContext.class);
-        if (loggerContext != null) {
-            loggerContext.close();
+        final LoggerContextConfig config =
+                getTestClassStore(context).get(LoggerContext.class, LoggerContextConfig.class);
+        if (config != null) {
+            config.close();
         }
     }
 
     @Override
     public void beforeEach(ExtensionContext context) throws Exception {
         final Class<?> testClass = context.getRequiredTestClass();
-        final LoggerContextSource testSource = testClass.getAnnotation(LoggerContextSource.class);
-        if (testSource != null && testSource.reconfigure() == ReconfigurationPolicy.BEFORE_EACH) {
-            final LoggerContext loggerContext = getTestClassStore(context).get(LoggerContext.class, LoggerContext.class);
-            if (loggerContext == null) {
+        if (testClass.isAnnotationPresent(LoggerContextSource.class)) {
+            final LoggerContextConfig config = getTestClassStore(context).get(LoggerContext.class, LoggerContextConfig.class);
+            if (config == null) {
                 throw new IllegalStateException(
-                        "Specified test class reconfiguration policy of BEFORE_EACH, but no LoggerContext found for test class " +
+                        "Specified @LoggerContextSource but no LoggerContext found for test class " +
                                 testClass.getCanonicalName());
             }
-            loggerContext.reconfigure();
+            if (config.reconfigurationPolicy == ReconfigurationPolicy.BEFORE_EACH) {
+                config.reconfigure();
+            }
         }
         final LoggerContextSource source = context.getRequiredTestMethod().getAnnotation(LoggerContextSource.class);
         if (source != null) {
-            final LoggerContext loggerContext = Configurator
-                    .initialize(context.getDisplayName(), testClass.getClassLoader(), source.value());
-            getTestInstanceStore(context).put(LoggerContext.class, loggerContext);
+            final LoggerContextConfig config = new LoggerContextConfig(source, context);
+            if (config.reconfigurationPolicy == ReconfigurationPolicy.BEFORE_EACH) {
+                config.reconfigure();
+            }
+            getTestInstanceStore(context).put(LoggerContext.class, config);
         }
     }
 
     @Override
     public void afterEach(ExtensionContext context) throws Exception {
         // method-annotated variant
-        final LoggerContext testInstanceContext = getTestInstanceStore(context).get(LoggerContext.class, LoggerContext.class);
-        if (testInstanceContext != null) {
-            testInstanceContext.close();
+        final LoggerContextConfig testInstanceConfig =
+                getTestInstanceStore(context).get(LoggerContext.class, LoggerContextConfig.class);
+        if (testInstanceConfig != null) {
+            testInstanceConfig.close();
         }
         // reloadable variant
         final Class<?> testClass = context.getRequiredTestClass();
-        final LoggerContextSource source = testClass.getAnnotation(LoggerContextSource.class);
-        if (source != null && source.reconfigure() == ReconfigurationPolicy.AFTER_EACH) {
-            final LoggerContext loggerContext = getTestClassStore(context).get(LoggerContext.class, LoggerContext.class);
-            if (loggerContext == null) {
+        if (testClass.isAnnotationPresent(LoggerContextSource.class)) {
+            final LoggerContextConfig config = getTestClassStore(context).get(LoggerContext.class, LoggerContextConfig.class);
+            if (config == null) {
                 throw new IllegalStateException(
-                        "Specified test class reconfiguration policy of AFTER_EACH, but no LoggerContext found for test class " +
+                        "Specified @LoggerContextSource but no LoggerContext found for test class " +
                                 testClass.getCanonicalName());
             }
-            loggerContext.reconfigure();
+            if (config.reconfigurationPolicy == ReconfigurationPolicy.AFTER_EACH) {
+                config.reconfigure();
+            }
         }
     }
 
@@ -109,12 +116,42 @@ class LoggerContextResolver extends TypeBasedParameterResolver<LoggerContext> im
 
     static LoggerContext getParameterLoggerContext(ParameterContext parameterContext, ExtensionContext extensionContext) {
         if (parameterContext.getDeclaringExecutable() instanceof Method) {
-            final LoggerContext loggerContext =
-                    getTestInstanceStore(extensionContext).get(LoggerContext.class, LoggerContext.class);
-            return loggerContext != null ? loggerContext :
-                    getTestClassStore(extensionContext).get(LoggerContext.class, LoggerContext.class);
+            final LoggerContextAccessor accessor =
+                    getTestInstanceStore(extensionContext).get(LoggerContext.class, LoggerContextAccessor.class);
+            return accessor != null ? accessor.getLoggerContext() :
+                    getTestClassStore(extensionContext).get(LoggerContext.class, LoggerContextAccessor.class).getLoggerContext();
+        }
+        return getTestClassStore(extensionContext).get(LoggerContext.class, LoggerContextAccessor.class).getLoggerContext();
+    }
+
+    private static class LoggerContextConfig implements AutoCloseable, LoggerContextAccessor {
+        private final LoggerContext context;
+        private final ReconfigurationPolicy reconfigurationPolicy;
+        private final long shutdownTimeout;
+        private final TimeUnit unit;
+
+        private LoggerContextConfig(final LoggerContextSource source, final ExtensionContext extensionContext) {
+            final String displayName = extensionContext.getDisplayName();
+            final ClassLoader classLoader = extensionContext.getRequiredTestClass().getClassLoader();
+            context = Configurator.initialize(displayName, classLoader, source.value());
+            reconfigurationPolicy = source.reconfigure();
+            shutdownTimeout = source.timeout();
+            unit = source.unit();
+        }
+
+        @Override
+        public LoggerContext getLoggerContext() {
+            return context;
+        }
+
+        public void reconfigure() {
+            context.reconfigure();
+        }
+
+        @Override
+        public void close() {
+            context.stop(shutdownTimeout, unit);
         }
-        return getTestClassStore(extensionContext).get(LoggerContext.class, LoggerContext.class);
     }
 
 }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextRule.java b/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextRule.java
index 2215350..73a994a 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextRule.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextRule.java
@@ -42,6 +42,9 @@ import static org.junit.Assert.*;
  * property {@code EBUG} is set (e.g., through the command line option {@code -DEBUG}), then the StatusLogger will be
  * set to the debug level. This allows for more debug messages as the StatusLogger will be in the error level until a
  * configuration file has been read and parsed into a tree of Nodes.
+ *
+ * @see LoggerContextSource
+ * @see Named
  */
 public class LoggerContextRule implements TestRule, LoggerContextAccessor {
 
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextSource.java b/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextSource.java
index a6cedfb..e0cba27 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextSource.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextSource.java
@@ -29,6 +29,7 @@ import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Specifies a configuration file to use for unit tests. This configuration file will be loaded once and used for all tests
@@ -65,4 +66,14 @@ public @interface LoggerContextSource {
      * Specifies when to {@linkplain LoggerContext#reconfigure() reconfigure} the logging system.
      */
     ReconfigurationPolicy reconfigure() default ReconfigurationPolicy.NEVER;
+
+    /**
+     * Specifies the shutdown timeout limit. Defaults to 0 to mean no limit.
+     */
+    long timeout() default 0L;
+
+    /**
+     * Specifies the time unit {@link #timeout()} is measured in.
+     */
+    TimeUnit unit() default TimeUnit.SECONDS;
 }