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.beanutils;
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.assertNull;
024    import static org.junit.Assert.assertTrue;
025    import static org.junit.Assert.fail;
026    
027    import java.util.Arrays;
028    import java.util.HashMap;
029    import java.util.List;
030    
031    import junitx.framework.ObjectAssert;
032    
033    import org.apache.commons.beanutils.DynaProperty;
034    import org.apache.commons.configuration.BaseConfiguration;
035    import org.apache.commons.configuration.Configuration;
036    import org.apache.commons.configuration.MapConfiguration;
037    import org.junit.Before;
038    import org.junit.Test;
039    
040    /**
041     * <p>Test Case for the <code>ConfigurationDynaBean</code> implementation class.
042     * These tests were based on the ones in <code>BasicDynaBeanTestCase</code>
043     * because the two classes provide similar levels of functionality.</p>
044     *
045     * @author <a href="mailto:ricardo.gladwell@btinternet.com">Ricardo Gladwell</a>
046     * @version $Id: TestConfigurationDynaBean.java 1225349 2011-12-28 21:36:59Z oheger $
047     */
048    public class TestConfigurationDynaBean
049    {
050        /**
051         * The basic test bean for each test.
052         */
053        private ConfigurationDynaBean bean;
054    
055        /**
056         * The set of property names we expect to have returned when calling
057         * <code>getDynaProperties()</code>.  You should update this list
058         * when new properties are added to TestBean.
059         */
060        String[] properties = {
061                "booleanProperty",
062                "booleanSecond",
063                "doubleProperty",
064                "floatProperty",
065                "intProperty",
066                "longProperty",
067                "mappedProperty.key1",
068                "mappedProperty.key2",
069                "mappedProperty.key3",
070                "mappedIntProperty.key1",
071                "shortProperty",
072                "stringProperty",
073                "byteProperty",
074                "charProperty"
075        };
076    
077        Object[] values = {
078                Boolean.TRUE,
079                Boolean.TRUE,
080                new Double(Double.MAX_VALUE),
081                new Float(Float.MAX_VALUE),
082                new Integer(Integer.MAX_VALUE),
083                new Long(Long.MAX_VALUE),
084                "First Value",
085                "Second Value",
086                "Third Value",
087                new Integer(Integer.MAX_VALUE),
088                new Short(Short.MAX_VALUE),
089                "This is a string",
090                new Byte(Byte.MAX_VALUE),
091                new Character(Character.MAX_VALUE)
092        };
093    
094        int[] intArray = {0, 10, 20, 30, 40};
095        boolean[] booleanArray = {true, false, true, false, true};
096        char[] charArray = {'a', 'b', 'c', 'd', 'e'};
097        byte[] byteArray = {0, 10, 20, 30, 40};
098        long[] longArray = {0, 10, 20, 30, 40};
099        short[] shortArray = {0, 10, 20, 30, 40};
100        float[] floatArray = {0, 10, 20, 30, 40};
101        double[] doubleArray = {0.0, 10.0, 20.0, 30.0, 40.0};
102        String[] stringArray = {"String 0", "String 1", "String 2", "String 3", "String 4"};
103    
104    
105        /**
106         * Set up instance variables required by this test case.
107         */
108        @Before
109        public void setUp() throws Exception
110        {
111            Configuration configuration = createConfiguration();
112    
113            for (int i = 0; i < properties.length; i++)
114            {
115                configuration.setProperty(properties[i], values[i]);
116            }
117    
118            for (int a = 0; a < intArray.length; a++)
119            {
120                configuration.addProperty("intIndexed", new Integer(intArray[a]));
121            }
122    
123            for (int a = 0; a < stringArray.length; a++)
124            {
125                configuration.addProperty("stringIndexed", stringArray[a]);
126            }
127    
128            List<String> list = Arrays.asList(stringArray);
129            configuration.addProperty("listIndexed", list);
130    
131            bean = new ConfigurationDynaBean(configuration);
132    
133            bean.set("listIndexed", list);
134            bean.set("intArray", intArray);
135            bean.set("booleanArray", booleanArray);
136            bean.set("charArray", charArray);
137            bean.set("longArray", longArray);
138            bean.set("shortArray", shortArray);
139            bean.set("floatArray", floatArray);
140            bean.set("doubleArray", doubleArray);
141            bean.set("byteArray", byteArray);
142            bean.set("stringArray", stringArray);
143        }
144    
145        /**
146         * Creates the underlying configuration object for the dyna bean.
147         * @return the underlying configuration object
148         */
149        protected Configuration createConfiguration()
150        {
151            return new BaseConfiguration();
152        }
153    
154        /**
155         * Corner cases on getDynaProperty invalid arguments.
156         */
157        @Test(expected = IllegalArgumentException.class)
158        public void testGetDescriptorArguments()
159        {
160            DynaProperty descriptor = bean.getDynaClass().getDynaProperty("unknown");
161            assertNull("Unknown property descriptor should be null", descriptor);
162            bean.getDynaClass().getDynaProperty(null);
163        }
164    
165        /**
166         * Positive getDynaProperty on property <code>booleanProperty</code>.
167         */
168        @Test
169        public void testGetDescriptorBoolean()
170        {
171            testGetDescriptorBase("booleanProperty", Boolean.TYPE);
172        }
173    
174        /**
175         * Positive getDynaProperty on property <code>doubleProperty</code>.
176         */
177        @Test
178        public void testGetDescriptorDouble()
179        {
180            testGetDescriptorBase("doubleProperty", Double.TYPE);
181        }
182    
183        /**
184         * Positive getDynaProperty on property <code>floatProperty</code>.
185         */
186        @Test
187        public void testGetDescriptorFloat()
188        {
189            testGetDescriptorBase("floatProperty", Float.TYPE);
190        }
191    
192        /**
193         * Positive getDynaProperty on property <code>intProperty</code>.
194         */
195        @Test
196        public void testGetDescriptorInt()
197        {
198            testGetDescriptorBase("intProperty", Integer.TYPE);
199        }
200    
201        /**
202         * Positive getDynaProperty on property <code>longProperty</code>.
203         */
204        @Test
205        public void testGetDescriptorLong()
206        {
207            testGetDescriptorBase("longProperty", Long.TYPE);
208        }
209    
210        /**
211         * Positive getDynaProperty on property <code>booleanSecond</code>
212         * that uses an "is" method as the getter.
213         */
214        @Test
215        public void testGetDescriptorSecond()
216        {
217            testGetDescriptorBase("booleanSecond", Boolean.TYPE);
218        }
219    
220        /**
221         * Positive getDynaProperty on property <code>shortProperty</code>.
222         */
223        @Test
224        public void testGetDescriptorShort()
225        {
226            testGetDescriptorBase("shortProperty", Short.TYPE);
227        }
228    
229        /**
230         * Positive getDynaProperty on property <code>stringProperty</code>.
231         */
232        @Test
233        public void testGetDescriptorString()
234        {
235            testGetDescriptorBase("stringProperty", String.class);
236        }
237    
238        /**
239         * Positive test for getDynaPropertys().  Each property name
240         * listed in <code>properties</code> should be returned exactly once.
241         */
242        @Test
243        public void testGetDescriptors()
244        {
245            DynaProperty pd[] = bean.getDynaClass().getDynaProperties();
246            assertNotNull("Got descriptors", pd);
247            int count[] = new int[properties.length];
248            for (int i = 0; i < pd.length; i++)
249            {
250                String name = pd[i].getName();
251                for (int j = 0; j < properties.length; j++)
252                {
253                    if (name.equals(properties[j]))
254                    {
255                        count[j]++;
256                    }
257                }
258            }
259    
260            for (int j = 0; j < properties.length; j++)
261            {
262                if (count[j] < 0)
263                {
264                    fail("Missing property " + properties[j]);
265                }
266                else if (count[j] > 1)
267                {
268                    fail("Duplicate property " + properties[j]);
269                }
270            }
271        }
272    
273        /**
274         * Corner cases on getIndexedProperty invalid arguments.
275         */
276        @Test(expected = IndexOutOfBoundsException.class)
277        public void testGetIndexedArguments()
278        {
279            bean.get("intArray", -1);
280        }
281    
282        /**
283         * Positive and negative tests on getIndexedProperty valid arguments.
284         */
285        @Test
286        public void testGetIndexedValues()
287        {
288            for (int i = 0; i < 5; i++)
289            {
290                Object value = bean.get("intArray", i);
291    
292                assertNotNull("intArray index " + i + " did not return value.", value);
293                ObjectAssert.assertInstanceOf("intArray index " + i, Integer.class, value);
294                assertEquals("intArray " + i + " returned incorrect value.", i * 10, ((Integer) value).intValue());
295    
296                value = bean.get("intIndexed", i);
297    
298                assertNotNull("intIndexed index " + i + "returned value " + i, value);
299                ObjectAssert.assertInstanceOf("intIndexed index " + i, Integer.class, value);
300                assertEquals("intIndexed index " + i + "returned correct " + i, i * 10, ((Integer) value).intValue());
301    
302                value = bean.get("listIndexed", i);
303    
304                assertNotNull("listIndexed index " + i + "returned value " + i, value);
305                ObjectAssert.assertInstanceOf("list index " + i, String.class, value);
306                assertEquals("listIndexed index " + i + "returned correct " + i, "String " + i, value);
307    
308                value = bean.get("stringArray", i);
309    
310                assertNotNull("stringArray index " + i + " returnde null.", value);
311                assertFalse("stringArray index " + i + " returned array instead of String.", value.getClass().isArray());
312                ObjectAssert.assertInstanceOf("stringArray index " + i, String.class, value);
313                assertEquals("stringArray returned correct " + i, "String " + i, value);
314    
315                value = bean.get("stringIndexed", i);
316    
317                assertNotNull("stringIndexed returned value " + i, value);
318                ObjectAssert.assertInstanceOf("stringIndexed", String.class, value);
319                assertEquals("stringIndexed returned correct " + i, "String " + i, value);
320            }
321        }
322    
323        /**
324         * Corner cases on getMappedProperty invalid arguments.
325         */
326        @Test
327        public void testGetMappedArguments()
328        {
329            try
330            {
331                Object value = bean.get("mappedProperty", "unknown");
332                assertNull("Should not return a value", value);
333            }
334            catch (Throwable t)
335            {
336                fail("Threw " + t + " instead of returning null");
337            }
338        }
339    
340        /**
341         * Positive and negative tests on getMappedProperty valid arguments.
342         */
343        @Test
344        public void testGetMappedValues()
345        {
346            Object value = bean.get("mappedProperty", "key1");
347            assertEquals("Can find first value", "First Value", value);
348    
349            value = bean.get("mappedProperty", "key2");
350            assertEquals("Can find second value", "Second Value", value);
351    
352            value = bean.get("mappedProperty", "key3");
353            assertNotNull("Cannot find third value", value);
354        }
355    
356        /**
357         * Corner cases on getSimpleProperty invalid arguments.
358         */
359        @Test(expected = IllegalArgumentException.class)
360        public void testGetSimpleArguments()
361        {
362            bean.get("a non existing property");
363        }
364    
365        /**
366         * Test getSimpleProperty on a boolean property.
367         */
368        @Test
369        public void testGetSimpleBoolean()
370        {
371            Object value = bean.get("booleanProperty");
372            assertNotNull("Got a value", value);
373            ObjectAssert.assertInstanceOf("Got correct type", Boolean.class, value);
374            assertTrue("Got correct value", ((Boolean) value).booleanValue());
375        }
376    
377        /**
378         * Test getSimpleProperty on a double property.
379         */
380        @Test
381        public void testGetSimpleDouble()
382        {
383            Object value = bean.get("doubleProperty");
384            assertNotNull("Got a value", value);
385            ObjectAssert.assertInstanceOf("Got correct type", Double.class, value);
386            assertEquals("Got correct value", ((Double) value).doubleValue(), Double.MAX_VALUE, 0.005);
387        }
388    
389        /**
390         * Test getSimpleProperty on a float property.
391         */
392        @Test
393        public void testGetSimpleFloat()
394        {
395            Object value = bean.get("floatProperty");
396            assertNotNull("Got a value", value);
397            ObjectAssert.assertInstanceOf("Got correct type", Float.class, value);
398            assertEquals("Got correct value", ((Float) value).floatValue(), Float.MAX_VALUE, 0.005f);
399        }
400    
401        /**
402         * Test getSimpleProperty on a int property.
403         */
404        @Test
405        public void testGetSimpleInt()
406        {
407            Object value = bean.get("intProperty");
408            assertNotNull("Failed to get value", value);
409            ObjectAssert.assertInstanceOf("Incorrect type", Integer.class, value);
410            assertEquals("Incorrect value", ((Integer) value).intValue(), Integer.MAX_VALUE);
411        }
412    
413        /**
414         * Test getSimpleProperty on a long property.
415         */
416        @Test
417        public void testGetSimpleLong()
418        {
419            Object value = bean.get("longProperty");
420            assertNotNull("Got a value", value);
421            ObjectAssert.assertInstanceOf("Returned incorrect type", Long.class, value);
422            assertEquals("Returned value of Incorrect value", ((Long) value).longValue(), Long.MAX_VALUE);
423        }
424    
425        /**
426         * Test getSimpleProperty on a short property.
427         */
428        @Test
429        public void testGetSimpleShort()
430        {
431            Object value = bean.get("shortProperty");
432            assertNotNull("Got a value", value);
433            ObjectAssert.assertInstanceOf("Got correct type", Short.class, value);
434            assertEquals("Got correct value", ((Short) value).shortValue(), Short.MAX_VALUE);
435        }
436    
437        /**
438         * Test getSimpleProperty on a String property.
439         */
440        @Test
441        public void testGetSimpleString()
442        {
443            Object value = bean.get("stringProperty");
444            assertNotNull("Got a value", value);
445            ObjectAssert.assertInstanceOf("Got correct type", String.class, value);
446            assertEquals("Got correct value", value, "This is a string");
447        }
448    
449        /**
450         * Test <code>contains()</code> method for mapped properties.
451         */
452        @Test
453        public void testMappedContains()
454        {
455            assertTrue("Can't see first key", bean.contains("mappedProperty", "key1"));
456            assertTrue("Can see unknown key", !bean.contains("mappedProperty", "Unknown Key"));
457        }
458    
459        /**
460         * Test <code>remove()</code> method for mapped properties.
461         */
462        @Test
463        public void testMappedRemove()
464        {
465            assertTrue("Can see first key", bean.contains("mappedProperty", "key1"));
466            bean.remove("mappedProperty", "key1");
467            assertTrue("Can not see first key", !bean.contains("mappedProperty", "key1"));
468    
469            assertTrue("Can not see unknown key", !bean.contains("mappedProperty", "key4"));
470            bean.remove("mappedProperty", "key4");
471            assertTrue("Can not see unknown key", !bean.contains("mappedProperty", "key4"));
472        }
473    
474        /**
475         * Corner cases on setIndexedProperty invalid arguments.
476         */
477        @Test(expected = IndexOutOfBoundsException.class)
478        public void testSetIndexedArguments()
479        {
480            bean.set("intArray", -1, new Integer(0));
481        }
482    
483        /**
484         * Positive and negative tests on setIndexedProperty valid arguments.
485         */
486        @Test
487        public void testSetIndexedValues()
488        {
489            bean.set("intArray", 0, new Integer(1));
490            Object value = bean.get("intArray", 0);
491    
492            assertNotNull("Returned new value 0", value);
493            ObjectAssert.assertInstanceOf("Returned Integer new value 0", Integer.class,  value);
494            assertEquals("Returned correct new value 0", 1, ((Integer) value).intValue());
495    
496    
497            bean.set("intIndexed", 1, new Integer(11));
498            value = bean.get("intIndexed", 1);
499    
500            assertNotNull("Returned new value 1", value);
501            ObjectAssert.assertInstanceOf("Returned Integer new value 1", Integer.class,  value);
502            assertEquals("Returned correct new value 1", 11, ((Integer) value).intValue());
503    
504    
505            bean.set("listIndexed", 2, "New Value 2");
506            value = bean.get("listIndexed", 2);
507    
508            assertNotNull("Returned new value 2", value);
509            ObjectAssert.assertInstanceOf("Returned String new value 2", String.class,  value);
510            assertEquals("Returned correct new value 2", "New Value 2", value);
511    
512    
513            bean.set("stringArray", 3, "New Value 3");
514            value = bean.get("stringArray", 3);
515    
516            assertNotNull("Returned new value 3", value);
517            ObjectAssert.assertInstanceOf("Returned String new value 3", String.class,  value);
518            assertEquals("Returned correct new value 3", "New Value 3", value);
519    
520    
521            bean.set("stringIndexed", 4, "New Value 4");
522            value = bean.get("stringIndexed", 4);
523    
524            assertNotNull("Returned new value 4", value);
525            ObjectAssert.assertInstanceOf("Returned String new value 4", String.class,  value);
526            assertEquals("Returned correct new value 4", "New Value 4", value);
527        }
528    
529        /**
530         * Test the modification of a configuration property stored internally as an array.
531         */
532        @Test
533        public void testSetArrayValue()
534        {
535            MapConfiguration configuration = new MapConfiguration(new HashMap<String, Object>());
536            configuration.getMap().put("objectArray", new Object[] {"value1", "value2", "value3"});
537    
538            ConfigurationDynaBean bean = new ConfigurationDynaBean(configuration);
539    
540            bean.set("objectArray", 1, "New Value 1");
541            Object value = bean.get("objectArray", 1);
542    
543            assertNotNull("Returned new value 1", value);
544            ObjectAssert.assertInstanceOf("Returned String new value 1", String.class,  value);
545            assertEquals("Returned correct new value 1", "New Value 1", value);
546        }
547    
548        /**
549         * Positive and negative tests on setMappedProperty valid arguments.
550         */
551        @Test
552        public void testSetMappedValues()
553        {
554            bean.set("mappedProperty", "First Key", "New First Value");
555            assertEquals("Can replace old value", "New First Value", bean.get("mappedProperty", "First Key"));
556    
557            bean.set("mappedProperty", "Fourth Key", "Fourth Value");
558            assertEquals("Can set new value", "Fourth Value", bean.get("mappedProperty", "Fourth Key"));
559        }
560    
561        /**
562         * Test setSimpleProperty on a boolean property.
563         */
564        @Test
565        public void testSetSimpleBoolean()
566        {
567            boolean oldValue = ((Boolean) bean.get("booleanProperty")).booleanValue();
568            boolean newValue = !oldValue;
569            bean.set("booleanProperty", new Boolean(newValue));
570            assertTrue("Matched new value", newValue == ((Boolean) bean.get("booleanProperty")).booleanValue());
571        }
572    
573        /**
574         * Test setSimpleProperty on a double property.
575         */
576        @Test
577        public void testSetSimpleDouble()
578        {
579            double oldValue = ((Double) bean.get("doubleProperty")).doubleValue();
580            double newValue = oldValue + 1.0;
581            bean.set("doubleProperty", new Double(newValue));
582            assertEquals("Matched new value", newValue, ((Double) bean.get("doubleProperty")).doubleValue(), 0.005);
583        }
584    
585        /**
586         * Test setSimpleProperty on a float property.
587         */
588        @Test
589        public void testSetSimpleFloat()
590        {
591            float oldValue = ((Float) bean.get("floatProperty")).floatValue();
592            float newValue = oldValue + (float) 1.0;
593            bean.set("floatProperty", new Float(newValue));
594            assertEquals("Matched new value", newValue, ((Float) bean.get("floatProperty")).floatValue(), 0.005f);
595        }
596    
597        /**
598         * Test setSimpleProperty on a int property.
599         */
600        @Test
601        public void testSetSimpleInt()
602        {
603            int oldValue = ((Integer) bean.get("intProperty")).intValue();
604            int newValue = oldValue + 1;
605            bean.set("intProperty", new Integer(newValue));
606            assertEquals("Matched new value", newValue, ((Integer) bean.get("intProperty")).intValue());
607        }
608    
609        /**
610         * Test setSimpleProperty on a long property.
611         */
612        @Test
613        public void testSetSimpleLong()
614        {
615            long oldValue = ((Long) bean.get("longProperty")).longValue();
616            long newValue = oldValue + 1;
617            bean.set("longProperty", new Long(newValue));
618            assertEquals("Matched new value", newValue, ((Long) bean.get("longProperty")).longValue());
619        }
620    
621        /**
622         * Test setSimpleProperty on a short property.
623         */
624        @Test
625        public void testSetSimpleShort()
626        {
627            short oldValue = ((Short) bean.get("shortProperty")).shortValue();
628            short newValue = (short) (oldValue + 1);
629            bean.set("shortProperty", new Short(newValue));
630            assertEquals("Matched new value", newValue, ((Short) bean.get("shortProperty")).shortValue());
631        }
632    
633        /**
634         * Test setSimpleProperty on a String property.
635         */
636        @Test
637        public void testSetSimpleString()
638        {
639            String oldValue = (String) bean.get("stringProperty");
640            String newValue = oldValue + " Extra Value";
641            bean.set("stringProperty", newValue);
642            assertEquals("Matched new value", newValue, bean.get("stringProperty"));
643        }
644    
645        /**
646         * Tests set on a null value: should throw NPE.
647         */
648        @Test(expected = NullPointerException.class)
649        public void testAddNullPropertyValue()
650        {
651            bean.set("nullProperty", null);
652        }
653    
654        /**
655         * Test the retrieval of a non-existent property.
656         */
657        @Test(expected = IllegalArgumentException.class)
658        public void testGetNonExistentProperty()
659        {
660            bean.get("nonexistProperty");
661        }
662    
663        /**
664         * Base for testGetDescriptorXxxxx() series of tests.
665         *
666         * @param name Name of the property to be retrieved
667         * @param type Expected class type of this property
668         */
669        protected void testGetDescriptorBase(String name, Class<?> type)
670        {
671            DynaProperty descriptor = bean.getDynaClass().getDynaProperty(name);
672    
673            assertNotNull("Failed to get descriptor", descriptor);
674            assertEquals("Got incorrect type", type, descriptor.getType());
675        }
676    
677        /**
678         * Tests whether nested properties can be accessed.
679         */
680        @Test
681        public void testNestedPropeties()
682        {
683            ConfigurationDynaBean nested = (ConfigurationDynaBean) bean.get("mappedProperty");
684    
685            String value = (String) nested.get("key1");
686            assertEquals("Can find first value", "First Value", value);
687    
688            nested.set("key1", "undefined");
689            assertEquals("Incorrect value returned", "undefined", bean.get("mappedProperty.key1"));
690        }
691    
692        /**
693         * Tests if reading a non-indexed property using the index
694         * get method throws an IllegalArgumentException as it
695         * should.
696         */
697        @Test(expected = IllegalArgumentException.class)
698        public void testGetNonIndexedProperties()
699        {
700            bean.get("booleanProperty", 0);
701        }
702    
703        /**
704         * Tests whether accessing a non-indexed string property using the index get
705         * method causes an exception.
706         */
707        @Test(expected = IllegalArgumentException.class)
708        public void testGetIndexedString()
709        {
710            bean.set("stringProp", "value");
711            bean.get("stringProp", 0);
712        }
713    
714        /**
715         * Tests whether an indexed access to a non-existing property causes an
716         * exception.
717         */
718        @Test(expected = IllegalArgumentException.class)
719        public void testGetIndexedNonExisting()
720        {
721            bean.get("Non existing property", 0);
722        }
723    
724        /**
725         * Tests if writing a non-indexed property using the index
726         * set method with an index &gt; 0 throws an IllegalArgumentException as it
727         * should.
728         */
729        @Test(expected = IllegalArgumentException.class)
730        public void testSetNonIndexedProperties()
731        {
732            bean.set("booleanProperty", 1, Boolean.TRUE);
733        }
734    }