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;
18  
19  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
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.when;
28  
29  import java.util.ArrayList;
30  import java.util.Arrays;
31  import java.util.Collection;
32  import java.util.Collections;
33  import java.util.HashMap;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.NoSuchElementException;
38  
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.DisabledListDelimiterHandler;
43  import org.apache.commons.configuration2.event.ConfigurationEvent;
44  import org.apache.commons.configuration2.event.EventListener;
45  import org.apache.commons.configuration2.event.EventType;
46  import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
47  import org.apache.commons.configuration2.interpol.Lookup;
48  import org.apache.commons.lang3.ArrayUtils;
49  import org.junit.jupiter.api.Test;
50  
51  /**
52   * A test class for some of the basic functionality implemented by AbstractConfiguration.
53   */
54  public class TestAbstractConfigurationBasicFeatures {
55      /**
56       * An event listener implementation that simply collects all received configuration events.
57       */
58      private static final class CollectingConfigurationListener implements EventListener<ConfigurationEvent> {
59          final List<ConfigurationEvent> events = new ArrayList<>();
60  
61          @Override
62          public void onEvent(final ConfigurationEvent event) {
63              events.add(event);
64          }
65      }
66  
67      /**
68       * A test configuration implementation. This implementation inherits directly from AbstractConfiguration. For
69       * implementing the required functionality another implementation of AbstractConfiguration is used; all methods that
70       * need to be implemented delegate to this wrapped configuration.
71       */
72      static class TestConfigurationImpl extends AbstractConfiguration {
73          /** Stores the underlying configuration. */
74          private final AbstractConfiguration config;
75  
76          public TestConfigurationImpl(final AbstractConfiguration wrappedConfig) {
77              config = wrappedConfig;
78          }
79  
80          @Override
81          protected void addPropertyDirect(final String key, final Object value) {
82              config.addPropertyDirect(key, value);
83          }
84  
85          @Override
86          protected void clearPropertyDirect(final String key) {
87              config.clearPropertyDirect(key);
88          }
89  
90          @Override
91          protected boolean containsKeyInternal(final String key) {
92              return config.containsKey(key);
93          }
94  
95          @Override
96          protected Iterator<String> getKeysInternal() {
97              return config.getKeys();
98          }
99  
100         @Override
101         protected Object getPropertyInternal(final String key) {
102             return config.getProperty(key);
103         }
104 
105         public AbstractConfiguration getUnderlyingConfiguration() {
106             return config;
107         }
108 
109         @Override
110         protected boolean isEmptyInternal() {
111             return config.isEmpty();
112         }
113     }
114 
115     /** Constant for text to be used in tests for variable substitution. */
116     private static final String SUBST_TXT = "The ${animal} jumps over the ${target}.";
117 
118     /** Constant for the prefix of test keys. */
119     private static final String KEY_PREFIX = "key";
120 
121     /** Constant for the number of properties in tests for copy operations. */
122     private static final int PROP_COUNT = 12;
123 
124     /**
125      * Prepares a test configuration for a test for a list conversion. The configuration is populated with a list property.
126      * The returned list contains the expected list values converted to integers.
127      *
128      * @param config the test configuration
129      * @return the list with expected values
130      */
131     private static List<Integer> prepareListTest(final PropertiesConfiguration config) {
132         final List<Integer> expected = new ArrayList<>(PROP_COUNT);
133         for (int i = 0; i < PROP_COUNT; i++) {
134             config.addProperty(KEY_PREFIX, String.valueOf(i));
135             expected.add(i);
136         }
137         return expected;
138     }
139 
140     /**
141      * Helper method for adding properties with multiple values.
142      *
143      * @param config the configuration to be used for testing
144      */
145     private void checkAddListProperty(final AbstractConfiguration config) {
146         config.addProperty("test", "value1");
147         final Object[] lstValues1 = {"value2", "value3"};
148         final Object[] lstValues2 = {"value4", "value5", "value6"};
149         config.addProperty("test", lstValues1);
150         config.addProperty("test", Arrays.asList(lstValues2));
151         final List<Object> lst = config.getList("test");
152 
153         final List<Object> expected = new ArrayList<>();
154         for (int i = 0; i < lst.size(); i++) {
155             expected.add("value" + (i + 1));
156         }
157         assertEquals(expected, lst);
158     }
159 
160     /**
161      * Tests whether the correct events are received for a copy operation.
162      *
163      * @param l the event listener
164      * @param src the configuration that was copied
165      * @param eventType the expected event type
166      */
167     private void checkCopyEvents(final CollectingConfigurationListener l, final Configuration src, final EventType<?> eventType) {
168         final Map<String, ConfigurationEvent> events = new HashMap<>();
169         for (final ConfigurationEvent e : l.events) {
170             assertEquals(eventType, e.getEventType());
171             assertTrue(src.containsKey(e.getPropertyName()), "Unknown property: " + e.getPropertyName());
172             if (!e.isBeforeUpdate()) {
173                 assertTrue(events.containsKey(e.getPropertyName()));
174             } else {
175                 events.put(e.getPropertyName(), e);
176             }
177         }
178 
179         for (final Iterator<String> it = src.getKeys(); it.hasNext();) {
180             final String key = it.next();
181             assertTrue(events.containsKey(key), "No event received for key " + key);
182         }
183     }
184 
185     /**
186      * Helper method for checking getList() if the property value is a scalar.
187      *
188      * @param value the value of the property
189      */
190     private void checkGetListScalar(final Object value) {
191         final BaseConfiguration config = new BaseConfiguration();
192         config.addProperty(KEY_PREFIX, value);
193         final List<Object> lst = config.getList(KEY_PREFIX);
194         assertEquals(Arrays.asList(value.toString()), lst);
195     }
196 
197     /**
198      * Helper method for checking getStringArray() if the property value is a scalar.
199      *
200      * @param value the value of the property
201      */
202     private void checkGetStringArrayScalar(final Object value) {
203         final BaseConfiguration config = new BaseConfiguration();
204         config.addProperty(KEY_PREFIX, value);
205         final String[] array = config.getStringArray(KEY_PREFIX);
206         assertArrayEquals(new String[] {value.toString()}, array);
207     }
208 
209     /**
210      * Tests the values of list properties after a copy operation.
211      *
212      * @param config the configuration to test
213      */
214     private void checkListProperties(final Configuration config) {
215         List<Object> values = config.getList("list1");
216         assertEquals(3, values.size());
217         values = config.getList("list2");
218         assertEquals(Arrays.asList("3,1415", "9,81"), values);
219     }
220 
221     /**
222      * Creates the destination configuration for testing the copy() and append() methods. This configuration contains keys
223      * with a running index and corresponding values starting with the prefix "value".
224      *
225      * @return the destination configuration for copy operations
226      */
227     private AbstractConfiguration setUpDestConfig() {
228         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
229         config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
230         for (int i = 0; i < PROP_COUNT; i++) {
231             config.addProperty(KEY_PREFIX + i, "value" + i);
232         }
233         return config;
234     }
235 
236     /**
237      * Creates the source configuration for testing the copy() and append() methods. This configuration contains keys with
238      * an odd index and values starting with the prefix "src". There are also some list properties.
239      *
240      * @return the source configuration for copy operations
241      */
242     private Configuration setUpSourceConfig() {
243         final BaseConfiguration config = new BaseConfiguration();
244         config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
245         for (int i = 1; i < PROP_COUNT; i += 2) {
246             config.addProperty(KEY_PREFIX + i, "src" + i);
247         }
248         config.addProperty("list1", "1,2,3");
249         config.addProperty("list2", "3\\,1415,9\\,81");
250         return config;
251     }
252 
253     /**
254      * Tests adding list properties. The single elements of the list should be added.
255      */
256     @Test
257     public void testAddPropertyList() {
258         checkAddListProperty(new TestConfigurationImpl(new PropertiesConfiguration()));
259     }
260 
261     /**
262      * Tests adding list properties if delimiter parsing is disabled.
263      */
264     @Test
265     public void testAddPropertyListNoDelimiterParsing() {
266         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
267         checkAddListProperty(config);
268     }
269 
270     /**
271      * Tests the append() method.
272      */
273     @Test
274     public void testAppend() {
275         final AbstractConfiguration config = setUpDestConfig();
276         final Configuration srcConfig = setUpSourceConfig();
277         config.append(srcConfig);
278         for (int i = 0; i < PROP_COUNT; i++) {
279             final String key = KEY_PREFIX + i;
280             if (srcConfig.containsKey(key)) {
281                 final List<Object> values = config.getList(key);
282                 assertEquals(Arrays.asList("value" + i, "src" + i), values, "Wrong values for " + key);
283             } else {
284                 assertEquals("value" + i, config.getProperty(key), "Value modified: " + key);
285             }
286         }
287     }
288 
289     /**
290      * Tests whether the list delimiter is correctly handled if a configuration is appended.
291      */
292     @Test
293     public void testAppendDelimiterHandling() {
294         final BaseConfiguration srcConfig = new BaseConfiguration();
295         final BaseConfiguration dstConfig = new BaseConfiguration();
296         dstConfig.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
297         srcConfig.setProperty(KEY_PREFIX, "C:\\Temp\\,D:\\Data");
298         dstConfig.append(srcConfig);
299         assertEquals(srcConfig.getString(KEY_PREFIX), dstConfig.getString(KEY_PREFIX));
300     }
301 
302     /**
303      * Tests the events generated by an append() operation.
304      */
305     @Test
306     public void testAppendEvents() {
307         final AbstractConfiguration config = setUpDestConfig();
308         final Configuration srcConfig = setUpSourceConfig();
309         final CollectingConfigurationListener l = new CollectingConfigurationListener();
310         config.addEventListener(ConfigurationEvent.ANY, l);
311         config.append(srcConfig);
312         checkCopyEvents(l, srcConfig, ConfigurationEvent.ADD_PROPERTY);
313     }
314 
315     /**
316      * Tests appending a null configuration. This should be a noop.
317      */
318     @Test
319     public void testAppendNull() {
320         final AbstractConfiguration config = setUpDestConfig();
321         config.append(null);
322         ConfigurationAssert.assertConfigurationEquals(setUpDestConfig(), config);
323     }
324 
325     /**
326      * Tests the append() method when properties with multiple values and escaped list delimiters are involved.
327      */
328     @Test
329     public void testAppendWithLists() {
330         final AbstractConfiguration config = setUpDestConfig();
331         config.append(setUpSourceConfig());
332         checkListProperties(config);
333     }
334 
335     /**
336      * Tests the clear() implementation of AbstractConfiguration if the iterator returned by getKeys() does not support the
337      * remove() operation.
338      */
339     @Test
340     public void testClearIteratorNoRemove() {
341         final AbstractConfiguration config = new TestConfigurationImpl(new BaseConfiguration()) {
342             // return an iterator that does not support remove operations
343             @Override
344             protected Iterator<String> getKeysInternal() {
345                 final Collection<String> keyCol = new ArrayList<>();
346                 ConfigurationAssert.appendKeys(getUnderlyingConfiguration(), keyCol);
347                 final String[] keys = keyCol.toArray(new String[keyCol.size()]);
348                 return Arrays.asList(keys).iterator();
349             }
350         };
351         for (int i = 0; i < 20; i++) {
352             config.addProperty("key" + i, "value" + i);
353         }
354         config.clear();
355         assertTrue(config.isEmpty());
356     }
357 
358     /**
359      * Tests the copy() method.
360      */
361     @Test
362     public void testCopy() {
363         final AbstractConfiguration config = setUpDestConfig();
364         final Configuration srcConfig = setUpSourceConfig();
365         config.copy(srcConfig);
366         for (int i = 0; i < PROP_COUNT; i++) {
367             final String key = KEY_PREFIX + i;
368             if (srcConfig.containsKey(key)) {
369                 assertEquals(srcConfig.getProperty(key), config.getProperty(key), "Value not replaced: " + key);
370             } else {
371                 assertEquals("value" + i, config.getProperty(key), "Value modified: " + key);
372             }
373         }
374     }
375 
376     /**
377      * Tests whether list delimiters are correctly handled when copying a configuration.
378      */
379     @Test
380     public void testCopyDelimiterHandling() {
381         final BaseConfiguration srcConfig = new BaseConfiguration();
382         final BaseConfiguration dstConfig = new BaseConfiguration();
383         dstConfig.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
384         srcConfig.setProperty(KEY_PREFIX, "C:\\Temp\\,D:\\Data");
385         dstConfig.copy(srcConfig);
386         assertEquals(srcConfig.getString(KEY_PREFIX), dstConfig.getString(KEY_PREFIX));
387     }
388 
389     /**
390      * Tests the events generated by a copy() operation.
391      */
392     @Test
393     public void testCopyEvents() {
394         final AbstractConfiguration config = setUpDestConfig();
395         final Configuration srcConfig = setUpSourceConfig();
396         final CollectingConfigurationListener l = new CollectingConfigurationListener();
397         config.addEventListener(ConfigurationEvent.ANY, l);
398         config.copy(srcConfig);
399         checkCopyEvents(l, srcConfig, ConfigurationEvent.SET_PROPERTY);
400     }
401 
402     /**
403      * Tests copying a null configuration. This should be a noop.
404      */
405     @Test
406     public void testCopyNull() {
407         final AbstractConfiguration config = setUpDestConfig();
408         config.copy(null);
409         ConfigurationAssert.assertConfigurationEquals(setUpDestConfig(), config);
410     }
411 
412     /**
413      * Tests the copy() method if properties with multiple values and escaped list delimiters are involved.
414      */
415     @Test
416     public void testCopyWithLists() {
417         final Configuration srcConfig = setUpSourceConfig();
418         final AbstractConfiguration config = setUpDestConfig();
419         config.copy(srcConfig);
420         checkListProperties(config);
421     }
422 
423     /**
424      * Tests an interpolation that leads to a cycle. This should throw an exception.
425      */
426     @Test
427     public void testCyclicInterpolation() {
428         final PropertiesConfiguration config = new PropertiesConfiguration();
429         config.addProperty("animal", "${animal_attr} ${species}");
430         config.addProperty("animal_attr", "quick brown");
431         config.addProperty("species", "${animal}");
432         config.addProperty(KEY_PREFIX, "This is a ${animal}");
433         assertThrows(IllegalStateException.class, () -> config.getString(KEY_PREFIX));
434     }
435 
436     /**
437      * Tests whether a configuration instance has a default conversion hander.
438      */
439     @Test
440     public void testDefaultConversionHandler() {
441         final PropertiesConfiguration config = new PropertiesConfiguration();
442         assertEquals(DefaultConversionHandler.class, config.getConversionHandler().getClass());
443     }
444 
445     /**
446      * Tests that the default conversion handler is shared between all configuration instances.
447      */
448     @Test
449     public void testDefaultConversionHandlerSharedInstance() {
450         final PropertiesConfiguration config = new PropertiesConfiguration();
451         final PropertiesConfiguration config2 = new PropertiesConfiguration();
452         assertSame(config.getConversionHandler(), config2.getConversionHandler());
453     }
454 
455     /**
456      * Tests the default list delimiter hander.
457      */
458     @Test
459     public void testDefaultListDelimiterHandler() {
460         final BaseConfiguration config = new BaseConfiguration();
461         assertInstanceOf(DisabledListDelimiterHandler.class, config.getListDelimiterHandler());
462     }
463 
464     /**
465      * Tests the generic get() method.
466      */
467     @Test
468     public void testGet() {
469         final PropertiesConfiguration config = new PropertiesConfiguration();
470         final Integer value = 20130816;
471         config.addProperty(KEY_PREFIX, value.toString());
472         assertEquals(value, config.get(Integer.class, KEY_PREFIX));
473     }
474 
475     /**
476      * Tests whether conversion to an array is possible.
477      */
478     @Test
479     public void testGetArray() {
480         final PropertiesConfiguration config = new PropertiesConfiguration();
481         final Integer[] expected = new Integer[PROP_COUNT];
482         for (int i = 0; i < PROP_COUNT; i++) {
483             config.addProperty(KEY_PREFIX, String.valueOf(i));
484             expected[i] = Integer.valueOf(i);
485         }
486         final Integer[] result = config.get(Integer[].class, KEY_PREFIX);
487         assertArrayEquals(expected, result);
488     }
489 
490     /**
491      * Tests getArray() if the default value is not an array.
492      */
493     @Test
494     public void testGetArrayDefaultValueNotAnArray() {
495         final PropertiesConfiguration config = new PropertiesConfiguration();
496         assertThrows(IllegalArgumentException.class, () -> config.getArray(Integer.class, KEY_PREFIX, this));
497     }
498 
499     /**
500      * Tests getArray() if the default value is an array with a different component type.
501      */
502     @Test
503     public void testGetArrayDefaultValueWrongComponentClass() {
504         final PropertiesConfiguration config = new PropertiesConfiguration();
505         assertThrows(IllegalArgumentException.class, () -> config.getArray(Integer.class, KEY_PREFIX, new short[1]));
506     }
507 
508     /**
509      * Tests a conversion to an array of primitive types.
510      */
511     @Test
512     public void testGetArrayPrimitive() {
513         final PropertiesConfiguration config = new PropertiesConfiguration();
514         final short[] expected = new short[PROP_COUNT];
515         for (int i = 0; i < PROP_COUNT; i++) {
516             config.addProperty(KEY_PREFIX, String.valueOf(i));
517             expected[i] = (short) i;
518         }
519         final short[] result = config.get(short[].class, KEY_PREFIX, ArrayUtils.EMPTY_SHORT_ARRAY);
520         assertArrayEquals(expected, result);
521     }
522 
523     /**
524      * Tests get() for an unknown array property if no default value is provided.
525      */
526     @Test
527     public void testGetArrayUnknownNoDefault() {
528         final PropertiesConfiguration config = new PropertiesConfiguration();
529         assertNull(config.get(Integer[].class, KEY_PREFIX));
530     }
531 
532     /**
533      * Tests get() for an unknown array property if a default value is provided.
534      */
535     @Test
536     public void testGetArrayUnknownWithDefault() {
537         final PropertiesConfiguration config = new PropertiesConfiguration();
538         final int[] defValue = {1, 2, 3};
539         assertArrayEquals(defValue, config.get(int[].class, KEY_PREFIX, defValue));
540     }
541 
542     /**
543      * Tests a conversion to a collection.
544      */
545     @Test
546     public void testGetCollection() {
547         final PropertiesConfiguration config = new PropertiesConfiguration();
548         final List<Integer> expected = prepareListTest(config);
549         final List<Integer> result = new ArrayList<>(PROP_COUNT);
550         assertSame(result, config.getCollection(Integer.class, KEY_PREFIX, result));
551         assertEquals(expected, result);
552     }
553 
554     /**
555      * Tests getCollection() if no target collection is provided.
556      */
557     @Test
558     public void testGetCollectionNullTarget() {
559         final PropertiesConfiguration config = new PropertiesConfiguration();
560         final List<Integer> expected = prepareListTest(config);
561         final Collection<Integer> result = config.getCollection(Integer.class, KEY_PREFIX, null, new ArrayList<>());
562         assertEquals(expected, result);
563     }
564 
565     /**
566      * Tests whether a single value property can be converted to a collection.
567      */
568     @Test
569     public void testGetCollectionSingleValue() {
570         final PropertiesConfiguration config = new PropertiesConfiguration();
571         config.addProperty(KEY_PREFIX, "1");
572         final List<Integer> result = new ArrayList<>(1);
573         config.getCollection(Integer.class, KEY_PREFIX, result);
574         assertEquals(Arrays.asList(1), result);
575     }
576 
577     /**
578      * Tests getCollection() for an unknown property if no default value is provided.
579      */
580     @Test
581     public void testGetCollectionUnknownNoDefault() {
582         final PropertiesConfiguration config = new PropertiesConfiguration();
583         final List<Integer> result = new ArrayList<>();
584         assertNull(config.getCollection(Integer.class, KEY_PREFIX, result));
585         assertTrue(result.isEmpty());
586     }
587 
588     /**
589      * Tests getCollection() for an unknown property if a default collection is provided.
590      */
591     @Test
592     public void testGetCollectionUnknownWithDefault() {
593         final PropertiesConfiguration config = new PropertiesConfiguration();
594         final List<Integer> defValue = Arrays.asList(1, 2, 4, 8, 16, 32);
595         final Collection<Integer> result = config.getCollection(Integer.class, KEY_PREFIX, null, defValue);
596         assertEquals(defValue, result);
597     }
598 
599     /**
600      * Tries to query an encoded string without a decoder.
601      */
602     @Test
603     public void testGetEncodedStringNoDecoder() {
604         final PropertiesConfiguration config = new PropertiesConfiguration();
605         assertThrows(IllegalArgumentException.class, () -> config.getEncodedString(KEY_PREFIX, null));
606     }
607 
608     /**
609      * Tries to query an encoded string with the default decoder if this property is not defined.
610      */
611     @Test
612     public void testGetEncodedStringNoDefaultDecoderDefined() {
613         final PropertiesConfiguration config = new PropertiesConfiguration();
614         assertThrows(IllegalStateException.class, () -> config.getEncodedString(KEY_PREFIX));
615     }
616 
617     /**
618      * Tests whether undefined keys are handled when querying encoded strings.
619      */
620     @Test
621     public void testGetEncodedStringNoValue() {
622         final ConfigurationDecoder decoder = mock(ConfigurationDecoder.class);
623         final PropertiesConfiguration config = new PropertiesConfiguration();
624         assertNull(config.getEncodedString(KEY_PREFIX, decoder));
625     }
626 
627     /**
628      * Tests whether an encoded value can be retrieved.
629      */
630     @Test
631     public void testGetEncodedStringValue() {
632         final ConfigurationDecoder decoder = mock(ConfigurationDecoder.class);
633         final String value = "original value";
634         final String decodedValue = "decoded value";
635 
636         when(decoder.decode(value)).thenReturn(decodedValue);
637 
638         final PropertiesConfiguration config = new PropertiesConfiguration();
639         config.addProperty(KEY_PREFIX, value);
640         assertEquals(decodedValue, config.getEncodedString(KEY_PREFIX, decoder));
641     }
642 
643     /**
644      * Tests whether a default decoder can be set which is queried for encoded strings.
645      */
646     @Test
647     public void testGetEncodedStringWithDefaultDecoder() {
648         final ConfigurationDecoder decoder = mock(ConfigurationDecoder.class);
649         final String value = "original value";
650         final String decodedValue = "decoded value";
651 
652         when(decoder.decode(value)).thenReturn(decodedValue);
653 
654         final PropertiesConfiguration config = new PropertiesConfiguration();
655         config.setConfigurationDecoder(decoder);
656         config.addProperty(KEY_PREFIX, value);
657         assertEquals(decodedValue, config.getEncodedString(KEY_PREFIX));
658     }
659 
660     /**
661      * Tests a conversion to a list.
662      */
663     @Test
664     public void testGetList() {
665         final PropertiesConfiguration config = new PropertiesConfiguration();
666         final List<Integer> expected = prepareListTest(config);
667         final List<Integer> result = config.getList(Integer.class, KEY_PREFIX);
668         assertEquals(expected, result);
669     }
670 
671     /**
672      * Tests getList() for single non-string values.
673      */
674     @Test
675     public void testGetListNonString() {
676         checkGetListScalar(Integer.valueOf(42));
677         checkGetListScalar(Long.valueOf(42));
678         checkGetListScalar(Short.valueOf((short) 42));
679         checkGetListScalar(Byte.valueOf((byte) 42));
680         checkGetListScalar(Float.valueOf(42));
681         checkGetListScalar(Double.valueOf(42));
682         checkGetListScalar(Boolean.TRUE);
683     }
684 
685     /**
686      * Tests a conversion to a list if the property is unknown and no default value is provided.
687      */
688     @Test
689     public void testGetListUnknownNoDefault() {
690         final PropertiesConfiguration config = new PropertiesConfiguration();
691         assertNull(config.getList(Integer.class, KEY_PREFIX));
692     }
693 
694     /**
695      * Tests a conversion to a list if the property is unknown and a default list is provided.
696      */
697     @Test
698     public void testGetListUnknownWithDefault() {
699         final PropertiesConfiguration config = new PropertiesConfiguration();
700         final List<Integer> defValue = Arrays.asList(1, 2, 3);
701         assertEquals(defValue, config.getList(Integer.class, KEY_PREFIX, defValue));
702     }
703 
704     /**
705      * Tests getStringArray() for single son-string values.
706      */
707     @Test
708     public void testGetStringArrayNonString() {
709         checkGetStringArrayScalar(Integer.valueOf(42));
710         checkGetStringArrayScalar(Long.valueOf(42));
711         checkGetStringArrayScalar(Short.valueOf((short) 42));
712         checkGetStringArrayScalar(Byte.valueOf((byte) 42));
713         checkGetStringArrayScalar(Float.valueOf(42));
714         checkGetStringArrayScalar(Double.valueOf(42));
715         checkGetStringArrayScalar(Boolean.TRUE);
716     }
717 
718     /**
719      * Tests getStringArray() if the key cannot be found.
720      */
721     @Test
722     public void testGetStringArrayUnknown() {
723         final BaseConfiguration config = new BaseConfiguration();
724         final String[] array = config.getStringArray(KEY_PREFIX);
725         assertEquals(0, array.length);
726     }
727 
728     /**
729      * Tests get() for an unknown property if no default value is provided.
730      */
731     @Test
732     public void testGetUnknownNoDefaultValue() {
733         final PropertiesConfiguration config = new PropertiesConfiguration();
734         assertNull(config.get(Integer.class, KEY_PREFIX));
735     }
736 
737     /**
738      * Tests get() for an unknown property if a default value is provided.
739      */
740     @Test
741     public void testGetUnknownWithDefaultValue() {
742         final PropertiesConfiguration config = new PropertiesConfiguration();
743         final Integer defaultValue = 2121;
744         assertEquals(defaultValue, config.get(Integer.class, KEY_PREFIX, defaultValue));
745     }
746 
747     /**
748      * Tests get() for an unknown property if the throwExceptionOnMissing flag is set.
749      */
750     @Test
751     public void testGetUnknownWithThrowExceptionOnMissing() {
752         final PropertiesConfiguration config = new PropertiesConfiguration();
753         config.setThrowExceptionOnMissing(true);
754         assertThrows(NoSuchElementException.class, () -> config.get(Integer.class, KEY_PREFIX));
755     }
756 
757     /**
758      * Tests get() for an unknown property with a default value and the throwExceptionOnMissing flag. Because of the default
759      * value no exception should be thrown.
760      */
761     @Test
762     public void testGetUnownWithDefaultValueThrowExceptionOnMissing() {
763         final PropertiesConfiguration config = new PropertiesConfiguration();
764         config.setThrowExceptionOnMissing(true);
765         final Integer defaultValue = 2121;
766         assertEquals(defaultValue, config.get(Integer.class, KEY_PREFIX, defaultValue));
767     }
768 
769     /**
770      * Tests whether a new {@code ConfigurationInterpolator} can be installed without providing custom lookups.
771      */
772     @Test
773     public void testInstallInterpolatorNull() {
774         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
775         config.installInterpolator(null, null);
776         assertTrue(config.getInterpolator().getLookups().isEmpty());
777         final List<Lookup> defLookups = config.getInterpolator().getDefaultLookups();
778         assertEquals(1, defLookups.size());
779         assertInstanceOf(ConfigurationLookup.class, defLookups.get(0));
780     }
781 
782     /**
783      * Tests whether a property can reference an array using interpolation. This is related to CONFIGURATION-633.
784      */
785     @Test
786     public void testInterpolateArray() {
787         final PropertiesConfiguration config = new PropertiesConfiguration();
788         final String[] values = {"some", "test", "values"};
789         final String keyArray = "testArray";
790         config.addProperty(keyArray, values);
791         config.addProperty(KEY_PREFIX, "${" + keyArray + "}");
792 
793         assertArrayEquals(values, config.getStringArray(KEY_PREFIX));
794     }
795 
796     /**
797      * Tests whether environment variables can be interpolated.
798      */
799     @Test
800     public void testInterpolateEnvironmentVariables() {
801         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
802         InterpolationTestHelper.testInterpolationEnvironment(config);
803     }
804 
805     /**
806      * Tests escaping the variable marker, so that no interpolation will be performed.
807      */
808     @Test
809     public void testInterpolateEscape() {
810         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
811         config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
812         config.addProperty("mypath", "$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar\\,$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar");
813         assertEquals("${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar,${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar", config.getString("mypath"));
814     }
815 
816     /**
817      * Tests whether a property can reference a list using interpolation. This is related to CONFIGURATION-633.
818      */
819     @Test
820     public void testInterpolateList() {
821         final PropertiesConfiguration config = new PropertiesConfiguration();
822         final List<String> values = Arrays.asList("some", "test", "values");
823         final String keyList = "testList";
824         config.addProperty(keyList, values);
825         config.addProperty(KEY_PREFIX, "${" + keyList + "}");
826 
827         assertEquals(values, config.getList(String.class, KEY_PREFIX));
828     }
829 
830     /**
831      * Tests complex interpolation where the variables' values contain in turn other variables.
832      */
833     @Test
834     public void testInterpolateRecursive() {
835         final PropertiesConfiguration config = new PropertiesConfiguration();
836         config.addProperty("animal", "${animal_attr} fox");
837         config.addProperty("target", "${target_attr} dog");
838         config.addProperty("animal_attr", "quick brown");
839         config.addProperty("target_attr", "lazy");
840         config.addProperty(KEY_PREFIX, SUBST_TXT);
841         assertEquals("The quick brown fox jumps over the lazy dog.", config.getString(KEY_PREFIX));
842     }
843 
844     /**
845      * Tests the interpolation features.
846      */
847     @Test
848     public void testInterpolateString() {
849         final PropertiesConfiguration config = new PropertiesConfiguration();
850         config.addProperty("animal", "quick brown fox");
851         config.addProperty("target", "lazy dog");
852         config.addProperty(KEY_PREFIX, SUBST_TXT);
853         assertEquals("The quick brown fox jumps over the lazy dog.", config.getString(KEY_PREFIX));
854     }
855 
856     /**
857      * Tests that variables with list values in interpolated string are resolved with the first element
858      * in the list.
859      */
860     @Test
861     public void testInterpolateStringWithListVariable() {
862         final PropertiesConfiguration config = new PropertiesConfiguration();
863         final List<String> values = Arrays.asList("some", "test", "values");
864         final String keyList = "testList";
865         config.addProperty(keyList, values);
866         config.addProperty(KEY_PREFIX, "result = ${" + keyList + "}");
867 
868         assertEquals("result = some", config.getString(KEY_PREFIX));
869     }
870 
871     /**
872      * Tests interpolate() if the configuration does not have a {@code ConfigurationInterpolator}.
873      */
874     @Test
875     public void testInterpolationNoInterpolator() {
876         final PropertiesConfiguration config = new PropertiesConfiguration();
877         config.addProperty("animal", "quick brown fox");
878         config.addProperty("target", "lazy dog");
879         config.addProperty(KEY_PREFIX, SUBST_TXT);
880         config.setInterpolator(null);
881         assertEquals(SUBST_TXT, config.getString(KEY_PREFIX));
882     }
883 
884     /**
885      * Tests interpolation if a variable is unknown. Then the variable won't be substituted.
886      */
887     @Test
888     public void testInterpolationUnknownVariable() {
889         final PropertiesConfiguration config = new PropertiesConfiguration();
890         config.addProperty("animal", "quick brown fox");
891         config.addProperty(KEY_PREFIX, SUBST_TXT);
892         assertEquals("The quick brown fox jumps over the ${target}.", config.getString(KEY_PREFIX));
893     }
894 
895     /**
896      * Tests whether interpolation works in variable names.
897      */
898     @Test
899     public void testNestedVariableInterpolation() {
900         final BaseConfiguration config = new BaseConfiguration();
901         config.getInterpolator().setEnableSubstitutionInVariables(true);
902         config.addProperty("java.version", "1.4");
903         config.addProperty("jre-1.4", "C:\\java\\1.4");
904         config.addProperty("jre.path", "${jre-${java.version}}");
905         assertEquals("C:\\java\\1.4", config.getString("jre.path"));
906     }
907 
908     /**
909      * Tests whether the conversion handler can be changed.
910      */
911     @Test
912     public void testSetDefaultConversionHandler() {
913         final PropertiesConfiguration config = new PropertiesConfiguration();
914         final ConversionHandler handler = new DefaultConversionHandler();
915         config.setConversionHandler(handler);
916         assertSame(handler, config.getConversionHandler());
917     }
918 
919     /**
920      * Tries to set a null value for the conversion handler.
921      */
922     @Test
923     public void testSetDefaultConversionHandlerNull() {
924         final PropertiesConfiguration configuration = new PropertiesConfiguration();
925         assertThrows(IllegalArgumentException.class, () -> configuration.setConversionHandler(null));
926     }
927 
928     /**
929      * Tests whether default lookups can be added to an already existing {@code ConfigurationInterpolator}.
930      */
931     @Test
932     public void testSetDefaultLookupsExistingInterpolator() {
933         final Lookup look = mock(Lookup.class);
934         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
935         config.getInterpolator().addDefaultLookup(new ConfigurationLookup(new PropertiesConfiguration()));
936         config.setDefaultLookups(Collections.singleton(look));
937         final List<Lookup> lookups = config.getInterpolator().getDefaultLookups();
938         assertEquals(3, lookups.size());
939         assertSame(look, lookups.get(1));
940         assertInstanceOf(ConfigurationLookup.class, lookups.get(2));
941     }
942 
943     /**
944      * Tests whether default lookups can be added if not {@code ConfigurationInterpolator} exists yet.
945      */
946     @Test
947     public void testSetDefaultLookupsNoInterpolator() {
948         final Lookup look = mock(Lookup.class);
949         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
950         config.setInterpolator(null);
951         config.setDefaultLookups(Collections.singleton(look));
952         final List<Lookup> lookups = config.getInterpolator().getDefaultLookups();
953         assertEquals(2, lookups.size());
954         assertSame(look, lookups.get(0));
955         assertInstanceOf(ConfigurationLookup.class, lookups.get(1));
956     }
957 
958     /**
959      * Tries to set a null list delimiter handler.
960      */
961     @Test
962     public void testSetListDelimiterHandlerNull() {
963         final BaseConfiguration config = new BaseConfiguration();
964         assertThrows(IllegalArgumentException.class, () -> config.setListDelimiterHandler(null));
965     }
966 
967     /**
968      * Tests whether a parent {@code ConfigurationInterpolator} can be set if already a {@code ConfigurationInterpolator} is
969      * available.
970      */
971     @Test
972     public void testSetParentInterpolatorExistingInterpolator() {
973         final ConfigurationInterpolator parent = mock(ConfigurationInterpolator.class);
974         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
975         final ConfigurationInterpolator ci = config.getInterpolator();
976         config.setParentInterpolator(parent);
977         assertSame(parent, config.getInterpolator().getParentInterpolator());
978         assertSame(ci, config.getInterpolator());
979     }
980 
981     /**
982      * Tests whether a parent {@code ConfigurationInterpolator} can be set if currently no {@code ConfigurationInterpolator}
983      * is available.
984      */
985     @Test
986     public void testSetParentInterpolatorNoInterpolator() {
987         final ConfigurationInterpolator parent = mock(ConfigurationInterpolator.class);
988         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
989         config.setInterpolator(null);
990         config.setParentInterpolator(parent);
991         assertSame(parent, config.getInterpolator().getParentInterpolator());
992     }
993 
994     /**
995      * Tests whether prefix lookups can be added to an existing {@code ConfigurationInterpolator}.
996      */
997     @Test
998     public void testSetPrefixLookupsExistingInterpolator() {
999         final Lookup look = mock(Lookup.class);
1000         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
1001         final int count = config.getInterpolator().getLookups().size();
1002         final Map<String, Lookup> lookups = new HashMap<>();
1003         lookups.put("test", look);
1004         config.setPrefixLookups(lookups);
1005         final Map<String, Lookup> lookups2 = config.getInterpolator().getLookups();
1006         assertEquals(count + 1, lookups2.size());
1007         assertSame(look, lookups2.get("test"));
1008     }
1009 
1010     /**
1011      * Tests whether prefix lookups can be added if no {@code ConfigurationInterpolator} exists yet.
1012      */
1013     @Test
1014     public void testSetPrefixLookupsNoInterpolator() {
1015         final Lookup look = mock(Lookup.class);
1016         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
1017         config.setInterpolator(null);
1018         config.setPrefixLookups(Collections.singletonMap("test", look));
1019         final Map<String, Lookup> lookups = config.getInterpolator().getLookups();
1020         assertEquals(1, lookups.size());
1021         assertSame(look, lookups.get("test"));
1022     }
1023 
1024     /**
1025      * Tests the default implementation of sizeInternal().
1026      */
1027     @Test
1028     public void testSizeInternal() {
1029         final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
1030         for (int i = 0; i < PROP_COUNT; i++) {
1031             config.addProperty(KEY_PREFIX + i, "value" + i);
1032         }
1033         assertEquals(PROP_COUNT, config.size());
1034     }
1035 }