Merge branch '1.5.x'

This commit is contained in:
Phillip Webb
2017-01-27 17:50:01 -08:00
19 changed files with 857 additions and 130 deletions

View File

@@ -16,7 +16,6 @@
package org.springframework.boot.test.autoconfigure.data.mongo;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
@@ -24,8 +23,6 @@ import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
/**
* {@link TypeExcludeFilter} for {@link DataMongoTest @DataMongoTest}.
@@ -63,15 +60,14 @@ class DataMongoTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter
return this.annotation.useDefaultFilters();
}
@Override
protected boolean defaultInclude(final MetadataReader metadataReader,
final MetadataReaderFactory metadataReaderFactory) throws IOException {
return false;
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -27,6 +27,7 @@ import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.ObjectUtils;
/**
* Abstract base class for a {@link TypeExcludeFilter} that can be customized using an
@@ -75,6 +76,11 @@ public abstract class AnnotationCustomizableTypeExcludeFilter extends TypeExclud
return true;
}
}
for (Class<?> component : getComponentIncludes()) {
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, component)) {
return true;
}
}
return false;
}
@@ -103,10 +109,50 @@ public abstract class AnnotationCustomizableTypeExcludeFilter extends TypeExclud
protected abstract Set<Class<?>> getDefaultIncludes();
protected abstract Set<Class<?>> getComponentIncludes();
protected enum FilterType {
INCLUDE, EXCLUDE
}
@Override
public int hashCode() {
final int prime = 31;
int result = 0;
result = prime * result + ObjectUtils.hashCode(hasAnnotation());
for (FilterType filterType : FilterType.values()) {
result = prime * result
+ ObjectUtils.nullSafeHashCode(getFilters(filterType));
}
result = prime * result + ObjectUtils.hashCode(isUseDefaultFilters());
result = prime * result + ObjectUtils.nullSafeHashCode(getDefaultIncludes());
result = prime * result + ObjectUtils.nullSafeHashCode(getComponentIncludes());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
AnnotationCustomizableTypeExcludeFilter other = (AnnotationCustomizableTypeExcludeFilter) obj;
boolean result = true;
result &= hasAnnotation() == other.hasAnnotation();
for (FilterType filterType : FilterType.values()) {
result &= ObjectUtils.nullSafeEquals(getFilters(filterType),
other.getFilters(filterType));
}
result &= isUseDefaultFilters() == other.isUseDefaultFilters();
result &= ObjectUtils.nullSafeEquals(getDefaultIncludes(),
other.getDefaultIncludes());
result &= ObjectUtils.nullSafeEquals(getComponentIncludes(),
other.getComponentIncludes());
return result;
};
}

View File

@@ -18,6 +18,7 @@ package org.springframework.boot.test.autoconfigure.filter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -40,48 +41,65 @@ class TypeExcludeFiltersContextCustomizer implements ContextCustomizer {
private static final String EXCLUDE_FILTER_BEAN_NAME = TypeExcludeFilters.class
.getName();
private final Class<?> testClass;
private final Set<Class<? extends TypeExcludeFilter>> filterClasses;
private final Set<TypeExcludeFilter> filters;
TypeExcludeFiltersContextCustomizer(Class<?> testClass,
Set<Class<? extends TypeExcludeFilter>> filterClasses) {
this.testClass = testClass;
this.filterClasses = filterClasses;
this.filters = instantiateTypeExcludeFilters(testClass, filterClasses);
}
private Set<TypeExcludeFilter> instantiateTypeExcludeFilters(Class<?> testClass,
Set<Class<? extends TypeExcludeFilter>> filterClasses) {
Set<TypeExcludeFilter> filters = new LinkedHashSet<TypeExcludeFilter>();
for (Class<? extends TypeExcludeFilter> filterClass : filterClasses) {
filters.add(instantiateTypeExcludeFilter(testClass, filterClass));
}
return Collections.unmodifiableSet(filters);
}
private TypeExcludeFilter instantiateTypeExcludeFilter(Class<?> testClass,
Class<?> filterClass) {
try {
Constructor<?> constructor = getTypeExcludeFilterConstructor(filterClass);
ReflectionUtils.makeAccessible(constructor);
if (constructor.getParameterTypes().length == 1) {
return (TypeExcludeFilter) constructor.newInstance(testClass);
}
return (TypeExcludeFilter) constructor.newInstance();
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create filter for " + filterClass,
ex);
}
}
@Override
public int hashCode() {
return this.filterClasses.hashCode();
return this.filters.hashCode();
}
@Override
public boolean equals(Object obj) {
return (obj != null && getClass().equals(obj.getClass()) && this.filterClasses
.equals(((TypeExcludeFiltersContextCustomizer) obj).filterClasses));
return (obj != null && getClass().equals(obj.getClass()) && this.filters
.equals(((TypeExcludeFiltersContextCustomizer) obj).filters));
}
@Override
public void customizeContext(ConfigurableApplicationContext context,
MergedContextConfiguration mergedContextConfiguration) {
if (!this.filterClasses.isEmpty()) {
if (!this.filters.isEmpty()) {
context.getBeanFactory().registerSingleton(EXCLUDE_FILTER_BEAN_NAME,
createDelegatingTypeExcludeFilter());
}
}
private TypeExcludeFilter createDelegatingTypeExcludeFilter() {
final Set<TypeExcludeFilter> filters = new LinkedHashSet<TypeExcludeFilter>(
this.filterClasses.size());
for (Class<? extends TypeExcludeFilter> filterClass : this.filterClasses) {
filters.add(createTypeExcludeFilter(filterClass));
}
return new TypeExcludeFilter() {
@Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
for (TypeExcludeFilter filter : filters) {
for (TypeExcludeFilter filter : TypeExcludeFiltersContextCustomizer.this.filters) {
if (filter.match(metadataReader, metadataReaderFactory)) {
return true;
}
@@ -92,20 +110,6 @@ class TypeExcludeFiltersContextCustomizer implements ContextCustomizer {
};
}
private TypeExcludeFilter createTypeExcludeFilter(Class<?> type) {
try {
Constructor<?> constructor = getTypeExcludeFilterConstructor(type);
ReflectionUtils.makeAccessible(constructor);
if (constructor.getParameterTypes().length == 1) {
return (TypeExcludeFilter) constructor.newInstance(this.testClass);
}
return (TypeExcludeFilter) constructor.newInstance();
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create filter for " + type, ex);
}
}
private Constructor<?> getTypeExcludeFilterConstructor(Class<?> type)
throws NoSuchMethodException {
try {

View File

@@ -16,7 +16,6 @@
package org.springframework.boot.test.autoconfigure.jdbc;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
@@ -24,8 +23,6 @@ import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
/**
* {@link TypeExcludeFilter} for {@link JdbcTest @JdbcTest}.
@@ -62,15 +59,14 @@ class JdbcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
return this.annotation.useDefaultFilters();
}
@Override
protected boolean defaultInclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
return false;
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -77,4 +77,9 @@ class JsonExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
return DEFAULT_INCLUDES;
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -16,7 +16,6 @@
package org.springframework.boot.test.autoconfigure.orm.jpa;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
@@ -24,8 +23,6 @@ import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
/**
* {@link TypeExcludeFilter} for {@link DataJpaTest @DataJpaTest}.
@@ -62,15 +59,14 @@ class DataJpaTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
return this.annotation.useDefaultFilters();
}
@Override
protected boolean defaultInclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
return false;
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -16,7 +16,7 @@
package org.springframework.boot.test.autoconfigure.web.client;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -26,8 +26,6 @@ import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
/**
@@ -65,20 +63,6 @@ class RestClientExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
RestClientTest.class);
}
@Override
protected boolean defaultInclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
if (super.defaultInclude(metadataReader, metadataReaderFactory)) {
return true;
}
for (Class<?> controller : this.annotation.components()) {
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, controller)) {
return true;
}
}
return false;
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
@@ -105,4 +89,9 @@ class RestClientExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
return DEFAULT_INCLUDES;
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return new LinkedHashSet<Class<?>>(Arrays.asList(this.annotation.components()));
}
}

View File

@@ -16,7 +16,7 @@
package org.springframework.boot.test.autoconfigure.web.servlet;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -29,8 +29,6 @@ import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBea
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
@@ -74,20 +72,6 @@ class WebMvcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
WebMvcTest.class);
}
@Override
protected boolean defaultInclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
if (super.defaultInclude(metadataReader, metadataReaderFactory)) {
return true;
}
for (Class<?> controller : this.annotation.controllers()) {
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, controller)) {
return true;
}
}
return false;
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
@@ -117,4 +101,9 @@ class WebMvcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
return DEFAULT_INCLUDES;
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return new LinkedHashSet<Class<?>>(Arrays.asList(this.annotation.controllers()));
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright 2012-2017 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.boot.test.autoconfigure.cache;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.junit.Test;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.InitializationError;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.ExampleEntity;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@code ImportsContextCustomizerFactory} when used with
* {@link ImportAutoConfiguration}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class ImportsContextCustomizerFactoryWithAutoConfigurationTests {
static ApplicationContext contextFromTest;
@Test
public void testClassesThatHaveSameAnnotationsShareAContext()
throws InitializationError {
RunNotifier notifier = new RunNotifier();
new SpringJUnit4ClassRunner(DataJpaTest1.class).run(notifier);
ApplicationContext test1Context = contextFromTest;
new SpringJUnit4ClassRunner(DataJpaTest3.class).run(notifier);
ApplicationContext test2Context = contextFromTest;
assertThat(test1Context).isSameAs(test2Context);
}
@Test
public void testClassesThatOnlyHaveDifferingUnrelatedAnnotationsShareAContext()
throws InitializationError {
RunNotifier notifier = new RunNotifier();
new SpringJUnit4ClassRunner(DataJpaTest1.class).run(notifier);
ApplicationContext test1Context = contextFromTest;
new SpringJUnit4ClassRunner(DataJpaTest2.class).run(notifier);
ApplicationContext test2Context = contextFromTest;
assertThat(test1Context).isSameAs(test2Context);
}
@Test
public void testClassesThatOnlyHaveDifferingPropertyMappedAnnotationAttributesDoNotShareAContext()
throws InitializationError {
RunNotifier notifier = new RunNotifier();
new SpringJUnit4ClassRunner(DataJpaTest1.class).run(notifier);
ApplicationContext test1Context = contextFromTest;
new SpringJUnit4ClassRunner(DataJpaTest4.class).run(notifier);
ApplicationContext test2Context = contextFromTest;
assertThat(test1Context).isNotSameAs(test2Context);
}
@DataJpaTest
@ContextConfiguration(classes = EmptyConfig.class)
@Unrelated1
public static class DataJpaTest1 {
@Autowired
private ApplicationContext context;
@Test
public void test() {
contextFromTest = this.context;
}
}
@ContextConfiguration(classes = EmptyConfig.class)
@DataJpaTest
@Unrelated2
public static class DataJpaTest2 {
@Autowired
private ApplicationContext context;
@Test
public void test() {
contextFromTest = this.context;
}
}
@ContextConfiguration(classes = EmptyConfig.class)
@DataJpaTest
@Unrelated1
public static class DataJpaTest3 {
@Autowired
private ApplicationContext context;
@Test
public void test() {
contextFromTest = this.context;
}
}
@ContextConfiguration(classes = EmptyConfig.class)
@DataJpaTest(showSql = false)
@Unrelated1
public static class DataJpaTest4 {
@Autowired
private ApplicationContext context;
@Test
public void test() {
contextFromTest = this.context;
}
}
@Retention(RetentionPolicy.RUNTIME)
static @interface Unrelated1 {
}
@Retention(RetentionPolicy.RUNTIME)
static @interface Unrelated2 {
}
@Configuration
@EntityScan(basePackageClasses = ExampleEntity.class)
static class EmptyConfig {
}
}

View File

@@ -122,12 +122,22 @@ public class TypeExcludeFiltersContextCustomizerFactoryTests {
.equals(getClass().getName());
}
@Override
public int hashCode() {
return SimpleExclude.class.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj.getClass().equals(getClass());
}
}
static class TestClassAwareExclude extends SimpleExclude {
TestClassAwareExclude(Class<?> testClass) {
assertThat(testClass).isEqualTo(WithExcludeFilters.class);
assertThat(testClass).isNotNull();
}
}