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