Merge pull request #780 from sbrannen/SPR-12683
Improve extensibility of TestContext bootstrapping & context caching These commits include numerous refactorings and enhancements to the bootstrapping and context caching mechanisms in the Spring TestContext Framework. Issue: SPR-12683
This commit is contained in:
commit
9e6a5ae4a7
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -28,14 +28,15 @@ package org.springframework.test.context;
|
|||
public interface BootstrapContext {
|
||||
|
||||
/**
|
||||
* Get the {@link Class test class} for this bootstrap context.
|
||||
* Get the {@linkplain Class test class} for this bootstrap context.
|
||||
* @return the test class (never {@code null})
|
||||
*/
|
||||
Class<?> getTestClass();
|
||||
|
||||
/**
|
||||
* Get the {@link CacheAwareContextLoaderDelegate} to use for transparent
|
||||
* interaction with the <em>context cache</em>.
|
||||
* interaction with the {@code ContextCache}.
|
||||
* @return the context loader delegate (never {@code null})
|
||||
*/
|
||||
CacheAwareContextLoaderDelegate getCacheAwareContextLoaderDelegate();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.test.context;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
|
@ -36,6 +38,10 @@ import static org.springframework.core.annotation.AnnotationUtils.*;
|
|||
*/
|
||||
abstract class BootstrapUtils {
|
||||
|
||||
private static final String DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME = "org.springframework.test.context.support.DefaultBootstrapContext";
|
||||
|
||||
private static final String DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_CLASS_NAME = "org.springframework.test.context.support.DefaultCacheAwareContextLoaderDelegate";
|
||||
|
||||
private static final String DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME = "org.springframework.test.context.support.DefaultTestContextBootstrapper";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(BootstrapUtils.class);
|
||||
|
|
@ -45,6 +51,55 @@ abstract class BootstrapUtils {
|
|||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@code BootstrapContext} for the specified {@linkplain Class test class}.
|
||||
*
|
||||
* <p>Uses reflection to create a {@link org.springframework.test.context.support.DefaultBootstrapContext}
|
||||
* that uses a {@link org.springframework.test.context.support.DefaultCacheAwareContextLoaderDelegate}.
|
||||
*
|
||||
* @param testClass the test class for which the bootstrap context should be created
|
||||
* @return a new {@code BootstrapContext}; never {@code null}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static BootstrapContext createBootstrapContext(Class<?> testClass) {
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = createCacheAwareContextLoaderDelegate();
|
||||
|
||||
Class<? extends BootstrapContext> clazz = null;
|
||||
try {
|
||||
clazz = (Class<? extends BootstrapContext>) ClassUtils.forName(DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME,
|
||||
BootstrapUtils.class.getClassLoader());
|
||||
|
||||
Constructor<? extends BootstrapContext> constructor = clazz.getConstructor(Class.class,
|
||||
CacheAwareContextLoaderDelegate.class);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Instantiating BootstrapContext using constructor [%s]", constructor));
|
||||
}
|
||||
return instantiateClass(constructor, testClass, cacheAwareContextLoaderDelegate);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw new IllegalStateException("Could not load BootstrapContext [" + clazz + "]", t);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static CacheAwareContextLoaderDelegate createCacheAwareContextLoaderDelegate() {
|
||||
Class<? extends CacheAwareContextLoaderDelegate> clazz = null;
|
||||
try {
|
||||
clazz = (Class<? extends CacheAwareContextLoaderDelegate>) ClassUtils.forName(
|
||||
DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_CLASS_NAME, BootstrapUtils.class.getClassLoader());
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Instantiating CacheAwareContextLoaderDelegate from class [%s]",
|
||||
clazz.getName()));
|
||||
}
|
||||
return instantiateClass(clazz, CacheAwareContextLoaderDelegate.class);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw new IllegalStateException("Could not load CacheAwareContextLoaderDelegate [" + clazz + "]", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the {@link TestContextBootstrapper} type for the test class in the
|
||||
* supplied {@link BootstrapContext}, instantiate it, and provide it a reference
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -23,7 +23,7 @@ import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
|||
/**
|
||||
* A {@code CacheAwareContextLoaderDelegate} is responsible for {@linkplain
|
||||
* #loadContext loading} and {@linkplain #closeContext closing} application
|
||||
* contexts, interacting transparently with a <em>context cache</em> behind
|
||||
* contexts, interacting transparently with a {@link ContextCache} behind
|
||||
* the scenes.
|
||||
*
|
||||
* <p>Note: {@code CacheAwareContextLoaderDelegate} does not extend the
|
||||
|
|
@ -38,8 +38,10 @@ public interface CacheAwareContextLoaderDelegate {
|
|||
* Load the {@linkplain ApplicationContext application context} for the supplied
|
||||
* {@link MergedContextConfiguration} by delegating to the {@link ContextLoader}
|
||||
* configured in the given {@code MergedContextConfiguration}.
|
||||
* <p>If the context is present in the <em>context cache</em> it will simply
|
||||
* <p>If the context is present in the {@code ContextCache} it will simply
|
||||
* be returned; otherwise, it will be loaded, stored in the cache, and returned.
|
||||
* <p>The cache statistics should be logged by invoking
|
||||
* {@link ContextCache#logStatistics()}.
|
||||
* @param mergedContextConfiguration the merged context configuration to use
|
||||
* to load the application context; never {@code null}
|
||||
* @return the application context
|
||||
|
|
@ -50,7 +52,7 @@ public interface CacheAwareContextLoaderDelegate {
|
|||
|
||||
/**
|
||||
* Remove the {@linkplain ApplicationContext application context} for the
|
||||
* supplied {@link MergedContextConfiguration} from the <em>context cache</em>
|
||||
* supplied {@link MergedContextConfiguration} from the {@code ContextCache}
|
||||
* and {@linkplain ConfigurableApplicationContext#close() close} it if it is
|
||||
* an instance of {@link ConfigurableApplicationContext}.
|
||||
* <p>The semantics of the supplied {@code HierarchyMode} must be honored when
|
||||
|
|
|
|||
|
|
@ -16,145 +16,55 @@
|
|||
|
||||
package org.springframework.test.context;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
/**
|
||||
* Cache for Spring {@link ApplicationContext ApplicationContexts} in a test
|
||||
* environment.
|
||||
* {@code ContextCache} defines the public API for caching Spring
|
||||
* {@link ApplicationContext ApplicationContexts} within the <em>Spring
|
||||
* TestContext Framework</em>.
|
||||
*
|
||||
* <p>A {@code ContextCache} maintains a cache of {@code ApplicationContexts}
|
||||
* keyed by {@link MergedContextConfiguration} instances.
|
||||
*
|
||||
* <h3>Rationale</h3>
|
||||
* <p>Caching has significant performance benefits if initializing the context
|
||||
* takes a considerable about of time. Although initializing a Spring context
|
||||
* itself is very quick, some beans in a context, such as a
|
||||
* {@code LocalSessionFactoryBean} for working with Hibernate, may take some
|
||||
* time to initialize. Hence it often makes sense to perform that initialization
|
||||
* only once per test suite.
|
||||
*
|
||||
* <h3>Implementation Details</h3>
|
||||
* <p>{@code ContextCache} maintains a cache of {@code ApplicationContexts}
|
||||
* keyed by {@link MergedContextConfiguration} instances. Behind the scenes,
|
||||
* Spring's {@link ConcurrentReferenceHashMap} is used to store
|
||||
* {@linkplain java.lang.ref.SoftReference soft references} to cached contexts
|
||||
* and {@code MergedContextConfiguration} instances.
|
||||
* <p>Context caching can have significant performance benefits if context
|
||||
* initialization is complex. So, although initializing a Spring context itself
|
||||
* is typically very quick, some beans in a context — for example, an
|
||||
* in-memory database or a {@code LocalSessionFactoryBean} for working with
|
||||
* Hibernate — may take several seconds to initialize. Hence it often
|
||||
* makes sense to perform that initialization only once per test suite.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see ConcurrentReferenceHashMap
|
||||
* @since 4.2
|
||||
*/
|
||||
class ContextCache {
|
||||
public interface ContextCache {
|
||||
|
||||
/**
|
||||
* Map of context keys to Spring {@code ApplicationContext} instances.
|
||||
* The name of the logging category used for reporting {@code ContextCache}
|
||||
* statistics.
|
||||
*/
|
||||
private final Map<MergedContextConfiguration, ApplicationContext> contextMap =
|
||||
new ConcurrentReferenceHashMap<MergedContextConfiguration, ApplicationContext>(64);
|
||||
public static final String CONTEXT_CACHE_LOGGING_CATEGORY = "org.springframework.test.context.cache";
|
||||
|
||||
/**
|
||||
* Map of parent keys to sets of children keys, representing a top-down <em>tree</em>
|
||||
* of context hierarchies. This information is used for determining which subtrees
|
||||
* need to be recursively removed and closed when removing a context that is a parent
|
||||
* of other contexts.
|
||||
*/
|
||||
private final Map<MergedContextConfiguration, Set<MergedContextConfiguration>> hierarchyMap =
|
||||
new ConcurrentReferenceHashMap<MergedContextConfiguration, Set<MergedContextConfiguration>>(64);
|
||||
|
||||
private final AtomicInteger hitCount = new AtomicInteger();
|
||||
|
||||
private final AtomicInteger missCount = new AtomicInteger();
|
||||
|
||||
/**
|
||||
* Reset all state maintained by this cache.
|
||||
* @see #clear()
|
||||
* @see #clearStatistics()
|
||||
*/
|
||||
public void reset() {
|
||||
synchronized (contextMap) {
|
||||
clear();
|
||||
clearStatistics();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all contexts from the cache and clear context hierarchy information as well.
|
||||
*/
|
||||
public void clear() {
|
||||
synchronized (contextMap) {
|
||||
this.contextMap.clear();
|
||||
this.hierarchyMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear hit and miss count statistics for the cache (i.e., reset counters to zero).
|
||||
*/
|
||||
public void clearStatistics() {
|
||||
synchronized (contextMap) {
|
||||
this.hitCount.set(0);
|
||||
this.missCount.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether there is a cached context for the given key.
|
||||
* @param key the context key (never {@code null})
|
||||
* @return {@code true} if the cache contains a context with the given key
|
||||
*/
|
||||
public boolean contains(MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
return this.contextMap.containsKey(key);
|
||||
}
|
||||
boolean contains(MergedContextConfiguration key);
|
||||
|
||||
/**
|
||||
* Obtain a cached {@code ApplicationContext} for the given key.
|
||||
* <p>The {@link #getHitCount() hit} and {@link #getMissCount() miss} counts will
|
||||
* be updated accordingly.
|
||||
* <p>The {@link #getHitCount() hit} and {@link #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
|
||||
*/
|
||||
public ApplicationContext get(MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
ApplicationContext context = this.contextMap.get(key);
|
||||
if (context == null) {
|
||||
this.missCount.incrementAndGet();
|
||||
}
|
||||
else {
|
||||
this.hitCount.incrementAndGet();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the overall hit count for this cache.
|
||||
* <p>A <em>hit</em> is any access to the cache that returns a non-null
|
||||
* context for the queried key.
|
||||
*/
|
||||
public int getHitCount() {
|
||||
return this.hitCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the overall miss count for this cache.
|
||||
* <p>A <em>miss</em> is any access to the cache that returns a {@code null}
|
||||
* context for the queried key.
|
||||
*/
|
||||
public int getMissCount() {
|
||||
return this.missCount.get();
|
||||
}
|
||||
ApplicationContext get(MergedContextConfiguration key);
|
||||
|
||||
/**
|
||||
* Explicitly add an {@code ApplicationContext} instance to the cache
|
||||
|
|
@ -162,122 +72,79 @@ class ContextCache {
|
|||
* @param key the context key (never {@code null})
|
||||
* @param context the {@code ApplicationContext} instance (never {@code null})
|
||||
*/
|
||||
public void put(MergedContextConfiguration key, ApplicationContext context) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
Assert.notNull(context, "ApplicationContext must not be null");
|
||||
|
||||
this.contextMap.put(key, context);
|
||||
MergedContextConfiguration child = key;
|
||||
MergedContextConfiguration parent = child.getParent();
|
||||
while (parent != null) {
|
||||
Set<MergedContextConfiguration> list = this.hierarchyMap.get(parent);
|
||||
if (list == null) {
|
||||
list = new HashSet<MergedContextConfiguration>();
|
||||
this.hierarchyMap.put(parent, list);
|
||||
}
|
||||
list.add(child);
|
||||
child = parent;
|
||||
parent = child.getParent();
|
||||
}
|
||||
}
|
||||
void put(MergedContextConfiguration key, ApplicationContext context);
|
||||
|
||||
/**
|
||||
* Remove the context with the given key from the cache and explicitly
|
||||
* {@linkplain ConfigurableApplicationContext#close() close} it if it is an
|
||||
* instance of {@link ConfigurableApplicationContext}.
|
||||
* <p>Generally speaking, you would only call this method if you change the
|
||||
* state of a singleton bean, potentially affecting future interaction with
|
||||
* the context.
|
||||
* <p>In addition, the semantics of the supplied {@code HierarchyMode} will
|
||||
* {@linkplain org.springframework.context.ConfigurableApplicationContext#close() close}
|
||||
* it if it is an instance of {@code ConfigurableApplicationContext}.
|
||||
* <p>Generally speaking, this method should be called if the state of
|
||||
* a singleton bean has been modified, potentially affecting future
|
||||
* interaction with the context.
|
||||
* <p>In addition, the semantics of the supplied {@code HierarchyMode} must
|
||||
* be honored. See the Javadoc for {@link HierarchyMode} for details.
|
||||
* @param key the context key; never {@code null}
|
||||
* @param hierarchyMode the hierarchy mode; may be {@code null} if the context
|
||||
* is not part of a hierarchy
|
||||
*/
|
||||
public void remove(MergedContextConfiguration key, HierarchyMode hierarchyMode) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
|
||||
// startKey is the level at which to begin clearing the cache, depending
|
||||
// on the configured hierarchy mode.
|
||||
MergedContextConfiguration startKey = key;
|
||||
if (hierarchyMode == HierarchyMode.EXHAUSTIVE) {
|
||||
while (startKey.getParent() != null) {
|
||||
startKey = startKey.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
List<MergedContextConfiguration> removedContexts = new ArrayList<MergedContextConfiguration>();
|
||||
remove(removedContexts, startKey);
|
||||
|
||||
// Remove all remaining references to any removed contexts from the
|
||||
// hierarchy map.
|
||||
for (MergedContextConfiguration currentKey : removedContexts) {
|
||||
for (Set<MergedContextConfiguration> children : this.hierarchyMap.values()) {
|
||||
children.remove(currentKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove empty entries from the hierarchy map.
|
||||
for (MergedContextConfiguration currentKey : this.hierarchyMap.keySet()) {
|
||||
if (this.hierarchyMap.get(currentKey).isEmpty()) {
|
||||
this.hierarchyMap.remove(currentKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(List<MergedContextConfiguration> removedContexts, MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
|
||||
Set<MergedContextConfiguration> children = this.hierarchyMap.get(key);
|
||||
if (children != null) {
|
||||
for (MergedContextConfiguration child : children) {
|
||||
// Recurse through lower levels
|
||||
remove(removedContexts, child);
|
||||
}
|
||||
// Remove the set of children for the current context from the hierarchy map.
|
||||
this.hierarchyMap.remove(key);
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (context instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) context).close();
|
||||
}
|
||||
removedContexts.add(key);
|
||||
}
|
||||
void remove(MergedContextConfiguration key, HierarchyMode hierarchyMode);
|
||||
|
||||
/**
|
||||
* Determine the number of contexts currently stored in the cache.
|
||||
* <p>If the cache contains more than {@code Integer.MAX_VALUE} elements,
|
||||
* this method returns {@code Integer.MAX_VALUE}.
|
||||
* this method must return {@code Integer.MAX_VALUE}.
|
||||
*/
|
||||
public int size() {
|
||||
return this.contextMap.size();
|
||||
}
|
||||
int size();
|
||||
|
||||
/**
|
||||
* Determine the number of parent contexts currently tracked within the cache.
|
||||
*/
|
||||
public int getParentContextCount() {
|
||||
return this.hierarchyMap.size();
|
||||
}
|
||||
int getParentContextCount();
|
||||
|
||||
/**
|
||||
* Generate a text string containing the statistics for this cache.
|
||||
* <p>Specifically, the returned string contains the {@linkplain #size},
|
||||
* {@linkplain #getHitCount() hit count}, {@linkplain #getMissCount() miss count},
|
||||
* and {@linkplain #getParentContextCount() parent context count}.
|
||||
* @return the statistics for this cache
|
||||
* Get the overall hit count for this cache.
|
||||
* <p>A <em>hit</em> is any access to the cache that returns a non-null
|
||||
* context for the queried key.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)
|
||||
.append("size", size())
|
||||
.append("hitCount", getHitCount())
|
||||
.append("missCount", getMissCount())
|
||||
.append("parentContextCount", getParentContextCount())
|
||||
.toString();
|
||||
}
|
||||
int getHitCount();
|
||||
|
||||
/**
|
||||
* Get the overall miss count for this cache.
|
||||
* <p>A <em>miss</em> is any access to the cache that returns a {@code null}
|
||||
* context for the queried key.
|
||||
*/
|
||||
int getMissCount();
|
||||
|
||||
/**
|
||||
* Reset all state maintained by this cache including statistics.
|
||||
* @see #clear()
|
||||
* @see #clearStatistics()
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Clear all contexts from the cache, clearing context hierarchy information as well.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Clear hit and miss count statistics for the cache (i.e., reset counters to zero).
|
||||
*/
|
||||
void clearStatistics();
|
||||
|
||||
/**
|
||||
* Log the statistics for this {@code ContextCache} at {@code DEBUG} level
|
||||
* using the {@value #CONTEXT_CACHE_LOGGING_CATEGORY} logging category.
|
||||
* <p>The following information should be logged.
|
||||
* <ul>
|
||||
* <li>name of the concrete {@code ContextCache} implementation</li>
|
||||
* <li>{@linkplain #size}</li>
|
||||
* <li>{@linkplain #getParentContextCount() parent context count}</li>
|
||||
* <li>{@linkplain #getHitCount() hit count}</li>
|
||||
* <li>{@linkplain #getMissCount() miss count}</li>
|
||||
* <li>any other information useful for monitoring the state of this cache</li>
|
||||
* </ul>
|
||||
*/
|
||||
void logStatistics();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -19,25 +19,33 @@ package org.springframework.test.context;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code TestContextBootstrapper} defines a strategy SPI for bootstrapping the
|
||||
* {@code TestContextBootstrapper} defines the SPI for bootstrapping the
|
||||
* <em>Spring TestContext Framework</em>.
|
||||
*
|
||||
* <p>A custom bootstrapping strategy can be configured for a test class via
|
||||
* {@link BootstrapWith @BootstrapWith}, either directly or as a meta-annotation.
|
||||
* See {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
|
||||
* <p>A {@code TestContextBootstrapper} is used by the {@link TestContextManager} to
|
||||
* {@linkplain #getTestExecutionListeners get the TestExecutionListeners} for the
|
||||
* current test and to {@linkplain #buildTestContext build the TestContext} that
|
||||
* it manages.
|
||||
*
|
||||
* <h3>Configuration</h3>
|
||||
*
|
||||
* <p>A custom bootstrapping strategy can be configured for a test class (or
|
||||
* test class hierarchy) via {@link BootstrapWith @BootstrapWith}, either
|
||||
* directly or as a meta-annotation. See
|
||||
* {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
|
||||
* for an example.
|
||||
*
|
||||
* <p>The {@link TestContextManager} uses a {@code TestContextBootstrapper} to
|
||||
* {@linkplain #getTestExecutionListeners get the TestExecutionListeners} for the
|
||||
* current test and to {@linkplain #buildMergedContextConfiguration build the
|
||||
* merged context configuration} necessary to create the {@link TestContext} that
|
||||
* it manages.
|
||||
* <p>If a bootstrapper is not explicitly configured via {@code @BootstrapWith}, the
|
||||
* {@link org.springframework.test.context.support.DefaultTestContextBootstrapper DefaultTestContextBootstrapper}
|
||||
* will be used.
|
||||
*
|
||||
* <h3>Implementation Notes</h3>
|
||||
*
|
||||
* <p>Concrete implementations must provide a {@code public} no-args constructor.
|
||||
*
|
||||
* <p><strong>Note</strong>: this SPI might potentially change in the future in
|
||||
* <p><strong>WARNING</strong>: this SPI will likely change in the future in
|
||||
* order to accommodate new requirements. Implementers are therefore strongly encouraged
|
||||
* <em>not</em> to implement this interface directly but rather to <em>extend</em>
|
||||
* <strong>not</strong> to implement this interface directly but rather to <em>extend</em>
|
||||
* {@link org.springframework.test.context.support.AbstractTestContextBootstrapper
|
||||
* AbstractTestContextBootstrapper} or one of its concrete subclasses instead.
|
||||
*
|
||||
|
|
@ -58,6 +66,46 @@ public interface TestContextBootstrapper {
|
|||
*/
|
||||
BootstrapContext getBootstrapContext();
|
||||
|
||||
/**
|
||||
* Build the {@link TestContext} for the {@link BootstrapContext}
|
||||
* associated with this bootstrapper.
|
||||
* @return a new {@link TestContext}, never {@code null}
|
||||
* @since 4.2
|
||||
* @see #buildMergedContextConfiguration()
|
||||
*/
|
||||
TestContext buildTestContext();
|
||||
|
||||
/**
|
||||
* Build the {@linkplain MergedContextConfiguration merged context configuration}
|
||||
* for the test class in the {@link BootstrapContext} associated with this
|
||||
* bootstrapper.
|
||||
* <p>Implementations must take the following into account when building the
|
||||
* merged configuration:
|
||||
* <ul>
|
||||
* <li>Context hierarchies declared via {@link ContextHierarchy @ContextHierarchy}
|
||||
* and {@link ContextConfiguration @ContextConfiguration}</li>
|
||||
* <li>Active bean definition profiles declared via {@link ActiveProfiles @ActiveProfiles}</li>
|
||||
* <li>{@linkplain org.springframework.context.ApplicationContextInitializer
|
||||
* Context initializers} declared via {@link ContextConfiguration#initializers}</li>
|
||||
* <li>Test property sources declared via {@link TestPropertySource @TestPropertySource}</li>
|
||||
* </ul>
|
||||
* <p>Consult the Javadoc for the aforementioned annotations for details on
|
||||
* the required semantics.
|
||||
* <p>Note that the implementation of {@link #buildTestContext()} should
|
||||
* typically delegate to this method when constructing the {@code TestContext}.
|
||||
* <p>When determining which {@link ContextLoader} to use for a given test
|
||||
* class, the following algorithm should be used:
|
||||
* <ol>
|
||||
* <li>If a {@code ContextLoader} class has been explicitly declared via
|
||||
* {@link ContextConfiguration#loader}, use it.</li>
|
||||
* <li>Otherwise, concrete implementations are free to determine which
|
||||
* {@code ContextLoader} class to use as as default.</li>
|
||||
* </ol>
|
||||
* @return the merged context configuration, never {@code null}
|
||||
* @see #buildTestContext()
|
||||
*/
|
||||
MergedContextConfiguration buildMergedContextConfiguration();
|
||||
|
||||
/**
|
||||
* Get a list of newly instantiated {@link TestExecutionListener TestExecutionListeners}
|
||||
* for the test class in the {@link BootstrapContext} associated with this bootstrapper.
|
||||
|
|
@ -82,31 +130,4 @@ public interface TestContextBootstrapper {
|
|||
*/
|
||||
List<TestExecutionListener> getTestExecutionListeners();
|
||||
|
||||
/**
|
||||
* Build the {@linkplain MergedContextConfiguration merged context configuration}
|
||||
* for the test class in the {@link BootstrapContext} associated with this
|
||||
* bootstrapper.
|
||||
* <p>Implementations must take the following into account when building the
|
||||
* merged configuration:
|
||||
* <ul>
|
||||
* <li>Context hierarchies declared via {@link ContextHierarchy @ContextHierarchy}
|
||||
* and {@link ContextConfiguration @ContextConfiguration}</li>
|
||||
* <li>Active bean definition profiles declared via {@link ActiveProfiles @ActiveProfiles}</li>
|
||||
* <li>{@linkplain org.springframework.context.ApplicationContextInitializer
|
||||
* Context initializers} declared via {@link ContextConfiguration#initializers}</li>
|
||||
* </ul>
|
||||
* <p>Consult the Javadoc for the aforementioned annotations for details on
|
||||
* the required semantics.
|
||||
* <p>When determining which {@link ContextLoader} to use for a given test
|
||||
* class, the following algorithm should be used:
|
||||
* <ol>
|
||||
* <li>If a {@code ContextLoader} class has been explicitly declared via
|
||||
* {@link ContextConfiguration#loader}, use it.</li>
|
||||
* <li>Otherwise, concrete implementations are free to determine which
|
||||
* {@code ContextLoader} class to use as as default.</li>
|
||||
* </ol>
|
||||
* @return the merged context configuration, never {@code null}
|
||||
*/
|
||||
MergedContextConfiguration buildMergedContextConfiguration();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -28,15 +28,11 @@ import org.springframework.util.Assert;
|
|||
|
||||
/**
|
||||
* {@code TestContextManager} is the main entry point into the <em>Spring
|
||||
* TestContext Framework</em>, which provides support for loading and accessing
|
||||
* {@link org.springframework.context.ApplicationContext application contexts},
|
||||
* dependency injection of test instances,
|
||||
* {@link org.springframework.transaction.annotation.Transactional transactional}
|
||||
* execution of test methods, etc.
|
||||
* TestContext Framework</em>.
|
||||
*
|
||||
* <p>Specifically, a {@code TestContextManager} is responsible for managing a
|
||||
* single {@link TestContext} and signaling events to all registered
|
||||
* {@link TestExecutionListener TestExecutionListeners} at well defined test
|
||||
* {@link TestExecutionListener TestExecutionListeners} at the following test
|
||||
* execution points:
|
||||
*
|
||||
* <ul>
|
||||
|
|
@ -56,6 +52,21 @@ import org.springframework.util.Assert;
|
|||
* 4's {@link org.junit.AfterClass @AfterClass})</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Support for loading and accessing
|
||||
* {@link org.springframework.context.ApplicationContext application contexts},
|
||||
* dependency injection of test instances,
|
||||
* {@link org.springframework.transaction.annotation.Transactional transactional}
|
||||
* execution of test methods, etc. is provided by
|
||||
* {@link SmartContextLoader ContextLoaders} and {@link TestExecutionListener
|
||||
* TestExecutionListeners}, which are configured via
|
||||
* {@link ContextConfiguration @ContextConfiguration} and
|
||||
* {@link TestExecutionListeners @TestExecutionListeners}.
|
||||
*
|
||||
* <p>Bootstrapping of the {@code TestContext}, the default {@code ContextLoader},
|
||||
* default {@code TestExecutionListeners}, and their collaborators is performed
|
||||
* by a {@link TestContextBootstrapper}, which is configured via
|
||||
* {@link BootstrapWith @BootstrapWith}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
|
|
@ -73,37 +84,42 @@ public class TestContextManager {
|
|||
|
||||
private static final Log logger = LogFactory.getLog(TestContextManager.class);
|
||||
|
||||
/**
|
||||
* Cache of Spring application contexts.
|
||||
* <p>This needs to be static, since test instances may be destroyed and
|
||||
* recreated between invocations of individual test methods, as is the case
|
||||
* with JUnit.
|
||||
*/
|
||||
static final ContextCache contextCache = new ContextCache();
|
||||
|
||||
private final TestContext testContext;
|
||||
|
||||
private final TestContextBootstrapper testContextBootstrapper;
|
||||
|
||||
private final List<TestExecutionListener> testExecutionListeners = new ArrayList<TestExecutionListener>();
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new {@code TestContextManager} for the specified {@linkplain Class test class}
|
||||
* and automatically {@link #registerTestExecutionListeners register} the
|
||||
* {@link TestExecutionListener TestExecutionListeners} configured for the test class
|
||||
* via the {@link TestExecutionListeners @TestExecutionListeners} annotation.
|
||||
* Construct a new {@code TestContextManager} for the specified {@linkplain Class test class},
|
||||
* automatically {@linkplain #registerTestExecutionListeners registering} the necessary
|
||||
* {@link TestExecutionListener TestExecutionListeners}.
|
||||
* <p>Delegates to a {@link TestContextBootstrapper} for building the {@code TestContext}
|
||||
* and retrieving the {@code TestExecutionListeners}.
|
||||
* @param testClass the test class to be managed
|
||||
* @see TestContextBootstrapper#buildTestContext
|
||||
* @see TestContextBootstrapper#getTestExecutionListeners
|
||||
* @see #registerTestExecutionListeners
|
||||
*/
|
||||
public TestContextManager(Class<?> testClass) {
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = new DefaultCacheAwareContextLoaderDelegate(contextCache);
|
||||
BootstrapContext bootstrapContext = new DefaultBootstrapContext(testClass, cacheAwareContextLoaderDelegate);
|
||||
this.testContextBootstrapper = BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext);
|
||||
this.testContext = new DefaultTestContext(this.testContextBootstrapper);
|
||||
registerTestExecutionListeners(this.testContextBootstrapper.getTestExecutionListeners());
|
||||
BootstrapContext bootstrapContext = createBootstrapContext(testClass);
|
||||
TestContextBootstrapper bootstrapper = BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext);
|
||||
this.testContext = bootstrapper.buildTestContext();
|
||||
registerTestExecutionListeners(bootstrapper.getTestExecutionListeners());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@code BootstrapContext} for the specified {@linkplain Class test class}.
|
||||
* <p>The default implementation creates a
|
||||
* {@link org.springframework.test.context.support.DefaultBootstrapContext DefaultBootstrapContext}
|
||||
* that uses a
|
||||
* {@link org.springframework.test.context.support.DefaultCacheAwareContextLoaderDelegate DefaultCacheAwareContextLoaderDelegate}.
|
||||
* <p>Can be overridden by subclasses as necessary.
|
||||
* @param testClass the test class for which the bootstrap context should be created
|
||||
* @return a new {@code BootstrapContext}; never {@code null}
|
||||
*/
|
||||
protected BootstrapContext createBootstrapContext(Class<?> testClass) {
|
||||
return BootstrapUtils.createBootstrapContext(testClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link TestContext} managed by this {@code TestContextManager}.
|
||||
|
|
@ -201,7 +217,7 @@ public class TestContextManager {
|
|||
* @see #getTestExecutionListeners()
|
||||
*/
|
||||
public void prepareTestInstance(Object testInstance) throws Exception {
|
||||
Assert.notNull(testInstance, "testInstance must not be null");
|
||||
Assert.notNull(testInstance, "Test instance must not be null");
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("prepareTestInstance(): instance [" + testInstance + "]");
|
||||
}
|
||||
|
|
@ -282,7 +298,7 @@ public class TestContextManager {
|
|||
* @see #getTestExecutionListeners()
|
||||
*/
|
||||
public void afterTestMethod(Object testInstance, Method testMethod, Throwable exception) throws Exception {
|
||||
Assert.notNull(testInstance, "testInstance must not be null");
|
||||
Assert.notNull(testInstance, "Test instance must not be null");
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("afterTestMethod(): instance [" + testInstance + "], method [" + testMethod +
|
||||
"], exception [" + exception + "]");
|
||||
|
|
|
|||
|
|
@ -38,12 +38,14 @@ import org.springframework.core.annotation.AnnotationUtils;
|
|||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.test.context.BootstrapContext;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.test.context.ContextCache;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestContextBootstrapper;
|
||||
import org.springframework.test.context.TestExecutionListener;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
|
|
@ -66,6 +68,9 @@ import org.springframework.util.StringUtils;
|
|||
* <li>{@link #processMergedContextConfiguration}
|
||||
* </ul>
|
||||
*
|
||||
* <p>To plug in custom {@link ContextCache} support, override
|
||||
* {@link #getCacheAwareContextLoaderDelegate()}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
|
|
@ -93,6 +98,21 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
return this.bootstrapContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new {@link DefaultTestContext} using the {@linkplain Class test class}
|
||||
* in the {@link BootstrapContext} associated with this bootstrapper and
|
||||
* by delegating to {@link #buildMergedContextConfiguration()} and
|
||||
* {@link #getCacheAwareContextLoaderDelegate()}.
|
||||
* <p>Concrete subclasses may choose to override this method to return a
|
||||
* custom {@link TestContext} implementation.
|
||||
* @since 4.2
|
||||
*/
|
||||
@Override
|
||||
public TestContext buildTestContext() {
|
||||
return new DefaultTestContext(getBootstrapContext().getTestClass(), buildMergedContextConfiguration(),
|
||||
getCacheAwareContextLoaderDelegate());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
@ -266,7 +286,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
@Override
|
||||
public final MergedContextConfiguration buildMergedContextConfiguration() {
|
||||
Class<?> testClass = getBootstrapContext().getTestClass();
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = getBootstrapContext().getCacheAwareContextLoaderDelegate();
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = getCacheAwareContextLoaderDelegate();
|
||||
|
||||
if (MetaAnnotationUtils.findAnnotationDescriptorForTypes(testClass, ContextConfiguration.class,
|
||||
ContextHierarchy.class) == null) {
|
||||
|
|
@ -455,6 +475,20 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link CacheAwareContextLoaderDelegate} to use for transparent
|
||||
* interaction with the {@code ContextCache}.
|
||||
* <p>The default implementation simply delegates to
|
||||
* {@code getBootstrapContext().getCacheAwareContextLoaderDelegate()}.
|
||||
* <p>Concrete subclasses may choose to override this method to return a
|
||||
* custom {@code CacheAwareContextLoaderDelegate} implementation with
|
||||
* custom {@link ContextCache} support.
|
||||
* @return the context loader delegate (never {@code null})
|
||||
*/
|
||||
protected CacheAwareContextLoaderDelegate getCacheAwareContextLoaderDelegate() {
|
||||
return getBootstrapContext().getCacheAwareContextLoaderDelegate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the default {@link ContextLoader} {@linkplain Class class}
|
||||
* to use for the supplied test class.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -14,9 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.test.context.BootstrapContext;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -25,13 +27,19 @@ import org.springframework.util.Assert;
|
|||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
class DefaultBootstrapContext implements BootstrapContext {
|
||||
public class DefaultBootstrapContext implements BootstrapContext {
|
||||
|
||||
private final Class<?> testClass;
|
||||
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
|
||||
|
||||
|
||||
DefaultBootstrapContext(Class<?> testClass, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
/**
|
||||
* Construct a new {@code DefaultBootstrapContext} from the supplied arguments.
|
||||
* @param testClass the test class for this bootstrap context; never {@code null}
|
||||
* @param cacheAwareContextLoaderDelegate the context loader delegate to use for
|
||||
* transparent interaction with the {@code ContextCache}; never {@code null}
|
||||
*/
|
||||
public DefaultBootstrapContext(Class<?> testClass, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
Assert.notNull(testClass, "Test class must not be null");
|
||||
Assert.notNull(cacheAwareContextLoaderDelegate, "CacheAwareContextLoaderDelegate must not be null");
|
||||
this.testClass = testClass;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -14,47 +14,78 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.test.context.ContextCache;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link CacheAwareContextLoaderDelegate} interface.
|
||||
*
|
||||
* <p>Although {@code DefaultCacheAwareContextLoaderDelegate} was first introduced
|
||||
* in Spring Framework 4.1, the initial implementation of this class was extracted
|
||||
* from the existing code base for {@code CacheAwareContextLoaderDelegate} when
|
||||
* {@code CacheAwareContextLoaderDelegate} was converted into an interface.
|
||||
* <p>To use a static {@code DefaultContextCache}, invoke the
|
||||
* {@link #DefaultCacheAwareContextLoaderDelegate()} constructor; otherwise,
|
||||
* invoke the {@link #DefaultCacheAwareContextLoaderDelegate(ContextCache)}
|
||||
* and provide a custom {@link ContextCache} implementation.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContextLoaderDelegate {
|
||||
public class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContextLoaderDelegate {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultCacheAwareContextLoaderDelegate.class);
|
||||
|
||||
private static final Log statsLogger = LogFactory.getLog("org.springframework.test.context.cache");
|
||||
/**
|
||||
* Default static cache of Spring application contexts.
|
||||
*/
|
||||
static final ContextCache defaultContextCache = new DefaultContextCache();
|
||||
|
||||
private final ContextCache contextCache;
|
||||
|
||||
|
||||
DefaultCacheAwareContextLoaderDelegate(ContextCache contextCache) {
|
||||
/**
|
||||
* Construct a new {@code DefaultCacheAwareContextLoaderDelegate} using
|
||||
* a static {@link DefaultContextCache}.
|
||||
* <p>This default cache is static so that each context can be cached
|
||||
* and reused for all subsequent tests that declare the same unique
|
||||
* context configuration within the same JVM process.
|
||||
* @see #DefaultCacheAwareContextLoaderDelegate(ContextCache)
|
||||
*/
|
||||
public DefaultCacheAwareContextLoaderDelegate() {
|
||||
this(defaultContextCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code DefaultCacheAwareContextLoaderDelegate} using
|
||||
* the supplied {@link ContextCache}.
|
||||
* @see #DefaultCacheAwareContextLoaderDelegate()
|
||||
*/
|
||||
public DefaultCacheAwareContextLoaderDelegate(ContextCache contextCache) {
|
||||
Assert.notNull(contextCache, "ContextCache must not be null");
|
||||
this.contextCache = contextCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ContextCache} used by this context loader delegate.
|
||||
*/
|
||||
protected ContextCache getContextCache() {
|
||||
return this.contextCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the {@code ApplicationContext} for the supplied merged context configuration.
|
||||
* <p>Supports both the {@link SmartContextLoader} and {@link ContextLoader} SPIs.
|
||||
* @throws Exception if an error occurs while loading the application context
|
||||
*/
|
||||
private ApplicationContext loadContextInternal(MergedContextConfiguration mergedContextConfiguration)
|
||||
protected ApplicationContext loadContextInternal(MergedContextConfiguration mergedContextConfiguration)
|
||||
throws Exception {
|
||||
|
||||
ContextLoader contextLoader = mergedContextConfiguration.getContextLoader();
|
||||
|
|
@ -101,9 +132,7 @@ class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContextLoaderD
|
|||
}
|
||||
}
|
||||
|
||||
if (statsLogger.isDebugEnabled()) {
|
||||
statsLogger.debug("Spring test ApplicationContext cache statistics: " + this.contextCache);
|
||||
}
|
||||
this.contextCache.logStatistics();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Copyright 2002-2015 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.test.context.ContextCache;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link ContextCache} API.
|
||||
*
|
||||
* <p>Uses Spring's {@link ConcurrentReferenceHashMap} to store
|
||||
* {@linkplain java.lang.ref.SoftReference soft references} to cached
|
||||
* contexts and {@code MergedContextConfiguration} instances.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see ConcurrentReferenceHashMap
|
||||
*/
|
||||
public class DefaultContextCache implements ContextCache {
|
||||
|
||||
private static final Log statsLogger = LogFactory.getLog(CONTEXT_CACHE_LOGGING_CATEGORY);
|
||||
|
||||
/**
|
||||
* Map of context keys to Spring {@code ApplicationContext} instances.
|
||||
*/
|
||||
private final Map<MergedContextConfiguration, ApplicationContext> contextMap =
|
||||
new ConcurrentReferenceHashMap<MergedContextConfiguration, ApplicationContext>(64);
|
||||
|
||||
/**
|
||||
* Map of parent keys to sets of children keys, representing a top-down <em>tree</em>
|
||||
* of context hierarchies. This information is used for determining which subtrees
|
||||
* need to be recursively removed and closed when removing a context that is a parent
|
||||
* of other contexts.
|
||||
*/
|
||||
private final Map<MergedContextConfiguration, Set<MergedContextConfiguration>> hierarchyMap =
|
||||
new ConcurrentReferenceHashMap<MergedContextConfiguration, Set<MergedContextConfiguration>>(64);
|
||||
|
||||
private final AtomicInteger hitCount = new AtomicInteger();
|
||||
|
||||
private final AtomicInteger missCount = new AtomicInteger();
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
return this.contextMap.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ApplicationContext get(MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
ApplicationContext context = this.contextMap.get(key);
|
||||
if (context == null) {
|
||||
this.missCount.incrementAndGet();
|
||||
}
|
||||
else {
|
||||
this.hitCount.incrementAndGet();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void put(MergedContextConfiguration key, ApplicationContext context) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
Assert.notNull(context, "ApplicationContext must not be null");
|
||||
|
||||
this.contextMap.put(key, context);
|
||||
MergedContextConfiguration child = key;
|
||||
MergedContextConfiguration parent = child.getParent();
|
||||
while (parent != null) {
|
||||
Set<MergedContextConfiguration> list = this.hierarchyMap.get(parent);
|
||||
if (list == null) {
|
||||
list = new HashSet<MergedContextConfiguration>();
|
||||
this.hierarchyMap.put(parent, list);
|
||||
}
|
||||
list.add(child);
|
||||
child = parent;
|
||||
parent = child.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void remove(MergedContextConfiguration key, HierarchyMode hierarchyMode) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
|
||||
// startKey is the level at which to begin clearing the cache, depending
|
||||
// on the configured hierarchy mode.
|
||||
MergedContextConfiguration startKey = key;
|
||||
if (hierarchyMode == HierarchyMode.EXHAUSTIVE) {
|
||||
while (startKey.getParent() != null) {
|
||||
startKey = startKey.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
List<MergedContextConfiguration> removedContexts = new ArrayList<MergedContextConfiguration>();
|
||||
remove(removedContexts, startKey);
|
||||
|
||||
// Remove all remaining references to any removed contexts from the
|
||||
// hierarchy map.
|
||||
for (MergedContextConfiguration currentKey : removedContexts) {
|
||||
for (Set<MergedContextConfiguration> children : this.hierarchyMap.values()) {
|
||||
children.remove(currentKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove empty entries from the hierarchy map.
|
||||
for (MergedContextConfiguration currentKey : this.hierarchyMap.keySet()) {
|
||||
if (this.hierarchyMap.get(currentKey).isEmpty()) {
|
||||
this.hierarchyMap.remove(currentKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(List<MergedContextConfiguration> removedContexts, MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
|
||||
Set<MergedContextConfiguration> children = this.hierarchyMap.get(key);
|
||||
if (children != null) {
|
||||
for (MergedContextConfiguration child : children) {
|
||||
// Recurse through lower levels
|
||||
remove(removedContexts, child);
|
||||
}
|
||||
// Remove the set of children for the current context from the hierarchy map.
|
||||
this.hierarchyMap.remove(key);
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (context instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) context).close();
|
||||
}
|
||||
removedContexts.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return this.contextMap.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int getParentContextCount() {
|
||||
return this.hierarchyMap.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int getHitCount() {
|
||||
return this.hitCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int getMissCount() {
|
||||
return this.missCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
synchronized (contextMap) {
|
||||
clear();
|
||||
clearStatistics();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
synchronized (contextMap) {
|
||||
this.contextMap.clear();
|
||||
this.hierarchyMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void clearStatistics() {
|
||||
synchronized (contextMap) {
|
||||
this.hitCount.set(0);
|
||||
this.missCount.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void logStatistics() {
|
||||
if (statsLogger.isDebugEnabled()) {
|
||||
statsLogger.debug("Spring test ApplicationContext cache statistics: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a text string containing the implementation type of this
|
||||
* cache and its statistics.
|
||||
* <p>The string returned by this method contains all information
|
||||
* required for compliance with the contract for {@link #logStatistics()}.
|
||||
* @return a string representation of this cache, including statistics
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)
|
||||
.append("size", size())
|
||||
.append("parentContextCount", getParentContextCount())
|
||||
.append("hitCount", getHitCount())
|
||||
.append("missCount", getMissCount())
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
|
@ -22,21 +22,19 @@ import org.springframework.context.ApplicationContext;
|
|||
import org.springframework.core.AttributeAccessorSupport;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link TestContext} interface.
|
||||
*
|
||||
* <p>Although {@code DefaultTestContext} was first introduced in Spring Framework
|
||||
* 4.0, the initial implementation of this class was extracted from the existing
|
||||
* code base for {@code TestContext} when {@code TestContext} was converted into
|
||||
* an interface.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
*/
|
||||
class DefaultTestContext extends AttributeAccessorSupport implements TestContext {
|
||||
public class DefaultTestContext extends AttributeAccessorSupport implements TestContext {
|
||||
|
||||
private static final long serialVersionUID = -5827157174866681233L;
|
||||
|
||||
|
|
@ -54,24 +52,42 @@ class DefaultTestContext extends AttributeAccessorSupport implements TestContext
|
|||
|
||||
|
||||
/**
|
||||
* Construct a new test context using the supplied {@link TestContextBootstrapper}.
|
||||
* @param testContextBootstrapper the {@code TestContextBootstrapper} to use
|
||||
* to construct the test context (must not be {@code null})
|
||||
* Construct a new {@code DefaultTestContext} from the supplied arguments.
|
||||
* @param testClass the test class for this test context; never {@code null}
|
||||
* @param mergedContextConfiguration the merged application context
|
||||
* configuration for this test context; never {@code null}
|
||||
* @param cacheAwareContextLoaderDelegate the delegate to use for loading
|
||||
* and closing the application context for this test context; never {@code null}
|
||||
*/
|
||||
DefaultTestContext(TestContextBootstrapper testContextBootstrapper) {
|
||||
Assert.notNull(testContextBootstrapper, "TestContextBootstrapper must not be null");
|
||||
|
||||
BootstrapContext bootstrapContext = testContextBootstrapper.getBootstrapContext();
|
||||
this.testClass = bootstrapContext.getTestClass();
|
||||
this.cacheAwareContextLoaderDelegate = bootstrapContext.getCacheAwareContextLoaderDelegate();
|
||||
this.mergedContextConfiguration = testContextBootstrapper.buildMergedContextConfiguration();
|
||||
public DefaultTestContext(Class<?> testClass, MergedContextConfiguration mergedContextConfiguration,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
Assert.notNull(testClass, "testClass must not be null");
|
||||
Assert.notNull(mergedContextConfiguration, "MergedContextConfiguration must not be null");
|
||||
Assert.notNull(cacheAwareContextLoaderDelegate, "CacheAwareContextLoaderDelegate must not be null");
|
||||
this.testClass = testClass;
|
||||
this.mergedContextConfiguration = mergedContextConfiguration;
|
||||
this.cacheAwareContextLoaderDelegate = cacheAwareContextLoaderDelegate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the {@linkplain ApplicationContext application context} for this
|
||||
* test context.
|
||||
* <p>The default implementation delegates to the {@link CacheAwareContextLoaderDelegate}
|
||||
* that was supplied when this {@code TestContext} was constructed.
|
||||
* @see CacheAwareContextLoaderDelegate#loadContext
|
||||
*/
|
||||
public ApplicationContext getApplicationContext() {
|
||||
return this.cacheAwareContextLoaderDelegate.loadContext(this.mergedContextConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the {@linkplain ApplicationContext application context} associated
|
||||
* with this test context as <em>dirty</em> (i.e., by removing it from the
|
||||
* context cache and closing it).
|
||||
* <p>The default implementation delegates to the {@link CacheAwareContextLoaderDelegate}
|
||||
* that was supplied when this {@code TestContext} was constructed.
|
||||
* @see CacheAwareContextLoaderDelegate#closeContext
|
||||
*/
|
||||
public void markApplicationContextDirty(HierarchyMode hierarchyMode) {
|
||||
this.cacheAwareContextLoaderDelegate.closeContext(this.mergedContextConfiguration, hierarchyMode);
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.test.context;
|
||||
|
||||
import org.springframework.test.context.support.DefaultBootstrapContext;
|
||||
|
||||
/**
|
||||
* Collection of test-related utility methods for working with {@link BootstrapContext
|
||||
* BootstrapContexts} and {@link TestContextBootstrapper TestContextBootstrappers}.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import org.springframework.test.context.testng.TrackingTestNGTestListener;
|
|||
import org.testng.TestNG;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.ContextCacheTestUtils.*;
|
||||
import static org.springframework.test.context.support.ContextCacheTestUtils.*;
|
||||
|
||||
/**
|
||||
* JUnit 4 based integration test which verifies correct {@linkplain ContextCache
|
||||
|
|
@ -79,8 +79,9 @@ public class ClassLevelDirtiesContextTestNGTests {
|
|||
|
||||
@BeforeClass
|
||||
public static void verifyInitialCacheState() {
|
||||
ContextCache contextCache = TestContextManager.contextCache;
|
||||
contextCache.reset();
|
||||
resetContextCache();
|
||||
// Reset static counters in case tests are run multiple times in a test suite --
|
||||
// for example, via JUnit's @Suite.
|
||||
cacheHits.set(0);
|
||||
cacheMisses.set(0);
|
||||
assertContextCacheStatistics("BeforeClass", 0, cacheHits.get(), cacheMisses.get());
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution
|
|||
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.ContextCacheTestUtils.*;
|
||||
import static org.springframework.test.context.support.ContextCacheTestUtils.*;
|
||||
|
||||
/**
|
||||
* JUnit 4 based integration test which verifies correct {@linkplain ContextCache
|
||||
|
|
@ -74,8 +74,9 @@ public class ClassLevelDirtiesContextTests {
|
|||
|
||||
@BeforeClass
|
||||
public static void verifyInitialCacheState() {
|
||||
ContextCache contextCache = TestContextManager.contextCache;
|
||||
contextCache.reset();
|
||||
resetContextCache();
|
||||
// Reset static counters in case tests are run multiple times in a test suite --
|
||||
// for example, via JUnit's @Suite.
|
||||
cacheHits.set(0);
|
||||
cacheMisses.set(0);
|
||||
assertContextCacheStatistics("BeforeClass", 0, cacheHits.get(), cacheMisses.get());
|
||||
|
|
|
|||
|
|
@ -23,10 +23,11 @@ import org.springframework.context.ApplicationContext;
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
import org.springframework.test.context.support.DefaultContextCache;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.ContextCacheTestUtils.*;
|
||||
import static org.springframework.test.context.support.ContextCacheTestUtils.*;
|
||||
|
||||
/**
|
||||
* Integration tests for verifying proper behavior of the {@link ContextCache} in
|
||||
|
|
@ -39,7 +40,7 @@ import static org.springframework.test.context.ContextCacheTestUtils.*;
|
|||
*/
|
||||
public class ContextCacheTests {
|
||||
|
||||
private ContextCache contextCache = new ContextCache();
|
||||
private ContextCache contextCache = new DefaultContextCache();
|
||||
|
||||
|
||||
@Before
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution
|
|||
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.ContextCacheTestUtils.*;
|
||||
import static org.springframework.test.context.support.ContextCacheTestUtils.*;
|
||||
|
||||
/**
|
||||
* JUnit 4 based unit test which verifies correct {@link ContextCache
|
||||
|
|
@ -58,8 +58,7 @@ public class SpringRunnerContextCacheTests {
|
|||
@BeforeClass
|
||||
public static void verifyInitialCacheState() {
|
||||
dirtiedApplicationContext = null;
|
||||
ContextCache contextCache = TestContextManager.contextCache;
|
||||
contextCache.reset();
|
||||
resetContextCache();
|
||||
assertContextCacheStatistics("BeforeClass", 0, 0, 0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
package org.springframework.test.context;
|
||||
|
||||
import org.springframework.test.context.support.DefaultBootstrapContext;
|
||||
import org.springframework.test.context.support.DefaultCacheAwareContextLoaderDelegate;
|
||||
|
||||
/**
|
||||
* Collection of test-related utility methods for working with {@link TestContext TestContexts}.
|
||||
*
|
||||
|
|
@ -28,12 +31,12 @@ public abstract class TestContextTestUtils {
|
|||
return buildTestContext(testClass, new DefaultCacheAwareContextLoaderDelegate(contextCache));
|
||||
}
|
||||
|
||||
public static TestContext buildTestContext(
|
||||
Class<?> testClass, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
public static TestContext buildTestContext(Class<?> testClass,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
|
||||
BootstrapContext bootstrapContext = new DefaultBootstrapContext(testClass, cacheAwareContextLoaderDelegate);
|
||||
TestContextBootstrapper testContextBootstrapper = BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext);
|
||||
return new DefaultTestContext(testContextBootstrapper);
|
||||
return testContextBootstrapper.buildTestContext();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import org.springframework.test.context.ContextCache;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
|
@ -28,7 +30,14 @@ import static org.junit.Assert.*;
|
|||
public class ContextCacheTestUtils {
|
||||
|
||||
/**
|
||||
* Assert the statistics of the context cache in {@link TestContextManager}.
|
||||
* Reset the state of the static context cache in {@link DefaultCacheAwareContextLoaderDelegate}.
|
||||
*/
|
||||
public static final void resetContextCache() {
|
||||
DefaultCacheAwareContextLoaderDelegate.defaultContextCache.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the statistics of the static context cache in {@link DefaultCacheAwareContextLoaderDelegate}.
|
||||
*
|
||||
* @param usageScenario the scenario in which the statistics are used
|
||||
* @param expectedSize the expected number of contexts in the cache
|
||||
|
|
@ -37,8 +46,8 @@ public class ContextCacheTestUtils {
|
|||
*/
|
||||
public static final void assertContextCacheStatistics(String usageScenario, int expectedSize, int expectedHitCount,
|
||||
int expectedMissCount) {
|
||||
assertContextCacheStatistics(TestContextManager.contextCache, usageScenario, expectedSize, expectedHitCount,
|
||||
expectedMissCount);
|
||||
assertContextCacheStatistics(DefaultCacheAwareContextLoaderDelegate.defaultContextCache, usageScenario,
|
||||
expectedSize, expectedHitCount, expectedMissCount);
|
||||
}
|
||||
|
||||
/**
|
||||
Loading…
Reference in New Issue