/** * Copyright (C) 2015 Valkyrie RCP * * Licensed 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 * * http://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.valkyriercp.binding.support; import org.junit.Before; import org.junit.Test; import org.springframework.beans.InvalidPropertyException; import org.springframework.beans.NotReadablePropertyException; import org.springframework.beans.NotWritablePropertyException; import org.springframework.beans.NullValueInNestedPathException; import org.valkyriercp.AbstractValkyrieTest; import org.valkyriercp.binding.MutablePropertyAccessStrategy; import org.valkyriercp.binding.PropertyMetadataAccessStrategy; import org.valkyriercp.binding.value.ValueModel; import org.valkyriercp.rules.closure.Closure; import org.valkyriercp.rules.closure.support.Block; import org.valkyriercp.test.TestBean; import org.valkyriercp.test.TestBeanWithPCP; import org.valkyriercp.test.TestPropertyChangeListener; import java.beans.PropertyChangeEvent; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; /** * Tests class {@link AbstractPropertyAccessStrategy}. * * @author Oliver Hutchison * @author Arne Limburg */ public abstract class AbstractPropertyAccessStrategyTests extends AbstractValkyrieTest { protected AbstractPropertyAccessStrategy pas; protected TestBean testBean; protected ValueModel vm; protected TestPropertyChangeListener pcl; @Before public void doSetUp() throws Exception { testBean = new TestBean(); pas = createPropertyAccessStrategy(testBean); pcl = new TestPropertyChangeListener(ValueModel.VALUE_PROPERTY); } protected abstract AbstractPropertyAccessStrategy createPropertyAccessStrategy(Object target); protected boolean isStrictNullHandlingEnabled() { return true; } @Test public void testSimpleProperty() { vm = pas.getPropertyValueModel("simpleProperty"); Block setValueDirectly = new Block() { public void handle(Object newValue) { testBean.setSimpleProperty((String) newValue); } }; Closure getValueDirectly = new Closure() { public Object call(Object ignore) { return testBean.getSimpleProperty(); } }; Object[] valuesToTest = new Object[]{"1", "2", null, "3"}; testSettingAndGetting(valuesToTest, getValueDirectly, setValueDirectly); } @Test public void testNestedProperty() { final TestBean nestedProperty = new TestBean(); testBean.setNestedProperty(nestedProperty); vm = pas.getPropertyValueModel("nestedProperty.simpleProperty"); Block setValueDirectly = new Block() { public void handle(Object newValue) { nestedProperty.setSimpleProperty((String) newValue); } }; Closure getValueDirectly = new Closure() { public Object call(Object ignored) { return nestedProperty.getSimpleProperty(); } }; Object[] valuesToTest = new Object[]{"1", "2", null, "3"}; testSettingAndGetting(valuesToTest, getValueDirectly, setValueDirectly); } @Test public void testChildPropertyAccessStrategy() { final TestBean nestedProperty = new TestBean(); testBean.setNestedProperty(nestedProperty); MutablePropertyAccessStrategy cpas = pas.getPropertyAccessStrategyForPath("nestedProperty"); assertEquals("Child domainObjectHolder should equal equivalent parent ValueModel", pas.getPropertyValueModel("nestedProperty"), cpas.getDomainObjectHolder()); vm = cpas.getPropertyValueModel("simpleProperty"); assertEquals("Child should return the same ValueModel as parent", pas.getPropertyValueModel("nestedProperty.simpleProperty"), vm); Block setValueDirectly = new Block() { public void handle(Object newValue) { nestedProperty.setSimpleProperty((String) newValue); } }; Closure getValueDirectly = new Closure() { public Object call(Object ignore) { return nestedProperty.getSimpleProperty(); } }; Object[] valuesToTest = new Object[]{"1", "2", null, "3"}; testSettingAndGetting(valuesToTest, getValueDirectly, setValueDirectly); try { pas.getPropertyValueModel("nestedProperty").setValue(null); if (isStrictNullHandlingEnabled()) fail("Should have thrown a NullValueInNestedPathException"); } catch (NullValueInNestedPathException e) { if (!isStrictNullHandlingEnabled()) fail("Should not have thrown a NullValueInNestedPathException"); } } @Test public void testMapProperty() { final Map map = new HashMap(); testBean.setMapProperty(map); vm = pas.getPropertyValueModel("mapProperty[.key]"); Block setValueDirectly = new Block() { public void handle(Object newValue) { map.put(".key", newValue); } }; Closure getValueDirectly = new Closure() { public Object call(Object ignore) { return map.get(".key"); } }; Object[] valuesToTest = new Object[]{"1", "2", null, "3"}; testSettingAndGetting(valuesToTest, getValueDirectly, setValueDirectly); try { pas.getPropertyValueModel("mapProperty").setValue(null); if (isStrictNullHandlingEnabled()) fail("Should have thrown a NullValueInNestedPathException"); } catch (NullValueInNestedPathException e) { if (!isStrictNullHandlingEnabled()) fail("Should not have thrown a NullValueInNestedPathException"); } catch (InvalidPropertyException e) { if (e.getCause() instanceof NullValueInNestedPathException) { if (!isStrictNullHandlingEnabled()) fail("Should not have thrown a NullValueInNestedPathException"); } else { throw e; } } } @Test public void testListProperty() { final List list = new ArrayList(); list.add(null); testBean.setListProperty(list); vm = pas.getPropertyValueModel("listProperty[0]"); Block setValueDirectly = new Block() { public void handle(Object newValue) { list.set(0, newValue); } }; Closure getValueDirectly = new Closure() { public Object call(Object ignore) { return list.get(0); } }; Object[] valuesToTest = new Object[]{"1", "2", null, "3"}; testSettingAndGetting(valuesToTest, getValueDirectly, setValueDirectly); list.add("a"); ValueModel vm2 = pas.getPropertyValueModel("listProperty[1]"); assertEquals("a", vm2.getValue()); try { List newList = new ArrayList(); pas.getPropertyValueModel("listProperty").setValue(newList); if (isStrictNullHandlingEnabled()) fail("Should have thrown an InvalidPropertyException"); } catch (InvalidPropertyException e) { if (!isStrictNullHandlingEnabled()) fail("Should not have thrown an InvalidPropertyException"); } try { pas.getPropertyValueModel("listProperty").setValue(null); if (isStrictNullHandlingEnabled()) fail("Should have thrown a NullValueInNestedPathException"); } catch (NullValueInNestedPathException e) { if (!isStrictNullHandlingEnabled()) fail("Should not have thrown a NullValueInNestedPathException"); } catch (InvalidPropertyException e) { if (e.getCause() instanceof NullValueInNestedPathException) { if (!isStrictNullHandlingEnabled()) fail("Should not have thrown a NullValueInNestedPathException"); } else { throw e; } } } @Test public void testReadOnlyProperty() { vm = pas.getPropertyValueModel("readOnly"); testBean.readOnly = "1"; assertEquals(testBean.readOnly, vm.getValue()); try { vm.setValue("2"); fail("should have thrown NotWritablePropertyException"); } catch (NotWritablePropertyException e) { // expected } } @Test public void testWriteOnlyProperty() { vm = pas.getPropertyValueModel("writeOnly"); vm.setValue("2"); assertEquals("2", testBean.writeOnly); try { vm.getValue(); fail("should have thrown NotReadablePropertyException"); } catch (NotReadablePropertyException e) { // expected } } @Test public void testBeanThatImplementsPropertyChangePublisher() { TestBeanWithPCP testBeanPCP = new TestBeanWithPCP(); pas.getDomainObjectHolder().setValue(testBeanPCP); vm = pas.getPropertyValueModel("boundProperty"); assertEquals("ValueModel should have registered a PropertyChangeListener", 1, testBeanPCP.getPropertyChangeListeners("boundProperty").length); vm.addValueChangeListener(pcl); testBeanPCP.setBoundProperty("1"); assertEquals("Change to bound property should have been detected by ValueModel", 1, pcl.getEventsRecevied() .size()); PropertyChangeEvent e = (PropertyChangeEvent) pcl.getEventsRecevied().get(0); assertEquals(vm, e.getSource()); assertEquals(null, e.getOldValue()); assertEquals("1", e.getNewValue()); pcl.reset(); vm.setValue("2"); assertEquals("Change to bound property should have been detected by ValueModel", 1, pcl.getEventsRecevied().size()); e = (PropertyChangeEvent) pcl.getEventsRecevied().get(0); assertEquals(vm, e.getSource()); assertEquals("1", e.getOldValue()); assertEquals("2", e.getNewValue()); TestBeanWithPCP testBeanPCP2 = new TestBeanWithPCP(); pas.getDomainObjectHolder().setValue(testBeanPCP2); assertEquals("ValueModel should have removed the PropertyChangeListener", 0, testBeanPCP.getPropertyChangeListeners("boundProperty").length); assertEquals("ValueModel should have registered a PropertyChangeListener", 1, testBeanPCP2.getPropertyChangeListeners("boundProperty").length); } private void testSettingAndGetting(Object[] valuesToTest, Closure getValueDirectly, Block setValueDirectly) { vm.addValueChangeListener(pcl); for (int i = 0; i < valuesToTest.length; i++) { final Object valueToTest = valuesToTest[i]; pcl.reset(); assertEquals("ValueModel does not have same value as bean property", getValueDirectly.call(null), vm.getValue()); setValueDirectly.call(valueToTest); assertEquals("Change to bean not picked up by ValueModel", valueToTest, vm.getValue()); setValueDirectly.call(null); assertEquals("Change to bean not picked up by ValueModel", null, vm.getValue()); vm.setValue(valueToTest); assertEquals("Change to ValueModel not reflected in bean", valueToTest, getValueDirectly.call(null)); assertEquals("Change to ValueModel had no effect", valueToTest, vm.getValue()); if (valueToTest != null) { assertEquals("Incorrect number of property change events fired by value model", 1, pcl.getEventsRecevied().size()); PropertyChangeEvent e = (PropertyChangeEvent) pcl.getEventsRecevied().get(0); assertEquals(vm, e.getSource()); assertEquals(null, e.getOldValue()); assertEquals(valueToTest, e.getNewValue()); } } } protected void assertPropertyMetadata(PropertyMetadataAccessStrategy mas, String property, Class type, boolean isReadable, boolean isWriteable) { assertEquals(type, mas.getPropertyType(property)); assertEquals(isReadable, mas.isReadable(property)); assertEquals(isWriteable, mas.isWriteable(property)); } }