From ab779eb431ebcfaf9c13b36cac905789c12b610c Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 30 Aug 2019 18:57:20 +0200 Subject: [PATCH] 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 --- .../support/TestPropertySourceUtils.java | 2 +- .../util/TestContextResourceUtils.java | 31 +++++++++++++++++-- ...PropertiesFileTestPropertySourceTests.java | 4 +-- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java index 442946450c4..913715b9513 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java @@ -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; diff --git a/spring-test/src/main/java/org/springframework/test/context/util/TestContextResourceUtils.java b/spring-test/src/main/java/org/springframework/test/context/util/TestContextResourceUtils.java index 7258bb2de6f..645594d2b73 100644 --- a/spring-test/src/main/java/org/springframework/test/context/util/TestContextResourceUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/util/TestContextResourceUtils.java @@ -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. + * + *

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. * - *

Each path will then be {@linkplain StringUtils#cleanPath cleaned}. + *

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 ${placeholder.name}. * @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; } diff --git a/spring-test/src/test/java/org/springframework/test/context/env/ExplicitPropertiesFileTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/ExplicitPropertiesFileTestPropertySourceTests.java index f399193aa45..b142718f431 100644 --- a/spring-test/src/test/java/org/springframework/test/context/env/ExplicitPropertiesFileTestPropertySourceTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/env/ExplicitPropertiesFileTestPropertySourceTests.java @@ -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 { }