    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 }