/* * ============LICENSE_START======================================================= * ONAP * ================================================================================ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * 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. * ============LICENSE_END========================================================= */ package org.onap.policy.common.gson.internal; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import com.google.gson.JsonParseException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; import java.util.stream.Collectors; import org.junit.Before; import org.junit.Test; import org.onap.policy.common.gson.annotation.GsonJsonAnyGetter; import org.onap.policy.common.gson.annotation.GsonJsonAnySetter; import org.onap.policy.common.gson.annotation.GsonJsonIgnore; import org.onap.policy.common.gson.annotation.GsonJsonProperty; public class ClassWalkerTest { private static final String SET_OVERRIDE = ".setOverride"; private static final String INVALID_FIELD_NAME = "invalidFieldName"; private MyWalker walker; /** * Set up. */ @Before public void setUp() { walker = new MyWalker(); } @Test public void testExamineClassOfQ_testExamineField_testExamineInField_testExamineOutField() { walker.walkClassHierarchy(DerivedFromBottom.class); assertEquals("[Intfc1, Intfc2, Intfc1, Intfc3, Bottom, DerivedFromBottom]", walker.classes.toString()); List inFields = walker.getInProps(Field.class).stream().map(Field::getName) .collect(Collectors.toList()); Collections.sort(inFields); assertEquals("[exposedField, overriddenValue, transField]", inFields.toString()); List outFields = walker.getInProps(Field.class).stream().map(Field::getName) .collect(Collectors.toList()); Collections.sort(outFields); assertEquals("[exposedField, overriddenValue, transField]", outFields.toString()); // should work with interfaces without throwing an NPE walker.walkClassHierarchy(Intfc1.class); } @Test public void testHasAnyGetter() { walker.walkClassHierarchy(Object.class); assertNull(walker.getAnyGetter()); assertNull(walker.getAnySetter()); walker.walkClassHierarchy(AnyGetterIgnored.class); assertNull(walker.getAnyGetter()); assertNull(walker.getAnySetter()); walker.walkClassHierarchy(AnyGetterOnly.class); assertNotNull(walker.getAnyGetter()); assertNull(walker.getAnySetter()); } @Test public void testHasAnySetter() { walker.walkClassHierarchy(Object.class); assertNull(walker.getAnySetter()); assertNull(walker.getAnyGetter()); walker.walkClassHierarchy(AnySetterIgnored.class); assertNull(walker.getAnySetter()); assertNull(walker.getAnyGetter()); walker.walkClassHierarchy(AnySetterOnly.class); assertNotNull(walker.getAnySetter()); assertNull(walker.getAnyGetter()); } @Test public void testExamineMethod() { walker.walkClassHierarchy(DerivedFromData.class); assertEquals("[Data, DerivedFromData]", walker.classes.toString()); // ensure all methods were examined Collections.sort(walker.methods); List lst = Arrays.asList("getId", "getValue", "getOnlyOut", "getStatic", "getText", "getTheMap", "getUnserialized", "getValue", "getWithParams", "setExtraParams", "setId", "setMap", "setMapValue", "setMissingParams", "setNonPublic", "setOnlyIn", "setText", "setUnserialized", "setValue", "setValue", "wrongGetPrefix", "wrongSetPrefix"); Collections.sort(lst); assertEquals(lst.toString(), walker.methods.toString()); assertNotNull(walker.getAnyGetter()); assertEquals("getTheMap", walker.getAnyGetter().getName()); List getters = walker.getOutProps(Method.class).stream().map(Method::getName) .collect(Collectors.toList()); Collections.sort(getters); assertEquals("[getId, getOnlyOut, getValue]", getters.toString()); assertNotNull(walker.getAnySetter()); assertEquals("setMapValue", walker.getAnySetter().getName()); List setters = walker.getInProps(Method.class).stream().map(Method::getName) .collect(Collectors.toList()); Collections.sort(setters); assertEquals("[setId, setOnlyIn, setValue]", setters.toString()); // getter with invalid parameter count assertThatThrownBy(() -> walker.walkClassHierarchy(AnyGetterMismatchParams.class)) .isInstanceOf(JsonParseException.class).hasMessage(ClassWalker.ANY_GETTER_MISMATCH_ERR + AnyGetterMismatchParams.class.getName() + ".getTheMap"); // setter with too few parameters assertThatThrownBy(() -> walker.walkClassHierarchy(AnySetterTooFewParams.class)) .isInstanceOf(JsonParseException.class).hasMessage(ClassWalker.ANY_SETTER_MISMATCH_ERR + AnySetterTooFewParams.class.getName() + SET_OVERRIDE); // setter with too many parameters assertThatThrownBy(() -> walker.walkClassHierarchy(AnySetterTooManyParams.class)) .isInstanceOf(JsonParseException.class).hasMessage(ClassWalker.ANY_SETTER_MISMATCH_ERR + AnySetterTooManyParams.class.getName() + SET_OVERRIDE); // setter with invalid parameter type assertThatThrownBy(() -> walker.walkClassHierarchy(AnySetterInvalidParam.class)) .isInstanceOf(JsonParseException.class).hasMessage(ClassWalker.ANY_SETTER_TYPE_ERR + AnySetterInvalidParam.class.getName() + SET_OVERRIDE); } @Test public void testExamineMethod_AnyGetter() { walker.walkClassHierarchy(AnyGetterOverride.class); assertNotNull(walker.getAnyGetter()); assertEquals("getOverride", walker.getAnyGetter().getName()); } @Test public void testExamineMethod_AnySetter() { walker.walkClassHierarchy(AnySetterOverride.class); assertNotNull(walker.getAnySetter()); assertEquals("setOverride", walker.getAnySetter().getName()); } @Test public void testGetInNotIgnored_testGetOutNotIgnored() { walker.walkClassHierarchy(DerivedFromData.class); assertEquals("[id, onlyIn, text, value]", new TreeSet<>(walker.getInNotIgnored()).toString()); assertEquals("[id, onlyOut, text, value]", new TreeSet<>(walker.getOutNotIgnored()).toString()); } /** * Walker subclass that records items that are examined. */ private static class MyWalker extends ClassWalker { private List classes = new ArrayList<>(); private List methods = new ArrayList<>(); @Override protected void examine(Class clazz) { classes.add(clazz.getSimpleName()); super.examine(clazz); } @Override protected void examine(Method method) { if (Adapter.isManaged(method)) { methods.add(method.getName()); } super.examine(method); } @Override protected String detmPropName(Field field) { if (INVALID_FIELD_NAME.equals(field.getName())) { return null; } return super.detmPropName(field); } } protected static interface Intfc1 { int id = 1000; } protected static interface Intfc2 { String text = "intfc2-text"; } private static interface Intfc3 { } protected static class Bottom implements Intfc1, Intfc3 { private int id; public String value; // this is not actually invalid, but will be treated as if it were public String invalidFieldName; @GsonJsonProperty("exposed") private String exposedField; @GsonJsonIgnore public int ignored; public transient int ignoredTransField; @GsonJsonProperty("trans") public transient int transField; @GsonJsonIgnore public int getId() { return id; } @GsonJsonIgnore public void setId(int id) { this.id = id; } } protected static class DerivedFromBottom extends Bottom implements Intfc1, Intfc2 { private String text; protected String anotherValue; @GsonJsonProperty("value") public String overriddenValue; @GsonJsonIgnore public String getText() { return text; } @GsonJsonIgnore public void setText(String text) { this.text = text; } } protected static class Data { private int id; private String text; public int getId() { return id; } public void setId(int id) { this.id = id; } // not public, but property provided @GsonJsonProperty("text") protected String getText() { return text; } // this will be ignored, because there's already a field by this name public void setText(String text) { this.text = text; } // should only show up in the output list public int getOnlyOut() { return 1100; } // will be overridden by subclass @GsonJsonProperty("super-value-getter") public String getValue() { return null; } // will be overridden by subclass @GsonJsonProperty("super-value-setter") public void setValue(String value) { // do nothing } } protected static class DerivedFromData extends Data { // not serialized private String unserialized; // overrides private field and public method from Data public String text; private Map map; private String value; @Override public String getValue() { return value; } @Override public void setValue(String value) { this.value = value; } @GsonJsonAnyGetter public Map getTheMap() { return map; } @GsonJsonIgnore public void setMap(Map map) { this.map = map; } @GsonJsonAnySetter public void setMapValue(String key, String value) { if (map == null) { map = new TreeMap<>(); } map.put(key, value); } @GsonJsonIgnore public String getUnserialized() { return unserialized; } @GsonJsonIgnore public void setUnserialized(String unserialized) { this.unserialized = unserialized; } // should only show up in the input list public void setOnlyIn(int value) { // do nothing } // has a param - shouldn't be serialized public int getWithParams(String text) { return 1000; } // too few params - shouldn't be serialized public void setMissingParams() { // do nothing } // too many params - shouldn't be serialized public void setExtraParams(String text, String moreText) { // do nothing } // not public - shouldn't be serialized protected void setNonPublic(String text) { // do nothing } // doesn't start with "get" public String wrongGetPrefix() { return null; } // doesn't start with "set" public void wrongSetPrefix(String text) { // do nothing } // static public static String getStatic() { return null; } } /** * The "get" method has an incorrect argument count. */ private static class AnyGetterMismatchParams { @GsonJsonAnyGetter public Map getTheMap(String arg) { return new TreeMap<>(); } } /** * Has {@link GsonJsonAnyGetter} method. */ private static class AnyGetterOnly { @GsonJsonAnyGetter private Map getOverride() { return null; } } /** * Has {@link GsonJsonAnyGetter} method, but it's ignored. */ private static class AnyGetterIgnored { @GsonJsonAnyGetter @GsonJsonIgnore private Map getOverride() { return null; } } /** * Has {@link GsonJsonAnySetter} method. */ private static class AnySetterOnly { @GsonJsonAnySetter private void setOverride(String key, int value) { // do nothing } } /** * Has {@link GsonJsonAnySetter} method, but it's ignored. */ private static class AnySetterIgnored { @GsonJsonAnySetter @GsonJsonIgnore private void setOverride(String key, int value) { // do nothing } } /** * Has {@link GsonJsonAnyGetter} method that overrides the super class' method. */ private static class AnyGetterOverride extends DerivedFromData { private Map overMap; @GsonJsonAnyGetter private Map getOverride() { return overMap; } } /** * Has {@link GsonJsonAnySetter} method that overrides the super class' method. */ private static class AnySetterOverride extends DerivedFromData { private Map overMap; @GsonJsonAnySetter private void setOverride(String key, int value) { if (overMap == null) { overMap = new TreeMap<>(); } overMap.put(key, value); } } /** * Has {@link GsonJsonAnySetter} method with too few parameters. */ private static class AnySetterTooFewParams extends DerivedFromData { @GsonJsonAnySetter public void setOverride(String key) { // do nothing } } /** * Has {@link GsonJsonAnySetter} method with too few parameters. */ private static class AnySetterTooManyParams extends DerivedFromData { @GsonJsonAnySetter public void setOverride(String key, int value, String anotherValue) { // do nothing } } /** * Has {@link GsonJsonAnySetter} method whose first argument type is incorrect. */ private static class AnySetterInvalidParam extends DerivedFromData { @GsonJsonAnySetter public void setOverride(Integer key, String value) { // do nothing } } }