001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements. See the NOTICE file distributed with this
004     * work for additional information regarding copyright ownership. The ASF
005     * licenses this file to You under the Apache License, Version 2.0 (the
006     * "License"); you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     * http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014     * License for the specific language governing permissions and limitations under
015     * the License.
016     */
017    
018    package org.apache.commons.configuration;
019    
020    import static org.junit.Assert.assertEquals;
021    import static org.junit.Assert.assertFalse;
022    import static org.junit.Assert.assertTrue;
023    
024    import java.io.File;
025    import java.io.FileWriter;
026    import java.io.IOException;
027    import java.io.PrintWriter;
028    import java.io.StringReader;
029    import java.io.StringWriter;
030    import java.io.Writer;
031    import java.util.HashSet;
032    import java.util.Iterator;
033    import java.util.List;
034    import java.util.Set;
035    
036    import org.junit.After;
037    import org.junit.Test;
038    
039    /**
040     * Test class for HierarchicalINIConfiguration.
041     *
042     * @author <a
043     *         href="http://commons.apache.org/configuration/team-list.html">Commons
044     *         Configuration team</a>
045     * @version $Id: TestHierarchicalINIConfiguration.java 1234362 2012-01-21 16:59:48Z oheger $
046     */
047    public class TestHierarchicalINIConfiguration
048    {
049        private static String LINE_SEPARATOR = System.getProperty("line.separator");
050    
051        /** Constant for the content of an ini file. */
052        private static final String INI_DATA = "[section1]" + LINE_SEPARATOR
053                + "var1 = foo" + LINE_SEPARATOR + "var2 = 451" + LINE_SEPARATOR
054                + LINE_SEPARATOR + "[section2]" + LINE_SEPARATOR + "var1 = 123.45"
055                + LINE_SEPARATOR + "var2 = bar" + LINE_SEPARATOR + LINE_SEPARATOR
056                + "[section3]" + LINE_SEPARATOR + "var1 = true" + LINE_SEPARATOR
057                + "interpolated = ${section3.var1}" + LINE_SEPARATOR
058                + "multi = foo" + LINE_SEPARATOR + "multi = bar" + LINE_SEPARATOR
059                + LINE_SEPARATOR;
060    
061        private static final String INI_DATA2 = "[section4]" + LINE_SEPARATOR
062                + "var1 = \"quoted value\"" + LINE_SEPARATOR
063                + "var2 = \"quoted value\\nwith \\\"quotes\\\"\"" + LINE_SEPARATOR
064                + "var3 = 123 ; comment" + LINE_SEPARATOR
065                + "var4 = \"1;2;3\" ; comment" + LINE_SEPARATOR
066                + "var5 = '\\'quoted\\' \"value\"' ; comment" + LINE_SEPARATOR
067                + "var6 = \"\"" + LINE_SEPARATOR;
068    
069        private static final String INI_DATA3 = "[section5]" + LINE_SEPARATOR
070                + "multiLine = one \\" + LINE_SEPARATOR
071                + "    two      \\" + LINE_SEPARATOR
072                + " three" + LINE_SEPARATOR
073                + "singleLine = C:\\Temp\\" + LINE_SEPARATOR
074                + "multiQuoted = one \\" + LINE_SEPARATOR
075                + "\"  two  \" \\" + LINE_SEPARATOR
076                + "  three" + LINE_SEPARATOR
077                + "multiComment = one \\ ; a comment" + LINE_SEPARATOR
078                + "two" + LINE_SEPARATOR
079                + "multiQuotedComment = \" one \" \\ ; comment" + LINE_SEPARATOR
080                + "two" + LINE_SEPARATOR
081                + "noFirstLine = \\" + LINE_SEPARATOR
082                + "  line 2" + LINE_SEPARATOR
083                + "continueNoLine = one \\" + LINE_SEPARATOR;
084    
085        private static final String INI_DATA_SEPARATORS = "[section]"
086                + LINE_SEPARATOR + "var1 = value1" + LINE_SEPARATOR
087                + "var2 : value2" + LINE_SEPARATOR
088                + "var3=value3" + LINE_SEPARATOR
089                + "var4:value4" + LINE_SEPARATOR
090                + "var5 : value=5" + LINE_SEPARATOR
091                + "var:6=value" + LINE_SEPARATOR
092                + "var:7=\"value7\"" + LINE_SEPARATOR
093                + "var:8 =  \"value8\"" + LINE_SEPARATOR;
094    
095        /** An ini file that contains only a property in the global section. */
096        private static final String INI_DATA_GLOBAL_ONLY = "globalVar = testGlobal"
097                + LINE_SEPARATOR + LINE_SEPARATOR;
098    
099        /** 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    }