1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core.util;
15
16 import static ch.qos.logback.core.subst.NodeToStringTransformer.CIRCULAR_VARIABLE_REFERENCE_DETECTED;
17 import static ch.qos.logback.core.subst.Parser.EXPECTING_DATA_AFTER_LEFT_ACCOLADE;
18 import static java.util.concurrent.TimeUnit.SECONDS;
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertThrows;
21 import static org.junit.jupiter.api.Assertions.fail;
22
23 import java.util.HashMap;
24 import java.util.Map;
25
26 import ch.qos.logback.core.testUtil.RandomUtil;
27 import org.junit.jupiter.api.BeforeEach;
28 import org.junit.jupiter.api.Disabled;
29 import org.junit.jupiter.api.Test;
30
31 import ch.qos.logback.core.Context;
32 import ch.qos.logback.core.ContextBase;
33 import ch.qos.logback.core.joran.spi.JoranException;
34 import ch.qos.logback.core.spi.ScanException;
35 import org.junit.jupiter.api.Timeout;
36
37 public class OptionHelperTest {
38
39 String text = "Testing ${v1} variable substitution ${v2}";
40 String expected = "Testing if variable substitution works";
41 Context context = new ContextBase();
42 Map<String, String> secondaryMap;
43
44 int diff = RandomUtil.getPositiveInt();
45
46 @BeforeEach
47 public void setUp() throws Exception {
48 secondaryMap = new HashMap<String, String>();
49 }
50
51 @Test
52 public void testLiteral() throws ScanException {
53 String noSubst = "hello world";
54 String result = OptionHelper.substVars(noSubst, context);
55 assertEquals(noSubst, result);
56 }
57
58 @Test
59 public void testUndefinedValues() throws ScanException {
60 String withUndefinedValues = "${axyz}";
61
62 String result = OptionHelper.substVars(withUndefinedValues, context);
63 assertEquals("axyz" + OptionHelper._IS_UNDEFINED, result);
64 }
65
66 @Test
67 public void testSubstVarsVariableNotClosed() throws ScanException {
68 String noSubst = "testing if ${v1 works";
69
70 try {
71 @SuppressWarnings("unused")
72 String result = OptionHelper.substVars(noSubst, context);
73 fail();
74 } catch (IllegalArgumentException e) {
75
76 }
77 }
78
79 @Test
80 public void testSubstVarsContextOnly() throws ScanException {
81 context.putProperty("v1", "if");
82 context.putProperty("v2", "works");
83
84 String result = OptionHelper.substVars(text, context);
85 assertEquals(expected, result);
86 }
87
88 @Test
89 public void testSubstVarsSystemProperties() throws ScanException {
90 System.setProperty("v1", "if");
91 System.setProperty("v2", "works");
92
93 String result = OptionHelper.substVars(text, context);
94 assertEquals(expected, result);
95
96 System.clearProperty("v1");
97 System.clearProperty("v2");
98 }
99
100 @Test
101 public void testSubstVarsWithDefault() throws ScanException {
102 context.putProperty("v1", "if");
103 String textWithDefault = "Testing ${v1} variable substitution ${v2:-toto}";
104 String resultWithDefault = "Testing if variable substitution toto";
105
106 String result = OptionHelper.substVars(textWithDefault, context);
107 assertEquals(resultWithDefault, result);
108 }
109
110 @Test
111 public void testSubstVarsRecursive() throws ScanException {
112 context.putProperty("v1", "if");
113 context.putProperty("v2", "${v3}");
114 context.putProperty("v3", "works");
115
116 String result = OptionHelper.substVars(text, context);
117 assertEquals(expected, result);
118 }
119
120 @Test
121 public void testSubstVarsTwoLevelsDeep() throws ScanException {
122 context.putProperty("v1", "if");
123 context.putProperty("v2", "${v3}");
124 context.putProperty("v3", "${v4}");
125 context.putProperty("v4", "works");
126
127 String result = OptionHelper.substVars(text, context);
128 assertEquals(expected, result);
129 }
130
131 @Test
132 public void testSubstVarsTwoLevelsWithDefault() throws ScanException {
133
134 context.putProperty("APP_NAME", "LOGBACK");
135 context.putProperty("ARCHIVE_SUFFIX", "archive.log");
136 context.putProperty("LOG_HOME", "${logfilepath.default:-logs}");
137 context.putProperty("ARCHIVE_PATH", "${LOG_HOME}/archive/${APP_NAME}");
138
139 String result = OptionHelper.substVars("${ARCHIVE_PATH}_trace_${ARCHIVE_SUFFIX}", context);
140 assertEquals("logs/archive/LOGBACK_trace_archive.log", result);
141 }
142
143 @Test
144 @Timeout(value = 1, unit = SECONDS)
145 public void stubstVarsShouldNotGoIntoInfiniteLoop() throws ScanException {
146 context.putProperty("v1", "if");
147 context.putProperty("v2", "${v3}");
148 context.putProperty("v3", "${v4}");
149 context.putProperty("v4", "${v2}c");
150
151 Exception e = assertThrows(Exception.class, () -> {
152 OptionHelper.substVars(text, context);
153 });
154 String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${v2} --> ${v3} --> ${v4} --> ${v2}]";
155 assertEquals(expectedMessage, e.getMessage());
156 }
157
158 @Test
159 public void nonCircularGraphShouldWork() throws ScanException {
160 context.putProperty("A", "${B} and ${C}");
161 context.putProperty("B", "${B1}");
162 context.putProperty("B1", "B1-value");
163 context.putProperty("C", "${C1} and ${B}");
164 context.putProperty("C1", "C1-value");
165
166 String result = OptionHelper.substVars("${A}", context);
167 assertEquals("B1-value and C1-value and B1-value", result);
168 }
169
170 @Test
171 @Timeout(value = 1, unit = SECONDS)
172 public void detectCircularReferences0() throws ScanException {
173 context.putProperty("A", "${A}");
174 Exception e = assertThrows(IllegalArgumentException.class, () -> {
175 OptionHelper.substVars("${A}", context);
176 });
177 String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${A} --> ${A}]";
178 assertEquals(expectedMessage, e.getMessage());
179 }
180
181 @Test
182 @Timeout(value = 1, unit = SECONDS)
183 public void detectCircularReferences1() throws ScanException {
184 context.putProperty("A", "${A}a");
185
186 Exception e = assertThrows(IllegalArgumentException.class, () -> {
187 OptionHelper.substVars("${A}", context);
188 });
189
190 String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${A} --> ${A}]";
191 assertEquals(expectedMessage, e.getMessage());
192 }
193
194 @Test
195 @Timeout(value = 1, unit = SECONDS)
196 public void detectCircularReferences2() throws ScanException {
197 context.putProperty("A", "${B}");
198 context.putProperty("B", "${C}");
199 context.putProperty("C", "${A}");
200
201 Exception e = assertThrows(IllegalArgumentException.class, () -> {
202 OptionHelper.substVars("${A}", context);
203 });
204 String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${A} --> ${B} --> ${C} --> ${A}]";
205 assertEquals(expectedMessage, e.getMessage());
206 }
207
208
209 @Test
210 public void recursionErrorWithNullLiteralPayload() throws ScanException {
211
212 Exception e = assertThrows(IllegalArgumentException.class, () -> {
213 OptionHelper.substVars("abc${AA$AA${}}}xyz", context);
214 });
215 String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${AA} --> ${}]";
216 assertEquals(expectedMessage, e.getMessage());
217 }
218
219
220 @Test
221 public void leftAccoladeFollowedByDefaultStateWithNoLiteral() throws ScanException {
222 Exception e = assertThrows(ScanException.class, () -> {
223 OptionHelper.substVars("x{:-a}", context);
224 });
225 String expectedMessage = EXPECTING_DATA_AFTER_LEFT_ACCOLADE;
226 assertEquals(expectedMessage, e.getMessage());
227 }
228
229
230 @Test
231 public void nestedEmptyVariables() throws ScanException {
232
233 Exception e = assertThrows(Exception.class, () -> {
234 OptionHelper.substVars("${${${}}}", context);
235 });
236 String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${ ? ? } --> ${ ? } --> ${}]";
237 assertEquals(expectedMessage, e.getMessage());
238 }
239
240
241
242 @Test
243 public void detectCircularReferencesInDefault() throws ScanException {
244 context.putProperty("A", "${B:-${A}}");
245
246
247 Exception e = assertThrows(IllegalArgumentException.class, () -> {
248 OptionHelper.substVars("${A}", context);
249 });
250
251 String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${A} --> ${B} --> ${A}]";
252 assertEquals(expectedMessage, e.getMessage());
253 }
254
255 @Test
256 @Timeout(value = 1, unit = SECONDS)
257 public void detectCircularReferences3() throws ScanException {
258 context.putProperty("A", "${B}");
259 context.putProperty("B", "${C}");
260 context.putProperty("C", "${A}");
261
262 Exception e = assertThrows(IllegalArgumentException.class, () -> {
263 OptionHelper.substVars("${B} ", context);
264 });
265 String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED + "${B} --> ${C} --> ${A} --> ${B}]";
266 assertEquals(expectedMessage, e.getMessage());
267
268 }
269
270 @Test
271 @Timeout(value = 1, unit = SECONDS)
272 public void detectCircularReferences4() throws ScanException {
273 context.putProperty("A", "${B}");
274 context.putProperty("B", "${C}");
275 context.putProperty("C", "${A}");
276
277
278 Exception e = assertThrows(IllegalArgumentException.class, () -> {
279 OptionHelper.substVars("${C} and ${A}", context);
280 });
281 String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${C} --> ${A} --> ${B} --> ${C}]";
282 assertEquals(expectedMessage, e.getMessage());
283 }
284
285 @Test
286 public void detectCircularReferences5() throws ScanException {
287 context.putProperty("A", "${B} and ${C}");
288 context.putProperty("B", "${B1}");
289 context.putProperty("B1", "B1-value");
290 context.putProperty("C", "${C1}");
291 context.putProperty("C1", "here's the loop: ${A}");
292
293
294 Exception e = assertThrows(IllegalArgumentException.class, () -> {
295 OptionHelper.substVars("${A}", context);
296 });
297 String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${A} --> ${C} --> ${C1} --> ${A}]";
298 assertEquals(expectedMessage, e.getMessage());
299 }
300
301 @Test
302 public void defaultValueReferencingAVariable() throws ScanException {
303 context.putProperty("v1", "k1");
304 String result = OptionHelper.substVars("${undef:-${v1}}", context);
305 assertEquals("k1", result);
306 }
307
308 @Test
309 public void jackrabbit_standalone() throws ScanException {
310 String r = OptionHelper.substVars("${jackrabbit.log:-${repo:-jackrabbit}/log/jackrabbit.log}", context);
311 assertEquals("jackrabbit/log/jackrabbit.log", r);
312 }
313
314 @Test
315 public void emptyVariableIsAccepted() throws JoranException, ScanException {
316 String varName = "var"+diff;
317 context.putProperty(varName, "");
318 String r = OptionHelper.substVars("x ${"+varName+"} b", context);
319 assertEquals("x b", r);
320 }
321
322
323
324 @Disabled
325 @Test
326 public void defaultExpansionForEmptyVariables() throws JoranException, ScanException {
327 String varName = "var"+diff;
328 context.putProperty(varName, "");
329
330 String r = OptionHelper.substVars("x ${"+varName+":-def} b", context);
331 assertEquals("x def b", r);
332 }
333
334 @Test
335 public void emptyDefault() throws ScanException {
336 String r = OptionHelper.substVars("a${undefinedX:-}b", context);
337 assertEquals("ab", r);
338 }
339
340 @Test
341 public void openBraceAsLastCharacter() throws JoranException, ScanException {
342 Exception e = assertThrows(IllegalArgumentException.class, () -> {
343 OptionHelper.substVars("a{a{", context);
344 });
345 String expectedMessage = "All tokens consumed but was expecting \"}\"";
346 assertEquals(expectedMessage, e.getMessage());
347 }
348
349
350 @Test
351 public void trailingColon_LOGBACK_1140() throws ScanException {
352 String prefix = "c:";
353 String suffix = "/tmp";
354 context.putProperty("var", prefix);
355 String r = OptionHelper.substVars("${var}" + suffix, context);
356 assertEquals(prefix + suffix, r);
357 }
358
359
360
361
362 @Test
363 public void curlyBraces_LOGBACK_1101() throws ScanException {
364 {
365 String input = "foo{bar}";
366 String r = OptionHelper.substVars(input, context);
367 assertEquals(input, r);
368 }
369 {
370 String input = "{foo{\"bar\"}}";
371 String r = OptionHelper.substVars(input, context);
372 assertEquals(input, r);
373 }
374 {
375 String input = "a:{y}";
376 String r = OptionHelper.substVars(input, context);
377 assertEquals(input, r);
378 }
379 {
380 String input = "{world:{yay}}";
381 String r = OptionHelper.substVars(input, context);
382 assertEquals(input, r);
383 }
384 {
385 String input = "{hello:{world:yay}}";
386 String r = OptionHelper.substVars(input, context);
387 assertEquals(input, r);
388 }
389 {
390 String input = "{\"hello\":{\"world\":\"yay\"}}";
391 String r = OptionHelper.substVars(input, context);
392 assertEquals(input, r);
393 }
394 }
395 }