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
9
* http://www.apache.org/licenses/LICENSE-2.0
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.
17
package org.apache.commons.configuration;
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;
27
import java.io.Reader;
28
import java.io.StringReader;
29
import java.io.StringWriter;
30
import java.util.Iterator;
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;
38
* Test class for PropertiesConfigurationLayout.
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 $
45
public class TestPropertiesConfigurationLayout
47
/** Constant for the line break character. */
48
private static final String CR = System.getProperty("line.separator");
50
/** Constant for the normalized line break character. */
51
private static final String CRNORM = "\n";
53
/** Constant for a test property key. */
54
static final String TEST_KEY = "myProperty";
56
/** Constant for a test comment. */
57
static final String TEST_COMMENT = "A comment for my test property";
59
/** Constant for a test property value. */
60
static final String TEST_VALUE = "myPropertyValue";
62
/** The layout object under test. */
63
PropertiesConfigurationLayout layout;
65
/** The associated configuration object. */
66
LayoutTestConfiguration config;
68
/** A properties builder that can be used for testing. */
69
PropertiesBuilder builder;
72
public void setUp() throws Exception
74
config = new LayoutTestConfiguration();
75
layout = new PropertiesConfigurationLayout(config);
76
config.setLayout(layout);
77
builder = new PropertiesBuilder();
81
* Tests a newly created instance.
84
public void testInit()
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
94
assertFalse("Force single line flag set", layout.isForceSingleLine());
95
assertNull("Got a global separator", layout.getGlobalSeparator());
99
* Tests creating a layout object with a null configuration. This should
100
* cause an exception.
102
@Test(expected = IllegalArgumentException.class)
103
public void testInitNull()
105
new PropertiesConfigurationLayout(null);
109
* Tests reading a simple properties file.
112
public void testReadSimple() throws ConfigurationException
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));
130
* Tests whether blanc lines before a property are correctly detected.
133
public void testBlancLines() throws ConfigurationException
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));
151
* Tests the single line flag for a simple property definition.
154
public void testIsSingleLine() throws ConfigurationException
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)
164
* Tests the single line flag if there are multiple property definitions.
167
public void testIsSingleLineMulti() throws ConfigurationException
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)
179
* Tests whether comments are combined for multiple occurrences.
182
public void testCombineComments() throws ConfigurationException
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(
193
assertEquals("Wrong combined blanc numbers", 0, layout
194
.getBlancLinesBefore(TEST_KEY));
198
* Tests if a header comment is detected.
201
public void testHeaderComment() throws ConfigurationException
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(
214
* Tests if a header comment containing blanc lines is correctly detected.
217
public void testHeaderCommentWithBlancs() throws ConfigurationException
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));
231
* Tests if a header comment is correctly detected when it contains blanc
232
* lines and the first property has a comment, too.
235
public void testHeaderCommentWithBlancsAndPropComment()
236
throws ConfigurationException
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));
252
* Tests fetching a canonical header comment when no comment is set.
255
public void testHeaderCommentNull()
257
assertNull("No null comment with comment chars", layout
258
.getCanonicalHeaderComment(true));
259
assertNull("No null comment without comment chars", layout
260
.getCanonicalHeaderComment(false));
264
* Tests if a property add event is correctly processed.
267
public void testEventAdd()
269
ConfigurationEvent event = new ConfigurationEvent(this,
270
AbstractConfiguration.EVENT_ADD_PROPERTY, TEST_KEY, TEST_VALUE,
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));
281
* Tests adding a property multiple time through an event. The property
282
* should then be a multi-line property.
285
public void testEventAddMultiple()
287
ConfigurationEvent event = new ConfigurationEvent(this,
288
AbstractConfiguration.EVENT_ADD_PROPERTY, TEST_KEY, TEST_VALUE,
290
layout.configurationChanged(event);
291
layout.configurationChanged(event);
292
assertFalse("No multi-line property", layout.isSingleLine(TEST_KEY));
296
* Tests if an add event is correctly processed if the affected property is
297
* already stored in the layout object.
300
public void testEventAddExisting() throws ConfigurationException
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,
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));
315
* Tests if a set property event for a non existing property is correctly
319
public void testEventSetNonExisting()
321
ConfigurationEvent event = new ConfigurationEvent(this,
322
AbstractConfiguration.EVENT_SET_PROPERTY, TEST_KEY, TEST_VALUE,
324
layout.configurationChanged(event);
325
assertTrue("New property was not found", layout.getKeys().contains(
330
* Tests if a property delete event is correctly processed.
333
public void testEventDelete()
335
ConfigurationEvent event = new ConfigurationEvent(this,
336
AbstractConfiguration.EVENT_ADD_PROPERTY, TEST_KEY, TEST_VALUE,
338
layout.configurationChanged(event);
339
event = new ConfigurationEvent(this,
340
AbstractConfiguration.EVENT_CLEAR_PROPERTY, TEST_KEY, null,
342
layout.configurationChanged(event);
343
assertFalse("Property still existing", layout.getKeys().contains(
348
* Tests if a clear event is correctly processed.
351
public void testEventClearConfig() throws Exception
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());
362
* Tests if a before update event is correctly ignored.
365
public void testEventAddBefore()
367
ConfigurationEvent event = new ConfigurationEvent(this,
368
AbstractConfiguration.EVENT_ADD_PROPERTY, TEST_KEY, TEST_VALUE,
370
layout.configurationChanged(event);
371
assertFalse("Property already stored", layout.getKeys().contains(
376
* Tests if a reload update is correctly processed.
379
public void testEventReload()
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());
390
* Tests the event after a reload has been performed. This should be
394
public void testEventReloadAfter()
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());
405
* Tests a recursive load call.
408
public void testRecursiveLoadCall() throws ConfigurationException
410
PropertiesBuilder b = new PropertiesBuilder();
411
b.addComment("A nested header comment.");
412
b.addComment("With multiple lines");
414
b.addComment("Second comment");
415
b.addProperty(TEST_KEY, TEST_VALUE);
416
b.addProperty(TEST_KEY + "2", TEST_VALUE + "2");
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");
426
layout.load(builder.getReader());
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,
439
* Tests whether the output of the layout object is identical to the source
440
* file (at least for simple properties files).
443
public void testReadAndWrite() throws ConfigurationException
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());
459
* Tests if the content of the layout object is correctly written.
462
public void testSave() throws ConfigurationException
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);
481
* Tests the force single line flag.
484
public void testSaveForceSingleLine() throws ConfigurationException
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);
498
* Tests the trimComment method.
501
public void testTrimComment()
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));
511
* Tests trimming a comment with trailing CRs.
514
public void testTrimCommentTrainlingCR()
516
assertEquals("Wrong trimmed comment", "Comment with" + CR
517
+ "trailing CR" + CR, PropertiesConfigurationLayout
518
.trimComment("Comment with" + CR + "! trailing CR" + CR, false));
522
* Tests enforcing comment characters in a comment.
525
public void testTrimCommentFalse()
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",
535
* Tests accessing data for a property, which is not stored.
538
public void testGetNonExistingLayouData()
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"));
547
* Tests accessing a property with a null key. This should throw an
550
@Test(expected = IllegalArgumentException.class)
551
public void testGetNullLayouttData()
553
layout.setComment(null, TEST_COMMENT);
557
* Tests resetting a comment.
560
public void testSetNullComment()
563
layout.setComment(TEST_KEY, null);
564
assertNull("Comment was not reset", layout.getComment(TEST_KEY));
568
* Tests saving when a comment for a non existing property is contained in
569
* the layout object. This comment should be ignored.
572
public void testSaveCommentForUnexistingProperty()
573
throws ConfigurationException
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);
585
* Tests saving an empty layout object.
588
public void testSaveEmptyLayout() throws ConfigurationException
590
checkLayoutString("");
594
* Tests the copy constructor.
597
public void testInitCopy()
600
PropertiesConfigurationLayout l2 = new PropertiesConfigurationLayout(
602
assertEquals("Wrong number of keys", layout.getKeys().size(), l2
604
for (Iterator<String> it = layout.getKeys().iterator(); it.hasNext();)
606
Object key = it.next();
607
assertTrue("Key was not found: " + key, l2.getKeys().contains(key));
612
* Tests if the copy and the original are independent from each other.
615
public void testInitCopyModify()
618
PropertiesConfigurationLayout l2 = new PropertiesConfigurationLayout(
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));
632
* Tests changing the separator for a property.
635
public void testSetSeparator() throws ConfigurationException
637
config.addProperty(TEST_KEY, TEST_VALUE);
638
layout.setSeparator(TEST_KEY, ":");
639
checkLayoutString(TEST_KEY + ":" + TEST_VALUE + CR);
643
* Tests setting the global separator. This separator should override the
644
* separators for all properties.
647
public void testSetGlobalSeparator() throws ConfigurationException
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
659
* Tests setting the line separator.
662
public void testSetLineSeparator() throws ConfigurationException
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);
675
* Tests whether the line separator is also taken into account within
679
public void testSetLineSeparatorInComments() throws ConfigurationException
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 + " = "
692
* Helper method for filling the layout object with some properties.
694
private void fillLayout()
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");
704
layout.load(builder.getReader());
706
catch (ConfigurationException cex)
709
fail("Exception was thrown: " + cex);
714
* Writes the layout's data into a string.
716
* @return the layout file's content as string
717
* @throws ConfigurationException if an error occurs
719
private String getLayoutString() throws ConfigurationException
721
StringWriter out = new StringWriter();
723
return out.toString();
727
* Checks if the layout's output is correct.
729
* @param expected the expected result
730
* @throws ConfigurationException if an error occurs
732
private void checkLayoutString(String expected)
733
throws ConfigurationException
735
assertEquals("Wrong layout file content", expected, getLayoutString());
739
* A helper class used for constructing test properties files.
741
static class PropertiesBuilder
743
/** A buffer for storing the data. */
744
private StringBuilder buf = new StringBuilder();
746
/** A counter for varying the comment character. */
747
private int commentCounter;
750
* Adds a property to the simulated file.
752
* @param key the property key
753
* @param value the value
755
public void addProperty(String key, String value)
757
buf.append(key).append(" = ").append(value).append(CR);
761
* Adds a comment line.
763
* @param s the comment (can be <b>null</b>, then a blanc line is
766
public void addComment(String s)
770
if (commentCounter % 2 == 0)
785
* Returns a reader for the simulated properties.
789
public Reader getReader()
791
return new StringReader(buf.toString());
795
* Returns a string representation of the buffer's content.
797
* @return the buffer as string
800
public String toString()
802
return buf.toString();
807
* A mock properties configuration implementation that is used to check
808
* whether some expected methods are called.
810
static class LayoutTestConfiguration extends PropertiesConfiguration
812
/** Stores a builder object. */
813
public PropertiesBuilder builder;
816
* Simulates the propertyLoaded() callback. If a builder was set, a
817
* load() call on the layout is invoked.
820
boolean propertyLoaded(String key, String value)
821
throws ConfigurationException
825
return super.propertyLoaded(key, value);
829
if (PropertiesConfiguration.getInclude().equals(key))
831
getLayout().load(builder.getReader());