    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
    18  package org.apache.commons.configuration;
    19
    20  import java.io.File;
    21  import java.io.FileOutputStream;
    22  import java.io.FileWriter;
    23  import java.io.IOException;
    24  import java.io.OutputStream;
    25  import java.io.PrintWriter;
    26  import java.io.Reader;
    27  import java.io.StringReader;
    28  import java.io.StringWriter;
    29  import java.net.HttpURLConnection;
    30  import java.net.URL;
    31  import java.net.URLConnection;
    32  import java.net.URLStreamHandler;
    33  import java.util.ArrayList;
    34  import java.util.Iterator;
    35  import java.util.List;
    36
    37  import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
    38
    39  import junit.framework.TestCase;
    40
    41  /***
    42   * Test for loading and saving properties files.
    43   *
    44   * @version $Id: TestPropertiesConfiguration.java 567771 2007-08-20 17:57:08Z oheger $
    45   */
    46  public class TestPropertiesConfiguration extends TestCase
    47  {
    48      private PropertiesConfiguration conf;
    49
    50      /*** The File that we test with */
    51      private String testProperties = new File("conf/test.properties").getAbsolutePath();
    52
    53      private String testBasePath = new File("conf").getAbsolutePath();
    54      private String testBasePath2 = new File("conf").getAbsoluteFile().getParentFile().getAbsolutePath();
    55      private File testSavePropertiesFile = new File("target/testsave.properties");
    56
    57      protected void setUp() throws Exception
    58      {
    59          conf = new PropertiesConfiguration(testProperties);
    60
    61          // remove the test save file if it exists
    62          if (testSavePropertiesFile.exists())
    63          {
    64              assertTrue("Test output file could not be deleted",
    65                      testSavePropertiesFile.delete());
    66          }
    67      }
    68
    69      public void testLoad() throws Exception
    70      {
    71          String loaded = conf.getString("configuration.loaded");
    72          assertEquals("true", loaded);
    73      }
    74
    75      /***
    76       * Tests if properties can be appended by simply calling load() another
    77       * time.
    78       */
    79      public void testAppend() throws Exception
    80      {
    81          File file2 = new File("conf/threesome.properties");
    82          conf.load(file2);
    83          assertEquals("aaa", conf.getString("test.threesome.one"));
    84          assertEquals("true", conf.getString("configuration.loaded"));
    85      }
    86
    87      /***
    88       * Tests that empty properties are treated as the empty string
    89       * (rather than as null).
    90       */
    91      public void testEmpty() throws Exception
    92      {
    93          String empty = conf.getString("test.empty");
    94          assertNotNull(empty);
    95          assertEquals("", empty);
    96      }
    97
    98      /***
    99       * Tests that references to other properties work
    100      */
    101     public void testReference() throws Exception
    102     {
    103         assertEquals("baseextra", conf.getString("base.reference"));
    104     }
    105
    106     /***
    107      * test if includes properties get loaded too
    108      */
    109     public void testLoadInclude() throws Exception
    110     {
    111         String loaded = conf.getString("include.loaded");
    112         assertEquals("true", loaded);
    113     }
    114
    115     public void testSetInclude() throws Exception
    116     {
    117         // change the include key
    118         PropertiesConfiguration.setInclude("import");
    119
    120         // load the configuration
    121         PropertiesConfiguration conf = new PropertiesConfiguration();
    122         conf.load("conf/test.properties");
    123
    124         // restore the previous value for the other tests
    125         PropertiesConfiguration.setInclude("include");
    126
    127         assertNull(conf.getString("include.loaded"));
    128     }
    129
    130     /***
    131      * Tests <code>List</code> parsing.
    132      */
    133     public void testList() throws Exception
    134     {
    135         List packages = conf.getList("packages");
    136         // we should get 3 packages here
    137         assertEquals(3, packages.size());
    138     }
    139
    140     public void testSave() throws Exception
    141     {
    142         // add an array of strings to the configuration
    143         conf.addProperty("string", "value1");
    144         List list = new ArrayList();
    145         for (int i = 1; i < 5; i++)
    146         {
    147             list.add("value" + i);
    148         }
    149         conf.addProperty("array", list);
    150
    151         // save the configuration
    152         String filename = testSavePropertiesFile.getAbsolutePath();
    153         conf.save(filename);
    154
    155         assertTrue("The saved file doesn't exist", testSavePropertiesFile.exists());
    156
    157         // read the configuration and compare the properties
    158         PropertiesConfiguration checkConfig = new PropertiesConfiguration(filename);
    159         ConfigurationAssert.assertEquals(conf, checkConfig);
    160
    161         // Save it again, verifing a save with a filename works.
    162         checkConfig.save();
    163     }
    164
    165     public void testSaveToCustomURL() throws Exception
    166     {
    167         // save the configuration to a custom URL
    168         URL url = new URL("foo", "", 0, "./target/testsave-custom-url.properties", new FileURLStreamHandler());
    169         conf.save(url);
    170
    171         // reload the configuration
    172         Configuration config2 = new PropertiesConfiguration(url);
    173         assertEquals("true", config2.getString("configuration.loaded"));
    174     }
    175
    176     public void testInMemoryCreatedSave() throws Exception
    177     {
    178         PropertiesConfiguration pc = new PropertiesConfiguration();
    179         // add an array of strings to the configuration
    180         pc.addProperty("string", "value1");
    181         List list = new ArrayList();
    182         for (int i = 1; i < 5; i++)
    183         {
    184             list.add("value" + i);
    185         }
    186         pc.addProperty("array", list);
    187
    188         // save the configuration
    189         String filename = testSavePropertiesFile.getAbsolutePath();
    190         pc.save(filename);
    191
    192         assertTrue("The saved file doesn't exist", testSavePropertiesFile.exists());
    193
    194         // read the configuration and compare the properties
    195         PropertiesConfiguration checkConfig = new PropertiesConfiguration(filename);
    196         ConfigurationAssert.assertEquals(pc, checkConfig);
    197
    198         // Save it again, verifing a save with a filename works.
    199         checkConfig.save();
    200     }
    201
    202     /***
    203      * Tests saving a configuration when delimiter parsing is disabled.
    204      */
    205     public void testSaveWithDelimiterParsingDisabled() throws ConfigurationException
    206     {
    207         conf.clear();
    208         conf.setDelimiterParsingDisabled(true);
    209         conf.addProperty("test.list", "a,b,c");
    210         conf.addProperty("test.dirs", "C://Temp//,D://Data//");
    211         conf.save(testSavePropertiesFile);
    212
    213         PropertiesConfiguration checkConfig = new PropertiesConfiguration();
    214         checkConfig.setDelimiterParsingDisabled(true);
    215         checkConfig.setFile(testSavePropertiesFile);
    216         checkConfig.load();
    217         ConfigurationAssert.assertEquals(conf, checkConfig);
    218     }
    219
    220     public void testSaveMissingFilename()
    221     {
    222         PropertiesConfiguration pc = new PropertiesConfiguration();
    223         try
    224         {
    225             pc.save();
    226             fail("Should have throw ConfigurationException");
    227         }
    228         catch (ConfigurationException ce)
    229         {
    230             //good
    231         }
    232     }
    233
    234     /***
    235      * Tests if the base path is taken into account by the save() method.
    236      * @throws Exception if an error occurs
    237      */
    238     public void testSaveWithBasePath() throws Exception
    239     {
    240         conf.setProperty("test", "true");
    241         conf.setBasePath(testSavePropertiesFile.getParentFile().toURL().toString());
    242         conf.setFileName(testSavePropertiesFile.getName());
    243         conf.save();
    244         assertTrue(testSavePropertiesFile.exists());
    245     }
    246
    247     /***
    248      * Tests whether the escape character for list delimiters can be itself
    249      * escaped and survives a save operation.
    250      */
    251     public void testSaveEscapedEscapingCharacter()
    252             throws ConfigurationException
    253     {
    254         conf.addProperty("test.dirs", "C://Temp////,D://Data////,E://Test//");
    255         List dirs = conf.getList("test.dirs");
    256         assertEquals("Wrong number of list elements", 3, dirs.size());
    257         conf.save(testSavePropertiesFile);
    258
    259         PropertiesConfiguration checkConfig = new PropertiesConfiguration(
    260                 testSavePropertiesFile);
    261         ConfigurationAssert.assertEquals(conf, checkConfig);
    262     }
    263
    264     public void testLoadViaProperty() throws Exception
    265     {
    266         PropertiesConfiguration pc = new PropertiesConfiguration();
    267         pc.setFileName(testProperties);
    268         pc.load();
    269
    270         assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
    271     }
    272
    273     public void testLoadViaPropertyWithBasePath() throws Exception
    274     {
    275         PropertiesConfiguration pc = new PropertiesConfiguration();
    276         pc.setBasePath(testBasePath);
    277         pc.setFileName("test.properties");
    278         pc.load();
    279
    280         assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
    281     }
    282
    283     public void testLoadViaPropertyWithBasePath2() throws Exception
    284     {
    285         PropertiesConfiguration pc = new PropertiesConfiguration();
    286         pc.setBasePath(testBasePath2);
    287         pc.setFileName("conf/test.properties");
    288         pc.load();
    289
    290         assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
    291
    292         pc = new PropertiesConfiguration();
    293         pc.setBasePath(testBasePath2);
    294         pc.setFileName("conf/test.properties");
    295         pc.load();
    296
    297         assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
    298     }
    299
    300     public void testLoadFromFile() throws Exception
    301     {
    302         File file = new File("conf/test.properties");
    303         conf = new PropertiesConfiguration(file);
    304
    305         assertEquals("true", conf.getString("configuration.loaded"));
    306     }
    307
    308     public void testLoadUnexistingFile()
    309     {
    310         try
    311         {
    312             conf = new PropertiesConfiguration("Unexisting file");
    313             fail("Unexisting file was loaded.");
    314         }
    315         catch(ConfigurationException cex)
    316         {
    317             // fine
    318         }
    319     }
    320
    321     /***
    322      * Tests to load a file with enabled auto save mode.
    323      */
    324     public void testLoadWithAutoSave() throws Exception
    325     {
    326         setUpSavedProperties();
    327     }
    328
    329     /***
    330      * Tests the auto save functionality when an existing property is modified.
    331      */
    332     public void testLoadWithAutoSaveAndSetExisting() throws Exception
    333     {
    334         setUpSavedProperties();
    335         conf.setProperty("a", "moreThanOne");
    336         checkSavedConfig();
    337     }
    338
    339     /***
    340      * Tests the auto save functionality when a new property is added using the
    341      * setProperty() method.
    342      */
    343     public void testLoadWithAutoSaveAndSetNew() throws Exception
    344     {
    345         setUpSavedProperties();
    346         conf.setProperty("d", "four");
    347         checkSavedConfig();
    348     }
    349
    350     /***
    351      * Tests the auto save functionality when a new property is added using the
    352      * addProperty() method.
    353      */
    354     public void testLoadWithAutoSaveAndAdd() throws Exception
    355     {
    356         setUpSavedProperties();
    357         conf.addProperty("d", "four");
    358         checkSavedConfig();
    359     }
    360
    361     /***
    362      * Tests the auto save functionality when a property is removed.
    363      */
    364     public void testLoadWithAutoSaveAndClear() throws Exception
    365     {
    366         setUpSavedProperties();
    367         conf.clearProperty("c");
    368         PropertiesConfiguration checkConfig = checkSavedConfig();
    369         assertFalse("The saved configuration contain the key '" + "c" + "'", checkConfig.containsKey("c"));
    370     }
    371
    372     /***
    373      * Creates a properties file on disk. Used for testing load and save
    374      * operations.
    375      *
    376      * @throws IOException if an I/O error occurs
    377      */
    378     private void setUpSavedProperties() throws IOException, ConfigurationException
    379     {
    380         PrintWriter out = null;
    381
    382         try
    383         {
    384             out = new PrintWriter(new FileWriter(testSavePropertiesFile));
    385             out.println("a = one");
    386             out.println("b = two");
    387             out.println("c = three");
    388             out.close();
    389             out = null;
    390
    391             conf = new PropertiesConfiguration();
    392             conf.setAutoSave(true);
    393             conf.setFile(testSavePropertiesFile);
    394             conf.load();
    395             assertEquals("one", conf.getString("a"));
    396             assertEquals("two", conf.getString("b"));
    397             assertEquals("three", conf.getString("c"));
    398         }
    399         finally
    400         {
    401             if (out != null)
    402             {
    403                 out.close();
    404             }
    405         }
    406     }
    407
    408     /***
    409      * Helper method for testing a saved configuration. Reads in the file using
    410      * a new instance and compares this instance with the original one.
    411      *
    412      * @return the newly created configuration instance
    413      * @throws ConfigurationException if an error occurs
    414      */
    415     private PropertiesConfiguration checkSavedConfig()
    416             throws ConfigurationException
    417     {
    418         PropertiesConfiguration checkConfig = new PropertiesConfiguration(testSavePropertiesFile);
    419         for (Iterator i = conf.getKeys(); i.hasNext();)
    420         {
    421             String key = (String) i.next();
    422             assertTrue("The saved configuration doesn't contain the key '" + key + "'", checkConfig.containsKey(key));
    423             assertEquals("Value of the '" + key + "' property", conf.getProperty(key), checkConfig.getProperty(key));
    424         }
    425         return checkConfig;
    426     }
    427
    428     public void testGetStringWithEscapedChars()
    429     {
    430         String property = conf.getString("test.unescape");
    431         assertEquals("String with escaped characters", "This \n string \t contains \" escaped // characters", property);
    432     }
    433
    434     public void testGetStringWithEscapedComma()
    435     {
    436         String property = conf.getString("test.unescape.list-separator");
    437         assertEquals("String with an escaped list separator", "This string contains , an escaped list separator", property);
    438     }
    439
    440     public void testUnescapeJava()
    441     {
    442         assertEquals("test//,test", PropertiesConfiguration.unescapeJava("test//,test", ','));
    443     }
    444
    445     public void testEscapedKey() throws Exception
    446     {
    447         PropertiesConfiguration conf = new PropertiesConfiguration();
    448         conf.load(new StringReader("//u0066//u006f//u006f=bar"));
    449
    450         assertEquals("value of the 'foo' property", "bar", conf.getString("foo"));
    451     }
    452
    453     public void testMixedArray()
    454     {
    455         String[] array = conf.getStringArray("test.mixed.array");
    456
    457         assertEquals("array length", 4, array.length);
    458         assertEquals("1st element", "a", array[0]);
    459         assertEquals("2nd element", "b", array[1]);
    460         assertEquals("3rd element", "c", array[2]);
    461         assertEquals("4th element", "d", array[3]);
    462     }
    463
    464     public void testMultilines()
    465     {
    466         String property = "This is a value spread out across several adjacent "
    467                 + "natural lines by escaping the line terminator with "
    468                 + "a backslash character.";
    469
    470         assertEquals("'test.multilines' property", property, conf.getString("test.multilines"));
    471     }
    472
    473     public void testChangingDefaultListDelimiter() throws Exception
    474     {
    475         PropertiesConfiguration pc = new PropertiesConfiguration(testProperties);
    476         assertEquals(4, pc.getList("test.mixed.array").size());
    477
    478         char delimiter = PropertiesConfiguration.getDefaultListDelimiter();
    479         PropertiesConfiguration.setDefaultListDelimiter('^');
    480         pc = new PropertiesConfiguration(testProperties);
    481         assertEquals(2, pc.getList("test.mixed.array").size());
    482         PropertiesConfiguration.setDefaultListDelimiter(delimiter);
    483     }
    484
    485     public void testChangingListDelimiter() throws Exception
    486     {
    487         PropertiesConfiguration pc1 = new PropertiesConfiguration(testProperties);
    488         assertEquals(4, pc1.getList("test.mixed.array").size());
    489
    490         PropertiesConfiguration pc2 = new PropertiesConfiguration();
    491         pc2.setListDelimiter('^');
    492         pc2.setFileName(testProperties);
    493         pc2.load();
    494         assertEquals("Should obtain the first value", "a", pc2.getString("test.mixed.array"));
    495         assertEquals(2, pc2.getList("test.mixed.array").size());
    496     }
    497
    498     public void testDisableListDelimiter() throws Exception
    499     {
    500         PropertiesConfiguration pc1 = new PropertiesConfiguration(testProperties);
    501         assertEquals(4, pc1.getList("test.mixed.array").size());
    502
    503         PropertiesConfiguration pc2 = new PropertiesConfiguration();
    504         pc2.setDelimiterParsingDisabled(true);
    505         pc2.setFileName(testProperties);
    506         pc2.load();
    507         assertEquals(2, pc2.getList("test.mixed.array").size());
    508     }
    509
    510     /***
    511      * Tests escaping of an end of line with a backslash.
    512      */
    513     public void testNewLineEscaping()
    514     {
    515         List list = conf.getList("test.path");
    516         assertEquals(3, list.size());
    517         assertEquals("C://path1//", list.get(0));
    518         assertEquals("C://path2//", list.get(1));
    519         assertEquals("C://path3//complex//test//", list.get(2));
    520     }
    521
    522     /***
    523      * Tests if included files are loaded when the source lies in the class path.
    524      */
    525     public void testLoadIncludeFromClassPath() throws ConfigurationException
    526     {
    527         conf = new PropertiesConfiguration("test.properties");
    528         assertEquals("true", conf.getString("include.loaded"));
    529     }
    530
    531     /***
    532      * Test if the lines starting with # or ! are properly ignored.
    533      */
    534     public void testComment() {
    535         assertFalse("comment line starting with '#' parsed as a property", conf.containsKey("#comment"));
    536         assertFalse("comment line starting with '!' parsed as a property", conf.containsKey("!comment"));
    537     }
    538
    539     /***
    540      * Check that key/value separators can be part of a key.
    541      */
    542     public void testEscapedKeyValueSeparator()
    543     {
    544         assertEquals("Escaped separator '=' not supported in keys", "foo", conf.getProperty("test.separator=in.key"));
    545         assertEquals("Escaped separator ':' not supported in keys", "bar", conf.getProperty("test.separator:in.key"));
    546         assertEquals("Escaped separator '//t' not supported in keys", "foo", conf.getProperty("test.separator\tin.key"));
    547         assertEquals("Escaped separator '//f' not supported in keys", "bar", conf.getProperty("test.separator\fin.key"));
    548         assertEquals("Escaped separator ' ' not supported in keys"  , "foo", conf.getProperty("test.separator in.key"));
    549     }
    550
    551     /***
    552      * Test all acceptable key/value separators ('=', ':' or white spaces).
    553      */
    554     public void testKeyValueSeparators() {
    555         assertEquals("equal separator not properly parsed",      "foo", conf.getProperty("test.separator.equal"));
    556         assertEquals("colon separator not properly parsed",      "foo", conf.getProperty("test.separator.colon"));
    557         assertEquals("tab separator not properly parsed",        "foo", conf.getProperty("test.separator.tab"));
    558         assertEquals("formfeed separator not properly parsed",   "foo", conf.getProperty("test.separator.formfeed"));
    559         assertEquals("whitespace separator not properly parsed", "foo", conf.getProperty("test.separator.whitespace"));
    560     }
    561
    562     /***
    563      * Tests including properties when they are loaded from a nested directory
    564      * structure.
    565      */
    566     public void testIncludeInSubDir() throws ConfigurationException
    567     {
    568         ConfigurationFactory factory = new ConfigurationFactory("conf/testFactoryPropertiesInclude.xml");
    569         Configuration config = factory.getConfiguration();
    570         assertEquals(true, config.getBoolean("deeptest"));
    571         assertEquals(true, config.getBoolean("deepinclude"));
    572         assertFalse(config.containsKey("deeptestinvalid"));
    573     }
    574
    575     /***
    576      * Tests whether the correct line separator is used.
    577      */
    578     public void testLineSeparator() throws ConfigurationException
    579     {
    580         final String EOL = System.getProperty("line.separator");
    581         conf = new PropertiesConfiguration();
    582         conf.setHeader("My header");
    583         conf.setProperty("prop", "value");
    584
    585         StringWriter out = new StringWriter();
    586         conf.save(out);
    587         String content = out.toString();
    588         assertTrue("Header could not be found", content.indexOf("# My header"
    589                 + EOL + EOL) == 0);
    590         assertTrue("Property could not be found", content.indexOf("prop = value" + EOL) > 0);
    591     }
    592
    593     /***
    594      * Tests what happens if a reloading strategy's <code>reloadingRequired()</code>
    595      * implementation accesses methods of the configuration that in turn cause a reload.
    596      */
    597     public void testReentrantReload()
    598     {
    599         conf.setProperty("shouldReload", Boolean.FALSE);
    600         conf.setReloadingStrategy(new FileChangedReloadingStrategy()
    601         {
    602             public boolean reloadingRequired()
    603             {
    604                 return configuration.getBoolean("shouldReload");
    605             }
    606         });
    607         assertFalse("Property has wrong value", conf.getBoolean("shouldReload"));
    608     }
    609
    610     /***
    611      * Tests accessing the layout object.
    612      */
    613     public void testGetLayout()
    614     {
    615         PropertiesConfigurationLayout layout = conf.getLayout();
    616         assertNotNull("Layout is null", layout);
    617         assertSame("Different object returned", layout, conf.getLayout());
    618         conf.setLayout(null);
    619         PropertiesConfigurationLayout layout2 = conf.getLayout();
    620         assertNotNull("Layout 2 is null", layout2);
    621         assertNotSame("Same object returned", layout, layout2);
    622     }
    623
    624     /***
    625      * Tests the propertyLoaded() method for a simple property.
    626      */
    627     public void testPropertyLoaded() throws ConfigurationException
    628     {
    629         DummyLayout layout = new DummyLayout(conf);
    630         conf.setLayout(layout);
    631         conf.propertyLoaded("layoutLoadedProperty", "yes");
    632         assertEquals("Layout's load() was called", 0, layout.loadCalls);
    633         assertEquals("Property not added", "yes", conf.getString("layoutLoadedProperty"));
    634     }
    635
    636     /***
    637      * Tests the propertyLoaded() method for an include property.
    638      */
    639     public void testPropertyLoadedInclude() throws ConfigurationException
    640     {
    641         DummyLayout layout = new DummyLayout(conf);
    642         conf.setLayout(layout);
    643         conf.propertyLoaded(PropertiesConfiguration.getInclude(), "testClasspath.properties,testEqual.properties");
    644         assertEquals("Layout's load() was not correctly called", 2, layout.loadCalls);
    645         assertFalse("Property was added", conf.containsKey(PropertiesConfiguration.getInclude()));
    646     }
    647
    648     /***
    649      * Tests propertyLoaded() for an include property, when includes are
    650      * disabled.
    651      */
    652     public void testPropertyLoadedIncludeNotAllowed() throws ConfigurationException
    653     {
    654         DummyLayout layout = new DummyLayout(conf);
    655         conf.setLayout(layout);
    656         conf.setIncludesAllowed(false);
    657         conf.propertyLoaded(PropertiesConfiguration.getInclude(), "testClassPath.properties,testEqual.properties");
    658         assertEquals("Layout's load() was called", 0, layout.loadCalls);
    659         assertFalse("Property was added", conf.containsKey(PropertiesConfiguration.getInclude()));
    660     }
    661
    662     /***
    663      * Tests whether comment lines are correctly detected.
    664      */
    665     public void testIsCommentLine()
    666     {
    667         assertTrue("Comment not detected", PropertiesConfiguration.isCommentLine("# a comment"));
    668         assertTrue("Alternative comment not detected", PropertiesConfiguration.isCommentLine("! a comment"));
    669         assertTrue("Comment with no space not detected", PropertiesConfiguration.isCommentLine("#a comment"));
    670         assertTrue("Comment with leading space not detected", PropertiesConfiguration.isCommentLine("    ! a comment"));
    671         assertFalse("Wrong comment", PropertiesConfiguration.isCommentLine("   a#comment"));
    672     }
    673
    674     /***
    675      * Tests whether a properties configuration can be successfully cloned. It
    676      * is especially checked whether the layout object is taken into account.
    677      */
    678     public void testClone() throws ConfigurationException
    679     {
    680         PropertiesConfiguration copy = (PropertiesConfiguration) conf.clone();
    681         assertNotSame("Copy has same layout object", conf.getLayout(), copy.getLayout());
    682         assertEquals("Wrong number of event listeners for original", 1, conf.getConfigurationListeners().size());
    683         assertEquals("Wrong number of event listeners for clone", 1, copy.getConfigurationListeners().size());
    684         assertSame("Wrong event listener for original", conf.getLayout(), conf.getConfigurationListeners().iterator().next());
    685         assertSame("Wrong event listener for clone", copy.getLayout(), copy.getConfigurationListeners().iterator().next());
    686         StringWriter outConf = new StringWriter();
    687         conf.save(outConf);
    688         StringWriter outCopy = new StringWriter();
    689         copy.save(outCopy);
    690         assertEquals("Output from copy is different", outConf.toString(), outCopy.toString());
    691     }
    692
    693     /***
    694      * Tests the clone() method when no layout object exists yet.
    695      */
    696     public void testCloneNullLayout()
    697     {
    698         conf = new PropertiesConfiguration();
    699         PropertiesConfiguration copy = (PropertiesConfiguration) conf.clone();
    700         assertNotSame("Layout objects are the same", conf.getLayout(), copy.getLayout());
    701     }
    702
    703     /***
    704      * Tests saving a file-based configuration to a HTTP server.
    705      */
    706     public void testSaveToHTTPServerSuccess() throws Exception
    707     {
    708         MockHttpURLStreamHandler handler = new MockHttpURLStreamHandler(
    709                 HttpURLConnection.HTTP_OK, testSavePropertiesFile);
    710         URL url = new URL(null, "http://jakarta.apache.org", handler);
    711         conf.save(url);
    712         MockHttpURLConnection con = handler.getMockConnection();
    713         assertTrue("Wrong output flag", con.getDoOutput());
    714         assertEquals("Wrong method", "PUT", con.getRequestMethod());
    715
    716         PropertiesConfiguration checkConfig = new PropertiesConfiguration(
    717                 testSavePropertiesFile);
    718         ConfigurationAssert.assertEquals(conf, checkConfig);
    719     }
    720
    721     /***
    722      * Tests saving a file-based configuration to a HTTP server when the server
    723      * reports a failure. This should cause an exception.
    724      */
    725     public void testSaveToHTTPServerFail() throws Exception
    726     {
    727         MockHttpURLStreamHandler handler = new MockHttpURLStreamHandler(
    728                 HttpURLConnection.HTTP_BAD_REQUEST, testSavePropertiesFile);
    729         URL url = new URL(null, "http://jakarta.apache.org", handler);
    730         try
    731         {
    732             conf.save(url);
    733             fail("Response code was not checked!");
    734         }
    735         catch (ConfigurationException cex)
    736         {
    737             assertTrue("Wrong root cause: " + cex,
    738                     cex.getCause() instanceof IOException);
    739         }
    740     }
    741
    742     /***
    743      * Tests initializing a properties configuration from a non existing file.
    744      * There was a bug, which caused properties getting lost when later save()
    745      * is called.
    746      */
    747     public void testInitFromNonExistingFile() throws ConfigurationException
    748     {
    749         final String testProperty = "test.successfull";
    750         conf = new PropertiesConfiguration(testSavePropertiesFile);
    751         conf.addProperty(testProperty, Boolean.TRUE);
    752         conf.save();
    753         PropertiesConfiguration checkConfig = new PropertiesConfiguration(
    754                 testSavePropertiesFile);
    755         assertTrue("Test property not found", checkConfig
    756                 .getBoolean(testProperty));
    757     }
    758
    759     /***
    760      * Tests copying another configuration into the test configuration. This
    761      * test ensures that the layout object is informed about the newly added
    762      * properties.
    763      */
    764     public void testCopyAndSave() throws ConfigurationException
    765     {
    766         Configuration copyConf = setUpCopyConfig();
    767         conf.copy(copyConf);
    768         checkCopiedConfig(copyConf);
    769     }
    770
    771     /***
    772      * Tests appending a configuration to the test configuration. Again it has
    773      * to be ensured that the layout object is correctly updated.
    774      */
    775     public void testAppendAndSave() throws ConfigurationException
    776     {
    777         Configuration copyConf = setUpCopyConfig();
    778         conf.append(copyConf);
    779         checkCopiedConfig(copyConf);
    780     }
    781
    782     /***
    783      * Creates a configuration that can be used for testing copy operations.
    784      *
    785      * @return the configuration to be copied
    786      */
    787     private Configuration setUpCopyConfig()
    788     {
    789         final int count = 25;
    790         Configuration result = new BaseConfiguration();
    791         for (int i = 1; i <= count; i++)
    792         {
    793             result.addProperty("copyKey" + i, "copyValue" + i);
    794         }
    795         return result;
    796     }
    797
    798     /***
    799      * Tests whether the data of a configuration that was copied into the test
    800      * configuration is correctly saved.
    801      *
    802      * @param copyConf the copied configuration
    803      * @throws ConfigurationException if an error occurs
    804      */
    805     private void checkCopiedConfig(Configuration copyConf)
    806             throws ConfigurationException
    807     {
    808         conf.save(testSavePropertiesFile);
    809         PropertiesConfiguration checkConf = new PropertiesConfiguration(
    810                 testSavePropertiesFile);
    811         for (Iterator it = copyConf.getKeys(); it.hasNext();)
    812         {
    813             String key = (String) it.next();
    814             assertEquals("Wrong value for property " + key, checkConf
    815                     .getProperty(key), copyConf.getProperty(key));
    816         }
    817     }
    818
    819     /***
    820      * A dummy layout implementation for checking whether certain methods are
    821      * correctly called by the configuration.
    822      */
    823     static class DummyLayout extends PropertiesConfigurationLayout
    824     {
    825         /*** Stores the number how often load() was called. */
    826         public int loadCalls;
    827
    828         public DummyLayout(PropertiesConfiguration config)
    829         {
    830             super(config);
    831         }
    832
    833         public void load(Reader in) throws ConfigurationException
    834         {
    835             loadCalls++;
    836         }
    837     }
    838
    839     /***
    840      * A mock implementation of a HttpURLConnection used for testing saving to
    841      * a HTTP server.
    842      */
    843     static class MockHttpURLConnection extends HttpURLConnection
    844     {
    845         /*** The response code to return.*/
    846         private int responseCode;
    847
    848         /*** The output file. The output stream will point to this file.*/
    849         private File outputFile;
    850
    851         protected MockHttpURLConnection(URL u, int respCode, File outFile)
    852         {
    853             super(u);
    854             responseCode = respCode;
    855             outputFile = outFile;
    856         }
    857
    858         public void disconnect()
    859         {
    860         }
    861
    862         public boolean usingProxy()
    863         {
    864             return false;
    865         }
    866
    867         public void connect() throws IOException
    868         {
    869         }
    870
    871         public int getResponseCode() throws IOException
    872         {
    873             return responseCode;
    874         }
    875
    876         public OutputStream getOutputStream() throws IOException
    877         {
    878             return new FileOutputStream(outputFile);
    879         }
    880     }
    881
    882     /***
    883      * A mock stream handler for working with the mock HttpURLConnection.
    884      */
    885     static class MockHttpURLStreamHandler extends URLStreamHandler
    886     {
    887         /*** Stores the response code.*/
    888         private int responseCode;
    889
    890         /*** Stores the output file.*/
    891         private File outputFile;
    892
    893         /*** Stores the connection.*/
    894         private MockHttpURLConnection connection;
    895
    896         public MockHttpURLStreamHandler(int respCode, File outFile)
    897         {
    898             responseCode = respCode;
    899             outputFile = outFile;
    900         }
    901
    902         public MockHttpURLConnection getMockConnection()
    903         {
    904             return connection;
    905         }
    906
    907         protected URLConnection openConnection(URL u) throws IOException
    908         {
    909             connection = new MockHttpURLConnection(u, responseCode, outputFile);
    910             return connection;
    911         }
    912     }
    913 }