View Javadoc

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 static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertFalse;
22  import static org.junit.Assert.assertTrue;
23  
24  import java.io.File;
25  import java.io.FileWriter;
26  import java.io.IOException;
27  import java.io.PrintWriter;
28  import java.io.StringReader;
29  import java.io.StringWriter;
30  import java.io.Writer;
31  import java.util.HashSet;
32  import java.util.Iterator;
33  import java.util.List;
34  import java.util.Set;
35  
36  import org.junit.After;
37  import org.junit.Test;
38  
39  /**
40   * Test class for HierarchicalINIConfiguration.
41   *
42   * @author <a
43   *         href="http://commons.apache.org/configuration/team-list.html">Commons
44   *         Configuration team</a>
45   * @version $Id: TestHierarchicalINIConfiguration.java 1234362 2012-01-21 16:59:48Z oheger $
46   */
47  public class TestHierarchicalINIConfiguration
48  {
49      private static String LINE_SEPARATOR = System.getProperty("line.separator");
50  
51      /** Constant for the content of an ini file. */
52      private static final String INI_DATA = "[section1]" + LINE_SEPARATOR
53              + "var1 = foo" + LINE_SEPARATOR + "var2 = 451" + LINE_SEPARATOR
54              + LINE_SEPARATOR + "[section2]" + LINE_SEPARATOR + "var1 = 123.45"
55              + LINE_SEPARATOR + "var2 = bar" + LINE_SEPARATOR + LINE_SEPARATOR
56              + "[section3]" + LINE_SEPARATOR + "var1 = true" + LINE_SEPARATOR
57              + "interpolated = ${section3.var1}" + LINE_SEPARATOR
58              + "multi = foo" + LINE_SEPARATOR + "multi = bar" + LINE_SEPARATOR
59              + LINE_SEPARATOR;
60  
61      private static final String INI_DATA2 = "[section4]" + LINE_SEPARATOR
62              + "var1 = \"quoted value\"" + LINE_SEPARATOR
63              + "var2 = \"quoted value\\nwith \\\"quotes\\\"\"" + LINE_SEPARATOR
64              + "var3 = 123 ; comment" + LINE_SEPARATOR
65              + "var4 = \"1;2;3\" ; comment" + LINE_SEPARATOR
66              + "var5 = '\\'quoted\\' \"value\"' ; comment" + LINE_SEPARATOR
67              + "var6 = \"\"" + LINE_SEPARATOR;
68  
69      private static final String INI_DATA3 = "[section5]" + LINE_SEPARATOR
70              + "multiLine = one \\" + LINE_SEPARATOR
71              + "    two      \\" + LINE_SEPARATOR
72              + " three" + LINE_SEPARATOR
73              + "singleLine = C:\\Temp\\" + LINE_SEPARATOR
74              + "multiQuoted = one \\" + LINE_SEPARATOR
75              + "\"  two  \" \\" + LINE_SEPARATOR
76              + "  three" + LINE_SEPARATOR
77              + "multiComment = one \\ ; a comment" + LINE_SEPARATOR
78              + "two" + LINE_SEPARATOR
79              + "multiQuotedComment = \" one \" \\ ; comment" + LINE_SEPARATOR
80              + "two" + LINE_SEPARATOR
81              + "noFirstLine = \\" + LINE_SEPARATOR
82              + "  line 2" + LINE_SEPARATOR
83              + "continueNoLine = one \\" + LINE_SEPARATOR;
84  
85      private static final String INI_DATA_SEPARATORS = "[section]"
86              + LINE_SEPARATOR + "var1 = value1" + LINE_SEPARATOR
87              + "var2 : value2" + LINE_SEPARATOR
88              + "var3=value3" + LINE_SEPARATOR
89              + "var4:value4" + LINE_SEPARATOR
90              + "var5 : value=5" + LINE_SEPARATOR
91              + "var:6=value" + LINE_SEPARATOR
92              + "var:7=\"value7\"" + LINE_SEPARATOR
93              + "var:8 =  \"value8\"" + LINE_SEPARATOR;
94  
95      /** An ini file that contains only a property in the global section. */
96      private static final String INI_DATA_GLOBAL_ONLY = "globalVar = testGlobal"
97              + LINE_SEPARATOR + LINE_SEPARATOR;
98  
99      /** An ini file with a global section. */
100     private static final String INI_DATA_GLOBAL = INI_DATA_GLOBAL_ONLY
101             + INI_DATA;
102 
103     /** A test ini file. */
104     private static final File TEST_FILE = new File("target/test.ini");
105 
106     @After
107     public void tearDown() throws Exception
108     {
109         if (TEST_FILE.exists())
110         {
111             assertTrue("Cannot remove test file: " + TEST_FILE, TEST_FILE
112                     .delete());
113         }
114     }
115 
116     /**
117      * Creates a HierarchicalINIConfiguration object that is initialized from
118      * the given data.
119      *
120      * @param data the data of the configuration (an ini file as string)
121      * @return the initialized configuration
122      * @throws ConfigurationException if an error occurs
123      */
124     private static HierarchicalINIConfiguration setUpConfig(String data)
125             throws ConfigurationException
126     {
127         HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
128         load(instance, data);
129         return instance;
130     }
131 
132     /**
133      * Loads the specified content into the given configuration instance.
134      *
135      * @param instance the configuration
136      * @param data the data to be loaded
137      * @throws ConfigurationException if an error occurs
138      */
139     private static void load(HierarchicalINIConfiguration instance, String data)
140             throws ConfigurationException
141     {
142         StringReader reader = new StringReader(data);
143         instance.load(reader);
144         reader.close();
145     }
146 
147     /**
148      * Writes a test ini file.
149      *
150      * @param content the content of the file
151      * @throws IOException if an error occurs
152      */
153     private static void writeTestFile(String content) throws IOException
154     {
155         PrintWriter out = new PrintWriter(new FileWriter(TEST_FILE));
156         try
157         {
158             out.println(content);
159         }
160         finally
161         {
162             out.close();
163         }
164     }
165 
166     /**
167      * Test of save method, of class {@link HierarchicalINIConfiguration}.
168      */
169     @Test
170     public void testSave() throws Exception
171     {
172         Writer writer = new StringWriter();
173         HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
174         instance.addProperty("section1.var1", "foo");
175         instance.addProperty("section1.var2", "451");
176         instance.addProperty("section2.var1", "123.45");
177         instance.addProperty("section2.var2", "bar");
178         instance.addProperty("section3.var1", "true");
179         instance.addProperty("section3.interpolated", "${section3.var1}");
180         instance.addProperty("section3.multi", "foo");
181         instance.addProperty("section3.multi", "bar");
182         instance.save(writer);
183 
184         assertEquals("Wrong content of ini file", INI_DATA, writer.toString());
185     }
186 
187     /**
188      * Helper method for testing a save operation. This method constructs a
189      * configuration from the specified content string. Then it saves this
190      * configuration and checks whether the result matches the original content.
191      *
192      * @param content the content of the configuration
193      * @throws ConfigurationException if an error occurs
194      */
195     private void checkSave(String content) throws ConfigurationException
196     {
197         HierarchicalINIConfiguration config = setUpConfig(content);
198         StringWriter writer = new StringWriter();
199         config.save(writer);
200         assertEquals("Wrong content of ini file", content, writer.toString());
201     }
202 
203     /**
204      * Tests saving a configuration that contains a global section.
205      */
206     @Test
207     public void testSaveWithGlobalSection() throws ConfigurationException
208     {
209         checkSave(INI_DATA_GLOBAL);
210     }
211 
212     /**
213      * Tests whether a configuration that contains only a global section can be
214      * saved correctly.
215      */
216     @Test
217     public void testSaveWithOnlyGlobalSection() throws ConfigurationException
218     {
219         checkSave(INI_DATA_GLOBAL_ONLY);
220     }
221 
222     /**
223      * Test of load method, of class {@link HierarchicalINIConfiguration}.
224      */
225     @Test
226     public void testLoad() throws Exception
227     {
228         checkLoad(INI_DATA);
229     }
230 
231     /**
232      * Tests the load() method when the alternative value separator is used (a
233      * ':' for '=').
234      */
235     @Test
236     public void testLoadAlternativeSeparator() throws Exception
237     {
238         checkLoad(INI_DATA.replace('=', ':'));
239     }
240 
241     /**
242      * Tests loading a configuration from a File.
243      */
244     @Test
245     public void testLoadFile() throws ConfigurationException, IOException
246     {
247         writeTestFile(INI_DATA);
248         HierarchicalINIConfiguration config = new HierarchicalINIConfiguration(
249                 TEST_FILE);
250         checkContent(config);
251     }
252 
253     /**
254      * Tests loading a configuration from a file name.
255      */
256     @Test
257     public void testLoadFileName() throws ConfigurationException, IOException
258     {
259         writeTestFile(INI_DATA);
260         HierarchicalINIConfiguration config = new HierarchicalINIConfiguration(
261                 TEST_FILE.getAbsolutePath());
262         checkContent(config);
263     }
264 
265     /**
266      * Tests loading a configuration from a URL.
267      */
268     @Test
269     public void testLoadURL() throws ConfigurationException, IOException
270     {
271         writeTestFile(INI_DATA);
272         HierarchicalINIConfiguration config = new HierarchicalINIConfiguration(
273                 TEST_FILE.toURI().toURL());
274         checkContent(config);
275     }
276 
277     /**
278      * Tests the values of some properties to ensure that the configuration was
279      * correctly loaded.
280      *
281      * @param instance the configuration to check
282      */
283     private void checkContent(HierarchicalINIConfiguration instance)
284     {
285         assertTrue(instance.getString("section1.var1").equals("foo"));
286         assertTrue(instance.getInt("section1.var2") == 451);
287         assertTrue(instance.getDouble("section2.var1") == 123.45);
288         assertTrue(instance.getString("section2.var2").equals("bar"));
289         assertTrue(instance.getBoolean("section3.var1"));
290         assertTrue(instance.getSections().size() == 3);
291     }
292 
293     /**
294      * Helper method for testing the load operation. Loads the specified content
295      * into a configuration and then checks some properties.
296      *
297      * @param data the data to load
298      */
299     private void checkLoad(String data) throws ConfigurationException
300     {
301         HierarchicalINIConfiguration instance = setUpConfig(data);
302         checkContent(instance);
303     }
304 
305     /**
306      * Test of isCommentLine method, of class
307      * {@link HierarchicalINIConfiguration}.
308      */
309     @Test
310     public void testIsCommentLine()
311     {
312         HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
313         assertTrue(instance.isCommentLine("#comment1"));
314         assertTrue(instance.isCommentLine(";comment1"));
315         assertFalse(instance.isCommentLine("nocomment=true"));
316         assertFalse(instance.isCommentLine(null));
317     }
318 
319     /**
320      * Test of isSectionLine method, of class
321      * {@link HierarchicalINIConfiguration}.
322      */
323     @Test
324     public void testIsSectionLine()
325     {
326         HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
327         assertTrue(instance.isSectionLine("[section]"));
328         assertFalse(instance.isSectionLine("nosection=true"));
329         assertFalse(instance.isSectionLine(null));
330     }
331 
332     /**
333      * Test of getSections method, of class {@link HierarchicalINIConfiguration}
334      * .
335      */
336     @Test
337     public void testGetSections()
338     {
339         HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
340         instance.addProperty("test1.foo", "bar");
341         instance.addProperty("test2.foo", "abc");
342         Set<String> expResult = new HashSet<String>();
343         expResult.add("test1");
344         expResult.add("test2");
345         Set<String> result = instance.getSections();
346         assertEquals(expResult, result);
347     }
348 
349     @Test
350     public void testQuotedValue() throws Exception
351     {
352         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
353         assertEquals("value", "quoted value", config.getString("section4.var1"));
354     }
355 
356     @Test
357     public void testQuotedValueWithQuotes() throws Exception
358     {
359         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
360         assertEquals("value", "quoted value\\nwith \"quotes\"", config
361                 .getString("section4.var2"));
362     }
363 
364     @Test
365     public void testValueWithComment() throws Exception
366     {
367         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
368         assertEquals("value", "123", config.getString("section4.var3"));
369     }
370 
371     @Test
372     public void testQuotedValueWithComment() throws Exception
373     {
374         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
375         assertEquals("value", "1;2;3", config.getString("section4.var4"));
376     }
377 
378     @Test
379     public void testQuotedValueWithSingleQuotes() throws Exception
380     {
381         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
382         assertEquals("value", "'quoted' \"value\"", config
383                 .getString("section4.var5"));
384     }
385 
386     @Test
387     public void testWriteValueWithCommentChar() throws Exception
388     {
389         HierarchicalINIConfiguration config = new HierarchicalINIConfiguration();
390         config.setProperty("section.key1", "1;2;3");
391 
392         StringWriter writer = new StringWriter();
393         config.save(writer);
394 
395         HierarchicalINIConfiguration config2 = new HierarchicalINIConfiguration();
396         config2.load(new StringReader(writer.toString()));
397 
398         assertEquals("value", "1;2;3", config2.getString("section.key1"));
399     }
400 
401     /**
402      * Tests whether whitespace is left unchanged for quoted values.
403      */
404     @Test
405     public void testQuotedValueWithWhitespace() throws Exception
406     {
407         final String content = "CmdPrompt = \" [test@cmd ~]$ \"";
408         HierarchicalINIConfiguration config = setUpConfig(content);
409         assertEquals("Wrong propert value", " [test@cmd ~]$ ", config
410                 .getString("CmdPrompt"));
411     }
412 
413     /**
414      * Tests a quoted value with space and a comment.
415      */
416     @Test
417     public void testQuotedValueWithWhitespaceAndComment() throws Exception
418     {
419         final String content = "CmdPrompt = \" [test@cmd ~]$ \" ; a comment";
420         HierarchicalINIConfiguration config = setUpConfig(content);
421         assertEquals("Wrong propert value", " [test@cmd ~]$ ", config
422                 .getString("CmdPrompt"));
423     }
424 
425     /**
426      * Tests an empty quoted value.
427      */
428     @Test
429     public void testQuotedValueEmpty() throws ConfigurationException
430     {
431         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
432         assertEquals("Wrong value for empty property", "", config
433                 .getString("section4.var6"));
434     }
435 
436     /**
437      * Tests a property that has no value.
438      */
439     @Test
440     public void testGetPropertyNoValue() throws ConfigurationException
441     {
442         final String data = INI_DATA2 + LINE_SEPARATOR + "noValue ="
443                 + LINE_SEPARATOR;
444         HierarchicalINIConfiguration config = setUpConfig(data);
445         assertEquals("Wrong value of key", "", config
446                 .getString("section4.noValue"));
447     }
448 
449     /**
450      * Tests a property that has no key.
451      */
452     @Test
453     public void testGetPropertyNoKey() throws ConfigurationException
454     {
455         final String data = INI_DATA2 + LINE_SEPARATOR + "= noKey"
456                 + LINE_SEPARATOR;
457         HierarchicalINIConfiguration config = setUpConfig(data);
458         assertEquals("Cannot find property with no key", "noKey", config
459                 .getString("section4. "));
460     }
461 
462     /**
463      * Tests reading a property from the global section.
464      */
465     @Test
466     public void testGlobalProperty() throws ConfigurationException
467     {
468         HierarchicalINIConfiguration config = setUpConfig(INI_DATA_GLOBAL);
469         assertEquals("Wrong value of global property", "testGlobal", config
470                 .getString("globalVar"));
471     }
472 
473     /**
474      * Tests whether the specified configuration contains exactly the expected
475      * sections.
476      *
477      * @param config the configuration to check
478      * @param expected an array with the expected sections
479      */
480     private void checkSectionNames(HierarchicalINIConfiguration config,
481             String[] expected)
482     {
483         Set<String> sectionNames = config.getSections();
484         Iterator<String> it = sectionNames.iterator();
485         for (int idx = 0; idx < expected.length; idx++)
486         {
487             assertEquals("Wrong section at " + idx, expected[idx], it.next());
488         }
489         assertFalse("Too many sections", it.hasNext());
490     }
491 
492     /**
493      * Tests the names of the sections returned by the configuration.
494      *
495      * @param data the data of the ini configuration
496      * @param expected the expected section names
497      * @return the configuration instance
498      */
499     private HierarchicalINIConfiguration checkSectionNames(String data,
500             String[] expected) throws ConfigurationException
501     {
502         HierarchicalINIConfiguration config = setUpConfig(data);
503         checkSectionNames(config, expected);
504         return config;
505     }
506 
507     /**
508      * Tests querying the sections if a global section if available.
509      */
510     @Test
511     public void testGetSectionsWithGlobal() throws ConfigurationException
512     {
513         checkSectionNames(INI_DATA_GLOBAL, new String[] {
514                 null, "section1", "section2", "section3"
515         });
516     }
517 
518     /**
519      * Tests querying the sections if there is no global section.
520      */
521     @Test
522     public void testGetSectionsNoGlobal() throws ConfigurationException
523     {
524         checkSectionNames(INI_DATA, new String[] {
525                 "section1", "section2", "section3"
526         });
527     }
528 
529     /**
530      * Tests whether the sections of a configuration can be queried that
531      * contains only a global section.
532      */
533     @Test
534     public void testGetSectionsGlobalOnly() throws ConfigurationException
535     {
536         checkSectionNames(INI_DATA_GLOBAL_ONLY, new String[] {
537             null
538         });
539     }
540 
541     /**
542      * Tests whether variables containing a dot are not misinterpreted as
543      * sections. This test is related to CONFIGURATION-327.
544      */
545     @Test
546     public void testGetSectionsDottedVar() throws ConfigurationException
547     {
548         final String data = "dotted.var = 1" + LINE_SEPARATOR + INI_DATA_GLOBAL;
549         HierarchicalINIConfiguration config = checkSectionNames(data,
550                 new String[] {
551                         null, "section1", "section2", "section3"
552                 });
553         assertEquals("Wrong value of dotted variable", 1, config
554                 .getInt("dotted..var"));
555     }
556 
557     /**
558      * Tests whether a section added later is also found by getSections().
559      */
560     @Test
561     public void testGetSectionsAdded() throws ConfigurationException
562     {
563         HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
564         config.addProperty("section5.test", Boolean.TRUE);
565         checkSectionNames(config, new String[] {
566                 "section4", "section5"
567         });
568     }
569 
570     /**
571      * Tests querying the properties of an existing section.
572      */
573     @Test
574     public void testGetSectionExisting() throws ConfigurationException
575     {
576         HierarchicalINIConfiguration config = setUpConfig(INI_DATA);
577         SubnodeConfiguration section = config.getSection("section1");
578         assertEquals("Wrong value of var1", "foo", section.getString("var1"));
579         assertEquals("Wrong value of var2", "451", section.getString("var2"));
580     }
581 
582     /**
583      * Tests querying the properties of a section that was merged from two
584      * sections with the same name.
585      */
586     @Test
587     public void testGetSectionMerged() throws ConfigurationException
588     {
589         final String data = INI_DATA + "[section1]" + LINE_SEPARATOR
590                 + "var3 = merged" + LINE_SEPARATOR;
591         HierarchicalINIConfiguration config = setUpConfig(data);
592         SubnodeConfiguration section = config.getSection("section1");
593         assertEquals("Wrong value of var1", "foo", section.getString("var1"));
594         assertEquals("Wrong value of var2", "451", section.getString("var2"));
595         assertEquals("Wrong value of var3", "merged", section.getString("var3"));
596     }
597 
598     /**
599      * Tests querying the content of the global section.
600      */
601     @Test
602     public void testGetSectionGlobal() throws ConfigurationException
603     {
604         HierarchicalINIConfiguration config = setUpConfig(INI_DATA_GLOBAL);
605         SubnodeConfiguration section = config.getSection(null);
606         assertEquals("Wrong value of global variable", "testGlobal", section
607                 .getString("globalVar"));
608     }
609 
610     /**
611      * Tests concurrent access to the global section.
612      */
613     @Test
614     public void testGetSectionGloabalMultiThreaded()
615             throws ConfigurationException, InterruptedException
616     {
617         HierarchicalINIConfiguration config = setUpConfig(INI_DATA_GLOBAL);
618         final int threadCount = 10;
619         GlobalSectionTestThread[] threads = new GlobalSectionTestThread[threadCount];
620         for (int i = 0; i < threadCount; i++)
621         {
622             threads[i] = new GlobalSectionTestThread(config);
623             threads[i].start();
624         }
625         for (int i = 0; i < threadCount; i++)
626         {
627             threads[i].join();
628             assertFalse("Exception occurred", threads[i].error);
629         }
630     }
631 
632     /**
633      * Tests querying the content of the global section if there is none.
634      */
635     @Test
636     public void testGetSectionGlobalNonExisting() throws ConfigurationException
637     {
638         HierarchicalINIConfiguration config = setUpConfig(INI_DATA);
639         SubnodeConfiguration section = config.getSection(null);
640         assertTrue("Sub config not empty", section.isEmpty());
641     }
642 
643     /**
644      * Tests querying a non existing section.
645      */
646     @Test
647     public void testGetSectionNonExisting() throws ConfigurationException
648     {
649         HierarchicalINIConfiguration config = setUpConfig(INI_DATA);
650         SubnodeConfiguration section = config
651                 .getSection("Non existing section");
652         assertTrue("Sub config not empty", section.isEmpty());
653     }
654 
655     /**
656      * Tests a property whose value spans multiple lines.
657      */
658     @Test
659     public void testLineContinuation() throws ConfigurationException
660     {
661         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
662         assertEquals("Wrong value", "one" + LINE_SEPARATOR + "two"
663                 + LINE_SEPARATOR + "three", config
664                 .getString("section5.multiLine"));
665     }
666 
667     /**
668      * Tests a property value that ends on a backslash, which is no line
669      * continuation character.
670      */
671     @Test
672     public void testLineContinuationNone() throws ConfigurationException
673     {
674         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
675         assertEquals("Wrong value", "C:\\Temp\\", config
676                 .getString("section5.singleLine"));
677     }
678 
679     /**
680      * Tests a property whose value spans multiple lines when quoting is
681      * involved. In this case whitespace must not be trimmed.
682      */
683     @Test
684     public void testLineContinuationQuoted() throws ConfigurationException
685     {
686         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
687         assertEquals("Wrong value", "one" + LINE_SEPARATOR + "  two  "
688                 + LINE_SEPARATOR + "three", config
689                 .getString("section5.multiQuoted"));
690     }
691 
692     /**
693      * Tests a property whose value spans multiple lines with a comment.
694      */
695     @Test
696     public void testLineContinuationComment() throws ConfigurationException
697     {
698         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
699         assertEquals("Wrong value", "one" + LINE_SEPARATOR + "two", config
700                 .getString("section5.multiComment"));
701     }
702 
703     /**
704      * Tests a property with a quoted value spanning multiple lines and a
705      * comment.
706      */
707     @Test
708     public void testLineContinuationQuotedComment()
709             throws ConfigurationException
710     {
711         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
712         assertEquals("Wrong value", " one " + LINE_SEPARATOR + "two", config
713                 .getString("section5.multiQuotedComment"));
714     }
715 
716     /**
717      * Tests a multi-line property value with an empty line.
718      */
719     @Test
720     public void testLineContinuationEmptyLine() throws ConfigurationException
721     {
722         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
723         assertEquals("Wrong value", LINE_SEPARATOR + "line 2", config
724                 .getString("section5.noFirstLine"));
725     }
726 
727     /**
728      * Tests a line continuation at the end of the file.
729      */
730     @Test
731     public void testLineContinuationAtEnd() throws ConfigurationException
732     {
733         HierarchicalINIConfiguration config = setUpConfig(INI_DATA3);
734         assertEquals("Wrong value", "one" + LINE_SEPARATOR, config
735                 .getString("section5.continueNoLine"));
736     }
737 
738     /**
739      * Tests whether a configuration can be saved that contains section keys
740      * with delimiter characters. This test is related to CONFIGURATION-409.
741      */
742     @Test
743     public void testSaveKeysWithDelimiters() throws ConfigurationException
744     {
745         HierarchicalINIConfiguration conf = new HierarchicalINIConfiguration();
746         final String section = "Section..with..dots";
747         conf.addProperty(section + ".test1", "test1");
748         conf.addProperty(section + ".test2", "test2");
749         conf.save(TEST_FILE);
750         conf = new HierarchicalINIConfiguration();
751         conf.load(TEST_FILE);
752         assertEquals("Wrong value (1)", "test1", conf.getString(section + ".test1"));
753         assertEquals("Wrong value (2)", "test2", conf.getString(section + ".test2"));
754     }
755 
756     /**
757      * Tests whether a value which contains a semicolon can be loaded
758      * successfully. This test is related to CONFIGURATION-434.
759      */
760     @Test
761     public void testValueWithSemicolon() throws ConfigurationException
762     {
763         final String path =
764                 "C:\\Program Files\\jar\\manage.jar;"
765                         + "C:\\Program Files\\jar\\guiLauncher.jar";
766         final String content =
767                 "[Environment]" + LINE_SEPARATOR + "Application Type=any"
768                         + LINE_SEPARATOR + "Class Path=" + path + "  ;comment"
769                         + LINE_SEPARATOR + "Path=" + path
770                         + "\t; another comment";
771         HierarchicalINIConfiguration config = setUpConfig(content);
772         assertEquals("Wrong class path", path,
773                 config.getString("Environment.Class Path"));
774         assertEquals("Wrong path", path, config.getString("Environment.Path"));
775     }
776 
777     /**
778      * Tests whether the different separators with or without whitespace are
779      * recognized.
780      */
781     @Test
782     public void testSeparators() throws ConfigurationException
783     {
784         HierarchicalINIConfiguration config = setUpConfig(INI_DATA_SEPARATORS);
785         for (int i = 1; i <= 4; i++)
786         {
787             assertEquals("Wrong value", "value" + i,
788                     config.getString("section.var" + i));
789         }
790     }
791 
792     /**
793      * Tests property definitions containing multiple separators.
794      */
795     @Test
796     public void testMultipleSeparators() throws ConfigurationException
797     {
798         HierarchicalINIConfiguration config = setUpConfig(INI_DATA_SEPARATORS);
799         assertEquals("Wrong value for var5", "value=5",
800                 config.getString("section.var5"));
801         assertEquals("Wrong value for var6", "6=value",
802                 config.getString("section.var"));
803     }
804 
805     /**
806      * Tests property definitions containing multiple separators that are
807      * quoted.
808      */
809     @Test
810     public void testMultipleSeparatorsQuoted() throws ConfigurationException
811     {
812         HierarchicalINIConfiguration config = setUpConfig(INI_DATA_SEPARATORS);
813         assertEquals("Wrong value for var7", "value7",
814                 config.getString("section.var:7"));
815         assertEquals("Wrong value for var8", "value8",
816                 config.getString("section.var:8"));
817     }
818 
819     /**
820      * Tests whether a section that has been cleared can be manipulated and
821      * saved later.
822      */
823     @Test
824     public void testSaveClearedSection() throws ConfigurationException
825     {
826         final String data = "[section]\ntest = failed\n";
827         HierarchicalINIConfiguration config = setUpConfig(data);
828         SubnodeConfiguration sub = config.getSection("section");
829         assertFalse("No content", sub.isEmpty());
830         sub.clear();
831         sub.setProperty("test", "success");
832         StringWriter writer = new StringWriter();
833         config.save(writer);
834         HierarchicalConfiguration config2 = setUpConfig(writer.toString());
835         assertEquals("Wrong value", "success",
836                 config2.getString("section.test"));
837     }
838 
839     /**
840      * Tests whether a duplicate session is merged.
841      */
842     @Test
843     public void testMergeDuplicateSection() throws ConfigurationException
844     {
845         final String data =
846                 "[section]\nvar1 = sec1\n\n" + "[section]\nvar2 = sec2\n";
847         HierarchicalINIConfiguration config = setUpConfig(data);
848         assertEquals("Wrong value 1", "sec1", config.getString("section.var1"));
849         assertEquals("Wrong value 2", "sec2", config.getString("section.var2"));
850         SubnodeConfiguration sub = config.getSection("section");
851         assertEquals("Wrong sub value 1", "sec1", sub.getString("var1"));
852         assertEquals("Wrong sub value 2", "sec2", sub.getString("var2"));
853         StringWriter writer = new StringWriter();
854         config.save(writer);
855         String content = writer.toString();
856         int pos = content.indexOf("[section]");
857         assertTrue("Section not found: " + content, pos >= 0);
858         assertTrue("Section found multiple times: " + content,
859                 content.indexOf("[section]", pos + 1) < 0);
860     }
861 
862     /**
863      * Tests whether a section that was created by getSection() can be
864      * manipulated.
865      */
866     @Test
867     public void testGetSectionNonExistingManipulate()
868             throws ConfigurationException
869     {
870         HierarchicalINIConfiguration config = setUpConfig(INI_DATA);
871         SubnodeConfiguration section = config.getSection("newSection");
872         section.addProperty("test", "success");
873         assertEquals("Main config not updated", "success",
874                 config.getString("newSection.test"));
875         StringWriter writer = new StringWriter();
876         config.save(writer);
877         HierarchicalINIConfiguration config2 = setUpConfig(writer.toString());
878         section = config2.getSection("newSection");
879         assertEquals("Wrong value", "success", section.getString("test"));
880     }
881 
882     /**
883      * Tests whether getSection() can deal with duplicate sections.
884      */
885     @Test
886     public void testGetSectionDuplicate()
887     {
888         HierarchicalINIConfiguration config =
889                 new HierarchicalINIConfiguration();
890         config.addProperty("section.var1", "value1");
891         config.addProperty("section(-1).var2", "value2");
892         SubnodeConfiguration section = config.getSection("section");
893         Iterator<String> keys = section.getKeys();
894         assertEquals("Wrong key", "var1", keys.next());
895         assertFalse("Too many keys", keys.hasNext());
896     }
897 
898     /**
899      * Tests whether the list delimiter character is recognized.
900      */
901     @Test
902     public void testValueWithDelimiters() throws ConfigurationException
903     {
904         HierarchicalINIConfiguration config =
905                 setUpConfig("[test]" + LINE_SEPARATOR + "list=1,2,3"
906                         + LINE_SEPARATOR);
907         List<Object> list = config.getList("test.list");
908         assertEquals("Wrong number of elements", 3, list.size());
909         assertEquals("Wrong element at 1", "1", list.get(0));
910         assertEquals("Wrong element at 2", "2", list.get(1));
911         assertEquals("Wrong element at 3", "3", list.get(2));
912     }
913 
914     /**
915      * Tests whether parsing of lists can be disabled.
916      */
917     @Test
918     public void testListParsingDisabled() throws ConfigurationException
919     {
920         HierarchicalINIConfiguration config =
921                 new HierarchicalINIConfiguration();
922         config.setDelimiterParsingDisabled(true);
923         load(config, "[test]" + LINE_SEPARATOR + "nolist=1,2,3");
924         assertEquals("Wrong value", "1,2,3", config.getString("test.nolist"));
925     }
926 
927     /**
928      * A thread class for testing concurrent access to the global section.
929      */
930     private static class GlobalSectionTestThread extends Thread
931     {
932         /** The configuration. */
933         private final HierarchicalINIConfiguration config;
934 
935         /** A flag whether an error was found. */
936         volatile boolean error;
937 
938         /**
939          * Creates a new instance of <code>GlobalSectionTestThread</code> and
940          * initializes it.
941          *
942          * @param conf the configuration object
943          */
944         public GlobalSectionTestThread(HierarchicalINIConfiguration conf)
945         {
946             config = conf;
947         }
948 
949         /**
950          * Accesses the global section in a loop. If there is no correct
951          * synchronization, this can cause an exception.
952          */
953         @Override
954         public void run()
955         {
956             final int loopCount = 250;
957 
958             for (int i = 0; i < loopCount && !error; i++)
959             {
960                 try
961                 {
962                     config.getSection(null);
963                 }
964                 catch (IllegalStateException istex)
965                 {
966                     error = true;
967                 }
968             }
969         }
970     }
971 }