    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with this
    4    * work for additional information regarding copyright ownership. The ASF
    5    * licenses this file to You under the Apache License, Version 2.0 (the
    6    * "License"); you may not use this file except in compliance with the License.
    7    * 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, WITHOUT
    13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    14   * License for the specific language governing permissions and limitations under
    15   * the License.
    16   */
    17
    18  package org.apache.commons.configuration;
    19
    20  import java.io.File;
    21  import java.io.FileWriter;
    22  import java.io.IOException;
    23  import java.io.PrintWriter;
    24  import java.io.StringReader;
    25  import java.io.StringWriter;
    26  import java.io.Writer;
    27  import java.util.HashSet;
    28  import java.util.Iterator;
    29  import java.util.Set;
    30
    31  import junit.framework.TestCase;
    32
    33  /***
    34   * Test class for HierarchicalINIConfiguration.
    35   *
    36   * @author <a
    37   *         href="http://commons.apache.org/configuration/team-list.html">Commons
    38   *         Configuration team</a>
    39   * @version $Id: TestHierarchicalINIConfiguration.java 719869 2008-11-22 16:57:36Z oheger $
    40   */
    41  public class TestHierarchicalINIConfiguration extends TestCase
    42  {
    43      private static String LINE_SEPARATOR = System.getProperty("line.separator");
    44
    45      /*** Constant for the content of an ini file. */
    46      private static final String INI_DATA = "[section1]" + LINE_SEPARATOR
    47              + "var1 = foo" + LINE_SEPARATOR + "var2 = 451" + LINE_SEPARATOR
    48              + LINE_SEPARATOR + "[section2]" + LINE_SEPARATOR + "var1 = 123.45"
    49              + LINE_SEPARATOR + "var2 = bar" + LINE_SEPARATOR + LINE_SEPARATOR
    50              + "[section3]" + LINE_SEPARATOR + "var1 = true" + LINE_SEPARATOR
    51              + "interpolated = ${section3.var1}" + LINE_SEPARATOR
    52              + "multi = foo" + LINE_SEPARATOR + "multi = bar" + LINE_SEPARATOR
    53              + LINE_SEPARATOR;
    54
    55      private static final String INI_DATA2 = "[section4]" + LINE_SEPARATOR
    56              + "var1 = \"quoted value\"" + LINE_SEPARATOR
    57              + "var2 = \"quoted value//nwith //\"quotes//\"\"" + LINE_SEPARATOR
    58              + "var3 = 123 ; comment" + LINE_SEPARATOR
    59              + "var4 = \"1;2;3\" ; comment" + LINE_SEPARATOR
    60              + "var5 = '//'quoted//' \"value\"' ; comment" + LINE_SEPARATOR
    61              + "var6 = \"\"" + LINE_SEPARATOR;
    62
    63      private static final String INI_DATA3 = "[section5]" + LINE_SEPARATOR
    64              + "multiLine = one //" + LINE_SEPARATOR
    65              + "    two      //" + LINE_SEPARATOR
    66              + " three" + LINE_SEPARATOR
    67              + "singleLine = C://Temp//" + LINE_SEPARATOR
    68              + "multiQuoted = one //" + LINE_SEPARATOR
    69              + "\"  two  \" //" + LINE_SEPARATOR
    70              + "  three" + LINE_SEPARATOR
    71              + "multiComment = one // ; a comment" + LINE_SEPARATOR
    72              + "two" + LINE_SEPARATOR
    73              + "multiQuotedComment = \" one \" // ; comment" + LINE_SEPARATOR
    74              + "two" + LINE_SEPARATOR
    75              + "noFirstLine = //" + LINE_SEPARATOR
    76              + "  line 2" + LINE_SEPARATOR
    77              + "continueNoLine = one //" + LINE_SEPARATOR;
    78
    79      /*** An ini file with a global section. */
    80      private static final String INI_DATA_GLOBAL = "globalVar = testGlobal"
    81              + LINE_SEPARATOR + LINE_SEPARATOR + INI_DATA;
    82
    83      /*** A test ini file. */
    84      private static final File TEST_FILE = new File("target/test.ini");
    85
    86      protected void tearDown() throws Exception
    87      {
    88          if (TEST_FILE.exists())
    89          {
    90              assertTrue("Cannot remove test file: " + TEST_FILE, TEST_FILE
    91                      .delete());
    92          }
    93
    94          super.tearDown();
    95      }
    96
    97      /***
    98       * Creates a HierarchicalINIConfiguration object that is initialized from
    99       * the given data.
    100      *
    101      * @param data the data of the configuration (an ini file as string)
    102      * @return the initialized configuration
    103      * @throws ConfigurationException if an error occurs
    104      */
    105     private static HierarchicalINIConfiguration setUpConfig(String data)
    106             throws ConfigurationException
    107     {
    108         StringReader reader = new StringReader(data);
    109         HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
    110         instance.load(reader);
    111         reader.close();
    112         return instance;
    113     }
    114
    115     /***
    116      * Writes a test ini file.
    117      *
    118      * @param content the content of the file
    119      * @throws IOException if an error occurs
    120      */
    121     private static void writeTestFile(String content) throws IOException
    122     {
    123         PrintWriter out = new PrintWriter(new FileWriter(TEST_FILE));
    124         try
    125         {
    126             out.println(content);
    127         }
    128         finally
    129         {
    130             out.close();
    131         }
    132     }
    133
    134     /***
    135      * Test of save method, of class {@link HierarchicalINIConfiguration}.
    136      */
    137     public void testSave() throws Exception
    138     {
    139         Writer writer = new StringWriter();
    140         HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
    141         instance.addProperty("section1.var1", "foo");
    142         instance.addProperty("section1.var2", "451");
    143         instance.addProperty("section2.var1", "123.45");
    144         instance.addProperty("section2.var2", "bar");
    145         instance.addProperty("section3.var1", "true");
    146         instance.addProperty("section3.interpolated", "${section3.var1}");
    147         instance.addProperty("section3.multi", "foo");
    148         instance.addProperty("section3.multi", "bar");
    149         instance.save(writer);
    150
    151         assertEquals("Wrong content of ini file", INI_DATA, writer.toString());
    152     }
    153
    154     /***
    155      * Tests saving a configuration that contains a global section.
    156      */
    157     public void testSaveWithGlobalSection() throws ConfigurationException
    158     {
    159         HierarchicalINIConfiguration config = setUpConfig(INI_DATA_GLOBAL);
    160         StringWriter writer = new StringWriter();
    161         config.save(writer);
    162         assertEquals("Wrong content of ini file", INI_DATA_GLOBAL, writer
    163                 .toString());
    164     }
    165
    166     /***
    167      * Test of load method, of class {@link HierarchicalINIConfiguration}.
    168      */
    169     public void testLoad() throws Exception
    170     {
    171         checkLoad(INI_DATA);
    172     }
    173
    174     /***
    175      * Tests the load() method when the alternative value separator is used (a
    176      * ':' for '=').
    177      */
    178     public void testLoadAlternativeSeparator() throws Exception
    179     {
    180         checkLoad(INI_DATA.replace('=', ':'));
    181     }
    182
    183     /***
    184      * Tests loading a configuration from a File.
    185      */
    186     public void testLoadFile() throws ConfigurationException, IOException
    187     {
    188         writeTestFile(INI_DATA);
    189         HierarchicalINIConfiguration config = new HierarchicalINIConfiguration(
    190                 TEST_FILE);
    191         checkContent(config);
    192     }
    193
    194     /***
    195      * Tests loading a configuration from a file name.
    196      */
    197     public void testLoadFileName() throws ConfigurationException, IOException
    198     {
    199         writeTestFile(INI_DATA);
    200         HierarchicalINIConfiguration config = new HierarchicalINIConfiguration(
    201                 TEST_FILE.getAbsolutePath());
    202         checkContent(config);
    203     }
    204
    205     /***
    206      * Tests loading a configuration from a URL.
    207      */
    208     public void testLoadURL() throws ConfigurationException, IOException
    209     {
    210         writeTestFile(INI_DATA);
    211         HierarchicalINIConfiguration config = new HierarchicalINIConfiguration(
    212                 TEST_FILE.toURL());
    213         checkContent(config);
    214     }
    215
    216     /***
    217      * Tests the values of some properties to ensure that the configuration was
    218      * correctly loaded.
    219      *
    220      * @param instance the configuration to check
    221      */
    222     private void checkContent(HierarchicalINIConfiguration instance)
    223     {
    224         assertTrue(instance.getString("section1.var1").equals("foo"));
    225         assertTrue(instance.getInt("section1.var2") == 451);
    226         assertTrue(instance.getDouble("section2.var1") == 123.45);
    227         assertTrue(instance.getString("section2.var2").equals("bar"));
    228         assertTrue(instance.getBoolean("section3.var1"));
    229         assertTrue(instance.getSections().size() == 3);
    230     }
    231
    232     /***
    233      * Helper method for testing the load operation. Loads the specified content
    234      * into a configuration and then checks some properties.
    235      *
    236      * @param data the data to load
    237      */
    238     private void checkLoad(String data) throws ConfigurationException
    239     {
    240         HierarchicalINIConfiguration instance = setUpConfig(data);
    241         checkContent(instance);
    242     }
    243
    244     /***
    245      * Test of isCommentLine method, of class
    246      * {@link HierarchicalINIConfiguration}.
    247      */
    248     public void testIsCommentLine()
    249     {
    250         HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
    251         assertTrue(instance.isCommentLine("#comment1"));
    252         assertTrue(instance.isCommentLine(";comment1"));
    253         assertFalse(instance.isCommentLine("nocomment=true"));
    254         assertFalse(instance.isCommentLine(null));
    255     }
    256
    257     /***
    258      * Test of isSectionLine method, of class
    259      * {@link HierarchicalINIConfiguration}.
    260      */
    261     public void testIsSectionLine()
    262     {
    263         HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
    264         assertTrue(instance.isSectionLine("[section]"));
    265         assertFalse(instance.isSectionLine("nosection=true"));
    266         assertFalse(instance.isSectionLine(null));
    267     }
    268
    269     /***
    270      * Test of getSections method, of class {@link HierarchicalINIConfiguration}
    271      * .
    272      */
    273     public void testGetSections()
    274     {
    275         HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
    276         instance.addProperty("test1.foo", "bar");
    277         instance.addProperty("test2.foo", "abc");
    278         Set expResult = new HashSet();
    279         expResult.add("test1");
    280         expResult.add("test2");
    281         Set result = instance.getSections();
    282         assertEquals(expResult, result);
    283     }
    284
    285     public void testQuotedValue() throws Exception
    286     {
    287         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
    288         assertEquals("value", "quoted value", config.getString("section4.var1"));
    289     }
    290
    291     public void testQuotedValueWithQuotes() throws Exception
    292     {
    293         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
    294         assertEquals("value", "quoted value//nwith \"quotes\"", config
    295                 .getString("section4.var2"));
    296     }
    297
    298     public void testValueWithComment() throws Exception
    299     {
    300         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
    301         assertEquals("value", "123", config.getString("section4.var3"));
    302     }
    303
    304     public void testQuotedValueWithComment() throws Exception
    305     {
    306         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
    307         assertEquals("value", "1;2;3", config.getString("section4.var4"));
    308     }
    309
    310     public void testQuotedValueWithSingleQuotes() throws Exception
    311     {
    312         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
    313         assertEquals("value", "'quoted' \"value\"", config
    314                 .getString("section4.var5"));
    315     }
    316
    317     public void testWriteValueWithCommentChar() throws Exception
    318     {
    319         HierarchicalINIConfiguration config = new HierarchicalINIConfiguration();
    320         config.setProperty("section.key1", "1;2;3");
    321
    322         StringWriter writer = new StringWriter();
    323         config.save(writer);
    324
    325         HierarchicalINIConfiguration config2 = new HierarchicalINIConfiguration();
    326         config2.load(new StringReader(writer.toString()));
    327
    328         assertEquals("value", "1;2;3", config2.getString("section.key1"));
    329     }
    330
    331     /***
    332      * Tests whether whitespace is left unchanged for quoted values.
    333      */
    334     public void testQuotedValueWithWhitespace() throws Exception
    335     {
    336         final String content = "CmdPrompt = \" [test@cmd ~]$ \"";
    337         HierarchicalINIConfiguration config = setUpConfig(content);
    338         assertEquals("Wrong propert value", " [test@cmd ~]$ ", config
    339                 .getString("CmdPrompt"));
    340     }
    341
    342     /***
    343      * Tests a quoted value with space and a comment.
    344      */
    345     public void testQuotedValueWithWhitespaceAndComment() throws Exception
    346     {
    347         final String content = "CmdPrompt = \" [test@cmd ~]$ \" ; a comment";
    348         HierarchicalINIConfiguration config = setUpConfig(content);
    349         assertEquals("Wrong propert value", " [test@cmd ~]$ ", config
    350                 .getString("CmdPrompt"));
    351     }
    352
    353     /***
    354      * Tests an empty quoted value.
    355      */
    356     public void testQuotedValueEmpty() throws ConfigurationException
    357     {
    358         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
    359         assertEquals("Wrong value for empty property", "", config
    360                 .getString("section4.var6"));
    361     }
    362
    363     /***
    364      * Tests a property that has no value.
    365      */
    366     public void testGetPropertyNoValue() throws ConfigurationException
    367     {
    368         final String data = INI_DATA2 + LINE_SEPARATOR + "noValue ="
    369                 + LINE_SEPARATOR;
    370         HierarchicalINIConfiguration config = setUpConfig(data);
    371         assertEquals("Wrong value of key", "", config
    372                 .getString("section4.noValue"));
    373     }
    374
    375     /***
    376      * Tests a property that has no key.
    377      */
    378     public void testGetPropertyNoKey() throws ConfigurationException
    379     {
    380         final String data = INI_DATA2 + LINE_SEPARATOR + "= noKey"
    381                 + LINE_SEPARATOR;
    382         HierarchicalINIConfiguration config = setUpConfig(data);
    383         assertEquals("Cannot find property with no key", "noKey", config
    384                 .getString("section4. "));
    385     }
    386
    387     /***
    388      * Tests reading a property from the global section.
    389      */
    390     public void testGlobalProperty() throws ConfigurationException
    391     {
    392         HierarchicalINIConfiguration config = setUpConfig(INI_DATA_GLOBAL);
    393         assertEquals("Wrong value of global property", "testGlobal", config
    394                 .getString("globalVar"));
    395     }
    396
    397     /***
    398      * Tests whether the specified configuration contains exactly the expected
    399      * sections.
    400      *
    401      * @param config the configuration to check
    402      * @param expected an array with the expected sections
    403      */
    404     private void checkSectionNames(HierarchicalINIConfiguration config,
    405             String[] expected)
    406     {
    407         Set sectionNames = config.getSections();
    408         Iterator it = sectionNames.iterator();
    409         for (int idx = 0; idx < expected.length; idx++)
    410         {
    411             assertEquals("Wrong section at " + idx, expected[idx], it.next());
    412         }
    413         assertFalse("Too many sections", it.hasNext());
    414     }
    415
    416     /***
    417      * Tests the names of the sections returned by the configuration.
    418      *
    419      * @param data the data of the ini configuration
    420      * @param expected the expected section names
    421      * @return the configuration instance
    422      */
    423     private HierarchicalINIConfiguration checkSectionNames(String data,
    424             String[] expected) throws ConfigurationException
    425     {
    426         HierarchicalINIConfiguration config = setUpConfig(data);
    427         checkSectionNames(config, expected);
    428         return config;
    429     }
    430
    431     /***
    432      * Tests querying the sections if a global section if available.
    433      */
    434     public void testGetSectionsWithGlobal() throws ConfigurationException
    435     {
    436         checkSectionNames(INI_DATA_GLOBAL, new String[] {
    437                 null, "section1", "section2", "section3"
    438         });
    439     }
    440
    441     /***
    442      * Tests querying the sections if there is no global section.
    443      */
    444     public void testGetSectionsNoGlobal() throws ConfigurationException
    445     {
    446         checkSectionNames(INI_DATA, new String[] {
    447                 "section1", "section2", "section3"
    448         });
    449     }
    450
    451     /***
    452      * Tests whether variables containing a dot are not misinterpreted as
    453      * sections. This test is related to CONFIGURATION-327.
    454      */
    455     public void testGetSectionsDottedVar() throws ConfigurationException
    456     {
    457         final String data = "dotted.var = 1" + LINE_SEPARATOR + INI_DATA_GLOBAL;
    458         HierarchicalINIConfiguration config = checkSectionNames(data,
    459                 new String[] {
    460                         null, "section1", "section2", "section3"
    461                 });
    462         assertEquals("Wrong value of dotted variable", 1, config
    463                 .getInt("dotted..var"));
    464     }
    465
    466     /***
    467      * Tests whether a section added later is also found by getSections().
    468      */
    469     public void testGetSectionsAdded() throws ConfigurationException
    470     {
    471         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
    472         config.addProperty("section5.test", Boolean.TRUE);
    473         checkSectionNames(config, new String[] {
    474                 "section4", "section5"
    475         });
    476     }
    477
    478     /***
    479      * Tests querying the properties of an existing section.
    480      */
    481     public void testGetSectionExisting() throws ConfigurationException
    482     {
    483         HierarchicalINIConfiguration config = setUpConfig(INI_DATA);
    484         SubnodeConfiguration section = config.getSection("section1");
    485         assertEquals("Wrong value of var1", "foo", section.getString("var1"));
    486         assertEquals("Wrong value of var2", "451", section.getString("var2"));
    487     }
    488
    489     /***
    490      * Tests querying the properties of a section that was merged from two
    491      * sections with the same name.
    492      */
    493     public void testGetSectionMerged() throws ConfigurationException
    494     {
    495         final String data = INI_DATA + "[section1]" + LINE_SEPARATOR
    496                 + "var3 = merged" + LINE_SEPARATOR;
    497         HierarchicalINIConfiguration config = setUpConfig(data);
    498         SubnodeConfiguration section = config.getSection("section1");
    499         assertEquals("Wrong value of var1", "foo", section.getString("var1"));
    500         assertEquals("Wrong value of var2", "451", section.getString("var2"));
    501         assertEquals("Wrong value of var3", "merged", section.getString("var3"));
    502     }
    503
    504     /***
    505      * Tests querying the content of the global section.
    506      */
    507     public void testGetSectionGlobal() throws ConfigurationException
    508     {
    509         HierarchicalINIConfiguration config = setUpConfig(INI_DATA_GLOBAL);
    510         SubnodeConfiguration section = config.getSection(null);
    511         assertEquals("Wrong value of global variable", "testGlobal", section
    512                 .getString("globalVar"));
    513     }
    514
    515     /***
    516      * Tests querying the content of the global section if there is none.
    517      */
    518     public void testGetSectionGlobalNonExisting() throws ConfigurationException
    519     {
    520         HierarchicalINIConfiguration config = setUpConfig(INI_DATA);
    521         SubnodeConfiguration section = config.getSection(null);
    522         assertTrue("Sub config not empty", section.isEmpty());
    523     }
    524
    525     /***
    526      * Tests querying a non existing section.
    527      */
    528     public void testGetSectionNonExisting() throws ConfigurationException
    529     {
    530         HierarchicalINIConfiguration config = setUpConfig(INI_DATA);
    531         SubnodeConfiguration section = config
    532                 .getSection("Non existing section");
    533         assertTrue("Sub config not empty", section.isEmpty());
    534     }
    535
    536     /***
    537      * Tests a property whose value spans multiple lines.
    538      */
    539     public void testLineContinuation() throws ConfigurationException
    540     {
    541         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
    542         assertEquals("Wrong value", "one" + LINE_SEPARATOR + "two"
    543                 + LINE_SEPARATOR + "three", config
    544                 .getString("section5.multiLine"));
    545     }
    546
    547     /***
    548      * Tests a property value that ends on a backslash, which is no line
    549      * continuation character.
    550      */
    551     public void testLineContinuationNone() throws ConfigurationException
    552     {
    553         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
    554         assertEquals("Wrong value", "C://Temp//", config
    555                 .getString("section5.singleLine"));
    556     }
    557
    558     /***
    559      * Tests a property whose value spans multiple lines when quoting is
    560      * involved. In this case whitespace must not be trimmed.
    561      */
    562     public void testLineContinuationQuoted() throws ConfigurationException
    563     {
    564         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
    565         assertEquals("Wrong value", "one" + LINE_SEPARATOR + "  two  "
    566                 + LINE_SEPARATOR + "three", config
    567                 .getString("section5.multiQuoted"));
    568     }
    569
    570     /***
    571      * Tests a property whose value spans multiple lines with a comment.
    572      */
    573     public void testLineContinuationComment() throws ConfigurationException
    574     {
    575         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
    576         assertEquals("Wrong value", "one" + LINE_SEPARATOR + "two", config
    577                 .getString("section5.multiComment"));
    578     }
    579
    580     /***
    581      * Tests a property with a quoted value spanning multiple lines and a
    582      * comment.
    583      */
    584     public void testLineContinuationQuotedComment()
    585             throws ConfigurationException
    586     {
    587         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
    588         assertEquals("Wrong value", " one " + LINE_SEPARATOR + "two", config
    589                 .getString("section5.multiQuotedComment"));
    590     }
    591
    592     /***
    593      * Tests a multi-line property value with an empty line.
    594      */
    595     public void testLineContinuationEmptyLine() throws ConfigurationException
    596     {
    597         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
    598         assertEquals("Wrong value", LINE_SEPARATOR + "line 2", config
    599                 .getString("section5.noFirstLine"));
    600     }
    601
    602     /***
    603      * Tests a line continuation at the end of the file.
    604      */
    605     public void testLineContinuationAtEnd() throws ConfigurationException
    606     {
    607         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
    608         assertEquals("Wrong value", "one" + LINE_SEPARATOR, config
    609                 .getString("section5.continueNoLine"));
    610     }
    611 }