001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.configuration;
019    
020    import static org.junit.Assert.assertEquals;
021    import static org.junit.Assert.assertFalse;
022    import static org.junit.Assert.assertNotNull;
023    import static org.junit.Assert.assertNotSame;
024    import static org.junit.Assert.assertNull;
025    import static org.junit.Assert.assertSame;
026    import static org.junit.Assert.assertTrue;
027    import static org.junit.Assert.fail;
028    
029    import java.io.File;
030    import java.io.FileWriter;
031    import java.io.IOException;
032    import java.io.PrintWriter;
033    import java.util.ArrayList;
034    import java.util.Collection;
035    import java.util.Iterator;
036    import java.util.List;
037    import java.util.NoSuchElementException;
038    
039    import org.apache.commons.configuration.event.ConfigurationEvent;
040    import org.apache.commons.configuration.event.ConfigurationListener;
041    import org.apache.commons.configuration.reloading.FileAlwaysReloadingStrategy;
042    import org.easymock.EasyMock;
043    import org.junit.Before;
044    import org.junit.Test;
045    
046    /**
047     * Test loading multiple configurations.
048     *
049     * @version $Id: TestCompositeConfiguration.java 1233058 2012-01-18 20:49:12Z oheger $
050     */
051    public class TestCompositeConfiguration
052    {
053        /** Constant for a test property to be checked.*/
054        private static final String TEST_PROPERTY = "test.source.property";
055    
056        protected PropertiesConfiguration conf1;
057        protected PropertiesConfiguration conf2;
058        protected XMLConfiguration xmlConf;
059        protected CompositeConfiguration cc;
060    
061        /**
062         * The File that we test with
063         */
064        private String testProperties = ConfigurationAssert.getTestFile("test.properties").getAbsolutePath();
065        private String testProperties2 = ConfigurationAssert.getTestFile("test2.properties").getAbsolutePath();
066        private String testPropertiesXML = ConfigurationAssert.getTestFile("test.xml").getAbsolutePath();
067    
068        @Before
069        public void setUp() throws Exception
070        {
071            cc = new CompositeConfiguration();
072            conf1 = new PropertiesConfiguration(testProperties);
073            conf2 = new PropertiesConfiguration(testProperties2);
074            xmlConf = new XMLConfiguration(new File(testPropertiesXML));
075    
076            cc.setThrowExceptionOnMissing(true);
077        }
078    
079        @Test
080        public void testThrowExceptionOnMissing()
081        {
082            assertTrue("Throw Exception Property is not set!", cc.isThrowExceptionOnMissing());
083        }
084    
085        @Test
086        public void testAddRemoveConfigurations() throws Exception
087        {
088            cc.addConfiguration(conf1);
089            assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
090            cc.addConfiguration(conf1);
091            assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
092            cc.addConfiguration(conf2);
093            assertEquals("Number of configurations", 3, cc.getNumberOfConfigurations());
094            cc.removeConfiguration(conf1);
095            assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
096            cc.clear();
097            assertEquals("Number of configurations", 1, cc.getNumberOfConfigurations());
098        }
099    
100        @Test
101        public void testGetPropertyWIncludes() throws Exception
102        {
103            cc.addConfiguration(conf1);
104            cc.addConfiguration(conf2);
105            List<Object> l = cc.getList("packages");
106            assertTrue(l.contains("packagea"));
107        }
108    
109        @Test
110        public void testGetProperty() throws Exception
111        {
112            cc.addConfiguration(conf1);
113            cc.addConfiguration(conf2);
114            assertEquals("Make sure we get the property from conf1 first", "test.properties", cc.getString("propertyInOrder"));
115            cc.clear();
116    
117            cc.addConfiguration(conf2);
118            cc.addConfiguration(conf1);
119            assertEquals("Make sure we get the property from conf2 first", "test2.properties", cc.getString("propertyInOrder"));
120        }
121    
122        @Test
123        public void testCantRemoveMemoryConfig() throws Exception
124        {
125            cc.clear();
126            assertEquals(1, cc.getNumberOfConfigurations());
127    
128            Configuration internal = cc.getConfiguration(0);
129            cc.removeConfiguration(internal);
130    
131            assertEquals(1, cc.getNumberOfConfigurations());
132        }
133    
134        @Test
135        public void testGetPropertyMissing() throws Exception
136        {
137            cc.addConfiguration(conf1);
138            cc.addConfiguration(conf2);
139            try
140            {
141                assertNull(cc.getString("bogus.property"));
142                fail("Should have thrown a NoSuchElementException");
143            }
144            catch (NoSuchElementException nsee)
145            {
146                assertTrue(nsee.getMessage().indexOf("bogus.property") > -1);
147            }
148    
149            assertTrue("Should be false", !cc.getBoolean("test.missing.boolean", false));
150            assertTrue("Should be true", cc.getBoolean("test.missing.boolean.true", true));
151        }
152    
153        /**
154         * Tests {@code List} parsing.
155         */
156        @Test
157        public void testMultipleTypesOfConfigs() throws Exception
158        {
159            cc.addConfiguration(conf1);
160            cc.addConfiguration(xmlConf);
161            assertEquals("Make sure we get the property from conf1 first", 1, cc.getInt("test.short"));
162            cc.clear();
163    
164            cc.addConfiguration(xmlConf);
165            cc.addConfiguration(conf1);
166            assertEquals("Make sure we get the property from xml", 8, cc.getInt("test.short"));
167        }
168    
169        @Test
170        public void testPropertyExistsInOnlyOneConfig() throws Exception
171        {
172            cc.addConfiguration(conf1);
173            cc.addConfiguration(xmlConf);
174            assertEquals("value", cc.getString("element"));
175        }
176    
177        /**
178         * Tests getting a default when the key doesn't exist
179         */
180        @Test
181        public void testDefaultValueWhenKeyMissing() throws Exception
182        {
183            cc.addConfiguration(conf1);
184            cc.addConfiguration(xmlConf);
185            assertEquals("default", cc.getString("bogus", "default"));
186            assertTrue(1.4 == cc.getDouble("bogus", 1.4));
187            assertTrue(1.4 == cc.getDouble("bogus", 1.4));
188        }
189    
190        @Test
191        public void testGettingConfiguration() throws Exception
192        {
193            cc.addConfiguration(conf1);
194            cc.addConfiguration(xmlConf);
195            assertEquals(PropertiesConfiguration.class, cc.getConfiguration(0).getClass());
196            assertEquals(XMLConfiguration.class, cc.getConfiguration(1).getClass());
197        }
198    
199        /**
200         * Tests setting values.  These are set in memory mode only!
201         */
202        @Test
203        public void testClearingProperty() throws Exception
204        {
205            cc.addConfiguration(conf1);
206            cc.addConfiguration(xmlConf);
207            cc.clearProperty("test.short");
208            assertTrue("Make sure test.short is gone!", !cc.containsKey("test.short"));
209        }
210    
211        /**
212         * Tests adding values.  Make sure they _DON'T_ override any other properties but add to the
213         * existing properties  and keep sequence
214         */
215        @Test
216        public void testAddingProperty() throws Exception
217        {
218            cc.addConfiguration(conf1);
219            cc.addConfiguration(xmlConf);
220    
221            String[] values = cc.getStringArray("test.short");
222    
223            assertEquals("Number of values before add is wrong!", 1, values.length);
224            assertEquals("First Value before add is wrong", "1", values[0]);
225    
226            cc.addProperty("test.short", "88");
227    
228            values = cc.getStringArray("test.short");
229    
230            assertEquals("Number of values is wrong!", 2, values.length);
231            assertEquals("First Value is wrong", "1", values[0]);
232            assertEquals("Third Value is wrong", "88", values[1]);
233        }
234    
235        /**
236         * Tests setting values.  These are set in memory mode only!
237         */
238        @Test
239        public void testSettingMissingProperty() throws Exception
240        {
241            cc.addConfiguration(conf1);
242            cc.addConfiguration(xmlConf);
243            cc.setProperty("my.new.property", "supernew");
244            assertEquals("supernew", cc.getString("my.new.property"));
245        }
246    
247        /**
248         * Tests retrieving subsets of configurations
249         */
250        @Test
251        public void testGettingSubset() throws Exception
252        {
253            cc.addConfiguration(conf1);
254            cc.addConfiguration(xmlConf);
255    
256            Configuration subset = cc.subset("test");
257            assertNotNull(subset);
258            assertFalse("Shouldn't be empty", subset.isEmpty());
259            assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "1", subset.getString("short"));
260    
261            cc.setProperty("test.short", "43");
262            subset = cc.subset("test");
263            assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "43", subset.getString("short"));
264        }
265    
266        /**
267         * Tests subsets and still can resolve elements
268         */
269        @Test
270        public void testSubsetCanResolve() throws Exception
271        {
272            cc = new CompositeConfiguration();
273            final BaseConfiguration config = new BaseConfiguration();
274            config.addProperty("subset.tempfile", "${java.io.tmpdir}/file.tmp");
275            cc.addConfiguration(config);
276            cc.addConfiguration(ConfigurationConverter.getConfiguration(System.getProperties()));
277    
278            Configuration subset = cc.subset("subset");
279            assertEquals(System.getProperty("java.io.tmpdir") + "/file.tmp", subset.getString("tempfile"));
280        }
281    
282        /**
283         * Tests {@code List} parsing.
284         */
285        @Test
286        public void testList() throws Exception
287        {
288            cc.addConfiguration(conf1);
289            cc.addConfiguration(xmlConf);
290    
291            List<Object> packages = cc.getList("packages");
292            // we should get 3 packages here
293            assertEquals(3, packages.size());
294    
295            List<Object> defaultList = new ArrayList<Object>();
296            defaultList.add("1");
297            defaultList.add("2");
298    
299            packages = cc.getList("packages.which.dont.exist", defaultList);
300            // we should get 2 packages here
301            assertEquals(2, packages.size());
302    
303        }
304    
305        /**
306         * Tests {@code String} array parsing.
307         */
308        @Test
309        public void testStringArray() throws Exception
310        {
311            cc.addConfiguration(conf1);
312            cc.addConfiguration(xmlConf);
313    
314            String[] packages = cc.getStringArray("packages");
315            // we should get 3 packages here
316            assertEquals(3, packages.length);
317    
318            packages = cc.getStringArray("packages.which.dont.exist");
319            // we should get 0 packages here
320            assertEquals(0, packages.length);
321        }
322    
323        @Test
324        public void testGetList()
325        {
326            Configuration conf1 = new BaseConfiguration();
327            conf1.addProperty("array", "value1");
328            conf1.addProperty("array", "value2");
329    
330            Configuration conf2 = new BaseConfiguration();
331            conf2.addProperty("array", "value3");
332            conf2.addProperty("array", "value4");
333    
334            cc.addConfiguration(conf1);
335            cc.addConfiguration(conf2);
336    
337            // check the composite 'array' property
338            List<Object> list = cc.getList("array");
339            assertNotNull("null list", list);
340            assertEquals("list size", 2, list.size());
341            assertTrue("'value1' not found in the list", list.contains("value1"));
342            assertTrue("'value2' not found in the list", list.contains("value2"));
343    
344            // add an element to the list in the composite configuration
345            cc.addProperty("array", "value5");
346    
347            // test the new list
348            list = cc.getList("array");
349            assertNotNull("null list", list);
350            assertEquals("list size", 3, list.size());
351            assertTrue("'value1' not found in the list", list.contains("value1"));
352            assertTrue("'value2' not found in the list", list.contains("value2"));
353            assertTrue("'value5' not found in the list", list.contains("value5"));
354        }
355    
356        /**
357         * Tests {@code getKeys} preserves the order
358         */
359        @Test
360        public void testGetKeysPreservesOrder() throws Exception
361        {
362            cc.addConfiguration(conf1);
363            List<String> orderedList = new ArrayList<String>();
364            for (Iterator<String> keys = conf1.getKeys(); keys.hasNext();)
365            {
366                orderedList.add(keys.next());
367            }
368            List<String> iteratedList = new ArrayList<String>();
369            for (Iterator<String> keys = cc.getKeys(); keys.hasNext();)
370            {
371                iteratedList.add(keys.next());
372            }
373            assertEquals(orderedList.size(), iteratedList.size());
374            for (int i = 0; i < orderedList.size(); i++)
375            {
376                assertEquals(orderedList.get(i), iteratedList.get(i));
377            }
378        }
379    
380        /**
381         * Tests {@code getKeys(String key)} preserves the order
382         */
383        @Test
384        public void testGetKeys2PreservesOrder() throws Exception
385        {
386            cc.addConfiguration(conf1);
387            List<String> orderedList = new ArrayList<String>();
388            for (Iterator<String> keys = conf1.getKeys("test"); keys.hasNext();)
389            {
390                orderedList.add(keys.next());
391            }
392            List<String> iteratedList = new ArrayList<String>();
393            for (Iterator<String> keys = cc.getKeys("test"); keys.hasNext();)
394            {
395                iteratedList.add(keys.next());
396            }
397            assertEquals(orderedList.size(), iteratedList.size());
398            for (int i = 0; i < orderedList.size(); i++)
399            {
400                assertEquals(orderedList.get(i), iteratedList.get(i));
401            }
402        }
403    
404        @Test
405        public void testGetStringWithDefaults()
406        {
407            BaseConfiguration defaults = new BaseConfiguration();
408            defaults.addProperty("default", "default string");
409    
410            CompositeConfiguration c = new CompositeConfiguration(defaults);
411            c.setThrowExceptionOnMissing(cc.isThrowExceptionOnMissing());
412            c.addProperty("string", "test string");
413    
414            assertEquals("test string", c.getString("string"));
415            try
416            {
417                c.getString("XXX");
418                fail("Should throw NoSuchElementException exception");
419            }
420            catch (NoSuchElementException e)
421            {
422                //ok
423            }
424            catch (Exception e)
425            {
426                fail("Should throw NoSuchElementException exception, not " + e);
427            }
428    
429            //test defaults
430            assertEquals("test string", c.getString("string", "some default value"));
431            assertEquals("default string", c.getString("default"));
432            assertEquals("default string", c.getString("default", "some default value"));
433            assertEquals("some default value", c.getString("XXX", "some default value"));
434        }
435    
436        @Test
437        public void testCheckingInMemoryConfiguration() throws Exception
438        {
439            String TEST_KEY = "testKey";
440            Configuration defaults = new PropertiesConfiguration();
441            defaults.setProperty(TEST_KEY, "testValue");
442            Configuration testConfiguration = new CompositeConfiguration(defaults);
443            assertTrue(testConfiguration.containsKey(TEST_KEY));
444            assertFalse(testConfiguration.isEmpty());
445            boolean foundTestKey = false;
446            Iterator<String> i = testConfiguration.getKeys();
447            //assertTrue(i instanceof IteratorChain);
448            //IteratorChain ic = (IteratorChain)i;
449            //assertEquals(2,i.size());
450            for (; i.hasNext();)
451            {
452                String key = i.next();
453                if (key.equals(TEST_KEY))
454                {
455                    foundTestKey = true;
456                }
457            }
458            assertTrue(foundTestKey);
459            testConfiguration.clearProperty(TEST_KEY);
460            assertFalse(testConfiguration.containsKey(TEST_KEY));
461        }
462    
463        @Test
464        public void testStringArrayInterpolation()
465        {
466            CompositeConfiguration config = new CompositeConfiguration();
467            config.addProperty("base", "foo");
468            config.addProperty("list", "${base}.bar1");
469            config.addProperty("list", "${base}.bar2");
470            config.addProperty("list", "${base}.bar3");
471    
472            String[] array = config.getStringArray("list");
473            assertEquals("size", 3, array.length);
474            assertEquals("1st element", "foo.bar1", array[0]);
475            assertEquals("2nd element", "foo.bar2", array[1]);
476            assertEquals("3rd element", "foo.bar3", array[2]);
477        }
478    
479        /**
480         * Tests whether global interpolation works with lists.
481         */
482        @Test
483        public void testListInterpolation()
484        {
485            PropertiesConfiguration c1 = new PropertiesConfiguration();
486            c1.addProperty("c1.value", "test1");
487            c1.addProperty("c1.value", "${c2.value}");
488            cc.addConfiguration(c1);
489            PropertiesConfiguration c2 = new PropertiesConfiguration();
490            c2.addProperty("c2.value", "test2");
491            cc.addConfiguration(c2);
492            List<Object> lst = cc.getList("c1.value");
493            assertEquals("Wrong list size", 2, lst.size());
494            assertEquals("Wrong first element", "test1", lst.get(0));
495            assertEquals("Wrong second element", "test2", lst.get(1));
496        }
497    
498        /**
499         * Tests interpolation in combination with reloading.
500         */
501        @Test
502        public void testInterpolationWithReload() throws IOException,
503                ConfigurationException
504        {
505            File testFile = new File("target/testConfig.properties");
506            final String propFirst = "first.name";
507            final String propFull = "full.name";
508    
509            try
510            {
511                writeTestConfig(testFile, propFirst, "John");
512                PropertiesConfiguration c1 = new PropertiesConfiguration(testFile);
513                c1.setReloadingStrategy(new FileAlwaysReloadingStrategy());
514                PropertiesConfiguration c2 = new PropertiesConfiguration();
515                c2.addProperty(propFull, "${" + propFirst + "} Doe");
516                CompositeConfiguration cc = new CompositeConfiguration();
517                cc.addConfiguration(c1);
518                cc.addConfiguration(c2);
519                assertEquals("Wrong name", "John Doe", cc.getString(propFull));
520    
521                writeTestConfig(testFile, propFirst, "Jane");
522                assertEquals("First name not changed", "Jane", c1
523                        .getString(propFirst));
524                assertEquals("First name not changed in composite", "Jane", cc
525                        .getString(propFirst));
526                assertEquals("Full name not changed", "Jane Doe", cc
527                        .getString(propFull));
528            }
529            finally
530            {
531                if (testFile.exists())
532                {
533                    testFile.delete();
534                }
535            }
536        }
537    
538        /**
539         * Writes a test properties file containing a single property definition.
540         *
541         * @param f the file to write
542         * @param prop the property name
543         * @param value the property value
544         * @throws IOException if an error occurs
545         */
546        private void writeTestConfig(File f, String prop, String value)
547                throws IOException
548        {
549            PrintWriter out = new PrintWriter(new FileWriter(f));
550            out.print(prop);
551            out.print("=");
552            out.println(value);
553            out.close();
554        }
555    
556        @Test
557        public void testInstanciateWithCollection()
558        {
559            Collection<Configuration> configs = new ArrayList<Configuration>();
560            configs.add(xmlConf);
561            configs.add(conf1);
562            configs.add(conf2);
563    
564            CompositeConfiguration config = new CompositeConfiguration(configs);
565            assertEquals("Number of configurations", 4, config.getNumberOfConfigurations());
566            assertTrue("The in memory configuration is not empty", config.getInMemoryConfiguration().isEmpty());
567        }
568    
569        @Test
570        public void testClone()
571        {
572            CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
573            assertEquals("Wrong number of contained configurations", cc
574                    .getNumberOfConfigurations(), cc2.getNumberOfConfigurations());
575    
576            StrictConfigurationComparator comp = new StrictConfigurationComparator();
577            for (int i = 0; i < cc.getNumberOfConfigurations(); i++)
578            {
579                assertEquals("Wrong configuration class at " + i, cc
580                        .getConfiguration(i).getClass(), cc2.getConfiguration(i)
581                        .getClass());
582                assertNotSame("Configuration was not cloned", cc
583                        .getConfiguration(i), cc2.getConfiguration(i));
584                assertTrue("Configurations at " + i + " not equal", comp.compare(cc
585                        .getConfiguration(i), cc2.getConfiguration(i)));
586            }
587    
588            assertTrue("Configurations are not equal", comp.compare(cc, cc2));
589        }
590    
591        /**
592         * Tests cloning if one of the contained configurations does not support
593         * this operation. This should cause an exception.
594         */
595        @Test(expected = ConfigurationRuntimeException.class)
596        public void testCloneNotSupported()
597        {
598            cc.addConfiguration(new NonCloneableConfiguration());
599            cc.clone();
600        }
601    
602        /**
603         * Ensures that event listeners are not cloned.
604         */
605        @Test
606        public void testCloneEventListener()
607        {
608            cc.addConfigurationListener(new TestEventListenerImpl());
609            CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
610            assertTrue("Listeners have been cloned", cc2
611                    .getConfigurationListeners().isEmpty());
612        }
613    
614        /**
615         * Tests whether add property events are triggered.
616         */
617        @Test
618        public void testEventAddProperty()
619        {
620            TestEventListenerImpl l = new TestEventListenerImpl();
621            cc.addConfigurationListener(l);
622            cc.addProperty("test", "value");
623            assertEquals("No add events received", 2, l.eventCount);
624        }
625    
626        /**
627         * Tests whether set property events are triggered.
628         */
629        @Test
630        public void testEventSetProperty()
631        {
632            TestEventListenerImpl l = new TestEventListenerImpl();
633            cc.addConfigurationListener(l);
634            cc.setProperty("test", "value");
635            assertEquals("No set events received", 2, l.eventCount);
636        }
637    
638        /**
639         * Tests whether clear property events are triggered.
640         */
641        @Test
642        public void testEventClearProperty()
643        {
644            cc.addConfiguration(conf1);
645            assertTrue("Wrong value for property", cc
646                    .getBoolean("configuration.loaded"));
647            TestEventListenerImpl l = new TestEventListenerImpl();
648            cc.addConfigurationListener(l);
649            cc.clearProperty("configuration.loaded");
650            assertFalse("Key still present", cc.containsKey("configuration.loaded"));
651            assertEquals("No clear events received", 2, l.eventCount);
652        }
653    
654        /**
655         * Tests changing the list delimiter character.
656         */
657        @Test
658        public void testSetListDelimiter()
659        {
660            cc.setListDelimiter('/');
661            checkSetListDelimiter();
662        }
663    
664        /**
665         * Tests whether the correct list delimiter is set after a clear operation.
666         */
667        @Test
668        public void testSetListDelimiterAfterClear()
669        {
670            cc.setListDelimiter('/');
671            cc.clear();
672            checkSetListDelimiter();
673        }
674    
675        /**
676         * Helper method for testing whether the list delimiter is correctly
677         * handled.
678         */
679        private void checkSetListDelimiter()
680        {
681            cc.addProperty("test.list", "a/b/c");
682            cc.addProperty("test.property", "a,b,c");
683            assertEquals("Wrong number of list elements", 3, cc
684                    .getList("test.list").size());
685            assertEquals("Wrong value of property", "a,b,c", cc
686                    .getString("test.property"));
687        }
688    
689        /**
690         * Tests whether list splitting can be disabled.
691         */
692        @Test
693        public void testSetDelimiterParsingDisabled()
694        {
695            cc.setDelimiterParsingDisabled(true);
696            checkSetListDelimiterParsingDisabled();
697        }
698    
699        /**
700         * Tests whether the list parsing flag is correctly handled after a clear()
701         * operation.
702         */
703        @Test
704        public void testSetDelimiterParsingDisabledAfterClear()
705        {
706            cc.setDelimiterParsingDisabled(true);
707            cc.clear();
708            checkSetListDelimiterParsingDisabled();
709        }
710    
711        /**
712         * Helper method for checking whether the list parsing flag is correctly
713         * handled.
714         */
715        private void checkSetListDelimiterParsingDisabled()
716        {
717            cc.addProperty("test.property", "a,b,c");
718            assertEquals("Wrong value of property", "a,b,c", cc
719                    .getString("test.property"));
720        }
721    
722        /**
723         * Prepares a test of the getSource() method.
724         */
725        private void setUpSourceTest()
726        {
727            cc.addConfiguration(conf1);
728            cc.addConfiguration(conf2);
729        }
730    
731        /**
732         * Tests the getSource() method if the property is defined in a single child
733         * configuration.
734         */
735        @Test
736        public void testGetSourceSingle()
737        {
738            setUpSourceTest();
739            conf1.addProperty(TEST_PROPERTY, Boolean.TRUE);
740            assertSame("Wrong source configuration", conf1, cc
741                    .getSource(TEST_PROPERTY));
742        }
743    
744        /**
745         * Tests the getSource() method for an unknown property key.
746         */
747        @Test
748        public void testGetSourceUnknown()
749        {
750            setUpSourceTest();
751            assertNull("Wrong source for unknown key", cc.getSource(TEST_PROPERTY));
752        }
753    
754        /**
755         * Tests the getSource() method for a property contained in the in memory
756         * configuration.
757         */
758        @Test
759        public void testGetSourceInMemory()
760        {
761            setUpSourceTest();
762            cc.addProperty(TEST_PROPERTY, Boolean.TRUE);
763            assertSame("Source not found in in-memory config", cc
764                    .getInMemoryConfiguration(), cc.getSource(TEST_PROPERTY));
765        }
766    
767        /**
768         * Tests the getSource() method if the property is defined by multiple child
769         * configurations. In this case an exception should be thrown.
770         */
771        @Test(expected = IllegalArgumentException.class)
772        public void testGetSourceMultiple()
773        {
774            setUpSourceTest();
775            conf1.addProperty(TEST_PROPERTY, Boolean.TRUE);
776            cc.addProperty(TEST_PROPERTY, "a value");
777            cc.getSource(TEST_PROPERTY);
778        }
779    
780        /**
781         * Tests the getSource() method for a null key. This should cause an
782         * exception.
783         */
784        @Test(expected = IllegalArgumentException.class)
785        public void testGetSourceNull()
786        {
787            cc.getSource(null);
788        }
789    
790        /**
791         * Prepares a test for interpolation with multiple configurations and
792         * similar properties.
793         */
794        private void prepareInterpolationTest()
795        {
796            PropertiesConfiguration p = new PropertiesConfiguration();
797            p.addProperty("foo", "initial");
798            p.addProperty("bar", "${foo}");
799            p.addProperty("prefix.foo", "override");
800    
801            cc.addConfiguration(p.subset("prefix"));
802            cc.addConfiguration(p);
803            assertEquals("Wrong value on direct access", "override", cc
804                    .getString("bar"));
805        }
806    
807        /**
808         * Tests querying a list when a tricky interpolation is involved. This is
809         * related to CONFIGURATION-339.
810         */
811        @Test
812        public void testGetListWithInterpolation()
813        {
814            prepareInterpolationTest();
815            List<Object> lst = cc.getList("bar");
816            assertEquals("Wrong number of values", 1, lst.size());
817            assertEquals("Wrong value in list", "override", lst.get(0));
818        }
819    
820        /**
821         * Tests querying a string array when a tricky interpolation is involved.
822         */
823        @Test
824        public void testGetStringArrayWithInterpolation()
825        {
826            prepareInterpolationTest();
827            String[] values = cc.getStringArray("bar");
828            assertEquals("Wrong number of values", 1, values.length);
829            assertEquals("Wrong value in array", "override", values[0]);
830        }
831    
832        /**
833         * Tests whether interpolation works if multiple configurations are
834         * involved. This test is related to CONFIGURATION-441.
835         */
836        @Test
837        public void testInterpolationInMultipleConfigs()
838        {
839            Configuration c1 = new PropertiesConfiguration();
840            c1.addProperty("property.one", "one");
841            c1.addProperty("property.two", "two");
842            Configuration c2 = new PropertiesConfiguration();
843            c2.addProperty("property.one.ref", "${property.one}");
844            cc.addConfiguration(c1);
845            cc.addConfiguration(c2);
846            assertEquals("Wrong interpolated value", "one",
847                    cc.getString("property.one.ref"));
848        }
849    
850        /**
851         * Tests the behavior of setListDelimiter() if the in-memory configuration
852         * is not derived from BaseConfiguration. This test is related to
853         * CONFIGURATION-476.
854         */
855        @Test
856        public void testSetListDelimiterInMemoryConfigNonBaseConfig()
857        {
858            Configuration inMemoryConfig = EasyMock.createMock(Configuration.class);
859            EasyMock.replay(inMemoryConfig);
860            cc = new CompositeConfiguration(inMemoryConfig);
861            cc.setListDelimiter(';');
862        }
863    
864        /**
865         * Tests the behavior of setDelimiterParsingDisabled() if the in-memory
866         * configuration is not derived from BaseConfiguration. This test is related
867         * to CONFIGURATION-476.
868         */
869        @Test
870        public void testSetDelimiterParsingDisabledInMemoryConfigNonBaseConfig()
871        {
872            Configuration inMemoryConfig = EasyMock.createMock(Configuration.class);
873            EasyMock.replay(inMemoryConfig);
874            cc = new CompositeConfiguration(inMemoryConfig);
875            cc.setDelimiterParsingDisabled(true);
876        }
877    
878        /**
879         * Tests whether a configuration can act as both regular child configuration
880         * and in-memory configuration. This test is related to CONFIGURATION-471.
881         */
882        @Test
883        public void testUseChildConfigAsInMemoryConfig()
884        {
885            conf1.setProperty(TEST_PROPERTY, "conf1");
886            conf2.setProperty(TEST_PROPERTY, "conf2");
887            cc.addConfiguration(conf1, true);
888            cc.addConfiguration(conf2);
889            assertEquals("Wrong number of configurations", 2,
890                    cc.getNumberOfConfigurations());
891            assertEquals("Wrong property", "conf1", cc.getString(TEST_PROPERTY));
892            cc.addProperty("newProperty", "newValue");
893            assertEquals("Not added to in-memory config", "newValue",
894                    conf1.getString("newProperty"));
895        }
896    
897        /**
898         * Tests whether the in-memory configuration can be replaced by a new child
899         * configuration.
900         */
901        @Test
902        public void testReplaceInMemoryConfig()
903        {
904            conf1.setProperty(TEST_PROPERTY, "conf1");
905            conf2.setProperty(TEST_PROPERTY, "conf2");
906            cc.addConfiguration(conf1, true);
907            cc.addProperty("newProperty1", "newValue1");
908            cc.addConfiguration(conf2, true);
909            cc.addProperty("newProperty2", "newValue2");
910            assertEquals("Wrong property", "conf1", cc.getString(TEST_PROPERTY));
911            assertEquals("Not added to in-memory config", "newValue1",
912                    conf1.getString("newProperty1"));
913            assertEquals("In-memory config not changed", "newValue2",
914                    conf2.getString("newProperty2"));
915        }
916    
917        /**
918         * A test configuration event listener that counts the number of received
919         * events. Used for testing the event facilities.
920         */
921        static class TestEventListenerImpl implements ConfigurationListener
922        {
923            /** The number of received events.*/
924            int eventCount;
925    
926            public void configurationChanged(ConfigurationEvent event)
927            {
928                eventCount++;
929            }
930        }
931    }