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
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration;
18  
19  import static org.junit.Assert.assertEquals;
20  import static org.junit.Assert.assertFalse;
21  import static org.junit.Assert.assertNotNull;
22  import static org.junit.Assert.assertNull;
23  import static org.junit.Assert.assertSame;
24  import static org.junit.Assert.assertTrue;
25  import static org.junit.Assert.fail;
26  
27  import java.io.Reader;
28  import java.io.StringReader;
29  import java.io.StringWriter;
30  import java.util.Iterator;
31  
32  import org.apache.commons.configuration.event.ConfigurationEvent;
33  import org.apache.commons.configuration.event.ConfigurationListener;
34  import org.junit.Before;
35  import org.junit.Test;
36  
37  /**
38   * Test class for PropertiesConfigurationLayout.
39   *
40   * @author <a
41   * href="http://commons.apache.org/configuration/team-list.html">Commons
42   * Configuration team</a>
43   * @version $Id: TestPropertiesConfigurationLayout.java 1301996 2012-03-17 20:30:39Z sebb $
44   */
45  public class TestPropertiesConfigurationLayout
46  {
47      /** Constant for the line break character. */
48      private static final String CR = System.getProperty("line.separator");
49  
50      /** Constant for the normalized line break character. */
51      private static final String CRNORM = "\n";
52  
53      /** Constant for a test property key. */
54      static final String TEST_KEY = "myProperty";
55  
56      /** Constant for a test comment. */
57      static final String TEST_COMMENT = "A comment for my test property";
58  
59      /** Constant for a test property value. */
60      static final String TEST_VALUE = "myPropertyValue";
61  
62      /** The layout object under test. */
63      PropertiesConfigurationLayout layout;
64  
65      /** The associated configuration object. */
66      LayoutTestConfiguration config;
67  
68      /** A properties builder that can be used for testing. */
69      PropertiesBuilder builder;
70  
71      @Before
72      public void setUp() throws Exception
73      {
74          config = new LayoutTestConfiguration();
75          layout = new PropertiesConfigurationLayout(config);
76          config.setLayout(layout);
77          builder = new PropertiesBuilder();
78      }
79  
80      /**
81       * Tests a newly created instance.
82       */
83      @Test
84      public void testInit()
85      {
86          assertTrue("Object contains keys", layout.getKeys().isEmpty());
87          assertNull("Header comment not null", layout.getHeaderComment());
88          Iterator<ConfigurationListener> it = config.getConfigurationListeners().iterator();
89          assertTrue("No event listener registered", it.hasNext());
90          assertSame("Layout not registered as event listener", layout, it.next());
91          assertFalse("Multiple event listeners registered", it.hasNext());
92          assertSame("Configuration not stored", config, layout
93                  .getConfiguration());
94          assertFalse("Force single line flag set", layout.isForceSingleLine());
95          assertNull("Got a global separator", layout.getGlobalSeparator());
96      }
97  
98      /**
99       * Tests creating a layout object with a null configuration. This should
100      * cause an exception.
101      */
102     @Test(expected = IllegalArgumentException.class)
103     public void testInitNull()
104     {
105         new PropertiesConfigurationLayout(null);
106     }
107 
108     /**
109      * Tests reading a simple properties file.
110      */
111     @Test
112     public void testReadSimple() throws ConfigurationException
113     {
114         builder.addComment(TEST_COMMENT);
115         builder.addProperty(TEST_KEY, TEST_VALUE);
116         layout.load(builder.getReader());
117         assertNull("A header comment was found", layout.getHeaderComment());
118         assertEquals("Wrong number of properties", 1, layout.getKeys().size());
119         assertTrue("Property not found", layout.getKeys().contains(TEST_KEY));
120         assertEquals("Comment not found", TEST_COMMENT, layout
121                 .getCanonicalComment(TEST_KEY, false));
122         assertEquals("Wrong number of blanc lines", 0, layout
123                 .getBlancLinesBefore(TEST_KEY));
124         assertTrue("Wrong single line flag", layout.isSingleLine(TEST_KEY));
125         assertEquals("Property not stored in config", TEST_VALUE, config
126                 .getString(TEST_KEY));
127     }
128 
129     /**
130      * Tests whether blanc lines before a property are correctly detected.
131      */
132     @Test
133     public void testBlancLines() throws ConfigurationException
134     {
135         builder.addProperty("prop", "value");
136         builder.addComment(null);
137         builder.addComment(null);
138         builder.addComment(TEST_COMMENT);
139         builder.addComment(null);
140         builder.addProperty(TEST_KEY, TEST_VALUE);
141         layout.load(builder.getReader());
142         assertEquals("Wrong number of blanc lines", 2, layout
143                 .getBlancLinesBefore(TEST_KEY));
144         assertEquals("Wrong comment", TEST_COMMENT + CRNORM, layout
145                 .getCanonicalComment(TEST_KEY, false));
146         assertEquals("Wrong property value", TEST_VALUE, config
147                 .getString(TEST_KEY));
148     }
149 
150     /**
151      * Tests the single line flag for a simple property definition.
152      */
153     @Test
154     public void testIsSingleLine() throws ConfigurationException
155     {
156         builder.addProperty(TEST_KEY, TEST_VALUE + "," + TEST_VALUE + "2");
157         layout.load(builder.getReader());
158         assertTrue("Wrong single line flag", layout.isSingleLine(TEST_KEY));
159         assertEquals("Wrong number of values", 2, config.getList(TEST_KEY)
160                 .size());
161     }
162 
163     /**
164      * Tests the single line flag if there are multiple property definitions.
165      */
166     @Test
167     public void testIsSingleLineMulti() throws ConfigurationException
168     {
169         builder.addProperty(TEST_KEY, TEST_VALUE);
170         builder.addProperty("anotherProp", "a value");
171         builder.addProperty(TEST_KEY, TEST_VALUE + "2");
172         layout.load(builder.getReader());
173         assertFalse("Wrong single line flag", layout.isSingleLine(TEST_KEY));
174         assertEquals("Wrong number of values", 2, config.getList(TEST_KEY)
175                 .size());
176     }
177 
178     /**
179      * Tests whether comments are combined for multiple occurrences.
180      */
181     @Test
182     public void testCombineComments() throws ConfigurationException
183     {
184         builder.addComment(TEST_COMMENT);
185         builder.addProperty(TEST_KEY, TEST_VALUE);
186         builder.addComment(null);
187         builder.addComment(TEST_COMMENT);
188         builder.addProperty(TEST_KEY, TEST_VALUE + "2");
189         layout.load(builder.getReader());
190         assertEquals("Wrong combined comment",
191                 TEST_COMMENT + CRNORM + TEST_COMMENT, layout.getCanonicalComment(
192                         TEST_KEY, false));
193         assertEquals("Wrong combined blanc numbers", 0, layout
194                 .getBlancLinesBefore(TEST_KEY));
195     }
196 
197     /**
198      * Tests if a header comment is detected.
199      */
200     @Test
201     public void testHeaderComment() throws ConfigurationException
202     {
203         builder.addComment(TEST_COMMENT);
204         builder.addComment(null);
205         builder.addProperty(TEST_KEY, TEST_VALUE);
206         layout.load(builder.getReader());
207         assertEquals("Wrong header comment", TEST_COMMENT, layout
208                 .getCanonicalHeaderComment(false));
209         assertNull("Wrong comment for property", layout.getCanonicalComment(
210                 TEST_KEY, false));
211     }
212 
213     /**
214      * Tests if a header comment containing blanc lines is correctly detected.
215      */
216     @Test
217     public void testHeaderCommentWithBlancs() throws ConfigurationException
218     {
219         builder.addComment(TEST_COMMENT);
220         builder.addComment(null);
221         builder.addComment(TEST_COMMENT);
222         builder.addComment(null);
223         builder.addProperty(TEST_KEY, TEST_VALUE);
224         layout.load(builder.getReader());
225         assertEquals("Wrong header comment", TEST_COMMENT + CRNORM + CRNORM
226                 + TEST_COMMENT, layout.getCanonicalHeaderComment(false));
227         assertNull("Wrong comment for property", layout.getComment(TEST_KEY));
228     }
229 
230     /**
231      * Tests if a header comment is correctly detected when it contains blanc
232      * lines and the first property has a comment, too.
233      */
234     @Test
235     public void testHeaderCommentWithBlancsAndPropComment()
236             throws ConfigurationException
237     {
238         builder.addComment(TEST_COMMENT);
239         builder.addComment(null);
240         builder.addComment(TEST_COMMENT);
241         builder.addComment(null);
242         builder.addComment(TEST_COMMENT);
243         builder.addProperty(TEST_KEY, TEST_VALUE);
244         layout.load(builder.getReader());
245         assertEquals("Wrong header comment", TEST_COMMENT + CRNORM + CRNORM
246                 + TEST_COMMENT, layout.getCanonicalHeaderComment(false));
247         assertEquals("Wrong comment for property", TEST_COMMENT, layout
248                 .getCanonicalComment(TEST_KEY, false));
249     }
250 
251     /**
252      * Tests fetching a canonical header comment when no comment is set.
253      */
254     @Test
255     public void testHeaderCommentNull()
256     {
257         assertNull("No null comment with comment chars", layout
258                 .getCanonicalHeaderComment(true));
259         assertNull("No null comment without comment chars", layout
260                 .getCanonicalHeaderComment(false));
261     }
262 
263     /**
264      * Tests if a property add event is correctly processed.
265      */
266     @Test
267     public void testEventAdd()
268     {
269         ConfigurationEvent event = new ConfigurationEvent(this,
270                 AbstractConfiguration.EVENT_ADD_PROPERTY, TEST_KEY, TEST_VALUE,
271                 false);
272         layout.configurationChanged(event);
273         assertTrue("Property not stored", layout.getKeys().contains(TEST_KEY));
274         assertEquals("Blanc lines before new property", 0, layout
275                 .getBlancLinesBefore(TEST_KEY));
276         assertTrue("No single line property", layout.isSingleLine(TEST_KEY));
277         assertEquals("Wrong separator", " = ", layout.getSeparator(TEST_KEY));
278     }
279 
280     /**
281      * Tests adding a property multiple time through an event. The property
282      * should then be a multi-line property.
283      */
284     @Test
285     public void testEventAddMultiple()
286     {
287         ConfigurationEvent event = new ConfigurationEvent(this,
288                 AbstractConfiguration.EVENT_ADD_PROPERTY, TEST_KEY, TEST_VALUE,
289                 false);
290         layout.configurationChanged(event);
291         layout.configurationChanged(event);
292         assertFalse("No multi-line property", layout.isSingleLine(TEST_KEY));
293     }
294 
295     /**
296      * Tests if an add event is correctly processed if the affected property is
297      * already stored in the layout object.
298      */
299     @Test
300     public void testEventAddExisting() throws ConfigurationException
301     {
302         builder.addComment(TEST_COMMENT);
303         builder.addProperty(TEST_KEY, TEST_VALUE);
304         layout.load(builder.getReader());
305         ConfigurationEvent event = new ConfigurationEvent(this,
306                 AbstractConfiguration.EVENT_ADD_PROPERTY, TEST_KEY, TEST_VALUE,
307                 false);
308         layout.configurationChanged(event);
309         assertFalse("No multi-line property", layout.isSingleLine(TEST_KEY));
310         assertEquals("Comment was modified", TEST_COMMENT, layout
311                 .getCanonicalComment(TEST_KEY, false));
312     }
313 
314     /**
315      * Tests if a set property event for a non existing property is correctly
316      * handled.
317      */
318     @Test
319     public void testEventSetNonExisting()
320     {
321         ConfigurationEvent event = new ConfigurationEvent(this,
322                 AbstractConfiguration.EVENT_SET_PROPERTY, TEST_KEY, TEST_VALUE,
323                 false);
324         layout.configurationChanged(event);
325         assertTrue("New property was not found", layout.getKeys().contains(
326                 TEST_KEY));
327     }
328 
329     /**
330      * Tests if a property delete event is correctly processed.
331      */
332     @Test
333     public void testEventDelete()
334     {
335         ConfigurationEvent event = new ConfigurationEvent(this,
336                 AbstractConfiguration.EVENT_ADD_PROPERTY, TEST_KEY, TEST_VALUE,
337                 false);
338         layout.configurationChanged(event);
339         event = new ConfigurationEvent(this,
340                 AbstractConfiguration.EVENT_CLEAR_PROPERTY, TEST_KEY, null,
341                 false);
342         layout.configurationChanged(event);
343         assertFalse("Property still existing", layout.getKeys().contains(
344                 TEST_KEY));
345     }
346 
347     /**
348      * Tests if a clear event is correctly processed.
349      */
350     @Test
351     public void testEventClearConfig() throws Exception
352     {
353         fillLayout();
354         ConfigurationEvent event = new ConfigurationEvent(this,
355                 AbstractConfiguration.EVENT_CLEAR, null, null, false);
356         layout.configurationChanged(event);
357         assertTrue("Keys not empty", layout.getKeys().isEmpty());
358         assertNull("Header comment was not reset", layout.getHeaderComment());
359     }
360 
361     /**
362      * Tests if a before update event is correctly ignored.
363      */
364     @Test
365     public void testEventAddBefore()
366     {
367         ConfigurationEvent event = new ConfigurationEvent(this,
368                 AbstractConfiguration.EVENT_ADD_PROPERTY, TEST_KEY, TEST_VALUE,
369                 true);
370         layout.configurationChanged(event);
371         assertFalse("Property already stored", layout.getKeys().contains(
372                 TEST_KEY));
373     }
374 
375     /**
376      * Tests if a reload update is correctly processed.
377      */
378     @Test
379     public void testEventReload()
380     {
381         fillLayout();
382         ConfigurationEvent event = new ConfigurationEvent(this,
383                 AbstractFileConfiguration.EVENT_RELOAD, null, null, true);
384         layout.configurationChanged(event);
385         assertTrue("Keys not empty", layout.getKeys().isEmpty());
386         assertNull("Header comment was not reset", layout.getHeaderComment());
387     }
388 
389     /**
390      * Tests the event after a reload has been performed. This should be
391      * ignored.
392      */
393     @Test
394     public void testEventReloadAfter()
395     {
396         fillLayout();
397         ConfigurationEvent event = new ConfigurationEvent(this,
398                 AbstractFileConfiguration.EVENT_RELOAD, null, null, false);
399         layout.configurationChanged(event);
400         assertFalse("Keys are empty", layout.getKeys().isEmpty());
401         assertNotNull("Header comment was reset", layout.getHeaderComment());
402     }
403 
404     /**
405      * Tests a recursive load call.
406      */
407     @Test
408     public void testRecursiveLoadCall() throws ConfigurationException
409     {
410         PropertiesBuilder b = new PropertiesBuilder();
411         b.addComment("A nested header comment.");
412         b.addComment("With multiple lines");
413         b.addComment(null);
414         b.addComment("Second comment");
415         b.addProperty(TEST_KEY, TEST_VALUE);
416         b.addProperty(TEST_KEY + "2", TEST_VALUE + "2");
417         config.builder = b;
418 
419         builder.addComment("Header comment");
420         builder.addComment(null);
421         builder.addComment(TEST_COMMENT);
422         builder.addProperty(TEST_KEY, TEST_VALUE);
423         builder.addComment("Include file");
424         builder.addProperty(PropertiesConfiguration.getInclude(), "test");
425 
426         layout.load(builder.getReader());
427 
428         assertEquals("Wrong header comment", "Header comment", layout
429                 .getCanonicalHeaderComment(false));
430         assertFalse("Include property was stored", layout.getKeys().contains(
431                 PropertiesConfiguration.getInclude()));
432         assertEquals("Wrong comment for property", TEST_COMMENT + CRNORM
433                 + "A nested header comment." + CRNORM + "With multiple lines" + CRNORM
434                 + CRNORM + "Second comment", layout.getCanonicalComment(TEST_KEY,
435                 false));
436     }
437 
438     /**
439      * Tests whether the output of the layout object is identical to the source
440      * file (at least for simple properties files).
441      */
442     @Test
443     public void testReadAndWrite() throws ConfigurationException
444     {
445         builder.addComment("This is my test properties file,");
446         builder.addComment("which contains a header comment.");
447         builder.addComment(null);
448         builder.addComment(TEST_COMMENT);
449         builder.addProperty(TEST_KEY, TEST_COMMENT);
450         builder.addComment(null);
451         builder.addComment(null);
452         builder.addComment("Another comment");
453         builder.addProperty("property", "and a value");
454         layout.load(builder.getReader());
455         checkLayoutString(builder.toString());
456     }
457 
458     /**
459      * Tests if the content of the layout object is correctly written.
460      */
461     @Test
462     public void testSave() throws ConfigurationException
463     {
464         config.addProperty(TEST_KEY, TEST_VALUE);
465         layout.setComment(TEST_KEY, TEST_COMMENT);
466         config.addProperty(TEST_KEY, TEST_VALUE + "2");
467         config.addProperty("AnotherProperty", "AnotherValue");
468         config.addProperty("AnotherProperty", "3rdValue");
469         layout.setComment("AnotherProperty", "AnotherComment");
470         layout.setBlancLinesBefore("AnotherProperty", 2);
471         layout.setSingleLine("AnotherProperty", true);
472         layout.setHeaderComment("A header comment" + CRNORM + "for my properties");
473         checkLayoutString("# A header comment" + CR + "# for my properties"
474                 + CR + CR + "# " + TEST_COMMENT + CR + TEST_KEY + " = "
475                 + TEST_VALUE + CR + TEST_KEY + " = " + TEST_VALUE + "2" + CR
476                 + CR + CR + "# AnotherComment" + CR
477                 + "AnotherProperty = AnotherValue,3rdValue" + CR);
478     }
479 
480     /**
481      * Tests the force single line flag.
482      */
483     @Test
484     public void testSaveForceSingleLine() throws ConfigurationException
485     {
486         config.setListDelimiter(';');
487         config.addProperty(TEST_KEY, TEST_VALUE);
488         config.addProperty(TEST_KEY, TEST_VALUE + "2");
489         config.addProperty("AnotherProperty", "value1;value2;value3");
490         layout.setComment(TEST_KEY, TEST_COMMENT);
491         layout.setForceSingleLine(true);
492         checkLayoutString("# " + TEST_COMMENT + CR + TEST_KEY + " = "
493                 + TEST_VALUE + ';' + TEST_VALUE + "2" + CR
494                 + "AnotherProperty = value1;value2;value3" + CR);
495     }
496 
497     /**
498      * Tests the trimComment method.
499      */
500     @Test
501     public void testTrimComment()
502     {
503         assertEquals("Wrong trimmed comment", "This is a comment" + CR
504                 + "that spans multiple" + CR + "lines in a" + CR
505                 + " complex way.", PropertiesConfigurationLayout.trimComment(
506                 "   # This is a comment" + CR + "that spans multiple" + CR
507                         + "!lines in a" + CR + " complex way.", false));
508     }
509 
510     /**
511      * Tests trimming a comment with trailing CRs.
512      */
513     @Test
514     public void testTrimCommentTrainlingCR()
515     {
516         assertEquals("Wrong trimmed comment", "Comment with" + CR
517                 + "trailing CR" + CR, PropertiesConfigurationLayout
518                 .trimComment("Comment with" + CR + "! trailing CR" + CR, false));
519     }
520 
521     /**
522      * Tests enforcing comment characters in a comment.
523      */
524     @Test
525     public void testTrimCommentFalse()
526     {
527         assertEquals("Wrong trimmed comment", "# Comment with" + CR
528                 + " ! some mixed " + CR + "#comment" + CR + "# lines",
529                 PropertiesConfigurationLayout.trimComment("Comment with" + CR
530                         + " ! some mixed " + CR + "#comment" + CR + "lines",
531                         true));
532     }
533 
534     /**
535      * Tests accessing data for a property, which is not stored.
536      */
537     @Test
538     public void testGetNonExistingLayouData()
539     {
540         assertNull("A comment was found", layout.getComment("unknown"));
541         assertTrue("A multi-line property", layout.isSingleLine("unknown"));
542         assertEquals("Leading blanc lines", 0, layout
543                 .getBlancLinesBefore("unknown"));
544     }
545 
546     /**
547      * Tests accessing a property with a null key. This should throw an
548      * exception.
549      */
550     @Test(expected = IllegalArgumentException.class)
551     public void testGetNullLayouttData()
552     {
553         layout.setComment(null, TEST_COMMENT);
554     }
555 
556     /**
557      * Tests resetting a comment.
558      */
559     @Test
560     public void testSetNullComment()
561     {
562         fillLayout();
563         layout.setComment(TEST_KEY, null);
564         assertNull("Comment was not reset", layout.getComment(TEST_KEY));
565     }
566 
567     /**
568      * Tests saving when a comment for a non existing property is contained in
569      * the layout object. This comment should be ignored.
570      */
571     @Test
572     public void testSaveCommentForUnexistingProperty()
573             throws ConfigurationException
574     {
575         fillLayout();
576         layout.setComment("NonExistingKey", "NonExistingComment");
577         String output = getLayoutString();
578         assertTrue("Non existing key was found", output
579                 .indexOf("NonExistingKey") < 0);
580         assertTrue("Non existing comment was found", output
581                 .indexOf("NonExistingComment") < 0);
582     }
583 
584     /**
585      * Tests saving an empty layout object.
586      */
587     @Test
588     public void testSaveEmptyLayout() throws ConfigurationException
589     {
590         checkLayoutString("");
591     }
592 
593     /**
594      * Tests the copy constructor.
595      */
596     @Test
597     public void testInitCopy()
598     {
599         fillLayout();
600         PropertiesConfigurationLayout l2 = new PropertiesConfigurationLayout(
601                 config, layout);
602         assertEquals("Wrong number of keys", layout.getKeys().size(), l2
603                 .getKeys().size());
604         for (Iterator<String> it = layout.getKeys().iterator(); it.hasNext();)
605         {
606             Object key = it.next();
607             assertTrue("Key was not found: " + key, l2.getKeys().contains(key));
608         }
609     }
610 
611     /**
612      * Tests if the copy and the original are independent from each other.
613      */
614     @Test
615     public void testInitCopyModify()
616     {
617         fillLayout();
618         PropertiesConfigurationLayout l2 = new PropertiesConfigurationLayout(
619                 config, layout);
620         assertEquals("Comments are not equal", layout.getComment(TEST_KEY), l2
621                 .getComment(TEST_KEY));
622         layout.setComment(TEST_KEY, "A new comment");
623         assertEquals("Comment was changed", TEST_COMMENT, l2
624                 .getCanonicalComment(TEST_KEY, false));
625         l2.setBlancLinesBefore(TEST_KEY, l2.getBlancLinesBefore(TEST_KEY) + 1);
626         assertFalse("Blanc lines do not differ", layout
627                 .getBlancLinesBefore(TEST_KEY) == l2
628                 .getBlancLinesBefore(TEST_KEY));
629     }
630 
631     /**
632      * Tests changing the separator for a property.
633      */
634     @Test
635     public void testSetSeparator() throws ConfigurationException
636     {
637         config.addProperty(TEST_KEY, TEST_VALUE);
638         layout.setSeparator(TEST_KEY, ":");
639         checkLayoutString(TEST_KEY + ":" + TEST_VALUE + CR);
640     }
641 
642     /**
643      * Tests setting the global separator. This separator should override the
644      * separators for all properties.
645      */
646     @Test
647     public void testSetGlobalSeparator() throws ConfigurationException
648     {
649         final String sep = "=";
650         config.addProperty(TEST_KEY, TEST_VALUE);
651         config.addProperty("key2", "value2");
652         layout.setSeparator(TEST_KEY, " : ");
653         layout.setGlobalSeparator(sep);
654         checkLayoutString(TEST_KEY + sep + TEST_VALUE + CR + "key2" + sep
655                 + "value2" + CR);
656     }
657 
658     /**
659      * Tests setting the line separator.
660      */
661     @Test
662     public void testSetLineSeparator() throws ConfigurationException
663     {
664         final String lf = CR + CR;
665         config.addProperty(TEST_KEY, TEST_VALUE);
666         layout.setBlancLinesBefore(TEST_KEY, 2);
667         layout.setComment(TEST_KEY, TEST_COMMENT);
668         layout.setHeaderComment("Header comment");
669         layout.setLineSeparator(lf);
670         checkLayoutString("# Header comment" + lf + lf + lf + lf + "# "
671                 + TEST_COMMENT + lf + TEST_KEY + " = " + TEST_VALUE + lf);
672     }
673 
674     /**
675      * Tests whether the line separator is also taken into account within
676      * comments.
677      */
678     @Test
679     public void testSetLineSeparatorInComments() throws ConfigurationException
680     {
681         final String lf = "<-\n";
682         config.addProperty(TEST_KEY, TEST_VALUE);
683         layout.setComment(TEST_KEY, TEST_COMMENT + "\nMore comment");
684         layout.setHeaderComment("Header\ncomment");
685         layout.setLineSeparator(lf);
686         checkLayoutString("# Header" + lf + "# comment" + lf + lf + "# "
687                 + TEST_COMMENT + lf + "# More comment" + lf + TEST_KEY + " = "
688                 + TEST_VALUE + lf);
689     }
690 
691     /**
692      * Helper method for filling the layout object with some properties.
693      */
694     private void fillLayout()
695     {
696         builder.addComment("A header comment");
697         builder.addComment(null);
698         builder.addProperty("prop", "value");
699         builder.addComment(TEST_COMMENT);
700         builder.addProperty(TEST_KEY, TEST_VALUE);
701         builder.addProperty("anotherProp", "anotherValue");
702         try
703         {
704             layout.load(builder.getReader());
705         }
706         catch (ConfigurationException cex)
707         {
708             // should not happen
709             fail("Exception was thrown: " + cex);
710         }
711     }
712 
713     /**
714      * Writes the layout's data into a string.
715      *
716      * @return the layout file's content as string
717      * @throws ConfigurationException if an error occurs
718      */
719     private String getLayoutString() throws ConfigurationException
720     {
721         StringWriter out = new StringWriter();
722         layout.save(out);
723         return out.toString();
724     }
725 
726     /**
727      * Checks if the layout's output is correct.
728      *
729      * @param expected the expected result
730      * @throws ConfigurationException if an error occurs
731      */
732     private void checkLayoutString(String expected)
733             throws ConfigurationException
734     {
735         assertEquals("Wrong layout file content", expected, getLayoutString());
736     }
737 
738     /**
739      * A helper class used for constructing test properties files.
740      */
741     static class PropertiesBuilder
742     {
743         /** A buffer for storing the data. */
744         private StringBuilder buf = new StringBuilder();
745 
746         /** A counter for varying the comment character. */
747         private int commentCounter;
748 
749         /**
750          * Adds a property to the simulated file.
751          *
752          * @param key the property key
753          * @param value the value
754          */
755         public void addProperty(String key, String value)
756         {
757             buf.append(key).append(" = ").append(value).append(CR);
758         }
759 
760         /**
761          * Adds a comment line.
762          *
763          * @param s the comment (can be <b>null</b>, then a blanc line is
764          * added)
765          */
766         public void addComment(String s)
767         {
768             if (s != null)
769             {
770                 if (commentCounter % 2 == 0)
771                 {
772                     buf.append("# ");
773                 }
774                 else
775                 {
776                     buf.append("! ");
777                 }
778                 buf.append(s);
779                 commentCounter++;
780             }
781             buf.append(CR);
782         }
783 
784         /**
785          * Returns a reader for the simulated properties.
786          *
787          * @return a reader
788          */
789         public Reader getReader()
790         {
791             return new StringReader(buf.toString());
792         }
793 
794         /**
795          * Returns a string representation of the buffer's content.
796          *
797          * @return the buffer as string
798          */
799         @Override
800         public String toString()
801         {
802             return buf.toString();
803         }
804     }
805 
806     /**
807      * A mock properties configuration implementation that is used to check
808      * whether some expected methods are called.
809      */
810     static class LayoutTestConfiguration extends PropertiesConfiguration
811     {
812         /** Stores a builder object. */
813         public PropertiesBuilder builder;
814 
815         /**
816          * Simulates the propertyLoaded() callback. If a builder was set, a
817          * load() call on the layout is invoked.
818          */
819         @Override
820         boolean propertyLoaded(String key, String value)
821                 throws ConfigurationException
822         {
823             if (builder == null)
824             {
825                 return super.propertyLoaded(key, value);
826             }
827             else
828             {
829                 if (PropertiesConfiguration.getInclude().equals(key))
830                 {
831                     getLayout().load(builder.getReader());
832                     return false;
833                 }
834                 else
835                 {
836                     return true;
837                 }
838             }
839         }
840     }
841 }