View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration2.builder;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotSame;
22  import static org.junit.jupiter.api.Assertions.assertNull;
23  import static org.junit.jupiter.api.Assertions.assertSame;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  import static org.mockito.Mockito.mock;
27  import static org.mockito.Mockito.verify;
28  import static org.mockito.Mockito.verifyNoMoreInteractions;
29  import static org.mockito.Mockito.when;
30  
31  import java.util.ArrayList;
32  import java.util.Collection;
33  import java.util.Collections;
34  import java.util.HashMap;
35  import java.util.Map;
36  
37  import org.apache.commons.configuration2.ConfigurationDecoder;
38  import org.apache.commons.configuration2.beanutils.BeanHelper;
39  import org.apache.commons.configuration2.convert.ConversionHandler;
40  import org.apache.commons.configuration2.convert.DefaultConversionHandler;
41  import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
42  import org.apache.commons.configuration2.convert.ListDelimiterHandler;
43  import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
44  import org.apache.commons.configuration2.interpol.InterpolatorSpecification;
45  import org.apache.commons.configuration2.interpol.Lookup;
46  import org.apache.commons.configuration2.io.ConfigurationLogger;
47  import org.apache.commons.configuration2.sync.ReadWriteSynchronizer;
48  import org.apache.commons.configuration2.sync.Synchronizer;
49  import org.junit.jupiter.api.BeforeEach;
50  import org.junit.jupiter.api.Test;
51  
52  /**
53   * Test class for {@code BasicBuilderParameters}.
54   */
55  public class TestBasicBuilderParameters {
56      /** The instance to be tested. */
57      private BasicBuilderParameters params;
58  
59      @BeforeEach
60      public void setUp() throws Exception {
61          params = new BasicBuilderParameters();
62      }
63  
64      /**
65       * Tests whether the collection with default lookups can be cloned, too.
66       */
67      @Test
68      public void testCloneDefaultLookups() {
69          final Lookup look = mock(Lookup.class);
70          final Collection<Lookup> looks = Collections.singleton(look);
71          params.setDefaultLookups(looks);
72          final BasicBuilderParameters clone = params.clone();
73          Collection<?> defLooks = (Collection<?>) params.getParameters().get("defaultLookups");
74          defLooks.clear();
75          defLooks = (Collection<?>) clone.getParameters().get("defaultLookups");
76          assertEquals(1, defLooks.size());
77          assertTrue(defLooks.contains(look));
78      }
79  
80      /**
81       * Tests whether the map with prefix lookups is cloned, too.
82       */
83      @Test
84      public void testClonePrefixLookups() {
85          final Lookup look = mock(Lookup.class);
86          final Map<String, Lookup> lookups = Collections.singletonMap("test", look);
87          params.setPrefixLookups(lookups);
88          final BasicBuilderParameters clone = params.clone();
89          Map<?, ?> map = (Map<?, ?>) params.getParameters().get("prefixLookups");
90          map.clear();
91          map = (Map<?, ?>) clone.getParameters().get("prefixLookups");
92          assertEquals(1, map.size());
93          assertSame(look, map.get("test"));
94      }
95  
96      /**
97       * Tests whether a cloned instance contains the same data as the original object.
98       */
99      @Test
100     public void testCloneValues() {
101         final ConfigurationLogger log = mock(ConfigurationLogger.class);
102         final ConfigurationInterpolator ci = mock(ConfigurationInterpolator.class);
103         final ListDelimiterHandler handler1 = mock(ListDelimiterHandler.class);
104         final ListDelimiterHandler handler2 = mock(ListDelimiterHandler.class);
105         params.setListDelimiterHandler(handler1);
106         params.setLogger(log);
107         params.setInterpolator(ci);
108         params.setThrowExceptionOnMissing(true);
109         final BasicBuilderParameters clone = params.clone();
110         params.setListDelimiterHandler(handler2);
111         params.setThrowExceptionOnMissing(false);
112         final Map<String, Object> map = clone.getParameters();
113         assertSame(log, map.get("logger"));
114         assertSame(ci, map.get("interpolator"));
115         assertEquals(handler1, map.get("listDelimiterHandler"));
116         assertEquals(Boolean.TRUE, map.get("throwExceptionOnMissing"));
117     }
118 
119     /**
120      * Tests the default parameter values.
121      */
122     @Test
123     public void testDefaults() {
124         final Map<String, Object> paramMap = params.getParameters();
125         assertTrue(paramMap.isEmpty());
126     }
127 
128     /**
129      * Tests fetchBeanHelper() if no helper was set.
130      */
131     @Test
132     public void testFetchBeanHelperNoSet() {
133         assertNull(BasicBuilderParameters.fetchBeanHelper(params.getParameters()));
134     }
135 
136     /**
137      * Tries to invoke fetchBeanHelper() on a null map.
138      */
139     @Test
140     public void testFetchBeanHelperNullMap() {
141         assertThrows(IllegalArgumentException.class, () -> BasicBuilderParameters.fetchBeanHelper(null));
142     }
143 
144     /**
145      * Tests whether a specification object for interpolation can be obtained.
146      */
147     @Test
148     public void testFetchInterpolatorSpecification() {
149         final ConfigurationInterpolator parent = mock(ConfigurationInterpolator.class);
150         final Lookup l1 = mock(Lookup.class);
151         final Lookup l2 = mock(Lookup.class);
152         final Lookup l3 = mock(Lookup.class);
153         final Map<String, Lookup> prefixLookups = new HashMap<>();
154         prefixLookups.put("p1", l1);
155         prefixLookups.put("p2", l2);
156         final Collection<Lookup> defLookups = Collections.singleton(l3);
157         params.setParentInterpolator(parent);
158         params.setPrefixLookups(prefixLookups);
159         params.setDefaultLookups(defLookups);
160         final Map<String, Object> map = params.getParameters();
161         final InterpolatorSpecification spec = BasicBuilderParameters.fetchInterpolatorSpecification(map);
162         assertSame(parent, spec.getParentInterpolator());
163         assertEquals(prefixLookups, spec.getPrefixLookups());
164         assertEquals(1, spec.getDefaultLookups().size());
165         assertTrue(spec.getDefaultLookups().contains(l3));
166     }
167 
168     /**
169      * Tests that an empty map does not cause any problems.
170      */
171     @Test
172     public void testFetchInterpolatorSpecificationEmpty() {
173         final InterpolatorSpecification spec = BasicBuilderParameters.fetchInterpolatorSpecification(params.getParameters());
174         assertNull(spec.getInterpolator());
175         assertTrue(spec.getDefaultLookups().isEmpty());
176     }
177 
178     /**
179      * Tests fetchInterpolatorSpecification() if the collection with default lookups contains an invalid value.
180      */
181     @Test
182     public void testFetchInterpolatorSpecificationInvalidCollectionValue() {
183         final Map<String, Object> map = new HashMap<>();
184         map.put("defaultLookups", Collections.singleton("not a lookup"));
185         assertThrows(IllegalArgumentException.class, () -> BasicBuilderParameters.fetchInterpolatorSpecification(map));
186     }
187 
188     /**
189      * Tests fetchInterpolatorSpecification() if the map contains a property of an invalid data type.
190      */
191     @Test
192     public void testFetchInterpolatorSpecificationInvalidDataType() {
193         final Map<String, Object> map = new HashMap<>();
194         map.put("interpolator", this);
195         assertThrows(IllegalArgumentException.class, () -> BasicBuilderParameters.fetchInterpolatorSpecification(map));
196     }
197 
198     /**
199      * Tests fetchInterpolatorSpecification() if the map with prefix lookups contains an invalid key.
200      */
201     @Test
202     public void testFetchInterpolatorSpecificationInvalidMapKey() {
203         final Map<String, Object> map = new HashMap<>();
204         final Map<Object, Object> prefix = new HashMap<>();
205         prefix.put(42, mock(Lookup.class));
206         map.put("prefixLookups", prefix);
207         assertThrows(IllegalArgumentException.class, () -> BasicBuilderParameters.fetchInterpolatorSpecification(map));
208     }
209 
210     /**
211      * Tests fetchInterpolatorSpecification() if the map with prefix lookups contains an invalid value.
212      */
213     @Test
214     public void testFetchInterpolatorSpecificationInvalidMapValue() {
215         final Map<String, Object> map = new HashMap<>();
216         final Map<Object, Object> prefix = new HashMap<>();
217         prefix.put("test", this);
218         map.put("prefixLookups", prefix);
219         assertThrows(IllegalArgumentException.class, () -> BasicBuilderParameters.fetchInterpolatorSpecification(map));
220     }
221 
222     /**
223      * Tries to obtain an {@code InterpolatorSpecification} from a null map.
224      */
225     @Test
226     public void testFetchInterpolatorSpecificationNull() {
227         assertThrows(IllegalArgumentException.class, () -> BasicBuilderParameters.fetchInterpolatorSpecification(null));
228     }
229 
230     /**
231      * Tests whether an InterpolatorSpecification can be fetched if a ConfigurationInterpolator is present.
232      */
233     @Test
234     public void testFetchInterpolatorSpecificationWithInterpolator() {
235         final ConfigurationInterpolator ci = mock(ConfigurationInterpolator.class);
236         params.setInterpolator(ci);
237         final InterpolatorSpecification spec = BasicBuilderParameters.fetchInterpolatorSpecification(params.getParameters());
238         assertSame(ci, spec.getInterpolator());
239         assertNull(spec.getParentInterpolator());
240     }
241 
242     /**
243      * Tests whether a defensive copy is created when the parameter map is returned.
244      */
245     @Test
246     public void testGetParametersDefensiveCopy() {
247         final Map<String, Object> map1 = params.getParameters();
248         final Map<String, Object> mapCopy = new HashMap<>(map1);
249         map1.put("otherProperty", "value");
250         final Map<String, Object> map2 = params.getParameters();
251         assertNotSame(map1, map2);
252         assertEquals(mapCopy, map2);
253     }
254 
255     /**
256      * Tests whether properties can be inherited from another parameters map.
257      */
258     @Test
259     public void testInheritFrom() {
260         final BeanHelper beanHelper = new BeanHelper();
261         final ConfigurationDecoder decoder = mock(ConfigurationDecoder.class);
262         final ConversionHandler conversionHandler = new DefaultConversionHandler();
263         final ListDelimiterHandler listDelimiterHandler = new DefaultListDelimiterHandler('#');
264         final ConfigurationLogger logger = new ConfigurationLogger("test");
265         final Synchronizer synchronizer = new ReadWriteSynchronizer();
266         params.setBeanHelper(beanHelper).setConfigurationDecoder(decoder).setConversionHandler(conversionHandler).setListDelimiterHandler(listDelimiterHandler)
267             .setLogger(logger).setSynchronizer(synchronizer).setThrowExceptionOnMissing(true);
268         final BasicBuilderParameters p2 = new BasicBuilderParameters();
269 
270         p2.inheritFrom(params.getParameters());
271         final Map<String, Object> parameters = p2.getParameters();
272         assertEquals(beanHelper, parameters.get("config-BeanHelper"));
273         assertEquals(decoder, parameters.get("configurationDecoder"));
274         assertEquals(conversionHandler, parameters.get("conversionHandler"));
275         assertEquals(listDelimiterHandler, parameters.get("listDelimiterHandler"));
276         assertEquals(logger, parameters.get("logger"));
277         assertEquals(synchronizer, parameters.get("synchronizer"));
278         assertEquals(Boolean.TRUE, parameters.get("throwExceptionOnMissing"));
279     }
280 
281     /**
282      * Tests whether null input is handled by inheritFrom().
283      */
284     @Test
285     public void testInheritFromNull() {
286         assertThrows(IllegalArgumentException.class, () -> params.inheritFrom(null));
287     }
288 
289     /**
290      * Tests that undefined properties are not copied over by inheritFrom().
291      */
292     @Test
293     public void testInheritFromUndefinedProperties() {
294         final BasicBuilderParameters p2 = new BasicBuilderParameters().setThrowExceptionOnMissing(true);
295 
296         p2.inheritFrom(Collections.<String, Object>emptyMap());
297         final Map<String, Object> parameters = p2.getParameters();
298         assertEquals(Collections.singletonMap("throwExceptionOnMissing", Boolean.TRUE), parameters);
299     }
300 
301     /**
302      * Tests whether properties of other parameter objects can be merged.
303      */
304     @Test
305     public void testMerge() {
306         final ListDelimiterHandler handler1 = mock(ListDelimiterHandler.class);
307         final ListDelimiterHandler handler2 = mock(ListDelimiterHandler.class);
308         final Map<String, Object> props = new HashMap<>();
309         props.put("throwExceptionOnMissing", Boolean.TRUE);
310         props.put("listDelimiterHandler", handler1);
311         props.put("other", "test");
312         props.put(BuilderParameters.RESERVED_PARAMETER_PREFIX + "test", "reserved");
313         final BuilderParameters p = mock(BuilderParameters.class);
314 
315         when(p.getParameters()).thenReturn(props);
316 
317         params.setListDelimiterHandler(handler2);
318         params.merge(p);
319         final Map<String, Object> map = params.getParameters();
320         assertEquals(handler2, map.get("listDelimiterHandler"));
321         assertEquals(Boolean.TRUE, map.get("throwExceptionOnMissing"));
322         assertEquals("test", map.get("other"));
323         assertFalse(map.containsKey(BuilderParameters.RESERVED_PARAMETER_PREFIX + "test"));
324 
325         verify(p).getParameters();
326         verifyNoMoreInteractions(p);
327     }
328 
329     /**
330      * Tries a merge with a null object.
331      */
332     @Test
333     public void testMergeNull() {
334         assertThrows(IllegalArgumentException.class, () -> params.merge(null));
335     }
336 
337     /**
338      * Tests whether a BeanHelper can be set.
339      */
340     @Test
341     public void testSetBeanHelper() {
342         final BeanHelper helper = new BeanHelper();
343         assertSame(params, params.setBeanHelper(helper));
344         assertSame(helper, BasicBuilderParameters.fetchBeanHelper(params.getParameters()));
345     }
346 
347     /**
348      * Tests whether a decoder can be set.
349      */
350     @Test
351     public void testSetConfigurationDecoder() {
352         final ConfigurationDecoder decoder = mock(ConfigurationDecoder.class);
353         assertSame(params, params.setConfigurationDecoder(decoder));
354         assertSame(decoder, params.getParameters().get("configurationDecoder"));
355     }
356 
357     /**
358      * Tests whether a ConversionHandler can be set.
359      */
360     @Test
361     public void testSetConversionHandler() {
362         final ConversionHandler handler = mock(ConversionHandler.class);
363         assertSame(params, params.setConversionHandler(handler));
364         assertSame(handler, params.getParameters().get("conversionHandler"));
365     }
366 
367     /**
368      * Tests whether default lookups can be set.
369      */
370     @Test
371     public void testSetDefaultLookups() {
372         final Lookup look = mock(Lookup.class);
373         final Collection<Lookup> looks = Collections.singleton(look);
374         assertSame(params, params.setDefaultLookups(looks));
375         final Collection<?> col = (Collection<?>) params.getParameters().get("defaultLookups");
376         assertNotSame(col, looks);
377         assertEquals(1, col.size());
378         assertSame(look, col.iterator().next());
379         final Collection<?> col2 = (Collection<?>) params.getParameters().get("defaultLookups");
380         assertNotSame(col, col2);
381     }
382 
383     /**
384      * Tests whether null values are handled by setDefaultLookups().
385      */
386     @Test
387     public void testSetDefaultLookupsNull() {
388         params.setDefaultLookups(new ArrayList<>());
389         params.setDefaultLookups(null);
390         assertFalse(params.getParameters().containsKey("defaultLookups"));
391     }
392 
393     /**
394      * Tests whether a {@code ConfigurationInterpolator} can be set.
395      */
396     @Test
397     public void testSetInterpolator() {
398         final ConfigurationInterpolator ci = mock(ConfigurationInterpolator.class);
399         assertSame(params, params.setInterpolator(ci));
400         assertSame(ci, params.getParameters().get("interpolator"));
401     }
402 
403     /**
404      * Tests whether the list delimiter handler property can be set.
405      */
406     @Test
407     public void testSetListDelimiter() {
408         final ListDelimiterHandler handler = mock(ListDelimiterHandler.class);
409         assertSame(params, params.setListDelimiterHandler(handler));
410         assertSame(handler, params.getParameters().get("listDelimiterHandler"));
411     }
412 
413     /**
414      * Tests whether the logger parameter can be set.
415      */
416     @Test
417     public void testSetLogger() {
418         final ConfigurationLogger log = mock(ConfigurationLogger.class);
419         assertSame(params, params.setLogger(log));
420         assertSame(log, params.getParameters().get("logger"));
421     }
422 
423     /**
424      * Tests whether a custom {@code ConfigurationInterpolator} overrides settings for custom lookups.
425      */
426     @Test
427     public void testSetLookupsAndInterpolator() {
428         final Lookup look1 = mock(Lookup.class);
429         final Lookup look2 = mock(Lookup.class);
430         final ConfigurationInterpolator parent = mock(ConfigurationInterpolator.class);
431         final ConfigurationInterpolator ci = mock(ConfigurationInterpolator.class);
432         params.setDefaultLookups(Collections.singleton(look1));
433         params.setPrefixLookups(Collections.singletonMap("test", look2));
434         params.setInterpolator(ci);
435         params.setParentInterpolator(parent);
436         final Map<String, Object> map = params.getParameters();
437         assertFalse(map.containsKey("prefixLookups"));
438         assertFalse(map.containsKey("defaultLookups"));
439         assertFalse(map.containsKey("parentInterpolator"));
440     }
441 
442     /**
443      * Tests whether a parent {@code ConfigurationInterpolator} can be set.
444      */
445     @Test
446     public void testSetParentInterpolator() {
447         final ConfigurationInterpolator parent = mock(ConfigurationInterpolator.class);
448         assertSame(params, params.setParentInterpolator(parent));
449         assertSame(parent, params.getParameters().get("parentInterpolator"));
450     }
451 
452     /**
453      * Tests whether prefix lookups can be set.
454      */
455     @Test
456     public void testSetPrefixLookups() {
457         final Lookup look = mock(Lookup.class);
458         final Map<String, Lookup> lookups = Collections.singletonMap("test", look);
459         assertSame(params, params.setPrefixLookups(lookups));
460         final Map<?, ?> map = (Map<?, ?>) params.getParameters().get("prefixLookups");
461         assertNotSame(lookups, map);
462         assertEquals(Collections.singletonMap("test", look), map);
463         final Map<?, ?> map2 = (Map<?, ?>) params.getParameters().get("prefixLookups");
464         assertNotSame(map, map2);
465     }
466 
467     /**
468      * Tests whether null values are handled by setPrefixLookups().
469      */
470     @Test
471     public void testSetPrefixLookupsNull() {
472         params.setPrefixLookups(new HashMap<>());
473         params.setPrefixLookups(null);
474         assertFalse(params.getParameters().containsKey("prefixLookups"));
475     }
476 
477     /**
478      * Tests whether a Synchronizer can be set.
479      */
480     @Test
481     public void testSetSynchronizer() {
482         final Synchronizer sync = mock(Synchronizer.class);
483         assertSame(params, params.setSynchronizer(sync));
484         assertSame(sync, params.getParameters().get("synchronizer"));
485     }
486 
487     /**
488      * Tests whether the throw exception on missing property can be set.
489      */
490     @Test
491     public void testSetThrowExceptionOnMissing() {
492         assertSame(params, params.setThrowExceptionOnMissing(true));
493         assertEquals(Boolean.TRUE, params.getParameters().get("throwExceptionOnMissing"));
494     }
495 }