001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.util;
015
016import static org.junit.Assert.assertEquals;
017import static org.junit.Assert.fail;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import org.junit.Before;
023import org.junit.Rule;
024import org.junit.Test;
025import org.junit.rules.ExpectedException;
026
027import ch.qos.logback.core.Context;
028import ch.qos.logback.core.ContextBase;
029import ch.qos.logback.core.joran.spi.JoranException;
030
031public class OptionHelperTest {
032
033    @Rule
034    public ExpectedException expectedException = ExpectedException.none();
035
036    String text = "Testing ${v1} variable substitution ${v2}";
037    String expected = "Testing if variable substitution works";
038    Context context = new ContextBase();
039    Map<String, String> secondaryMap;
040
041    @Before
042    public void setUp() throws Exception {
043        secondaryMap = new HashMap<String, String>();
044    }
045
046    @Test
047    public void testLiteral() {
048        String noSubst = "hello world";
049        String result = OptionHelper.substVars(noSubst, context);
050        assertEquals(noSubst, result);
051    }
052
053    @Test
054    public void testUndefinedValues() {
055        String withUndefinedValues = "${axyz}";
056
057        String result = OptionHelper.substVars(withUndefinedValues, context);
058        assertEquals("axyz" + OptionHelper._IS_UNDEFINED, result);
059    }
060
061    @Test
062    public void testSubstVarsVariableNotClosed() {
063        String noSubst = "testing if ${v1 works";
064
065        try {
066            @SuppressWarnings("unused")
067            String result = OptionHelper.substVars(noSubst, context);
068            fail();
069        } catch (IllegalArgumentException e) {
070            // ok
071        }
072    }
073
074    @Test
075    public void testSubstVarsContextOnly() {
076        context.putProperty("v1", "if");
077        context.putProperty("v2", "works");
078
079        String result = OptionHelper.substVars(text, context);
080        assertEquals(expected, result);
081    }
082
083    @Test
084    public void testSubstVarsSystemProperties() {
085        System.setProperty("v1", "if");
086        System.setProperty("v2", "works");
087
088        String result = OptionHelper.substVars(text, context);
089        assertEquals(expected, result);
090
091        System.clearProperty("v1");
092        System.clearProperty("v2");
093    }
094
095    @Test
096    public void testSubstVarsWithDefault() {
097        context.putProperty("v1", "if");
098        String textWithDefault = "Testing ${v1} variable substitution ${v2:-toto}";
099        String resultWithDefault = "Testing if variable substitution toto";
100
101        String result = OptionHelper.substVars(textWithDefault, context);
102        assertEquals(resultWithDefault, result);
103    }
104
105    @Test
106    public void testSubstVarsRecursive() {
107        context.putProperty("v1", "if");
108        context.putProperty("v2", "${v3}");
109        context.putProperty("v3", "works");
110
111        String result = OptionHelper.substVars(text, context);
112        assertEquals(expected, result);
113    }
114
115    @Test
116    public void testSubstVarsTwoLevelsDeep() {
117        context.putProperty("v1", "if");
118        context.putProperty("v2", "${v3}");
119        context.putProperty("v3", "${v4}");
120        context.putProperty("v4", "works");
121
122        String result = OptionHelper.substVars(text, context);
123        assertEquals(expected, result);
124    }
125
126    @Test
127    public void testSubstVarsTwoLevelsWithDefault() {
128        // Example input taken from LOGBCK-943 bug report
129        context.putProperty("APP_NAME", "LOGBACK");
130        context.putProperty("ARCHIVE_SUFFIX", "archive.log");
131        context.putProperty("LOG_HOME", "${logfilepath.default:-logs}");
132        context.putProperty("ARCHIVE_PATH", "${LOG_HOME}/archive/${APP_NAME}");
133
134        String result = OptionHelper.substVars("${ARCHIVE_PATH}_trace_${ARCHIVE_SUFFIX}", context);
135        assertEquals("logs/archive/LOGBACK_trace_archive.log", result);
136    }
137
138    @Test(timeout = 1000)
139    public void stubstVarsShouldNotGoIntoInfiniteLoop() {
140        context.putProperty("v1", "if");
141        context.putProperty("v2", "${v3}");
142        context.putProperty("v3", "${v4}");
143        context.putProperty("v4", "${v2}c");
144
145        expectedException.expect(Exception.class);
146        OptionHelper.substVars(text, context);
147    }
148
149    @Test
150    public void nonCircularGraphShouldWork() {
151        context.putProperty("A", "${B} and ${C}");
152        context.putProperty("B", "${B1}");
153        context.putProperty("B1", "B1-value");
154        context.putProperty("C", "${C1} and ${B}");
155        context.putProperty("C1", "C1-value");
156
157        String result = OptionHelper.substVars("${A}", context);
158        assertEquals("B1-value and C1-value and B1-value", result);
159    }
160
161    @Test(timeout = 1000)
162    public void detectCircularReferences0() {
163        context.putProperty("A", "${A}");
164
165        expectedException.expect(IllegalArgumentException.class);
166        expectedException.expectMessage("Circular variable reference detected while parsing input [${A} --> ${A}]");
167        OptionHelper.substVars("${A}", context);
168    }
169
170    @Test(timeout = 1000)
171    public void detectCircularReferences1() {
172        context.putProperty("A", "${A}a");
173
174        expectedException.expect(IllegalArgumentException.class);
175        expectedException.expectMessage("Circular variable reference detected while parsing input [${A} --> ${A}]");
176        OptionHelper.substVars("${A}", context);
177    }
178
179    @Test(timeout = 1000)
180    public void detectCircularReferences2() {
181        context.putProperty("A", "${B}");
182        context.putProperty("B", "${C}");
183        context.putProperty("C", "${A}");
184
185        expectedException.expect(IllegalArgumentException.class);
186        expectedException.expectMessage("Circular variable reference detected while parsing input [${A} --> ${B} --> ${C} --> ${A}]");
187        OptionHelper.substVars("${A}", context);
188    }
189
190    @Test
191    public void detectCircularReferencesInDefault() {
192        context.putProperty("A", "${B:-${A}}");
193        expectedException.expect(IllegalArgumentException.class);
194        expectedException.expectMessage("Circular variable reference detected while parsing input [${A} --> ${B} --> ${A}]");
195        OptionHelper.substVars("${A}", context);
196    }
197
198    @Test(timeout = 1000)
199    public void detectCircularReferences3() {
200        context.putProperty("A", "${B}");
201        context.putProperty("B", "${C}");
202        context.putProperty("C", "${A}");
203
204        expectedException.expect(IllegalArgumentException.class);
205        expectedException.expectMessage("Circular variable reference detected while parsing input [${B} --> ${C} --> ${A} --> ${B}]");
206        OptionHelper.substVars("${B} ", context);
207    }
208
209    @Test(timeout = 1000)
210    public void detectCircularReferences4() {
211        context.putProperty("A", "${B}");
212        context.putProperty("B", "${C}");
213        context.putProperty("C", "${A}");
214
215        expectedException.expect(IllegalArgumentException.class);
216        expectedException.expectMessage("Circular variable reference detected while parsing input [${C} --> ${A} --> ${B} --> ${C}]");
217        OptionHelper.substVars("${C} and ${A}", context);
218    }
219
220    @Test
221    public void detectCircularReferences5() {
222        context.putProperty("A", "${B} and ${C}");
223        context.putProperty("B", "${B1}");
224        context.putProperty("B1", "B1-value");
225        context.putProperty("C", "${C1}");
226        context.putProperty("C1", "here's the loop: ${A}");
227
228        expectedException.expect(IllegalArgumentException.class);
229        expectedException.expectMessage("Circular variable reference detected while parsing input [${A} --> ${C} --> ${C1} --> ${A}]");
230        String result = OptionHelper.substVars("${A}", context);
231        System.err.println(result);
232    }
233
234    @Test
235    public void defaultValueReferencingAVariable() {
236        context.putProperty("v1", "k1");
237        String result = OptionHelper.substVars("${undef:-${v1}}", context);
238        assertEquals("k1", result);
239    }
240
241    @Test
242    public void jackrabbit_standalone() {
243        String r = OptionHelper.substVars("${jackrabbit.log:-${repo:-jackrabbit}/log/jackrabbit.log}", context);
244        assertEquals("jackrabbit/log/jackrabbit.log", r);
245    }
246
247    @Test
248    public void doesNotThrowNullPointerExceptionForEmptyVariable() throws JoranException {
249        context.putProperty("var", "");
250        OptionHelper.substVars("${var}", context);
251    }
252
253    @Test
254    public void trailingColon_LOGBACK_1140() {
255        String prefix = "c:";
256        String suffix = "/tmp";
257        context.putProperty("var", prefix);
258        String r = OptionHelper.substVars("${var}" + suffix, context);
259        assertEquals(prefix + suffix, r);
260    }
261
262    @Test
263    public void curlyBraces_LOGBACK_1101() {
264        {
265            String input = "foo{bar}";
266            String r = OptionHelper.substVars(input, context);
267            assertEquals(input, r);
268        }
269        {
270            String input = "{foo{\"bar\"}}";
271            String r = OptionHelper.substVars(input, context);
272            assertEquals(input, r);
273        }
274        {
275            String input = "a:{y}";
276            String r = OptionHelper.substVars(input, context);
277            assertEquals(input, r);
278        }
279        {
280            String input = "{world:{yay}}";
281            String r = OptionHelper.substVars(input, context);
282            assertEquals(input, r);
283        }
284        {
285            String input = "{hello:{world:yay}}";
286            String r = OptionHelper.substVars(input, context);
287            assertEquals(input, r);
288        }
289        {
290            String input = "{\"hello\":{\"world\":\"yay\"}}";
291            String r = OptionHelper.substVars(input, context);
292            assertEquals(input, r);
293        }
294    }
295}