Add system property support to TestPropertyValues
Update `TestPropertyValues` so that it can also be used to update system properties. Properties are set before the call is made and restored to their previous value afterwards. Fixes gh-9792
This commit is contained in:
parent
2f0f25f5ad
commit
c6f55ef46d
|
@ -16,29 +16,36 @@
|
||||||
|
|
||||||
package org.springframework.boot.test.util;
|
package org.springframework.boot.test.util;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.io.Closeable;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import com.google.common.collect.Streams;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.core.env.MapPropertySource;
|
import org.springframework.core.env.MapPropertySource;
|
||||||
import org.springframework.core.env.MutablePropertySources;
|
import org.springframework.core.env.MutablePropertySources;
|
||||||
import org.springframework.core.env.PropertySource;
|
import org.springframework.core.env.PropertySource;
|
||||||
import org.springframework.core.env.SystemEnvironmentPropertySource;
|
import org.springframework.core.env.SystemEnvironmentPropertySource;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test utilities for adding properties to the environment. The type of
|
* Test utilities for adding properties. Properties can be applied to a Spring
|
||||||
* {@link PropertySource} to be added can be specified by {@link Type}.
|
* {@link Environment} or to the {@link System#getProperties() system environment}.
|
||||||
*
|
*
|
||||||
* @author Madhura Bhave
|
* @author Madhura Bhave
|
||||||
|
* @author Phillip Webb
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public final class TestPropertyValues {
|
public final class TestPropertyValues {
|
||||||
|
|
||||||
private final Map<String, Object> properties = new HashMap<>();
|
private final Map<String, Object> properties = new LinkedHashMap<>();
|
||||||
|
|
||||||
private TestPropertyValues(String[] pairs) {
|
private TestPropertyValues(String[] pairs) {
|
||||||
addProperties(pairs);
|
addProperties(pairs);
|
||||||
|
@ -46,11 +53,21 @@ public final class TestPropertyValues {
|
||||||
|
|
||||||
private void addProperties(String[] pairs) {
|
private void addProperties(String[] pairs) {
|
||||||
for (String pair : pairs) {
|
for (String pair : pairs) {
|
||||||
|
and(pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder method to append another property pair the underlying map of properties.
|
||||||
|
* @param pair The property pair to add
|
||||||
|
* @return the existing instance of {@link TestPropertyValues}
|
||||||
|
*/
|
||||||
|
public TestPropertyValues and(String pair) {
|
||||||
int index = getSeparatorIndex(pair);
|
int index = getSeparatorIndex(pair);
|
||||||
String key = pair.substring(0, index > 0 ? index : pair.length());
|
String key = pair.substring(0, index > 0 ? index : pair.length());
|
||||||
String value = index > 0 ? pair.substring(index + 1) : "";
|
String value = index > 0 ? pair.substring(index + 1) : "";
|
||||||
this.properties.put(key.trim(), value.trim());
|
and(key.trim(), value.trim());
|
||||||
}
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getSeparatorIndex(String pair) {
|
private int getSeparatorIndex(String pair) {
|
||||||
|
@ -67,12 +84,16 @@ public final class TestPropertyValues {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder method to append another property to the underlying map of properties.
|
* Builder method to append another property to the underlying map of properties.
|
||||||
* @param key The property key
|
* @param name The property name
|
||||||
* @param value The property value
|
* @param value The property value
|
||||||
* @return the existing instance of {@link TestPropertyValues}
|
* @return the existing instance of {@link TestPropertyValues}
|
||||||
*/
|
*/
|
||||||
public TestPropertyValues and(String key, String value) {
|
public TestPropertyValues and(String name, String value) {
|
||||||
this.properties.put(key, value);
|
if (StringUtils.isEmpty(name) && StringUtils.isEmpty(value)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
Assert.hasLength(name, "Name must not be empty");
|
||||||
|
this.properties.put(name, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +141,25 @@ public final class TestPropertyValues {
|
||||||
ConfigurationPropertySources.attach(environment);
|
ConfigurationPropertySources.attach(environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the properties to the {@link System#getProperties() system properties} for the
|
||||||
|
* duration of the {@code call}, restoring previous values then the call completes.
|
||||||
|
* @param <T> the result type
|
||||||
|
* @param call the call to make
|
||||||
|
* @return the result of the call
|
||||||
|
*/
|
||||||
|
public <T> T applyToSystemProperties(Callable<T> call) {
|
||||||
|
try (SystemPropertiesHandler handler = new SystemPropertiesHandler()) {
|
||||||
|
return call.call();
|
||||||
|
}
|
||||||
|
catch (RuntimeException ex) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void addToSources(MutablePropertySources sources, Type type, String name) {
|
private void addToSources(MutablePropertySources sources, Type type, String name) {
|
||||||
if (sources.contains(name)) {
|
if (sources.contains(name)) {
|
||||||
|
@ -136,6 +176,41 @@ public final class TestPropertyValues {
|
||||||
sources.addFirst(source);
|
sources.addFirst(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new empty {@link TestPropertyValues} instance.
|
||||||
|
* @return an empty instance
|
||||||
|
*/
|
||||||
|
public static TestPropertyValues empty() {
|
||||||
|
return of();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new {@link TestPropertyValues} with the underlying map populated with the
|
||||||
|
* given property pair.
|
||||||
|
* @param name the property name
|
||||||
|
* @param value the property value
|
||||||
|
* @return the new instance
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static TestPropertyValues ofPair(String name, String value) {
|
||||||
|
return of().and(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new {@link TestPropertyValues} with the underlying map populated with the
|
||||||
|
* given property pairs. Name-value pairs can be specified with colon (":") or equals
|
||||||
|
* ("=") separators.
|
||||||
|
* @param pairs The key value pairs for properties that need to be added to the
|
||||||
|
* environment
|
||||||
|
* @return the new instance
|
||||||
|
*/
|
||||||
|
public static TestPropertyValues of(Iterable<String> pairs) {
|
||||||
|
if (pairs == null) {
|
||||||
|
return of();
|
||||||
|
}
|
||||||
|
return of(Streams.stream(pairs).toArray(String[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a new {@link TestPropertyValues} with the underlying map populated with the
|
* Return a new {@link TestPropertyValues} with the underlying map populated with the
|
||||||
* given property pairs. Name-value pairs can be specified with colon (":") or equals
|
* given property pairs. Name-value pairs can be specified with colon (":") or equals
|
||||||
|
@ -175,4 +250,40 @@ public final class TestPropertyValues {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to apply and restore system properties.
|
||||||
|
*/
|
||||||
|
private class SystemPropertiesHandler implements Closeable {
|
||||||
|
|
||||||
|
private final Map<String, Object> properties;
|
||||||
|
|
||||||
|
private final Map<String, String> previous;
|
||||||
|
|
||||||
|
SystemPropertiesHandler() {
|
||||||
|
this.properties = new LinkedHashMap<>(TestPropertyValues.this.properties);
|
||||||
|
this.previous = apply(this.properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> apply(Map<String, ?> properties) {
|
||||||
|
Map<String, String> previous = new LinkedHashMap<>();
|
||||||
|
properties.forEach((name, value) -> previous.put(name,
|
||||||
|
setOrClear(name, (String) value)));
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
this.previous.forEach(this::setOrClear);
|
||||||
|
};
|
||||||
|
|
||||||
|
private String setOrClear(String name, String value) {
|
||||||
|
Assert.notNull(name, "Name must not be null");
|
||||||
|
if (value == null) {
|
||||||
|
return (String) System.getProperties().remove(name);
|
||||||
|
}
|
||||||
|
return (String) System.getProperties().setProperty(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
* Tests for {@link TestPropertyValues}.
|
* Tests for {@link TestPropertyValues}.
|
||||||
*
|
*
|
||||||
* @author Madhura Bhave
|
* @author Madhura Bhave
|
||||||
|
* @author Phillip Webb
|
||||||
*/
|
*/
|
||||||
public class TestPropertyValuesTests {
|
public class TestPropertyValuesTests {
|
||||||
|
|
||||||
|
@ -95,4 +96,47 @@ public class TestPropertyValuesTests {
|
||||||
assertThat(this.environment.getProperty("bling.blah")).isEqualTo("bing");
|
assertThat(this.environment.getProperty("bling.blah")).isEqualTo("bing");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void applyToSystemPropertiesShouldSetSystemProperties() throws Exception {
|
||||||
|
TestPropertyValues.of("foo=bar").applyToSystemProperties(() -> {
|
||||||
|
assertThat(System.getProperty("foo")).isEqualTo("bar");
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void applyToSystemPropertiesShouldRestoreSystemProperties() throws Exception {
|
||||||
|
System.setProperty("foo", "bar1");
|
||||||
|
System.clearProperty("baz");
|
||||||
|
try {
|
||||||
|
TestPropertyValues.of("foo=bar2", "baz=bing").applyToSystemProperties(() -> {
|
||||||
|
assertThat(System.getProperty("foo")).isEqualTo("bar2");
|
||||||
|
assertThat(System.getProperty("baz")).isEqualTo("bing");
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
assertThat(System.getProperty("foo")).isEqualTo("bar1");
|
||||||
|
assertThat(System.getProperties()).doesNotContainKey("baz");
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
System.clearProperty("foo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void applyToSystemPropertiesWhenValueIsNullShouldRemoveProperty()
|
||||||
|
throws Exception {
|
||||||
|
System.setProperty("foo", "bar1");
|
||||||
|
try {
|
||||||
|
TestPropertyValues.ofPair("foo", null).applyToSystemProperties(() -> {
|
||||||
|
assertThat(System.getProperties()).doesNotContainKey("foo");
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
assertThat(System.getProperty("foo")).isEqualTo("bar1");
|
||||||
|
assertThat(System.getProperties()).doesNotContainKey("baz");
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
System.clearProperty("foo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue