| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.beanutils2; |
| |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertFalse; |
| import static org.junit.jupiter.api.Assertions.assertInstanceOf; |
| import static org.junit.jupiter.api.Assertions.assertNotNull; |
| import static org.junit.jupiter.api.Assertions.assertNull; |
| import static org.junit.jupiter.api.Assertions.assertThrows; |
| import static org.junit.jupiter.api.Assertions.assertTrue; |
| import static org.junit.jupiter.api.Assertions.fail; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.NotSerializableException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.WriteAbortedException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.junit.jupiter.api.AfterEach; |
| import org.junit.jupiter.api.BeforeEach; |
| import org.junit.jupiter.api.Test; |
| |
| /** |
| * <p> |
| * Test Case for the {@code BasicDynaBean} implementation class. These tests were based on the ones in {@code PropertyUtilsTestCase} because the two classes |
| * provide similar levels of functionality. |
| * </p> |
| */ |
| class BasicDynaBeanTest { |
| |
| /** |
| * The set of property names we expect to have returned when calling {@code getDynaProperties()}. You should update this list when new properties are added |
| * to TestBean. |
| */ |
| protected static final String[] properties = { "booleanProperty", "booleanSecond", "doubleProperty", "floatProperty", "intArray", "intIndexed", |
| "intProperty", "listIndexed", "longProperty", "mappedProperty", "mappedIntProperty", "nullProperty", "shortProperty", "stringArray", |
| "stringIndexed", "stringProperty", }; |
| |
| /** |
| * The basic test bean for each test. |
| */ |
| protected DynaBean bean; |
| |
| /** |
| * Create and return a {@code DynaClass} instance for our test {@code DynaBean}. |
| */ |
| protected DynaClass createDynaClass() { |
| final int[] intArray = {}; |
| final String[] stringArray = {}; |
| final DynaClass dynaClass = new BasicDynaClass("TestDynaClass", null, new DynaProperty[] { new DynaProperty("booleanProperty", Boolean.TYPE), |
| new DynaProperty("booleanSecond", Boolean.TYPE), new DynaProperty("doubleProperty", Double.TYPE), new DynaProperty("floatProperty", Float.TYPE), |
| new DynaProperty("intArray", intArray.getClass()), new DynaProperty("intIndexed", intArray.getClass()), |
| new DynaProperty("intProperty", Integer.TYPE), new DynaProperty("listIndexed", List.class), new DynaProperty("longProperty", Long.TYPE), |
| new DynaProperty("mappedProperty", Map.class), new DynaProperty("mappedIntProperty", Map.class), new DynaProperty("nullProperty", String.class), |
| new DynaProperty("shortProperty", Short.TYPE), new DynaProperty("stringArray", stringArray.getClass()), |
| new DynaProperty("stringIndexed", stringArray.getClass()), new DynaProperty("stringProperty", String.class), }); |
| return dynaClass; |
| } |
| |
| /** |
| * Sets up instance variables required by this test case. |
| */ |
| @BeforeEach |
| public void setUp() throws Exception { |
| // Instantiate a new DynaBean instance |
| final DynaClass dynaClass = createDynaClass(); |
| bean = dynaClass.newInstance(); |
| // Initialize the DynaBean's property values (like TestBean) |
| bean.set("booleanProperty", Boolean.valueOf(true)); |
| bean.set("booleanSecond", Boolean.valueOf(true)); |
| bean.set("doubleProperty", Double.valueOf(321.0)); |
| bean.set("floatProperty", Float.valueOf((float) 123.0)); |
| final int[] intArray = { 0, 10, 20, 30, 40 }; |
| bean.set("intArray", intArray); |
| final int[] intIndexed = { 0, 10, 20, 30, 40 }; |
| bean.set("intIndexed", intIndexed); |
| bean.set("intProperty", Integer.valueOf(123)); |
| final List<String> listIndexed = new ArrayList<>(); |
| listIndexed.add("String 0"); |
| listIndexed.add("String 1"); |
| listIndexed.add("String 2"); |
| listIndexed.add("String 3"); |
| listIndexed.add("String 4"); |
| bean.set("listIndexed", listIndexed); |
| bean.set("longProperty", Long.valueOf(321)); |
| final HashMap<String, String> mappedProperty = new HashMap<>(); |
| mappedProperty.put("First Key", "First Value"); |
| mappedProperty.put("Second Key", "Second Value"); |
| bean.set("mappedProperty", mappedProperty); |
| final HashMap<String, Integer> mappedIntProperty = new HashMap<>(); |
| mappedIntProperty.put("One", Integer.valueOf(1)); |
| mappedIntProperty.put("Two", Integer.valueOf(2)); |
| bean.set("mappedIntProperty", mappedIntProperty); |
| // Property "nullProperty" is not initialized, so it should return null |
| bean.set("shortProperty", Short.valueOf((short) 987)); |
| final String[] stringArray = { "String 0", "String 1", "String 2", "String 3", "String 4" }; |
| bean.set("stringArray", stringArray); |
| final String[] stringIndexed = { "String 0", "String 1", "String 2", "String 3", "String 4" }; |
| bean.set("stringIndexed", stringIndexed); |
| bean.set("stringProperty", "This is a string"); |
| } |
| |
| /** |
| * Tear down instance variables required by this test case. |
| */ |
| @AfterEach |
| public void tearDown() { |
| |
| bean = null; |
| |
| } |
| |
| /** |
| * Corner cases on getDynaProperty invalid arguments. |
| */ |
| @Test |
| void testGetDescriptorArguments() { |
| assertNull(bean.getDynaClass().getDynaProperty("unknown")); |
| assertThrows(NullPointerException.class, () -> bean.getDynaClass().getDynaProperty(null)); |
| } |
| |
| /** |
| * Base for testGetDescriptorXxxxx() series of tests. |
| * |
| * @param name Name of the property to be retrieved |
| * @param type Expected class type of this property |
| */ |
| protected void testGetDescriptorBase(final String name, final Class<?> type) { |
| final DynaProperty descriptor = bean.getDynaClass().getDynaProperty(name); |
| assertNotNull(descriptor, "Got descriptor"); |
| assertEquals(type, descriptor.getType(), "Got correct type"); |
| } |
| |
| /** |
| * Positive getDynaProperty on property {@code booleanProperty}. |
| */ |
| @Test |
| void testGetDescriptorBoolean() { |
| testGetDescriptorBase("booleanProperty", Boolean.TYPE); |
| } |
| |
| /** |
| * Positive getDynaProperty on property {@code doubleProperty}. |
| */ |
| @Test |
| void testGetDescriptorDouble() { |
| testGetDescriptorBase("doubleProperty", Double.TYPE); |
| } |
| |
| /** |
| * Positive getDynaProperty on property {@code floatProperty}. |
| */ |
| @Test |
| void testGetDescriptorFloat() { |
| testGetDescriptorBase("floatProperty", Float.TYPE); |
| } |
| |
| /** |
| * Positive getDynaProperty on property {@code intProperty}. |
| */ |
| @Test |
| void testGetDescriptorInt() { |
| testGetDescriptorBase("intProperty", Integer.TYPE); |
| } |
| |
| /** |
| * Positive getDynaProperty on property {@code longProperty}. |
| */ |
| @Test |
| void testGetDescriptorLong() { |
| testGetDescriptorBase("longProperty", Long.TYPE); |
| } |
| |
| /** |
| * Positive test for getDynaProperties(). Each property name listed in {@code properties} should be returned exactly once. |
| */ |
| @Test |
| void testGetDescriptors() { |
| final DynaProperty[] pd = bean.getDynaClass().getDynaProperties(); |
| assertNotNull(pd, "Got descriptors"); |
| final int[] count = new int[properties.length]; |
| for (final DynaProperty element : pd) { |
| final String name = element.getName(); |
| for (int j = 0; j < properties.length; j++) { |
| if (name.equals(properties[j])) { |
| count[j]++; |
| } |
| } |
| } |
| for (int j = 0; j < properties.length; j++) { |
| if (count[j] < 0) { |
| fail("Missing property " + properties[j]); |
| } else if (count[j] > 1) { |
| fail("Duplicate property " + properties[j]); |
| } |
| } |
| } |
| |
| /** |
| * Positive getDynaProperty on property {@code booleanSecond} that uses an "is" method as the getter. |
| */ |
| @Test |
| void testGetDescriptorSecond() { |
| testGetDescriptorBase("booleanSecond", Boolean.TYPE); |
| } |
| |
| /** |
| * Positive getDynaProperty on property {@code shortProperty}. |
| */ |
| @Test |
| void testGetDescriptorShort() { |
| testGetDescriptorBase("shortProperty", Short.TYPE); |
| } |
| |
| /** |
| * Positive getDynaProperty on property {@code stringProperty}. |
| */ |
| @Test |
| void testGetDescriptorString() { |
| testGetDescriptorBase("stringProperty", String.class); |
| } |
| |
| /** |
| * Corner cases on getIndexedProperty invalid arguments. |
| */ |
| @Test |
| void testGetIndexedArguments() { |
| assertThrows(IndexOutOfBoundsException.class, () -> bean.get("intArray", -1)); |
| } |
| |
| /** |
| * Positive and negative tests on getIndexedProperty valid arguments. |
| */ |
| @Test |
| void testGetIndexedValues() { |
| Object value = null; |
| for (int i = 0; i < 5; i++) { |
| value = bean.get("intArray", i); |
| assertNotNull(value, "intArray returned value " + i); |
| assertInstanceOf(Integer.class, value, "intArray returned Integer " + i); |
| assertEquals(i * 10, ((Integer) value).intValue(), "intArray returned correct " + i); |
| value = bean.get("intIndexed", i); |
| assertNotNull(value, "intIndexed returned value " + i); |
| assertInstanceOf(Integer.class, value, "intIndexed returned Integer " + i); |
| assertEquals(i * 10, ((Integer) value).intValue(), "intIndexed returned correct " + i); |
| value = bean.get("listIndexed", i); |
| assertNotNull(value, "listIndexed returned value " + i); |
| assertInstanceOf(String.class, value, "list returned String " + i); |
| assertEquals("String " + i, (String) value, "listIndexed returned correct " + i); |
| value = bean.get("stringArray", i); |
| assertNotNull(value, "stringArray returned value " + i); |
| assertInstanceOf(String.class, value, "stringArray returned String " + i); |
| assertEquals("String " + i, (String) value, "stringArray returned correct " + i); |
| value = bean.get("stringIndexed", i); |
| assertNotNull(value, "stringIndexed returned value " + i); |
| assertInstanceOf(String.class, value, "stringIndexed returned String " + i); |
| assertEquals("String " + i, (String) value, "stringIndexed returned correct " + i); |
| } |
| } |
| |
| /** |
| * Corner cases on getMappedProperty invalid arguments. |
| */ |
| @Test |
| void testGetMappedArguments() { |
| final Object value = bean.get("mappedProperty", "unknown"); |
| assertNull(value, "Should not return a value"); |
| } |
| |
| /** |
| * Positive and negative tests on getMappedProperty valid arguments. |
| */ |
| @Test |
| void testGetMappedValues() { |
| Object value = null; |
| value = bean.get("mappedProperty", "First Key"); |
| assertEquals("First Value", value, "Can find first value"); |
| value = bean.get("mappedProperty", "Second Key"); |
| assertEquals("Second Value", value, "Can find second value"); |
| value = bean.get("mappedProperty", "Third Key"); |
| assertNull(value, "Can not find third value"); |
| } |
| |
| /** |
| * Corner cases on getSimpleProperty invalid arguments. |
| */ |
| @Test |
| void testGetSimpleArguments() { |
| assertThrows(NullPointerException.class, () -> bean.get(null)); |
| } |
| |
| /** |
| * Test getSimpleProperty on a boolean property. |
| */ |
| @Test |
| void testGetSimpleBoolean() { |
| final Object value = bean.get("booleanProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Boolean.class, value, "Got correct type"); |
| assertTrue((Boolean) value, "Got correct value"); |
| } |
| |
| /** |
| * Test getSimpleProperty on a double property. |
| */ |
| @Test |
| void testGetSimpleDouble() { |
| final Object value = bean.get("doubleProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Double.class, value, "Got correct type"); |
| assertEquals(((Double) value).doubleValue(), 321.0, 0.005, "Got correct value"); |
| } |
| |
| /** |
| * Test getSimpleProperty on a float property. |
| */ |
| @Test |
| void testGetSimpleFloat() { |
| final Object value = bean.get("floatProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Float.class, value, "Got correct type"); |
| assertEquals(((Float) value).floatValue(), (float) 123.0, (float) 0.005, "Got correct value"); |
| } |
| |
| /** |
| * Test getSimpleProperty on a int property. |
| */ |
| @Test |
| void testGetSimpleInt() { |
| final Object value = bean.get("intProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Integer.class, value, "Got correct type"); |
| assertEquals(((Integer) value).intValue(), 123, "Got correct value"); |
| } |
| |
| /** |
| * Test getSimpleProperty on a long property. |
| */ |
| @Test |
| void testGetSimpleLong() { |
| final Object value = bean.get("longProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Long.class, value, "Got correct type"); |
| assertEquals(((Long) value).longValue(), 321, "Got correct value"); |
| } |
| |
| /** |
| * Test getSimpleProperty on a short property. |
| */ |
| @Test |
| void testGetSimpleShort() { |
| final Object value = bean.get("shortProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Short.class, value, "Got correct type"); |
| assertEquals(((Short) value).shortValue(), (short) 987, "Got correct value"); |
| } |
| |
| /** |
| * Test getSimpleProperty on a String property. |
| */ |
| @Test |
| void testGetSimpleString() { |
| final Object value = bean.get("stringProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(String.class, value, "Got correct type"); |
| assertEquals((String) value, "This is a string", "Got correct value"); |
| } |
| |
| /** |
| * Test {@code contains()} method for mapped properties. |
| */ |
| @Test |
| void testMappedContains() { |
| assertTrue(bean.contains("mappedProperty", "First Key"), "Can see first key"); |
| assertFalse(bean.contains("mappedProperty", "Unknown Key"), "Can not see unknown key"); |
| } |
| |
| /** |
| * Test {@code remove()} method for mapped properties. |
| */ |
| @Test |
| void testMappedRemove() { |
| assertTrue(bean.contains("mappedProperty", "First Key"), "Can see first key"); |
| bean.remove("mappedProperty", "First Key"); |
| assertFalse(bean.contains("mappedProperty", "First Key"), "Can not see first key"); |
| assertFalse(bean.contains("mappedProperty", "Unknown Key"), "Can not see unknown key"); |
| bean.remove("mappedProperty", "Unknown Key"); |
| assertFalse(bean.contains("mappedProperty", "Unknown Key"), "Can not see unknown key"); |
| } |
| |
| /** |
| * Test serialization and deserialization. |
| */ |
| @Test |
| void testNotSerializableException() throws Exception { |
| // Serialize the test bean |
| final ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { |
| assertThrows(NotSerializableException.class, () -> oos.writeObject(bean)); |
| oos.flush(); |
| } |
| // Deserialize the test bean |
| bean = null; |
| try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray())) { |
| final ObjectInputStream ois = new ObjectInputStream(bais); |
| assertThrows(WriteAbortedException.class, () -> bean = (DynaBean) ois.readObject()); |
| } |
| assertNull(bean); |
| } |
| |
| /** |
| * Corner cases on setIndexedProperty invalid arguments. |
| */ |
| @Test |
| void testSetIndexedArguments() { |
| assertThrows(IndexOutOfBoundsException.class, () -> bean.set("intArray", -1, Integer.valueOf(0))); |
| } |
| |
| /** |
| * Positive and negative tests on setIndexedProperty valid arguments. |
| */ |
| @Test |
| void testSetIndexedValues() { |
| Object value = null; |
| bean.set("intArray", 0, Integer.valueOf(1)); |
| value = bean.get("intArray", 0); |
| assertNotNull(value, "Returned new value 0"); |
| assertInstanceOf(Integer.class, value, "Returned Integer new value 0"); |
| assertEquals(1, ((Integer) value).intValue(), "Returned correct new value 0"); |
| bean.set("intIndexed", 1, Integer.valueOf(11)); |
| value = bean.get("intIndexed", 1); |
| assertNotNull(value, "Returned new value 1"); |
| assertInstanceOf(Integer.class, value, "Returned Integer new value 1"); |
| assertEquals(11, ((Integer) value).intValue(), "Returned correct new value 1"); |
| bean.set("listIndexed", 2, "New Value 2"); |
| value = bean.get("listIndexed", 2); |
| assertNotNull(value, "Returned new value 2"); |
| assertInstanceOf(String.class, value, "Returned String new value 2"); |
| assertEquals("New Value 2", (String) value, "Returned correct new value 2"); |
| bean.set("stringArray", 3, "New Value 3"); |
| value = bean.get("stringArray", 3); |
| assertNotNull(value, "Returned new value 3"); |
| assertInstanceOf(String.class, value, "Returned String new value 3"); |
| assertEquals("New Value 3", (String) value, "Returned correct new value 3"); |
| bean.set("stringIndexed", 4, "New Value 4"); |
| value = bean.get("stringIndexed", 4); |
| assertNotNull(value, "Returned new value 4"); |
| assertInstanceOf(String.class, value, "Returned String new value 4"); |
| assertEquals("New Value 4", (String) value, "Returned correct new value 4"); |
| } |
| |
| /** |
| * Positive and negative tests on setMappedProperty valid arguments. |
| */ |
| @Test |
| void testSetMappedValues() { |
| bean.set("mappedProperty", "First Key", "New First Value"); |
| assertEquals("New First Value", (String) bean.get("mappedProperty", "First Key"), "Can replace old value"); |
| bean.set("mappedProperty", "Fourth Key", "Fourth Value"); |
| assertEquals("Fourth Value", (String) bean.get("mappedProperty", "Fourth Key"), "Can set new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on a boolean property. |
| */ |
| @Test |
| void testSetSimpleBoolean() { |
| final boolean oldValue = ((Boolean) bean.get("booleanProperty")).booleanValue(); |
| final boolean newValue = !oldValue; |
| bean.set("booleanProperty", Boolean.valueOf(newValue)); |
| assertEquals(newValue, ((Boolean) bean.get("booleanProperty")).booleanValue(), "Matched new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on a double property. |
| */ |
| @Test |
| void testSetSimpleDouble() { |
| final double oldValue = ((Double) bean.get("doubleProperty")).doubleValue(); |
| final double newValue = oldValue + 1.0; |
| bean.set("doubleProperty", Double.valueOf(newValue)); |
| assertEquals(newValue, ((Double) bean.get("doubleProperty")).doubleValue(), 0.005, "Matched new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on a float property. |
| */ |
| @Test |
| void testSetSimpleFloat() { |
| final float oldValue = ((Float) bean.get("floatProperty")).floatValue(); |
| final float newValue = oldValue + (float) 1.0; |
| bean.set("floatProperty", Float.valueOf(newValue)); |
| assertEquals(newValue, ((Float) bean.get("floatProperty")).floatValue(), (float) 0.005, "Matched new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on a int property. |
| */ |
| @Test |
| void testSetSimpleInt() { |
| final int oldValue = ((Integer) bean.get("intProperty")).intValue(); |
| final int newValue = oldValue + 1; |
| bean.set("intProperty", Integer.valueOf(newValue)); |
| assertEquals(newValue, ((Integer) bean.get("intProperty")).intValue(), "Matched new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on a long property. |
| */ |
| @Test |
| void testSetSimpleLong() { |
| final long oldValue = ((Long) bean.get("longProperty")).longValue(); |
| final long newValue = oldValue + 1; |
| bean.set("longProperty", Long.valueOf(newValue)); |
| assertEquals(newValue, ((Long) bean.get("longProperty")).longValue(), "Matched new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on a short property. |
| */ |
| @Test |
| void testSetSimpleShort() { |
| final short oldValue = ((Short) bean.get("shortProperty")).shortValue(); |
| final short newValue = (short) (oldValue + 1); |
| bean.set("shortProperty", Short.valueOf(newValue)); |
| assertEquals(newValue, ((Short) bean.get("shortProperty")).shortValue(), "Matched new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on a String property. |
| */ |
| @Test |
| void testSetSimpleString() { |
| final String oldValue = (String) bean.get("stringProperty"); |
| final String newValue = oldValue + " Extra Value"; |
| bean.set("stringProperty", newValue); |
| assertEquals(newValue, (String) bean.get("stringProperty"), "Matched new value"); |
| } |
| |
| } |