Close contexts when clearing test context cache

This updates the TestContext Framework cache so that clearing the
cache also closes cached ConfigurableApplicationContext instances
instead of only dropping the internal references. The implementation
reuses the existing removal path, preserving the hierarchy-aware close
behavior already used by cache eviction/removal.

The ContextCache contract now documents the close behavior for
clear(), and LruContextCacheTests covers both clear() and reset(),
since reset() delegates to clear().

See gh-26196
Closes gh-36825

Signed-off-by: Will-thom <116388885+Will-thom@users.noreply.github.com>
This commit is contained in:
Will-thom
2026-08-31 13:07:54 +02:00
committed by Sam Brannen
parent 5524fc6a55
commit 97067f9c9c
3 changed files with 43 additions and 1 deletions
@@ -314,7 +314,10 @@ public interface ContextCache {
void reset();
/**
* Clear all contexts from the cache, clearing context hierarchy information as well.
* Clear all contexts from the cache, explicitly
* {@linkplain org.springframework.context.ConfigurableApplicationContext#close() closing}
* each context that is an instance of {@code ConfigurableApplicationContext}
* and clearing context hierarchy information as well.
*/
void clear();
@@ -426,6 +426,9 @@ public class DefaultContextCache implements ContextCache {
@Override
public void clear() {
synchronized (this.contextMap) {
for (MergedContextConfiguration key : new ArrayList<>(this.contextMap.keySet())) {
remove(key, HierarchyMode.CURRENT_LEVEL);
}
this.contextMap.clear();
this.hierarchyMap.clear();
this.contextUsageMap.clear();
@@ -81,6 +81,42 @@ class LruContextCacheTests {
assertThatIllegalArgumentException().isThrownBy(() -> new DefaultContextCache(0));
}
@Test
void clearClosesContexts() {
DefaultContextCache cache = new DefaultContextCache(4);
cache.put(fooConfig, key -> fooContext);
cache.put(barConfig, key -> barContext);
cache.put(bazConfig, key -> bazContext);
assertCacheContents(cache, "Foo", "Bar", "Baz");
cache.clear();
assertCacheContents(cache);
verify(fooContext, times(1)).close();
verify(barContext, times(1)).close();
verify(bazContext, times(1)).close();
verify(abcContext, never()).close();
}
@Test
void resetClosesContexts() {
DefaultContextCache cache = new DefaultContextCache(4);
cache.put(fooConfig, key -> fooContext);
cache.put(barConfig, key -> barContext);
cache.get(fooConfig);
assertThat(cache.getHitCount()).isEqualTo(1);
assertCacheContents(cache, "Bar", "Foo");
cache.reset();
assertCacheContents(cache);
assertThat(cache.getHitCount()).isZero();
verify(fooContext, times(1)).close();
verify(barContext, times(1)).close();
}
@Nested
@SuppressWarnings("deprecation")