View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.configuration2.plist;
19  
20  import static org.apache.commons.configuration2.TempDirUtils.newFile;
21  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertFalse;
24  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
25  import static org.junit.jupiter.api.Assertions.assertNotNull;
26  import static org.junit.jupiter.api.Assertions.assertNull;
27  import static org.junit.jupiter.api.Assertions.assertThrows;
28  import static org.junit.jupiter.api.Assertions.assertTrue;
29  
30  import java.io.File;
31  import java.io.StringReader;
32  import java.util.Arrays;
33  import java.util.Calendar;
34  import java.util.Date;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.TimeZone;
38  
39  import org.apache.commons.configuration2.Configuration;
40  import org.apache.commons.configuration2.ConfigurationAssert;
41  import org.apache.commons.configuration2.ConfigurationComparator;
42  import org.apache.commons.configuration2.HierarchicalConfiguration;
43  import org.apache.commons.configuration2.StrictConfigurationComparator;
44  import org.apache.commons.configuration2.ex.ConfigurationException;
45  import org.apache.commons.configuration2.io.FileHandler;
46  import org.apache.commons.configuration2.tree.ImmutableNode;
47  import org.apache.commons.configuration2.tree.NodeHandler;
48  import org.junit.jupiter.api.BeforeEach;
49  import org.junit.jupiter.api.Test;
50  import org.junit.jupiter.api.io.TempDir;
51  
52  /**
53   */
54  public class TestPropertyListConfiguration {
55      /**
56       * Returns a list with the children of the given configuration's root note with the specified name.
57       *
58       * @param config the configuration
59       * @param name the name of the desired children
60       * @return the list with the corresponding child nodes
61       */
62      private static List<ImmutableNode> getNamedChildren(final HierarchicalConfiguration<ImmutableNode> config, final String name) {
63          final NodeHandler<ImmutableNode> handler = config.getNodeModel().getNodeHandler();
64          return handler.getChildren(handler.getRootNode(), name);
65      }
66  
67      /**
68       * Loads a configuration from the specified test file.
69       *
70       * @param c the configuration to be loaded
71       * @param f the file to be loaded
72       * @throws ConfigurationException if an error occurs
73       */
74      private static void load(final PropertyListConfiguration c, final File f) throws ConfigurationException {
75          new FileHandler(c).load(f);
76      }
77  
78      /** A folder for temporary files. */
79      @TempDir
80      public File tempFolder;
81  
82      private PropertyListConfiguration config;
83  
84      private final File testProperties = ConfigurationAssert.getTestFile("test.plist");
85  
86      /**
87       * Saves the test configuration to the specified file.
88       *
89       * @param file the target file
90       * @throws ConfigurationException if an error occurs
91       */
92      private void saveConfig(final File file) throws ConfigurationException {
93          new FileHandler(config).save(file);
94      }
95  
96      @BeforeEach
97      public void setUp() throws Exception {
98          config = new PropertyListConfiguration();
99          load(config, testProperties);
100     }
101 
102     /**
103      * Ensure that addProperty doesn't alter an array of byte
104      */
105     @Test
106     public void testAddDataProperty() throws Exception {
107         final File saveFile = newFile(tempFolder);
108         final byte[] expected = {1, 2, 3, 4};
109         config = new PropertyListConfiguration();
110         config.addProperty("foo", expected);
111         saveConfig(saveFile);
112 
113         final PropertyListConfiguration config2 = new PropertyListConfiguration();
114         load(config2, saveFile);
115         final Object array = config2.getProperty("foo");
116 
117         assertNotNull(array);
118         assertEquals(byte[].class, array.getClass());
119         assertArrayEquals(expected, (byte[]) array);
120     }
121 
122     @Test
123     public void testArray() {
124         final List<?> list = assertInstanceOf(List.class, config.getProperty("array"));
125         assertEquals(Arrays.asList("value1", "value2", "value3"), list);
126     }
127 
128     @Test
129     public void testData() {
130         final byte[] bytes = assertInstanceOf(byte[].class, config.getProperty("data"));
131         assertArrayEquals("foo bar".getBytes(), bytes);
132     }
133 
134     @Test
135     public void testDate() throws Exception {
136         final Calendar cal = Calendar.getInstance();
137         cal.clear();
138         cal.set(2002, Calendar.MARCH, 22, 11, 30, 0);
139         cal.setTimeZone(TimeZone.getTimeZone("GMT+0100"));
140         final Date date = cal.getTime();
141 
142         assertEquals(date, config.getProperty("date"));
143     }
144 
145     @Test
146     public void testDictionary() {
147         assertEquals("bar1", config.getProperty("dictionary.foo1"));
148         assertEquals("bar2", config.getProperty("dictionary.foo2"));
149     }
150 
151     @Test
152     public void testDictionaryArray() {
153         final String key = "dictionary-array";
154 
155         final Object array = config.getProperty(key);
156 
157         // root array
158         assertNotNull(array);
159         assertInstanceOf(List.class, array);
160         final List<?> list = config.getList(key);
161 
162         assertEquals(2, list.size());
163 
164         // 1st dictionary
165         final Configuration conf1 = assertInstanceOf(Configuration.class, list.get(0));
166         assertFalse(conf1.isEmpty());
167         assertEquals("bar", conf1.getProperty("foo"));
168 
169         // 2nd dictionary
170         final Configuration conf2 = assertInstanceOf(Configuration.class, list.get(1));
171         assertFalse(conf2.isEmpty());
172         assertEquals("value", conf2.getProperty("key"));
173     }
174 
175     @Test
176     public void testEmptyArray() {
177         final String key = "empty-array";
178         assertNotNull(config.getProperty(key));
179 
180         final List<?> list = (List<?>) config.getProperty(key);
181         assertTrue(list.isEmpty());
182     }
183 
184     /**
185      * Tests formatting a date.
186      */
187     @Test
188     public void testFormatDate() {
189         final Calendar cal = Calendar.getInstance();
190         cal.clear();
191         cal.set(2007, Calendar.OCTOBER, 29, 23, 4, 30);
192         cal.setTimeZone(TimeZone.getTimeZone("GMT-0230"));
193         assertEquals("<*D2007-10-29 23:04:30 -0230>", PropertyListConfiguration.formatDate(cal));
194         cal.clear();
195         cal.set(2007, Calendar.OCTOBER, 30, 22, 2, 15);
196         cal.setTimeZone(TimeZone.getTimeZone("GMT+1111"));
197         assertEquals("<*D2007-10-30 22:02:15 +1111>", PropertyListConfiguration.formatDate(cal));
198     }
199 
200     @Test
201     public void testInitCopy() {
202         final PropertyListConfiguration copy = new PropertyListConfiguration(config);
203         assertFalse(copy.isEmpty());
204     }
205 
206     @Test
207     public void testLoad() {
208         assertFalse(config.isEmpty());
209     }
210 
211     @Test
212     public void testLoadWithError() {
213         config = new PropertyListConfiguration();
214         final FileHandler fileHandler = new FileHandler(config);
215         final StringReader reader = new StringReader("");
216         final ConfigurationException e = assertThrows(ConfigurationException.class, () -> fileHandler.load(reader));
217         assertNotNull(e.getMessage());
218     }
219 
220     @Test
221     public void testNestedArrays() {
222         final String key = "nested-arrays";
223 
224         final Object array = config.getProperty(key);
225 
226         // root array
227         assertNotNull(array);
228         assertInstanceOf(List.class, array);
229         final List<?> list = config.getList(key);
230 
231         assertEquals(2, list.size());
232 
233         // 1st array
234         final List<?> list1 = assertInstanceOf(List.class, list.get(0));
235         assertEquals(Arrays.asList("a", "b"), list1);
236 
237         // 2nd array
238         final List<?> list2 = assertInstanceOf(List.class, list.get(1));
239         assertEquals(Arrays.asList("c", "d"), list2);
240     }
241 
242     @Test
243     public void testNestedDictionaries() {
244         assertEquals("value", config.getString("nested-dictionaries.foo.bar.key"));
245     }
246 
247     /**
248      * Tests parsing a date that contains an invalid separator character.
249      */
250     @Test
251     public void testParseDateInvalidChar() {
252         assertThrows(ParseException.class, () -> PropertyListConfiguration.parseDate("<*D2002+03-22 11:30:00 +0100>"));
253     }
254 
255     /**
256      * Tests parsing a date with an invalid numeric value.
257      */
258     @Test
259     public void testParseDateNoNumber() {
260         assertThrows(ParseException.class, () -> PropertyListConfiguration.parseDate("<*D2002-03-22 1c:30:00 +0100>"));
261     }
262 
263     /**
264      * Tries parsing a null date. This should cause an exception.n
265      */
266     @Test
267     public void testParseDateNull() {
268         assertThrows(ParseException.class, () -> PropertyListConfiguration.parseDate(null));
269     }
270 
271     /**
272      * Tests parsing a date that is not long enough.
273      */
274     @Test
275     public void testParseDateTooShort() {
276         assertThrows(ParseException.class, () -> PropertyListConfiguration.parseDate("<*D2002-03-22 11:3>"));
277     }
278 
279     @Test
280     public void testQuotedString() {
281         assertEquals("string2", config.getProperty("quoted-string"));
282         assertEquals("this is a string", config.getProperty("quoted-string2"));
283         assertEquals("this is a \"complex\" string {(=,;)}", config.getProperty("complex-string"));
284     }
285 
286     @Test
287     public void testQuoteString() {
288         assertNull(config.quoteString(null));
289         assertEquals("abcd", config.quoteString("abcd"));
290         assertEquals("\"ab cd\"", config.quoteString("ab cd"));
291         assertEquals("\"foo\\\"bar\"", config.quoteString("foo\"bar"));
292         assertEquals("\"foo;bar\"", config.quoteString("foo;bar"));
293     }
294 
295     @Test
296     public void testSave() throws Exception {
297         final File savedFile = newFile("testsave.plist", tempFolder);
298 
299         // save the configuration
300         saveConfig(savedFile);
301         assertTrue(savedFile.exists());
302 
303         // read the configuration and compare the properties
304         final PropertyListConfiguration checkConfig = new PropertyListConfiguration();
305         load(checkConfig, savedFile);
306 
307         final Iterator<String> it = config.getKeys();
308         while (it.hasNext()) {
309             final String key = it.next();
310             assertTrue(checkConfig.containsKey(key), "The saved configuration doesn't contain the key '" + key + "'");
311 
312             final Object value = checkConfig.getProperty(key);
313             if (value instanceof byte[]) {
314                 final byte[] array = (byte[]) value;
315                 assertArrayEquals((byte[]) config.getProperty(key), array, "Value of the '" + key + "' property");
316             } else if (value instanceof List) {
317                 final List<?> list1 = (List<?>) config.getProperty(key);
318                 final List<?> list2 = (List<?>) value;
319 
320                 assertEquals(list1.size(), list2.size(), "The size of the list for the key '" + key + "' doesn't match");
321 
322                 for (int i = 0; i < list2.size(); i++) {
323                     final Object value1 = list1.get(i);
324                     final Object value2 = list2.get(i);
325 
326                     if (value1 instanceof Configuration) {
327                         final ConfigurationComparator comparator = new StrictConfigurationComparator();
328                         assertTrue(comparator.compare((Configuration) value1, (Configuration) value2),
329                                 "The dictionnary at index " + i + " for the key '" + key + "' doesn't match");
330                     } else {
331                         assertEquals(value1, value2, "Element at index " + i + " for the key '" + key + "'");
332                     }
333                 }
334 
335                 assertEquals(config.getProperty(key), list1, "Value of the '" + key + "' property");
336             } else {
337                 assertEquals(config.getProperty(key), checkConfig.getProperty(key), "Value of the '" + key + "' property");
338             }
339 
340         }
341     }
342 
343     @Test
344     public void testSaveEmptyDictionary() throws Exception {
345         final File savedFile = newFile("testsave.plist", tempFolder);
346 
347         // save the configuration
348         saveConfig(savedFile);
349         assertTrue(savedFile.exists());
350 
351         // read the configuration and compare the properties
352         final PropertyListConfiguration checkConfig = new PropertyListConfiguration();
353         load(checkConfig, savedFile);
354 
355         assertFalse(getNamedChildren(config, "empty-dictionary").isEmpty());
356         assertFalse(getNamedChildren(checkConfig, "empty-dictionary").isEmpty());
357     }
358 
359     /**
360      * Ensure that setProperty doesn't alter an array of byte since it's a first class type in plist file
361      */
362     @Test
363     public void testSetDataProperty() throws Exception {
364         final File saveFile = newFile(tempFolder);
365         final byte[] expected = {1, 2, 3, 4};
366         config = new PropertyListConfiguration();
367         config.setProperty("foo", expected);
368         saveConfig(saveFile);
369 
370         final PropertyListConfiguration config2 = new PropertyListConfiguration();
371         load(config2, saveFile);
372         final Object array = config2.getProperty("foo");
373 
374         assertNotNull(array);
375         assertEquals(byte[].class, array.getClass());
376         assertArrayEquals(expected, (byte[]) array);
377     }
378 
379     @Test
380     public void testString() {
381         assertEquals("string1", config.getProperty("simple-string"));
382     }
383 }