/* * 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.context.properties.bind; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.junit.Before; import org.junit.Test; import org.springframework.boot.context.properties.source.ConfigurationProperty; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.MockConfigurationPropertySource; import org.springframework.core.ResolvableType; import org.springframework.core.env.StandardEnvironment; import org.springframework.test.context.support.TestPropertySourceUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; /** * Tests for {@link CollectionBinder}. * * @author Phillip Webb * @author Madhura Bhave */ public class CollectionBinderTests { private static final Bindable<List<Integer>> INTEGER_LIST = Bindable .listOf(Integer.class); private static final Bindable<List<String>> STRING_LIST = Bindable .listOf(String.class); private static final Bindable<Set<String>> STRING_SET = Bindable.setOf(String.class); private List<ConfigurationPropertySource> sources = new ArrayList<>(); private Binder binder; @Before public void setup() { this.binder = new Binder(this.sources); } @Test public void bindToCollectionShouldReturnPopulatedCollection() throws Exception { MockConfigurationPropertySource source = new MockConfigurationPropertySource(); source.put("foo[0]", "1"); source.put("foo[1]", "2"); source.put("foo[2]", "3"); this.sources.add(source); List<Integer> result = this.binder.bind("foo", INTEGER_LIST).get(); assertThat(result).containsExactly(1, 2, 3); } @Test public void bindToSetShouldReturnPopulatedCollection() throws Exception { MockConfigurationPropertySource source = new MockConfigurationPropertySource(); source.put("foo[0]", "a"); source.put("foo[1]", "b"); source.put("foo[2]", "c"); this.sources.add(source); Set<String> result = this.binder.bind("foo", STRING_SET).get(); assertThat(result).containsExactly("a", "b", "c"); } @Test public void bindToCollectionWhenNestedShouldReturnPopulatedCollection() throws Exception { MockConfigurationPropertySource source = new MockConfigurationPropertySource(); source.put("foo[0][0]", "1"); source.put("foo[0][1]", "2"); source.put("foo[1][0]", "3"); source.put("foo[1][1]", "4"); this.sources.add(source); Bindable<List<List<Integer>>> target = Bindable.of( ResolvableType.forClassWithGenerics(List.class, INTEGER_LIST.getType())); List<List<Integer>> result = this.binder.bind("foo", target).get(); assertThat(result).hasSize(2); assertThat(result.get(0)).containsExactly(1, 2); assertThat(result.get(1)).containsExactly(3, 4); } @Test public void bindToCollectionWhenNotInOrderShouldReturnPopulatedCollection() throws Exception { MockConfigurationPropertySource source = new MockConfigurationPropertySource(); source.put("foo[1]", "2"); source.put("foo[0]", "1"); source.put("foo[2]", "3"); this.sources.add(source); List<Integer> result = this.binder.bind("foo", INTEGER_LIST).get(); assertThat(result).containsExactly(1, 2, 3); } @Test public void bindToCollectionWhenNonSequentialShouldThrowException() throws Exception { MockConfigurationPropertySource source = new MockConfigurationPropertySource(); source.put("foo[0]", "2"); source.put("foo[1]", "1"); source.put("foo[3]", "3"); this.sources.add(source); try { this.binder.bind("foo", INTEGER_LIST); fail("No exception thrown"); } catch (BindException ex) { ex.printStackTrace(); Set<ConfigurationProperty> unbound = ((UnboundConfigurationPropertiesException) ex .getCause()).getUnboundProperties(); assertThat(unbound).hasSize(1); ConfigurationProperty property = unbound.iterator().next(); assertThat(property.getName().toString()).isEqualTo("foo[3]"); assertThat(property.getValue()).isEqualTo("3"); } } @Test public void bindToCollectionWhenNonIterableShouldReturnPopulatedCollection() throws Exception { MockConfigurationPropertySource source = new MockConfigurationPropertySource(); source.put("foo[1]", "2"); source.put("foo[0]", "1"); source.put("foo[2]", "3"); this.sources.add(source.nonIterable()); List<Integer> result = this.binder.bind("foo", INTEGER_LIST).get(); assertThat(result).containsExactly(1, 2, 3); } @Test public void bindToCollectionWhenMultipleSourceShouldOnlyUseFirst() throws Exception { MockConfigurationPropertySource source1 = new MockConfigurationPropertySource(); source1.put("bar", "baz"); this.sources.add(source1); MockConfigurationPropertySource source2 = new MockConfigurationPropertySource(); source2.put("foo[0]", "1"); source2.put("foo[1]", "2"); this.sources.add(source2); MockConfigurationPropertySource source3 = new MockConfigurationPropertySource(); source3.put("foo[0]", "7"); source3.put("foo[1]", "8"); source3.put("foo[2]", "9"); this.sources.add(source3); List<Integer> result = this.binder.bind("foo", INTEGER_LIST).get(); assertThat(result).containsExactly(1, 2); } @Test public void bindToCollectionWhenHasExistingCollectionShouldReplaceAllContents() throws Exception { this.sources.add(new MockConfigurationPropertySource("foo[0]", "1")); List<Integer> existing = new LinkedList<>(); existing.add(1000); existing.add(1001); List<Integer> result = this.binder .bind("foo", INTEGER_LIST.withExistingValue(existing)).get(); assertThat(result).isExactlyInstanceOf(LinkedList.class); assertThat(result).isSameAs(existing); assertThat(result).containsExactly(1); } @Test public void bindToCollectionWhenHasExistingCollectionButNoValueShouldReturnUnbound() throws Exception { this.sources.add(new MockConfigurationPropertySource("faf[0]", "1")); List<Integer> existing = new LinkedList<>(); existing.add(1000); BindResult<List<Integer>> result = this.binder.bind("foo", INTEGER_LIST.withExistingValue(existing)); assertThat(result.isBound()).isFalse(); } @Test public void bindToCollectionShouldRespectCollectionType() throws Exception { this.sources.add(new MockConfigurationPropertySource("foo[0]", "1")); ResolvableType type = ResolvableType.forClassWithGenerics(LinkedList.class, Integer.class); Object defaultList = this.binder.bind("foo", INTEGER_LIST).get(); Object customList = this.binder.bind("foo", Bindable.of(type)).get(); assertThat(customList).isExactlyInstanceOf(LinkedList.class) .isNotInstanceOf(defaultList.getClass()); } @Test public void bindToCollectionWhenNoValueShouldReturnUnbound() throws Exception { this.sources.add(new MockConfigurationPropertySource("faf.bar", "1")); BindResult<List<Integer>> result = this.binder.bind("foo", INTEGER_LIST); assertThat(result.isBound()).isFalse(); } @Test public void bindToCollectionWhenCommaListShouldReturnPopulatedCollection() throws Exception { this.sources.add(new MockConfigurationPropertySource("foo", "1,2,3")); List<Integer> result = this.binder.bind("foo", INTEGER_LIST).get(); assertThat(result).containsExactly(1, 2, 3); } @Test public void bindToCollectionWhenCommaListWithPlaceholdersShouldReturnPopulatedCollection() throws Exception { StandardEnvironment environment = new StandardEnvironment(); TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, "bar=1,2,3"); this.binder = new Binder(this.sources, new PropertySourcesPlaceholdersResolver(environment)); this.sources.add(new MockConfigurationPropertySource("foo", "${bar}")); List<Integer> result = this.binder.bind("foo", INTEGER_LIST).get(); assertThat(result).containsExactly(1, 2, 3); } @Test public void bindToCollectionWhenCommaListAndIndexedShouldOnlyUseFirst() throws Exception { MockConfigurationPropertySource source1 = new MockConfigurationPropertySource(); source1.put("foo", "1,2"); this.sources.add(source1); MockConfigurationPropertySource source2 = new MockConfigurationPropertySource(); source2.put("foo[0]", "2"); source2.put("foo[1]", "3"); List<Integer> result = this.binder.bind("foo", INTEGER_LIST).get(); assertThat(result).containsExactly(1, 2); } @Test public void bindToCollectionWhenIndexedAndCommaListShouldOnlyUseFirst() throws Exception { MockConfigurationPropertySource source1 = new MockConfigurationPropertySource(); source1.put("foo[0]", "1"); source1.put("foo[1]", "2"); this.sources.add(source1); MockConfigurationPropertySource source2 = new MockConfigurationPropertySource(); source2.put("foo", "2,3"); List<Integer> result = this.binder.bind("foo", INTEGER_LIST).get(); assertThat(result).containsExactly(1, 2); } @Test public void bindToCollectionWhenItemContainsCommasShouldReturnPopulatedCollection() throws Exception { MockConfigurationPropertySource source = new MockConfigurationPropertySource(); source.put("foo[0]", "1,2"); source.put("foo[1]", "3"); this.sources.add(source); List<String> result = this.binder.bind("foo", STRING_LIST).get(); assertThat(result).containsExactly("1,2", "3"); } @Test public void bindToCollectionWhenEmptyStringShouldReturnEmptyCollection() throws Exception { MockConfigurationPropertySource source = new MockConfigurationPropertySource(); source.put("foo", ""); this.sources.add(source); List<String> result = this.binder.bind("foo", STRING_LIST).get(); assertThat(result).isNotNull().isEmpty(); } }