1 package org.codehaus.plexus.interpolation;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Properties;
9
10 import org.codehaus.plexus.interpolation.os.OperatingSystemUtils;
11 import org.junit.jupiter.api.BeforeEach;
12 import org.junit.jupiter.api.Test;
13
14 import static org.junit.jupiter.api.Assertions.assertEquals;
15 import static org.junit.jupiter.api.Assertions.fail;
16
17 public class StringSearchInterpolatorTest {
18
19 @BeforeEach
20 void setUp() {
21 EnvarBasedValueSource.resetStatics();
22 }
23
24 @Test
25 void longDelimitersInContext() throws Exception {
26 String src = "This is a <expression>test.label</expression> for long delimiters in context.";
27 String result = "This is a test for long delimiters in context.";
28
29 Properties p = new Properties();
30 p.setProperty("test.label", "test");
31
32 StringSearchInterpolator interpolator = new StringSearchInterpolator("<expression>", "</expression>");
33 interpolator.addValueSource(new PropertiesBasedValueSource(p));
34
35 assertEquals(result, interpolator.interpolate(src));
36 }
37
38 @Test
39 void longDelimitersWithNoStartContext() throws Exception {
40 String src = "<expression>test.label</expression> for long delimiters in context.";
41 String result = "test for long delimiters in context.";
42
43 Properties p = new Properties();
44 p.setProperty("test.label", "test");
45
46 StringSearchInterpolator interpolator = new StringSearchInterpolator("<expression>", "</expression>");
47 interpolator.addValueSource(new PropertiesBasedValueSource(p));
48
49 assertEquals(result, interpolator.interpolate(src));
50 }
51
52 @Test
53 void longDelimitersWithNoEndContext() throws Exception {
54 String src = "This is a <expression>test.label</expression>";
55 String result = "This is a test";
56
57 Properties p = new Properties();
58 p.setProperty("test.label", "test");
59
60 StringSearchInterpolator interpolator = new StringSearchInterpolator("<expression>", "</expression>");
61 interpolator.addValueSource(new PropertiesBasedValueSource(p));
62
63 assertEquals(result, interpolator.interpolate(src));
64 }
65
66 @Test
67 void longDelimitersWithNoContext() throws Exception {
68 String src = "<expression>test.label</expression>";
69 String result = "test";
70
71 Properties p = new Properties();
72 p.setProperty("test.label", "test");
73
74 StringSearchInterpolator interpolator = new StringSearchInterpolator("<expression>", "</expression>");
75 interpolator.addValueSource(new PropertiesBasedValueSource(p));
76
77 assertEquals(result, interpolator.interpolate(src));
78 }
79
80 @Test
81 void longDelimitersPassedToValueSource() throws Exception {
82 String src = "<expression>test</expression>";
83
84 StringSearchInterpolator interpolator = new StringSearchInterpolator("<expression>", "</expression>");
85 interpolator.addValueSource(new AbstractValueSource(false) {
86
87 @Override
88 public Object getValue(String expression, String expressionStartDelimiter, String expressionEndDelimiter) {
89 assertEquals("<expression>", expressionStartDelimiter);
90 assertEquals("</expression>", expressionEndDelimiter);
91 return expression;
92 }
93
94 @Override
95 public Object getValue(String expression) {
96 fail("This method is not supposed to be called");
97 return null;
98 }
99 });
100
101 assertEquals("test", interpolator.interpolate(src));
102 }
103
104 @Test
105 void simpleSubstitution() throws Exception {
106 Properties p = new Properties();
107 p.setProperty("key", "value");
108
109 StringSearchInterpolator interpolator = new StringSearchInterpolator();
110 interpolator.addValueSource(new PropertiesBasedValueSource(p));
111
112 assertEquals("This is a test value.", interpolator.interpolate("This is a test ${key}."));
113 }
114
115 @Test
116 void simpleSubstitutionTwoExpressions() throws Exception {
117 Properties p = new Properties();
118 p.setProperty("key", "value");
119 p.setProperty("key2", "value2");
120
121 StringSearchInterpolator interpolator = new StringSearchInterpolator();
122 interpolator.addValueSource(new PropertiesBasedValueSource(p));
123
124 assertEquals("value-value2", interpolator.interpolate("${key}-${key2}"));
125 }
126
127 @Test
128 void brokenExpressionLeaveItAlone() throws Exception {
129 Properties p = new Properties();
130 p.setProperty("key", "value");
131
132 StringSearchInterpolator interpolator = new StringSearchInterpolator();
133 interpolator.addValueSource(new PropertiesBasedValueSource(p));
134
135 assertEquals("This is a test ${key.", interpolator.interpolate("This is a test ${key."));
136 }
137
138 @Test
139 void shouldFailOnExpressionCycle() {
140 Properties props = new Properties();
141 props.setProperty("key1", "${key2}");
142 props.setProperty("key2", "${key1}");
143
144 StringSearchInterpolator rbi = new StringSearchInterpolator();
145 rbi.addValueSource(new PropertiesBasedValueSource(props));
146
147 try {
148 rbi.interpolate("${key1}", new SimpleRecursionInterceptor());
149
150 fail("Should detect expression cycle and fail.");
151 } catch (InterpolationException e) {
152
153 }
154 }
155
156 @Test
157 void shouldResolveByUsingObjectListMap() throws Exception {
158 StringSearchInterpolator rbi = new StringSearchInterpolator();
159 rbi.addValueSource(new ObjectBasedValueSource(this));
160 String result =
161 rbi.interpolate("this is a ${var} ${list[1].name} ${anArray[2].name} ${map(Key with spaces).name}");
162
163 assertEquals("this is a testVar testIndexedWithList testIndexedWithArray testMap", result);
164 }
165
166 @Test
167 void shouldResolveByContextValue() throws Exception {
168 StringSearchInterpolator rbi = new StringSearchInterpolator();
169
170 Map<String, String> context = new HashMap<>();
171 context.put("var", "testVar");
172
173 rbi.addValueSource(new MapBasedValueSource(context));
174
175 String result = rbi.interpolate("this is a ${var}");
176
177 assertEquals("this is a testVar", result);
178 }
179
180 @Test
181 void shouldResolveByEnvar() throws Exception {
182 OperatingSystemUtils.setEnvVarSource(() -> {
183 HashMap<String, String> map = new HashMap<>();
184 map.put("SOME_ENV", "variable");
185 map.put("OTHER_ENV", "other variable");
186 return map;
187 });
188
189 StringSearchInterpolator rbi = new StringSearchInterpolator();
190
191 rbi.addValueSource(new EnvarBasedValueSource(false));
192
193 String result = rbi.interpolate("this is a ${env.SOME_ENV} ${env.OTHER_ENV}");
194
195 assertEquals("this is a variable other variable", result);
196 }
197
198 @Test
199 void usePostProcessorDoesNotChangeValue() throws Exception {
200 StringSearchInterpolator rbi = new StringSearchInterpolator();
201
202 Map<String, String> context = new HashMap<>();
203 context.put("test.var", "testVar");
204
205 rbi.addValueSource(new MapBasedValueSource(context));
206
207 rbi.addPostProcessor((expression, value) -> null);
208
209 String result = rbi.interpolate("this is a ${test.var}");
210
211 assertEquals("this is a testVar", result);
212 }
213
214 @Test
215 void usePostProcessorChangesValue() throws Exception {
216
217 StringSearchInterpolator rbi = new StringSearchInterpolator();
218
219 Map<String, String> context = new HashMap<>();
220 context.put("test.var", "testVar");
221
222 rbi.addValueSource(new MapBasedValueSource(context));
223
224 rbi.addPostProcessor((expression, value) -> value + "2");
225
226 String result = rbi.interpolate("this is a ${test.var}");
227
228 assertEquals("this is a testVar2", result);
229 }
230
231 @Test
232 void simpleSubstitutionWithDefinedExpr() throws Exception {
233 Properties p = new Properties();
234 p.setProperty("key", "value");
235
236 StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
237 interpolator.addValueSource(new PropertiesBasedValueSource(p));
238
239 assertEquals("This is a test value.", interpolator.interpolate("This is a test @{key}."));
240 }
241
242 @Test
243 void escape() throws Exception {
244 Properties p = new Properties();
245 p.setProperty("key", "value");
246
247 StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
248 interpolator.setEscapeString("\\");
249 interpolator.addValueSource(new PropertiesBasedValueSource(p));
250
251 String result = interpolator.interpolate("This is a test \\@{key}.");
252
253 assertEquals("This is a test @{key}.", result);
254 }
255
256 @Test
257 void escapeWithLongEscapeStr() throws Exception {
258 Properties p = new Properties();
259 p.setProperty("key", "value");
260
261 StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
262 interpolator.setEscapeString("$$");
263 interpolator.addValueSource(new PropertiesBasedValueSource(p));
264
265 String result = interpolator.interpolate("This is a test $$@{key}.");
266
267 assertEquals("This is a test @{key}.", result);
268 }
269
270 @Test
271 void escapeWithLongEscapeStrAtStart() throws Exception {
272 Properties p = new Properties();
273 p.setProperty("key", "value");
274
275 StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
276 interpolator.setEscapeString("$$");
277 interpolator.addValueSource(new PropertiesBasedValueSource(p));
278
279 String result = interpolator.interpolate("$$@{key} This is a test.");
280
281 assertEquals("@{key} This is a test.", result);
282 }
283
284 @Test
285 void notEscapeWithLongEscapeStrAtStart() throws Exception {
286 Properties p = new Properties();
287 p.setProperty("key", "value");
288
289 StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
290 interpolator.setEscapeString("$$");
291 interpolator.addValueSource(new PropertiesBasedValueSource(p));
292
293 String result = interpolator.interpolate("@{key} This is a test.");
294
295 assertEquals("value This is a test.", result);
296 }
297
298 @Test
299 void escapeNotFailWithNullEscapeStr() throws Exception {
300 Properties p = new Properties();
301 p.setProperty("key", "value");
302
303 StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
304 interpolator.setEscapeString(null);
305 interpolator.addValueSource(new PropertiesBasedValueSource(p));
306
307 String result = interpolator.interpolate("This is a test @{key}.");
308
309 assertEquals("This is a test value.", result);
310 }
311
312 @Test
313 void onlyEscapeExprAtStart() throws Exception {
314 Properties p = new Properties();
315 p.setProperty("key", "value");
316
317 StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
318 interpolator.setEscapeString("\\");
319 interpolator.addValueSource(new PropertiesBasedValueSource(p));
320
321 String result = interpolator.interpolate("\\@{key} This is a test.");
322
323 assertEquals("@{key} This is a test.", result);
324 }
325
326 @Test
327 void notEscapeExprAtStart() throws Exception {
328 Properties p = new Properties();
329 p.setProperty("key", "value");
330
331 StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
332 interpolator.setEscapeString("\\");
333 interpolator.addValueSource(new PropertiesBasedValueSource(p));
334
335 String result = interpolator.interpolate("@{key} This is a test.");
336
337 assertEquals("value This is a test.", result);
338 }
339
340 @Test
341 void escapeExprAtStart() throws Exception {
342 Properties p = new Properties();
343 p.setProperty("key", "value");
344
345 StringSearchInterpolator interpolator = new StringSearchInterpolator("@", "@");
346 interpolator.setEscapeString("\\");
347 interpolator.addValueSource(new PropertiesBasedValueSource(p));
348
349 String result = interpolator.interpolate("\\@key@ This is a test @key@.");
350
351 assertEquals("@key@ This is a test value.", result);
352 }
353
354 @Test
355 void npeFree() throws Exception {
356 Properties p = new Properties();
357 p.setProperty("key", "value");
358
359 StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
360 interpolator.setEscapeString("\\");
361 interpolator.addValueSource(new PropertiesBasedValueSource(p));
362
363 String result = interpolator.interpolate(null);
364
365 assertEquals("", result);
366 }
367
368 @Test
369 void interruptedInterpolate() throws Exception {
370 Interpolator interpolator = new StringSearchInterpolator();
371 RecursionInterceptor recursionInterceptor = new SimpleRecursionInterceptor();
372 final boolean[] error = new boolean[] {false};
373 interpolator.addValueSource(new ValueSource() {
374 public Object getValue(String expression) {
375 if (expression.equals("key")) {
376 if (error[0]) {
377 throw new IllegalStateException("broken");
378 }
379 return "val";
380 } else {
381 return null;
382 }
383 }
384
385 public List getFeedback() {
386 return Collections.EMPTY_LIST;
387 }
388
389 public void clearFeedback() {}
390 });
391 assertEquals("-val-", interpolator.interpolate("-${key}-", recursionInterceptor), "control case");
392 error[0] = true;
393 try {
394 interpolator.interpolate("-${key}-", recursionInterceptor);
395 fail("should have thrown exception");
396 } catch (IllegalStateException x) {
397
398 }
399 error[0] = false;
400 assertEquals(
401 "-val-",
402 interpolator.interpolate("-${key}-", recursionInterceptor),
403 "should not believe there is a cycle here");
404 }
405
406 @Test
407 void cacheAnswersTrue() throws Exception {
408 Properties p = new Properties();
409 p.setProperty("key", "value");
410
411 class CountingStringSearchInterpolator extends StringSearchInterpolator {
412 private int existingCallCount;
413
414 @Override
415 protected Object getExistingAnswer(String key) {
416 Object value = super.getExistingAnswer(key);
417 if (value != null) {
418 ++existingCallCount;
419 }
420 return value;
421 }
422
423 public int getExistingCallCount() {
424 return existingCallCount;
425 }
426 }
427
428 CountingStringSearchInterpolator interpolator = new CountingStringSearchInterpolator();
429 interpolator.setCacheAnswers(true);
430 interpolator.addValueSource(new PropertiesBasedValueSource(p));
431
432 String result = interpolator.interpolate("${key}-${key}-${key}-${key}");
433
434 assertEquals("value-value-value-value", result);
435
436 assertEquals(3, interpolator.getExistingCallCount());
437
438
439 result = interpolator.interpolate("${key}-${key}-${key}-${key}");
440 assertEquals("value-value-value-value", result);
441
442 assertEquals(3 + 4, interpolator.getExistingCallCount());
443 }
444
445 @Test
446 void cacheAnswersFalse() throws Exception {
447 Properties p = new Properties();
448 p.setProperty("key", "value");
449
450 class CountingStringSearchInterpolator extends StringSearchInterpolator {
451 private int existingCallCount;
452
453 @Override
454 protected Object getExistingAnswer(String key) {
455 Object value = super.getExistingAnswer(key);
456 if (value != null) {
457 ++existingCallCount;
458 }
459 return value;
460 }
461
462 public int getExistingCallCount() {
463 return existingCallCount;
464 }
465 }
466
467 CountingStringSearchInterpolator interpolator = new CountingStringSearchInterpolator();
468 interpolator.addValueSource(new PropertiesBasedValueSource(p));
469
470 String result = interpolator.interpolate("${key}-${key}-${key}-${key}");
471
472 assertEquals("value-value-value-value", result);
473
474 assertEquals(0, interpolator.getExistingCallCount());
475 }
476
477 public String getVar() {
478 return "testVar";
479 }
480
481 public Person[] getAnArray() {
482 Person[] array = new Person[3];
483 array[0] = new Person("Gabriel");
484 array[1] = new Person("Daniela");
485 array[2] = new Person("testIndexedWithArray");
486 return array;
487 }
488
489 public List<Person> getList() {
490 List<Person> list = new ArrayList<>();
491 list.add(new Person("Gabriel"));
492 list.add(new Person("testIndexedWithList"));
493 list.add(new Person("Daniela"));
494 return list;
495 }
496
497 public Map<String, Person> getMap() {
498 Map<String, Person> map = new HashMap<>();
499 map.put("Key with spaces", new Person("testMap"));
500 return map;
501 }
502
503 public static class Person {
504 private final String name;
505
506 public Person(String name) {
507 this.name = name;
508 }
509
510 public String getName() {
511 return name;
512 }
513 }
514 }