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