Skip to content

Commit 9045364

Browse files
committed
Simplify and revise PlaceholderParserTests for consistency
1 parent 5a2cbc1 commit 9045364

File tree

1 file changed

+99
-107
lines changed

1 file changed

+99
-107
lines changed

spring-core/src/test/java/org/springframework/util/PlaceholderParserTests.java

+99-107
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package org.springframework.util;
1818

19-
import java.util.Properties;
19+
import java.util.Map;
2020
import java.util.stream.Stream;
2121

2222
import org.junit.jupiter.api.Nested;
@@ -36,13 +36,13 @@
3636
import static org.mockito.BDDMockito.given;
3737
import static org.mockito.Mockito.inOrder;
3838
import static org.mockito.Mockito.mock;
39-
import static org.mockito.Mockito.verify;
4039
import static org.mockito.Mockito.verifyNoMoreInteractions;
4140

4241
/**
4342
* Tests for {@link PlaceholderParser}.
4443
*
4544
* @author Stephane Nicoll
45+
* @author Sam Brannen
4646
*/
4747
class PlaceholderParserTests {
4848

@@ -54,11 +54,11 @@ class OnlyPlaceholderTests {
5454
@ParameterizedTest(name = "{0} -> {1}")
5555
@MethodSource("placeholders")
5656
void placeholderIsReplaced(String text, String expected) {
57-
Properties properties = new Properties();
58-
properties.setProperty("firstName", "John");
59-
properties.setProperty("nested0", "first");
60-
properties.setProperty("nested1", "Name");
61-
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected);
57+
Map<String, String> properties = Map.of(
58+
"firstName", "John",
59+
"nested0", "first",
60+
"nested1", "Name");
61+
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected);
6262
}
6363

6464
static Stream<Arguments> placeholders() {
@@ -79,13 +79,13 @@ static Stream<Arguments> placeholders() {
7979
@ParameterizedTest(name = "{0} -> {1}")
8080
@MethodSource("nestedPlaceholders")
8181
void nestedPlaceholdersAreReplaced(String text, String expected) {
82-
Properties properties = new Properties();
83-
properties.setProperty("p1", "v1");
84-
properties.setProperty("p2", "v2");
85-
properties.setProperty("p3", "${p1}:${p2}"); // nested placeholders
86-
properties.setProperty("p4", "${p3}"); // deeply nested placeholders
87-
properties.setProperty("p5", "${p1}:${p2}:${bogus}"); // unresolvable placeholder
88-
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected);
82+
Map<String, String> properties = Map.of(
83+
"p1", "v1",
84+
"p2", "v2",
85+
"p3", "${p1}:${p2}", // nested placeholders
86+
"p4", "${p3}", // deeply nested placeholders
87+
"p5", "${p1}:${p2}:${bogus}"); // unresolvable placeholder
88+
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected);
8989
}
9090

9191
static Stream<Arguments> nestedPlaceholders() {
@@ -101,69 +101,59 @@ static Stream<Arguments> nestedPlaceholders() {
101101
@Test
102102
void parseWithSinglePlaceholder() {
103103
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "John");
104-
assertThat(this.parser.replacePlaceholders("${firstName}", resolver))
105-
.isEqualTo("John");
106-
verify(resolver).resolvePlaceholder("firstName");
107-
verifyNoMoreInteractions(resolver);
104+
assertThat(this.parser.replacePlaceholders("${firstName}", resolver)).isEqualTo("John");
105+
verifyPlaceholderResolutions(resolver, "firstName");
108106
}
109107

110108
@Test
111109
void parseWithPlaceholderAndPrefixText() {
112110
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "John");
113-
assertThat(this.parser.replacePlaceholders("This is ${firstName}", resolver))
114-
.isEqualTo("This is John");
115-
verify(resolver).resolvePlaceholder("firstName");
116-
verifyNoMoreInteractions(resolver);
111+
assertThat(this.parser.replacePlaceholders("This is ${firstName}", resolver)).isEqualTo("This is John");
112+
verifyPlaceholderResolutions(resolver, "firstName");
117113
}
118114

119115
@Test
120116
void parseWithMultiplePlaceholdersAndText() {
121117
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "John", "lastName", "Smith");
122118
assertThat(this.parser.replacePlaceholders("User: ${firstName} - ${lastName}.", resolver))
123119
.isEqualTo("User: John - Smith.");
124-
verify(resolver).resolvePlaceholder("firstName");
125-
verify(resolver).resolvePlaceholder("lastName");
126-
verifyNoMoreInteractions(resolver);
120+
verifyPlaceholderResolutions(resolver, "firstName", "lastName");
127121
}
128122

129123
@Test
130124
void parseWithNestedPlaceholderInKey() {
131-
PlaceholderResolver resolver = mockPlaceholderResolver(
132-
"nested", "Name", "firstName", "John");
133-
assertThat(this.parser.replacePlaceholders("${first${nested}}", resolver))
134-
.isEqualTo("John");
125+
PlaceholderResolver resolver = mockPlaceholderResolver("nested", "Name", "firstName", "John");
126+
assertThat(this.parser.replacePlaceholders("${first${nested}}", resolver)).isEqualTo("John");
135127
verifyPlaceholderResolutions(resolver, "nested", "firstName");
136128
}
137129

138130
@Test
139131
void parseWithMultipleNestedPlaceholdersInKey() {
140-
PlaceholderResolver resolver = mockPlaceholderResolver(
141-
"nested0", "first", "nested1", "Name", "firstName", "John");
142-
assertThat(this.parser.replacePlaceholders("${${nested0}${nested1}}", resolver))
143-
.isEqualTo("John");
132+
PlaceholderResolver resolver = mockPlaceholderResolver("nested0", "first", "nested1", "Name", "firstName", "John");
133+
assertThat(this.parser.replacePlaceholders("${${nested0}${nested1}}", resolver)).isEqualTo("John");
144134
verifyPlaceholderResolutions(resolver, "nested0", "nested1", "firstName");
145135
}
146136

147137
@Test
148-
void placeholdersWithSeparatorAreHandledAsIs() {
138+
void placeholderValueContainingSeparatorIsHandledAsIs() {
149139
PlaceholderResolver resolver = mockPlaceholderResolver("my:test", "value");
150140
assertThat(this.parser.replacePlaceholders("${my:test}", resolver)).isEqualTo("value");
151141
verifyPlaceholderResolutions(resolver, "my:test");
152142
}
153143

154144
@Test
155145
void placeholdersWithoutEscapeCharAreNotEscaped() {
156-
PlaceholderResolver resolver = mockPlaceholderResolver("test", "value");
157-
assertThat(this.parser.replacePlaceholders("\\${test}", resolver)).isEqualTo("\\value");
158-
verifyPlaceholderResolutions(resolver, "test");
146+
PlaceholderResolver resolver = mockPlaceholderResolver("p1", "v1");
147+
assertThat(this.parser.replacePlaceholders("\\${p1}", resolver)).isEqualTo("\\v1");
148+
verifyPlaceholderResolutions(resolver, "p1");
159149
}
160150

161151
@Test
162-
void textWithInvalidPlaceholderIsMerged() {
152+
void textWithInvalidPlaceholderSyntaxIsMerged() {
163153
String text = "test${of${with${and${";
164154
ParsedValue parsedValue = this.parser.parse(text);
165-
assertThat(parsedValue.parts()).singleElement().isInstanceOfSatisfying(
166-
TextPart.class, textPart -> assertThat(textPart.text()).isEqualTo(text));
155+
assertThat(parsedValue.parts()).singleElement().isInstanceOfSatisfying(TextPart.class,
156+
textPart -> assertThat(textPart.text()).isEqualTo(text));
167157
}
168158

169159
}
@@ -176,11 +166,11 @@ class DefaultValueTests {
176166
@ParameterizedTest(name = "{0} -> {1}")
177167
@MethodSource("placeholders")
178168
void placeholderIsReplaced(String text, String expected) {
179-
Properties properties = new Properties();
180-
properties.setProperty("firstName", "John");
181-
properties.setProperty("nested0", "first");
182-
properties.setProperty("nested1", "Name");
183-
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected);
169+
Map<String, String> properties = Map.of(
170+
"firstName", "John",
171+
"nested0", "first",
172+
"nested1", "Name");
173+
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected);
184174
}
185175

186176
static Stream<Arguments> placeholders() {
@@ -199,14 +189,14 @@ static Stream<Arguments> placeholders() {
199189
@ParameterizedTest(name = "{0} -> {1}")
200190
@MethodSource("nestedPlaceholders")
201191
void nestedPlaceholdersAreReplaced(String text, String expected) {
202-
Properties properties = new Properties();
203-
properties.setProperty("p1", "v1");
204-
properties.setProperty("p2", "v2");
205-
properties.setProperty("p3", "${p1}:${p2}"); // nested placeholders
206-
properties.setProperty("p4", "${p3}"); // deeply nested placeholders
207-
properties.setProperty("p5", "${p1}:${p2}:${bogus}"); // unresolvable placeholder
208-
properties.setProperty("p6", "${p1}:${p2}:${bogus:def}"); // unresolvable w/ default
209-
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected);
192+
Map<String, String> properties = Map.of(
193+
"p1", "v1",
194+
"p2", "v2",
195+
"p3", "${p1}:${p2}", // nested placeholders
196+
"p4", "${p3}", // deeply nested placeholders
197+
"p5", "${p1}:${p2}:${bogus}", // unresolvable placeholder
198+
"p6", "${p1}:${p2}:${bogus:def}"); // unresolvable w/ default
199+
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected);
210200
}
211201

212202
static Stream<Arguments> nestedPlaceholders() {
@@ -225,11 +215,11 @@ static Stream<Arguments> nestedPlaceholders() {
225215
@ParameterizedTest(name = "{0} -> {1}")
226216
@MethodSource("exactMatchPlaceholders")
227217
void placeholdersWithExactMatchAreConsidered(String text, String expected) {
228-
Properties properties = new Properties();
229-
properties.setProperty("prefix://my-service", "example-service");
230-
properties.setProperty("px", "prefix");
231-
properties.setProperty("p1", "${prefix://my-service}");
232-
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected);
218+
Map<String, String> properties = Map.of(
219+
"prefix://my-service", "example-service",
220+
"px", "prefix",
221+
"p1", "${prefix://my-service}");
222+
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected);
233223
}
234224

235225
static Stream<Arguments> exactMatchPlaceholders() {
@@ -242,74 +232,55 @@ static Stream<Arguments> exactMatchPlaceholders() {
242232
@Test
243233
void parseWithKeyEqualsToText() {
244234
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "Steve");
245-
assertThat(this.parser.replacePlaceholders("${firstName}", resolver))
246-
.isEqualTo("Steve");
235+
assertThat(this.parser.replacePlaceholders("${firstName}", resolver)).isEqualTo("Steve");
247236
verifyPlaceholderResolutions(resolver, "firstName");
248237
}
249238

250239
@Test
251240
void parseWithHardcodedFallback() {
252241
PlaceholderResolver resolver = mockPlaceholderResolver();
253-
assertThat(this.parser.replacePlaceholders("${firstName:Steve}", resolver))
254-
.isEqualTo("Steve");
242+
assertThat(this.parser.replacePlaceholders("${firstName:Steve}", resolver)).isEqualTo("Steve");
255243
verifyPlaceholderResolutions(resolver, "firstName:Steve", "firstName");
256244
}
257245

258246
@Test
259247
void parseWithNestedPlaceholderInKeyUsingFallback() {
260248
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "John");
261-
assertThat(this.parser.replacePlaceholders("${first${invalid:Name}}", resolver))
262-
.isEqualTo("John");
249+
assertThat(this.parser.replacePlaceholders("${first${invalid:Name}}", resolver)).isEqualTo("John");
263250
verifyPlaceholderResolutions(resolver, "invalid:Name", "invalid", "firstName");
264251
}
265252

266253
@Test
267254
void parseWithFallbackUsingPlaceholder() {
268255
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "John");
269-
assertThat(this.parser.replacePlaceholders("${invalid:${firstName}}", resolver))
270-
.isEqualTo("John");
256+
assertThat(this.parser.replacePlaceholders("${invalid:${firstName}}", resolver)).isEqualTo("John");
271257
verifyPlaceholderResolutions(resolver, "invalid", "firstName");
272258
}
273259

274260
}
275261

276-
@Nested // Tests with the use of the escape character
262+
/**
263+
* Tests that use the escape character.
264+
*/
265+
@Nested
277266
class EscapedTests {
278267

279268
private final PlaceholderParser parser = new PlaceholderParser("${", "}", ":", '\\', true);
280269

281-
@ParameterizedTest(name = "{0} -> {1}")
282-
@MethodSource("escapedInNestedPlaceholders")
283-
void escapedSeparatorInNestedPlaceholder(String text, String expected) {
284-
Properties properties = new Properties();
285-
properties.setProperty("app.environment", "qa");
286-
properties.setProperty("app.service", "protocol");
287-
properties.setProperty("protocol://host/qa/name", "protocol://example.com/qa/name");
288-
properties.setProperty("service/host/qa/name", "https://example.com/qa/name");
289-
properties.setProperty("service/host/qa/name:value", "https://example.com/qa/name-value");
290-
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected);
291-
}
292-
293-
static Stream<Arguments> escapedInNestedPlaceholders() {
294-
return Stream.of(
295-
Arguments.of("${protocol\\://host/${app.environment}/name}", "protocol://example.com/qa/name"),
296-
Arguments.of("${${app.service}\\://host/${app.environment}/name}", "protocol://example.com/qa/name"),
297-
Arguments.of("${service/host/${app.environment}/name:\\value}", "https://example.com/qa/name"),
298-
Arguments.of("${service/host/${name\\:value}/}", "${service/host/${name:value}/}"));
299-
}
300-
301270
@ParameterizedTest(name = "{0} -> {1}")
302271
@MethodSource("escapedPlaceholders")
303272
void escapedPlaceholderIsNotReplaced(String text, String expected) {
304-
PlaceholderResolver resolver = mockPlaceholderResolver(
305-
"firstName", "John", "nested0", "first", "nested1", "Name",
273+
Map<String, String> properties = Map.of(
274+
"firstName", "John",
306275
"${test}", "John",
307-
"p1", "v1", "p2", "\\${p1:default}", "p3", "${p2}",
276+
"p1", "v1",
277+
"p2", "\\${p1:default}",
278+
"p3", "${p2}",
308279
"p4", "adc${p0:\\${p1}}",
309280
"p5", "adc${\\${p0}:${p1}}",
310281
"p6", "adc${p0:def\\${p1}}",
311282
"p7", "adc\\${");
312-
assertThat(this.parser.replacePlaceholders(text, resolver)).isEqualTo(expected);
283+
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected);
313284
}
314285

315286
static Stream<Arguments> escapedPlaceholders() {
@@ -324,18 +295,15 @@ static Stream<Arguments> escapedPlaceholders() {
324295
Arguments.of("${p4}", "adc${p1}"),
325296
Arguments.of("${p5}", "adcv1"),
326297
Arguments.of("${p6}", "adcdef${p1}"),
327-
Arguments.of("${p7}", "adc\\${"));
328-
298+
Arguments.of("${p7}", "adc\\${")
299+
);
329300
}
330301

331302
@ParameterizedTest(name = "{0} -> {1}")
332303
@MethodSource("escapedSeparators")
333304
void escapedSeparatorIsNotReplaced(String text, String expected) {
334-
Properties properties = new Properties();
335-
properties.setProperty("first:Name", "John");
336-
properties.setProperty("nested0", "first");
337-
properties.setProperty("nested1", "Name");
338-
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected);
305+
Map<String, String> properties = Map.of("first:Name", "John");
306+
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected);
339307
}
340308

341309
static Stream<Arguments> escapedSeparators() {
@@ -345,6 +313,26 @@ static Stream<Arguments> escapedSeparators() {
345313
);
346314
}
347315

316+
@ParameterizedTest(name = "{0} -> {1}")
317+
@MethodSource("escapedSeparatorsInNestedPlaceholders")
318+
void escapedSeparatorInNestedPlaceholderIsNotReplaced(String text, String expected) {
319+
Map<String, String> properties = Map.of(
320+
"app.environment", "qa",
321+
"app.service", "protocol",
322+
"protocol://host/qa/name", "protocol://example.com/qa/name",
323+
"service/host/qa/name", "https://example.com/qa/name",
324+
"service/host/qa/name:value", "https://example.com/qa/name-value");
325+
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected);
326+
}
327+
328+
static Stream<Arguments> escapedSeparatorsInNestedPlaceholders() {
329+
return Stream.of(
330+
Arguments.of("${protocol\\://host/${app.environment}/name}", "protocol://example.com/qa/name"),
331+
Arguments.of("${${app.service}\\://host/${app.environment}/name}", "protocol://example.com/qa/name"),
332+
Arguments.of("${service/host/${app.environment}/name:\\value}", "https://example.com/qa/name"),
333+
Arguments.of("${service/host/${name\\:value}/}", "${service/host/${name:value}/}"));
334+
}
335+
348336
}
349337

350338
@Nested
@@ -354,34 +342,38 @@ class ExceptionTests {
354342

355343
@Test
356344
void textWithCircularReference() {
357-
PlaceholderResolver resolver = mockPlaceholderResolver("pL", "${pR}", "pR", "${pL}");
358-
assertThatThrownBy(() -> this.parser.replacePlaceholders("${pL}", resolver))
345+
Map<String, String> properties = Map.of(
346+
"pL", "${pR}",
347+
"pR", "${pL}");
348+
assertThatThrownBy(() -> this.parser.replacePlaceholders("${pL}", properties::get))
359349
.isInstanceOf(PlaceholderResolutionException.class)
360350
.hasMessage("Circular placeholder reference 'pL' in value \"${pL}\" <-- \"${pR}\" <-- \"${pL}\"");
361351
}
362352

363353
@Test
364354
void unresolvablePlaceholderIsReported() {
365-
PlaceholderResolver resolver = mockPlaceholderResolver();
366355
assertThatExceptionOfType(PlaceholderResolutionException.class)
367-
.isThrownBy(() -> this.parser.replacePlaceholders("${bogus}", resolver))
368-
.withMessage("Could not resolve placeholder 'bogus' in value \"${bogus}\"")
356+
.isThrownBy(() -> this.parser.replacePlaceholders("X${bogus}Z", placeholderName -> null))
357+
.withMessage("Could not resolve placeholder 'bogus' in value \"X${bogus}Z\"")
369358
.withNoCause();
370359
}
371360

372361
@Test
373362
void unresolvablePlaceholderInNestedPlaceholderIsReportedWithChain() {
374-
PlaceholderResolver resolver = mockPlaceholderResolver("p1", "v1", "p2", "v2",
363+
Map<String, String> properties = Map.of(
364+
"p1", "v1",
365+
"p2", "v2",
375366
"p3", "${p1}:${p2}:${bogus}");
376367
assertThatExceptionOfType(PlaceholderResolutionException.class)
377-
.isThrownBy(() -> this.parser.replacePlaceholders("${p3}", resolver))
368+
.isThrownBy(() -> this.parser.replacePlaceholders("${p3}", properties::get))
378369
.withMessage("Could not resolve placeholder 'bogus' in value \"${p1}:${p2}:${bogus}\" <-- \"${p3}\"")
379370
.withNoCause();
380371
}
381372

382373
}
383374

384-
PlaceholderResolver mockPlaceholderResolver(String... pairs) {
375+
376+
private static PlaceholderResolver mockPlaceholderResolver(String... pairs) {
385377
if (pairs.length % 2 == 1) {
386378
throw new IllegalArgumentException("size must be even, it is a set of key=value pairs");
387379
}
@@ -394,7 +386,7 @@ PlaceholderResolver mockPlaceholderResolver(String... pairs) {
394386
return resolver;
395387
}
396388

397-
void verifyPlaceholderResolutions(PlaceholderResolver mock, String... placeholders) {
389+
private static void verifyPlaceholderResolutions(PlaceholderResolver mock, String... placeholders) {
398390
InOrder ordered = inOrder(mock);
399391
for (String placeholder : placeholders) {
400392
ordered.verify(mock).resolvePlaceholder(placeholder);

0 commit comments

Comments
 (0)