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 { }