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 static org.junit.Assert.assertEquals; 021 import static org.junit.Assert.assertFalse; 022 import static org.junit.Assert.assertNotNull; 023 import static org.junit.Assert.assertNotSame; 024 import static org.junit.Assert.assertNull; 025 import static org.junit.Assert.assertSame; 026 import static org.junit.Assert.assertTrue; 027 import static org.junit.Assert.fail; 028 029 import java.io.BufferedReader; 030 import java.io.File; 031 import java.io.FileOutputStream; 032 import java.io.FileReader; 033 import java.io.FileWriter; 034 import java.io.IOException; 035 import java.io.InputStream; 036 import java.io.OutputStream; 037 import java.io.PrintWriter; 038 import java.io.Reader; 039 import java.io.StringReader; 040 import java.io.StringWriter; 041 import java.io.Writer; 042 import java.net.HttpURLConnection; 043 import java.net.URL; 044 import java.net.URLConnection; 045 import java.net.URLStreamHandler; 046 import java.util.ArrayList; 047 import java.util.Arrays; 048 import java.util.HashSet; 049 import java.util.Iterator; 050 import java.util.List; 051 import java.util.Set; 052 053 import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy; 054 import org.apache.commons.lang.SystemUtils; 055 import org.junit.Before; 056 import org.junit.Test; 057 058 /** 059 * Test for loading and saving properties files. 060 * 061 * @version $Id: TestPropertiesConfiguration.java 1330666 2012-04-26 06:12:30Z oheger $ 062 */ 063 public class TestPropertiesConfiguration 064 { 065 /** Constant for a test property name.*/ 066 private static final String PROP_NAME = "testProperty"; 067 068 /** Constant for a test property value.*/ 069 private static final String PROP_VALUE = "value"; 070 071 /** The configuration to be tested.*/ 072 private PropertiesConfiguration conf; 073 074 /** The File that we test with */ 075 private static String testProperties = ConfigurationAssert.getTestFile("test.properties").getAbsolutePath(); 076 077 private static String testBasePath = ConfigurationAssert.TEST_DIR.getAbsolutePath(); 078 private static String testBasePath2 = ConfigurationAssert.TEST_DIR.getParentFile().getAbsolutePath(); 079 private static File testSavePropertiesFile = ConfigurationAssert.getOutFile("testsave.properties"); 080 081 @Before 082 public void setUp() throws Exception 083 { 084 conf = new PropertiesConfiguration(testProperties); 085 086 // remove the test save file if it exists 087 if (testSavePropertiesFile.exists()) 088 { 089 assertTrue("Test output file could not be deleted", 090 testSavePropertiesFile.delete()); 091 } 092 } 093 094 @Test 095 public void testLoad() throws Exception 096 { 097 String loaded = conf.getString("configuration.loaded"); 098 assertEquals("true", loaded); 099 } 100 101 /** 102 * Tests if properties can be appended by simply calling load() another 103 * time. 104 */ 105 @Test 106 public void testAppend() throws Exception 107 { 108 File file2 = ConfigurationAssert.getTestFile("threesome.properties"); 109 conf.load(file2); 110 assertEquals("aaa", conf.getString("test.threesome.one")); 111 assertEquals("true", conf.getString("configuration.loaded")); 112 } 113 114 /** 115 * Tests that empty properties are treated as the empty string 116 * (rather than as null). 117 */ 118 @Test 119 public void testEmpty() throws Exception 120 { 121 String empty = conf.getString("test.empty"); 122 assertNotNull(empty); 123 assertEquals("", empty); 124 } 125 126 /** 127 * Tests that references to other properties work 128 */ 129 @Test 130 public void testReference() throws Exception 131 { 132 assertEquals("baseextra", conf.getString("base.reference")); 133 } 134 135 /** 136 * test if includes properties get loaded too 137 */ 138 @Test 139 public void testLoadInclude() throws Exception 140 { 141 String loaded = conf.getString("include.loaded"); 142 assertEquals("true", loaded); 143 } 144 145 /** 146 * test if includes properties from interpolated file 147 * name get loaded 148 */ 149 @Test 150 public void testLoadIncludeInterpol() throws Exception 151 { 152 String loaded = conf.getString("include.interpol.loaded"); 153 assertEquals("true", loaded); 154 } 155 156 @Test 157 public void testSetInclude() throws Exception 158 { 159 // change the include key 160 PropertiesConfiguration.setInclude("import"); 161 162 // load the configuration 163 PropertiesConfiguration conf = new PropertiesConfiguration(); 164 conf.load(ConfigurationAssert.getTestFile("test.properties")); 165 166 // restore the previous value for the other tests 167 PropertiesConfiguration.setInclude("include"); 168 169 assertNull(conf.getString("include.loaded")); 170 } 171 172 /** 173 * Tests {@code List} parsing. 174 */ 175 @Test 176 public void testList() throws Exception 177 { 178 List<Object> packages = conf.getList("packages"); 179 // we should get 3 packages here 180 assertEquals(3, packages.size()); 181 } 182 183 @Test 184 public void testSave() throws Exception 185 { 186 // add an array of strings to the configuration 187 conf.addProperty("string", "value1"); 188 List<Object> list = new ArrayList<Object>(); 189 for (int i = 1; i < 5; i++) 190 { 191 list.add("value" + i); 192 } 193 conf.addProperty("array", list); 194 195 // save the configuration 196 String filename = testSavePropertiesFile.getAbsolutePath(); 197 conf.save(filename); 198 199 assertTrue("The saved file doesn't exist", testSavePropertiesFile.exists()); 200 201 // read the configuration and compare the properties 202 PropertiesConfiguration checkConfig = new PropertiesConfiguration(filename); 203 ConfigurationAssert.assertEquals(conf, checkConfig); 204 205 // Save it again, verifying a save with a filename works. 206 checkConfig.save(); 207 } 208 209 @Test 210 public void testSaveToCustomURL() throws Exception 211 { 212 // save the configuration to a custom URL 213 URL url = new URL("foo", "", 0, "./target/testsave-custom-url.properties", new FileURLStreamHandler()); 214 conf.save(url); 215 216 // reload the configuration 217 Configuration config2 = new PropertiesConfiguration(url); 218 assertEquals("true", config2.getString("configuration.loaded")); 219 } 220 221 @Test 222 public void testInMemoryCreatedSave() throws Exception 223 { 224 PropertiesConfiguration pc = new PropertiesConfiguration(); 225 // add an array of strings to the configuration 226 pc.addProperty("string", "value1"); 227 List<Object> list = new ArrayList<Object>(); 228 for (int i = 1; i < 5; i++) 229 { 230 list.add("value" + i); 231 } 232 pc.addProperty("array", list); 233 234 // save the configuration 235 String filename = testSavePropertiesFile.getAbsolutePath(); 236 pc.save(filename); 237 238 assertTrue("The saved file doesn't exist", testSavePropertiesFile.exists()); 239 240 // read the configuration and compare the properties 241 PropertiesConfiguration checkConfig = new PropertiesConfiguration(filename); 242 ConfigurationAssert.assertEquals(pc, checkConfig); 243 244 // Save it again, verifying a save with a filename works. 245 checkConfig.save(); 246 } 247 248 /** 249 * Tests saving a configuration when delimiter parsing is disabled. 250 */ 251 @Test 252 public void testSaveWithDelimiterParsingDisabled() throws ConfigurationException 253 { 254 conf.clear(); 255 conf.setDelimiterParsingDisabled(true); 256 conf.addProperty("test.list", "a,b,c"); 257 conf.addProperty("test.dirs", "C:\\Temp\\,D:\\Data\\"); 258 conf.save(testSavePropertiesFile); 259 260 PropertiesConfiguration checkConfig = new PropertiesConfiguration(); 261 checkConfig.setDelimiterParsingDisabled(true); 262 checkConfig.setFile(testSavePropertiesFile); 263 checkConfig.load(); 264 ConfigurationAssert.assertEquals(conf, checkConfig); 265 } 266 267 @Test(expected = ConfigurationException.class) 268 public void testSaveMissingFilename() throws ConfigurationException 269 { 270 PropertiesConfiguration pc = new PropertiesConfiguration(); 271 pc.save(); 272 } 273 274 /** 275 * Tests if the base path is taken into account by the save() method. 276 * @throws Exception if an error occurs 277 */ 278 @Test 279 public void testSaveWithBasePath() throws Exception 280 { 281 conf.setProperty("test", "true"); 282 conf.setBasePath(testSavePropertiesFile.getParentFile().toURI().toURL() 283 .toString()); 284 conf.setFileName(testSavePropertiesFile.getName()); 285 conf.save(); 286 assertTrue(testSavePropertiesFile.exists()); 287 } 288 289 /** 290 * Tests whether the escape character for list delimiters can be itself 291 * escaped and survives a save operation. 292 */ 293 @Test 294 public void testSaveEscapedEscapingCharacter() 295 throws ConfigurationException 296 { 297 conf.addProperty("test.dirs", "C:\\Temp\\\\,D:\\Data\\\\,E:\\Test\\"); 298 List<Object> dirs = conf.getList("test.dirs"); 299 assertEquals("Wrong number of list elements", 3, dirs.size()); 300 conf.save(testSavePropertiesFile); 301 302 PropertiesConfiguration checkConfig = new PropertiesConfiguration( 303 testSavePropertiesFile); 304 ConfigurationAssert.assertEquals(conf, checkConfig); 305 } 306 307 @Test 308 public void testLoadViaProperty() throws Exception 309 { 310 PropertiesConfiguration pc = new PropertiesConfiguration(); 311 pc.setFileName(testProperties); 312 pc.load(); 313 314 assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean")); 315 } 316 317 @Test 318 public void testLoadViaPropertyWithBasePath() throws Exception 319 { 320 PropertiesConfiguration pc = new PropertiesConfiguration(); 321 pc.setBasePath(testBasePath); 322 pc.setFileName("test.properties"); 323 pc.load(); 324 325 assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean")); 326 } 327 328 @Test 329 public void testLoadViaPropertyWithBasePath2() throws Exception 330 { 331 PropertiesConfiguration pc = new PropertiesConfiguration(); 332 pc.setBasePath(testBasePath2); 333 pc.setFileName("test.properties"); 334 pc.load(); 335 336 assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean")); 337 338 pc = new PropertiesConfiguration(); 339 pc.setBasePath(testBasePath2); 340 pc.setFileName("test.properties"); 341 pc.load(); 342 343 assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean")); 344 } 345 346 @Test 347 public void testLoadFromFile() throws Exception 348 { 349 File file = ConfigurationAssert.getTestFile("test.properties"); 350 conf = new PropertiesConfiguration(file); 351 352 assertEquals("true", conf.getString("configuration.loaded")); 353 } 354 355 @Test(expected = ConfigurationException.class) 356 public void testLoadUnexistingFile() throws ConfigurationException 357 { 358 conf = new PropertiesConfiguration("Unexisting file"); 359 } 360 361 /** 362 * Tests to load a file with enabled auto save mode. 363 */ 364 @Test 365 public void testLoadWithAutoSave() throws Exception 366 { 367 setUpSavedProperties(); 368 } 369 370 /** 371 * Tests the auto save functionality when an existing property is modified. 372 */ 373 @Test 374 public void testLoadWithAutoSaveAndSetExisting() throws Exception 375 { 376 setUpSavedProperties(); 377 conf.setProperty("a", "moreThanOne"); 378 checkSavedConfig(); 379 } 380 381 /** 382 * Tests the auto save functionality when a new property is added using the 383 * setProperty() method. 384 */ 385 @Test 386 public void testLoadWithAutoSaveAndSetNew() throws Exception 387 { 388 setUpSavedProperties(); 389 conf.setProperty("d", "four"); 390 checkSavedConfig(); 391 } 392 393 /** 394 * Tests the auto save functionality when a new property is added using the 395 * addProperty() method. 396 */ 397 @Test 398 public void testLoadWithAutoSaveAndAdd() throws Exception 399 { 400 setUpSavedProperties(); 401 conf.addProperty("d", "four"); 402 checkSavedConfig(); 403 } 404 405 /** 406 * Tests the auto save functionality when a property is removed. 407 */ 408 @Test 409 public void testLoadWithAutoSaveAndClear() throws Exception 410 { 411 setUpSavedProperties(); 412 conf.clearProperty("c"); 413 PropertiesConfiguration checkConfig = checkSavedConfig(); 414 assertFalse("The saved configuration contain the key '" + "c" + "'", checkConfig.containsKey("c")); 415 } 416 417 /** 418 * Creates a properties file on disk. Used for testing load and save 419 * operations. 420 * 421 * @throws IOException if an I/O error occurs 422 */ 423 private void setUpSavedProperties() throws IOException, ConfigurationException 424 { 425 PrintWriter out = null; 426 427 try 428 { 429 out = new PrintWriter(new FileWriter(testSavePropertiesFile)); 430 out.println("a = one"); 431 out.println("b = two"); 432 out.println("c = three"); 433 out.close(); 434 out = null; 435 436 conf = new PropertiesConfiguration(); 437 conf.setAutoSave(true); 438 conf.setFile(testSavePropertiesFile); 439 conf.load(); 440 assertEquals("one", conf.getString("a")); 441 assertEquals("two", conf.getString("b")); 442 assertEquals("three", conf.getString("c")); 443 } 444 finally 445 { 446 if (out != null) 447 { 448 out.close(); 449 } 450 } 451 } 452 453 /** 454 * Helper method for testing a saved configuration. Reads in the file using 455 * a new instance and compares this instance with the original one. 456 * 457 * @return the newly created configuration instance 458 * @throws ConfigurationException if an error occurs 459 */ 460 private PropertiesConfiguration checkSavedConfig() 461 throws ConfigurationException 462 { 463 PropertiesConfiguration checkConfig = new PropertiesConfiguration(testSavePropertiesFile); 464 ConfigurationAssert.assertEquals(conf, checkConfig); 465 return checkConfig; 466 } 467 468 @Test 469 public void testGetStringWithEscapedChars() 470 { 471 String property = conf.getString("test.unescape"); 472 assertEquals("String with escaped characters", "This \n string \t contains \" escaped \\ characters", property); 473 } 474 475 @Test 476 public void testGetStringWithEscapedComma() 477 { 478 String property = conf.getString("test.unescape.list-separator"); 479 assertEquals("String with an escaped list separator", "This string contains , an escaped list separator", property); 480 } 481 482 @Test 483 public void testUnescapeJava() 484 { 485 assertEquals("test\\,test", PropertiesConfiguration.unescapeJava("test\\,test", ',')); 486 } 487 488 @Test 489 public void testEscapedKey() throws Exception 490 { 491 PropertiesConfiguration conf = new PropertiesConfiguration(); 492 conf.load(new StringReader("\\u0066\\u006f\\u006f=bar")); 493 494 assertEquals("value of the 'foo' property", "bar", conf.getString("foo")); 495 } 496 497 @Test 498 public void testMixedArray() 499 { 500 String[] array = conf.getStringArray("test.mixed.array"); 501 502 assertEquals("array length", 4, array.length); 503 assertEquals("1st element", "a", array[0]); 504 assertEquals("2nd element", "b", array[1]); 505 assertEquals("3rd element", "c", array[2]); 506 assertEquals("4th element", "d", array[3]); 507 } 508 509 @Test 510 public void testMultilines() 511 { 512 String property = "This is a value spread out across several adjacent " 513 + "natural lines by escaping the line terminator with " 514 + "a backslash character."; 515 516 assertEquals("'test.multilines' property", property, conf.getString("test.multilines")); 517 } 518 519 @Test 520 public void testChangingDefaultListDelimiter() throws Exception 521 { 522 PropertiesConfiguration pc = new PropertiesConfiguration(testProperties); 523 assertEquals(4, pc.getList("test.mixed.array").size()); 524 525 char delimiter = PropertiesConfiguration.getDefaultListDelimiter(); 526 PropertiesConfiguration.setDefaultListDelimiter('^'); 527 pc = new PropertiesConfiguration(testProperties); 528 assertEquals(2, pc.getList("test.mixed.array").size()); 529 PropertiesConfiguration.setDefaultListDelimiter(delimiter); 530 } 531 532 @Test 533 public void testChangingListDelimiter() throws Exception 534 { 535 PropertiesConfiguration pc1 = new PropertiesConfiguration(testProperties); 536 assertEquals(4, pc1.getList("test.mixed.array").size()); 537 538 PropertiesConfiguration pc2 = new PropertiesConfiguration(); 539 pc2.setListDelimiter('^'); 540 pc2.setFileName(testProperties); 541 pc2.load(); 542 assertEquals("Should obtain the first value", "a", pc2.getString("test.mixed.array")); 543 assertEquals(2, pc2.getList("test.mixed.array").size()); 544 } 545 546 @Test 547 public void testDisableListDelimiter() throws Exception 548 { 549 PropertiesConfiguration pc1 = new PropertiesConfiguration(testProperties); 550 assertEquals(4, pc1.getList("test.mixed.array").size()); 551 552 PropertiesConfiguration pc2 = new PropertiesConfiguration(); 553 pc2.setDelimiterParsingDisabled(true); 554 pc2.setFileName(testProperties); 555 pc2.load(); 556 assertEquals(2, pc2.getList("test.mixed.array").size()); 557 } 558 559 /** 560 * Tests escaping of an end of line with a backslash. 561 */ 562 @Test 563 public void testNewLineEscaping() 564 { 565 List<Object> list = conf.getList("test.path"); 566 assertEquals(3, list.size()); 567 assertEquals("C:\\path1\\", list.get(0)); 568 assertEquals("C:\\path2\\", list.get(1)); 569 assertEquals("C:\\path3\\complex\\test\\", list.get(2)); 570 } 571 572 /** 573 * Tests if included files are loaded when the source lies in the class path. 574 */ 575 @Test 576 public void testLoadIncludeFromClassPath() throws ConfigurationException 577 { 578 conf = new PropertiesConfiguration("test.properties"); 579 assertEquals("true", conf.getString("include.loaded")); 580 } 581 582 /** 583 * Test if the lines starting with # or ! are properly ignored. 584 */ 585 @Test 586 public void testComment() { 587 assertFalse("comment line starting with '#' parsed as a property", conf.containsKey("#comment")); 588 assertFalse("comment line starting with '!' parsed as a property", conf.containsKey("!comment")); 589 } 590 591 /** 592 * Check that key/value separators can be part of a key. 593 */ 594 @Test 595 public void testEscapedKeyValueSeparator() 596 { 597 assertEquals("Escaped separator '=' not supported in keys", "foo", conf.getProperty("test.separator=in.key")); 598 assertEquals("Escaped separator ':' not supported in keys", "bar", conf.getProperty("test.separator:in.key")); 599 assertEquals("Escaped separator '\\t' not supported in keys", "foo", conf.getProperty("test.separator\tin.key")); 600 assertEquals("Escaped separator '\\f' not supported in keys", "bar", conf.getProperty("test.separator\fin.key")); 601 assertEquals("Escaped separator ' ' not supported in keys" , "foo", conf.getProperty("test.separator in.key")); 602 } 603 604 /** 605 * Test all acceptable key/value separators ('=', ':' or white spaces). 606 */ 607 @Test 608 public void testKeyValueSeparators() { 609 assertEquals("equal separator not properly parsed", "foo", conf.getProperty("test.separator.equal")); 610 assertEquals("colon separator not properly parsed", "foo", conf.getProperty("test.separator.colon")); 611 assertEquals("tab separator not properly parsed", "foo", conf.getProperty("test.separator.tab")); 612 assertEquals("formfeed separator not properly parsed", "foo", conf.getProperty("test.separator.formfeed")); 613 assertEquals("whitespace separator not properly parsed", "foo", conf.getProperty("test.separator.whitespace")); 614 } 615 616 /** 617 * Tests including properties when they are loaded from a nested directory 618 * structure. 619 */ 620 @SuppressWarnings("deprecation") 621 @Test 622 public void testIncludeInSubDir() throws ConfigurationException 623 { 624 ConfigurationFactory factory = new ConfigurationFactory("conf/testFactoryPropertiesInclude.xml"); 625 Configuration config = factory.getConfiguration(); 626 assertTrue(config.getBoolean("deeptest")); 627 assertTrue(config.getBoolean("deepinclude")); 628 assertFalse(config.containsKey("deeptestinvalid")); 629 } 630 631 /** 632 * Tests whether the correct line separator is used. 633 */ 634 @Test 635 public void testLineSeparator() throws ConfigurationException 636 { 637 final String EOL = System.getProperty("line.separator"); 638 conf = new PropertiesConfiguration(); 639 conf.setHeader("My header"); 640 conf.setProperty("prop", "value"); 641 642 StringWriter out = new StringWriter(); 643 conf.save(out); 644 String content = out.toString(); 645 assertTrue("Header could not be found", content.indexOf("# My header" 646 + EOL + EOL) == 0); 647 assertTrue("Property could not be found", content.indexOf("prop = value" + EOL) > 0); 648 } 649 650 /** 651 * Tests what happens if a reloading strategy's <code>reloadingRequired()</code> 652 * implementation accesses methods of the configuration that in turn cause a reload. 653 */ 654 @Test 655 public void testReentrantReload() 656 { 657 conf.setProperty("shouldReload", Boolean.FALSE); 658 conf.setReloadingStrategy(new FileChangedReloadingStrategy() 659 { 660 @Override 661 public boolean reloadingRequired() 662 { 663 return configuration.getBoolean("shouldReload"); 664 } 665 }); 666 assertFalse("Property has wrong value", conf.getBoolean("shouldReload")); 667 } 668 669 /** 670 * Tests accessing the layout object. 671 */ 672 @Test 673 public void testGetLayout() 674 { 675 PropertiesConfigurationLayout layout = conf.getLayout(); 676 assertNotNull("Layout is null", layout); 677 assertSame("Different object returned", layout, conf.getLayout()); 678 conf.setLayout(null); 679 PropertiesConfigurationLayout layout2 = conf.getLayout(); 680 assertNotNull("Layout 2 is null", layout2); 681 assertNotSame("Same object returned", layout, layout2); 682 } 683 684 /** 685 * Tests the propertyLoaded() method for a simple property. 686 */ 687 @Test 688 public void testPropertyLoaded() throws ConfigurationException 689 { 690 DummyLayout layout = new DummyLayout(conf); 691 conf.setLayout(layout); 692 conf.propertyLoaded("layoutLoadedProperty", "yes"); 693 assertEquals("Layout's load() was called", 0, layout.loadCalls); 694 assertEquals("Property not added", "yes", conf.getString("layoutLoadedProperty")); 695 } 696 697 /** 698 * Tests the propertyLoaded() method for an include property. 699 */ 700 @Test 701 public void testPropertyLoadedInclude() throws ConfigurationException 702 { 703 DummyLayout layout = new DummyLayout(conf); 704 conf.setLayout(layout); 705 conf.propertyLoaded(PropertiesConfiguration.getInclude(), "testClasspath.properties,testEqual.properties"); 706 assertEquals("Layout's load() was not correctly called", 2, layout.loadCalls); 707 assertFalse("Property was added", conf.containsKey(PropertiesConfiguration.getInclude())); 708 } 709 710 /** 711 * Tests propertyLoaded() for an include property, when includes are 712 * disabled. 713 */ 714 @Test 715 public void testPropertyLoadedIncludeNotAllowed() throws ConfigurationException 716 { 717 DummyLayout layout = new DummyLayout(conf); 718 conf.setLayout(layout); 719 conf.setIncludesAllowed(false); 720 conf.propertyLoaded(PropertiesConfiguration.getInclude(), "testClassPath.properties,testEqual.properties"); 721 assertEquals("Layout's load() was called", 0, layout.loadCalls); 722 assertFalse("Property was added", conf.containsKey(PropertiesConfiguration.getInclude())); 723 } 724 725 /** 726 * Tests whether comment lines are correctly detected. 727 */ 728 @Test 729 public void testIsCommentLine() 730 { 731 assertTrue("Comment not detected", PropertiesConfiguration.isCommentLine("# a comment")); 732 assertTrue("Alternative comment not detected", PropertiesConfiguration.isCommentLine("! a comment")); 733 assertTrue("Comment with no space not detected", PropertiesConfiguration.isCommentLine("#a comment")); 734 assertTrue("Comment with leading space not detected", PropertiesConfiguration.isCommentLine(" ! a comment")); 735 assertFalse("Wrong comment", PropertiesConfiguration.isCommentLine(" a#comment")); 736 } 737 738 /** 739 * Tests whether a properties configuration can be successfully cloned. It 740 * is especially checked whether the layout object is taken into account. 741 */ 742 @Test 743 public void testClone() throws ConfigurationException 744 { 745 PropertiesConfiguration copy = (PropertiesConfiguration) conf.clone(); 746 assertNotSame("Copy has same layout object", conf.getLayout(), copy.getLayout()); 747 assertEquals("Wrong number of event listeners for original", 1, conf.getConfigurationListeners().size()); 748 assertEquals("Wrong number of event listeners for clone", 1, copy.getConfigurationListeners().size()); 749 assertSame("Wrong event listener for original", conf.getLayout(), conf.getConfigurationListeners().iterator().next()); 750 assertSame("Wrong event listener for clone", copy.getLayout(), copy.getConfigurationListeners().iterator().next()); 751 StringWriter outConf = new StringWriter(); 752 conf.save(outConf); 753 StringWriter outCopy = new StringWriter(); 754 copy.save(outCopy); 755 assertEquals("Output from copy is different", outConf.toString(), outCopy.toString()); 756 } 757 758 /** 759 * Tests the clone() method when no layout object exists yet. 760 */ 761 @Test 762 public void testCloneNullLayout() 763 { 764 conf = new PropertiesConfiguration(); 765 PropertiesConfiguration copy = (PropertiesConfiguration) conf.clone(); 766 assertNotSame("Layout objects are the same", conf.getLayout(), copy.getLayout()); 767 } 768 769 /** 770 * Tests saving a file-based configuration to a HTTP server. 771 */ 772 @Test 773 public void testSaveToHTTPServerSuccess() throws Exception 774 { 775 MockHttpURLStreamHandler handler = new MockHttpURLStreamHandler( 776 HttpURLConnection.HTTP_OK, testSavePropertiesFile); 777 URL url = new URL(null, "http://jakarta.apache.org", handler); 778 conf.save(url); 779 MockHttpURLConnection con = handler.getMockConnection(); 780 assertTrue("Wrong output flag", con.getDoOutput()); 781 assertEquals("Wrong method", "PUT", con.getRequestMethod()); 782 783 PropertiesConfiguration checkConfig = new PropertiesConfiguration( 784 testSavePropertiesFile); 785 ConfigurationAssert.assertEquals(conf, checkConfig); 786 } 787 788 /** 789 * Tests saving a file-based configuration to a HTTP server when the server 790 * reports a failure. This should cause an exception. 791 */ 792 @Test 793 public void testSaveToHTTPServerFail() throws Exception 794 { 795 MockHttpURLStreamHandler handler = new MockHttpURLStreamHandler( 796 HttpURLConnection.HTTP_BAD_REQUEST, testSavePropertiesFile); 797 URL url = new URL(null, "http://jakarta.apache.org", handler); 798 try 799 { 800 conf.save(url); 801 fail("Response code was not checked!"); 802 } 803 catch (ConfigurationException cex) 804 { 805 assertTrue("Wrong root cause: " + cex, 806 cex.getCause() instanceof IOException); 807 } 808 } 809 810 /** 811 * Test the creation of a file containing a '#' in its name. This test is 812 * skipped on Java 1.3 as it always fails. 813 */ 814 @Test 815 public void testFileWithSharpSymbol() throws Exception 816 { 817 if (SystemUtils.isJavaVersionAtLeast(1.4f)) 818 { 819 File file = new File("target/sharp#1.properties"); 820 file.createNewFile(); 821 822 PropertiesConfiguration conf = new PropertiesConfiguration(file); 823 conf.save(); 824 825 assertTrue("Missing file " + file, file.exists()); 826 } 827 } 828 829 /** 830 * Tests initializing a properties configuration from a non existing file. 831 * There was a bug, which caused properties getting lost when later save() 832 * is called. 833 */ 834 @Test 835 public void testInitFromNonExistingFile() throws ConfigurationException 836 { 837 final String testProperty = "test.successfull"; 838 conf = new PropertiesConfiguration(testSavePropertiesFile); 839 conf.addProperty(testProperty, Boolean.TRUE); 840 conf.save(); 841 PropertiesConfiguration checkConfig = new PropertiesConfiguration( 842 testSavePropertiesFile); 843 assertTrue("Test property not found", checkConfig 844 .getBoolean(testProperty)); 845 } 846 847 /** 848 * Tests copying another configuration into the test configuration. This 849 * test ensures that the layout object is informed about the newly added 850 * properties. 851 */ 852 @Test 853 public void testCopyAndSave() throws ConfigurationException 854 { 855 Configuration copyConf = setUpCopyConfig(); 856 conf.copy(copyConf); 857 checkCopiedConfig(copyConf); 858 } 859 860 /** 861 * Tests appending a configuration to the test configuration. Again it has 862 * to be ensured that the layout object is correctly updated. 863 */ 864 @Test 865 public void testAppendAndSave() throws ConfigurationException 866 { 867 Configuration copyConf = setUpCopyConfig(); 868 conf.append(copyConf); 869 checkCopiedConfig(copyConf); 870 } 871 872 /** 873 * Tests adding properties through a DataConfiguration. This is related to 874 * CONFIGURATION-332. 875 */ 876 @Test 877 public void testSaveWithDataConfig() throws ConfigurationException 878 { 879 conf = new PropertiesConfiguration(testSavePropertiesFile); 880 DataConfiguration dataConfig = new DataConfiguration(conf); 881 dataConfig.setProperty("foo", "bar"); 882 assertEquals("Property not set", "bar", conf.getString("foo")); 883 884 conf.save(); 885 PropertiesConfiguration config2 = new PropertiesConfiguration( 886 testSavePropertiesFile); 887 assertEquals("Property not saved", "bar", config2.getString("foo")); 888 } 889 890 /** 891 * Tests whether the correct default encoding is used when loading a 892 * properties file. This test is related to CONFIGURATION-345. 893 */ 894 @Test 895 public void testLoadWithDefaultEncoding() throws ConfigurationException 896 { 897 class PropertiesConfigurationTestImpl extends PropertiesConfiguration 898 { 899 String loadEncoding; 900 901 public PropertiesConfigurationTestImpl(String fileName) 902 throws ConfigurationException 903 { 904 super(fileName); 905 } 906 907 @Override 908 public void load(InputStream in, String encoding) 909 throws ConfigurationException 910 { 911 loadEncoding = encoding; 912 super.load(in, encoding); 913 } 914 } 915 916 PropertiesConfigurationTestImpl testConf = new PropertiesConfigurationTestImpl( 917 testProperties); 918 assertEquals("Default encoding not used", "ISO-8859-1", 919 testConf.loadEncoding); 920 } 921 922 /** 923 * Tests whether a default IOFactory is set. 924 */ 925 @Test 926 public void testGetIOFactoryDefault() 927 { 928 assertNotNull("No default IO factory", conf.getIOFactory()); 929 } 930 931 /** 932 * Tests setting the IOFactory to null. This should cause an exception. 933 */ 934 @Test(expected = IllegalArgumentException.class) 935 public void testSetIOFactoryNull() 936 { 937 conf.setIOFactory(null); 938 } 939 940 /** 941 * Tests setting an IOFactory that uses a specialized reader. 942 */ 943 @Test 944 public void testSetIOFactoryReader() throws ConfigurationException 945 { 946 final int propertyCount = 10; 947 conf.clear(); 948 conf.setIOFactory(new PropertiesConfiguration.IOFactory() 949 { 950 public PropertiesConfiguration.PropertiesReader createPropertiesReader( 951 Reader in, char delimiter) 952 { 953 return new PropertiesReaderTestImpl(in, delimiter, 954 propertyCount); 955 } 956 957 public PropertiesConfiguration.PropertiesWriter createPropertiesWriter( 958 Writer out, char delimiter) 959 { 960 throw new UnsupportedOperationException("Unexpected call!"); 961 } 962 }); 963 conf.load(); 964 for (int i = 1; i <= propertyCount; i++) 965 { 966 assertEquals("Wrong property value at " + i, PROP_VALUE + i, conf 967 .getString(PROP_NAME + i)); 968 } 969 } 970 971 /** 972 * Tests setting an IOFactory that uses a specialized writer. 973 */ 974 @Test 975 public void testSetIOFactoryWriter() throws ConfigurationException, IOException 976 { 977 final PropertiesWriterTestImpl testWriter = new PropertiesWriterTestImpl(','); 978 conf.setIOFactory(new PropertiesConfiguration.IOFactory() 979 { 980 public PropertiesConfiguration.PropertiesReader createPropertiesReader( 981 Reader in, char delimiter) 982 { 983 throw new UnsupportedOperationException("Unexpected call!"); 984 } 985 986 public PropertiesConfiguration.PropertiesWriter createPropertiesWriter( 987 Writer out, char delimiter) 988 { 989 return testWriter; 990 } 991 }); 992 conf.save(new StringWriter()); 993 testWriter.close(); 994 checkSavedConfig(); 995 } 996 997 /** 998 * Tests that the property separators are retained when saving the 999 * configuration. 1000 */ 1001 @Test 1002 public void testKeepSeparators() throws ConfigurationException, IOException 1003 { 1004 conf.save(testSavePropertiesFile); 1005 final String[] separatorTests = { 1006 "test.separator.equal = foo", "test.separator.colon : foo", 1007 "test.separator.tab\tfoo", "test.separator.whitespace foo", 1008 "test.separator.no.space=foo" 1009 }; 1010 Set<String> foundLines = new HashSet<String>(); 1011 BufferedReader in = new BufferedReader(new FileReader( 1012 testSavePropertiesFile)); 1013 try 1014 { 1015 String s; 1016 while ((s = in.readLine()) != null) 1017 { 1018 for (int i = 0; i < separatorTests.length; i++) 1019 { 1020 if (separatorTests[i].equals(s)) 1021 { 1022 foundLines.add(s); 1023 } 1024 } 1025 } 1026 } 1027 finally 1028 { 1029 in.close(); 1030 } 1031 assertEquals("No all separators were found: " + foundLines, 1032 separatorTests.length, foundLines.size()); 1033 } 1034 1035 /** 1036 * Tests whether properties with slashes in their values can be saved. This 1037 * test is related to CONFIGURATION-408. 1038 */ 1039 @Test 1040 public void testSlashEscaping() throws ConfigurationException 1041 { 1042 conf.setProperty(PROP_NAME, "http://www.apache.org"); 1043 StringWriter writer = new StringWriter(); 1044 conf.save(writer); 1045 String s = writer.toString(); 1046 assertTrue("Value not found: " + s, s.indexOf(PROP_NAME 1047 + " = http://www.apache.org") >= 0); 1048 } 1049 1050 /** 1051 * Tests whether backslashes are correctly handled if lists are parsed. This 1052 * test is related to CONFIGURATION-418. 1053 */ 1054 @Test 1055 public void testBackslashEscapingInLists() throws Exception 1056 { 1057 checkBackslashList("share2"); 1058 checkBackslashList("share1"); 1059 } 1060 1061 /** 1062 * Tests whether a list property is handled correctly if delimiter parsing 1063 * is disabled. This test is related to CONFIGURATION-495. 1064 */ 1065 @Test 1066 public void testSetPropertyListWithDelimiterParsingDisabled() 1067 throws ConfigurationException 1068 { 1069 String prop = "delimiterListProp"; 1070 conf.setDelimiterParsingDisabled(true); 1071 List<String> list = Arrays.asList("val", "val2", "val3"); 1072 conf.setProperty(prop, list); 1073 conf.setFile(testSavePropertiesFile); 1074 conf.save(); 1075 conf.clear(); 1076 conf.load(); 1077 assertEquals("Wrong list property", list, conf.getProperty(prop)); 1078 } 1079 1080 /** 1081 * Helper method for testing the content of a list with elements that 1082 * contain backslashes. 1083 * 1084 * @param key the key 1085 */ 1086 private void checkBackslashList(String key) 1087 { 1088 Object prop = conf.getProperty("test." + key); 1089 assertTrue("Not a list", prop instanceof List); 1090 List<?> list = (List<?>) prop; 1091 assertEquals("Wrong number of list elements", 2, list.size()); 1092 final String prefix = "\\\\" + key; 1093 assertEquals("Wrong element 1", prefix + "a", list.get(0)); 1094 assertEquals("Wrong element 2", prefix + "b", list.get(1)); 1095 } 1096 1097 /** 1098 * Creates a configuration that can be used for testing copy operations. 1099 * 1100 * @return the configuration to be copied 1101 */ 1102 private Configuration setUpCopyConfig() 1103 { 1104 final int count = 25; 1105 Configuration result = new BaseConfiguration(); 1106 for (int i = 1; i <= count; i++) 1107 { 1108 result.addProperty("copyKey" + i, "copyValue" + i); 1109 } 1110 return result; 1111 } 1112 1113 /** 1114 * Tests whether the data of a configuration that was copied into the test 1115 * configuration is correctly saved. 1116 * 1117 * @param copyConf the copied configuration 1118 * @throws ConfigurationException if an error occurs 1119 */ 1120 private void checkCopiedConfig(Configuration copyConf) 1121 throws ConfigurationException 1122 { 1123 conf.save(testSavePropertiesFile); 1124 PropertiesConfiguration checkConf = new PropertiesConfiguration( 1125 testSavePropertiesFile); 1126 for (Iterator<String> it = copyConf.getKeys(); it.hasNext();) 1127 { 1128 String key = it.next(); 1129 assertEquals("Wrong value for property " + key, checkConf 1130 .getProperty(key), copyConf.getProperty(key)); 1131 } 1132 } 1133 1134 /** 1135 * A dummy layout implementation for checking whether certain methods are 1136 * correctly called by the configuration. 1137 */ 1138 static class DummyLayout extends PropertiesConfigurationLayout 1139 { 1140 /** Stores the number how often load() was called. */ 1141 public int loadCalls; 1142 1143 public DummyLayout(PropertiesConfiguration config) 1144 { 1145 super(config); 1146 } 1147 1148 @Override 1149 public void load(Reader in) throws ConfigurationException 1150 { 1151 loadCalls++; 1152 } 1153 } 1154 1155 /** 1156 * A mock implementation of a HttpURLConnection used for testing saving to 1157 * a HTTP server. 1158 */ 1159 static class MockHttpURLConnection extends HttpURLConnection 1160 { 1161 /** The response code to return.*/ 1162 private final int returnCode; 1163 1164 /** The output file. The output stream will point to this file.*/ 1165 private final File outputFile; 1166 1167 protected MockHttpURLConnection(URL u, int respCode, File outFile) 1168 { 1169 super(u); 1170 returnCode = respCode; 1171 outputFile = outFile; 1172 } 1173 1174 @Override 1175 public void disconnect() 1176 { 1177 } 1178 1179 @Override 1180 public boolean usingProxy() 1181 { 1182 return false; 1183 } 1184 1185 @Override 1186 public void connect() throws IOException 1187 { 1188 } 1189 1190 @Override 1191 public int getResponseCode() throws IOException 1192 { 1193 return returnCode; 1194 } 1195 1196 @Override 1197 public OutputStream getOutputStream() throws IOException 1198 { 1199 return new FileOutputStream(outputFile); 1200 } 1201 } 1202 1203 /** 1204 * A mock stream handler for working with the mock HttpURLConnection. 1205 */ 1206 static class MockHttpURLStreamHandler extends URLStreamHandler 1207 { 1208 /** Stores the response code.*/ 1209 private int responseCode; 1210 1211 /** Stores the output file.*/ 1212 private File outputFile; 1213 1214 /** Stores the connection.*/ 1215 private MockHttpURLConnection connection; 1216 1217 public MockHttpURLStreamHandler(int respCode, File outFile) 1218 { 1219 responseCode = respCode; 1220 outputFile = outFile; 1221 } 1222 1223 public MockHttpURLConnection getMockConnection() 1224 { 1225 return connection; 1226 } 1227 1228 @Override 1229 protected URLConnection openConnection(URL u) throws IOException 1230 { 1231 connection = new MockHttpURLConnection(u, responseCode, outputFile); 1232 return connection; 1233 } 1234 } 1235 1236 /** 1237 * A test PropertiesReader for testing whether a custom reader can be 1238 * injected. This implementation creates a configurable number of synthetic 1239 * test properties. 1240 */ 1241 private static class PropertiesReaderTestImpl extends 1242 PropertiesConfiguration.PropertiesReader 1243 { 1244 /** The number of test properties to be created. */ 1245 private final int maxProperties; 1246 1247 /** The current number of properties. */ 1248 private int propertyCount; 1249 1250 public PropertiesReaderTestImpl(Reader reader, char listDelimiter, 1251 int maxProps) 1252 { 1253 super(reader, listDelimiter); 1254 assertEquals("Wrong list delimiter", ',', listDelimiter); 1255 maxProperties = maxProps; 1256 } 1257 1258 @Override 1259 public String getPropertyName() 1260 { 1261 return PROP_NAME + propertyCount; 1262 } 1263 1264 @Override 1265 public String getPropertyValue() 1266 { 1267 return PROP_VALUE + propertyCount; 1268 } 1269 1270 @Override 1271 public boolean nextProperty() throws IOException 1272 { 1273 propertyCount++; 1274 return propertyCount <= maxProperties; 1275 } 1276 } 1277 1278 /** 1279 * A test PropertiesWriter for testing whether a custom writer can be 1280 * injected. This implementation simply redirects all output into a test 1281 * file. 1282 */ 1283 private static class PropertiesWriterTestImpl extends 1284 PropertiesConfiguration.PropertiesWriter 1285 { 1286 public PropertiesWriterTestImpl(char delimiter) throws IOException 1287 { 1288 super(new FileWriter(testSavePropertiesFile), delimiter); 1289 } 1290 } 1291 }