Skip to content

Close contexts when clearing test context cache - #36825

Open
Will-thom wants to merge 1 commit into
spring-projects:mainfrom
Will-thom:fix/context-cache-clear-closes-contexts
Open

Close contexts when clearing test context cache#36825
Will-thom wants to merge 1 commit into
spring-projects:mainfrom
Will-thom:fix/context-cache-clear-closes-contexts

Conversation

@Will-thom

Copy link
Copy Markdown

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().

Closes gh-26196

Tests:

  • ./gradlew.bat --no-daemon --max-workers=1 :spring-test:test --tests org.springframework.test.context.cache.LruContextCacheTests
  • ./gradlew.bat --no-daemon --max-workers=1 :spring-test:test --tests org.springframework.test.context.cache.ContextCacheTests
  • ./gradlew.bat --no-daemon --max-workers=1 :spring-test:check

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label May 21, 2026
@bclozel bclozel added the in: test Issues in the test module label May 21, 2026
@sbrannen sbrannen self-assigned this May 22, 2026
@sbrannen sbrannen added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels May 22, 2026
@sbrannen sbrannen added this to the 7.0.8 milestone May 22, 2026
@sbrannen

Copy link
Copy Markdown
Member

Thanks for the PR, @Will-thom. 👍

Closes gh-26196

Please note that this change would only serve as a building block for #26196, since it would not address the broader topic raised in that issue.

In any case, it makes sense for clear() and reset() to actually close contexts instead of just removing them from the cache.

Also, please make sure you comply with the DCO requirement by signing your commits. Otherwise, we cannot process this PR.

@sbrannen sbrannen added the status: waiting-for-feedback We need additional information before we can continue label May 24, 2026
@sbrannen sbrannen modified the milestones: 7.0.8, 7.1.0-M1 May 26, 2026
@sbrannen

This comment was marked as outdated.

Signed-off-by: Will-thom <116388885+Will-thom@users.noreply.github.com>
@Will-thom
Will-thom force-pushed the fix/context-cache-clear-closes-contexts branch from 9272ec3 to 0923445 Compare May 26, 2026 17:18
@sbrannen sbrannen removed the status: waiting-for-feedback We need additional information before we can continue label May 28, 2026
@Will-thom

Copy link
Copy Markdown
Author

Thanks for the reminder. The commit now includes the required Signed-off-by line, and the DCO check is passing on the latest PR head. Could you please take another look when you have a chance?

@sbrannen sbrannen modified the milestones: 7.1.0-M1, 7.1.0-M2 Aug 10, 2026

@sbrannen sbrannen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update, @Will-thom. The DCO check looks good now.

I went through the clear()/reset() change in detail, and the core approach is correct: reusing remove(key, HierarchyMode.CURRENT_LEVEL) over a snapshot of the cache's keys closes every cached context exactly once, in the correct bottom-up order, regardless of iteration order.

I've requested a minor change and improvements to the tests, after which I think this PR will be good to go.


verify(fooContext, times(1)).close();
verify(barContext, times(1)).close();
}

@sbrannen sbrannen Aug 16, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a couple of things I'd like addressed on the test side before merging:

  1. Hierarchy coverage. These two new tests only cover flat, non-hierarchical configs (config() always passes a null parent). Since this change goes through the hierarchy-aware remove() path, could you please add a test that builds an actual multi-level @ContextHierarchy (similar to the removeContextHierarchyCacheLevel*() tests in ContextCacheTests) and asserts that clear()/reset() closes every level correctly?

  2. Invariant coverage. Neither of the new tests verifies that hierarchyMap and contextUsageMap actually end up empty — only contextMap is checked, via assertCacheContents(). Since those two structures are exactly what this change touches inside remove(), please also assert getParentContextCount() and getContextUsageCount() return to 0 after clear()/reset() – for example, using the existing assertContextCacheStatistics() / assertParentContextCount() helpers already used in ContextCacheTests. For resetClosesContexts(), it'd also be good to assert getMissCount() is 0 for symmetry with the existing getHitCount() check.

This would give us a real regression guard on these new code paths, rather than only checking that the mock contexts were closed.


As side note, I think using Mockito.inOrder() for the verification would also serve us well to ensure the contexts are closed in the expected order within a context hierarchy.

However, inOrder should only assert order along a single ancestor → descendant chain (e.g. child → parent). It shouldn't assert anything about the relative order of sibling contexts, since children of the same parent are stored in a HashSet and closed in unspecified order. In other words, asserting sibling order would result in a flaky test.

for (MergedContextConfiguration key : new ArrayList<>(this.contextMap.keySet())) {
remove(key, HierarchyMode.CURRENT_LEVEL);
}
this.contextMap.clear();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this new loop, the four subsequent calls to this.contextMap.clear(), this.hierarchyMap.clear(), this.contextUsageMap.clear(), and this.unusedContexts.clear() are now redundant: the loop's calls to remove() should already empty all four as an invariant, since every key in the pre-loop snapshot ends up removed either directly or as a descendant.

Could you please remove those clear() calls and ensure the tests cover those invariants (see other comment regarding tests)?

@sbrannen sbrannen added the status: waiting-for-feedback We need additional information before we can continue label Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in: test Issues in the test module status: waiting-for-feedback We need additional information before we can continue type: enhancement A general enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Close all ApplicationContexts in the TestContext framework after all tests have been executed

4 participants