Skip to content

Commit 3f58da1

Browse files
committed
[SPR-7326] Added unit tests to verify proper semantics of TestContext's cache key generation.
1 parent 72420c7 commit 3f58da1

File tree

4 files changed

+140
-8
lines changed

4 files changed

+140
-8
lines changed

org.springframework.test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.Arrays;
21-
import java.util.LinkedHashSet;
2221
import java.util.List;
23-
import java.util.Set;
22+
import java.util.SortedSet;
23+
import java.util.TreeSet;
2424

2525
import org.apache.commons.logging.Log;
2626
import org.apache.commons.logging.LogFactory;
@@ -243,13 +243,16 @@ static String[] resolveActiveProfiles(Class<?> clazz) {
243243
annotationType, clazz));
244244
}
245245

246-
final Set<String> activeProfiles = new LinkedHashSet<String>();
246+
// Active profiles must be sorted due to cache key generation in
247+
// TestContext. Specifically, profile sets {foo,bar} and {bar,foo}
248+
// must both result in the same array (e.g., [bar,foo]).
249+
final SortedSet<String> activeProfiles = new TreeSet<String>();
247250

248251
while (declaringClass != null) {
249252
ActiveProfiles annotation = declaringClass.getAnnotation(annotationType);
250253

251254
if (logger.isTraceEnabled()) {
252-
logger.trace(String.format("Retrieved @ActiveProfiles [%s] for declaring class [%s].", activeProfiles,
255+
logger.trace(String.format("Retrieved @ActiveProfiles [%s] for declaring class [%s].", annotation,
253256
declaringClass));
254257
}
255258

org.springframework.test/src/main/java/org/springframework/test/context/TestContext.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,24 @@ public ApplicationContext getApplicationContext() {
170170
if (context == null) {
171171
try {
172172
context = loadApplicationContext();
173+
if (logger.isDebugEnabled()) {
174+
logger.debug(String.format(
175+
"Storing ApplicationContext for test class [%s] in cache under key [%s].", testClass,
176+
contextKey));
177+
}
173178
contextCache.put(contextKey, context);
174179
}
175180
catch (Exception ex) {
176181
throw new IllegalStateException("Failed to load ApplicationContext", ex);
177182
}
178183
}
184+
else {
185+
if (logger.isDebugEnabled()) {
186+
logger.debug(String.format(
187+
"Retrieved ApplicationContext for test class [%s] from cache with key [%s].", testClass,
188+
contextKey));
189+
}
190+
}
179191
return context;
180192
}
181193
}

org.springframework.test/src/test/java/org/springframework/test/context/SpringRunnerContextCacheTests.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2011 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@
3939
* @author Sam Brannen
4040
* @author Juergen Hoeller
4141
* @since 2.5
42+
* @see TestContextCacheKeyTests
4243
*/
4344
@RunWith(SpringJUnit4ClassRunner.class)
4445
@ContextConfiguration("/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerAppCtxTests-context.xml")
@@ -50,18 +51,32 @@ public class SpringRunnerContextCacheTests {
5051
protected ApplicationContext applicationContext;
5152

5253

54+
/**
55+
* Asserts the statistics of the context cache in {@link TestContextManager}.
56+
*
57+
* @param usageScenario the scenario in which the statistics are used
58+
* @param expectedSize the expected number of contexts in the cache
59+
* @param expectedHitCount the expected hit count
60+
* @param expectedMissCount the expected miss count
61+
*/
62+
private static final void assertContextCacheStatistics(String usageScenario, int expectedSize,
63+
int expectedHitCount, int expectedMissCount) {
64+
assertContextCacheStatistics(TestContextManager.contextCache, usageScenario, expectedSize, expectedHitCount,
65+
expectedMissCount);
66+
}
67+
5368
/**
5469
* Asserts the statistics of the supplied context cache.
5570
*
71+
* @param contextCache the cache to assert against
5672
* @param usageScenario the scenario in which the statistics are used
5773
* @param expectedSize the expected number of contexts in the cache
5874
* @param expectedHitCount the expected hit count
5975
* @param expectedMissCount the expected miss count
6076
*/
61-
public static final void assertContextCacheStatistics(String usageScenario, int expectedSize, int expectedHitCount,
62-
int expectedMissCount) {
77+
public static final void assertContextCacheStatistics(ContextCache contextCache, String usageScenario,
78+
int expectedSize, int expectedHitCount, int expectedMissCount) {
6379

64-
ContextCache contextCache = TestContextManager.contextCache;
6580
assertEquals("Verifying number of contexts in cache (" + usageScenario + ").", expectedSize,
6681
contextCache.size());
6782
assertEquals("Verifying number of cache hits (" + usageScenario + ").", expectedHitCount,
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright 2002-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context;
18+
19+
import static org.junit.Assert.assertNotNull;
20+
import static org.springframework.test.context.SpringRunnerContextCacheTests.assertContextCacheStatistics;
21+
22+
import org.junit.Before;
23+
import org.junit.Test;
24+
import org.springframework.context.ApplicationContext;
25+
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.test.context.support.AnnotationConfigContextLoader;
27+
28+
/**
29+
* Unit tests for verifying proper behavior of the {@link ContextCache} in
30+
* conjunction with cache keys generated in {@link TestContext}.
31+
*
32+
* @author Sam Brannen
33+
* @since 3.1
34+
* @see SpringRunnerContextCacheTests
35+
*/
36+
public class TestContextCacheKeyTests {
37+
38+
private ContextCache contextCache = new ContextCache();
39+
40+
41+
@Before
42+
public void initialCacheState() {
43+
assertContextCacheStatistics(contextCache, "initial state", 0, 0, 0);
44+
}
45+
46+
private void loadAppCtxAndAssertCacheStats(Class<?> testClass, int expectedSize, int expectedHitCount,
47+
int expectedMissCount) {
48+
TestContext testContext = new TestContext(testClass, contextCache);
49+
ApplicationContext context = testContext.getApplicationContext();
50+
assertNotNull(context);
51+
assertContextCacheStatistics(contextCache, testClass.getName(), expectedSize, expectedHitCount,
52+
expectedMissCount);
53+
}
54+
55+
@Test
56+
public void verifyCacheKeyIsBasedOnContextLoader() {
57+
loadAppCtxAndAssertCacheStats(AnnotationConfigContextLoaderTestCase.class, 1, 0, 1);
58+
loadAppCtxAndAssertCacheStats(AnnotationConfigContextLoaderTestCase.class, 1, 1, 1);
59+
loadAppCtxAndAssertCacheStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 1, 2);
60+
loadAppCtxAndAssertCacheStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 2, 2);
61+
loadAppCtxAndAssertCacheStats(AnnotationConfigContextLoaderTestCase.class, 2, 3, 2);
62+
loadAppCtxAndAssertCacheStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 4, 2);
63+
}
64+
65+
@Test
66+
public void verifyCacheKeyIsBasedOnActiveProfiles() {
67+
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 0, 1);
68+
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 1, 1);
69+
// Profiles {foo, bar} should hash to the same as {bar,foo}
70+
loadAppCtxAndAssertCacheStats(BarFooProfilesTestCase.class, 1, 2, 1);
71+
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 3, 1);
72+
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 4, 1);
73+
loadAppCtxAndAssertCacheStats(BarFooProfilesTestCase.class, 1, 5, 1);
74+
}
75+
76+
77+
@Configuration
78+
static class Config {
79+
}
80+
81+
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
82+
private static class AnnotationConfigContextLoaderTestCase {
83+
}
84+
85+
@ContextConfiguration(classes = Config.class, loader = CustomAnnotationConfigContextLoader.class)
86+
private static class CustomAnnotationConfigContextLoaderTestCase {
87+
}
88+
89+
private static class CustomAnnotationConfigContextLoader extends AnnotationConfigContextLoader {
90+
}
91+
92+
@ActiveProfiles({ "foo", "bar" })
93+
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
94+
private static class FooBarProfilesTestCase {
95+
}
96+
97+
@ActiveProfiles({ "bar", "foo" })
98+
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
99+
private static class BarFooProfilesTestCase {
100+
}
101+
102+
}

0 commit comments

Comments
 (0)