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