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.configuration2;
18  
19  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertNotEquals;
23  import static org.junit.jupiter.api.Assertions.assertNull;
24  import static org.junit.jupiter.api.Assertions.assertSame;
25  import static org.junit.jupiter.api.Assertions.assertThrows;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  
28  import java.io.Reader;
29  import java.io.StringReader;
30  import java.io.StringWriter;
31  import java.net.URL;
32  import java.util.Deque;
33  import java.util.Iterator;
34  
35  import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
36  import org.apache.commons.configuration2.convert.LegacyListDelimiterHandler;
37  import org.apache.commons.configuration2.event.ConfigurationEvent;
38  import org.apache.commons.configuration2.event.EventListener;
39  import org.apache.commons.configuration2.ex.ConfigurationException;
40  import org.junit.jupiter.api.BeforeEach;
41  import org.junit.jupiter.api.Test;
42  
43  /**
44   * Test class for PropertiesConfigurationLayout.
45   */
46  public class TestPropertiesConfigurationLayout {
47  
48      /**
49       * A mock properties configuration implementation that is used to check whether some expected methods are called.
50       */
51      static class LayoutTestConfiguration extends PropertiesConfiguration {
52  
53          /** Stores a builder object. */
54          private PropertiesBuilder builder;
55  
56          /**
57           * Simulates the propertyLoaded() callback. If a builder was set, a load() call on the layout is invoked.
58           */
59          @Override
60          boolean propertyLoaded(final String key, final String value, final Deque<URL> seenStack) throws ConfigurationException {
61              if (builder == null) {
62                  return super.propertyLoaded(key, value, seenStack);
63              }
64              if (PropertiesConfiguration.getInclude().equals(key)) {
65                  getLayout().load(this, builder.getReader());
66                  return false;
67              }
68              return true;
69          }
70      }
71  
72      /**
73       * A helper class used for constructing test properties files.
74       */
75      static class PropertiesBuilder {
76          /** A buffer for storing the data. */
77          private final StringBuilder buf = new StringBuilder();
78  
79          /** A counter for varying the comment character. */
80          private int commentCounter;
81  
82          /**
83           * Adds a comment line.
84           *
85           * @param s the comment (can be <strong>null</strong>, then a blank line is added)
86           */
87          public void addComment(final String s) {
88              if (s != null) {
89                  if (commentCounter % 2 == 0) {
90                      buf.append("# ");
91                  } else {
92                      buf.append("! ");
93                  }
94                  buf.append(s);
95                  commentCounter++;
96              }
97              buf.append(CR);
98          }
99  
100         /**
101          * Adds a line verbatim to the simulated file.
102          *
103          * @param s the content of the line
104          */
105         public void addLine(final String s) {
106             buf.append(s).append(CR);
107         }
108 
109         /**
110          * Adds a property to the simulated file.
111          *
112          * @param key the property key
113          * @param value the value
114          */
115         public void addProperty(final String key, final String value) {
116             buf.append(key).append(" = ").append(value).append(CR);
117         }
118 
119         /**
120          * Returns a reader for the simulated properties.
121          *
122          * @return a reader
123          */
124         public Reader getReader() {
125             return new StringReader(buf.toString());
126         }
127 
128         /**
129          * Returns a string representation of the buffer's content.
130          *
131          * @return the buffer as string
132          */
133         @Override
134         public String toString() {
135             return buf.toString();
136         }
137     }
138 
139     /** Constant for the line break character. */
140     private static final String CR = System.lineSeparator();
141 
142     /** Constant for the normalized line break character. */
143     private static final String CRNORM = "\n";
144 
145     /** Constant for a test property key. */
146     private static final String TEST_KEY = "myProperty";
147 
148     /** Constant for a test comment. */
149     private static final String TEST_COMMENT = "A comment for my test property";
150 
151     /** Constant for a test property value. */
152     private static final String TEST_VALUE = "myPropertyValue";
153 
154     /** The layout object under test. */
155     private PropertiesConfigurationLayout layout;
156 
157     /** The associated configuration object. */
158     private LayoutTestConfiguration config;
159 
160     /** A properties builder that can be used for testing. */
161     private PropertiesBuilder builder;
162 
163     /**
164      * Checks if the layout's output is correct.
165      *
166      * @param expected the expected result
167      * @throws ConfigurationException if an error occurs
168      */
169     private void checkLayoutString(final String expected) throws ConfigurationException {
170         assertEquals(expected, getLayoutString());
171     }
172 
173     /**
174      * Helper method for filling the layout object with some properties.
175      */
176     private void fillLayout() {
177         builder.addComment("A header comment");
178         builder.addComment(null);
179         builder.addProperty("prop", "value");
180         builder.addComment(TEST_COMMENT);
181         builder.addProperty(TEST_KEY, TEST_VALUE);
182         builder.addProperty("anotherProp", "anotherValue");
183         builder.addComment("A footer comment");
184         assertDoesNotThrow(() -> layout.load(config, builder.getReader()));
185     }
186 
187     /**
188      * Writes the layout's data into a string.
189      *
190      * @return the layout file's content as string
191      * @throws ConfigurationException if an error occurs
192      */
193     private String getLayoutString() throws ConfigurationException {
194         final StringWriter out = new StringWriter();
195         layout.save(config, out);
196         return out.toString();
197     }
198 
199     @BeforeEach
200     public void setUp() throws Exception {
201         config = new LayoutTestConfiguration();
202         config.setListDelimiterHandler(new LegacyListDelimiterHandler(','));
203         layout = new PropertiesConfigurationLayout();
204         config.setLayout(layout);
205         builder = new PropertiesBuilder();
206     }
207 
208     /**
209      * Tests whether blank lines before a property are correctly detected.
210      */
211     @Test
212     public void testBlankLines() throws ConfigurationException {
213         builder.addProperty("prop", "value");
214         builder.addComment(null);
215         builder.addComment(null);
216         builder.addComment(TEST_COMMENT);
217         builder.addComment(null);
218         builder.addProperty(TEST_KEY, TEST_VALUE);
219         layout.load(config, builder.getReader());
220         assertEquals(2, layout.getBlankLinesBefore(TEST_KEY));
221         assertEquals(TEST_COMMENT + CRNORM, layout.getCanonicalComment(TEST_KEY, false));
222         assertEquals(TEST_VALUE, config.getString(TEST_KEY));
223     }
224 
225     /**
226      * Tests whether blank lines before a property are correctly detected if a header comment is present
227      */
228     @Test
229     public void testBlankLinesWithHeaderComment() throws ConfigurationException {
230         builder.addComment(TEST_COMMENT);
231         builder.addComment(null);
232         builder.addComment(null);
233         builder.addComment(TEST_COMMENT);
234         builder.addProperty(TEST_KEY, TEST_VALUE);
235         layout.load(config, builder.getReader());
236         assertEquals(2, layout.getBlankLinesBefore(TEST_KEY));
237         assertEquals(TEST_COMMENT, layout.getCanonicalComment(TEST_KEY, false));
238         assertEquals(TEST_VALUE, config.getString(TEST_KEY));
239     }
240 
241     /**
242      * Tests whether comments are combined for multiple occurrences.
243      */
244     @Test
245     public void testCombineComments() throws ConfigurationException {
246         builder.addComment(TEST_COMMENT);
247         builder.addProperty(TEST_KEY, TEST_VALUE);
248         builder.addComment(null);
249         builder.addComment(TEST_COMMENT);
250         builder.addProperty(TEST_KEY, TEST_VALUE + "2");
251         layout.load(config, builder.getReader());
252         assertEquals(TEST_COMMENT + CRNORM + TEST_COMMENT, layout.getCanonicalComment(TEST_KEY, false));
253         assertEquals(0, layout.getBlankLinesBefore(TEST_KEY));
254     }
255 
256     /**
257      * Tests if a property add event is correctly processed.
258      */
259     @Test
260     public void testEventAdd() {
261         final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.ADD_PROPERTY, TEST_KEY, TEST_VALUE, false);
262         layout.onEvent(event);
263         assertTrue(layout.getKeys().contains(TEST_KEY));
264         assertEquals(0, layout.getBlankLinesBefore(TEST_KEY));
265         assertTrue(layout.isSingleLine(TEST_KEY));
266         assertEquals(" = ", layout.getSeparator(TEST_KEY));
267     }
268 
269     /**
270      * Tests if a before update event is correctly ignored.
271      */
272     @Test
273     public void testEventAddBefore() {
274         final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.ADD_PROPERTY, TEST_KEY, TEST_VALUE, true);
275         layout.onEvent(event);
276         assertFalse(layout.getKeys().contains(TEST_KEY));
277     }
278 
279     /**
280      * Tests if an add event is correctly processed if the affected property is already stored in the layout object.
281      */
282     @Test
283     public void testEventAddExisting() throws ConfigurationException {
284         builder.addComment(TEST_COMMENT);
285         builder.addProperty(TEST_KEY, TEST_VALUE);
286         layout.load(config, builder.getReader());
287         final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.ADD_PROPERTY, TEST_KEY, TEST_VALUE, false);
288         layout.onEvent(event);
289         assertFalse(layout.isSingleLine(TEST_KEY));
290         assertEquals(TEST_COMMENT, layout.getCanonicalComment(TEST_KEY, false));
291     }
292 
293     /**
294      * Tests adding a property multiple time through an event. The property should then be a multi-line property.
295      */
296     @Test
297     public void testEventAddMultiple() {
298         final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.ADD_PROPERTY, TEST_KEY, TEST_VALUE, false);
299         layout.onEvent(event);
300         layout.onEvent(event);
301         assertFalse(layout.isSingleLine(TEST_KEY));
302     }
303 
304     /**
305      * Tests if a clear event is correctly processed.
306      */
307     @Test
308     public void testEventClearConfig() throws Exception {
309         fillLayout();
310         final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.CLEAR, null, null, false);
311         layout.onEvent(event);
312         assertTrue(layout.getKeys().isEmpty());
313         assertNull(layout.getHeaderComment());
314     }
315 
316     /**
317      * Tests if a property delete event is correctly processed.
318      */
319     @Test
320     public void testEventDelete() {
321         ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.ADD_PROPERTY, TEST_KEY, TEST_VALUE, false);
322         layout.onEvent(event);
323         event = new ConfigurationEvent(this, ConfigurationEvent.CLEAR_PROPERTY, TEST_KEY, null, false);
324         layout.onEvent(event);
325         assertFalse(layout.getKeys().contains(TEST_KEY));
326     }
327 
328     /**
329      * Tests if a set property event for a non existing property is correctly handled.
330      */
331     @Test
332     public void testEventSetNonExisting() {
333         final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.SET_PROPERTY, TEST_KEY, TEST_VALUE, false);
334         layout.onEvent(event);
335         assertTrue(layout.getKeys().contains(TEST_KEY));
336     }
337 
338     /**
339      * Tests accessing data for a property, which is not stored.
340      */
341     @Test
342     public void testGetNonExistingLayouData() {
343         assertNull(layout.getComment("unknown"));
344         assertTrue(layout.isSingleLine("unknown"));
345         assertEquals(0, layout.getBlankLinesBefore("unknown"));
346     }
347 
348     /**
349      * Tests accessing a property with a null key. This should throw an exception.
350      */
351     @Test
352     public void testGetNullLayouttData() {
353         assertThrows(IllegalArgumentException.class, () -> layout.setComment(null, TEST_COMMENT));
354     }
355 
356     /**
357      * Tests if a header comment is detected.
358      */
359     @Test
360     public void testHeaderComment() throws ConfigurationException {
361         builder.addComment(TEST_COMMENT);
362         builder.addComment(null);
363         builder.addProperty(TEST_KEY, TEST_VALUE);
364         layout.load(config, builder.getReader());
365         assertEquals(TEST_COMMENT, layout.getCanonicalHeaderComment(false));
366         assertNull(layout.getCanonicalComment(TEST_KEY, false));
367     }
368 
369     /**
370      * Tests fetching a canonical header comment when no comment is set.
371      */
372     @Test
373     public void testHeaderCommentNull() {
374         assertNull(layout.getCanonicalHeaderComment(true));
375         assertNull(layout.getCanonicalHeaderComment(false));
376     }
377 
378     /**
379      * Tests if a header comment containing blank lines is correctly detected.
380      */
381     @Test
382     public void testHeaderCommentWithBlanks() throws ConfigurationException {
383         builder.addComment(TEST_COMMENT);
384         builder.addComment(null);
385         builder.addComment(TEST_COMMENT);
386         builder.addComment(null);
387         builder.addProperty(TEST_KEY, TEST_VALUE);
388         layout.load(config, builder.getReader());
389         assertEquals(TEST_COMMENT + CRNORM + CRNORM + TEST_COMMENT, layout.getCanonicalHeaderComment(false));
390         assertNull(layout.getComment(TEST_KEY));
391     }
392 
393     /**
394      * Tests if a header comment containing blank lines is correctly detected and doesn't overflow into the property comment
395      * in the case that the header comment is already set
396      */
397     @Test
398     public void testHeaderCommentWithBlanksAndPresetHeaderComment() throws ConfigurationException {
399         final String presetHeaderComment = "preset" + TEST_COMMENT + CRNORM + CRNORM + TEST_COMMENT;
400         builder.addComment(TEST_COMMENT);
401         builder.addComment(null);
402         builder.addComment(TEST_COMMENT);
403         builder.addComment(null);
404         builder.addProperty(TEST_KEY, TEST_VALUE);
405         layout.setHeaderComment(presetHeaderComment);
406         layout.load(config, builder.getReader());
407         assertEquals(presetHeaderComment, layout.getCanonicalHeaderComment(false));
408         assertNull(layout.getComment(TEST_KEY));
409     }
410 
411     /**
412      * Tests if a header comment is correctly detected when it contains blank lines and the first property has a comment,
413      * too.
414      */
415     @Test
416     public void testHeaderCommentWithBlanksAndPropComment() throws ConfigurationException {
417         builder.addComment(TEST_COMMENT);
418         builder.addComment(null);
419         builder.addComment(TEST_COMMENT);
420         builder.addComment(null);
421         builder.addComment(TEST_COMMENT);
422         builder.addProperty(TEST_KEY, TEST_VALUE);
423         layout.load(config, builder.getReader());
424         assertEquals(TEST_COMMENT + CRNORM + CRNORM + TEST_COMMENT, layout.getCanonicalHeaderComment(false));
425         assertEquals(TEST_COMMENT, layout.getCanonicalComment(TEST_KEY, false));
426     }
427 
428     /**
429      * Tests a newly created instance.
430      */
431     @Test
432     public void testInit() {
433         assertTrue(layout.getKeys().isEmpty());
434         assertNull(layout.getHeaderComment());
435         final Iterator<EventListener<? super ConfigurationEvent>> it = config.getEventListeners(ConfigurationEvent.ANY).iterator();
436         assertTrue(it.hasNext());
437         assertSame(layout, it.next());
438         assertFalse(it.hasNext());
439         assertFalse(layout.isForceSingleLine());
440         assertNull(layout.getGlobalSeparator());
441     }
442 
443     /**
444      * Tests the copy constructor.
445      */
446     @Test
447     public void testInitCopy() {
448         fillLayout();
449         final PropertiesConfigurationLayout l2 = new PropertiesConfigurationLayout(layout);
450         assertEquals(l2.getKeys(), layout.getKeys());
451         assertEquals(layout.getHeaderComment(), l2.getHeaderComment());
452         assertEquals(layout.getFooterComment(), l2.getFooterComment());
453     }
454 
455     /**
456      * Tests if the copy and the original are independent from each other.
457      */
458     @Test
459     public void testInitCopyModify() {
460         fillLayout();
461         final PropertiesConfigurationLayout l2 = new PropertiesConfigurationLayout(layout);
462         assertEquals(layout.getComment(TEST_KEY), l2.getComment(TEST_KEY));
463         layout.setComment(TEST_KEY, "A new comment");
464         assertEquals(TEST_COMMENT, l2.getCanonicalComment(TEST_KEY, false));
465         l2.setBlankLinesBefore(TEST_KEY, l2.getBlankLinesBefore(TEST_KEY) + 1);
466         assertNotEquals(layout.getBlankLinesBefore(TEST_KEY), l2.getBlankLinesBefore(TEST_KEY));
467     }
468 
469     /**
470      * Tests the copy constructor if no other layout object is passed.
471      */
472     @Test
473     public void testInitNull() {
474         layout = new PropertiesConfigurationLayout(null);
475         assertTrue(layout.getKeys().isEmpty());
476     }
477 
478     /**
479      * Tests the single line flag for a simple property definition.
480      */
481     @Test
482     public void testIsSingleLine() throws ConfigurationException {
483         builder.addProperty(TEST_KEY, TEST_VALUE + "," + TEST_VALUE + "2");
484         layout.load(config, builder.getReader());
485         assertTrue(layout.isSingleLine(TEST_KEY));
486         assertEquals(2, config.getList(TEST_KEY).size());
487     }
488 
489     /**
490      * Tests the single line flag if there are multiple property definitions.
491      */
492     @Test
493     public void testIsSingleLineMulti() throws ConfigurationException {
494         builder.addProperty(TEST_KEY, TEST_VALUE);
495         builder.addProperty("anotherProp", "a value");
496         builder.addProperty(TEST_KEY, TEST_VALUE + "2");
497         layout.load(config, builder.getReader());
498         assertFalse(layout.isSingleLine(TEST_KEY));
499         assertEquals(2, config.getList(TEST_KEY).size());
500     }
501 
502     /**
503      * Tests whether a line with whitespace is handled correctly. This is related to CONFIGURATION-582.
504      */
505     @Test
506     public void testLineWithBlank() throws ConfigurationException {
507         builder.addComment(TEST_COMMENT);
508         builder.addLine(" ");
509         builder.addProperty(TEST_KEY, TEST_VALUE);
510         layout.load(config, builder.getReader());
511         assertEquals(TEST_COMMENT + CRNORM + " ", layout.getCanonicalComment(TEST_KEY, false));
512     }
513 
514     /**
515      * Tests whether the output of the layout object is identical to the source file (at least for simple properties files).
516      */
517     @Test
518     public void testReadAndWrite() throws ConfigurationException {
519         builder.addComment("This is my test properties file,");
520         builder.addComment("which contains a header comment.");
521         builder.addComment(null);
522         builder.addComment(null);
523         builder.addComment(TEST_COMMENT);
524         builder.addProperty(TEST_KEY, TEST_COMMENT);
525         builder.addComment(null);
526         builder.addComment(null);
527         builder.addComment("Another comment");
528         builder.addProperty("property", "and a value");
529         layout.load(config, builder.getReader());
530         checkLayoutString(builder.toString());
531     }
532 
533     /**
534      * Tests reading a simple properties file.
535      */
536     @Test
537     public void testReadSimple() throws ConfigurationException {
538         builder.addComment(TEST_COMMENT);
539         builder.addProperty(TEST_KEY, TEST_VALUE);
540         layout.load(config, builder.getReader());
541         assertNull(layout.getHeaderComment());
542         assertEquals(1, layout.getKeys().size());
543         assertTrue(layout.getKeys().contains(TEST_KEY));
544         assertEquals(TEST_COMMENT, layout.getCanonicalComment(TEST_KEY, false));
545         assertEquals(0, layout.getBlankLinesBefore(TEST_KEY));
546         assertTrue(layout.isSingleLine(TEST_KEY));
547         assertEquals(TEST_VALUE, config.getString(TEST_KEY));
548     }
549 
550     /**
551      * Tests a recursive load call.
552      */
553     @Test
554     public void testRecursiveLoadCall() throws ConfigurationException {
555         final PropertiesBuilder b = new PropertiesBuilder();
556         b.addComment("A nested header comment.");
557         b.addComment("With multiple lines");
558         b.addComment(null);
559         b.addComment("Second comment");
560         b.addProperty(TEST_KEY, TEST_VALUE);
561         b.addProperty(TEST_KEY + "2", TEST_VALUE + "2");
562         config.builder = b;
563 
564         builder.addComment("Header comment");
565         builder.addComment(null);
566         builder.addComment(TEST_COMMENT);
567         builder.addProperty(TEST_KEY, TEST_VALUE);
568         builder.addComment("Include file");
569         builder.addProperty(PropertiesConfiguration.getInclude(), "test");
570 
571         layout.load(config, builder.getReader());
572 
573         assertEquals("Header comment", layout.getCanonicalHeaderComment(false));
574         assertFalse(layout.getKeys().contains(PropertiesConfiguration.getInclude()));
575         assertEquals(TEST_COMMENT + CRNORM + "A nested header comment." + CRNORM + "With multiple lines" + CRNORM + CRNORM + "Second comment",
576                 layout.getCanonicalComment(TEST_KEY, false));
577     }
578 
579     /**
580      * Tests if the content of the layout object is correctly written.
581      */
582     @Test
583     public void testSave() throws ConfigurationException {
584         config.addProperty(TEST_KEY, TEST_VALUE);
585         layout.setComment(TEST_KEY, TEST_COMMENT);
586         config.addProperty(TEST_KEY, TEST_VALUE + "2");
587         config.addProperty("AnotherProperty", "AnotherValue");
588         config.addProperty("AnotherProperty", "3rdValue");
589         layout.setComment("AnotherProperty", "AnotherComment");
590         layout.setBlankLinesBefore("AnotherProperty", 2);
591         layout.setSingleLine("AnotherProperty", true);
592         layout.setHeaderComment("A header comment" + CRNORM + "for my properties");
593         checkLayoutString("# A header comment" + CR + "# for my properties" + CR + CR + "# " + TEST_COMMENT + CR + TEST_KEY + " = " + TEST_VALUE + CR + TEST_KEY
594             + " = " + TEST_VALUE + "2" + CR + CR + CR + "# AnotherComment" + CR + "AnotherProperty = AnotherValue,3rdValue" + CR);
595     }
596 
597     /**
598      * Tests saving when a comment for a non existing property is contained in the layout object. This comment should be
599      * ignored.
600      */
601     @Test
602     public void testSaveCommentForUnexistingProperty() throws ConfigurationException {
603         fillLayout();
604         layout.setComment("NonExistingKey", "NonExistingComment");
605         final String output = getLayoutString();
606         assertFalse(output.contains("NonExistingKey"));
607         assertFalse(output.contains("NonExistingComment"));
608     }
609 
610     /**
611      * Tests saving an empty layout object.
612      */
613     @Test
614     public void testSaveEmptyLayout() throws ConfigurationException {
615         checkLayoutString("");
616     }
617 
618     /**
619      * Tests the force single line flag.
620      */
621     @Test
622     public void testSaveForceSingleLine() throws ConfigurationException {
623         config.setListDelimiterHandler(new DefaultListDelimiterHandler(';'));
624         config.addProperty(TEST_KEY, TEST_VALUE);
625         config.addProperty(TEST_KEY, TEST_VALUE + "2");
626         config.addProperty("AnotherProperty", "value1;value2;value3");
627         layout.setComment(TEST_KEY, TEST_COMMENT);
628         layout.setForceSingleLine(true);
629         checkLayoutString(
630             "# " + TEST_COMMENT + CR + TEST_KEY + " = " + TEST_VALUE + ';' + TEST_VALUE + "2" + CR + "AnotherProperty = value1;value2;value3" + CR);
631     }
632 
633     /**
634      * Tests setting the global separator. This separator should override the separators for all properties.
635      */
636     @Test
637     public void testSetGlobalSeparator() throws ConfigurationException {
638         final String sep = "=";
639         config.addProperty(TEST_KEY, TEST_VALUE);
640         config.addProperty("key2", "value2");
641         layout.setSeparator(TEST_KEY, " : ");
642         layout.setGlobalSeparator(sep);
643         checkLayoutString(TEST_KEY + sep + TEST_VALUE + CR + "key2" + sep + "value2" + CR);
644     }
645 
646     /**
647      * Tests setting the line separator.
648      */
649     @Test
650     public void testSetLineSeparator() throws ConfigurationException {
651         final String lf = CR + CR;
652         config.addProperty(TEST_KEY, TEST_VALUE);
653         layout.setBlankLinesBefore(TEST_KEY, 2);
654         layout.setComment(TEST_KEY, TEST_COMMENT);
655         layout.setHeaderComment("Header comment");
656         layout.setLineSeparator(lf);
657         checkLayoutString("# Header comment" + lf + lf + lf + "# " + TEST_COMMENT + lf + TEST_KEY + " = " + TEST_VALUE + lf);
658     }
659 
660     /**
661      * Tests whether the line separator is also taken into account within comments.
662      */
663     @Test
664     public void testSetLineSeparatorInComments() throws ConfigurationException {
665         final String lf = "<-\n";
666         config.addProperty(TEST_KEY, TEST_VALUE);
667         layout.setComment(TEST_KEY, TEST_COMMENT + "\nMore comment");
668         layout.setHeaderComment("Header\ncomment");
669         layout.setLineSeparator(lf);
670         checkLayoutString("# Header" + lf + "# comment" + lf + lf + "# " + TEST_COMMENT + lf + "# More comment" + lf + TEST_KEY + " = " + TEST_VALUE + lf);
671     }
672 
673     /**
674      * Tests resetting a comment.
675      */
676     @Test
677     public void testSetNullComment() {
678         fillLayout();
679         layout.setComment(TEST_KEY, null);
680         assertNull(layout.getComment(TEST_KEY));
681     }
682 
683     /**
684      * Tests changing the separator for a property.
685      */
686     @Test
687     public void testSetSeparator() throws ConfigurationException {
688         config.addProperty(TEST_KEY, TEST_VALUE);
689         layout.setSeparator(TEST_KEY, ":");
690         checkLayoutString(TEST_KEY + ":" + TEST_VALUE + CR);
691     }
692 
693     /**
694      * Tests the trimComment method.
695      */
696     @Test
697     public void testTrimComment() {
698         assertEquals("This is a comment" + CR + "that spans multiple" + CR + "lines in a" + CR + " complex way.",
699                 PropertiesConfigurationLayout.trimComment(
700                         "   # This is a comment" + CR + "that spans multiple" + CR + "!lines in a" + CR + " complex way.",
701                         false
702                 ));
703     }
704 
705     /**
706      * Tests enforcing comment characters in a comment.
707      */
708     @Test
709     public void testTrimCommentFalse() {
710         assertEquals("# Comment with" + CR + " ! some mixed " + CR + "#comment" + CR + "# lines",
711                 PropertiesConfigurationLayout.trimComment("Comment with" + CR + " ! some mixed " + CR + "#comment" + CR + "lines", true));
712     }
713 
714     /**
715      * Tests trimming a comment with trailing CRs.
716      */
717     @Test
718     public void testTrimCommentTrainlingCR() {
719         assertEquals("Comment with" + CR + "trailing CR" + CR,
720                 PropertiesConfigurationLayout.trimComment("Comment with" + CR + "! trailing CR" + CR, false));
721     }
722 }