mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Introduce Spring property to disable context pausing for tests
Spring Framework 7.0 introduced support for pausing inactive application contexts between test classes and restarting them once they are needed again. If pausing and restarting are fast, this feature does not have a negative impact on test suites. However, if the pausing or restarting of certain Lifecycle components in the application context is slow, that can have a negative impact on the duration of the overall test suite. In gh-36044, we hope to find a way to avoid unnecessarily pausing an application context after a test class if the same context is used by the next test class that is run. That should help reduce the risk of a negative impact caused by the pause/restart feature; however, for certain scenarios that may not be enough. In light of that, this commit introduces a mechanism for completely disabling the pausing feature via a Spring property or JVM system property, as follows. -Dspring.test.context.cache.pause=never See gh-35168 See gh-36044 Closes gh-36117
This commit is contained in:
@@ -124,6 +124,10 @@ on a test class. See xref:testing/annotations/integration-junit-jupiter.adoc#int
|
||||
| The maximum size of the context cache in the _Spring TestContext Framework_. See
|
||||
xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching].
|
||||
|
||||
| `spring.test.context.cache.pause`
|
||||
| The pause mode for the context cache in the _Spring TestContext Framework_. See
|
||||
xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching].
|
||||
|
||||
| `spring.test.context.failure.threshold`
|
||||
| The failure threshold for errors encountered while attempting to load an `ApplicationContext`
|
||||
in the _Spring TestContext Framework_. See
|
||||
|
||||
+16
@@ -73,6 +73,22 @@ will be in a "stopped" state until the context is used again by a test. Note, ho
|
||||
that `SmartLifecycle` components can opt out of pausing by returning `false` from
|
||||
`SmartLifecycle#isPauseable()`.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
If you encounter issues with `Lifecycle` components that cannot or should not opt out of
|
||||
pausing, or if you discover that your test suite runs more slowly due to the pausing and
|
||||
restarting of application contexts, you can disable the pausing feature from the command
|
||||
line or a build script by setting a JVM system property named
|
||||
`spring.test.context.cache.pause` to `never`. For example:
|
||||
|
||||
```shell
|
||||
-Dspring.test.context.cache.pause=never
|
||||
```
|
||||
|
||||
As an alternative, you can set the same property via the
|
||||
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
|
||||
====
|
||||
|
||||
Since having a large number of application contexts loaded within a given test suite can
|
||||
cause the suite to take an unnecessarily long time to run, it is often beneficial to
|
||||
know exactly how many contexts have been loaded and cached. To view the statistics for
|
||||
|
||||
@@ -50,6 +50,7 @@ import org.jspecify.annotations.Nullable;
|
||||
* @see org.springframework.test.context.NestedTestConfiguration#ENCLOSING_CONFIGURATION_PROPERTY_NAME
|
||||
* @see org.springframework.test.context.TestConstructor#TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME
|
||||
* @see org.springframework.test.context.cache.ContextCache#MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME
|
||||
* @see org.springframework.test.context.cache.ContextCache#CONTEXT_CACHE_PAUSE_PROPERTY_NAME
|
||||
*/
|
||||
public final class SpringProperties {
|
||||
|
||||
|
||||
+73
-1
@@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.test.context.cache;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -83,6 +87,24 @@ public interface ContextCache {
|
||||
*/
|
||||
String MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME = "spring.test.context.cache.maxSize";
|
||||
|
||||
/**
|
||||
* System property used to configure whether inactive application contexts
|
||||
* stored in the {@link ContextCache} should be paused: {@value}.
|
||||
* <p>Defaults to {@code always}. Set this property to {@code never} to
|
||||
* disable pausing of inactive application contexts — for example:
|
||||
* <p>{@code -Dspring.test.context.cache.pause=never}
|
||||
* <p>May alternatively be configured via the
|
||||
* {@link org.springframework.core.SpringProperties} mechanism.
|
||||
* <p>Note that implementations of {@code ContextCache} are not required to
|
||||
* support context pausing. Consult the documentation of the corresponding
|
||||
* implementation for details.
|
||||
* @since 7.0.3
|
||||
* @see PauseMode
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#pause()
|
||||
* @see #unregisterContextUsage(MergedContextConfiguration, Class)
|
||||
*/
|
||||
String CONTEXT_CACHE_PAUSE_PROPERTY_NAME = "spring.test.context.cache.pause";
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether there is a cached context for the given key.
|
||||
@@ -221,7 +243,8 @@ public interface ContextCache {
|
||||
* {@link MergedContextConfiguration} and any of its parents.
|
||||
* <p>If no other test classes are actively using the same application
|
||||
* context(s), the application context(s) should be
|
||||
* {@linkplain org.springframework.context.ConfigurableApplicationContext#pause() paused}.
|
||||
* {@linkplain org.springframework.context.ConfigurableApplicationContext#pause()
|
||||
* paused} according to the configured {@link PauseMode}.
|
||||
* <p>The default implementation of this method does nothing. Concrete
|
||||
* implementations are therefore highly encouraged to override this
|
||||
* method, {@link #registerContextUsage(MergedContextConfiguration, Class)},
|
||||
@@ -336,4 +359,53 @@ public interface ContextCache {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration of <em>modes</em> that dictate whether inactive application contexts
|
||||
* stored in the {@link ContextCache} should be
|
||||
* {@linkplain org.springframework.context.ConfigurableApplicationContext#pause() paused}.
|
||||
*
|
||||
* @since 7.0.3
|
||||
* @see #ALWAYS
|
||||
* @see #NEVER
|
||||
* @see ContextCache#CONTEXT_CACHE_PAUSE_PROPERTY_NAME
|
||||
*/
|
||||
enum PauseMode {
|
||||
|
||||
/**
|
||||
* Always pause inactive application contexts.
|
||||
*/
|
||||
ALWAYS,
|
||||
|
||||
/**
|
||||
* Never pause inactive application contexts, effectively disabling the
|
||||
* pausing feature of the {@link ContextCache}.
|
||||
*/
|
||||
NEVER;
|
||||
|
||||
|
||||
/**
|
||||
* Get the {@code PauseMode} enum constant with the supplied name,
|
||||
* {@linkplain String#strip() stripped} and ignoring case.
|
||||
* @param name the name of the enum constant to retrieve
|
||||
* @return the corresponding enum constant or {@code null} if not found
|
||||
* @see PauseMode#valueOf(String)
|
||||
*/
|
||||
public static @Nullable PauseMode from(@Nullable String name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return PauseMode.valueOf(name.strip().toUpperCase(Locale.ROOT));
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
Log logger = LogFactory.getLog(PauseMode.class);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to parse PauseMode from '%s': %s"
|
||||
.formatted(name, ex.getMessage()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+24
@@ -18,6 +18,7 @@ package org.springframework.test.context.cache;
|
||||
|
||||
import org.springframework.core.SpringProperties;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.test.context.cache.ContextCache.PauseMode;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -60,6 +61,29 @@ public abstract class ContextCacheUtils {
|
||||
return retrieveProperty(propertyName, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the {@link PauseMode} for the {@link ContextCache}.
|
||||
* <p>Uses {@link SpringProperties} to retrieve a system property or Spring
|
||||
* property named {@value ContextCache#CONTEXT_CACHE_PAUSE_PROPERTY_NAME}.
|
||||
* <p>Defaults to {@link PauseMode#ALWAYS} if no such property has been set.
|
||||
* @return the configured or default {@code PauseMode}
|
||||
* @since 7.0.3
|
||||
* @see ContextCache#CONTEXT_CACHE_PAUSE_PROPERTY_NAME
|
||||
* @see PauseMode#from(String)
|
||||
*/
|
||||
public static PauseMode retrievePauseMode() {
|
||||
String value = SpringProperties.getProperty(ContextCache.CONTEXT_CACHE_PAUSE_PROPERTY_NAME);
|
||||
if (StringUtils.hasText(value)) {
|
||||
PauseMode pauseMode = PauseMode.from(value);
|
||||
if (pauseMode == null) {
|
||||
throw new IllegalArgumentException("Unsupported value '%s' for property '%s'"
|
||||
.formatted(value, ContextCache.CONTEXT_CACHE_PAUSE_PROPERTY_NAME));
|
||||
}
|
||||
return pauseMode;
|
||||
}
|
||||
return PauseMode.ALWAYS;
|
||||
}
|
||||
|
||||
private static int retrieveProperty(String key, int defaultValue) {
|
||||
try {
|
||||
String value = SpringProperties.getProperty(key);
|
||||
|
||||
+33
-3
@@ -49,10 +49,15 @@ import org.springframework.util.Assert;
|
||||
* constructor argument} or set via a system property or Spring property named
|
||||
* {@value ContextCache#MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME}.
|
||||
*
|
||||
* <p>The {@link PauseMode} may be supplied as a {@linkplain #DefaultContextCache(int, PauseMode)
|
||||
* constructor argument} or set via a system property or Spring property named
|
||||
* {@value ContextCache#CONTEXT_CACHE_PAUSE_PROPERTY_NAME}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see ContextCacheUtils#retrieveMaxCacheSize()
|
||||
* @see ContextCacheUtils#retrievePauseMode()
|
||||
*/
|
||||
public class DefaultContextCache implements ContextCache {
|
||||
|
||||
@@ -91,6 +96,8 @@ public class DefaultContextCache implements ContextCache {
|
||||
|
||||
private final int maxSize;
|
||||
|
||||
private final PauseMode pauseMode;
|
||||
|
||||
private final AtomicInteger hitCount = new AtomicInteger();
|
||||
|
||||
private final AtomicInteger missCount = new AtomicInteger();
|
||||
@@ -98,10 +105,13 @@ public class DefaultContextCache implements ContextCache {
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultContextCache} using the maximum cache size
|
||||
* obtained via {@link ContextCacheUtils#retrieveMaxCacheSize()}.
|
||||
* obtained via {@link ContextCacheUtils#retrieveMaxCacheSize()} and the
|
||||
* {@link PauseMode} obtained via {@link ContextCacheUtils#retrievePauseMode()}.
|
||||
* @since 4.3
|
||||
* @see #DefaultContextCache(int)
|
||||
* @see #DefaultContextCache(int, PauseMode)
|
||||
* @see ContextCacheUtils#retrieveMaxCacheSize()
|
||||
* @see ContextCacheUtils#retrievePauseMode()
|
||||
*/
|
||||
public DefaultContextCache() {
|
||||
this(ContextCacheUtils.retrieveMaxCacheSize());
|
||||
@@ -109,16 +119,35 @@ public class DefaultContextCache implements ContextCache {
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultContextCache} using the supplied maximum
|
||||
* cache size.
|
||||
* cache size and the {@link PauseMode} obtained via
|
||||
* {@link ContextCacheUtils#retrievePauseMode()}.
|
||||
* @param maxSize the maximum cache size
|
||||
* @throws IllegalArgumentException if the supplied {@code maxSize} value
|
||||
* is not positive
|
||||
* @since 4.3
|
||||
* @see #DefaultContextCache()
|
||||
* @see #DefaultContextCache(int, PauseMode)
|
||||
* @see ContextCacheUtils#retrievePauseMode()
|
||||
*/
|
||||
public DefaultContextCache(int maxSize) {
|
||||
this(maxSize, ContextCacheUtils.retrievePauseMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultContextCache} using the supplied maximum
|
||||
* cache size and {@link PauseMode}.
|
||||
* @param maxSize the maximum cache size
|
||||
* @param pauseMode the {@code PauseMode} to use
|
||||
* @throws IllegalArgumentException if the supplied {@code maxSize} value
|
||||
* is not positive or if the supplied {@code PauseMode} is {@code null}
|
||||
* @since 7.0.3
|
||||
* @see #DefaultContextCache()
|
||||
*/
|
||||
public DefaultContextCache(int maxSize, PauseMode pauseMode) {
|
||||
Assert.isTrue(maxSize > 0, "'maxSize' must be positive");
|
||||
Assert.notNull(pauseMode, "'pauseMode' must not be null");
|
||||
this.maxSize = maxSize;
|
||||
this.pauseMode = pauseMode;
|
||||
}
|
||||
|
||||
|
||||
@@ -222,7 +251,8 @@ public class DefaultContextCache implements ContextCache {
|
||||
Set<Class<?>> activeTestClasses = getActiveTestClasses(mergedConfig);
|
||||
activeTestClasses.remove(testClass);
|
||||
if (activeTestClasses.isEmpty()) {
|
||||
if (context instanceof ConfigurableApplicationContext cac && cac.isRunning()) {
|
||||
if ((this.pauseMode == PauseMode.ALWAYS) &&
|
||||
(context instanceof ConfigurableApplicationContext cac && cac.isRunning())) {
|
||||
cac.pause();
|
||||
}
|
||||
this.contextUsageMap.remove(mergedConfig);
|
||||
|
||||
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.test.context.cache;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextCustomizerFactories;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestContextTestUtils;
|
||||
import org.springframework.test.context.cache.ContextCache.PauseMode;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.context.cache.ContextCache.DEFAULT_MAX_CONTEXT_CACHE_SIZE;
|
||||
import static org.springframework.test.context.cache.ContextCacheTestUtils.assertContextCacheStatistics;
|
||||
|
||||
/**
|
||||
* Integration tests for verifying proper behavior of the {@link ContextCache} in
|
||||
* conjunction with {@link PauseMode}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 7.0.3
|
||||
* @see ContextCacheTests
|
||||
* @see UnusedContextsIntegrationTests
|
||||
*/
|
||||
class ContextCachePauseModeTests {
|
||||
|
||||
private ContextCache contextCache;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void clearApplicationEvents() {
|
||||
EventTracker.events.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
void topLevelTestClassesWithPauseModeAlways() {
|
||||
this.contextCache = new DefaultContextCache(DEFAULT_MAX_CONTEXT_CACHE_SIZE, PauseMode.ALWAYS);
|
||||
|
||||
loadCtxAndAssertStats(TestCase1A.class, 1, 1, 0, 1);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRefreshed:TestCase1A", "ContextPaused:TestCase1A");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase1A.class, 1, 1, 1, 1);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRestarted:TestCase1A", "ContextPaused:TestCase1A");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase1B.class, 1, 1, 2, 1);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRestarted:TestCase1A", "ContextPaused:TestCase1A");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase1A.class, 1, 1, 3, 1);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRestarted:TestCase1A", "ContextPaused:TestCase1A");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase2.class, 2, 1, 3, 2);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRefreshed:TestCase2", "ContextPaused:TestCase2");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase1B.class, 2, 1, 4, 2);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRestarted:TestCase1A", "ContextPaused:TestCase1A");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase1A.class, 2, 1, 5, 2);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRestarted:TestCase1A", "ContextPaused:TestCase1A");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase2.class, 2, 1, 6, 2);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRestarted:TestCase2", "ContextPaused:TestCase2");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase2.class, 2, 1, 7, 2);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRestarted:TestCase2", "ContextPaused:TestCase2");
|
||||
clearApplicationEvents();
|
||||
|
||||
markContextDirty(TestCase2.class);
|
||||
assertThat(EventTracker.events).containsExactly("ContextClosed:TestCase2");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase2.class, 2, 1, 7, 3);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRefreshed:TestCase2", "ContextPaused:TestCase2");
|
||||
clearApplicationEvents();
|
||||
}
|
||||
|
||||
@Test
|
||||
void topLevelTestClassesWithPauseModeNever() {
|
||||
this.contextCache = new DefaultContextCache(DEFAULT_MAX_CONTEXT_CACHE_SIZE, PauseMode.NEVER);
|
||||
|
||||
loadCtxAndAssertStats(TestCase1A.class, 1, 1, 0, 1);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRefreshed:TestCase1A");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase1A.class, 1, 1, 1, 1);
|
||||
assertThat(EventTracker.events).isEmpty();
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase1B.class, 1, 1, 2, 1);
|
||||
assertThat(EventTracker.events).isEmpty();
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase1A.class, 1, 1, 3, 1);
|
||||
assertThat(EventTracker.events).isEmpty();
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase2.class, 2, 1, 3, 2);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRefreshed:TestCase2");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase1B.class, 2, 1, 4, 2);
|
||||
assertThat(EventTracker.events).isEmpty();
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase1A.class, 2, 1, 5, 2);
|
||||
assertThat(EventTracker.events).isEmpty();
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase2.class, 2, 1, 6, 2);
|
||||
assertThat(EventTracker.events).isEmpty();
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase2.class, 2, 1, 7, 2);
|
||||
assertThat(EventTracker.events).isEmpty();
|
||||
clearApplicationEvents();
|
||||
|
||||
markContextDirty(TestCase2.class);
|
||||
assertThat(EventTracker.events).containsExactly("ContextClosed:TestCase2");
|
||||
clearApplicationEvents();
|
||||
|
||||
loadCtxAndAssertStats(TestCase2.class, 2, 1, 7, 3);
|
||||
assertThat(EventTracker.events).containsExactly("ContextRefreshed:TestCase2");
|
||||
clearApplicationEvents();
|
||||
}
|
||||
|
||||
private void loadCtxAndAssertStats(Class<?> testClass, int expectedSize, int expectedActiveContextsCount,
|
||||
int expectedHitCount, int expectedMissCount) {
|
||||
|
||||
TestContext testContext = TestContextTestUtils.buildTestContext(testClass, contextCache);
|
||||
|
||||
ApplicationContext context = testContext.getApplicationContext();
|
||||
assertThat(context).isNotNull();
|
||||
assertContextCacheStatistics(contextCache, testClass.getName(), expectedSize, expectedActiveContextsCount,
|
||||
expectedHitCount, expectedMissCount);
|
||||
testContext.markApplicationContextUnused();
|
||||
assertThat(contextCache.getContextUsageCount())
|
||||
.as("active contexts in cache (%s)", testClass.getSimpleName()).isZero();
|
||||
}
|
||||
|
||||
private void markContextDirty(Class<?> testClass) {
|
||||
TestContext testContext = TestContextTestUtils.buildTestContext(testClass, contextCache);
|
||||
testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Import(EventTracker.class)
|
||||
static class Config1 {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(EventTracker.class)
|
||||
static class Config2 {
|
||||
}
|
||||
|
||||
@ContextConfiguration(classes = Config1.class)
|
||||
@ContextCustomizerFactories(DisplayNameCustomizerFactory.class)
|
||||
private abstract static class AbstractTestCase1 {
|
||||
}
|
||||
|
||||
private static class TestCase1A extends AbstractTestCase1 {
|
||||
}
|
||||
|
||||
private static class TestCase1B extends AbstractTestCase1 {
|
||||
}
|
||||
|
||||
@ContextConfiguration(classes = Config2.class)
|
||||
@ContextCustomizerFactories(DisplayNameCustomizerFactory.class)
|
||||
private static class TestCase2 {
|
||||
}
|
||||
|
||||
}
|
||||
+2
@@ -71,6 +71,8 @@ class ContextCacheTests {
|
||||
assertContextCacheStatistics(contextCache, testClass.getName(), expectedSize, expectedActiveContextsCount,
|
||||
expectedHitCount, expectedMissCount);
|
||||
testContext.markApplicationContextUnused();
|
||||
assertThat(contextCache.getContextUsageCount())
|
||||
.as("active contexts in cache (%s)", testClass.getSimpleName()).isZero();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+133
-41
@@ -18,14 +18,21 @@ package org.springframework.test.context.cache;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.FieldSource;
|
||||
|
||||
import org.springframework.core.SpringProperties;
|
||||
import org.springframework.test.context.cache.ContextCache.PauseMode;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.springframework.test.context.cache.ContextCache.CONTEXT_CACHE_PAUSE_PROPERTY_NAME;
|
||||
import static org.springframework.test.context.cache.ContextCache.DEFAULT_MAX_CONTEXT_CACHE_SIZE;
|
||||
import static org.springframework.test.context.cache.ContextCache.MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME;
|
||||
import static org.springframework.test.context.cache.ContextCacheUtils.retrieveMaxCacheSize;
|
||||
import static org.springframework.test.context.cache.ContextCacheUtils.retrievePauseMode;
|
||||
|
||||
/**
|
||||
* Tests for {@link ContextCacheUtils}.
|
||||
@@ -35,56 +42,141 @@ import static org.springframework.test.context.cache.ContextCacheUtils.retrieveM
|
||||
*/
|
||||
class ContextCacheUtilsTests {
|
||||
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void clearProperties() {
|
||||
System.clearProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME);
|
||||
SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, null);
|
||||
@Nested
|
||||
class MaxCacheSizeTests {
|
||||
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void clearProperties() {
|
||||
System.clearProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME);
|
||||
SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromDefault() {
|
||||
assertDefaultValue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromBogusSystemProperty() {
|
||||
System.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "bogus");
|
||||
assertDefaultValue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromBogusSpringProperty() {
|
||||
SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "bogus");
|
||||
assertDefaultValue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromDecimalSpringProperty() {
|
||||
SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "3.14");
|
||||
assertDefaultValue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromSystemProperty() {
|
||||
System.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "42");
|
||||
assertThat(retrieveMaxCacheSize()).isEqualTo(42);
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromSystemPropertyContainingWhitespace() {
|
||||
System.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "42\t");
|
||||
assertThat(retrieveMaxCacheSize()).isEqualTo(42);
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromSpringProperty() {
|
||||
SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "99");
|
||||
assertThat(retrieveMaxCacheSize()).isEqualTo(99);
|
||||
}
|
||||
|
||||
private static void assertDefaultValue() {
|
||||
assertThat(retrieveMaxCacheSize()).isEqualTo(DEFAULT_MAX_CONTEXT_CACHE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromDefault() {
|
||||
assertDefaultValue();
|
||||
}
|
||||
/**
|
||||
* Tests for {@link PauseMode} support.
|
||||
* @since 7.0.3
|
||||
*/
|
||||
@Nested
|
||||
class PauseModeTests {
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromBogusSystemProperty() {
|
||||
System.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "bogus");
|
||||
assertDefaultValue();
|
||||
}
|
||||
static final String[] ALWAYS_VALUES = {
|
||||
"always",
|
||||
"Always",
|
||||
"ALWAYS",
|
||||
"\talways\u000B"
|
||||
};
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromBogusSpringProperty() {
|
||||
SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "bogus");
|
||||
assertDefaultValue();
|
||||
}
|
||||
static final String[] NEVER_VALUES = {
|
||||
"never",
|
||||
"Never",
|
||||
"NEVER",
|
||||
"\tnever\u000B"
|
||||
};
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromDecimalSpringProperty() {
|
||||
SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "3.14");
|
||||
assertDefaultValue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromSystemProperty() {
|
||||
System.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "42");
|
||||
assertThat(retrieveMaxCacheSize()).isEqualTo(42);
|
||||
}
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void clearProperties() {
|
||||
System.clearProperty(CONTEXT_CACHE_PAUSE_PROPERTY_NAME);
|
||||
SpringProperties.setProperty(CONTEXT_CACHE_PAUSE_PROPERTY_NAME, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromSystemPropertyContainingWhitespace() {
|
||||
System.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "42\t");
|
||||
assertThat(retrieveMaxCacheSize()).isEqualTo(42);
|
||||
}
|
||||
@Test
|
||||
void retrievePauseModeFromDefault() {
|
||||
assertThat(retrievePauseMode()).isEqualTo(PauseMode.ALWAYS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveMaxCacheSizeFromSpringProperty() {
|
||||
SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "99");
|
||||
assertThat(retrieveMaxCacheSize()).isEqualTo(99);
|
||||
}
|
||||
@Test
|
||||
void retrievePauseModeFromBogusSystemProperty() {
|
||||
System.setProperty(CONTEXT_CACHE_PAUSE_PROPERTY_NAME, "bogus");
|
||||
asssertUnsupportedValue();
|
||||
}
|
||||
|
||||
private static void assertDefaultValue() {
|
||||
assertThat(retrieveMaxCacheSize()).isEqualTo(DEFAULT_MAX_CONTEXT_CACHE_SIZE);
|
||||
@Test
|
||||
void retrievePauseModeFromBogusSpringProperty() {
|
||||
SpringProperties.setProperty(CONTEXT_CACHE_PAUSE_PROPERTY_NAME, "bogus");
|
||||
asssertUnsupportedValue();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@FieldSource("ALWAYS_VALUES")
|
||||
void retrievePauseModeFromSystemPropertyWithValueAlways(String value) {
|
||||
System.setProperty(CONTEXT_CACHE_PAUSE_PROPERTY_NAME, value);
|
||||
assertThat(retrievePauseMode()).isEqualTo(PauseMode.ALWAYS);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@FieldSource("NEVER_VALUES")
|
||||
void retrievePauseModeFromSystemPropertyWithValueNever(String value) {
|
||||
System.setProperty(CONTEXT_CACHE_PAUSE_PROPERTY_NAME, value);
|
||||
assertThat(retrievePauseMode()).isEqualTo(PauseMode.NEVER);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@FieldSource("ALWAYS_VALUES")
|
||||
void retrievePauseModeFromSpringPropertyWithValueAlways(String value) {
|
||||
SpringProperties.setProperty(CONTEXT_CACHE_PAUSE_PROPERTY_NAME, value);
|
||||
assertThat(retrievePauseMode()).isEqualTo(PauseMode.ALWAYS);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@FieldSource("NEVER_VALUES")
|
||||
void retrievePauseModeFromSpringPropertyWithValueNever(String value) {
|
||||
SpringProperties.setProperty(CONTEXT_CACHE_PAUSE_PROPERTY_NAME, value);
|
||||
assertThat(retrievePauseMode()).isEqualTo(PauseMode.NEVER);
|
||||
}
|
||||
|
||||
private void asssertUnsupportedValue() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(ContextCacheUtils::retrievePauseMode)
|
||||
.withMessage("Unsupported value 'bogus' for property 'spring.test.context.cache.pause'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user