You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by pk...@apache.org on 2022/07/24 18:54:37 UTC
[logging-log4j2] 01/04: Add `StatusMessages` and `TestProperties` extension
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 3a249203ba88962b53837dd64968e5e3ae739536
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Fri Jun 24 21:12:01 2022 +0200
Add `StatusMessages` and `TestProperties` extension
Add a JUnit 5 extension, which:
* capture per-test status logger messages and print them to the console
only if the test fails,
* set properties that apply only to the current thread.
---
log4j-api-test/pom.xml | 1 +
...herSessionListener.java => StatusMessages.java} | 27 ++--
...herSessionListener.java => TestProperties.java} | 27 ++--
.../log4j/test/junit/ExtensionContextAnchor.java | 8 +-
.../test/junit/Log4j2LauncherSessionListener.java | 33 ++++-
.../logging/log4j/test/junit/SetTestProperty.java | 59 +++++++++
.../log4j/test/junit/StatusLoggerExtension.java | 134 ++++++++++++++++++++
.../log4j/test/junit/TestPropertyResolver.java | 75 +++++++++++
.../log4j/test/junit/TestPropertySource.java | 141 +++++++++++++++++++++
...SessionListener.java => UsingStatusLogger.java} | 33 +++--
...ssionListener.java => UsingTestProperties.java} | 36 ++++--
.../org.junit.jupiter.api.extension.Extension | 4 +-
12 files changed, 522 insertions(+), 56 deletions(-)
diff --git a/log4j-api-test/pom.xml b/log4j-api-test/pom.xml
index 698af4e3bc..de7d3667a6 100644
--- a/log4j-api-test/pom.xml
+++ b/log4j-api-test/pom.xml
@@ -147,6 +147,7 @@
<!-- Enables the `ExtensionContextAnchor` 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>
</systemPropertyVariables>
</configuration>
</plugin>
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/StatusMessages.java
similarity index 58%
copy from log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
copy to log4j-api-test/src/main/java/org/apache/logging/log4j/test/StatusMessages.java
index 51abd82afe..e59a10ce8e 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/StatusMessages.java
@@ -14,22 +14,23 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j.test.junit;
-import org.apache.logging.log4j.util.PropertiesUtil;
-import org.junit.platform.launcher.LauncherSession;
-import org.junit.platform.launcher.LauncherSessionListener;
+package org.apache.logging.log4j.test;
-/**
- * Global Log4j2 test setup.
- */
-public class Log4j2LauncherSessionListener implements LauncherSessionListener {
+import java.util.stream.Stream;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.status.StatusData;
+
+public interface StatusMessages {
- @Override
- public void launcherSessionOpened(LauncherSession session) {
- // Prevents `PropertiesUtil` from initializing (and caching the results)
- // in the middle of a test.
- PropertiesUtil.getProperties();
+ Stream<StatusData> getMessages();
+
+ default Stream<StatusData> findStatusMessages(Level level) {
+ return getMessages().filter(data -> level.isLessSpecificThan(data.getLevel()));
}
+ default Stream<StatusData> findStatusMessages(Level level, String regex) {
+ return findStatusMessages(level).filter(data -> data.getMessage().getFormattedMessage().matches(regex));
+ }
}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestProperties.java
similarity index 59%
copy from log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
copy to log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestProperties.java
index 51abd82afe..0c66fc8b94 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestProperties.java
@@ -14,22 +14,27 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j.test.junit;
-import org.apache.logging.log4j.util.PropertiesUtil;
-import org.junit.platform.launcher.LauncherSession;
-import org.junit.platform.launcher.LauncherSessionListener;
+package org.apache.logging.log4j.test;
/**
- * Global Log4j2 test setup.
+ * A container for per-test properties.
*/
-public class Log4j2LauncherSessionListener implements LauncherSessionListener {
+public interface TestProperties {
- @Override
- public void launcherSessionOpened(LauncherSession session) {
- // Prevents `PropertiesUtil` from initializing (and caching the results)
- // in the middle of a test.
- PropertiesUtil.getProperties();
+ String getProperty(final String key);
+
+ boolean containsProperty(final String key);
+
+ void setProperty(final String key, final String value);
+
+ default void setProperty(final String key, final boolean value) {
+ setProperty(key, value ? "true" : "false");
+ }
+
+ default void setProperty(final String key, final int value) {
+ setProperty(key, Integer.toString(value));
}
+ void clearProperty(final String key);
}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java
index 0b4cc50d60..25c3117171 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java
@@ -32,7 +32,7 @@ public class ExtensionContextAnchor
public static Namespace LOG4J2_NAMESPACE = Namespace.create("org.apache.logging.log4j.junit");
private static final ThreadLocal<ExtensionContext> EXTENSION_CONTEXT = new InheritableThreadLocal<>();
- private static void bind(ExtensionContext context) {
+ static void bind(ExtensionContext context) {
EXTENSION_CONTEXT.set(context);
}
@@ -48,19 +48,19 @@ public class ExtensionContextAnchor
return context != null ? context : EXTENSION_CONTEXT.get();
}
- static <T> T getAttribute(Object key, Class<T> clazz, ExtensionContext context) {
+ public static <T> T getAttribute(Object key, Class<T> clazz, ExtensionContext context) {
final ExtensionContext actualContext = getContext(context);
assertNotNull(actualContext, "missing ExtensionContext");
return actualContext.getStore(LOG4J2_NAMESPACE).get(key, clazz);
}
- static void setAttribute(Object key, Object value, ExtensionContext context) {
+ public static void setAttribute(Object key, Object value, ExtensionContext context) {
final ExtensionContext actualContext = getContext(context);
assertNotNull(actualContext, "missing ExtensionContext");
actualContext.getStore(LOG4J2_NAMESPACE).put(key, value);
}
- static void removeAttribute(Object key, ExtensionContext context) {
+ public static void removeAttribute(Object key, ExtensionContext context) {
final ExtensionContext actualContext = getContext(context);
if (actualContext != null) {
actualContext.getStore(LOG4J2_NAMESPACE).remove(key);
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
index 51abd82afe..c88aa37d73 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
@@ -16,6 +16,11 @@
*/
package org.apache.logging.log4j.test.junit;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.status.StatusConsoleListener;
+import org.apache.logging.log4j.status.StatusData;
+import org.apache.logging.log4j.status.StatusListener;
+import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.junit.platform.launcher.LauncherSession;
import org.junit.platform.launcher.LauncherSessionListener;
@@ -25,11 +30,37 @@ import org.junit.platform.launcher.LauncherSessionListener;
*/
public class Log4j2LauncherSessionListener implements LauncherSessionListener {
+ private static final String DISABLE_CONSOLE_STATUS_LISTENER = "log4j2.junit.disableConsoleStatusListener";
+
@Override
public void launcherSessionOpened(LauncherSession session) {
// Prevents `PropertiesUtil` from initializing (and caching the results)
// in the middle of a test.
- PropertiesUtil.getProperties();
+ final PropertiesUtil properties = PropertiesUtil.getProperties();
+ if (properties.getBooleanProperty(DISABLE_CONSOLE_STATUS_LISTENER)) {
+ replaceStatusConsoleListener();
+ }
+ }
+
+ private static void replaceStatusConsoleListener() {
+ final StatusLogger logger = StatusLogger.getLogger();
+ for (final StatusListener listener : logger.getListeners()) {
+ if (listener instanceof StatusConsoleListener) {
+ logger.removeListener(listener);
+ }
+ }
+ logger.registerListener(new NoOpStatusConsoleListener());
+ }
+
+ private static class NoOpStatusConsoleListener extends StatusConsoleListener {
+
+ public NoOpStatusConsoleListener() {
+ super(Level.OFF);
+ }
+
+ public void log(final StatusData data) {
+ // NOP
+ }
}
}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SetTestProperty.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SetTestProperty.java
new file mode 100644
index 0000000000..d277125255
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SetTestProperty.java
@@ -0,0 +1,59 @@
+/*
+ * 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.test.junit;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * Registers a Log4j2 system property with the {@link TestPropertySource}. The
+ * property will also be available in configuration files using the
+ * {@code ${test:...} lookup.
+ *
+ */
+@Retention(RUNTIME)
+@Target({ TYPE, METHOD })
+@Inherited
+@Documented
+@ExtendWith(ExtensionContextAnchor.class)
+@ExtendWith(TestPropertyResolver.class)
+@Repeatable(SetTestProperty.SetTestProperties.class)
+public @interface SetTestProperty {
+
+ String key();
+
+ String value();
+
+ @Retention(RUNTIME)
+ @Target({ TYPE, METHOD })
+ @Documented
+ @Inherited
+ public @interface SetTestProperties {
+
+ SetTestProperty[] value();
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerExtension.java
new file mode 100644
index 0000000000..e2cfdacfa8
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerExtension.java
@@ -0,0 +1,134 @@
+/*
+ * 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.test.junit;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.status.StatusConsoleListener;
+import org.apache.logging.log4j.status.StatusData;
+import org.apache.logging.log4j.status.StatusListener;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.test.StatusMessages;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
+import org.junit.jupiter.api.extension.support.TypeBasedParameterResolver;
+
+public class StatusLoggerExtension extends TypeBasedParameterResolver<StatusMessages>
+ implements BeforeEachCallback, TestExecutionExceptionHandler {
+
+ private static final StatusLogger LOGGER = StatusLogger.getLogger();
+ private static final StatusConsoleListener CONSOLE_LISTENER = new StatusConsoleListener(Level.ALL);
+ private static final Object KEY = StatusMessages.class;
+
+ public StatusLoggerExtension() {
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ ExtensionContextAnchor.bind(context);
+ final StatusMessagesHolder holder = new StatusMessagesHolder(context);
+ ExtensionContextAnchor.setAttribute(KEY, holder, context);
+ }
+
+ @Override
+ public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
+ printStatusMessages(context);
+ throw throwable;
+ }
+
+ public void printStatusMessages(ExtensionContext context) {
+ final StatusMessages statusListener = getStatusMessages(context);
+ statusListener.getMessages().forEach(CONSOLE_LISTENER::log);
+ }
+
+ @Override
+ public StatusMessages resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+ throws ParameterResolutionException {
+ return getStatusMessages(extensionContext);
+ }
+
+ private static StatusMessages getStatusMessages(ExtensionContext extensionContext) {
+ final StatusMessagesHolder holder = ExtensionContextAnchor.getAttribute(KEY, StatusMessagesHolder.class,
+ extensionContext);
+ return holder.get();
+ }
+
+ private static class StatusMessagesHolder implements CloseableResource, Supplier<StatusMessages> {
+
+ private final JUnitStatusMessages statusListener;
+
+ public StatusMessagesHolder(ExtensionContext context) {
+ this.statusListener = new JUnitStatusMessages(context);
+ LOGGER.registerListener(statusListener);
+ }
+
+ @Override
+ public StatusMessages get() {
+ return statusListener;
+ }
+
+ @Override
+ public void close() throws Throwable {
+ LOGGER.removeListener(statusListener);
+ }
+
+ }
+
+ private static class JUnitStatusMessages implements StatusMessages, StatusListener {
+
+ private final ExtensionContext context;
+ private final List<StatusData> statusData = Collections.synchronizedList(new ArrayList<StatusData>());
+
+ public JUnitStatusMessages(ExtensionContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public void log(StatusData data) {
+ if (context.equals(ExtensionContextAnchor.getContext())) {
+ statusData.add(data);
+ }
+ }
+
+ @Override
+ public Level getStatusLevel() {
+ return Level.DEBUG;
+ }
+
+ @Override
+ public void close() throws IOException {
+ // NOP
+ }
+
+ @Override
+ public Stream<StatusData> getMessages() {
+ return statusData.stream();
+ }
+
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertyResolver.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertyResolver.java
new file mode 100644
index 0000000000..7c7e9177af
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertyResolver.java
@@ -0,0 +1,75 @@
+/*
+ * 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.test.junit;
+
+import java.lang.reflect.Modifier;
+
+import org.apache.logging.log4j.test.TestProperties;
+import org.apache.logging.log4j.util.ReflectionUtil;
+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.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.support.TypeBasedParameterResolver;
+import org.junit.platform.commons.util.AnnotationUtils;
+
+public class TestPropertyResolver extends TypeBasedParameterResolver<TestProperties>
+ implements BeforeAllCallback, BeforeEachCallback {
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ final TestProperties props = TestPropertySource.createProperties(context);
+ final SetTestProperty[] setProperties = context.getRequiredTestMethod()
+ .getAnnotationsByType(SetTestProperty.class);
+ if (setProperties.length > 0) {
+ for (final SetTestProperty setProperty : setProperties) {
+ props.setProperty(setProperty.key(), setProperty.value());
+ }
+ }
+ final Class<?> testClass = context.getRequiredTestClass();
+ Object testInstance = context.getRequiredTestInstance();
+ AnnotationUtils
+ .findAnnotatedFields(testClass, UsingTestProperties.class,
+ field -> !Modifier.isStatic(field.getModifiers()))
+ .forEach(field -> ReflectionUtil.setFieldValue(field, testInstance, props));
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ final TestProperties props = TestPropertySource.createProperties(context);
+ final SetTestProperty[] setProperties = context.getRequiredTestClass()
+ .getAnnotationsByType(SetTestProperty.class);
+ if (setProperties.length > 0) {
+ for (final SetTestProperty setProperty : setProperties) {
+ props.setProperty(setProperty.key(), setProperty.value());
+ }
+ }
+ final Class<?> testClass = context.getRequiredTestClass();
+ AnnotationUtils
+ .findAnnotatedFields(testClass, UsingTestProperties.class,
+ field -> Modifier.isStatic(field.getModifiers()))
+ .forEach(field -> ReflectionUtil.setStaticFieldValue(field, props));
+ }
+
+ @Override
+ public TestProperties resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+ throws ParameterResolutionException {
+ return TestPropertySource.createProperties(extensionContext);
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertySource.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertySource.java
new file mode 100644
index 0000000000..92547a463f
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertySource.java
@@ -0,0 +1,141 @@
+/*
+ * 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.test.junit;
+
+import org.apache.logging.log4j.test.TestProperties;
+import org.apache.logging.log4j.util.PropertySource;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
+
+public class TestPropertySource implements PropertySource {
+
+ private static final String PREFIX = "log4j2.";
+ private static final Namespace NAMESPACE = ExtensionContextAnchor.LOG4J2_NAMESPACE.append("properties");
+ private static final TestProperties EMPTY_PROPERTIES = new EmptyTestProperties();
+
+ @Override
+ public int getPriority() {
+ // Highest priority
+ return Integer.MIN_VALUE;
+ }
+
+ public static TestProperties createProperties(ExtensionContext context) {
+ TestProperties props = getProperties(context);
+ // Make sure that the properties do not come from the parent ExtensionContext
+ if (props instanceof JUnitTestProperties && context.equals(((JUnitTestProperties) props).getContext())) {
+ return props;
+ }
+ props = new JUnitTestProperties(context);
+ ExtensionContextAnchor.setAttribute(TestProperties.class, props, context);
+ return props;
+ }
+
+ private static TestProperties getProperties() {
+ return getProperties(null);
+ }
+
+ private static TestProperties getProperties(ExtensionContext context) {
+ final ExtensionContext actualContext = context != null ? context : ExtensionContextAnchor.getContext();
+ if (actualContext != null) {
+ TestProperties props = ExtensionContextAnchor.getAttribute(TestProperties.class, TestProperties.class,
+ actualContext);
+ if (props != null) {
+ return props;
+ }
+ }
+ return EMPTY_PROPERTIES;
+ }
+
+ @Override
+ public CharSequence getNormalForm(Iterable<? extends CharSequence> tokens) {
+ final CharSequence camelCase = Util.joinAsCamelCase(tokens);
+ // Do not use Strings to prevent recursive initialization
+ return camelCase.length() > 0 ? PREFIX + camelCase.toString() : null;
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return getProperties().getProperty(key);
+ }
+
+ @Override
+ public boolean containsProperty(String key) {
+ return getProperties().containsProperty(key);
+ }
+
+ private static class JUnitTestProperties implements TestProperties {
+
+ private final ExtensionContext context;
+ private final Store store;
+
+ public JUnitTestProperties(ExtensionContext context) {
+ this.context = context;
+ this.store = context.getStore(NAMESPACE);
+ }
+
+ public ExtensionContext getContext() {
+ return context;
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return store.get(key, String.class);
+ }
+
+ @Override
+ public boolean containsProperty(String key) {
+ return getProperty(key) != null;
+ }
+
+ @Override
+ public void setProperty(String key, String value) {
+ store.put(key, value);
+ }
+
+ @Override
+ public void clearProperty(String key) {
+ store.remove(key, String.class);
+ }
+
+ }
+
+ private static class EmptyTestProperties implements TestProperties {
+
+ @Override
+ public String getProperty(String key) {
+ return null;
+ }
+
+ @Override
+ public boolean containsProperty(String key) {
+ return false;
+ }
+
+ @Override
+ public void setProperty(String key, String value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clearProperty(String key) {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingStatusLogger.java
similarity index 53%
copy from log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
copy to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingStatusLogger.java
index 51abd82afe..875973415d 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingStatusLogger.java
@@ -14,22 +14,29 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
+
package org.apache.logging.log4j.test.junit;
-import org.apache.logging.log4j.util.PropertiesUtil;
-import org.junit.platform.launcher.LauncherSession;
-import org.junit.platform.launcher.LauncherSessionListener;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
/**
- * Global Log4j2 test setup.
+ * Marks a test class as using {@link org.apache.logging.log4j.ThreadContext} APIs. This will automatically clear and restore
+ * both the thread context map and stack for each test invocation.
+ *
+ * @since 2.14.0
*/
-public class Log4j2LauncherSessionListener implements LauncherSessionListener {
-
- @Override
- public void launcherSessionOpened(LauncherSession session) {
- // Prevents `PropertiesUtil` from initializing (and caching the results)
- // in the middle of a test.
- PropertiesUtil.getProperties();
- }
-
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Documented
+@Inherited
+@ExtendWith(ExtensionContextAnchor.class)
+@ExtendWith(StatusLoggerExtension.class)
+public @interface UsingStatusLogger {
}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingTestProperties.java
similarity index 50%
copy from log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
copy to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingTestProperties.java
index 51abd82afe..eb9ab84267 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingTestProperties.java
@@ -14,22 +14,32 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
+
package org.apache.logging.log4j.test.junit;
-import org.apache.logging.log4j.util.PropertiesUtil;
-import org.junit.platform.launcher.LauncherSession;
-import org.junit.platform.launcher.LauncherSessionListener;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
-/**
- * Global Log4j2 test setup.
- */
-public class Log4j2LauncherSessionListener implements LauncherSessionListener {
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
- @Override
- public void launcherSessionOpened(LauncherSession session) {
- // Prevents `PropertiesUtil` from initializing (and caching the results)
- // in the middle of a test.
- PropertiesUtil.getProperties();
- }
+import org.apache.logging.log4j.test.TestProperties;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+/**
+ * A field or method parameter of type {@link TestProperties} will be injected with a per-test source of Log4j2's
+ * system properties.
+ */
+@Retention(RUNTIME)
+@Target({ FIELD, METHOD })
+@Inherited
+@Documented
+@ExtendWith(ExtensionContextAnchor.class)
+@ExtendWith(TestPropertyResolver.class)
+@ReadsSystemProperty
+public @interface UsingTestProperties {
}
diff --git a/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
index ca7ce84edd..a28e0eea70 100644
--- a/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
+++ b/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
@@ -12,4 +12,6 @@
# 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.
-org.apache.logging.log4j.test.junit.ExtensionContextAnchor
\ No newline at end of file
+
+org.apache.logging.log4j.test.junit.ExtensionContextAnchor
+org.apache.logging.log4j.test.junit.StatusLoggerExtension
\ No newline at end of file