mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-24 07:10:29 +00:00
Pause unused application contexts in the TestContext framework
Since the introduction of the Spring TestContext Framework in 2007, application contexts have always been stored in the context cache in a "running" state. However, leaving a context running means that components in the context may continue to run in the background. For example, JMS listeners may continue to consume messages from a queue; scheduled tasks may continue to perform active work, etc.; and this can lead to issues within a test suite. To address such issues, this commit introduces built-in support for pausing application contexts when they are not in use and restarting them if they are needed again. Specifically, the TestContextManager now marks a test's application context as "unused" after execution of the test class has ended, and the underlying ContextCache then "stops" the application context if no other test class is currently using the context. When a TestExecutionListener later attempts to obtain a paused application context -- for example, for a subsequent test class that shares the same application context -- the ContextCache ensures that context is restarted before returning it. See https://github.com/spring-projects/spring-boot/issues/28312 See gh-35171 Closes gh-35168
This commit is contained in:
+9
@@ -62,6 +62,15 @@ script by setting a JVM system property named `spring.test.context.cache.maxSize
|
||||
alternative, you can set the same property via the
|
||||
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
|
||||
|
||||
As of Spring Framework 7.0, an application context stored in the context cache will be
|
||||
stopped when it is no longer actively in use and automatically restarted the next time
|
||||
the context is retrieved from the cache. Specifically, the latter will restart all
|
||||
auto-startup beans in the application context, effectively restoring the lifecycle state.
|
||||
This ensures that background processes within the context are not actively running while
|
||||
the context is not used by tests. For example, JMS listener containers, scheduled tasks,
|
||||
and any other components in the context that implement `Lifecycle` or `SmartLifecycle`
|
||||
will be in a "stopped" state until the context is used again by a test.
|
||||
|
||||
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
|
||||
|
||||
+39
@@ -29,6 +29,11 @@ import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
* {@link org.springframework.test.context.cache.ContextCache ContextCache}
|
||||
* behind the scenes.
|
||||
*
|
||||
* <p>As of Spring Framework 7.0, this SPI includes optional support for
|
||||
* {@linkplain #registerContextUsage(MergedContextConfiguration, Class) registering} and
|
||||
* {@linkplain #unregisterContextUsage(MergedContextConfiguration, Class) unregistering}
|
||||
* context usage.
|
||||
*
|
||||
* <p>Note: {@code CacheAwareContextLoaderDelegate} does not extend the
|
||||
* {@link ContextLoader} or {@link SmartContextLoader} interface.
|
||||
*
|
||||
@@ -142,4 +147,38 @@ public interface CacheAwareContextLoaderDelegate {
|
||||
*/
|
||||
void closeContext(MergedContextConfiguration mergedConfig, @Nullable HierarchyMode hierarchyMode);
|
||||
|
||||
/**
|
||||
* Register usage of the {@linkplain ApplicationContext application context}
|
||||
* for the supplied {@link MergedContextConfiguration} as well as usage of the
|
||||
* application context for its {@linkplain MergedContextConfiguration#getParent()
|
||||
* parent}, recursively.
|
||||
* <p>This is intended to be invoked whenever a
|
||||
* {@link org.springframework.test.context.TestExecutionListener TestExecutionListener}
|
||||
* interacts with the application context(s) on behalf of the supplied test class.
|
||||
* @param key the context key; never {@code null}
|
||||
* @param testClass the test class that is using the application context(s)
|
||||
* @since 7.0
|
||||
* @see #unregisterContextUsage(MergedContextConfiguration, Class)
|
||||
*/
|
||||
default void registerContextUsage(MergedContextConfiguration key, Class<?> testClass) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister usage of the {@linkplain ApplicationContext application context}
|
||||
* for the supplied {@link MergedContextConfiguration} as well as usage of the
|
||||
* application context for its {@linkplain MergedContextConfiguration#getParent()
|
||||
* parent}, recursively.
|
||||
* <p>This informs the {@code ContextCache} that the application context(s) can
|
||||
* be safely {@linkplain org.springframework.context.Lifecycle#stop() stopped}
|
||||
* if no other test classes are actively using the same application context(s).
|
||||
* @param key the context key; never {@code null}
|
||||
* @param testClass the test class that is no longer using the application context(s)
|
||||
* @since 7.0
|
||||
* @see #registerContextUsage(MergedContextConfiguration, Class)
|
||||
*/
|
||||
default void unregisterContextUsage(MergedContextConfiguration key, Class<?> testClass) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ public interface TestContext extends AttributeAccessor, Serializable {
|
||||
* Determine if the {@linkplain ApplicationContext application context} for
|
||||
* this test context is known to be available.
|
||||
* <p>If this method returns {@code true}, a subsequent invocation of
|
||||
* {@link #getApplicationContext()} should succeed.
|
||||
* {@link #getApplicationContext()} or {@link #markApplicationContextUnused()}
|
||||
* should succeed.
|
||||
* <p>The default implementation of this method always returns {@code false}.
|
||||
* Custom {@code TestContext} implementations are therefore highly encouraged
|
||||
* to override this method with a more meaningful implementation. Note that
|
||||
@@ -62,6 +63,7 @@ public interface TestContext extends AttributeAccessor, Serializable {
|
||||
* @return {@code true} if the application context has already been loaded
|
||||
* @since 5.2
|
||||
* @see #getApplicationContext()
|
||||
* @see #markApplicationContextUnused()
|
||||
*/
|
||||
default boolean hasApplicationContext() {
|
||||
return false;
|
||||
@@ -77,6 +79,7 @@ public interface TestContext extends AttributeAccessor, Serializable {
|
||||
* @throws IllegalStateException if an error occurs while retrieving the
|
||||
* application context
|
||||
* @see #hasApplicationContext()
|
||||
* @see #markApplicationContextUnused()
|
||||
*/
|
||||
ApplicationContext getApplicationContext();
|
||||
|
||||
@@ -128,6 +131,24 @@ public interface TestContext extends AttributeAccessor, Serializable {
|
||||
*/
|
||||
@Nullable Throwable getTestException();
|
||||
|
||||
/**
|
||||
* Call this method to signal that the {@linkplain #getTestClass() test class}
|
||||
* is no longer using the {@linkplain ApplicationContext application context}
|
||||
* associated with this test context.
|
||||
* <p>This informs the context cache that the application context can be
|
||||
* safely {@linkplain org.springframework.context.Lifecycle#stop() stopped}
|
||||
* if no other test classes are actively using the same application context.
|
||||
* <p>This method is intended to be invoked after execution of the test class
|
||||
* has ended and should not be invoked unless the application context for this
|
||||
* test context is known to be {@linkplain #hasApplicationContext() available}.
|
||||
* <p>This feature is primarily intended for use within the framework.
|
||||
* @since 7.0
|
||||
* @see TestContextManager#afterTestClass()
|
||||
*/
|
||||
default void markApplicationContextUnused() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to signal that the {@linkplain ApplicationContext application
|
||||
* context} associated with this test context is <em>dirty</em> and should be
|
||||
|
||||
@@ -520,10 +520,14 @@ public class TestContextManager {
|
||||
* the first exception.
|
||||
* <p>Note that listeners will be executed in the opposite order in which they
|
||||
* were registered.
|
||||
* <p>As of Spring Framework 7.0, this method also ensures that the application
|
||||
* context for the current {@link #getTestContext() TestContext} is marked as
|
||||
* {@linkplain TestContext#markApplicationContextUnused() unused}.
|
||||
* @throws Exception if a registered TestExecutionListener throws an exception
|
||||
* @since 3.0
|
||||
* @see #getTestExecutionListeners()
|
||||
* @see Throwable#addSuppressed(Throwable)
|
||||
* @see TestContext#markApplicationContextUnused()
|
||||
*/
|
||||
public void afterTestClass() throws Exception {
|
||||
Class<?> testClass = getTestContext().getTestClass();
|
||||
@@ -550,6 +554,20 @@ public class TestContextManager {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (getTestContext().hasApplicationContext()) {
|
||||
getTestContext().markApplicationContextUnused();
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (afterTestClassException == null) {
|
||||
afterTestClassException = ex;
|
||||
}
|
||||
else {
|
||||
afterTestClassException.addSuppressed(ex);
|
||||
}
|
||||
}
|
||||
|
||||
this.testContextHolder.remove();
|
||||
|
||||
if (afterTestClassException != null) {
|
||||
|
||||
+73
-5
@@ -35,7 +35,10 @@ import org.springframework.test.context.MergedContextConfiguration;
|
||||
* <p>As of Spring Framework 6.1, this SPI includes optional support for
|
||||
* {@linkplain #getFailureCount(MergedContextConfiguration) tracking} and
|
||||
* {@linkplain #incrementFailureCount(MergedContextConfiguration) incrementing}
|
||||
* failure counts.
|
||||
* failure counts. As of Spring Framework 7.0, this SPI includes optional support for
|
||||
* {@linkplain #registerContextUsage(MergedContextConfiguration, Class) registering} and
|
||||
* {@linkplain #unregisterContextUsage(MergedContextConfiguration, Class) unregistering}
|
||||
* context usage.
|
||||
*
|
||||
* <h3>Rationale</h3>
|
||||
* <p>Context caching can have significant performance benefits if context
|
||||
@@ -88,13 +91,19 @@ public interface ContextCache {
|
||||
boolean contains(MergedContextConfiguration key);
|
||||
|
||||
/**
|
||||
* Obtain a cached {@code ApplicationContext} for the given key.
|
||||
* <p>The {@linkplain #getHitCount() hit} and {@linkplain #getMissCount() miss}
|
||||
* counts must be updated accordingly.
|
||||
* Obtain a cached {@link ApplicationContext} for the given key.
|
||||
* <p>If the cached application context was previously
|
||||
* {@linkplain org.springframework.context.Lifecycle#stop() stopped}, it
|
||||
* must be
|
||||
* {@linkplain org.springframework.context.support.AbstractApplicationContext#restart()
|
||||
* restarted}. This applies to parent contexts as well.
|
||||
* <p>In addition, the {@linkplain #getHitCount() hit} and
|
||||
* {@linkplain #getMissCount() miss} counts must be updated accordingly.
|
||||
* @param key the context key (never {@code null})
|
||||
* @return the corresponding {@code ApplicationContext} instance, or {@code null}
|
||||
* if not found in the cache
|
||||
* @see #remove
|
||||
* @see #unregisterContextUsage(MergedContextConfiguration, Class)
|
||||
* @see #remove(MergedContextConfiguration, HierarchyMode)
|
||||
*/
|
||||
@Nullable ApplicationContext get(MergedContextConfiguration key);
|
||||
|
||||
@@ -151,6 +160,64 @@ public interface ContextCache {
|
||||
* @see #getFailureCount(MergedContextConfiguration)
|
||||
*/
|
||||
default void incrementFailureCount(MergedContextConfiguration key) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Register usage of the {@link ApplicationContext} for the supplied
|
||||
* {@link MergedContextConfiguration} and any of its parents.
|
||||
* <p>The default implementation of this method does nothing. Concrete
|
||||
* implementations are therefore highly encouraged to override this
|
||||
* method, {@link #unregisterContextUsage(MergedContextConfiguration, Class)},
|
||||
* and {@link #getContextUsageCount()} with appropriate behavior. Note that
|
||||
* the standard {@code ContextContext} implementation in Spring overrides
|
||||
* these methods appropriately.
|
||||
* @param key the context key; never {@code null}
|
||||
* @param testClass the test class that is using the application context(s)
|
||||
* @since 7.0
|
||||
* @see #unregisterContextUsage(MergedContextConfiguration, Class)
|
||||
* @see #getContextUsageCount()
|
||||
*/
|
||||
default void registerContextUsage(MergedContextConfiguration key, Class<?> testClass) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister usage of the {@link ApplicationContext} for the supplied
|
||||
* {@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.Lifecycle#stop() stopped}.
|
||||
* <p>The default implementation of this method does nothing. Concrete
|
||||
* implementations are therefore highly encouraged to override this
|
||||
* method, {@link #registerContextUsage(MergedContextConfiguration, Class)},
|
||||
* and {@link #getContextUsageCount()} with appropriate behavior. Note that
|
||||
* the standard {@code ContextContext} implementation in Spring overrides
|
||||
* these methods appropriately.
|
||||
* @param key the context key; never {@code null}
|
||||
* @param testClass the test class that is no longer using the application context(s)
|
||||
* @since 7.0
|
||||
* @see #registerContextUsage(MergedContextConfiguration, Class)
|
||||
* @see #getContextUsageCount()
|
||||
*/
|
||||
default void unregisterContextUsage(MergedContextConfiguration key, Class<?> testClass) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the number of contexts within the cache that are currently in use.
|
||||
* <p>The default implementation of this method always returns {@code 0}.
|
||||
* Concrete implementations are therefore highly encouraged to override this
|
||||
* method, {@link #registerContextUsage(MergedContextConfiguration, Class)},
|
||||
* and {@link #unregisterContextUsage(MergedContextConfiguration, Class)} with
|
||||
* appropriate behavior. Note that the standard {@code ContextContext}
|
||||
* implementation in Spring overrides these methods appropriately.
|
||||
* @since 7.0
|
||||
* @see #registerContextUsage(MergedContextConfiguration, Class)
|
||||
* @see #unregisterContextUsage(MergedContextConfiguration, Class)
|
||||
*/
|
||||
default int getContextUsageCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,6 +271,7 @@ public interface ContextCache {
|
||||
* <ul>
|
||||
* <li>name of the concrete {@code ContextCache} implementation</li>
|
||||
* <li>{@linkplain #size}</li>
|
||||
* <li>{@linkplain #getContextUsageCount() context usage count}</li>
|
||||
* <li>{@linkplain #getParentContextCount() parent context count}</li>
|
||||
* <li>{@linkplain #getHitCount() hit count}</li>
|
||||
* <li>{@linkplain #getMissCount() miss count}</li>
|
||||
|
||||
+21
@@ -58,6 +58,11 @@ import org.springframework.util.Assert;
|
||||
* delegating to {@link ContextCacheUtils#retrieveContextFailureThreshold()} to
|
||||
* obtain the threshold value to use.
|
||||
*
|
||||
* <p>As of Spring Framework 7.0, this class provides support for
|
||||
* {@linkplain #registerContextUsage(MergedContextConfiguration, Class) registering} and
|
||||
* {@linkplain #unregisterContextUsage(MergedContextConfiguration, Class) unregistering}
|
||||
* context usage.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
@@ -204,6 +209,22 @@ public class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContext
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerContextUsage(MergedContextConfiguration mergedConfig, Class<?> testClass) {
|
||||
mergedConfig = replaceIfNecessary(mergedConfig);
|
||||
synchronized (this.contextCache) {
|
||||
this.contextCache.registerContextUsage(mergedConfig, testClass);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterContextUsage(MergedContextConfiguration mergedConfig, Class<?> testClass) {
|
||||
mergedConfig = replaceIfNecessary(mergedConfig);
|
||||
synchronized (this.contextCache) {
|
||||
this.contextCache.unregisterContextUsage(mergedConfig, testClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ContextCache} used by this context loader delegate.
|
||||
*/
|
||||
|
||||
+62
@@ -73,6 +73,13 @@ public class DefaultContextCache implements ContextCache {
|
||||
private final Map<MergedContextConfiguration, Set<MergedContextConfiguration>> hierarchyMap =
|
||||
new ConcurrentHashMap<>(32);
|
||||
|
||||
/**
|
||||
* Map of context keys to active test classes (i.e., test classes that are actively
|
||||
* using the corresponding {@link ApplicationContext}).
|
||||
* @since 7.0
|
||||
*/
|
||||
private final Map<MergedContextConfiguration, Set<Class<?>>> contextUsageMap = new ConcurrentHashMap<>(32);
|
||||
|
||||
/**
|
||||
* Map of context keys to context load failure counts.
|
||||
* @since 6.1
|
||||
@@ -129,10 +136,22 @@ public class DefaultContextCache implements ContextCache {
|
||||
}
|
||||
else {
|
||||
this.hitCount.incrementAndGet();
|
||||
restartContextIfNecessary(context);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
private void restartContextIfNecessary(ApplicationContext context) {
|
||||
// Recurse up the context hierarchy first.
|
||||
ApplicationContext parent = context.getParent();
|
||||
if (parent != null) {
|
||||
restartContextIfNecessary(parent);
|
||||
}
|
||||
if (context instanceof ConfigurableApplicationContext cac && !cac.isRunning()) {
|
||||
cac.restart();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(MergedContextConfiguration key, ApplicationContext context) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
@@ -149,6 +168,41 @@ public class DefaultContextCache implements ContextCache {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerContextUsage(MergedContextConfiguration mergedConfig, Class<?> testClass) {
|
||||
// Recurse up the context hierarchy first.
|
||||
MergedContextConfiguration parent = mergedConfig.getParent();
|
||||
if (parent != null) {
|
||||
registerContextUsage(parent, testClass);
|
||||
}
|
||||
getActiveTestClasses(mergedConfig).add(testClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterContextUsage(MergedContextConfiguration mergedConfig, Class<?> testClass) {
|
||||
ApplicationContext context = this.contextMap.get(mergedConfig);
|
||||
Assert.state(context != null, "ApplicationContext must not be null for: " + mergedConfig);
|
||||
|
||||
Set<Class<?>> activeTestClasses = getActiveTestClasses(mergedConfig);
|
||||
activeTestClasses.remove(testClass);
|
||||
if (activeTestClasses.isEmpty()) {
|
||||
if (context instanceof ConfigurableApplicationContext cac && cac.isRunning()) {
|
||||
cac.stop();
|
||||
}
|
||||
this.contextUsageMap.remove(mergedConfig);
|
||||
}
|
||||
|
||||
// Recurse up the context hierarchy last.
|
||||
MergedContextConfiguration parent = mergedConfig.getParent();
|
||||
if (parent != null) {
|
||||
unregisterContextUsage(parent, testClass);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Class<?>> getActiveTestClasses(MergedContextConfiguration mergedConfig) {
|
||||
return this.contextUsageMap.computeIfAbsent(mergedConfig, mcc -> new HashSet<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(MergedContextConfiguration key, @Nullable HierarchyMode hierarchyMode) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
@@ -199,6 +253,7 @@ public class DefaultContextCache implements ContextCache {
|
||||
// Physically remove and close leaf nodes first (i.e., on the way back up the
|
||||
// stack as opposed to prior to the recursive call).
|
||||
ApplicationContext context = this.contextMap.remove(key);
|
||||
this.contextUsageMap.remove(key);
|
||||
if (context instanceof ConfigurableApplicationContext cac) {
|
||||
cac.close();
|
||||
}
|
||||
@@ -228,6 +283,11 @@ public class DefaultContextCache implements ContextCache {
|
||||
return this.maxSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContextUsageCount() {
|
||||
return this.contextUsageMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParentContextCount() {
|
||||
return this.hierarchyMap.size();
|
||||
@@ -258,6 +318,7 @@ public class DefaultContextCache implements ContextCache {
|
||||
synchronized (this.contextMap) {
|
||||
this.contextMap.clear();
|
||||
this.hierarchyMap.clear();
|
||||
this.contextUsageMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +349,7 @@ public class DefaultContextCache implements ContextCache {
|
||||
return new ToStringCreator(this)
|
||||
.append("size", size())
|
||||
.append("maxSize", getMaxSize())
|
||||
.append("contextUsageCount", getContextUsageCount())
|
||||
.append("parentContextCount", getParentContextCount())
|
||||
.append("hitCount", getHitCount())
|
||||
.append("missCount", getMissCount())
|
||||
|
||||
+16
@@ -136,9 +136,25 @@ public class DefaultTestContext implements TestContext {
|
||||
from the ContextCache due to a maximum cache size policy."""
|
||||
.formatted(this.mergedConfig));
|
||||
}
|
||||
this.cacheAwareContextLoaderDelegate.registerContextUsage(this.mergedConfig, this.testClass);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the {@linkplain ApplicationContext application context} associated
|
||||
* with this test context as <em>unused</em> so that it can be safely
|
||||
* {@linkplain org.springframework.context.Lifecycle#stop() stopped} if no
|
||||
* other test classes are actively using the same application context.
|
||||
* <p>The default implementation delegates to the {@link CacheAwareContextLoaderDelegate}
|
||||
* that was supplied when this {@code TestContext} was constructed.
|
||||
* @since 7.0
|
||||
* @see CacheAwareContextLoaderDelegate#unregisterContextUsage(MergedContextConfiguration, Class)
|
||||
*/
|
||||
@Override
|
||||
public void markApplicationContextUnused() {
|
||||
this.cacheAwareContextLoaderDelegate.unregisterContextUsage(this.mergedConfig, this.testClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the {@linkplain ApplicationContext application context} associated
|
||||
* with this test context as <em>dirty</em> (i.e., by removing it from the
|
||||
|
||||
+16
-16
@@ -64,7 +64,7 @@ class ClassLevelDirtiesContextTestNGTests {
|
||||
// for example, via JUnit's @Suite.
|
||||
cacheHits.set(0);
|
||||
cacheMisses.set(0);
|
||||
assertContextCacheStatistics("BeforeClass", 0, cacheHits.get(), cacheMisses.get());
|
||||
assertContextCacheStatistics("BeforeClass", 0, 0, cacheHits.get(), cacheMisses.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -74,63 +74,63 @@ class ClassLevelDirtiesContextTestNGTests {
|
||||
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with clean test method and default class mode",
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, 0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase.class, 1);
|
||||
assertContextCacheStatistics(
|
||||
"after inherited class-level @DirtiesContext with clean test method and default class mode", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithCleanMethodsAndAfterClassModeTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with clean test method and AFTER_CLASS mode",
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, 0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithCleanMethodsAndAfterClassModeTestCase.class, 1);
|
||||
assertContextCacheStatistics(
|
||||
"after inherited class-level @DirtiesContext with clean test method and AFTER_CLASS mode", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithAfterEachTestMethodModeTestCase.class, 3);
|
||||
assertContextCacheStatistics(
|
||||
"after class-level @DirtiesContext with clean test method and AFTER_EACH_TEST_METHOD mode", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.addAndGet(2));
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.addAndGet(2));
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithAfterEachTestMethodModeTestCase.class, 3);
|
||||
assertContextCacheStatistics(
|
||||
"after inherited class-level @DirtiesContext with clean test method and AFTER_EACH_TEST_METHOD mode", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.addAndGet(2));
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.addAndGet(2));
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0, cacheHits.get(),
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0, 0, cacheHits.get(),
|
||||
cacheMisses.incrementAndGet());
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0, cacheHits.get(),
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0, 0, cacheHits.get(),
|
||||
cacheMisses.incrementAndGet());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after inherited class-level @DirtiesContext with dirty test method", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after inherited class-level @DirtiesContext with dirty test method", 0,
|
||||
cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
0, cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after inherited class-level @DirtiesContext with dirty test method", 0,
|
||||
cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
0, cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithCleanMethodsAndAfterClassModeTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with clean test method and AFTER_CLASS mode",
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, 0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
}
|
||||
|
||||
private void runTestClassAndAssertStats(Class<?> testClass, int expectedTestCount) {
|
||||
@@ -152,12 +152,12 @@ class ClassLevelDirtiesContextTestNGTests {
|
||||
|
||||
private void assertBehaviorForCleanTestCase() {
|
||||
runTestClassAndAssertStats(CleanTestCase.class, 1);
|
||||
assertContextCacheStatistics("after clean test class", 1, cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
assertContextCacheStatistics("after clean test class", 1, 0, cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void verifyFinalCacheState() {
|
||||
assertContextCacheStatistics("AfterClass", 0, cacheHits.get(), cacheMisses.get());
|
||||
assertContextCacheStatistics("AfterClass", 0, 0, cacheHits.get(), cacheMisses.get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+19
-19
@@ -62,81 +62,81 @@ class ClassLevelDirtiesContextTests {
|
||||
// for example, via JUnit's @Suite.
|
||||
cacheHits.set(0);
|
||||
cacheMisses.set(0);
|
||||
assertContextCacheStatistics("BeforeClass", 0, cacheHits.get(), cacheMisses.get());
|
||||
assertContextCacheStatistics("BeforeClass", 0, 0, cacheHits.get(), cacheMisses.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyDirtiesContextBehavior() throws Exception {
|
||||
void verifyDirtiesContextBehavior() {
|
||||
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with clean test method and default class mode",
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, 0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase.class, 1);
|
||||
assertContextCacheStatistics(
|
||||
"after inherited class-level @DirtiesContext with clean test method and default class mode", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithCleanMethodsAndAfterClassModeTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with clean test method and AFTER_CLASS mode",
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, 0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithCleanMethodsAndAfterClassModeTestCase.class, 1);
|
||||
assertContextCacheStatistics(
|
||||
"after inherited class-level @DirtiesContext with clean test method and AFTER_CLASS mode", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithAfterEachTestMethodModeTestCase.class, 3);
|
||||
assertContextCacheStatistics(
|
||||
"after class-level @DirtiesContext with clean test method and AFTER_EACH_TEST_METHOD mode", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.addAndGet(2));
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.addAndGet(2));
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithAfterEachTestMethodModeTestCase.class, 3);
|
||||
assertContextCacheStatistics(
|
||||
"after inherited class-level @DirtiesContext with clean test method and AFTER_EACH_TEST_METHOD mode", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.addAndGet(2));
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.addAndGet(2));
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0, cacheHits.get(),
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0, 0, cacheHits.get(),
|
||||
cacheMisses.incrementAndGet());
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0, cacheHits.get(),
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0, 0, cacheHits.get(),
|
||||
cacheMisses.incrementAndGet());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after inherited class-level @DirtiesContext with dirty test method", 0,
|
||||
cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after inherited class-level @DirtiesContext with dirty test method", 0,
|
||||
cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
0, cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
runTestClassAndAssertStats(InheritedClassLevelDirtiesContextWithDirtyMethodsTestCase.class, 1);
|
||||
assertContextCacheStatistics("after inherited class-level @DirtiesContext with dirty test method", 0,
|
||||
cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
0, cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
assertBehaviorForCleanTestCase();
|
||||
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithCleanMethodsAndAfterClassModeTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with clean test method and AFTER_CLASS mode",
|
||||
0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
0, 0, cacheHits.incrementAndGet(), cacheMisses.get());
|
||||
}
|
||||
|
||||
private void assertBehaviorForCleanTestCase() throws Exception {
|
||||
private void assertBehaviorForCleanTestCase() {
|
||||
runTestClassAndAssertStats(CleanTestCase.class, 1);
|
||||
assertContextCacheStatistics("after clean test class", 1, cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
assertContextCacheStatistics("after clean test class", 1, 0, cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
}
|
||||
|
||||
private void runTestClassAndAssertStats(Class<?> testClass, int expectedTestCount) throws Exception {
|
||||
private void runTestClassAndAssertStats(Class<?> testClass, int expectedTestCount) {
|
||||
EngineTestKit.engine("junit-jupiter")
|
||||
.selectors(selectClass(testClass))
|
||||
.execute()
|
||||
@@ -151,7 +151,7 @@ class ClassLevelDirtiesContextTests {
|
||||
|
||||
@AfterAll
|
||||
static void verifyFinalCacheState() {
|
||||
assertContextCacheStatistics("AfterClass", 0, cacheHits.get(), cacheMisses.get());
|
||||
assertContextCacheStatistics("AfterClass", 0, 0, cacheHits.get(), cacheMisses.get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+28
-8
@@ -44,7 +44,17 @@ public class ContextCacheTestUtils {
|
||||
* @param expectedMissCount the expected miss count
|
||||
*/
|
||||
public static void assertContextCacheStatistics(int expectedSize, int expectedHitCount, int expectedMissCount) {
|
||||
assertContextCacheStatistics(null, expectedSize, expectedHitCount, expectedMissCount);
|
||||
ContextCache contextCache = DefaultCacheAwareContextLoaderDelegate.defaultContextCache;
|
||||
String context = (StringUtils.hasText((String) null) ? " (" + null + ")" : "");
|
||||
|
||||
assertSoftly(softly -> {
|
||||
softly.assertThat(contextCache.size())
|
||||
.as("contexts in cache" + context).isEqualTo(expectedSize);
|
||||
softly.assertThat(contextCache.getHitCount())
|
||||
.as("cache hits" + context).isEqualTo(expectedHitCount);
|
||||
softly.assertThat(contextCache.getMissCount())
|
||||
.as("cache misses" + context).isEqualTo(expectedMissCount);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,13 +62,16 @@ public class ContextCacheTestUtils {
|
||||
*
|
||||
* @param usageScenario the scenario in which the statistics are used
|
||||
* @param expectedSize the expected number of contexts in the cache
|
||||
* @param expectedContextUsageCount the expected number of actively running
|
||||
* contexts in the cache
|
||||
* @param expectedHitCount the expected hit count
|
||||
* @param expectedMissCount the expected miss count
|
||||
*/
|
||||
public static void assertContextCacheStatistics(String usageScenario, int expectedSize, int expectedHitCount,
|
||||
int expectedMissCount) {
|
||||
public static void assertContextCacheStatistics(String usageScenario, int expectedSize,
|
||||
int expectedContextUsageCount, int expectedHitCount, int expectedMissCount) {
|
||||
|
||||
assertContextCacheStatistics(DefaultCacheAwareContextLoaderDelegate.defaultContextCache, usageScenario,
|
||||
expectedSize, expectedHitCount, expectedMissCount);
|
||||
expectedSize, expectedContextUsageCount, expectedHitCount, expectedMissCount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,18 +80,25 @@ public class ContextCacheTestUtils {
|
||||
* @param contextCache the cache to assert against
|
||||
* @param usageScenario the scenario in which the statistics are used
|
||||
* @param expectedSize the expected number of contexts in the cache
|
||||
* @param expectedContextUsageCount the expected number of actively running
|
||||
* contexts in the cache
|
||||
* @param expectedHitCount the expected hit count
|
||||
* @param expectedMissCount the expected miss count
|
||||
*/
|
||||
public static void assertContextCacheStatistics(ContextCache contextCache, String usageScenario,
|
||||
int expectedSize, int expectedHitCount, int expectedMissCount) {
|
||||
int expectedSize, int expectedContextUsageCount, int expectedHitCount, int expectedMissCount) {
|
||||
|
||||
String context = (StringUtils.hasText(usageScenario) ? " (" + usageScenario + ")" : "");
|
||||
|
||||
assertSoftly(softly -> {
|
||||
softly.assertThat(contextCache.size()).as("contexts in cache" + context).isEqualTo(expectedSize);
|
||||
softly.assertThat(contextCache.getHitCount()).as("cache hits" + context).isEqualTo(expectedHitCount);
|
||||
softly.assertThat(contextCache.getMissCount()).as("cache misses" + context).isEqualTo(expectedMissCount);
|
||||
softly.assertThat(contextCache.size())
|
||||
.as("contexts in cache" + context).isEqualTo(expectedSize);
|
||||
softly.assertThat(contextCache.getContextUsageCount())
|
||||
.as("active contexts in cache" + context).isEqualTo(expectedContextUsageCount);
|
||||
softly.assertThat(contextCache.getHitCount())
|
||||
.as("cache hits" + context).isEqualTo(expectedHitCount);
|
||||
softly.assertThat(contextCache.getMissCount())
|
||||
.as("cache misses" + context).isEqualTo(expectedMissCount);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+87
-103
@@ -19,13 +19,11 @@ package org.springframework.test.context.cache;
|
||||
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.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ActiveProfilesResolver;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestContextTestUtils;
|
||||
@@ -52,7 +50,7 @@ class ContextCacheTests {
|
||||
|
||||
@BeforeEach
|
||||
void initialCacheState() {
|
||||
assertContextCacheStatistics(contextCache, "initial state", 0, 0, 0);
|
||||
assertContextCacheStatistics(contextCache, "initial state", 0, 0, 0, 0);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@@ -64,38 +62,39 @@ class ContextCacheTests {
|
||||
return (MergedContextConfiguration) ReflectionTestUtils.getField(testContext, "mergedConfig");
|
||||
}
|
||||
|
||||
private ApplicationContext loadContext(Class<?> testClass) {
|
||||
private void loadCtxAndAssertStats(Class<?> testClass, int expectedSize, int expectedActiveContextsCount,
|
||||
int expectedHitCount, int expectedMissCount) {
|
||||
|
||||
TestContext testContext = TestContextTestUtils.buildTestContext(testClass, contextCache);
|
||||
return testContext.getApplicationContext();
|
||||
|
||||
assertThat(testContext.getApplicationContext()).isNotNull();
|
||||
assertContextCacheStatistics(contextCache, testClass.getName(), expectedSize, expectedActiveContextsCount,
|
||||
expectedHitCount, expectedMissCount);
|
||||
testContext.markApplicationContextUnused();
|
||||
}
|
||||
|
||||
private void loadCtxAndAssertStats(Class<?> testClass, int expectedSize, int expectedHitCount, int expectedMissCount) {
|
||||
assertThat(loadContext(testClass)).isNotNull();
|
||||
assertContextCacheStatistics(contextCache, testClass.getName(), expectedSize, expectedHitCount,
|
||||
expectedMissCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyCacheKeyIsBasedOnContextLoader() {
|
||||
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 1, 0, 1);
|
||||
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 1, 1, 1);
|
||||
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 1, 2);
|
||||
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 2, 2);
|
||||
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 2, 3, 2);
|
||||
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 4, 2);
|
||||
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 1, 1, 0, 1);
|
||||
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 1, 1, 1, 1);
|
||||
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 1, 1, 2);
|
||||
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 1, 2, 2);
|
||||
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 2, 1, 3, 2);
|
||||
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 1, 4, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyCacheKeyIsBasedOnActiveProfiles() {
|
||||
int size = 0, hit = 0, miss = 0;
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, ++size, hit, ++miss);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, size, ++hit, miss);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, ++size, 1, hit, ++miss);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, size, 1, ++hit, miss);
|
||||
// Profiles {foo, bar} should not hash to the same as {bar,foo}
|
||||
loadCtxAndAssertStats(BarFooProfilesTestCase.class, ++size, hit, ++miss);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, size, ++hit, miss);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, size, ++hit, miss);
|
||||
loadCtxAndAssertStats(BarFooProfilesTestCase.class, size, ++hit, miss);
|
||||
loadCtxAndAssertStats(FooBarActiveProfilesResolverTestCase.class, size, ++hit, miss);
|
||||
loadCtxAndAssertStats(BarFooProfilesTestCase.class, ++size, 1, hit, ++miss);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, size, 1, ++hit, miss);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, size, 1, ++hit, miss);
|
||||
loadCtxAndAssertStats(BarFooProfilesTestCase.class, size, 1, ++hit, miss);
|
||||
loadCtxAndAssertStats(FooBarActiveProfilesResolverTestCase.class, size, 1, ++hit, miss);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,24 +104,24 @@ class ContextCacheTests {
|
||||
int misses = 0;
|
||||
|
||||
// Level 1
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel1TestCase.class, ++size, hits, ++misses);
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel1TestCase.class, size, ++hits, misses);
|
||||
loadCtxAndAssertStats(ContextHierarchyLevel1TestCase.class, ++size, 1, hits, ++misses);
|
||||
loadCtxAndAssertStats(ContextHierarchyLevel1TestCase.class, size, 1, ++hits, misses);
|
||||
|
||||
// Level 2
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel2TestCase.class, ++size /* L2 */, ++hits /* L1 */,
|
||||
loadCtxAndAssertStats(ContextHierarchyLevel2TestCase.class, ++size /* L2 */, 2, ++hits /* L1 */,
|
||||
++misses /* L2 */);
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel2TestCase.class, size, ++hits /* L2 */, misses);
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel2TestCase.class, size, ++hits /* L2 */, misses);
|
||||
loadCtxAndAssertStats(ContextHierarchyLevel2TestCase.class, size, 2, ++hits /* L2 */, misses);
|
||||
loadCtxAndAssertStats(ContextHierarchyLevel2TestCase.class, size, 2, ++hits /* L2 */, misses);
|
||||
|
||||
// Level 3-A
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3aTestCase.class, ++size /* L3A */, ++hits /* L2 */,
|
||||
loadCtxAndAssertStats(ContextHierarchyLevel3a1TestCase.class, ++size /* L3A */, 3, ++hits /* L2 */,
|
||||
++misses /* L3A */);
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3aTestCase.class, size, ++hits /* L3A */, misses);
|
||||
loadCtxAndAssertStats(ContextHierarchyLevel3a1TestCase.class, size, 3, ++hits /* L3A */, misses);
|
||||
|
||||
// Level 3-B
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3bTestCase.class, ++size /* L3B */, ++hits /* L2 */,
|
||||
loadCtxAndAssertStats(ContextHierarchyLevel3bTestCase.class, ++size /* L3B */, 3, ++hits /* L2 */,
|
||||
++misses /* L3B */);
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3bTestCase.class, size, ++hits /* L3B */, misses);
|
||||
loadCtxAndAssertStats(ContextHierarchyLevel3bTestCase.class, size, 3, ++hits /* L3B */, misses);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,23 +129,27 @@ class ContextCacheTests {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3a1TestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
testContext3a.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 0, 3);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 3, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
testContext3b.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 0, 1, 4);
|
||||
|
||||
// Remove Level 1
|
||||
// Should also remove Levels 2, 3-A, and 3-B, leaving nothing.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent().getParent(),
|
||||
HierarchyMode.CURRENT_LEVEL);
|
||||
assertContextCacheStatistics(contextCache, "removed level 1", 0, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "removed level 1", 0, 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@@ -155,23 +158,27 @@ class ContextCacheTests {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3a1TestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
testContext3a.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 0, 3);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 3, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
testContext3b.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 0, 1, 4);
|
||||
|
||||
// Remove Level 1
|
||||
// Should also remove Levels 2, 3-A, and 3-B, leaving nothing.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent().getParent(),
|
||||
HierarchyMode.EXHAUSTIVE);
|
||||
assertContextCacheStatistics(contextCache, "removed level 1", 0, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "removed level 1", 0, 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@@ -180,24 +187,28 @@ class ContextCacheTests {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3a1TestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
testContext3a.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 0, 3);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 3, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
testContext3b.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 0, 1, 4);
|
||||
|
||||
// Remove Level 2
|
||||
// Should also remove Levels 3-A and 3-B, leaving only Level 1 as a context in the
|
||||
// cache but also removing the Level 1 hierarchy since all children have been
|
||||
// removed.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent(), HierarchyMode.CURRENT_LEVEL);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 1, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 1, 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@@ -206,22 +217,26 @@ class ContextCacheTests {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3a1TestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
testContext3a.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 0, 3);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 3, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
testContext3b.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 0, 1, 4);
|
||||
|
||||
// Remove Level 2
|
||||
// Should wipe the cache
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent(), HierarchyMode.EXHAUSTIVE);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 0, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 0, 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@@ -230,27 +245,31 @@ class ContextCacheTests {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3a1TestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
testContext3a.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 0, 3);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 3, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
testContext3b.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 0, 1, 4);
|
||||
|
||||
// Remove Level 3-A
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a), HierarchyMode.CURRENT_LEVEL);
|
||||
assertContextCacheStatistics(contextCache, "removed level 3-A", 3, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "removed level 3-A", 3, 0, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Remove Level 2
|
||||
// Should also remove Level 3-B, leaving only Level 1.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3b).getParent(), HierarchyMode.CURRENT_LEVEL);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 1, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 1, 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@@ -259,29 +278,33 @@ class ContextCacheTests {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3a1TestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
testContext3a.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 0, 3);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
ContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 3, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
testContext3b.markApplicationContextUnused();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 0, 1, 4);
|
||||
|
||||
// Remove Level 3-A
|
||||
// Should wipe the cache.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a), HierarchyMode.EXHAUSTIVE);
|
||||
assertContextCacheStatistics(contextCache, "removed level 3-A", 0, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "removed level 3-A", 0, 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
|
||||
// Remove Level 2
|
||||
// Should not actually do anything since the cache was cleared in the
|
||||
// previous step. So the stats should remain the same.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3b).getParent(), HierarchyMode.EXHAUSTIVE);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 0, 1, 4);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 0, 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@@ -324,43 +347,4 @@ class ContextCacheTests {
|
||||
private static class FooBarActiveProfilesResolverTestCase {
|
||||
}
|
||||
|
||||
@ContextHierarchy({ @ContextConfiguration })
|
||||
private static class ClassHierarchyContextHierarchyLevel1TestCase {
|
||||
|
||||
@Configuration
|
||||
static class Level1Config {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ContextHierarchy({ @ContextConfiguration })
|
||||
private static class ClassHierarchyContextHierarchyLevel2TestCase extends
|
||||
ClassHierarchyContextHierarchyLevel1TestCase {
|
||||
|
||||
@Configuration
|
||||
static class Level2Config {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ContextHierarchy({ @ContextConfiguration })
|
||||
private static class ClassHierarchyContextHierarchyLevel3aTestCase extends
|
||||
ClassHierarchyContextHierarchyLevel2TestCase {
|
||||
|
||||
@Configuration
|
||||
static class Level3aConfig {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ContextHierarchy({ @ContextConfiguration })
|
||||
private static class ClassHierarchyContextHierarchyLevel3bTestCase extends
|
||||
ClassHierarchyContextHierarchyLevel2TestCase {
|
||||
|
||||
@Configuration
|
||||
static class Level3bConfig {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
spring-test/src/test/java/org/springframework/test/context/cache/ContextHierarchyLevel1TestCase.java
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.Test;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.context.ContextCustomizerFactories;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 7.0
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@ContextCustomizerFactories(DisplayNameCustomizerFactory.class)
|
||||
class ContextHierarchyLevel1TestCase {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(EventTracker.class)
|
||||
static class Level1Config {
|
||||
}
|
||||
|
||||
}
|
||||
spring-test/src/test/java/org/springframework/test/context/cache/ContextHierarchyLevel2TestCase.java
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 7.0
|
||||
*/
|
||||
@ContextHierarchy(@ContextConfiguration)
|
||||
class ContextHierarchyLevel2TestCase extends ContextHierarchyLevel1TestCase {
|
||||
|
||||
@Configuration
|
||||
static class Level2Config {
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 7.0
|
||||
*/
|
||||
@ContextHierarchy(@ContextConfiguration)
|
||||
class ContextHierarchyLevel3a1TestCase extends ContextHierarchyLevel2TestCase {
|
||||
|
||||
@Configuration
|
||||
static class Level3a1Config {
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 7.0
|
||||
*/
|
||||
@ContextHierarchy(@ContextConfiguration(classes = ContextHierarchyLevel3a1TestCase.Level3a1Config.class))
|
||||
class ContextHierarchyLevel3a2TestCase extends ContextHierarchyLevel2TestCase {
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 7.0
|
||||
*/
|
||||
@ContextHierarchy(@ContextConfiguration)
|
||||
class ContextHierarchyLevel3bTestCase extends ContextHierarchyLevel2TestCase {
|
||||
|
||||
@Configuration
|
||||
static class Level3bConfig {
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 7.0
|
||||
*/
|
||||
class DisplayNameCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public @Nullable ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> __) {
|
||||
|
||||
return (context, mergedConfig) ->
|
||||
((AbstractApplicationContext) context).setDisplayName(mergedConfig.getTestClass().getSimpleName());
|
||||
}
|
||||
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.event.ApplicationContextEvent;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.ContextRestartedEvent;
|
||||
import org.springframework.context.event.ContextStoppedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.test.context.event.TestContextEvent;
|
||||
import org.springframework.test.context.event.annotation.AfterTestClass;
|
||||
import org.springframework.test.context.event.annotation.BeforeTestClass;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 7.0
|
||||
*/
|
||||
@Component
|
||||
class EventTracker {
|
||||
|
||||
static final List<String> events = new ArrayList<>();
|
||||
|
||||
|
||||
@EventListener(ContextRefreshedEvent.class)
|
||||
void contextRefreshed(ContextRefreshedEvent event) {
|
||||
trackApplicationContextEvent(event);
|
||||
}
|
||||
|
||||
@EventListener(ContextRestartedEvent.class)
|
||||
void contextRestarted(ContextRestartedEvent event) {
|
||||
trackApplicationContextEvent(event);
|
||||
}
|
||||
|
||||
@EventListener(ContextStoppedEvent.class)
|
||||
void contextStopped(ContextStoppedEvent event) {
|
||||
trackApplicationContextEvent(event);
|
||||
}
|
||||
|
||||
@EventListener(ContextClosedEvent.class)
|
||||
void contextClosed(ContextClosedEvent event) {
|
||||
trackApplicationContextEvent(event);
|
||||
}
|
||||
|
||||
@BeforeTestClass
|
||||
void beforeTestClass(TestContextEvent event) {
|
||||
trackTestContextEvent(event);
|
||||
}
|
||||
|
||||
@AfterTestClass
|
||||
void afterTestClass(TestContextEvent event) {
|
||||
trackTestContextEvent(event);
|
||||
}
|
||||
|
||||
private static void trackApplicationContextEvent(ApplicationContextEvent event) {
|
||||
events.add(eventName(event) + ":" + event.getSource().getDisplayName());
|
||||
}
|
||||
|
||||
private static void trackTestContextEvent(TestContextEvent event) {
|
||||
events.add(eventName(event) + ":" +
|
||||
event.getSource().getTestClass().getSimpleName());
|
||||
}
|
||||
|
||||
private static String eventName(ApplicationEvent event) {
|
||||
String name = event.getClass().getSimpleName();
|
||||
return name.substring(0, name.length() - "Event".length());
|
||||
}
|
||||
|
||||
}
|
||||
+5
-5
@@ -61,19 +61,19 @@ class SpringExtensionContextCacheTests {
|
||||
static void verifyInitialCacheState() {
|
||||
dirtiedApplicationContext = null;
|
||||
resetContextCache();
|
||||
assertContextCacheStatistics("BeforeClass", 0, 0, 0);
|
||||
assertContextCacheStatistics("BeforeClass", 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void verifyFinalCacheState() {
|
||||
assertContextCacheStatistics("AfterClass", 1, 1, 2);
|
||||
assertContextCacheStatistics("AfterClass", 1, 1, 1, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
@Order(1)
|
||||
void dirtyContext() {
|
||||
assertContextCacheStatistics("dirtyContext()", 1, 0, 1);
|
||||
assertContextCacheStatistics("dirtyContext()", 1, 1, 0, 1);
|
||||
assertThat(this.applicationContext).as("The application context should have been autowired.").isNotNull();
|
||||
SpringExtensionContextCacheTests.dirtiedApplicationContext = this.applicationContext;
|
||||
}
|
||||
@@ -81,7 +81,7 @@ class SpringExtensionContextCacheTests {
|
||||
@Test
|
||||
@Order(2)
|
||||
void verifyContextDirty() {
|
||||
assertContextCacheStatistics("verifyContextWasDirtied()", 1, 0, 2);
|
||||
assertContextCacheStatistics("verifyContextWasDirtied()", 1, 1, 0, 2);
|
||||
assertThat(this.applicationContext).as("The application context should have been autowired.").isNotNull();
|
||||
assertThat(this.applicationContext).as("The application context should have been 'dirtied'.").isNotSameAs(SpringExtensionContextCacheTests.dirtiedApplicationContext);
|
||||
SpringExtensionContextCacheTests.dirtiedApplicationContext = this.applicationContext;
|
||||
@@ -90,7 +90,7 @@ class SpringExtensionContextCacheTests {
|
||||
@Test
|
||||
@Order(3)
|
||||
void verifyContextNotDirty() {
|
||||
assertContextCacheStatistics("verifyContextWasNotDirtied()", 1, 1, 2);
|
||||
assertContextCacheStatistics("verifyContextWasNotDirtied()", 1, 1, 1, 2);
|
||||
assertThat(this.applicationContext).as("The application context should have been autowired.").isNotNull();
|
||||
assertThat(this.applicationContext).as("The application context should NOT have been 'dirtied'.").isSameAs(SpringExtensionContextCacheTests.dirtiedApplicationContext);
|
||||
}
|
||||
|
||||
spring-test/src/test/java/org/springframework/test/context/cache/UnusedContextsIntegrationTests.java
Vendored
+308
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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 java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.ClassOrderer;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestClassOrder;
|
||||
import org.junit.platform.engine.discovery.ClassSelector;
|
||||
import org.junit.platform.engine.discovery.DiscoverySelectors;
|
||||
import org.junit.platform.testkit.engine.EngineTestKit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextCustomizerFactories;
|
||||
import org.springframework.test.context.NestedTestConfiguration;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.INHERIT;
|
||||
import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.OVERRIDE;
|
||||
|
||||
/**
|
||||
* Integration tests for pausing and restarting "unused" contexts.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 7.0
|
||||
* @see TestContext#markApplicationContextUnused()
|
||||
*/
|
||||
class UnusedContextsIntegrationTests {
|
||||
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void clearApplicationEvents() {
|
||||
EventTracker.events.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
void topLevelTestClassesWithSharedApplicationContext() {
|
||||
runTestClasses(5, TestCase1.class, TestCase2.class, TestCase3.class, TestCase4.class, TestCase5.class);
|
||||
|
||||
assertThat(EventTracker.events).containsExactly(
|
||||
|
||||
// --- TestCase1 -----------------------------------------------
|
||||
// Refreshed instead of Restarted, since this is the first time
|
||||
// the context is loaded.
|
||||
"ContextRefreshed:TestCase1",
|
||||
// No BeforeTestClass, since EventPublishingTestExecutionListener
|
||||
// only publishes events for a context that has already been loaded.
|
||||
"AfterTestClass:TestCase1",
|
||||
"ContextStopped:TestCase1",
|
||||
|
||||
// --- TestCase2 -----------------------------------------------
|
||||
"ContextRestarted:TestCase1",
|
||||
"BeforeTestClass:TestCase2",
|
||||
"AfterTestClass:TestCase2",
|
||||
"ContextStopped:TestCase1",
|
||||
|
||||
// --- TestCase3 -----------------------------------------------
|
||||
"ContextRestarted:TestCase1",
|
||||
"BeforeTestClass:TestCase3",
|
||||
"AfterTestClass:TestCase3",
|
||||
// Closed instead of Stopped, since TestCase3 uses @DirtiesContext
|
||||
"ContextClosed:TestCase1",
|
||||
|
||||
// --- TestCase4 -----------------------------------------------
|
||||
// Refreshed instead of Restarted, since TestCase3 uses @DirtiesContext
|
||||
"ContextRefreshed:TestCase4",
|
||||
// No BeforeTestClass, since EventPublishingTestExecutionListener
|
||||
// only publishes events for a context that has already been loaded.
|
||||
"AfterTestClass:TestCase4",
|
||||
"ContextStopped:TestCase4",
|
||||
|
||||
// --- TestCase5 -----------------------------------------------
|
||||
"ContextRestarted:TestCase4",
|
||||
"BeforeTestClass:TestCase5",
|
||||
"AfterTestClass:TestCase5",
|
||||
"ContextStopped:TestCase4"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassesInNestedTestHierarchy() {
|
||||
runTestClasses(5, EnclosingTestCase.class);
|
||||
|
||||
assertThat(EventTracker.events).containsExactly(
|
||||
|
||||
// --- EnclosingTestCase -------------------------------------------
|
||||
"ContextRefreshed:EnclosingTestCase",
|
||||
// No BeforeTestClass, since EventPublishingTestExecutionListener
|
||||
// only publishes events for a context that has already been loaded.
|
||||
|
||||
// --- NestedTestCase ------------------------------------------
|
||||
// No Refreshed or Restarted event, since NestedTestCase shares the
|
||||
// active context used by EnclosingTestCase.
|
||||
"BeforeTestClass:NestedTestCase",
|
||||
|
||||
// --- OverridingNestedTestCase1 ---------------------------
|
||||
"ContextRefreshed:OverridingNestedTestCase1",
|
||||
// No BeforeTestClass, since EventPublishingTestExecutionListener
|
||||
// only publishes events for a context that has already been loaded.
|
||||
|
||||
// --- InheritingNestedTestCase ------------------------
|
||||
// No Refreshed or Restarted event, since InheritingNestedTestCase
|
||||
// shares the active context used by OverridingNestedTestCase1.
|
||||
"BeforeTestClass:InheritingNestedTestCase",
|
||||
"AfterTestClass:InheritingNestedTestCase",
|
||||
// No Stopped event, since OverridingNestedTestCase1 is still
|
||||
// using the context
|
||||
|
||||
"AfterTestClass:OverridingNestedTestCase1",
|
||||
"ContextStopped:OverridingNestedTestCase1",
|
||||
|
||||
// --- OverridingNestedTestCase2 ---------------------------
|
||||
"ContextRestarted:OverridingNestedTestCase1",
|
||||
"BeforeTestClass:OverridingNestedTestCase2",
|
||||
"AfterTestClass:OverridingNestedTestCase2",
|
||||
"ContextStopped:OverridingNestedTestCase1",
|
||||
|
||||
"AfterTestClass:NestedTestCase",
|
||||
// No Stopped event, since EnclosingTestCase is still using the context
|
||||
|
||||
"AfterTestClass:EnclosingTestCase",
|
||||
"ContextStopped:EnclosingTestCase"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassesWithContextHierarchies() {
|
||||
runTestClasses(5,
|
||||
ContextHierarchyLevel1TestCase.class,
|
||||
ContextHierarchyLevel2TestCase.class,
|
||||
ContextHierarchyLevel3a1TestCase.class,
|
||||
ContextHierarchyLevel3a2TestCase.class,
|
||||
ContextHierarchyLevel3bTestCase.class
|
||||
);
|
||||
|
||||
assertThat(EventTracker.events).containsExactly(
|
||||
|
||||
// --- ContextHierarchyLevel1TestCase ------------------------------
|
||||
"ContextRefreshed:ContextHierarchyLevel1TestCase",
|
||||
"AfterTestClass:ContextHierarchyLevel1TestCase",
|
||||
"ContextStopped:ContextHierarchyLevel1TestCase",
|
||||
|
||||
// --- ContextHierarchyLevel2TestCase ------------------------------
|
||||
"ContextRestarted:ContextHierarchyLevel1TestCase",
|
||||
"ContextRefreshed:ContextHierarchyLevel2TestCase",
|
||||
"AfterTestClass:ContextHierarchyLevel2TestCase",
|
||||
"ContextStopped:ContextHierarchyLevel2TestCase",
|
||||
"ContextStopped:ContextHierarchyLevel1TestCase",
|
||||
|
||||
// --- ContextHierarchyLevel3a1TestCase -----------------------------
|
||||
"ContextRestarted:ContextHierarchyLevel1TestCase",
|
||||
"ContextRestarted:ContextHierarchyLevel2TestCase",
|
||||
"ContextRefreshed:ContextHierarchyLevel3a1TestCase",
|
||||
"AfterTestClass:ContextHierarchyLevel3a1TestCase",
|
||||
"ContextStopped:ContextHierarchyLevel3a1TestCase",
|
||||
"ContextStopped:ContextHierarchyLevel2TestCase",
|
||||
"ContextStopped:ContextHierarchyLevel1TestCase",
|
||||
|
||||
// --- ContextHierarchyLevel3a2TestCase -----------------------------
|
||||
"ContextRestarted:ContextHierarchyLevel1TestCase",
|
||||
"ContextRestarted:ContextHierarchyLevel2TestCase",
|
||||
"ContextRestarted:ContextHierarchyLevel3a1TestCase",
|
||||
"BeforeTestClass:ContextHierarchyLevel3a2TestCase",
|
||||
"AfterTestClass:ContextHierarchyLevel3a2TestCase",
|
||||
"ContextStopped:ContextHierarchyLevel3a1TestCase",
|
||||
"ContextStopped:ContextHierarchyLevel2TestCase",
|
||||
"ContextStopped:ContextHierarchyLevel1TestCase",
|
||||
|
||||
// --- ContextHierarchyLevel3bTestCase -----------------------------
|
||||
"ContextRestarted:ContextHierarchyLevel1TestCase",
|
||||
"ContextRestarted:ContextHierarchyLevel2TestCase",
|
||||
"ContextRefreshed:ContextHierarchyLevel3bTestCase",
|
||||
"AfterTestClass:ContextHierarchyLevel3bTestCase",
|
||||
"ContextStopped:ContextHierarchyLevel3bTestCase",
|
||||
"ContextStopped:ContextHierarchyLevel2TestCase",
|
||||
"ContextStopped:ContextHierarchyLevel1TestCase"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private static void runTestClasses(int expectedTestCount, Class<?>... classes) {
|
||||
EngineTestKit.engine("junit-jupiter")
|
||||
.selectors(selectClasses(classes))
|
||||
.execute()
|
||||
.testEvents()
|
||||
.assertStatistics(stats -> stats.started(expectedTestCount).succeeded(expectedTestCount));
|
||||
}
|
||||
|
||||
private static ClassSelector[] selectClasses(Class<?>... classes) {
|
||||
return Arrays.stream(classes).map(DiscoverySelectors::selectClass).toArray(ClassSelector[]::new);
|
||||
}
|
||||
|
||||
|
||||
@SpringJUnitConfig(EventTracker.class)
|
||||
@ContextCustomizerFactories(DisplayNameCustomizerFactory.class)
|
||||
private abstract static class AbstractTestCase {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
static class TestCase1 extends AbstractTestCase {
|
||||
}
|
||||
|
||||
static class TestCase2 extends AbstractTestCase {
|
||||
}
|
||||
|
||||
@DirtiesContext
|
||||
static class TestCase3 extends AbstractTestCase {
|
||||
}
|
||||
|
||||
static class TestCase4 extends AbstractTestCase {
|
||||
}
|
||||
|
||||
static class TestCase5 extends AbstractTestCase {
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(EventTracker.class)
|
||||
@ContextCustomizerFactories(DisplayNameCustomizerFactory.class)
|
||||
@TestPropertySource(properties = "magicKey = puzzle")
|
||||
static class EnclosingTestCase {
|
||||
|
||||
@Test
|
||||
void test(@Value("${magicKey}") String magicKey) {
|
||||
assertThat(magicKey).isEqualTo("puzzle");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestClassOrder(ClassOrderer.OrderAnnotation.class)
|
||||
class NestedTestCase {
|
||||
|
||||
@Test
|
||||
void test(@Value("${magicKey}") String magicKey) {
|
||||
assertThat(magicKey).isEqualTo("puzzle");
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates configuration of {@link OverridingNestedTestCase2}.
|
||||
*/
|
||||
@Nested
|
||||
@Order(1)
|
||||
@NestedTestConfiguration(OVERRIDE)
|
||||
@SpringJUnitConfig(EventTracker.class)
|
||||
@ContextCustomizerFactories(DisplayNameCustomizerFactory.class)
|
||||
@TestPropertySource(properties = "magicKey = enigma")
|
||||
class OverridingNestedTestCase1 {
|
||||
|
||||
@Test
|
||||
void test(@Value("${magicKey}") String magicKey) {
|
||||
assertThat(magicKey).isEqualTo("enigma");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@NestedTestConfiguration(INHERIT)
|
||||
class InheritingNestedTestCase {
|
||||
|
||||
@Test
|
||||
void test(@Value("${magicKey}") String magicKey) {
|
||||
assertThat(magicKey).isEqualTo("enigma");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates configuration of {@link OverridingNestedTestCase1}.
|
||||
*/
|
||||
@Nested
|
||||
@Order(2)
|
||||
@NestedTestConfiguration(OVERRIDE)
|
||||
@SpringJUnitConfig(EventTracker.class)
|
||||
@ContextCustomizerFactories(DisplayNameCustomizerFactory.class)
|
||||
@TestPropertySource(properties = "magicKey = enigma")
|
||||
class OverridingNestedTestCase2 {
|
||||
|
||||
@Test
|
||||
void test(@Value("${magicKey}") String magicKey) {
|
||||
assertThat(magicKey).isEqualTo("enigma");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -55,19 +55,19 @@ class DirtiesContextInterfaceTests {
|
||||
// for example, via JUnit's @Suite.
|
||||
cacheHits.set(0);
|
||||
cacheMisses.set(0);
|
||||
assertContextCacheStatistics("BeforeClass", 0, cacheHits.get(), cacheMisses.get());
|
||||
assertContextCacheStatistics("BeforeClass", 0, 0, cacheHits.get(), cacheMisses.get());
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void verifyFinalCacheState() {
|
||||
assertContextCacheStatistics("AfterClass", 0, cacheHits.get(), cacheMisses.get());
|
||||
assertContextCacheStatistics("AfterClass", 0, 0, cacheHits.get(), cacheMisses.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyDirtiesContextBehavior() throws Exception {
|
||||
runTestClassAndAssertStats(ClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase.class, 1);
|
||||
assertContextCacheStatistics("after class-level @DirtiesContext with clean test method and default class mode",
|
||||
0, cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
0, 0, cacheHits.get(), cacheMisses.incrementAndGet());
|
||||
}
|
||||
|
||||
private void runTestClassAndAssertStats(Class<?> testClass, int expectedTestCount) throws Exception {
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
<suppress files="src[\\/]main[\\/]java[\\/]org[\\/]springframework[\\/]test[\\/]web[\\/]reactive[\\/]server[\\/].+" checks="IllegalImport" id="bannedHamcrestImports"/>
|
||||
<suppress files="src[\\/]main[\\/]java[\\/]org[\\/]springframework[\\/]test[\\/]web[\\/]servlet[\\/]result[\\/].+Matchers" checks="IllegalImport" id="bannedHamcrestImports"/>
|
||||
<!-- spring-test - test -->
|
||||
<suppress files="ContextHierarchyLevel1TestCase" checks="IllegalImport" id="bannedJUnitJupiterImports"/>
|
||||
<suppress files="src[\\/]test[\\/]java[\\/]org[\\/]springframework[\\/]test[\\/].+TestNGTests" checks="IllegalImport" id="bannedTestNGImports"/>
|
||||
<suppress files="src[\\/]test[\\/]java[\\/]org[\\/]springframework[\\/]test[\\/]context[\\/]aot[\\/]samples[\\/]web[\\/].+Tests" checks="IllegalImport" id="bannedHamcrestImports"/>
|
||||
<suppress files="src[\\/]test[\\/]java[\\/]org[\\/]springframework[\\/]test[\\/]context[\\/]junit[\\/]jupiter[\\/]web[\\/].+Tests" checks="IllegalImport" id="bannedHamcrestImports"/>
|
||||
|
||||
Reference in New Issue
Block a user