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.configuration;
18  
19  import java.util.Collection;
20  import java.util.Set;
21  
22  import org.apache.commons.configuration.event.ConfigurationEvent;
23  import org.apache.commons.configuration.event.ConfigurationListener;
24  import org.apache.commons.configuration.tree.NodeCombiner;
25  import org.apache.commons.configuration.tree.UnionCombiner;
26  
27  import junit.framework.Assert;
28  import junit.framework.TestCase;
29  
30  /***
31   * Test class for CombinedConfiguration.
32   *
33   * @version $Id: TestCombinedConfiguration.java 439648 2006-09-02 20:42:10Z oheger $
34   */
35  public class TestCombinedConfiguration extends TestCase
36  {
37      /*** Constant for the name of a sub configuration. */
38      static final String TEST_NAME = "SUBCONFIG";
39  
40      /*** Constant for a test key. */
41      static final String TEST_KEY = "test.value";
42  
43      /*** The configuration to be tested. */
44      CombinedConfiguration config;
45  
46      /*** The test event listener. */
47      CombinedListener listener;
48  
49      protected void setUp() throws Exception
50      {
51          super.setUp();
52          config = new CombinedConfiguration();
53          listener = new CombinedListener();
54          config.addConfigurationListener(listener);
55      }
56  
57      /***
58       * Tests accessing a newly created combined configuration.
59       */
60      public void testInit()
61      {
62          assertEquals("Already configurations contained", 0, config
63                  .getNumberOfConfigurations());
64          assertTrue("Set of names is not empty", config.getConfigurationNames()
65                  .isEmpty());
66          assertTrue("Wrong node combiner",
67                  config.getNodeCombiner() instanceof UnionCombiner);
68          assertNull("Test config was found", config.getConfiguration(TEST_NAME));
69      }
70  
71      /***
72       * Tests adding a configuration (without further information).
73       */
74      public void testAddConfiguration()
75      {
76          AbstractConfiguration c = setUpTestConfiguration();
77          config.addConfiguration(c);
78          checkAddConfig(c);
79          assertEquals("Wrong number of configs", 1, config
80                  .getNumberOfConfigurations());
81          assertTrue("Name list is not empty", config.getConfigurationNames()
82                  .isEmpty());
83          assertSame("Added config not found", c, config.getConfiguration(0));
84          assertTrue("Wrong property value", config.getBoolean(TEST_KEY));
85          listener.checkEvent(1, 0);
86      }
87  
88      /***
89       * Tests adding a configuration with a name.
90       */
91      public void testAddConfigurationWithName()
92      {
93          AbstractConfiguration c = setUpTestConfiguration();
94          config.addConfiguration(c, TEST_NAME);
95          checkAddConfig(c);
96          assertEquals("Wrong number of configs", 1, config
97                  .getNumberOfConfigurations());
98          assertSame("Added config not found", c, config.getConfiguration(0));
99          assertSame("Added config not found by name", c, config
100                 .getConfiguration(TEST_NAME));
101         Set names = config.getConfigurationNames();
102         assertEquals("Wrong number of config names", 1, names.size());
103         assertTrue("Name not found", names.contains(TEST_NAME));
104         assertTrue("Wrong property value", config.getBoolean(TEST_KEY));
105         listener.checkEvent(1, 0);
106     }
107 
108     /***
109      * Tests adding a configuration with a name when this name already exists.
110      * This should cause an exception.
111      */
112     public void testAddConfigurationWithNameTwice()
113     {
114         config.addConfiguration(setUpTestConfiguration(), TEST_NAME);
115         try
116         {
117             config.addConfiguration(setUpTestConfiguration(), TEST_NAME,
118                     "prefix");
119             fail("Could add config with same name!");
120         }
121         catch (ConfigurationRuntimeException cex)
122         {
123             // ok
124         }
125     }
126 
127     /***
128      * Tests adding a configuration and specifying an at position.
129      */
130     public void testAddConfigurationAt()
131     {
132         AbstractConfiguration c = setUpTestConfiguration();
133         config.addConfiguration(c, null, "my");
134         checkAddConfig(c);
135         assertTrue("Wrong property value", config.getBoolean("my." + TEST_KEY));
136     }
137 
138     /***
139      * Tests adding a configuration with a complex at position. Here the at path
140      * contains a dot, which must be escaped.
141      */
142     public void testAddConfigurationComplexAt()
143     {
144         AbstractConfiguration c = setUpTestConfiguration();
145         config.addConfiguration(c, null, "This..is.a.complex");
146         checkAddConfig(c);
147         assertTrue("Wrong property value", config
148                 .getBoolean("This..is.a.complex." + TEST_KEY));
149     }
150 
151     /***
152      * Checks if a configuration was correctly added to the combined config.
153      *
154      * @param c the config to check
155      */
156     private void checkAddConfig(AbstractConfiguration c)
157     {
158         Collection listeners = c.getConfigurationListeners();
159         assertEquals("Wrong number of configuration listeners", 1, listeners
160                 .size());
161         assertTrue("Combined config is no listener", listeners.contains(config));
162     }
163 
164     /***
165      * Tests adding a null configuration. This should cause an exception to be
166      * thrown.
167      */
168     public void testAddNullConfiguration()
169     {
170         try
171         {
172             config.addConfiguration(null);
173             fail("Could add null configuration!");
174         }
175         catch (IllegalArgumentException iex)
176         {
177             // ok
178         }
179     }
180 
181     /***
182      * Tests accessing properties if no configurations have been added.
183      */
184     public void testAccessPropertyEmpty()
185     {
186         assertFalse("Found a key", config.containsKey(TEST_KEY));
187         assertNull("Key has a value", config.getString("test.comment"));
188         assertTrue("Config is not empty", config.isEmpty());
189     }
190 
191     /***
192      * Tests accessing properties if multiple configurations have been added.
193      */
194     public void testAccessPropertyMulti()
195     {
196         config.addConfiguration(setUpTestConfiguration());
197         config.addConfiguration(setUpTestConfiguration(), null, "prefix1");
198         config.addConfiguration(setUpTestConfiguration(), null, "prefix2");
199         assertTrue("Prop1 not found", config.getBoolean(TEST_KEY));
200         assertTrue("Prop 2 not found", config.getBoolean("prefix1." + TEST_KEY));
201         assertTrue("Prop 3 not found", config.getBoolean("prefix2." + TEST_KEY));
202         assertFalse("Configuration is empty", config.isEmpty());
203         listener.checkEvent(3, 0);
204     }
205 
206     /***
207      * Tests removing a configuration.
208      */
209     public void testRemoveConfiguration()
210     {
211         AbstractConfiguration c = setUpTestConfiguration();
212         config.addConfiguration(c);
213         checkAddConfig(c);
214         assertTrue("Config could not be removed", config.removeConfiguration(c));
215         checkRemoveConfig(c);
216     }
217 
218     /***
219      * Tests removing a configuration by index.
220      */
221     public void testRemoveConfigurationAt()
222     {
223         AbstractConfiguration c = setUpTestConfiguration();
224         config.addConfiguration(c);
225         assertSame("Wrong config removed", c, config.removeConfigurationAt(0));
226         checkRemoveConfig(c);
227     }
228 
229     /***
230      * Tests removing a configuration by name.
231      */
232     public void testRemoveConfigurationByName()
233     {
234         AbstractConfiguration c = setUpTestConfiguration();
235         config.addConfiguration(c, TEST_NAME);
236         assertSame("Wrong config removed", c, config
237                 .removeConfiguration(TEST_NAME));
238         checkRemoveConfig(c);
239     }
240 
241     /***
242      * Tests removing a configuration with a name.
243      */
244     public void testRemoveNamedConfiguration()
245     {
246         AbstractConfiguration c = setUpTestConfiguration();
247         config.addConfiguration(c, TEST_NAME);
248         config.removeConfiguration(c);
249         checkRemoveConfig(c);
250     }
251 
252     /***
253      * Tests removing a named configuration by index.
254      */
255     public void testRemoveNamedConfigurationAt()
256     {
257         AbstractConfiguration c = setUpTestConfiguration();
258         config.addConfiguration(c, TEST_NAME);
259         assertSame("Wrong config removed", c, config.removeConfigurationAt(0));
260         checkRemoveConfig(c);
261     }
262 
263     /***
264      * Tests removing a configuration that was not added prior.
265      */
266     public void testRemoveNonContainedConfiguration()
267     {
268         assertFalse("Could remove non contained config", config
269                 .removeConfiguration(setUpTestConfiguration()));
270         listener.checkEvent(0, 0);
271     }
272 
273     /***
274      * Tests removing a configuration by name, which is not contained.
275      */
276     public void testRemoveConfigurationByUnknownName()
277     {
278         assertNull("Could remove configuration by unknown name", config
279                 .removeConfiguration("unknownName"));
280         listener.checkEvent(0, 0);
281     }
282 
283     /***
284      * Tests whether a configuration was completely removed.
285      *
286      * @param c the removed configuration
287      */
288     private void checkRemoveConfig(AbstractConfiguration c)
289     {
290         assertTrue("Listener was not removed", c.getConfigurationListeners()
291                 .isEmpty());
292         assertEquals("Wrong number of contained configs", 0, config
293                 .getNumberOfConfigurations());
294         assertTrue("Name was not removed", config.getConfigurationNames()
295                 .isEmpty());
296         listener.checkEvent(2, 0);
297     }
298 
299     /***
300      * Tests if an update of a contained configuration leeds to an invalidation
301      * of the combined configuration.
302      */
303     public void testUpdateContainedConfiguration()
304     {
305         AbstractConfiguration c = setUpTestConfiguration();
306         config.addConfiguration(c);
307         c.addProperty("test.otherTest", "yes");
308         assertEquals("New property not found", "yes", config
309                 .getString("test.otherTest"));
310         listener.checkEvent(3, 0);
311     }
312 
313     /***
314      * Tests if setting a node combiner causes an invalidation.
315      */
316     public void testSetNodeCombiner()
317     {
318         NodeCombiner combiner = new UnionCombiner();
319         config.setNodeCombiner(combiner);
320         assertSame("Node combiner was not set", combiner, config
321                 .getNodeCombiner());
322         listener.checkEvent(1, 0);
323     }
324 
325     /***
326      * Tests setting a null node combiner. This should cause an exception.
327      */
328     public void testSetNullNodeCombiner()
329     {
330         try
331         {
332             config.setNodeCombiner(null);
333             fail("Could set null node combiner!");
334         }
335         catch (IllegalArgumentException iex)
336         {
337             // ok
338         }
339     }
340 
341     /***
342      * Tests cloning a combined configuration.
343      */
344     public void testClone()
345     {
346         config.addConfiguration(setUpTestConfiguration());
347         config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "conf2");
348         config.addConfiguration(new PropertiesConfiguration(), "props");
349 
350         CombinedConfiguration cc2 = (CombinedConfiguration) config.clone();
351         assertEquals("Wrong number of contained configurations", config
352                 .getNumberOfConfigurations(), cc2.getNumberOfConfigurations());
353         assertSame("Wrong node combiner", config.getNodeCombiner(), cc2
354                 .getNodeCombiner());
355         assertEquals("Wrong number of names", config.getConfigurationNames()
356                 .size(), cc2.getConfigurationNames().size());
357         assertTrue("Event listeners were cloned", cc2
358                 .getConfigurationListeners().isEmpty());
359 
360         StrictConfigurationComparator comp = new StrictConfigurationComparator();
361         for (int i = 0; i < config.getNumberOfConfigurations(); i++)
362         {
363             assertNotSame("Configuration at " + i + " was not cloned", config
364                     .getConfiguration(i), cc2.getConfiguration(i));
365             assertEquals("Wrong config class at " + i, config.getConfiguration(
366                     i).getClass(), cc2.getConfiguration(i).getClass());
367             assertTrue("Configs not equal at " + i, comp.compare(config
368                     .getConfiguration(i), cc2.getConfiguration(i)));
369         }
370 
371         assertTrue("Combined configs not equal", comp.compare(config, cc2));
372     }
373 
374     /***
375      * Tests if the cloned configuration is decoupled from the original.
376      */
377     public void testCloneModify()
378     {
379         config.addConfiguration(setUpTestConfiguration(), TEST_NAME);
380         CombinedConfiguration cc2 = (CombinedConfiguration) config.clone();
381         assertTrue("Name is missing", cc2.getConfigurationNames().contains(
382                 TEST_NAME));
383         cc2.removeConfiguration(TEST_NAME);
384         assertFalse("Names in original changed", config.getConfigurationNames()
385                 .isEmpty());
386     }
387 
388     /***
389      * Tests clearing a combined configuration. This should remove all contained
390      * configurations.
391      */
392     public void testClear()
393     {
394         config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "test");
395         config.addConfiguration(setUpTestConfiguration());
396 
397         config.clear();
398         assertEquals("Still configs contained", 0, config
399                 .getNumberOfConfigurations());
400         assertTrue("Still names contained", config.getConfigurationNames()
401                 .isEmpty());
402         assertTrue("Config is not empty", config.isEmpty());
403 
404         listener.checkEvent(3, 2);
405     }
406 
407     /***
408      * Helper method for creating a test configuration to be added to the
409      * combined configuration.
410      *
411      * @return the test configuration
412      */
413     private AbstractConfiguration setUpTestConfiguration()
414     {
415         HierarchicalConfiguration config = new HierarchicalConfiguration();
416         config.addProperty(TEST_KEY, Boolean.TRUE);
417         config.addProperty("test.comment", "This is a test");
418         return config;
419     }
420 
421     /***
422      * Test event listener class for checking if the expected invalidate events
423      * are fired.
424      */
425     static class CombinedListener implements ConfigurationListener
426     {
427         int invalidateEvents;
428 
429         int otherEvents;
430 
431         public void configurationChanged(ConfigurationEvent event)
432         {
433             if (event.getType() == CombinedConfiguration.EVENT_COMBINED_INVALIDATE)
434             {
435                 invalidateEvents++;
436             }
437             else
438             {
439                 otherEvents++;
440             }
441         }
442 
443         /***
444          * Checks if the expected number of events was fired.
445          *
446          * @param expectedInvalidate the expected number of invalidate events
447          * @param expectedOthers the expected number of other events
448          */
449         public void checkEvent(int expectedInvalidate, int expectedOthers)
450         {
451             Assert.assertEquals("Wrong number of invalidate events",
452                     expectedInvalidate, invalidateEvents);
453             Assert.assertEquals("Wrong number of other events", expectedOthers,
454                     otherEvents);
455         }
456     }
457 }