/* * Copyright (c) 2017 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.magma.beans; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import org.easymock.EasyMock; import org.junit.Test; import org.obiba.magma.Category; import org.obiba.magma.Value; import org.obiba.magma.ValueSet; import org.obiba.magma.Variable; import org.obiba.magma.VariableValueSource; import org.obiba.magma.VariableValueSourceFactory; import org.obiba.magma.test.AbstractMagmaTest; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import static org.fest.assertions.api.Assertions.assertThat; /** * */ public class BeanVariableProviderTest extends AbstractMagmaTest { @Test public void testSimpleProperties() { Set<String> properties = Sets .newHashSet("firstName", "lastName", "integer", "decimal", "enumProperty", "language", "state"); BeanVariableValueSourceFactory<TestBean> bvp = new BeanVariableValueSourceFactory<>("Test", TestBean.class); bvp.setProperties(properties); assertVariablesFromProperties(bvp, properties); } @Test public void test_correctValueTypesAreInferred() { Set<String> properties = Sets.newLinkedHashSet( ImmutableList.of("firstName", "lastName", "integer", "decimal", "enumProperty", "language", "state", "date")); List<String> types = ImmutableList .of("text", "text", "integer", "decimal", "text", "locale", "boolean", "datetime"); BeanVariableValueSourceFactory<TestBean> bvp = new BeanVariableValueSourceFactory<>("Test", TestBean.class); bvp.setProperties(properties); Iterator<String> i = types.iterator(); for(VariableValueSource source : bvp.createSources()) { assertThat(i.next()).isEqualTo(source.getValueType().getName()) .overridingErrorMessage("wrong type for property " + source.getVariable().getName()); } } @Test public void testEnumHasCategories() { Set<String> properties = Sets.newHashSet("enumProperty"); BeanVariableValueSourceFactory<TestBean> bvp = new BeanVariableValueSourceFactory<>("Test", TestBean.class); bvp.setProperties(properties); Set<VariableValueSource> sources = assertVariablesFromProperties(bvp, properties); Set<Category> categories = Iterables.get(sources, 0).getVariable().getCategories(); assertThat(categories).isNotNull(); assertThat(categories).isNotEmpty(); for(Category c : categories) { TestEnum e = TestEnum.valueOf(c.getName()); assertThat(e).isNotNull(); } } @Test public void testPropertyWithoutField() { Set<String> properties = Sets.newHashSet("composedProperty"); BeanVariableValueSourceFactory<TestBean> bvp = new BeanVariableValueSourceFactory<>("Test", TestBean.class); bvp.setProperties(properties); assertVariablesFromProperties(bvp, properties); } @Test public void testNestedProperties() { Set<String> properties = Sets.newHashSet("nestedBean.decimal", "nestedBean.data"); BeanVariableValueSourceFactory<TestBean> bvp = new BeanVariableValueSourceFactory<>("Test", TestBean.class); bvp.setProperties(properties); assertVariablesFromProperties(bvp, properties); } @Test public void testOverriddenVariableName() { Set<String> properties = Sets.newHashSet("nestedBean.decimal", "firstName"); Map<String, String> nameOverride = new ImmutableMap.Builder<String, String>() .put("nestedBean.decimal", "NestedDecimal").put("firstName", "FirstName").build(); BeanVariableValueSourceFactory<TestBean> bvp = new BeanVariableValueSourceFactory<>("Test", TestBean.class); bvp.setProperties(properties); bvp.setPropertyNameToVariableName(nameOverride); assertVariablesFromProperties(bvp, properties, nameOverride); } @Test public void testMapAttributes() { Set<String> properties = Sets.newHashSet("attributes[phoneNumber].data"); Map<String, Class<?>> mappedPropertyType = new ImmutableMap.Builder<String, Class<?>>() .put("attributes", NestedTestBean.class).build(); BeanVariableValueSourceFactory<TestBean> bvp = new BeanVariableValueSourceFactory<>("Test", TestBean.class); bvp.setProperties(properties); bvp.setMappedPropertyType(mappedPropertyType); assertVariablesFromProperties(bvp, properties); } @Test @SuppressWarnings({ "PMD.NcssMethodCount", "OverlyLongMethod" }) public void testMapAttributesWithDotsInKeyValues() { TestBean tb = new TestBean(); tb.setFirstName("TestBean"); NestedTestBean nb = new NestedTestBean(); nb.setDecimal(42.0); nb.setData(new byte[] { 0x01, 0x02 }); tb.setAttributes(ImmutableMap.of("phone.number", nb)); Set<String> properties = Sets.newHashSet("attributes[phone.number].data"); Map<String, Class<?>> mappedPropertyType = new ImmutableMap.Builder<String, Class<?>>() .put("attributes", NestedTestBean.class).build(); BeanVariableValueSourceFactory<TestBean> bvp = new BeanVariableValueSourceFactory<>("Test", TestBean.class); bvp.setProperties(properties); bvp.setMappedPropertyType(mappedPropertyType); Set<VariableValueSource> variableValueSources = assertVariablesFromProperties(bvp, properties); BeanValueSet bvs = EasyMock.createMock(BeanValueSet.class); // "If you use an argument matcher for one argument, you must use an argument matcher for all the arguments." EasyMock.expect( bvs.resolve((Class<?>) EasyMock.anyObject(), (ValueSet) EasyMock.anyObject(), (Variable) EasyMock.anyObject())) .andReturn(tb).anyTimes(); EasyMock.replay(bvs); for(VariableValueSource source : variableValueSources) { Value value = source.getValue(bvs); String name = source.getVariable().getName(); assertThat(value).isNotNull().overridingErrorMessage("Value cannot be null " + name); assertThat(value.getValueType()).isNotNull().overridingErrorMessage("ValueType cannot be null " + name); assertThat(value.getValue()).isNotNull().overridingErrorMessage("Value's value cannot be null " + name); } } @Test @SuppressWarnings({ "PMD.NcssMethodCount", "OverlyLongMethod" }) public void testValues() { TestBean tb = new TestBean(); tb.setFirstName("TestBean"); NestedTestBean nb = new NestedTestBean(); nb.setDecimal(42.0); nb.setData(new byte[] { 0x01, 0x02 }); tb.setNestedBean(nb); Set<String> properties = Sets.newHashSet("firstName", "nestedBean.decimal", "nestedBean.data"); BeanVariableValueSourceFactory<TestBean> bvp = new BeanVariableValueSourceFactory<>("Test", TestBean.class); bvp.setProperties(properties); Set<VariableValueSource> variableValueSources = assertVariablesFromProperties(bvp, properties); BeanValueSet bvs = EasyMock.createMock(BeanValueSet.class); // "If you use an argument matcher for one argument, you must use an argument matcher for all the arguments." EasyMock.expect( bvs.resolve((Class<?>) EasyMock.anyObject(), (ValueSet) EasyMock.anyObject(), (Variable) EasyMock.anyObject())) .andReturn(tb).anyTimes(); EasyMock.replay(bvs); for(VariableValueSource source : variableValueSources) { Value value = source.getValue(bvs); String name = source.getVariable().getName(); assertThat(value).isNotNull().overridingErrorMessage("Value cannot be null " + name); assertThat(value.getValueType()).isNotNull().overridingErrorMessage("ValueType cannot be null " + name); assertThat(value.getValue()).isNotNull().overridingErrorMessage("Value's value cannot be null " + name); } } @Test public void testNullValueInPropertyPath() { Set<String> properties = Sets.newHashSet("anotherNestedBean.data"); BeanVariableValueSourceFactory<TestBean> bvp = new BeanVariableValueSourceFactory<>("Test", TestBean.class); bvp.setProperties(properties); Set<VariableValueSource> variableValueSources = assertVariablesFromProperties(bvp, properties); BeanValueSet bvs = EasyMock.createMock(BeanValueSet.class); // "If you use an argument matcher for one argument, you must use an argument matcher for all the arguments." EasyMock.expect( bvs.resolve((Class<?>) EasyMock.anyObject(), (ValueSet) EasyMock.anyObject(), (Variable) EasyMock.anyObject())) .andReturn(new TestBean()).anyTimes(); EasyMock.replay(bvs); for(VariableValueSource source : variableValueSources) { Value value = source.getValue(bvs); String name = source.getVariable().getName(); assertThat(value).isNotNull().overridingErrorMessage("Value cannot be null " + name); assertThat(value.getValueType()).isNotNull().overridingErrorMessage("ValueType cannot be null " + name); assertThat(value.isNull()).isTrue().overridingErrorMessage("Value's value should be null " + name); } } protected Set<VariableValueSource> assertVariablesFromProperties(VariableValueSourceFactory bvp, Collection<String> properties) { return assertVariablesFromProperties(bvp, properties, null); } protected Set<VariableValueSource> assertVariablesFromProperties(VariableValueSourceFactory bvp, Collection<String> properties, Map<String, String> nameOverride) { Set<VariableValueSource> variables = bvp.createSources(); // There are no more and no less than what was specified assertThat(properties.size()).isEqualTo(variables.size()); Collection<String> nameSet = nameOverride != null ? nameOverride.values() : properties; for(VariableValueSource variableValueSource : variables) { String variableName = variableValueSource.getVariable().getName(); assertThat(nameSet).contains(variableName).overridingErrorMessage("Unexpected variable name " + variableName); } return variables; } @SuppressWarnings({ "UnusedDeclaration", "AssignmentToDateFieldFromParameter" }) public static class TestBean { private String firstName; private String lastName; private TestEnum enumProperty; private boolean state; private Date date; private Integer integer; private Double decimal; private NestedTestBean nestedBean; private NestedTestBean anotherNestedBean; private Locale language; private Map<String, NestedTestBean> attributes; public TestBean() { } public final String getFirstName() { return firstName; } public final void setFirstName(String firstName) { this.firstName = firstName; } public final String getLastName() { return lastName; } public final void setLastName(String lastName) { this.lastName = lastName; } // A property that has no underlying class field public final String getComposedProperty() { return firstName + lastName; } public final TestEnum getEnumProperty() { return enumProperty; } public final void setEnumProperty(TestEnum enumProperty) { this.enumProperty = enumProperty; } public final boolean isState() { return state; } public final void setState(boolean state) { this.state = state; } @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "EI_EXPOSE_REP") public final Date getDate() { return date; } @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "EI_EXPOSE_REP2") public final void setDate(Date date) { this.date = date; } public final NestedTestBean getNestedBean() { return nestedBean; } public final void setNestedBean(NestedTestBean nestedBean) { this.nestedBean = nestedBean; } public final Integer getInteger() { return integer; } public final void setInteger(Integer integer) { this.integer = integer; } public Double getDecimal() { return decimal; } public void setDecimal(Double decimal) { this.decimal = decimal; } public NestedTestBean getAnotherNestedBean() { return anotherNestedBean; } public void setAnotherNestedBean(NestedTestBean anotherNestedBean) { this.anotherNestedBean = anotherNestedBean; } public final Locale getLanguage() { return language; } public final void setLanguage(Locale language) { this.language = language; } public final Map<String, NestedTestBean> getAttributes() { return attributes; } public final void setAttributes(Map<String, NestedTestBean> attributes) { this.attributes = attributes; } } @SuppressWarnings("UnusedDeclaration") public static class NestedTestBean { private Double decimal; private byte[] data; public final Double getDecimal() { return decimal; } public final void setDecimal(Double decimal) { this.decimal = decimal; } @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "EI_EXPOSE_REP") public final byte[] getData() { return data; } @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "EI_EXPOSE_REP2") public final void setData(byte... data) { this.data = data; } } @SuppressWarnings("UnusedDeclaration") public enum TestEnum { VALUE1, VALUE2 } }