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.beanutils;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertNull;
25  import static org.junit.jupiter.api.Assertions.assertThrows;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  
28  import java.util.Arrays;
29  import java.util.HashMap;
30  import java.util.List;
31  
32  import org.apache.commons.beanutils.DynaProperty;
33  import org.apache.commons.configuration2.BaseConfiguration;
34  import org.apache.commons.configuration2.Configuration;
35  import org.apache.commons.configuration2.MapConfiguration;
36  import org.junit.jupiter.api.BeforeEach;
37  import org.junit.jupiter.api.Test;
38  
39  /**
40   * <p>
41   * Test Case for the {@code ConfigurationDynaBean} implementation class. These tests were based on the ones in
42   * {@code BasicDynaBeanTestCase} because the two classes provide similar levels of functionality.
43   * </p>
44   */
45  public class TestConfigurationDynaBean {
46  
47      /**
48       * The basic test bean for each test.
49       */
50      private ConfigurationDynaBean bean;
51  
52      /**
53       * The set of property names we expect to have returned when calling {@code getDynaProperties()}. You should update this
54       * list when new properties are added to TestBean.
55       */
56      private final String[] properties = {"booleanProperty", "booleanSecond", "doubleProperty", "floatProperty", "intProperty", "longProperty",
57              "mappedProperty.key1", "mappedProperty.key2", "mappedProperty.key3", "mappedIntProperty.key1", "shortProperty", "stringProperty", "byteProperty",
58              "charProperty"};
59  
60      private final Object[] values = {Boolean.TRUE, Boolean.TRUE, Double.MAX_VALUE, Float.MAX_VALUE, Integer.MAX_VALUE, Long.MAX_VALUE, "First Value",
61              "Second Value", "Third Value", Integer.MAX_VALUE, Short.MAX_VALUE, "This is a string", Byte.MAX_VALUE, Character.MAX_VALUE};
62  
63      private final int[] intArray = {0, 10, 20, 30, 40};
64      private final boolean[] booleanArray = {true, false, true, false, true};
65      private final char[] charArray = {'a', 'b', 'c', 'd', 'e'};
66      private final byte[] byteArray = {0, 10, 20, 30, 40};
67      private final long[] longArray = {0, 10, 20, 30, 40};
68      private final short[] shortArray = {0, 10, 20, 30, 40};
69      private final float[] floatArray = {0, 10, 20, 30, 40};
70      private final double[] doubleArray = {0.0, 10.0, 20.0, 30.0, 40.0};
71      private final String[] stringArray = {"String 0", "String 1", "String 2", "String 3", "String 4"};
72  
73      /**
74       * Creates the underlying configuration object for the dyna bean.
75       *
76       * @return the underlying configuration object
77       */
78      protected Configuration createConfiguration() {
79          return new BaseConfiguration();
80      }
81  
82      /**
83       * Sets up instance variables required by this test case.
84       */
85      @BeforeEach
86      public void setUp() throws Exception {
87          final Configuration configuration = createConfiguration();
88  
89          for (int i = 0; i < properties.length; i++) {
90              configuration.setProperty(properties[i], values[i]);
91          }
92  
93          for (final int element : intArray) {
94              configuration.addProperty("intIndexed", element);
95          }
96  
97          for (final String element : stringArray) {
98              configuration.addProperty("stringIndexed", element);
99          }
100 
101         final List<String> list = Arrays.asList(stringArray);
102         configuration.addProperty("listIndexed", list);
103 
104         bean = new ConfigurationDynaBean(configuration);
105 
106         bean.set("listIndexed", list);
107         bean.set("intArray", intArray);
108         bean.set("booleanArray", booleanArray);
109         bean.set("charArray", charArray);
110         bean.set("longArray", longArray);
111         bean.set("shortArray", shortArray);
112         bean.set("floatArray", floatArray);
113         bean.set("doubleArray", doubleArray);
114         bean.set("byteArray", byteArray);
115         bean.set("stringArray", stringArray);
116     }
117 
118     /**
119      * Tests set on a null value: should throw NPE.
120      */
121     @Test
122     public void testAddNullPropertyValue() {
123         assertThrows(NullPointerException.class, () -> bean.set("nullProperty", null));
124     }
125 
126     /**
127      * Corner cases on getDynaProperty invalid arguments.
128      */
129     @Test
130     public void testGetDescriptorArguments() {
131         final DynaProperty descriptor = bean.getDynaClass().getDynaProperty("unknown");
132         assertNull(descriptor);
133         assertThrows(IllegalArgumentException.class, () -> bean.getDynaClass().getDynaProperty(null));
134     }
135 
136     /**
137      * Base for testGetDescriptorXxxxx() series of tests.
138      *
139      * @param name Name of the property to be retrieved
140      * @param type Expected class type of this property
141      */
142     protected void testGetDescriptorBase(final String name, final Class<?> type) {
143         final DynaProperty descriptor = bean.getDynaClass().getDynaProperty(name);
144 
145         assertNotNull(descriptor);
146         assertEquals(type, descriptor.getType());
147     }
148 
149     /**
150      * Positive getDynaProperty on property {@code booleanProperty}.
151      */
152     @Test
153     public void testGetDescriptorBoolean() {
154         testGetDescriptorBase("booleanProperty", Boolean.TYPE);
155     }
156 
157     /**
158      * Positive getDynaProperty on property {@code doubleProperty}.
159      */
160     @Test
161     public void testGetDescriptorDouble() {
162         testGetDescriptorBase("doubleProperty", Double.TYPE);
163     }
164 
165     /**
166      * Positive getDynaProperty on property {@code floatProperty}.
167      */
168     @Test
169     public void testGetDescriptorFloat() {
170         testGetDescriptorBase("floatProperty", Float.TYPE);
171     }
172 
173     /**
174      * Positive getDynaProperty on property {@code intProperty}.
175      */
176     @Test
177     public void testGetDescriptorInt() {
178         testGetDescriptorBase("intProperty", Integer.TYPE);
179     }
180 
181     /**
182      * Positive getDynaProperty on property {@code longProperty}.
183      */
184     @Test
185     public void testGetDescriptorLong() {
186         testGetDescriptorBase("longProperty", Long.TYPE);
187     }
188 
189     /**
190      * Positive test for getDynaPropertys(). Each property name listed in {@code properties} should be returned exactly
191      * once.
192      */
193     @Test
194     public void testGetDescriptors() {
195         final DynaProperty[] pd = bean.getDynaClass().getDynaProperties();
196         assertNotNull(pd);
197         final int[] count = new int[properties.length];
198         for (final DynaProperty element : pd) {
199             final String name = element.getName();
200             for (int j = 0; j < properties.length; j++) {
201                 if (name.equals(properties[j])) {
202                     count[j]++;
203                 }
204             }
205         }
206 
207         for (int j = 0; j < properties.length; j++) {
208             assertFalse(count[j] < 0, "Missing property " + properties[j]);
209             assertFalse(count[j] > 1, "Duplicate property " + properties[j]);
210         }
211     }
212 
213     /**
214      * Positive getDynaProperty on property {@code booleanSecond} that uses an "is" method as the getter.
215      */
216     @Test
217     public void testGetDescriptorSecond() {
218         testGetDescriptorBase("booleanSecond", Boolean.TYPE);
219     }
220 
221     /**
222      * Positive getDynaProperty on property {@code shortProperty}.
223      */
224     @Test
225     public void testGetDescriptorShort() {
226         testGetDescriptorBase("shortProperty", Short.TYPE);
227     }
228 
229     /**
230      * Positive getDynaProperty on property {@code stringProperty}.
231      */
232     @Test
233     public void testGetDescriptorString() {
234         testGetDescriptorBase("stringProperty", String.class);
235     }
236 
237     /**
238      * Corner cases on getIndexedProperty invalid arguments.
239      */
240     @Test
241     public void testGetIndexedArguments() {
242         assertThrows(IndexOutOfBoundsException.class, () -> bean.get("intArray", -1));
243     }
244 
245     /**
246      * Tests whether an indexed access to a non-existing property causes an exception.
247      */
248     @Test
249     public void testGetIndexedNonExisting() {
250         assertThrows(IllegalArgumentException.class, () -> bean.get("Non existing property", 0));
251     }
252 
253     /**
254      * Tests whether accessing a non-indexed string property using the index get method causes an exception.
255      */
256     @Test
257     public void testGetIndexedString() {
258         bean.set("stringProp", "value");
259         assertThrows(IllegalArgumentException.class, () -> bean.get("stringProp", 0));
260     }
261 
262     /**
263      * Positive and negative tests on getIndexedProperty valid arguments.
264      */
265     @Test
266     public void testGetIndexedValues() {
267         for (int i = 0; i < 5; i++) {
268             Object value = bean.get("intArray", i);
269 
270             int intValue = assertInstanceOf(Integer.class, value, "intArray index " + i);
271             assertEquals(i * 10, intValue, "intArray " + i);
272 
273             value = bean.get("intIndexed", i);
274 
275             intValue = assertInstanceOf(Integer.class, value, "intIndexed index " + i);
276             assertEquals(i * 10, intValue, "intIndexed index " + i);
277 
278             value = bean.get("listIndexed", i);
279 
280             assertInstanceOf(String.class, value, "list index " + i);
281             assertEquals("String " + i, value, "listIndexed index " + i);
282 
283             value = bean.get("stringArray", i);
284 
285             assertInstanceOf(String.class, value, "stringArray index " + i);
286             assertEquals("String " + i, value, "stringArray index " + i);
287 
288             value = bean.get("stringIndexed", i);
289 
290             assertInstanceOf(String.class, value, "stringIndexed index " + i);
291             assertEquals("String " + i, value, "stringIndexed index " + i);
292         }
293     }
294 
295     /**
296      * Corner cases on getMappedProperty invalid arguments.
297      */
298     @Test
299     public void testGetMappedArguments() {
300         final Object value = bean.get("mappedProperty", "unknown");
301         assertNull(value);
302     }
303 
304     /**
305      * Positive and negative tests on getMappedProperty valid arguments.
306      */
307     @Test
308     public void testGetMappedValues() {
309         Object value = bean.get("mappedProperty", "key1");
310         assertEquals("First Value", value);
311 
312         value = bean.get("mappedProperty", "key2");
313         assertEquals("Second Value", value);
314 
315         value = bean.get("mappedProperty", "key3");
316         assertNotNull(value);
317     }
318 
319     /**
320      * Test the retrieval of a non-existent property.
321      */
322     @Test
323     public void testGetNonExistentProperty() {
324         assertThrows(IllegalArgumentException.class, () -> bean.get("nonexistProperty"));
325     }
326 
327     /**
328      * Tests if reading a non-indexed property using the index get method throws an IllegalArgumentException as it should.
329      */
330     @Test
331     public void testGetNonIndexedProperties() {
332         assertThrows(IllegalArgumentException.class, () -> bean.get("booleanProperty", 0));
333     }
334 
335     /**
336      * Corner cases on getSimpleProperty invalid arguments.
337      */
338     @Test
339     public void testGetSimpleArguments() {
340         assertThrows(IllegalArgumentException.class, () -> bean.get("a non existing property"));
341     }
342 
343     /**
344      * Test getSimpleProperty on a boolean property.
345      */
346     @Test
347     public void testGetSimpleBoolean() {
348         final Object value = bean.get("booleanProperty");
349         assertInstanceOf(Boolean.class, value);
350         assertEquals(Boolean.TRUE, value);
351     }
352 
353     /**
354      * Test getSimpleProperty on a double property.
355      */
356     @Test
357     public void testGetSimpleDouble() {
358         final Object value = bean.get("doubleProperty");
359         final double doubleValue = assertInstanceOf(Double.class, value);
360         assertEquals(Double.MAX_VALUE, doubleValue, 0.005);
361     }
362 
363     /**
364      * Test getSimpleProperty on a float property.
365      */
366     @Test
367     public void testGetSimpleFloat() {
368         final Object value = bean.get("floatProperty");
369         final float floatValue = assertInstanceOf(Float.class, value);
370         assertEquals(Float.MAX_VALUE, floatValue, 0.005f);
371     }
372 
373     /**
374      * Test getSimpleProperty on a int property.
375      */
376     @Test
377     public void testGetSimpleInt() {
378         final Object value = bean.get("intProperty");
379         final int intValue = assertInstanceOf(Integer.class, value);
380         assertEquals(Integer.MAX_VALUE, intValue);
381     }
382 
383     /**
384      * Test getSimpleProperty on a long property.
385      */
386     @Test
387     public void testGetSimpleLong() {
388         final Object value = bean.get("longProperty");
389         final long longValue = assertInstanceOf(Long.class, value);
390         assertEquals(Long.MAX_VALUE, longValue);
391     }
392 
393     /**
394      * Test getSimpleProperty on a short property.
395      */
396     @Test
397     public void testGetSimpleShort() {
398         final Object value = bean.get("shortProperty");
399         final short shortValue = assertInstanceOf(Short.class, value);
400         assertEquals(Short.MAX_VALUE, shortValue);
401     }
402 
403     /**
404      * Test getSimpleProperty on a String property.
405      */
406     @Test
407     public void testGetSimpleString() {
408         final Object value = bean.get("stringProperty");
409         assertInstanceOf(String.class, value);
410         assertEquals("This is a string", value);
411     }
412 
413     /**
414      * Test {@code contains()} method for mapped properties.
415      */
416     @Test
417     public void testMappedContains() {
418         assertTrue(bean.contains("mappedProperty", "key1"));
419         assertFalse(bean.contains("mappedProperty", "Unknown Key"));
420     }
421 
422     /**
423      * Test {@code remove()} method for mapped properties.
424      */
425     @Test
426     public void testMappedRemove() {
427         assertTrue(bean.contains("mappedProperty", "key1"));
428         bean.remove("mappedProperty", "key1");
429         assertFalse(bean.contains("mappedProperty", "key1"));
430 
431         assertFalse(bean.contains("mappedProperty", "key4"));
432         bean.remove("mappedProperty", "key4");
433         assertFalse(bean.contains("mappedProperty", "key4"));
434     }
435 
436     /**
437      * Tests whether nested properties can be accessed.
438      */
439     @Test
440     public void testNestedPropeties() {
441         final ConfigurationDynaBean nested = (ConfigurationDynaBean) bean.get("mappedProperty");
442 
443         final String value = (String) nested.get("key1");
444         assertEquals("First Value", value);
445 
446         nested.set("key1", "undefined");
447         assertEquals("undefined", bean.get("mappedProperty.key1"));
448     }
449 
450     /**
451      * Test the modification of a configuration property stored internally as an array.
452      */
453     @Test
454     public void testSetArrayValue() {
455         final MapConfiguration configuration = new MapConfiguration(new HashMap<>());
456         configuration.getMap().put("objectArray", new Object[] {"value1", "value2", "value3"});
457 
458         final ConfigurationDynaBean bean = new ConfigurationDynaBean(configuration);
459 
460         bean.set("objectArray", 1, "New Value 1");
461         final Object value = bean.get("objectArray", 1);
462 
463         assertInstanceOf(String.class, value);
464         assertEquals("New Value 1", value);
465     }
466 
467     /**
468      * Corner cases on setIndexedProperty invalid arguments.
469      */
470     @Test
471     public void testSetIndexedArguments() {
472         assertThrows(IndexOutOfBoundsException.class, () -> bean.set("intArray", -1, 0));
473     }
474 
475     /**
476      * Positive and negative tests on setIndexedProperty valid arguments.
477      */
478     @Test
479     public void testSetIndexedValues() {
480         bean.set("intArray", 0, 1);
481         Object value = bean.get("intArray", 0);
482 
483         int intValue = assertInstanceOf(Integer.class, value);
484         assertEquals(1, intValue);
485 
486         bean.set("intIndexed", 1, 11);
487         value = bean.get("intIndexed", 1);
488 
489         intValue = assertInstanceOf(Integer.class, value);
490         assertEquals(11, intValue);
491 
492         bean.set("listIndexed", 2, "New Value 2");
493         value = bean.get("listIndexed", 2);
494 
495         assertInstanceOf(String.class, value);
496         assertEquals("New Value 2", value);
497 
498         bean.set("stringArray", 3, "New Value 3");
499         value = bean.get("stringArray", 3);
500 
501         assertInstanceOf(String.class, value);
502         assertEquals("New Value 3", value);
503 
504         bean.set("stringIndexed", 4, "New Value 4");
505         value = bean.get("stringIndexed", 4);
506 
507         assertInstanceOf(String.class, value);
508         assertEquals("New Value 4", value);
509     }
510 
511     /**
512      * Positive and negative tests on setMappedProperty valid arguments.
513      */
514     @Test
515     public void testSetMappedValues() {
516         bean.set("mappedProperty", "First Key", "New First Value");
517         assertEquals("New First Value", bean.get("mappedProperty", "First Key"));
518 
519         bean.set("mappedProperty", "Fourth Key", "Fourth Value");
520         assertEquals("Fourth Value", bean.get("mappedProperty", "Fourth Key"));
521     }
522 
523     /**
524      * Tests if writing a non-indexed property using the index set method with an index &gt; 0 throws an
525      * IllegalArgumentException as it should.
526      */
527     @Test
528     public void testSetNonIndexedProperties() {
529         assertThrows(IllegalArgumentException.class, () -> bean.set("booleanProperty", 1, Boolean.TRUE));
530     }
531 
532     /**
533      * Test setSimpleProperty on a boolean property.
534      */
535     @Test
536     public void testSetSimpleBoolean() {
537         final boolean oldValue = ((Boolean) bean.get("booleanProperty")).booleanValue();
538         final boolean newValue = !oldValue;
539         bean.set("booleanProperty", newValue);
540         assertEquals(newValue, ((Boolean) bean.get("booleanProperty")).booleanValue());
541     }
542 
543     /**
544      * Test setSimpleProperty on a double property.
545      */
546     @Test
547     public void testSetSimpleDouble() {
548         final double oldValue = ((Double) bean.get("doubleProperty")).doubleValue();
549         final double newValue = oldValue + 1.0;
550         bean.set("doubleProperty", newValue);
551         assertEquals(newValue, ((Double) bean.get("doubleProperty")).doubleValue(), 0.005);
552     }
553 
554     /**
555      * Test setSimpleProperty on a float property.
556      */
557     @Test
558     public void testSetSimpleFloat() {
559         final float oldValue = ((Float) bean.get("floatProperty")).floatValue();
560         final float newValue = oldValue + (float) 1.0;
561         bean.set("floatProperty", newValue);
562         assertEquals(newValue, ((Float) bean.get("floatProperty")).floatValue(), 0.005f);
563     }
564 
565     /**
566      * Test setSimpleProperty on a int property.
567      */
568     @Test
569     public void testSetSimpleInt() {
570         final int oldValue = ((Integer) bean.get("intProperty")).intValue();
571         final int newValue = oldValue + 1;
572         bean.set("intProperty", newValue);
573         assertEquals(newValue, ((Integer) bean.get("intProperty")).intValue());
574     }
575 
576     /**
577      * Test setSimpleProperty on a long property.
578      */
579     @Test
580     public void testSetSimpleLong() {
581         final long oldValue = ((Long) bean.get("longProperty")).longValue();
582         final long newValue = oldValue + 1;
583         bean.set("longProperty", newValue);
584         assertEquals(newValue, ((Long) bean.get("longProperty")).longValue());
585     }
586 
587     /**
588      * Test setSimpleProperty on a short property.
589      */
590     @Test
591     public void testSetSimpleShort() {
592         final short oldValue = ((Short) bean.get("shortProperty")).shortValue();
593         final short newValue = (short) (oldValue + 1);
594         bean.set("shortProperty", newValue);
595         assertEquals(newValue, ((Short) bean.get("shortProperty")).shortValue());
596     }
597 
598     /**
599      * Test setSimpleProperty on a String property.
600      */
601     @Test
602     public void testSetSimpleString() {
603         final String oldValue = (String) bean.get("stringProperty");
604         final String newValue = oldValue + " Extra Value";
605         bean.set("stringProperty", newValue);
606         assertEquals(newValue, bean.get("stringProperty"));
607     }
608 }