diff options
Diffstat (limited to 'utils-test/src/main')
3 files changed, 412 insertions, 0 deletions
diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/gson/GsonSerializer.java b/utils-test/src/main/java/org/onap/policy/common/utils/gson/GsonSerializer.java new file mode 100644 index 00000000..db9c1c7e --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/gson/GsonSerializer.java @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * policy-management + * ================================================================================ + * Copyright (C) 2017-2018 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.utils.gson; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import java.io.IOException; + +/** + * Gson serializer, providing stub implementation of "read". + * + * @param <T> type of object that this serializes + */ +public abstract class GsonSerializer<T> extends TypeAdapter<T> { + @Override + public T read(JsonReader in) throws IOException { + throw new UnsupportedOperationException("read from pseudo TypeAdapter"); + } +} diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/gson/GsonTestUtils.java b/utils-test/src/main/java/org/onap/policy/common/utils/gson/GsonTestUtils.java new file mode 100644 index 00000000..bfdca97c --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/gson/GsonTestUtils.java @@ -0,0 +1,314 @@ +/*- + * ============LICENSE_START======================================================= + * policy-management + * ================================================================================ + * Copyright (C) 2017-2018 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.utils.gson; + +import static org.junit.Assert.assertEquals; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utilities used to test encoding and decoding of Policy objects. + */ +public class GsonTestUtils { + + private static final Logger logger = LoggerFactory.getLogger(GsonTestUtils.class); + + /** + * Matches script items, of the form ${xxx}, within text. + */ + private static final Pattern SCRIPT_PAT = Pattern.compile("\\$\\{([^}]+)\\}"); + + /** + * Engine used to interpolate strings before they're compared. + */ + private static volatile ScriptEngine engine = null; + + /** + * Used to encode and decode an object via gson. + */ + private Gson gson; + + /** + * Constructs the object. + */ + public GsonTestUtils() { + GsonTestUtils other = new GsonTestUtilsBuilder().build(); + + gson = other.gson; + } + + /** + * Constructs the object. + * + * @param gson used to encode via gson + */ + protected GsonTestUtils(Gson gson) { + this.gson = gson; + } + + public Gson getGson() { + return gson; + } + + /** + * Serializes and then deserializes an object using gson. + * + * @param object the object to be serialized + * @param clazz the class of object to deserialize + * @return the deserialized object + */ + public <T> T gsonRoundTrip(T object, Class<T> clazz) { + String sgson = gsonEncode(object); + return gson.fromJson(sgson, clazz); + } + + /** + * Encodes an object using gson and then compares it to the expected value, after + * sorting the elements. The class name is used to find the json file, whose contents + * is interpolated (i.e., script elements, of the form ${obj.xxx}, are expanded). + * + * @param object the object to be encoded + * @param expected the expected value + * @throws Exception if the file cannot be read + */ + public void compareGson(Object object, Class<?> expected) { + compareGson(object, new File(expected.getSimpleName() + ".json")); + } + + /** + * Encodes an object using gson and then compares it to the expected value, after + * sorting the elements. The content of the file is interpolated (i.e., script + * elements, of the form ${obj.xxx}, are expanded). + * + * @param object the object to be encoded + * @param expected the expected value + * @throws Exception if the file cannot be read + */ + public void compareGson(Object object, File expected) { + // file is not required to have a full path - find it via getResource() + URL url = object.getClass().getResource(expected.getName()); + if (url == null) { + throw new JsonParseException(new FileNotFoundException(expected.getName())); + } + + String expectedText; + try { + expectedText = readFile(new File(url.getFile())); + + } catch (IOException e) { + throw new JsonParseException("error reading: " + expected, e); + } + + compareGson(object, expectedText); + } + + /** + * Encodes an object using gson and then compares it to the expected value, after + * sorting the elements. The expected value is interpolated (i.e., script elements, of + * the form ${obj.xxx}, are expanded). + * + * @param object the object to be encoded + * @param expected the expected value + */ + public void compareGson(Object object, String expected) { + String result = applyScripts(expected, object); + compareGson(object, gson.fromJson(result, JsonElement.class)); + } + + /** + * Encodes an object using gson and then compares it to the expected value, after + * sorting the elements. + * + * @param object the object to be encoded + * @param expected the expected value + */ + public void compareGson(Object object, JsonElement expected) { + String sgson = gsonEncode(object); + + JsonElement gsonjo = reorder(gson.fromJson(sgson, JsonElement.class)); + JsonElement expjo = reorder(expected); + + assertEquals(expjo.toString(), gsonjo.toString()); + } + + /** + * Reads the content of a file. + * @param file file to read + * @return the content of the file + * @throws IOException if an error occurs + */ + protected String readFile(File file) throws IOException { + return new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + } + + + /** + * Interpolates script elements, of the form ${obj.xxx}, within some text. The script + * is evaluated via javascript, where "obj" references the object used by each script + * element. + * + * @param object object to be used by the script + * @param text text to be evaluated + * @return the text, after interpolating the script elements + */ + public String applyScripts(String text, Object object) { + Matcher mat = SCRIPT_PAT.matcher(text); + if (!mat.find()) { + // contains no script elements - just return it as is + return text; + } + + // create the engine and bind the object to the variable, "obj" + if (engine == null) { + // race condition here, but it's ok to overwrite with a new engine + engine = new ScriptEngineManager().getEngineByName("javascript"); + } + + Bindings bindings = engine.createBindings(); + bindings.put("obj", object); + + // work our way through the text, interpolating script elements as we go + StringBuilder bldr = new StringBuilder(); + int ilast = 0; + mat.reset(); + while (mat.find(ilast)) { + // append segment that appears between last match and this + int inext = mat.start(); + bldr.append(text.substring(ilast, inext)); + + // next match begins after the current match + ilast = mat.end(); + + // interpolate the script + String script = mat.group(1); + try { + Object result = engine.eval(script, bindings); + bldr.append(result == null ? "null" : result.toString()); + + } catch (ScriptException e) { + throw new RuntimeException("cannot expand element: " + mat.group(), e); + } + } + + // append final segment + bldr.append(text.substring(ilast)); + + return bldr.toString(); + } + + /** + * Encodes an object using gson. + * + * @param object the object to be encoded + * @return the encoded object + */ + public String gsonEncode(Object object) { + String sgson = gson.toJson(object); + logger.debug("gson=" + sgson); + return sgson; + } + + /** + * Recursively re-orders a json object, arranging the keys alphabetically and removing + * null items. + * + * @param jsonObj object from which nulls are to be removed + * @return a new object, without the null items + */ + public JsonObject reorder(JsonObject jsonObj) { + JsonObject newjo = new JsonObject(); + + // sort the keys before copying to the new object + List<Entry<String, JsonElement>> sortedSet = new ArrayList<>(jsonObj.entrySet()); + Collections.sort(sortedSet, (left, right) -> left.getKey().compareTo(right.getKey())); + + for (Entry<String, JsonElement> ent : sortedSet) { + JsonElement val = ent.getValue(); + if (val.isJsonNull()) { + continue; + } + + newjo.add(ent.getKey(), reorder(val)); + } + + return newjo; + } + + /** + * Recursively re-orders a json array, arranging the keys alphabetically and removing + * null items. + * + * @param jsonArray array from which nulls are to be removed + * @return a new array, with null items removed from all elements + */ + public JsonArray reorder(JsonArray jsonArray) { + JsonArray newarr = new JsonArray(); + for (JsonElement ent : jsonArray) { + newarr.add(reorder(ent)); + } + + return newarr; + } + + /** + * Recursively re-orders a json element, arranging the keys alphabetically and + * removing null items. + * + * @param jsonEl element from which nulls are to be removed + * @return a new element, with null items removed + */ + public JsonElement reorder(JsonElement jsonEl) { + if (jsonEl == null) { + return null; + + } else if (jsonEl.isJsonObject()) { + return reorder(jsonEl.getAsJsonObject()); + + } else if (jsonEl.isJsonArray()) { + return reorder(jsonEl.getAsJsonArray()); + + } else { + return jsonEl; + } + } +} diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/gson/GsonTestUtilsBuilder.java b/utils-test/src/main/java/org/onap/policy/common/utils/gson/GsonTestUtilsBuilder.java new file mode 100644 index 00000000..1f697fdb --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/gson/GsonTestUtilsBuilder.java @@ -0,0 +1,61 @@ +/*- + * ============LICENSE_START======================================================= + * policy-management + * ================================================================================ + * Copyright (C) 2017-2018 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.utils.gson; + +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapterFactory; +import org.onap.policy.common.gson.JacksonHandler; + +/** + * Used to builder a utility class. + */ +public class GsonTestUtilsBuilder { + private final GsonBuilder gsonBldr; + + /** + * Constructs the object. + */ + public GsonTestUtilsBuilder() { + gsonBldr = new GsonBuilder(); + + // register jackson behaviors with the builder + new JacksonHandler(gsonBldr); + } + + /** + * Builds the utility. + * + * @return a new utility + */ + public GsonTestUtils build() { + return new GsonTestUtils(gsonBldr.create()); + } + + /** + * Adds gson support for serializing a mock of a class. + * + * @param clazz mocked class to be supported + * @param sgson gson serializer + */ + protected <T> void addMock(Class<T> clazz, TypeAdapterFactory sgson) { + gsonBldr.registerTypeAdapterFactory(sgson); + } +} |