/*
* 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.autoconfigure;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration;
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Tests for {@link ImportAutoConfigurationImportSelector}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class ImportAutoConfigurationImportSelectorTests {
private final ImportAutoConfigurationImportSelector importSelector = new TestImportAutoConfigurationImportSelector();
private final ConfigurableListableBeanFactory beanFactory = new DefaultListableBeanFactory();
@Mock
private Environment environment;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.importSelector.setBeanFactory(this.beanFactory);
this.importSelector.setEnvironment(this.environment);
this.importSelector.setResourceLoader(new DefaultResourceLoader());
}
@Test
public void importsAreSelected() throws Exception {
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ImportFreeMarker.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsExactly(FreeMarkerAutoConfiguration.class.getName());
}
@Test
public void importsAreSelectedUsingClassesAttribute() throws Exception {
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ImportFreeMarkerUsingClassesAttribute.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsExactly(FreeMarkerAutoConfiguration.class.getName());
}
@Test
public void propertyExclusionsAreNotApplied() throws Exception {
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ImportFreeMarker.class);
this.importSelector.selectImports(annotationMetadata);
verifyZeroInteractions(this.environment);
}
@Test
public void multipleImportsAreFound() throws Exception {
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
MultipleImports.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsOnly(FreeMarkerAutoConfiguration.class.getName(),
ThymeleafAutoConfiguration.class.getName());
}
@Test
public void selfAnnotatingAnnotationDoesNotCauseStackOverflow() throws IOException {
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ImportWithSelfAnnotatingAnnotation.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsOnly(ThymeleafAutoConfiguration.class.getName());
}
@Test
public void exclusionsAreApplied() throws Exception {
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
MultipleImportsWithExclusion.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsOnly(FreeMarkerAutoConfiguration.class.getName());
}
@Test
public void exclusionsWithoutImport() throws Exception {
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ExclusionWithoutImport.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsOnly(FreeMarkerAutoConfiguration.class.getName());
}
@Test
public void exclusionsAliasesAreApplied() throws Exception {
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ImportWithSelfAnnotatingAnnotationExclude.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).isEmpty();
}
@Test
public void determineImportsWhenUsingMetaWithoutClassesShouldBeEqual()
throws Exception {
Set<Object> set1 = this.importSelector.determineImports(
getAnnotationMetadata(ImportMetaAutoConfigurationWithUnrelatedOne.class));
Set<Object> set2 = this.importSelector.determineImports(
getAnnotationMetadata(ImportMetaAutoConfigurationWithUnrelatedTwo.class));
assertThat(set1).isEqualTo(set2);
assertThat(set1.hashCode()).isEqualTo(set2.hashCode());
}
@Test
public void determineImportsWhenUsingNonMetaWithoutClassesShouldBeSame()
throws Exception {
Set<Object> set1 = this.importSelector.determineImports(
getAnnotationMetadata(ImportAutoConfigurationWithUnrelatedOne.class));
Set<Object> set2 = this.importSelector.determineImports(
getAnnotationMetadata(ImportAutoConfigurationWithUnrelatedTwo.class));
assertThat(set1).isEqualTo(set2);
}
@Test
public void determineImportsWhenUsingNonMetaWithClassesShouldBeSame()
throws Exception {
Set<Object> set1 = this.importSelector.determineImports(
getAnnotationMetadata(ImportAutoConfigurationWithItemsOne.class));
Set<Object> set2 = this.importSelector.determineImports(
getAnnotationMetadata(ImportAutoConfigurationWithItemsTwo.class));
assertThat(set1).isEqualTo(set2);
}
@Test
public void determineImportsWhenUsingMetaExcludeWithoutClassesShouldBeEqual()
throws Exception {
Set<Object> set1 = this.importSelector.determineImports(getAnnotationMetadata(
ImportMetaAutoConfigurationExcludeWithUnrelatedOne.class));
Set<Object> set2 = this.importSelector.determineImports(getAnnotationMetadata(
ImportMetaAutoConfigurationExcludeWithUnrelatedTwo.class));
assertThat(set1).isEqualTo(set2);
assertThat(set1.hashCode()).isEqualTo(set2.hashCode());
}
@Test
public void determineImportsWhenUsingMetaDifferentExcludeWithoutClassesShouldBeDifferent()
throws Exception {
Set<Object> set1 = this.importSelector.determineImports(getAnnotationMetadata(
ImportMetaAutoConfigurationExcludeWithUnrelatedOne.class));
Set<Object> set2 = this.importSelector.determineImports(
getAnnotationMetadata(ImportMetaAutoConfigurationWithUnrelatedTwo.class));
assertThat(set1).isNotEqualTo(set2);
}
private AnnotationMetadata getAnnotationMetadata(Class<?> source) throws IOException {
return new SimpleMetadataReaderFactory().getMetadataReader(source.getName())
.getAnnotationMetadata();
}
@ImportAutoConfiguration(FreeMarkerAutoConfiguration.class)
static class ImportFreeMarker {
}
@ImportAutoConfiguration(classes = FreeMarkerAutoConfiguration.class)
static class ImportFreeMarkerUsingClassesAttribute {
}
@ImportOne
@ImportTwo
static class MultipleImports {
}
@ImportOne
@ImportTwo
@ImportAutoConfiguration(exclude = ThymeleafAutoConfiguration.class)
static class MultipleImportsWithExclusion {
}
@ImportOne
@ImportAutoConfiguration(exclude = ThymeleafAutoConfiguration.class)
static class ExclusionWithoutImport {
}
@SelfAnnotating
static class ImportWithSelfAnnotatingAnnotation {
}
@SelfAnnotating(excludeAutoConfiguration = ThymeleafAutoConfiguration.class)
static class ImportWithSelfAnnotatingAnnotationExclude {
}
@Retention(RetentionPolicy.RUNTIME)
@ImportAutoConfiguration(FreeMarkerAutoConfiguration.class)
@interface ImportOne {
}
@Retention(RetentionPolicy.RUNTIME)
@ImportAutoConfiguration(ThymeleafAutoConfiguration.class)
@interface ImportTwo {
}
@MetaImportAutoConfiguration
@UnrelatedOne
static class ImportMetaAutoConfigurationWithUnrelatedOne {
}
@MetaImportAutoConfiguration
@UnrelatedTwo
static class ImportMetaAutoConfigurationWithUnrelatedTwo {
}
@ImportAutoConfiguration
@UnrelatedOne
static class ImportAutoConfigurationWithUnrelatedOne {
}
@ImportAutoConfiguration
@UnrelatedTwo
static class ImportAutoConfigurationWithUnrelatedTwo {
}
@ImportAutoConfiguration(classes = ThymeleafAutoConfiguration.class)
@UnrelatedOne
static class ImportAutoConfigurationWithItemsOne {
}
@ImportAutoConfiguration(classes = ThymeleafAutoConfiguration.class)
@UnrelatedTwo
static class ImportAutoConfigurationWithItemsTwo {
}
@MetaImportAutoConfiguration(exclude = ThymeleafAutoConfiguration.class)
@UnrelatedOne
static class ImportMetaAutoConfigurationExcludeWithUnrelatedOne {
}
@MetaImportAutoConfiguration(exclude = ThymeleafAutoConfiguration.class)
@UnrelatedTwo
static class ImportMetaAutoConfigurationExcludeWithUnrelatedTwo {
}
@ImportAutoConfiguration
@Retention(RetentionPolicy.RUNTIME)
@interface MetaImportAutoConfiguration {
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] exclude() default {};
}
@Retention(RetentionPolicy.RUNTIME)
@interface UnrelatedOne {
}
@Retention(RetentionPolicy.RUNTIME)
@interface UnrelatedTwo {
}
@Retention(RetentionPolicy.RUNTIME)
@ImportAutoConfiguration(ThymeleafAutoConfiguration.class)
@SelfAnnotating
@interface SelfAnnotating {
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}
private static class TestImportAutoConfigurationImportSelector
extends ImportAutoConfigurationImportSelector {
@Override
protected Collection<String> loadFactoryNames(Class<?> source) {
if (source == MetaImportAutoConfiguration.class) {
return Arrays.asList(ThymeleafAutoConfiguration.class.getName(),
FreeMarkerAutoConfiguration.class.getName());
}
return super.loadFactoryNames(source);
}
}
}