1   /*
2    * Copyright 2001-2005 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.commons.configuration;
18  
19  import java.io.File;
20  import java.io.FileWriter;
21  import java.io.PrintWriter;
22  import java.io.StringReader;
23  import java.io.StringWriter;
24  import java.util.ArrayList;
25  import java.util.Iterator;
26  import java.util.List;
27  
28  import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
29  
30  import junit.framework.TestCase;
31  
32  /***
33   * Test for loading and saving properties files.
34   *
35   * @version $Id: TestPropertiesConfiguration.java 331135 2005-11-06 18:57:39Z oheger $
36   */
37  public class TestPropertiesConfiguration extends TestCase
38  {
39      private PropertiesConfiguration conf;
40  
41      /*** The File that we test with */
42      private String testProperties = new File("conf/test.properties").getAbsolutePath();
43  
44      private String testBasePath = new File("conf").getAbsolutePath();
45      private String testBasePath2 = new File("conf").getAbsoluteFile().getParentFile().getAbsolutePath();
46      private File testSavePropertiesFile = new File("target/testsave.properties");
47  
48      protected void setUp() throws Exception
49      {
50          conf = new PropertiesConfiguration(testProperties);
51      }
52  
53      public void testLoad() throws Exception
54      {
55          String loaded = conf.getString("configuration.loaded");
56          assertEquals("true", loaded);
57      }
58      
59      /***
60       * Tests if properties can be appended by simply calling load() another
61       * time.
62       */
63      public void testAppend() throws Exception
64      {
65          File file2 = new File("conf/threesome.properties");
66          conf.load(file2);
67          assertEquals("aaa", conf.getString("test.threesome.one"));
68          assertEquals("true", conf.getString("configuration.loaded"));
69      }
70  
71      /***
72       * Tests that empty properties are treated as the empty string
73       * (rather than as null).
74       */
75      public void testEmpty() throws Exception
76      {
77          String empty = conf.getString("test.empty");
78          assertNotNull(empty);
79          assertEquals("", empty);
80      }
81  
82      /***
83       * Tests that references to other properties work
84       */
85      public void testReference() throws Exception
86      {
87          assertEquals("baseextra", conf.getString("base.reference"));
88      }
89  
90      /***
91       * test if includes properties get loaded too
92       */
93      public void testLoadInclude() throws Exception
94      {
95          String loaded = conf.getString("include.loaded");
96          assertEquals("true", loaded);
97      }
98  
99      public void testSetInclude() throws Exception
100     {
101         // change the include key
102         PropertiesConfiguration.setInclude("import");
103 
104         // load the configuration
105         PropertiesConfiguration conf = new PropertiesConfiguration();
106         conf.load("conf/test.properties");
107 
108         // restore the previous value for the other tests
109         PropertiesConfiguration.setInclude("include");
110 
111         assertNull(conf.getString("include.loaded"));
112     }
113 
114     /***
115      * Tests <code>List</code> parsing.
116      */
117     public void testList() throws Exception
118     {
119         List packages = conf.getList("packages");
120         // we should get 3 packages here
121         assertEquals(3, packages.size());
122     }
123 
124     public void testSave() throws Exception
125     {
126         // remove the file previously saved if necessary
127         if (testSavePropertiesFile.exists())
128         {
129             assertTrue(testSavePropertiesFile.delete());
130         }
131 
132         // add an array of strings to the configuration
133         conf.addProperty("string", "value1");
134         List list = new ArrayList();
135         for (int i = 1; i < 5; i++)
136         {
137             list.add("value" + i);
138         }
139         conf.addProperty("array", list);
140 
141         // save the configuration
142         String filename = testSavePropertiesFile.getAbsolutePath();
143         conf.save(filename);
144 
145         assertTrue("The saved file doesn't exist", testSavePropertiesFile.exists());
146 
147         // read the configuration and compare the properties
148         PropertiesConfiguration checkConfig = new PropertiesConfiguration(filename);
149         for (Iterator i = conf.getKeys(); i.hasNext();)
150         {
151             String key = (String) i.next();
152             assertTrue("The saved configuration doesn't contain the key '" + key + "'", checkConfig.containsKey(key));
153             assertEquals("Value of the '" + key + "' property", conf.getProperty(key), checkConfig.getProperty(key));
154         }
155 
156         // Save it again, verifing a save with a filename works.
157         checkConfig.save();
158     }
159 
160     public void testSaveMissingFilename()
161     {
162         PropertiesConfiguration pc = new PropertiesConfiguration();
163         try
164         {
165             pc.save();
166             fail("Should have throw ConfigurationException");
167         }
168         catch (ConfigurationException ce)
169         {
170             //good
171         }
172     }
173     
174     /***
175      * Tests if the base path is taken into account by the save() method.
176      * @throws Exception if an error occurs
177      */
178     public void testSaveWithBasePath() throws Exception
179     {
180         // remove the file previously saved if necessary
181         if (testSavePropertiesFile.exists())
182         {
183             assertTrue(testSavePropertiesFile.delete());
184         }
185         
186         conf.setProperty("test", "true");
187         conf.setBasePath(testSavePropertiesFile.getParentFile().toURL().toString());
188         conf.setFileName(testSavePropertiesFile.getName());
189         conf.save();
190         assertTrue(testSavePropertiesFile.exists());
191     }
192     
193     public void testLoadViaProperty() throws Exception
194     {
195         PropertiesConfiguration pc = new PropertiesConfiguration();
196         pc.setFileName(testProperties);
197         pc.load();
198 
199         assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
200     }
201 
202     public void testLoadViaPropertyWithBasePath() throws Exception
203     {
204         PropertiesConfiguration pc = new PropertiesConfiguration();
205         pc.setBasePath(testBasePath);
206         pc.setFileName("test.properties");
207         pc.load();
208 
209         assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
210     }
211 
212     public void testLoadViaPropertyWithBasePath2() throws Exception
213     {
214         PropertiesConfiguration pc = new PropertiesConfiguration();
215         pc.setBasePath(testBasePath2);
216         pc.setFileName("conf/test.properties");
217         pc.load();
218 
219         assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
220 
221         pc = new PropertiesConfiguration();
222         pc.setBasePath(testBasePath2);
223         pc.setFileName("conf/test.properties");
224         pc.load();
225 
226         assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
227     }
228 
229     public void testLoadFromFile() throws Exception
230     {
231         File file = new File("conf/test.properties");
232         conf = new PropertiesConfiguration(file);
233 
234         assertEquals("true", conf.getString("configuration.loaded"));
235     }
236     
237     public void testLoadUnexistingFile()
238     {
239         try
240         {
241             conf = new PropertiesConfiguration("Unexisting file");
242             fail("Unexisting file was loaded.");
243         }
244         catch(ConfigurationException cex)
245         {
246             // fine
247         }
248     }
249     
250     /***
251      * Tests to load a file with enabled auto save mode.
252      */
253     public void testLoadWithAutoSave() throws Exception
254     {
255         PrintWriter out = null;
256         
257         try
258         {
259             out = new PrintWriter(new FileWriter(testSavePropertiesFile));
260             out.println("a = one");
261             out.println("b = two");
262             out.println("c = three");
263             out.close();
264             out = null;
265             
266             conf = new PropertiesConfiguration();
267             conf.setAutoSave(true);
268             conf.setFile(testSavePropertiesFile);
269             conf.load();
270             assertEquals("one", conf.getString("a"));
271             assertEquals("two", conf.getString("b"));
272             assertEquals("three", conf.getString("c"));
273         }
274         finally
275         {
276             if(out != null)
277             {
278                 out.close();
279             }
280         }
281     }
282 
283     public void testGetStringWithEscapedChars()
284     {
285         String property = conf.getString("test.unescape");
286         assertEquals("String with escaped characters", "This \n string \t contains \" escaped // characters", property);
287     }
288 
289     public void testGetStringWithEscapedComma()
290     {
291         String property = conf.getString("test.unescape.list-separator");
292         assertEquals("String with an escaped list separator", "This string contains , an escaped list separator", property);
293     }
294 
295     public void testUnescapeJava()
296     {
297         assertEquals("test//,test", PropertiesConfiguration.unescapeJava("test//,test", ','));
298     }
299 
300     public void testEscapedKey() throws Exception
301     {
302         PropertiesConfiguration conf = new PropertiesConfiguration();
303         conf.load(new StringReader("//u0066//u006f//u006f=bar"));
304 
305         assertEquals("value of the 'foo' property", "bar", conf.getString("foo"));
306     }
307 
308     public void testMixedArray()
309     {
310         String[] array = conf.getStringArray("test.mixed.array");
311 
312         assertEquals("array length", 4, array.length);
313         assertEquals("1st element", "a", array[0]);
314         assertEquals("2nd element", "b", array[1]);
315         assertEquals("3rd element", "c", array[2]);
316         assertEquals("4th element", "d", array[3]);
317     }
318 
319     public void testMultilines()
320     {
321         String property = "This is a value spread out across several adjacent "
322                 + "natural lines by escaping the line terminator with "
323                 + "a backslash character.";
324 
325         assertEquals("'test.multilines' property", property, conf.getString("test.multilines"));
326     }
327 
328     public void testChangingDelimiter() throws Exception
329     {
330         PropertiesConfiguration pc = new PropertiesConfiguration(testProperties);
331         assertEquals(4, pc.getList("test.mixed.array").size());
332 
333         char delimiter = PropertiesConfiguration.getDelimiter();
334         PropertiesConfiguration.setDelimiter('^');
335         pc = new PropertiesConfiguration(testProperties);
336         assertEquals(2, pc.getList("test.mixed.array").size());
337         PropertiesConfiguration.setDelimiter(delimiter);
338     }
339 
340     /***
341      * Tests escaping of an end of line with a backslash.
342      */
343     public void testNewLineEscaping()
344     {
345         List list = conf.getList("test.path");
346         assertEquals(3, list.size());
347         assertEquals("C://path1//", list.get(0));
348         assertEquals("C://path2//", list.get(1));
349         assertEquals("C://path3//complex//test//", list.get(2));
350     }
351 
352     /***
353      * Tests if included files are loaded when the source lies in the class path.
354      */
355     public void testLoadIncludeFromClassPath() throws ConfigurationException
356     {
357         conf = new PropertiesConfiguration("test.properties");
358         assertEquals("true", conf.getString("include.loaded"));
359     }
360 
361     /***
362      * Test if the lines starting with # or ! are properly ignored.
363      */
364     public void testComment() {
365         assertFalse("comment line starting with '#' parsed as a property", conf.containsKey("#comment"));
366         assertFalse("comment line starting with '!' parsed as a property", conf.containsKey("!comment"));
367     }
368 
369     /***
370      * Check that key/value separators can be part of a key.
371      */
372     public void testEscapedKeyValueSeparator()
373     {
374         assertEquals("Escaped separator '=' not supported in keys", "foo", conf.getProperty("test.separator=in.key"));
375         assertEquals("Escaped separator ':' not supported in keys", "bar", conf.getProperty("test.separator:in.key"));
376         assertEquals("Escaped separator '//t' not supported in keys", "foo", conf.getProperty("test.separator\tin.key"));
377         assertEquals("Escaped separator '//f' not supported in keys", "bar", conf.getProperty("test.separator\fin.key"));
378         assertEquals("Escaped separator ' ' not supported in keys"  , "foo", conf.getProperty("test.separator in.key"));
379     }
380 
381     /***
382      * Test all acceptable key/value separators ('=', ':' or white spaces).
383      */
384     public void testKeyValueSeparators() {
385         assertEquals("equal separator not properly parsed",      "foo", conf.getProperty("test.separator.equal"));
386         assertEquals("colon separator not properly parsed",      "foo", conf.getProperty("test.separator.colon"));
387         assertEquals("tab separator not properly parsed",        "foo", conf.getProperty("test.separator.tab"));
388         assertEquals("formfeed separator not properly parsed",   "foo", conf.getProperty("test.separator.formfeed"));
389         assertEquals("whitespace separator not properly parsed", "foo", conf.getProperty("test.separator.whitespace"));
390     }
391     
392     /***
393      * Tests including properties when they are loaded from a nested directory
394      * structure.
395      */
396     public void testIncludeInSubDir() throws ConfigurationException
397     {
398         ConfigurationFactory factory = new ConfigurationFactory("conf/testFactoryPropertiesInclude.xml");
399         Configuration config = factory.getConfiguration();
400         assertEquals(true, config.getBoolean("deeptest"));
401         assertEquals(true, config.getBoolean("deepinclude"));
402         assertFalse(config.containsKey("deeptestinvalid"));
403     }
404 
405     /***
406      * Tests whether the correct line separator is used.
407      */
408     public void testLineSeparator() throws ConfigurationException
409     {
410         final String EOL = System.getProperty("line.separator");
411         conf = new PropertiesConfiguration();
412         conf.setHeader("My header");
413         conf.setProperty("prop", "value");
414 
415         StringWriter out = new StringWriter();
416         conf.save(out);
417         String content = out.toString();
418         assertTrue("Header could not be found", content.indexOf("# My header"
419                 + EOL + EOL) == 0);
420         assertTrue("Property could not be found", content
421                 .indexOf("prop = value" + EOL) > 0);
422     }
423     
424     /***
425      * Tests what happens if a reloading strategy's <code>reloadingRequired()</code>
426      * implementation accesses methods of the configuration that in turn cause a reload.
427      */
428     public void testReentrantReload()
429     {
430         conf.setProperty("shouldReload", Boolean.FALSE);
431         conf.setReloadingStrategy(new FileChangedReloadingStrategy()
432         {
433             public boolean reloadingRequired()
434             {
435                 return configuration.getBoolean("shouldReload");
436             }
437         });
438         assertFalse("Property has wrong value", conf.getBoolean("shouldReload"));
439     }
440 }