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  
18  package org.apache.commons.configuration2;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
23  import static org.junit.jupiter.api.Assertions.assertNull;
24  import static org.junit.jupiter.api.Assertions.assertSame;
25  import static org.junit.jupiter.api.Assertions.assertThrows;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  import static org.mockito.Mockito.mock;
28  
29  import java.math.BigDecimal;
30  import java.math.BigInteger;
31  import java.time.Duration;
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.Collection;
35  import java.util.HashMap;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.NoSuchElementException;
40  import java.util.Properties;
41  import java.util.StringTokenizer;
42  
43  import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
44  import org.apache.commons.configuration2.event.ConfigurationEvent;
45  import org.apache.commons.configuration2.event.EventListener;
46  import org.apache.commons.configuration2.event.EventListenerTestImpl;
47  import org.apache.commons.configuration2.ex.ConversionException;
48  import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
49  import org.apache.commons.configuration2.interpol.Lookup;
50  import org.junit.jupiter.api.BeforeEach;
51  import org.junit.jupiter.api.Test;
52  
53  /**
54   * Tests some basic functions of the BaseConfiguration class. Missing keys will throw Exceptions
55   */
56  public class TestBaseConfiguration {
57      /** Constant for the number key. */
58      static final String KEY_NUMBER = "number";
59  
60      protected static Class<?> missingElementException = NoSuchElementException.class;
61      protected static Class<?> incompatibleElementException = ConversionException.class;
62      protected BaseConfiguration config;
63  
64      @BeforeEach
65      public void setUp() throws Exception {
66          config = new BaseConfiguration();
67          config.setThrowExceptionOnMissing(true);
68          config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
69      }
70  
71      @Test
72      void testAddProperty() throws Exception {
73          Collection<Object> props = new ArrayList<>();
74          props.add("one");
75          props.add("two,three,four");
76          props.add(new String[] {"5.1", "5.2", "5.3,5.4", "5.5"});
77          props.add("six");
78          config.addProperty("complex.property", props);
79  
80          Object val = config.getProperty("complex.property");
81          Collection<?> col = assertInstanceOf(Collection.class, val);
82          assertEquals(10, col.size());
83  
84          props = new ArrayList<>();
85          props.add("quick");
86          props.add("brown");
87          props.add("fox,jumps");
88          final Object[] data = {"The", props, "over,the", "lazy", "dog."};
89          config.setProperty("complex.property", data);
90          val = config.getProperty("complex.property");
91          col = assertInstanceOf(Collection.class, val);
92          final Iterator<?> it = col.iterator();
93          final StringTokenizer tok = new StringTokenizer("The quick brown fox jumps over the lazy dog.", " ");
94          while (tok.hasMoreTokens()) {
95              assertTrue(it.hasNext());
96              assertEquals(tok.nextToken(), it.next());
97          }
98          assertFalse(it.hasNext());
99  
100         config.setProperty("complex.property", null);
101         assertFalse(config.containsKey("complex.property"));
102     }
103 
104     /**
105      * Tests cloning a BaseConfiguration.
106      */
107     @Test
108     void testClone() {
109         for (int i = 0; i < 10; i++) {
110             config.addProperty("key" + i, Integer.valueOf(i));
111         }
112         final BaseConfiguration config2 = (BaseConfiguration) config.clone();
113 
114         for (final Iterator<String> it = config.getKeys(); it.hasNext();) {
115             final String key = it.next();
116             assertTrue(config2.containsKey(key), "Key not found: " + key);
117             assertEquals(config.getProperty(key), config2.getProperty(key), "Wrong value for key " + key);
118         }
119     }
120 
121     /**
122      * Tests whether interpolation works as expected after cloning.
123      */
124     @Test
125     void testCloneInterpolation() {
126         final String keyAnswer = "answer";
127         config.addProperty(keyAnswer, "The answer is ${" + KEY_NUMBER + "}.");
128         config.addProperty(KEY_NUMBER, 42);
129         final BaseConfiguration clone = (BaseConfiguration) config.clone();
130         clone.setProperty(KEY_NUMBER, 43);
131         assertEquals("The answer is 42.", config.getString(keyAnswer));
132         assertEquals("The answer is 43.", clone.getString(keyAnswer));
133     }
134 
135     /**
136      * Tests the clone() method if a list property is involved.
137      */
138     @Test
139     void testCloneListProperty() {
140         final String key = "list";
141         config.addProperty(key, "value1");
142         config.addProperty(key, "value2");
143         final BaseConfiguration config2 = (BaseConfiguration) config.clone();
144         config2.addProperty(key, "value3");
145         assertEquals(2, config.getList(key).size());
146     }
147 
148     /**
149      * Tests whether a cloned configuration is decoupled from its original.
150      */
151     @Test
152     void testCloneModify() {
153         final EventListener<ConfigurationEvent> l = new EventListenerTestImpl(config);
154         config.addEventListener(ConfigurationEvent.ANY, l);
155         config.addProperty("original", Boolean.TRUE);
156         final BaseConfiguration config2 = (BaseConfiguration) config.clone();
157 
158         config2.addProperty("clone", Boolean.TRUE);
159         assertFalse(config.containsKey("clone"));
160         config2.setProperty("original", Boolean.FALSE);
161         assertTrue(config.getBoolean("original"));
162 
163         assertTrue(config2.getEventListeners(ConfigurationEvent.ANY).isEmpty());
164     }
165 
166     @Test
167     void testCommaSeparatedString() {
168         final String prop = "hey, that's a test";
169         config.setProperty("prop.string", prop);
170         final List<Object> list = config.getList("prop.string");
171         assertEquals(Arrays.asList("hey", "that's a test"), list);
172     }
173 
174     @Test
175     void testCommaSeparatedStringEscaped() {
176         final String prop2 = "hey\\, that's a test";
177         config.setProperty("prop.string", prop2);
178         assertEquals("hey, that's a test", config.getString("prop.string"));
179     }
180 
181     @Test
182     void testContainsValue() {
183         assertFalse(config.containsValue(null));
184     }
185 
186     @Test
187     void testGetBigDecimal() {
188         config.setProperty("numberBigD", "123.456");
189         final BigDecimal number = new BigDecimal("123.456");
190         final BigDecimal defaultValue = new BigDecimal("654.321");
191 
192         assertEquals(number, config.getBigDecimal("numberBigD"));
193         assertEquals(number, config.getBigDecimal("numberBigD", defaultValue));
194         assertEquals(defaultValue, config.getBigDecimal("numberNotInConfig", defaultValue));
195     }
196 
197     @Test
198     void testGetBigDecimalIncompatibleType() {
199         config.setProperty("test.empty", "");
200         assertThrows(ConversionException.class, () -> config.getBigDecimal("test.empty"));
201     }
202 
203     @Test
204     void testGetBigDecimalUnknown() {
205         assertThrows(NoSuchElementException.class, () -> config.getBigDecimal("numberNotInConfig"));
206     }
207 
208     @Test
209     void testGetBigInteger() {
210         config.setProperty("numberBigI", "1234567890");
211         final BigInteger number = new BigInteger("1234567890");
212         final BigInteger defaultValue = new BigInteger("654321");
213 
214         assertEquals(number, config.getBigInteger("numberBigI"));
215         assertEquals(number, config.getBigInteger("numberBigI", defaultValue));
216         assertEquals(defaultValue, config.getBigInteger("numberNotInConfig", defaultValue));
217     }
218 
219     @Test
220     void testGetBigIntegerIncompatibleType() {
221         config.setProperty("test.empty", "");
222         assertThrows(ConversionException.class, () -> config.getBigInteger("test.empty"));
223     }
224 
225     @Test
226     void testGetBigIntegerUnknown() {
227         assertThrows(NoSuchElementException.class, () -> config.getBigInteger("numberNotInConfig"));
228     }
229 
230     @Test
231     void testGetBinaryValue() {
232         config.setProperty("number", "0b11111111");
233         assertEquals((byte) 0xFF, config.getByte("number"));
234 
235         config.setProperty("number", "0b1111111111111111");
236         assertEquals((short) 0xFFFF, config.getShort("number"));
237 
238         config.setProperty("number", "0b11111111111111111111111111111111");
239         assertEquals(0xFFFFFFFF, config.getInt("number"));
240 
241         config.setProperty("number", "0b1111111111111111111111111111111111111111111111111111111111111111");
242         assertEquals(0xFFFFFFFFFFFFFFFFL, config.getLong("number"));
243 
244         assertEquals(0xFFFFFFFFFFFFFFFFL, config.getBigInteger("number").longValue());
245     }
246 
247     @Test
248     void testGetBoolean() {
249         config.setProperty("boolA", Boolean.TRUE);
250         final boolean boolT = true;
251         final boolean boolF = false;
252         assertEquals(boolT, config.getBoolean("boolA"));
253         assertEquals(boolT, config.getBoolean("boolA", boolF));
254         assertEquals(boolF, config.getBoolean("boolNotInConfig", boolF));
255         assertEquals(Boolean.valueOf(boolT), config.getBoolean("boolA", Boolean.valueOf(boolF)));
256     }
257 
258     @Test
259     void testGetBooleanIncompatibleType() {
260         config.setProperty("test.empty", "");
261         assertThrows(ConversionException.class, () -> config.getBoolean("test.empty"));
262     }
263 
264     @Test
265     void testGetBooleanUnknown() {
266         assertThrows(NoSuchElementException.class, () -> config.getBoolean("numberNotInConfig"));
267     }
268 
269     @Test
270     void testGetByte() {
271         config.setProperty("number", "1");
272         final byte oneB = 1;
273         final byte twoB = 2;
274         assertEquals(oneB, config.getByte("number"));
275         assertEquals(oneB, config.getByte("number", twoB));
276         assertEquals(twoB, config.getByte("numberNotInConfig", twoB));
277         assertEquals(Byte.valueOf(oneB), config.getByte("number", Byte.valueOf("2")));
278     }
279 
280     @Test
281     void testGetByteIncompatibleType() {
282         config.setProperty("test.empty", "");
283         assertThrows(ConversionException.class, () -> config.getByte("test.empty"));
284     }
285 
286     @Test
287     void testGetByteUnknown() {
288         assertThrows(NoSuchElementException.class, () -> config.getByte("numberNotInConfig"));
289     }
290 
291     @Test
292     void testGetDouble() {
293         config.setProperty("numberD", "1.0");
294         final double oneD = 1;
295         final double twoD = 2;
296         assertEquals(oneD, config.getDouble("numberD"), 0);
297         assertEquals(oneD, config.getDouble("numberD", twoD), 0);
298         assertEquals(twoD, config.getDouble("numberNotInConfig", twoD), 0);
299         assertEquals(Double.valueOf(oneD), config.getDouble("numberD", Double.valueOf("2")));
300     }
301 
302     @Test
303     void testGetDoubleIncompatibleType() {
304         config.setProperty("test.empty", "");
305         assertThrows(ConversionException.class, () -> config.getDouble("test.empty"));
306     }
307 
308     @Test
309     void testGetDoubleUnknown() {
310         assertThrows(NoSuchElementException.class, () -> config.getDouble("numberNotInConfig"));
311     }
312 
313     @Test
314     void testGetDuration() {
315         final Duration d = Duration.ofSeconds(1);
316         config.setProperty("durationD", d.toString());
317         final Duration oneD = Duration.ofSeconds(1);
318         final Duration twoD = Duration.ofSeconds(2);
319         assertEquals(oneD, config.getDuration("durationD"));
320         assertEquals(oneD, config.getDuration("durationD", twoD));
321         assertEquals(twoD, config.getDuration("numberNotInConfig", twoD));
322         assertEquals(oneD, config.getDuration("durationD", twoD));
323     }
324 
325     @Test
326     void testGetDurationIncompatibleType() {
327         config.setProperty("test.empty", "");
328         assertThrows(ConversionException.class, () -> config.getDuration("test.empty"));
329     }
330 
331     @Test
332     void testGetDurationUnknown() {
333         assertThrows(NoSuchElementException.class, () -> config.getDuration("numberNotInConfig"));
334     }
335 
336     @Test
337     void testGetEnum() {
338         config.setProperty("testEnum", EnumFixture.SMALLTALK.name());
339         config.setProperty("testBadEnum", "This is not an enum value.");
340         final EnumFixture enum1 = EnumFixture.SMALLTALK;
341         final EnumFixture defaultValue = EnumFixture.JAVA;
342         //
343         assertEquals(enum1, config.getEnum("testEnum", EnumFixture.class));
344         assertEquals(enum1, config.getEnum("testEnum", EnumFixture.class, defaultValue));
345         assertEquals(defaultValue, config.getEnum("stringNotInConfig", EnumFixture.class, defaultValue));
346         //
347         assertThrows(ConversionException.class, () -> config.getEnum("testBadEnum", EnumFixture.class));
348         //
349         assertThrows(ConversionException.class, () -> config.getEnum("testBadEnum", EnumFixture.class, defaultValue));
350     }
351 
352     @Test
353     void testGetFloat() {
354         config.setProperty("numberF", "1.0");
355         final float oneF = 1;
356         final float twoF = 2;
357         assertEquals(oneF, config.getFloat("numberF"), 0);
358         assertEquals(oneF, config.getFloat("numberF", twoF), 0);
359         assertEquals(twoF, config.getFloat("numberNotInConfig", twoF), 0);
360         assertEquals(Float.valueOf(oneF), config.getFloat("numberF", Float.valueOf("2")));
361     }
362 
363     @Test
364     void testGetFloatIncompatibleType() {
365         config.setProperty("test.empty", "");
366         assertThrows(ConversionException.class, () -> config.getFloat("test.empty"));
367     }
368 
369     @Test
370     void testGetFloatUnknown() {
371         assertThrows(NoSuchElementException.class, () -> config.getFloat("numberNotInConfig"));
372     }
373 
374     @Test
375     void testGetHexadecimalValue() {
376         config.setProperty("number", "0xFF");
377         assertEquals((byte) 0xFF, config.getByte("number"));
378 
379         config.setProperty("number", "0xFFFF");
380         assertEquals((short) 0xFFFF, config.getShort("number"));
381 
382         config.setProperty("number", "0xFFFFFFFF");
383         assertEquals(0xFFFFFFFF, config.getInt("number"));
384 
385         config.setProperty("number", "0xFFFFFFFFFFFFFFFF");
386         assertEquals(0xFFFFFFFFFFFFFFFFL, config.getLong("number"));
387 
388         assertEquals(0xFFFFFFFFFFFFFFFFL, config.getBigInteger("number").longValue());
389     }
390 
391     @Test
392     void testGetInterpolatedList() {
393         config.addProperty("number", "1");
394         config.addProperty("array", "${number}");
395         config.addProperty("array", "${number}");
396 
397         final List<String> list = new ArrayList<>();
398         list.add("1");
399         list.add("1");
400 
401         assertEquals(list, config.getList("array"));
402     }
403 
404     @Test
405     void testGetInterpolatedPrimitives() {
406         config.addProperty("number", "1");
407         config.addProperty("value", "${number}");
408 
409         config.addProperty("boolean", "true");
410         config.addProperty("booleanValue", "${boolean}");
411 
412         // primitive types
413         assertTrue(config.getBoolean("booleanValue"));
414         assertEquals(1, config.getByte("value"));
415         assertEquals(1, config.getShort("value"));
416         assertEquals(1, config.getInt("value"));
417         assertEquals(1, config.getLong("value"));
418         assertEquals(1, config.getFloat("value"), 0);
419         assertEquals(1, config.getDouble("value"), 0);
420 
421         // primitive wrappers
422         assertEquals(Boolean.TRUE, config.getBoolean("booleanValue", null));
423         assertEquals(Byte.valueOf("1"), config.getByte("value", null));
424         assertEquals(Short.valueOf("1"), config.getShort("value", null));
425         assertEquals(Integer.valueOf("1"), config.getInteger("value", null));
426         assertEquals(Long.valueOf("1"), config.getLong("value", null));
427         assertEquals(Float.valueOf("1"), config.getFloat("value", null));
428         assertEquals(Double.valueOf("1"), config.getDouble("value", null));
429 
430         assertEquals(new BigInteger("1"), config.getBigInteger("value", null));
431         assertEquals(new BigDecimal("1"), config.getBigDecimal("value", null));
432     }
433 
434     /**
435      * Tests accessing and manipulating the interpolator object.
436      */
437     @Test
438     void testGetInterpolator() {
439         InterpolationTestHelper.testGetInterpolator(config);
440     }
441 
442     @Test
443     void testGetList() {
444         config.addProperty("number", "1");
445         config.addProperty("number", "2");
446         final List<Object> list = config.getList("number");
447         assertEquals(Arrays.asList("1", "2"), list);
448     }
449 
450     @Test
451     void testGetLong() {
452         config.setProperty("numberL", "1");
453         final long oneL = 1;
454         final long twoL = 2;
455         assertEquals(oneL, config.getLong("numberL"));
456         assertEquals(oneL, config.getLong("numberL", twoL));
457         assertEquals(twoL, config.getLong("numberNotInConfig", twoL));
458         assertEquals(Long.valueOf(oneL), config.getLong("numberL", Long.valueOf("2")));
459     }
460 
461     @Test
462     void testGetLongIncompatibleTypes() {
463         config.setProperty("test.empty", "");
464         assertThrows(ConversionException.class, () -> config.getLong("test.empty"));
465     }
466 
467     @Test
468     void testGetLongUnknown() {
469         assertThrows(NoSuchElementException.class, () -> config.getLong("numberNotInConfig"));
470     }
471 
472     @Test
473     void testGetProperty() {
474         /* should be empty and return null */
475         assertNull(config.getProperty("foo"));
476 
477         /* add a real value, and get it two different ways */
478         config.setProperty("number", "1");
479         assertEquals("1", config.getProperty("number"));
480         assertEquals("1", config.getString("number"));
481     }
482 
483     @Test
484     void testGetShort() {
485         config.setProperty("numberS", "1");
486         final short oneS = 1;
487         final short twoS = 2;
488         assertEquals(oneS, config.getShort("numberS"));
489         assertEquals(oneS, config.getShort("numberS", twoS));
490         assertEquals(twoS, config.getShort("numberNotInConfig", twoS));
491         assertEquals(Short.valueOf(oneS), config.getShort("numberS", Short.valueOf("2")));
492     }
493 
494     @Test
495     void testGetShortIncompatibleType() {
496         config.setProperty("test.empty", "");
497         assertThrows(ConversionException.class, () -> config.getShort("test.empty"));
498     }
499 
500     @Test
501     void testGetShortUnknown() {
502         assertThrows(NoSuchElementException.class, () -> config.getShort("numberNotInConfig"));
503     }
504 
505     @Test
506     void testGetString() {
507         config.setProperty("testString", "The quick brown fox");
508         final String string = "The quick brown fox";
509         final String defaultValue = "jumps over the lazy dog";
510 
511         assertEquals(string, config.getString("testString"));
512         assertEquals(string, config.getString("testString", defaultValue));
513         assertEquals(defaultValue, config.getString("stringNotInConfig", defaultValue));
514     }
515 
516     /**
517      * Tests that the first scalar of a list is returned.
518      */
519     @Test
520     void testGetStringForListValue() {
521         config.addProperty("number", "1");
522         config.addProperty("number", "2");
523         assertEquals("1", config.getString("number"));
524     }
525 
526     @Test
527     void testGetStringUnknown() {
528         assertThrows(NoSuchElementException.class, () -> config.getString("stringNotInConfig"));
529     }
530 
531     /**
532      * Tests whether a {@code ConfigurationInterpolator} can be created and installed.
533      */
534     @Test
535     void testInstallInterpolator() {
536         final Lookup prefixLookup = mock(Lookup.class);
537         final Lookup defLookup = mock(Lookup.class);
538         final Map<String, Lookup> prefixLookups = new HashMap<>();
539         prefixLookups.put("test", prefixLookup);
540         final List<Lookup> defLookups = new ArrayList<>();
541         defLookups.add(defLookup);
542         config.installInterpolator(prefixLookups, defLookups);
543         final ConfigurationInterpolator interpolator = config.getInterpolator();
544         assertEquals(prefixLookups, interpolator.getLookups());
545         final List<Lookup> defLookups2 = interpolator.getDefaultLookups();
546         assertEquals(2, defLookups2.size());
547         assertSame(defLookup, defLookups2.get(0));
548         final String var = "testVariable";
549         final Object value = 42;
550         config.addProperty(var, value);
551         assertEquals(value, defLookups2.get(1).lookup(var));
552     }
553 
554     /**
555      * Tests obtaining a configuration with all variables replaced by their actual values.
556      */
557     @Test
558     void testInterpolatedConfiguration() {
559         InterpolationTestHelper.testInterpolatedConfiguration(config);
560     }
561 
562     @Test
563     void testInterpolation() {
564         InterpolationTestHelper.testInterpolation(config);
565     }
566 
567     /**
568      * Tests interpolation of constant values.
569      */
570     @Test
571     void testInterpolationConstants() {
572         InterpolationTestHelper.testInterpolationConstants(config);
573     }
574 
575     /**
576      * Tests interpolation of environment properties.
577      */
578     @Test
579     void testInterpolationEnvironment() {
580         InterpolationTestHelper.testInterpolationEnvironment(config);
581     }
582 
583     /**
584      * Tests whether a variable can be escaped, so that it won't be interpolated.
585      */
586     @Test
587     void testInterpolationEscaped() {
588         InterpolationTestHelper.testInterpolationEscaped(config);
589     }
590 
591     /**
592      * Tests interpolation with localhost values.
593      */
594     @Test
595     void testInterpolationLocalhost() {
596         InterpolationTestHelper.testInterpolationLocalhost(config);
597     }
598 
599     @Test
600     void testInterpolationLoop() {
601         InterpolationTestHelper.testInterpolationLoop(config);
602     }
603 
604     /**
605      * Tests interpolation when a subset configuration is involved.
606      */
607     @Test
608     void testInterpolationSubset() {
609         InterpolationTestHelper.testInterpolationSubset(config);
610     }
611 
612     /**
613      * Tests interpolation of system properties.
614      */
615     @Test
616     void testInterpolationSystemProperties() {
617         InterpolationTestHelper.testInterpolationSystemProperties(config);
618     }
619 
620     /**
621      * Tests interpolation when the referred property is not found.
622      */
623     @Test
624     void testInterpolationUnknownProperty() {
625         InterpolationTestHelper.testInterpolationUnknownProperty(config);
626     }
627 
628     @Test
629     void testMultipleInterpolation() {
630         InterpolationTestHelper.testMultipleInterpolation(config);
631     }
632 
633     /**
634      * Tests whether property access is possible without a {@code ConfigurationInterpolator}.
635      */
636     @Test
637     void testNoInterpolator() {
638         config.setProperty("test", "${value}");
639         config.setInterpolator(null);
640         assertEquals("${value}", config.getString("test"));
641     }
642 
643     /**
644      * Tests if conversion between number types is possible.
645      */
646     @Test
647     void testNumberConversions() {
648         config.setProperty(KEY_NUMBER, Integer.valueOf(42));
649         assertEquals(42, config.getInt(KEY_NUMBER));
650         assertEquals(42L, config.getLong(KEY_NUMBER));
651         assertEquals((byte) 42, config.getByte(KEY_NUMBER));
652         assertEquals(42.0f, config.getFloat(KEY_NUMBER), 0.01f);
653         assertEquals(42.0, config.getDouble(KEY_NUMBER), 0.001);
654 
655         assertEquals(Long.valueOf(42L), config.getLong(KEY_NUMBER, null));
656         assertEquals(new BigInteger("42"), config.getBigInteger(KEY_NUMBER));
657         assertEquals(new BigDecimal("42.0"), config.getBigDecimal(KEY_NUMBER));
658     }
659 
660     @Test
661     void testPropertyAccess() {
662         config.clearProperty("prop.properties");
663         config.setProperty("prop.properties", "");
664         assertEquals(new Properties(), config.getProperties("prop.properties"));
665         config.clearProperty("prop.properties");
666         config.setProperty("prop.properties", "foo=bar, baz=moo, seal=clubber");
667 
668         final Properties p = new Properties();
669         p.setProperty("foo", "bar");
670         p.setProperty("baz", "moo");
671         p.setProperty("seal", "clubber");
672         assertEquals(p, config.getProperties("prop.properties"));
673     }
674 
675     /**
676      * Tests whether a {@code ConfigurationInterpolator} can be set.
677      */
678     @Test
679     void testSetInterpolator() {
680         final ConfigurationInterpolator interpolator = mock(ConfigurationInterpolator.class);
681         config.setInterpolator(interpolator);
682         assertSame(interpolator, config.getInterpolator());
683     }
684 
685     /**
686      * Tests the specific size() implementation.
687      */
688     @Test
689     void testSize() {
690         final int count = 16;
691         for (int i = 0; i < count; i++) {
692             config.addProperty("key" + i, "value" + i);
693         }
694         assertEquals(count, config.size());
695     }
696 
697     @Test
698     void testSubset() {
699         /*
700          * test subset : assure we don't reprocess the data elements when generating the subset
701          */
702 
703         final String prop = "hey, that's a test";
704         final String prop2 = "hey\\, that's a test";
705         config.setProperty("prop.string", prop2);
706         config.setProperty("property.string", "hello");
707 
708         Configuration subEprop = config.subset("prop");
709 
710         assertEquals(prop, subEprop.getString("string"));
711         assertEquals(1, subEprop.getList("string").size());
712 
713         Iterator<String> it = subEprop.getKeys();
714         it.next();
715         assertFalse(it.hasNext());
716 
717         subEprop = config.subset("prop.");
718         it = subEprop.getKeys();
719         assertFalse(it.hasNext());
720     }
721 
722     @Test
723     void testThrowExceptionOnMissing() {
724         assertTrue(config.isThrowExceptionOnMissing());
725     }
726 }