Use Gradle test fixture support for spring-context-support

See gh-23550
This commit is contained in:
Sam Brannen
2019-12-29 15:43:15 +01:00
parent 61d4ee594d
commit 0335db23c9
17 changed files with 33 additions and 18 deletions

View File

@@ -1,474 +0,0 @@
/*
* Copyright 2002-2019 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.cache.jcache.config;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.ApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIOException;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
/**
* @author Stephane Nicoll
*/
public abstract class AbstractJCacheAnnotationTests {
public static final String DEFAULT_CACHE = "default";
public static final String EXCEPTION_CACHE = "exception";
protected String keyItem;
protected ApplicationContext ctx;
private JCacheableService<?> service;
private CacheManager cacheManager;
protected abstract ApplicationContext getApplicationContext();
@BeforeEach
public void setUp(TestInfo testInfo) {
this.keyItem = testInfo.getTestMethod().get().getName();
this.ctx = getApplicationContext();
this.service = this.ctx.getBean(JCacheableService.class);
this.cacheManager = this.ctx.getBean("cacheManager", CacheManager.class);
}
@Test
public void cache() {
Object first = service.cache(this.keyItem);
Object second = service.cache(this.keyItem);
assertThat(second).isSameAs(first);
}
@Test
public void cacheNull() {
Cache cache = getCache(DEFAULT_CACHE);
assertThat(cache.get(this.keyItem)).isNull();
Object first = service.cacheNull(this.keyItem);
Object second = service.cacheNull(this.keyItem);
assertThat(second).isSameAs(first);
Cache.ValueWrapper wrapper = cache.get(this.keyItem);
assertThat(wrapper).isNotNull();
assertThat(wrapper.get()).isSameAs(first);
assertThat(wrapper.get()).as("Cached value should be null").isNull();
}
@Test
public void cacheException() {
Cache cache = getCache(EXCEPTION_CACHE);
Object key = createKey(this.keyItem);
assertThat(cache.get(key)).isNull();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
service.cacheWithException(this.keyItem, true));
Cache.ValueWrapper result = cache.get(key);
assertThat(result).isNotNull();
assertThat(result.get().getClass()).isEqualTo(UnsupportedOperationException.class);
}
@Test
public void cacheExceptionVetoed() {
Cache cache = getCache(EXCEPTION_CACHE);
Object key = createKey(this.keyItem);
assertThat(cache.get(key)).isNull();
assertThatNullPointerException().isThrownBy(() ->
service.cacheWithException(this.keyItem, false));
assertThat(cache.get(key)).isNull();
}
@Test
public void cacheCheckedException() {
Cache cache = getCache(EXCEPTION_CACHE);
Object key = createKey(this.keyItem);
assertThat(cache.get(key)).isNull();
assertThatIOException().isThrownBy(() ->
service.cacheWithCheckedException(this.keyItem, true));
Cache.ValueWrapper result = cache.get(key);
assertThat(result).isNotNull();
assertThat(result.get().getClass()).isEqualTo(IOException.class);
}
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
@Test
public void cacheExceptionRewriteCallStack() {
long ref = service.exceptionInvocations();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
service.cacheWithException(this.keyItem, true))
.satisfies(first -> {
// Sanity check, this particular call has called the service
// First call should not have been cached
assertThat(service.exceptionInvocations()).isEqualTo(ref + 1);
UnsupportedOperationException second = methodInCallStack(this.keyItem);
// Sanity check, this particular call has *not* called the service
// Second call should have been cached
assertThat(service.exceptionInvocations()).isEqualTo(ref + 1);
assertThat(first).hasCause(second.getCause());
assertThat(first).hasMessage(second.getMessage());
// Original stack must not contain any reference to methodInCallStack
assertThat(contain(first, AbstractJCacheAnnotationTests.class.getName(), "methodInCallStack")).isFalse();
assertThat(contain(second, AbstractJCacheAnnotationTests.class.getName(), "methodInCallStack")).isTrue();
});
}
@Test
public void cacheAlwaysInvoke() {
Object first = service.cacheAlwaysInvoke(this.keyItem);
Object second = service.cacheAlwaysInvoke(this.keyItem);
assertThat(second).isNotSameAs(first);
}
@Test
public void cacheWithPartialKey() {
Object first = service.cacheWithPartialKey(this.keyItem, true);
Object second = service.cacheWithPartialKey(this.keyItem, false);
// second argument not used, see config
assertThat(second).isSameAs(first);
}
@Test
public void cacheWithCustomCacheResolver() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
service.cacheWithCustomCacheResolver(this.keyItem);
// Cache in mock cache
assertThat(cache.get(key)).isNull();
}
@Test
public void cacheWithCustomKeyGenerator() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
service.cacheWithCustomKeyGenerator(this.keyItem, "ignored");
assertThat(cache.get(key)).isNull();
}
@Test
public void put() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
assertThat(cache.get(key)).isNull();
service.put(this.keyItem, value);
Cache.ValueWrapper result = cache.get(key);
assertThat(result).isNotNull();
assertThat(result.get()).isEqualTo(value);
}
@Test
public void putWithException() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
assertThat(cache.get(key)).isNull();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
service.putWithException(this.keyItem, value, true));
Cache.ValueWrapper result = cache.get(key);
assertThat(result).isNotNull();
assertThat(result.get()).isEqualTo(value);
}
@Test
public void putWithExceptionVetoPut() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
assertThat(cache.get(key)).isNull();
assertThatNullPointerException().isThrownBy(() ->
service.putWithException(this.keyItem, value, false));
assertThat(cache.get(key)).isNull();
}
@Test
public void earlyPut() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
assertThat(cache.get(key)).isNull();
service.earlyPut(this.keyItem, value);
Cache.ValueWrapper result = cache.get(key);
assertThat(result).isNotNull();
assertThat(result.get()).isEqualTo(value);
}
@Test
public void earlyPutWithException() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
assertThat(cache.get(key)).isNull();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
service.earlyPutWithException(this.keyItem, value, true));
Cache.ValueWrapper result = cache.get(key);
assertThat(result).isNotNull();
assertThat(result.get()).isEqualTo(value);
}
@Test
public void earlyPutWithExceptionVetoPut() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
assertThat(cache.get(key)).isNull();
assertThatNullPointerException().isThrownBy(() ->
service.earlyPutWithException(this.keyItem, value, false));
// This will be cached anyway as the earlyPut has updated the cache before
Cache.ValueWrapper result = cache.get(key);
assertThat(result).isNotNull();
assertThat(result.get()).isEqualTo(value);
}
@Test
public void remove() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
cache.put(key, value);
service.remove(this.keyItem);
assertThat(cache.get(key)).isNull();
}
@Test
public void removeWithException() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
cache.put(key, value);
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
service.removeWithException(this.keyItem, true));
assertThat(cache.get(key)).isNull();
}
@Test
public void removeWithExceptionVetoRemove() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
cache.put(key, value);
assertThatNullPointerException().isThrownBy(() ->
service.removeWithException(this.keyItem, false));
Cache.ValueWrapper wrapper = cache.get(key);
assertThat(wrapper).isNotNull();
assertThat(wrapper.get()).isEqualTo(value);
}
@Test
public void earlyRemove() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
cache.put(key, value);
service.earlyRemove(this.keyItem);
assertThat(cache.get(key)).isNull();
}
@Test
public void earlyRemoveWithException() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
cache.put(key, value);
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
service.earlyRemoveWithException(this.keyItem, true));
assertThat(cache.get(key)).isNull();
}
@Test
public void earlyRemoveWithExceptionVetoRemove() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
Object value = new Object();
cache.put(key, value);
assertThatNullPointerException().isThrownBy(() ->
service.earlyRemoveWithException(this.keyItem, false));
// This will be remove anyway as the earlyRemove has removed the cache before
assertThat(cache.get(key)).isNull();
}
@Test
public void removeAll() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
cache.put(key, new Object());
service.removeAll();
assertThat(isEmpty(cache)).isTrue();
}
@Test
public void removeAllWithException() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
cache.put(key, new Object());
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
service.removeAllWithException(true));
assertThat(isEmpty(cache)).isTrue();
}
@Test
public void removeAllWithExceptionVetoRemove() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
cache.put(key, new Object());
assertThatNullPointerException().isThrownBy(() ->
service.removeAllWithException(false));
assertThat(cache.get(key)).isNotNull();
}
@Test
public void earlyRemoveAll() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
cache.put(key, new Object());
service.earlyRemoveAll();
assertThat(isEmpty(cache)).isTrue();
}
@Test
public void earlyRemoveAllWithException() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
cache.put(key, new Object());
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
service.earlyRemoveAllWithException(true));
assertThat(isEmpty(cache)).isTrue();
}
@Test
public void earlyRemoveAllWithExceptionVetoRemove() {
Cache cache = getCache(DEFAULT_CACHE);
Object key = createKey(this.keyItem);
cache.put(key, new Object());
assertThatNullPointerException().isThrownBy(() ->
service.earlyRemoveAllWithException(false));
// This will be remove anyway as the earlyRemove has removed the cache before
assertThat(isEmpty(cache)).isTrue();
}
protected boolean isEmpty(Cache cache) {
ConcurrentHashMap<?, ?> nativeCache = (ConcurrentHashMap<?, ?>) cache.getNativeCache();
return nativeCache.isEmpty();
}
private Object createKey(Object... params) {
return SimpleKeyGenerator.generateKey(params);
}
private Cache getCache(String name) {
Cache cache = cacheManager.getCache(name);
assertThat(cache).as("required cache " + name + " does not exist").isNotNull();
return cache;
}
/**
* The only purpose of this method is to invoke a particular method on the
* service so that the call stack is different.
*/
private UnsupportedOperationException methodInCallStack(String keyItem) {
try {
service.cacheWithException(keyItem, true);
throw new IllegalStateException("Should have thrown an exception");
}
catch (UnsupportedOperationException e) {
return e;
}
}
private boolean contain(Throwable t, String className, String methodName) {
for (StackTraceElement element : t.getStackTrace()) {
if (className.equals(element.getClassName()) && methodName.equals(element.getMethodName())) {
return true;
}
}
return false;
}
}

View File

@@ -37,6 +37,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.contextsupport.testfixture.jcache.JCacheableService;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

View File

@@ -43,6 +43,8 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.test.fixtures.cache.SomeKeyGenerator;
import org.springframework.contextsupport.testfixture.jcache.AbstractJCacheAnnotationTests;
import org.springframework.contextsupport.testfixture.jcache.JCacheableService;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;

View File

@@ -24,6 +24,7 @@ import org.springframework.cache.jcache.interceptor.JCacheInterceptor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.contextsupport.testfixture.jcache.AbstractJCacheAnnotationTests;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -18,6 +18,7 @@ package org.springframework.cache.jcache.config;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.contextsupport.testfixture.jcache.AbstractJCacheAnnotationTests;
/**
* @author Stephane Nicoll

View File

@@ -1,68 +0,0 @@
/*
* 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
*
* 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.cache.jcache.config;
import java.io.IOException;
/**
* @author Stephane Nicoll
*/
public interface JCacheableService<T> {
T cache(String id);
T cacheNull(String id);
T cacheWithException(String id, boolean matchFilter);
T cacheWithCheckedException(String id, boolean matchFilter) throws IOException;
T cacheAlwaysInvoke(String id);
T cacheWithPartialKey(String id, boolean notUsed);
T cacheWithCustomCacheResolver(String id);
T cacheWithCustomKeyGenerator(String id, String anotherId);
void put(String id, Object value);
void putWithException(String id, Object value, boolean matchFilter);
void earlyPut(String id, Object value);
void earlyPutWithException(String id, Object value, boolean matchFilter);
void remove(String id);
void removeWithException(String id, boolean matchFilter);
void earlyRemove(String id);
void earlyRemoveWithException(String id, boolean matchFilter);
void removeAll();
void removeAllWithException(boolean matchFilter);
void earlyRemoveAll();
void earlyRemoveAllWithException(boolean matchFilter);
long exceptionInvocations();
}

View File

@@ -30,9 +30,9 @@ import javax.cache.annotation.CacheValue;
import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.cache.jcache.config.JCacheableService;
import org.springframework.cache.jcache.support.TestableCacheKeyGenerator;
import org.springframework.cache.jcache.support.TestableCacheResolverFactory;
import org.springframework.contextsupport.testfixture.cache.TestableCacheKeyGenerator;
import org.springframework.contextsupport.testfixture.cache.TestableCacheResolverFactory;
import org.springframework.contextsupport.testfixture.jcache.JCacheableService;
/**
* Repository sample with a @CacheDefaults annotation

View File

@@ -32,9 +32,9 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.jcache.AbstractJCacheTests;
import org.springframework.cache.jcache.support.TestableCacheKeyGenerator;
import org.springframework.cache.jcache.support.TestableCacheResolver;
import org.springframework.cache.jcache.support.TestableCacheResolverFactory;
import org.springframework.contextsupport.testfixture.cache.TestableCacheKeyGenerator;
import org.springframework.contextsupport.testfixture.cache.TestableCacheResolver;
import org.springframework.contextsupport.testfixture.cache.TestableCacheResolverFactory;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

View File

@@ -1,51 +0,0 @@
/*
* Copyright 2002-2019 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.cache.jcache.support;
import java.lang.annotation.Annotation;
import javax.cache.annotation.CacheKeyGenerator;
import javax.cache.annotation.CacheKeyInvocationContext;
import javax.cache.annotation.GeneratedCacheKey;
import org.springframework.cache.interceptor.SimpleKey;
/**
* A simple test key generator that only takes the first key arguments into
* account. To be used with a multi parameters key to validate it has been
* used properly.
*
* @author Stephane Nicoll
*/
public class TestableCacheKeyGenerator implements CacheKeyGenerator {
@Override
public GeneratedCacheKey generateCacheKey(CacheKeyInvocationContext<? extends Annotation> context) {
return new SimpleGeneratedCacheKey(context.getKeyParameters()[0]);
}
@SuppressWarnings("serial")
private static class SimpleGeneratedCacheKey extends SimpleKey implements GeneratedCacheKey {
public SimpleGeneratedCacheKey(Object... elements) {
super(elements);
}
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright 2002-2019 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.cache.jcache.support;
import java.lang.annotation.Annotation;
import javax.cache.Cache;
import javax.cache.annotation.CacheInvocationContext;
import javax.cache.annotation.CacheResolver;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* @author Stephane Nicoll
*/
public class TestableCacheResolver implements CacheResolver {
@Override
public <K, V> Cache<K, V> resolveCache(CacheInvocationContext<? extends Annotation> cacheInvocationContext) {
String cacheName = cacheInvocationContext.getCacheName();
@SuppressWarnings("unchecked")
Cache<K, V> mock = mock(Cache.class);
given(mock.getName()).willReturn(cacheName);
return mock;
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2002-2014 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.cache.jcache.support;
import java.lang.annotation.Annotation;
import javax.cache.annotation.CacheMethodDetails;
import javax.cache.annotation.CacheResolver;
import javax.cache.annotation.CacheResolverFactory;
import javax.cache.annotation.CacheResult;
/**
* @author Stephane Nicoll
*/
public class TestableCacheResolverFactory implements CacheResolverFactory {
@Override
public CacheResolver getCacheResolver(CacheMethodDetails<? extends Annotation> cacheMethodDetails) {
return new TestableCacheResolver();
}
@Override
public CacheResolver getExceptionCacheResolver(CacheMethodDetails<CacheResult> cacheMethodDetails) {
return new TestableCacheResolver();
}
}