001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. 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,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.commons.configuration;
019:
020: import java.io.File;
021: import java.io.FileWriter;
022: import java.io.IOException;
023: import java.io.PrintWriter;
024: import java.io.Reader;
025: import java.io.StringReader;
026: import java.io.StringWriter;
027: import java.util.ArrayList;
028: import java.util.Iterator;
029: import java.util.List;
030:
031: import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
032:
033: import junit.framework.TestCase;
034:
035: /**
036: * Test for loading and saving properties files.
037: *
038: * @version $Id: TestPropertiesConfiguration.java 439648 2006-09-02 20:42:10Z oheger $
039: */
040: public class TestPropertiesConfiguration extends TestCase {
041: private PropertiesConfiguration conf;
042:
043: /** The File that we test with */
044: private String testProperties = new File("conf/test.properties")
045: .getAbsolutePath();
046:
047: private String testBasePath = new File("conf").getAbsolutePath();
048: private String testBasePath2 = new File("conf").getAbsoluteFile()
049: .getParentFile().getAbsolutePath();
050: private File testSavePropertiesFile = new File(
051: "target/testsave.properties");
052:
053: protected void setUp() throws Exception {
054: conf = new PropertiesConfiguration(testProperties);
055: }
056:
057: public void testLoad() throws Exception {
058: String loaded = conf.getString("configuration.loaded");
059: assertEquals("true", loaded);
060: }
061:
062: /**
063: * Tests if properties can be appended by simply calling load() another
064: * time.
065: */
066: public void testAppend() throws Exception {
067: File file2 = new File("conf/threesome.properties");
068: conf.load(file2);
069: assertEquals("aaa", conf.getString("test.threesome.one"));
070: assertEquals("true", conf.getString("configuration.loaded"));
071: }
072:
073: /**
074: * Tests that empty properties are treated as the empty string
075: * (rather than as null).
076: */
077: public void testEmpty() throws Exception {
078: String empty = conf.getString("test.empty");
079: assertNotNull(empty);
080: assertEquals("", empty);
081: }
082:
083: /**
084: * Tests that references to other properties work
085: */
086: public void testReference() throws Exception {
087: assertEquals("baseextra", conf.getString("base.reference"));
088: }
089:
090: /**
091: * test if includes properties get loaded too
092: */
093: public void testLoadInclude() throws Exception {
094: String loaded = conf.getString("include.loaded");
095: assertEquals("true", loaded);
096: }
097:
098: public void testSetInclude() throws Exception {
099: // change the include key
100: PropertiesConfiguration.setInclude("import");
101:
102: // load the configuration
103: PropertiesConfiguration conf = new PropertiesConfiguration();
104: conf.load("conf/test.properties");
105:
106: // restore the previous value for the other tests
107: PropertiesConfiguration.setInclude("include");
108:
109: assertNull(conf.getString("include.loaded"));
110: }
111:
112: /**
113: * Tests <code>List</code> parsing.
114: */
115: public void testList() throws Exception {
116: List packages = conf.getList("packages");
117: // we should get 3 packages here
118: assertEquals(3, packages.size());
119: }
120:
121: public void testSave() throws Exception {
122: // remove the file previously saved if necessary
123: if (testSavePropertiesFile.exists()) {
124: assertTrue(testSavePropertiesFile.delete());
125: }
126:
127: // add an array of strings to the configuration
128: conf.addProperty("string", "value1");
129: List list = new ArrayList();
130: for (int i = 1; i < 5; i++) {
131: list.add("value" + i);
132: }
133: conf.addProperty("array", list);
134:
135: // save the configuration
136: String filename = testSavePropertiesFile.getAbsolutePath();
137: conf.save(filename);
138:
139: assertTrue("The saved file doesn't exist",
140: testSavePropertiesFile.exists());
141:
142: // read the configuration and compare the properties
143: PropertiesConfiguration checkConfig = new PropertiesConfiguration(
144: filename);
145: for (Iterator i = conf.getKeys(); i.hasNext();) {
146: String key = (String) i.next();
147: assertTrue(
148: "The saved configuration doesn't contain the key '"
149: + key + "'", checkConfig.containsKey(key));
150: assertEquals("Value of the '" + key + "' property", conf
151: .getProperty(key), checkConfig.getProperty(key));
152: }
153:
154: // Save it again, verifing a save with a filename works.
155: checkConfig.save();
156: }
157:
158: public void testInMemoryCreatedSave() throws Exception {
159: // remove the file previously saved if necessary
160: if (testSavePropertiesFile.exists()) {
161: assertTrue(testSavePropertiesFile.delete());
162: }
163:
164: PropertiesConfiguration pc = new PropertiesConfiguration();
165: // add an array of strings to the configuration
166: pc.addProperty("string", "value1");
167: List list = new ArrayList();
168: for (int i = 1; i < 5; i++) {
169: list.add("value" + i);
170: }
171: pc.addProperty("array", list);
172:
173: // save the configuration
174: String filename = testSavePropertiesFile.getAbsolutePath();
175: pc.save(filename);
176:
177: assertTrue("The saved file doesn't exist",
178: testSavePropertiesFile.exists());
179:
180: // read the configuration and compare the properties
181: PropertiesConfiguration checkConfig = new PropertiesConfiguration(
182: filename);
183: for (Iterator i = pc.getKeys(); i.hasNext();) {
184: String key = (String) i.next();
185: assertTrue(
186: "The saved configuration doesn't contain the key '"
187: + key + "'", checkConfig.containsKey(key));
188: assertEquals("Value of the '" + key + "' property", pc
189: .getProperty(key), checkConfig.getProperty(key));
190: }
191:
192: // Save it again, verifing a save with a filename works.
193: checkConfig.save();
194: }
195:
196: public void testSaveMissingFilename() {
197: PropertiesConfiguration pc = new PropertiesConfiguration();
198: try {
199: pc.save();
200: fail("Should have throw ConfigurationException");
201: } catch (ConfigurationException ce) {
202: //good
203: }
204: }
205:
206: /**
207: * Tests if the base path is taken into account by the save() method.
208: * @throws Exception if an error occurs
209: */
210: public void testSaveWithBasePath() throws Exception {
211: // remove the file previously saved if necessary
212: if (testSavePropertiesFile.exists()) {
213: assertTrue(testSavePropertiesFile.delete());
214: }
215:
216: conf.setProperty("test", "true");
217: conf.setBasePath(testSavePropertiesFile.getParentFile().toURL()
218: .toString());
219: conf.setFileName(testSavePropertiesFile.getName());
220: conf.save();
221: assertTrue(testSavePropertiesFile.exists());
222: }
223:
224: public void testLoadViaProperty() throws Exception {
225: PropertiesConfiguration pc = new PropertiesConfiguration();
226: pc.setFileName(testProperties);
227: pc.load();
228:
229: assertTrue("Make sure we have multiple keys", pc
230: .getBoolean("test.boolean"));
231: }
232:
233: public void testLoadViaPropertyWithBasePath() throws Exception {
234: PropertiesConfiguration pc = new PropertiesConfiguration();
235: pc.setBasePath(testBasePath);
236: pc.setFileName("test.properties");
237: pc.load();
238:
239: assertTrue("Make sure we have multiple keys", pc
240: .getBoolean("test.boolean"));
241: }
242:
243: public void testLoadViaPropertyWithBasePath2() throws Exception {
244: PropertiesConfiguration pc = new PropertiesConfiguration();
245: pc.setBasePath(testBasePath2);
246: pc.setFileName("conf/test.properties");
247: pc.load();
248:
249: assertTrue("Make sure we have multiple keys", pc
250: .getBoolean("test.boolean"));
251:
252: pc = new PropertiesConfiguration();
253: pc.setBasePath(testBasePath2);
254: pc.setFileName("conf/test.properties");
255: pc.load();
256:
257: assertTrue("Make sure we have multiple keys", pc
258: .getBoolean("test.boolean"));
259: }
260:
261: public void testLoadFromFile() throws Exception {
262: File file = new File("conf/test.properties");
263: conf = new PropertiesConfiguration(file);
264:
265: assertEquals("true", conf.getString("configuration.loaded"));
266: }
267:
268: public void testLoadUnexistingFile() {
269: try {
270: conf = new PropertiesConfiguration("Unexisting file");
271: fail("Unexisting file was loaded.");
272: } catch (ConfigurationException cex) {
273: // fine
274: }
275: }
276:
277: /**
278: * Tests to load a file with enabled auto save mode.
279: */
280: public void testLoadWithAutoSave() throws Exception {
281: setUpSavedProperties();
282: }
283:
284: /**
285: * Tests the auto save functionality when an existing property is modified.
286: */
287: public void testLoadWithAutoSaveAndSetExisting() throws Exception {
288: setUpSavedProperties();
289: conf.setProperty("a", "moreThanOne");
290: checkSavedConfig();
291: }
292:
293: /**
294: * Tests the auto save functionality when a new property is added using the
295: * setProperty() method.
296: */
297: public void testLoadWithAutoSaveAndSetNew() throws Exception {
298: setUpSavedProperties();
299: conf.setProperty("d", "four");
300: checkSavedConfig();
301: }
302:
303: /**
304: * Tests the auto save functionality when a new property is added using the
305: * addProperty() method.
306: */
307: public void testLoadWithAutoSaveAndAdd() throws Exception {
308: setUpSavedProperties();
309: conf.addProperty("d", "four");
310: checkSavedConfig();
311: }
312:
313: /**
314: * Tests the auto save functionality when a property is removed.
315: */
316: public void testLoadWithAutoSaveAndClear() throws Exception {
317: setUpSavedProperties();
318: conf.clearProperty("c");
319: PropertiesConfiguration checkConfig = checkSavedConfig();
320: assertFalse("The saved configuration contain the key '" + "c"
321: + "'", checkConfig.containsKey("c"));
322: }
323:
324: /**
325: * Creates a properties file on disk. Used for testing load and save
326: * operations.
327: *
328: * @throws IOException if an I/O error occurs
329: */
330: private void setUpSavedProperties() throws IOException,
331: ConfigurationException {
332: PrintWriter out = null;
333:
334: try {
335: out = new PrintWriter(
336: new FileWriter(testSavePropertiesFile));
337: out.println("a = one");
338: out.println("b = two");
339: out.println("c = three");
340: out.close();
341: out = null;
342:
343: conf = new PropertiesConfiguration();
344: conf.setAutoSave(true);
345: conf.setFile(testSavePropertiesFile);
346: conf.load();
347: assertEquals("one", conf.getString("a"));
348: assertEquals("two", conf.getString("b"));
349: assertEquals("three", conf.getString("c"));
350: } finally {
351: if (out != null) {
352: out.close();
353: }
354: }
355: }
356:
357: /**
358: * Helper method for testing a saved configuration. Reads in the file using
359: * a new instance and compares this instance with the original one.
360: *
361: * @return the newly created configuration instance
362: * @throws ConfigurationException if an error occurs
363: */
364: private PropertiesConfiguration checkSavedConfig()
365: throws ConfigurationException {
366: PropertiesConfiguration checkConfig = new PropertiesConfiguration(
367: testSavePropertiesFile);
368: for (Iterator i = conf.getKeys(); i.hasNext();) {
369: String key = (String) i.next();
370: assertTrue(
371: "The saved configuration doesn't contain the key '"
372: + key + "'", checkConfig.containsKey(key));
373: assertEquals("Value of the '" + key + "' property", conf
374: .getProperty(key), checkConfig.getProperty(key));
375: }
376: return checkConfig;
377: }
378:
379: public void testGetStringWithEscapedChars() {
380: String property = conf.getString("test.unescape");
381: assertEquals("String with escaped characters",
382: "This \n string \t contains \" escaped \\ characters",
383: property);
384: }
385:
386: public void testGetStringWithEscapedComma() {
387: String property = conf
388: .getString("test.unescape.list-separator");
389: assertEquals("String with an escaped list separator",
390: "This string contains , an escaped list separator",
391: property);
392: }
393:
394: public void testUnescapeJava() {
395: assertEquals("test\\,test", PropertiesConfiguration
396: .unescapeJava("test\\,test", ','));
397: }
398:
399: public void testEscapedKey() throws Exception {
400: PropertiesConfiguration conf = new PropertiesConfiguration();
401: conf.load(new StringReader("\\u0066\\u006f\\u006f=bar"));
402:
403: assertEquals("value of the 'foo' property", "bar", conf
404: .getString("foo"));
405: }
406:
407: public void testMixedArray() {
408: String[] array = conf.getStringArray("test.mixed.array");
409:
410: assertEquals("array length", 4, array.length);
411: assertEquals("1st element", "a", array[0]);
412: assertEquals("2nd element", "b", array[1]);
413: assertEquals("3rd element", "c", array[2]);
414: assertEquals("4th element", "d", array[3]);
415: }
416:
417: public void testMultilines() {
418: String property = "This is a value spread out across several adjacent "
419: + "natural lines by escaping the line terminator with "
420: + "a backslash character.";
421:
422: assertEquals("'test.multilines' property", property, conf
423: .getString("test.multilines"));
424: }
425:
426: public void testChangingDefaultListDelimiter() throws Exception {
427: PropertiesConfiguration pc = new PropertiesConfiguration(
428: testProperties);
429: assertEquals(4, pc.getList("test.mixed.array").size());
430:
431: char delimiter = PropertiesConfiguration
432: .getDefaultListDelimiter();
433: PropertiesConfiguration.setDefaultListDelimiter('^');
434: pc = new PropertiesConfiguration(testProperties);
435: assertEquals(2, pc.getList("test.mixed.array").size());
436: PropertiesConfiguration.setDefaultListDelimiter(delimiter);
437: }
438:
439: public void testChangingListDelimiter() throws Exception {
440: PropertiesConfiguration pc1 = new PropertiesConfiguration(
441: testProperties);
442: assertEquals(4, pc1.getList("test.mixed.array").size());
443:
444: PropertiesConfiguration pc2 = new PropertiesConfiguration();
445: pc2.setListDelimiter('^');
446: pc2.setFileName(testProperties);
447: pc2.load();
448: assertEquals("Should obtain the first value", "a", pc2
449: .getString("test.mixed.array"));
450: assertEquals(2, pc2.getList("test.mixed.array").size());
451: }
452:
453: public void testDisableListDelimiter() throws Exception {
454: PropertiesConfiguration pc1 = new PropertiesConfiguration(
455: testProperties);
456: assertEquals(4, pc1.getList("test.mixed.array").size());
457:
458: PropertiesConfiguration pc2 = new PropertiesConfiguration();
459: pc2.setDelimiterParsingDisabled(true);
460: pc2.setFileName(testProperties);
461: pc2.load();
462: assertEquals(2, pc2.getList("test.mixed.array").size());
463: }
464:
465: /**
466: * Tests escaping of an end of line with a backslash.
467: */
468: public void testNewLineEscaping() {
469: List list = conf.getList("test.path");
470: assertEquals(3, list.size());
471: assertEquals("C:\\path1\\", list.get(0));
472: assertEquals("C:\\path2\\", list.get(1));
473: assertEquals("C:\\path3\\complex\\test\\", list.get(2));
474: }
475:
476: /**
477: * Tests if included files are loaded when the source lies in the class path.
478: */
479: public void testLoadIncludeFromClassPath()
480: throws ConfigurationException {
481: conf = new PropertiesConfiguration("test.properties");
482: assertEquals("true", conf.getString("include.loaded"));
483: }
484:
485: /**
486: * Test if the lines starting with # or ! are properly ignored.
487: */
488: public void testComment() {
489: assertFalse(
490: "comment line starting with '#' parsed as a property",
491: conf.containsKey("#comment"));
492: assertFalse(
493: "comment line starting with '!' parsed as a property",
494: conf.containsKey("!comment"));
495: }
496:
497: /**
498: * Check that key/value separators can be part of a key.
499: */
500: public void testEscapedKeyValueSeparator() {
501: assertEquals("Escaped separator '=' not supported in keys",
502: "foo", conf.getProperty("test.separator=in.key"));
503: assertEquals("Escaped separator ':' not supported in keys",
504: "bar", conf.getProperty("test.separator:in.key"));
505: assertEquals("Escaped separator '\\t' not supported in keys",
506: "foo", conf.getProperty("test.separator\tin.key"));
507: assertEquals("Escaped separator '\\f' not supported in keys",
508: "bar", conf.getProperty("test.separator\fin.key"));
509: assertEquals("Escaped separator ' ' not supported in keys",
510: "foo", conf.getProperty("test.separator in.key"));
511: }
512:
513: /**
514: * Test all acceptable key/value separators ('=', ':' or white spaces).
515: */
516: public void testKeyValueSeparators() {
517: assertEquals("equal separator not properly parsed", "foo", conf
518: .getProperty("test.separator.equal"));
519: assertEquals("colon separator not properly parsed", "foo", conf
520: .getProperty("test.separator.colon"));
521: assertEquals("tab separator not properly parsed", "foo", conf
522: .getProperty("test.separator.tab"));
523: assertEquals("formfeed separator not properly parsed", "foo",
524: conf.getProperty("test.separator.formfeed"));
525: assertEquals("whitespace separator not properly parsed", "foo",
526: conf.getProperty("test.separator.whitespace"));
527: }
528:
529: /**
530: * Tests including properties when they are loaded from a nested directory
531: * structure.
532: */
533: public void testIncludeInSubDir() throws ConfigurationException {
534: ConfigurationFactory factory = new ConfigurationFactory(
535: "conf/testFactoryPropertiesInclude.xml");
536: Configuration config = factory.getConfiguration();
537: assertEquals(true, config.getBoolean("deeptest"));
538: assertEquals(true, config.getBoolean("deepinclude"));
539: assertFalse(config.containsKey("deeptestinvalid"));
540: }
541:
542: /**
543: * Tests whether the correct line separator is used.
544: */
545: public void testLineSeparator() throws ConfigurationException {
546: final String EOL = System.getProperty("line.separator");
547: conf = new PropertiesConfiguration();
548: conf.setHeader("My header");
549: conf.setProperty("prop", "value");
550:
551: StringWriter out = new StringWriter();
552: conf.save(out);
553: String content = out.toString();
554: assertTrue("Header could not be found", content
555: .indexOf("# My header" + EOL + EOL) == 0);
556: assertTrue("Property could not be found", content
557: .indexOf("prop = value" + EOL) > 0);
558: }
559:
560: /**
561: * Tests what happens if a reloading strategy's <code>reloadingRequired()</code>
562: * implementation accesses methods of the configuration that in turn cause a reload.
563: */
564: public void testReentrantReload() {
565: conf.setProperty("shouldReload", Boolean.FALSE);
566: conf.setReloadingStrategy(new FileChangedReloadingStrategy() {
567: public boolean reloadingRequired() {
568: return configuration.getBoolean("shouldReload");
569: }
570: });
571: assertFalse("Property has wrong value", conf
572: .getBoolean("shouldReload"));
573: }
574:
575: /**
576: * Tests accessing the layout object.
577: */
578: public void testGetLayout() {
579: PropertiesConfigurationLayout layout = conf.getLayout();
580: assertNotNull("Layout is null", layout);
581: assertSame("Different object returned", layout, conf
582: .getLayout());
583: conf.setLayout(null);
584: PropertiesConfigurationLayout layout2 = conf.getLayout();
585: assertNotNull("Layout 2 is null", layout2);
586: assertNotSame("Same object returned", layout, layout2);
587: }
588:
589: /**
590: * Tests the propertyLoaded() method for a simple property.
591: */
592: public void testPropertyLoaded() throws ConfigurationException {
593: DummyLayout layout = new DummyLayout(conf);
594: conf.setLayout(layout);
595: conf.propertyLoaded("layoutLoadedProperty", "yes");
596: assertEquals("Layout's load() was called", 0, layout.loadCalls);
597: assertEquals("Property not added", "yes", conf
598: .getString("layoutLoadedProperty"));
599: }
600:
601: /**
602: * Tests the propertyLoaded() method for an include property.
603: */
604: public void testPropertyLoadedInclude()
605: throws ConfigurationException {
606: DummyLayout layout = new DummyLayout(conf);
607: conf.setLayout(layout);
608: conf.propertyLoaded(PropertiesConfiguration.getInclude(),
609: "testClasspath.properties,testEqual.properties");
610: assertEquals("Layout's load() was not correctly called", 2,
611: layout.loadCalls);
612: assertFalse("Property was added", conf
613: .containsKey(PropertiesConfiguration.getInclude()));
614: }
615:
616: /**
617: * Tests propertyLoaded() for an include property, when includes are
618: * disabled.
619: */
620: public void testPropertyLoadedIncludeNotAllowed()
621: throws ConfigurationException {
622: DummyLayout layout = new DummyLayout(conf);
623: conf.setLayout(layout);
624: conf.setIncludesAllowed(false);
625: conf.propertyLoaded(PropertiesConfiguration.getInclude(),
626: "testClassPath.properties,testEqual.properties");
627: assertEquals("Layout's load() was called", 0, layout.loadCalls);
628: assertFalse("Property was added", conf
629: .containsKey(PropertiesConfiguration.getInclude()));
630: }
631:
632: /**
633: * Tests whether comment lines are correctly detected.
634: */
635: public void testIsCommentLine() {
636: assertTrue("Comment not detected", PropertiesConfiguration
637: .isCommentLine("# a comment"));
638: assertTrue("Alternative comment not detected",
639: PropertiesConfiguration.isCommentLine("! a comment"));
640: assertTrue("Comment with no space not detected",
641: PropertiesConfiguration.isCommentLine("#a comment"));
642: assertTrue("Comment with leading space not detected",
643: PropertiesConfiguration
644: .isCommentLine(" ! a comment"));
645: assertFalse("Wrong comment", PropertiesConfiguration
646: .isCommentLine(" a#comment"));
647: }
648:
649: /**
650: * Tests whether a properties configuration can be successfully cloned. It
651: * is especially checked whether the layout object is taken into account.
652: */
653: public void testClone() throws ConfigurationException {
654: PropertiesConfiguration copy = (PropertiesConfiguration) conf
655: .clone();
656: assertNotSame("Copy has same layout object", conf.getLayout(),
657: copy.getLayout());
658: assertEquals("Wrong number of event listeners for original", 1,
659: conf.getConfigurationListeners().size());
660: assertEquals("Wrong number of event listeners for clone", 1,
661: copy.getConfigurationListeners().size());
662: assertSame("Wrong event listener for original", conf
663: .getLayout(), conf.getConfigurationListeners()
664: .iterator().next());
665: assertSame("Wrong event listener for clone", copy.getLayout(),
666: copy.getConfigurationListeners().iterator().next());
667: StringWriter outConf = new StringWriter();
668: conf.save(outConf);
669: StringWriter outCopy = new StringWriter();
670: copy.save(outCopy);
671: assertEquals("Output from copy is different", outConf
672: .toString(), outCopy.toString());
673: }
674:
675: /**
676: * Tests the clone() method when no layout object exists yet.
677: */
678: public void testCloneNullLayout() {
679: conf = new PropertiesConfiguration();
680: PropertiesConfiguration copy = (PropertiesConfiguration) conf
681: .clone();
682: assertNotSame("Layout objects are the same", conf.getLayout(),
683: copy.getLayout());
684: }
685:
686: /**
687: * A dummy layout implementation for checking whether certain methods are
688: * correctly called by the configuration.
689: */
690: static class DummyLayout extends PropertiesConfigurationLayout {
691: /** Stores the number how often load() was called. */
692: public int loadCalls;
693:
694: public DummyLayout(PropertiesConfiguration config) {
695: super (config);
696: }
697:
698: public void load(Reader in) throws ConfigurationException {
699: loadCalls++;
700: }
701: }
702: }
|