/* * Copyright 2012-2016 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.properties; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.core.annotation.AliasFor; import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@link AnnotationsPropertySource}. * * @author Phillip Webb * @author Andy Wilkinson */ public class AnnotationsPropertySourceTests { @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void createWhenSourceIsNullShouldThrowException() throws Exception { this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("Property source must not be null"); new AnnotationsPropertySource(null); } @Test public void propertiesWhenHasNoAnnotationShouldBeEmpty() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource( NoAnnotation.class); assertThat(source.getPropertyNames()).isEmpty(); assertThat(source.getProperty("value")).isNull(); } @Test public void propertiesWhenHasTypeLevelAnnotationShouldUseAttributeName() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource(TypeLevel.class); assertThat(source.getPropertyNames()).containsExactly("value"); assertThat(source.getProperty("value")).isEqualTo("abc"); } @Test public void propertiesWhenHasTypeLevelWithPrefixShouldUsePrefixedName() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource( TypeLevelWithPrefix.class); assertThat(source.getPropertyNames()).containsExactly("test.value"); assertThat(source.getProperty("test.value")).isEqualTo("abc"); } @Test public void propertiesWhenHasAttributeLevelWithPrefixShouldUsePrefixedName() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource( AttributeLevelWithPrefix.class); assertThat(source.getPropertyNames()).containsExactly("test"); assertThat(source.getProperty("test")).isEqualTo("abc"); } @Test public void propertiesWhenHasTypeAndAttributeLevelWithPrefixShouldUsePrefixedName() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource( TypeAndAttributeLevelWithPrefix.class); assertThat(source.getPropertyNames()).containsExactly("test.example"); assertThat(source.getProperty("test.example")).isEqualTo("abc"); } @Test public void propertiesWhenNotMappedAtTypeLevelShouldIgnoreAttributes() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource( NotMappedAtTypeLevel.class); assertThat(source.getPropertyNames()).containsExactly("value"); assertThat(source.getProperty("ignore")).isNull(); } @Test public void propertiesWhenNotMappedAtAttributeLevelShouldIgnoreAttributes() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource( NotMappedAtAttributeLevel.class); assertThat(source.getPropertyNames()).containsExactly("value"); assertThat(source.getProperty("ignore")).isNull(); } @Test public void propertiesWhenContainsArraysShouldExpandNames() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource(Arrays.class); assertThat(source.getPropertyNames()).contains("strings[0]", "strings[1]", "classes[0]", "classes[1]", "ints[0]", "ints[1]", "longs[0]", "longs[1]", "floats[0]", "floats[1]", "doubles[0]", "doubles[1]", "booleans[0]", "booleans[1]"); assertThat(source.getProperty("strings[0]")).isEqualTo("a"); assertThat(source.getProperty("strings[1]")).isEqualTo("b"); assertThat(source.getProperty("classes[0]")).isEqualTo(Integer.class); assertThat(source.getProperty("classes[1]")).isEqualTo(Long.class); assertThat(source.getProperty("ints[0]")).isEqualTo(1); assertThat(source.getProperty("ints[1]")).isEqualTo(2); assertThat(source.getProperty("longs[0]")).isEqualTo(1L); assertThat(source.getProperty("longs[1]")).isEqualTo(2L); assertThat(source.getProperty("floats[0]")).isEqualTo(1.0f); assertThat(source.getProperty("floats[1]")).isEqualTo(2.0f); assertThat(source.getProperty("doubles[0]")).isEqualTo(1.0); assertThat(source.getProperty("doubles[1]")).isEqualTo(2.0); assertThat(source.getProperty("booleans[0]")).isEqualTo(false); assertThat(source.getProperty("booleans[1]")).isEqualTo(true); } @Test public void propertiesWhenHasCamelCaseShouldConvertToKebabCase() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource( CamelCaseToKebabCase.class); assertThat(source.getPropertyNames()).contains("camel-case-to-kebab-case"); } @Test public void propertiesFromMetaAnnotationsAreMapped() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource( PropertiesFromSingleMetaAnnotation.class); assertThat(source.getPropertyNames()).containsExactly("value"); assertThat(source.getProperty("value")).isEqualTo("foo"); } @Test public void propertiesFromMultipleMetaAnnotationsAreMappedUsingTheirOwnPropertyMapping() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource( PropertiesFromMultipleMetaAnnotations.class); assertThat(source.getPropertyNames()).containsExactly("value", "test.value", "test.example"); assertThat(source.getProperty("value")).isEqualTo("alpha"); assertThat(source.getProperty("test.value")).isEqualTo("bravo"); assertThat(source.getProperty("test.example")).isEqualTo("charlie"); } @Test public void propertyMappedAttributesCanBeAliased() { AnnotationsPropertySource source = new AnnotationsPropertySource( PropertyMappedAttributeWithAnAlias.class); assertThat(source.getPropertyNames()).containsExactly("aliasing.value"); assertThat(source.getProperty("aliasing.value")).isEqualTo("baz"); } @Test public void selfAnnotatingAnnotationDoesNotCauseStackOverflow() { new AnnotationsPropertySource(PropertyMappedWithSelfAnnotatingAnnotation.class); } @Test public void typeLevelAnnotationOnSuperClass() { AnnotationsPropertySource source = new AnnotationsPropertySource( PropertyMappedAnnotationOnSuperClass.class); assertThat(source.getPropertyNames()).containsExactly("value"); assertThat(source.getProperty("value")).isEqualTo("abc"); } @Test public void aliasedPropertyMappedAttributeOnSuperClass() { AnnotationsPropertySource source = new AnnotationsPropertySource( AliasedPropertyMappedAnnotationOnSuperClass.class); assertThat(source.getPropertyNames()).containsExactly("aliasing.value"); assertThat(source.getProperty("aliasing.value")).isEqualTo("baz"); } @Test public void enumValueMapped() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource( EnumValueMapped.class); assertThat(source.getProperty("testenum.value")).isEqualTo(EnumItem.TWO); } @Test public void enumValueNotMapped() throws Exception { AnnotationsPropertySource source = new AnnotationsPropertySource( EnumValueNotMapped.class); assertThat(source.containsProperty("testenum.value")).isFalse(); } static class NoAnnotation { } @TypeLevelAnnotation("abc") static class TypeLevel { } @Retention(RetentionPolicy.RUNTIME) @PropertyMapping static @interface TypeLevelAnnotation { String value(); } @TypeLevelWithPrefixAnnotation("abc") static class TypeLevelWithPrefix { } @Retention(RetentionPolicy.RUNTIME) @PropertyMapping("test") static @interface TypeLevelWithPrefixAnnotation { String value(); } @AttributeLevelWithPrefixAnnotation("abc") static class AttributeLevelWithPrefix { } @Retention(RetentionPolicy.RUNTIME) static @interface AttributeLevelWithPrefixAnnotation { @PropertyMapping("test") String value(); } @TypeAndAttributeLevelWithPrefixAnnotation("abc") static class TypeAndAttributeLevelWithPrefix { } @Retention(RetentionPolicy.RUNTIME) @PropertyMapping("test") static @interface TypeAndAttributeLevelWithPrefixAnnotation { @PropertyMapping("example") String value(); } @NotMappedAtTypeLevelAnnotation("abc") static class NotMappedAtTypeLevel { } @Retention(RetentionPolicy.RUNTIME) @PropertyMapping(skip = SkipPropertyMapping.YES) static @interface NotMappedAtTypeLevelAnnotation { @PropertyMapping String value(); String ignore() default "xyz"; } @NotMappedAtAttributeLevelAnnotation("abc") static class NotMappedAtAttributeLevel { } @Retention(RetentionPolicy.RUNTIME) @PropertyMapping static @interface NotMappedAtAttributeLevelAnnotation { String value(); @PropertyMapping(skip = SkipPropertyMapping.YES) String ignore() default "xyz"; } @ArraysAnnotation(strings = { "a", "b" }, classes = { Integer.class, Long.class }, ints = { 1, 2 }, longs = { 1, 2 }, floats = { 1.0f, 2.0f }, doubles = { 1.0, 2.0 }, booleans = { false, true }) static class Arrays { } @Retention(RetentionPolicy.RUNTIME) @PropertyMapping static @interface ArraysAnnotation { String[] strings(); Class<?>[] classes(); int[] ints(); long[] longs(); float[] floats(); double[] doubles(); boolean[] booleans(); } @CamelCaseToKebabCaseAnnotation static class CamelCaseToKebabCase { } @Retention(RetentionPolicy.RUNTIME) @PropertyMapping static @interface CamelCaseToKebabCaseAnnotation { String camelCaseToKebabCase() default "abc"; } @PropertiesFromSingleMetaAnnotationAnnotation static class PropertiesFromSingleMetaAnnotation { } @Retention(RetentionPolicy.RUNTIME) @TypeLevelAnnotation("foo") static @interface PropertiesFromSingleMetaAnnotationAnnotation { } @PropertiesFromMultipleMetaAnnotationsAnnotation static class PropertiesFromMultipleMetaAnnotations { } @Retention(RetentionPolicy.RUNTIME) @TypeLevelAnnotation("alpha") @TypeLevelWithPrefixAnnotation("bravo") @TypeAndAttributeLevelWithPrefixAnnotation("charlie") static @interface PropertiesFromMultipleMetaAnnotationsAnnotation { } @AttributeWithAliasAnnotation("baz") static class PropertyMappedAttributeWithAnAlias { } @Retention(RetentionPolicy.RUNTIME) @AliasedAttributeAnnotation static @interface AttributeWithAliasAnnotation { @AliasFor(annotation = AliasedAttributeAnnotation.class, attribute = "value") String value() default "foo"; String someOtherAttribute() default "shouldNotBeMapped"; } @Retention(RetentionPolicy.RUNTIME) @PropertyMapping("aliasing") static @interface AliasedAttributeAnnotation { String value() default "bar"; } @Retention(RetentionPolicy.RUNTIME) @SelfAnnotating static @interface SelfAnnotating { } @SelfAnnotating static class PropertyMappedWithSelfAnnotatingAnnotation { } static class PropertyMappedAnnotationOnSuperClass extends TypeLevel { } static class AliasedPropertyMappedAnnotationOnSuperClass extends PropertyMappedAttributeWithAnAlias { } @EnumAnnotation(EnumItem.TWO) static class EnumValueMapped { } @EnumAnnotation(EnumItem.DEFAULT) static class EnumValueNotMapped { } @Retention(RetentionPolicy.RUNTIME) @PropertyMapping("testenum") static @interface EnumAnnotation { @PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE) EnumItem value() default EnumItem.DEFAULT; } enum EnumItem { DEFAULT, ONE, TWO } }