Preserve placeholders in @TestPropertySource locations

Prior to this commit, it was impossible to include a placeholder (i.e.,
${placeholder.name}) in a Properties file location configured via
@TestPropertySource if the placeholder was immediately followed by a
relative path (i.e., "../"). This was due to the fact that the location
was always cleaned using StringUtils.cleanPath(), which removed the
placeholder and the relative path syntax.

This commit fixes this by preserving all placeholders in
@TestPropertySource locations by simply not cleaning the locations if
they contain placeholders.

Closes gh-23544
This commit is contained in:
Sam Brannen 2019-08-30 18:57:20 +02:00
parent 9fa9a09007
commit ab779eb431
3 changed files with 30 additions and 7 deletions

View File

@ -114,7 +114,7 @@ public abstract class TestPropertySourceUtils {
logger.trace(String.format("Processing locations for TestPropertySource attributes %s", attrs));
}
String[] locationsArray = TestContextResourceUtils.convertToClasspathResourcePaths(
attrs.getDeclaringClass(), attrs.getLocations());
attrs.getDeclaringClass(), true, attrs.getLocations());
locations.addAll(0, Arrays.asList(locationsArray));
if (!attrs.isInheritLocations()) {
break;

View File

@ -18,6 +18,7 @@ package org.springframework.test.context.util;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -46,6 +47,22 @@ public abstract class TestContextResourceUtils {
private static final String SLASH = "/";
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(".*\\$\\{[^\\}]+\\}.*");
/**
* Convert the supplied paths to classpath resource paths.
*
* <p>Delegates to {@link #convertToClasspathResourcePaths(Class, boolean, String...)}
* with {@code false} supplied for the {@code preservePlaceholders} flag.
* @param clazz the class with which the paths are associated
* @param paths the paths to be converted
* @return a new array of converted resource paths
* @see #convertToResources
*/
public static String[] convertToClasspathResourcePaths(Class<?> clazz, String... paths) {
return convertToClasspathResourcePaths(clazz, false, paths);
}
/**
* Convert the supplied paths to classpath resource paths.
@ -63,18 +80,23 @@ public abstract class TestContextResourceUtils {
* {@code classpath:}, {@code file:}, {@code http:}, etc.) will not have its
* protocol modified.
* </ul>
* <p>Each path will then be {@linkplain StringUtils#cleanPath cleaned}.
* <p>Each path will then be {@linkplain StringUtils#cleanPath cleaned},
* unless the {@code preservePlaceholders} flag is {@code true} and the path
* contains one or more placeholders in the form <code>${placeholder.name}</code>.
* @param clazz the class with which the paths are associated
* @param preservePlaceholders {@code true} if placeholders should be preserved
* @param paths the paths to be converted
* @return a new array of converted resource paths
* @since 5.2
* @see #convertToResources
* @see ResourceUtils#CLASSPATH_URL_PREFIX
* @see ResourceUtils#FILE_URL_PREFIX
*/
public static String[] convertToClasspathResourcePaths(Class<?> clazz, String... paths) {
public static String[] convertToClasspathResourcePaths(Class<?> clazz, boolean preservePlaceholders, String... paths) {
String[] convertedPaths = new String[paths.length];
for (int i = 0; i < paths.length; i++) {
String path = paths[i];
// Absolute path
if (path.startsWith(SLASH)) {
convertedPaths[i] = ResourceUtils.CLASSPATH_URL_PREFIX + path;
@ -88,7 +110,10 @@ public abstract class TestContextResourceUtils {
else {
convertedPaths[i] = path;
}
convertedPaths[i] = StringUtils.cleanPath(convertedPaths[i]);
if (!(preservePlaceholders && PLACEHOLDER_PATTERN.matcher(convertedPaths[i]).matches())) {
convertedPaths[i] = StringUtils.cleanPath(convertedPaths[i]);
}
}
return convertedPaths;
}

View File

@ -18,7 +18,6 @@ package org.springframework.test.context.env;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
@ -135,9 +134,8 @@ class ExplicitPropertiesFileTestPropertySourceTests {
}
@Nested
@TestPropertySource("file:${user.dir}/../spring-test/src/test/resources/org/springframework/test/context/env/explicit.properties")
@Disabled("Currently not supported (see https://github.com/spring-projects/spring-framework/issues/23544)")
@DisplayName("with placeholders followed immediately by relative paths")
@TestPropertySource("file:${user.dir}/../spring-test/src/test/resources/${current.test.package}/../env/explicit.properties")
class PlaceholdersFollowedByRelativePathsTests extends AbstractExplicitPropertiesFileTests {
}