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 }