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