From 7f56250b1c9d4de1ee8cf85be0aff275f29dd801 Mon Sep 17 00:00:00 2001 From: Zachary Bischoff <116595361+bischoffz@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:31:57 -0500 Subject: [PATCH] Taskit 3.0.0 (#5) * update version 3.0.0-SNAPSHOT * Allow multiple translation engines; Taskit 3.0.0 (#4) * initial design impl fix and add tests * Update README.md * Update README.md * move translators to translation engine * add parentchildmap to engine add missing tests jacoco back to 100% * fix translation engine builder inheritance issue * fix for tests * method scope reduction where possible * update engine to use paths instead of readers * update engine to use paths instead of writers * update to 3.0.0 proper --- README.md | 15 +- .../ms/taskit/core/CoreTranslationError.java | 42 +- .../ms/taskit/core/TranslationController.java | 445 +++++------------- .../ms/taskit/core/TranslationEngine.java | 307 +++++++++++- .../ms/taskit/core/TranslationEngineType.java | 7 + .../aspr/ms/taskit/core/TranslationSpec.java | 28 +- .../hhs/aspr/ms/taskit/core/Translator.java | 6 +- .../ms/taskit/core/TranslatorContext.java | 36 +- .../testsupport/TestTranslationEngine.java | 69 ++- .../taskit/core/AT_TranslationController.java | 401 ++++++++-------- .../ms/taskit/core/AT_TranslationEngine.java | 262 +++++------ .../ms/taskit/core/AT_TranslatorContext.java | 58 +-- .../core/TranslationEngineTestHelper.java | 176 +++++++ .../testsupport/AT_TestTranslationEngine.java | 89 ++-- .../AT_TestComplexObjectTranslationSpec.java | 8 +- .../AT_TestObjectTranslationSpec.java | 8 +- pom.xml | 2 +- .../protobuf/PrimitiveTranslationSpecs.java | 10 +- .../ProtobufCoreTranslationError.java | 6 +- .../protobuf/ProtobufTranslationEngine.java | 81 ++-- .../ProtobufTranslationEngineTestHelper.java | 176 +++++++ .../AT_ProtobufTranslationEngine.java | 97 ++-- ...tProtobufComplexObjectTranslationSpec.java | 2 - .../AT_TestProtobufEnumTranslationSpec.java | 2 - .../AT_TestProtobufObjectTranslationSpec.java | 2 - .../AT_AnyTranslationSpec.java | 3 - .../AT_EnumTranslationSpec.java | 2 - 27 files changed, 1344 insertions(+), 996 deletions(-) create mode 100644 core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationEngineType.java create mode 100644 core/src/test/java/gov/hhs/aspr/ms/taskit/core/TranslationEngineTestHelper.java create mode 100644 protobuf/src/test/java/gov/hhs/aspr/ms/taskit/core/ProtobufTranslationEngineTestHelper.java diff --git a/README.md b/README.md index 0758233..0d897ab 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ [![GPL LICENSE][license-shield]][license-url] +[![GitHub tag (with filter)][tag-shield]][tag-url] [![GitHub contributors][contributors-shield]][contributors-url] +[![GitHub Workflow Status (with event)][dev-build-shield]][dev-build-url] +[![GitHub Workflow Status (with event)][build-shield]][build-url] # Translation And Serialization Toolkit A toolkit made to help with converting between input files and Java Objects. This will herein be reffered to as Taskit. @@ -46,8 +49,7 @@ See [TestObject](protobuf/src/main/proto/gov/hhs/aspr/ms/taskit/protobuf/testobj - Modeling Util located [here](https://github.com/HHS/ASPR-ms-util) ## Building -To build, first navigate into the ```core``` directory and run the command ```mvn clean install``` -Then go into the ```protobuf``` directory and run the command ```mvn clean install``` +To build, navigate into the root directory and run the command ```mvn clean install``` ## Documentation Documentation has yet to be created. In the interim, the code is mostly commented and the javadocs do provide good detail with regards to method and class expectations. @@ -59,7 +61,12 @@ Distributed under the GPLv3 License. See [LICENSE](LICENSE) for more information [contributors-shield]: https://img.shields.io/github/contributors/HHS/ASPR-ms-taskit [contributors-url]: https://github.com/HHS/ASPR-ms-taskit/graphs/contributors - - +[tag-shield]: https://img.shields.io/github/v/tag/HHS/ASPR-ms-taskit +[tag-url]: https://github.com/HHS/ASPR-ms-taskit/releases/latest [license-shield]: https://img.shields.io/github/license/HHS/ASPR-ms-taskit [license-url]: LICENSE +[dev-build-shield]: https://img.shields.io/github/actions/workflow/status/HHS/ASPR-ms-taskit/dev-pre-mavencentral.yml?label=dev-build +[dev-build-url]: https://github.com/HHS/ASPR-ms-taskit/actions/workflows/dev-pre-mavencentral.yml +[build-shield]: https://img.shields.io/github/actions/workflow/status/HHS/ASPR-ms-taskit/create_release_on_tag.yml?label=release-build +[build-url]: https://github.com/HHS/ASPR-ms-taskit/actions/workflows/create_release_on_tag.yml + diff --git a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/CoreTranslationError.java b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/CoreTranslationError.java index 5526df4..feb25e7 100644 --- a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/CoreTranslationError.java +++ b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/CoreTranslationError.java @@ -4,39 +4,43 @@ public enum CoreTranslationError implements ContractError { + CIRCULAR_TRANSLATOR_DEPENDENCIES("Circular translator dependencies: "), + DUPLICATE_CLASSREF("Duplicate ClassRef"), + DUPLICATE_CLASSREF_SCENARIO_PAIR("Duplicate ClassRef and Scenario Pair"), + DUPLICATE_DEPENDENCY("Duplicate Dependency"), + DUPLICATE_INPUT_PATH("Duplicate Input Path"), + DUPLICATE_OUTPUT_PATH("Duplicate Output Path"), + DUPLICATE_TRANSLATOR("Duplicate Translator"), + DUPLICATE_TRANSLATION_SPEC("Duplicate TranslationSpec"), + INVALID_TRANSLATION_ENGINE_CLASS_REF( + "The given Translation Engine classRef does not match the class of the actual Translation Engine"), + INVALID_TRANSLATION_ENGINE_BUILDER_CLASS_REF( + "The given Translation Engine Builder classRef does not match the class of the actual Translation Engine Builder"), + INVALID_OUTPUT_CLASSREF("The given class does not have a output file path associated with it."), + INVALID_OUTPUT_PATH( + "The given output file path does not exist. While the file will be created on write, the directory will not."), + INVALID_INPUT_PATH("The given input file path does not exist"), + MISSING_TRANSLATOR("Missing Translator: "), + NO_TRANSLATION_ENGINES("There are no translation engines added to this controller."), NULL_TRANSLATOR_ID("Null TranslatorId"), NULL_TRANSLATOR("Null Translator"), NULL_TRANSLATION_ENGINE_BUILDER("Null Translation Engine Builder"), NULL_TRANSLATION_ENGINE("Null Translation Engine"), NULL_OBJECT_FOR_TRANSLATION("The object to be translated was null"), - INVALID_TRANSLATION_ENGINE( - "The given Translation Engine classRef does not match the class of the actual Translation Engine"), - INVALID_TRANSLATION_ENGINE_BUILDER( - "The given Translation Engine Builder classRef does not match the class of the actual Translation Engine Builder"), - DUPLICATE_TRANSLATOR("Duplicate Translator"), - MISSING_TRANSLATOR("Missing Translator: "), - CIRCULAR_TRANSLATOR_DEPENDENCIES("Circular translator dependencies: "), NULL_INIT_CONSUMER("Null Initilizer Consumer"), NULL_DEPENDENCY("Null dependency"), - DUPLICATE_DEPENDENCY("Duplicate Dependency"), NULL_PATH("Null Path"), - DUPLICATE_INPUT_PATH("Duplicate Input Path"), - INVALID_INPUT_PATH("The given input file path does not exist"), NULL_CLASS_REF("Null Class Ref"), - INVALID_OUTPUT_CLASSREF("The given class does not have a output file path associated with it."), - DUPLICATE_OUTPUT_PATH("Duplicate Output Path"), - INVALID_OUTPUT_PATH( - "The given output file path does not exist. While the file will be created on write, the directory will not."), - DUPLICATE_CLASSREF_SCENARIO_PAIR("Duplicate ClassRef and Scenario Pair"), - DUPLICATE_CLASSREF("Duplicate ClassRef"), - UNKNOWN_CLASSREF("No object has been read in with the specified classRef"), NULL_TRANSLATION_SPEC("Null TranslationSpec"), NULL_TRANSLATION_SPEC_APP_CLASS("Null TranslationSpec App Class"), NULL_TRANSLATION_SPEC_INPUT_CLASS("Null TranslationSpec Input Class"), - DUPLICATE_TRANSLATION_SPEC("Duplicate TranslationSpec"), UNKNOWN_TRANSLATION_SPEC("No translation spec was provided for the given class"), UNITIALIZED_TRANSLATION_SPEC("TranslationSpec not initialized"), - UNKNOWN_OBJECT("Object is not Translatable by this TranslationSpec"); + UNINITIALIZED_TRANSLATORS( + "Translators were added to the builder but were not initialized. Make sure to call super.initTranslators() during your custom engine build method"), + UNKNOWN_OBJECT("Object is not Translatable by this TranslationSpec"), + UNKNWON_TRANSLATION_ENGINE_TYPE("Translation Engine Type was not set"), + UNKNOWN_CLASSREF("No object has been read in with the specified classRef"); private final String description; diff --git a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationController.java b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationController.java index 098a5cf..927fa6f 100644 --- a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationController.java +++ b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationController.java @@ -1,60 +1,53 @@ package gov.hhs.aspr.ms.taskit.core; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; import java.io.Reader; -import java.io.Writer; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import org.apache.commons.math3.util.Pair; import util.errors.ContractException; -import util.graph.Graph; -import util.graph.GraphDepthEvaluator; -import util.graph.Graphs; -import util.graph.MutableGraph; /** * The TranslatorController serves as the master of cerimonies for translating * between two types of objects. Additionally, it has the ability to distribute * Input/Output files for reading and writing. */ -public class TranslationController { +public final class TranslationController { protected final Data data; - protected TranslationEngine translationEngine; + protected final Map translationEngines = new LinkedHashMap<>(); + protected final Map, TranslationEngineType> translationEngineClassToTypeMap = new LinkedHashMap<>(); protected final List objects = Collections.synchronizedList(new ArrayList<>()); - protected TranslatorId focalTranslatorId = null; - protected TranslationController(Data data) { + TranslationController(Data data) { this.data = data; } - protected static class Data { - protected TranslationEngine.Builder translationEngineBuilder; + final static class Data { + protected Map, TranslationEngine> translationEngines = new LinkedHashMap<>(); protected final List translators = new ArrayList<>(); protected final Map> inputFilePathMap = new LinkedHashMap<>(); + protected final Map inputFilePathEngine = new LinkedHashMap<>(); protected final Map, Integer>, Path> outputFilePathMap = new LinkedHashMap<>(); + protected final Map outputFilePathEngine = new LinkedHashMap<>(); protected final Map, Class> parentChildClassRelationshipMap = new LinkedHashMap<>(); - protected Data() { + Data() { } } - public static class Builder { - protected Data data; + public final static class Builder { + Data data; - protected Builder(Data data) { + Builder(Data data) { this.data = data; } @@ -80,15 +73,19 @@ private void validatePathNotDuplicate(Path filePath, boolean in, boolean out) { } } - private void validateTranslatorNotNull(Translator translator) { - if (translator == null) { - throw new ContractException(CoreTranslationError.NULL_TRANSLATOR); + private void validateTranslationEngineNotNull(TranslationEngine translationEngine) { + if (translationEngine == null) { + throw new ContractException(CoreTranslationError.NULL_TRANSLATION_ENGINE); } } - private void validateTranslationEngineBuilderNotNull(TranslationEngine.Builder translationEngineBuilder) { - if (translationEngineBuilder == null) { - throw new ContractException(CoreTranslationError.NULL_TRANSLATION_ENGINE_BUILDER); + private void validateTranslationEnginesNotNull() { + if (this.data.translationEngines.isEmpty()) { + throw new ContractException(CoreTranslationError.NULL_TRANSLATION_ENGINE, + "No TranslationEngine Builders were added"); + } + for (TranslationEngine engine : this.data.translationEngines.values()) { + validateTranslationEngineNotNull(engine); } } @@ -98,20 +95,22 @@ private void validateTranslationEngineBuilderNotNull(TranslationEngine.Builder t * * @throws ContractException *
    - *
  • {@linkplain CoreTranslationError#NULL_TRANSLATION_ENGINE_BUILDER} + *
  • {@linkplain CoreTranslationError#NULL_TRANSLATION_ENGINE} * if translationEngineBuilder has not been set
  • *
*/ public TranslationController build() { - validateTranslationEngineBuilderNotNull(this.data.translationEngineBuilder); + validateTranslationEnginesNotNull(); TranslationController translatorController = new TranslationController(this.data); - translatorController.initTranslators(); + translatorController.initTranslationEngines(); + translatorController.validateTranslationEngines(); + return translatorController; } - protected TranslationController buildWithoutInitAndChecks() { + TranslationController buildWithoutInitAndChecks() { return new TranslationController(this.data); } @@ -131,7 +130,7 @@ protected TranslationController buildWithoutInitAndChecks() { * if filePath does not exist on the system * */ - public Builder addInputFilePath(Path filePath, Class classRef) { + public Builder addInputFilePath(Path filePath, Class classRef, TranslationEngineType translationEngineType) { validateFilePathNotNull(filePath); validateClassRefNotNull(classRef); validatePathNotDuplicate(filePath, true, false); @@ -141,6 +140,7 @@ public Builder addInputFilePath(Path filePath, Class classRef) { } this.data.inputFilePathMap.put(filePath, classRef); + this.data.inputFilePathEngine.put(filePath, translationEngineType); return this; } @@ -163,8 +163,9 @@ public Builder addInputFilePath(Path filePath, Class classRef) { * if filePath does not exist on the system * */ - public Builder addOutputFilePath(Path filePath, Class classRef) { - return this.addOutputFilePath(filePath, classRef, 0); + public Builder addOutputFilePath(Path filePath, Class classRef, + TranslationEngineType translationEngineType) { + return this.addOutputFilePath(filePath, classRef, 0, translationEngineType); } /** @@ -186,7 +187,8 @@ public Builder addOutputFilePath(Path filePath, Class classRef) { * if filePath does not exist on the system * */ - public Builder addOutputFilePath(Path filePath, Class classRef, Integer scenarioId) { + public Builder addOutputFilePath(Path filePath, Class classRef, Integer scenarioId, + TranslationEngineType translationEngineType) { validateFilePathNotNull(filePath); validateClassRefNotNull(classRef); validatePathNotDuplicate(filePath, false, true); @@ -202,6 +204,7 @@ public Builder addOutputFilePath(Path filePath, Class classRef, Integer scena } this.data.outputFilePathMap.put(key, filePath); + this.data.outputFilePathEngine.put(filePath, translationEngineType); return this; } @@ -237,126 +240,45 @@ public Builder addParentChildClassRelationship(Class classRe } /** - * Add a {@link Translator} - * - * @throws ContractException - *
    - *
  • {@linkplain CoreTranslationError#NULL_TRANSLATOR} - * if translator is null
  • - *
  • {@linkplain CoreTranslationError#DUPLICATE_TRANSLATOR} - * if translator has alaready been added
  • - *
- */ - public Builder addTranslator(Translator translator) { - validateTranslatorNotNull(translator); - - if (this.data.translators.contains(translator)) { - throw new ContractException(CoreTranslationError.DUPLICATE_TRANSLATOR); - } - - this.data.translators.add(translator); - return this; - } - - /** - * Sets the {@link TranslationEngine.Builder} + * Adds a {@link TranslationEngine.Builder} * * @throws ContractException *
    - *
  • {@linkplain CoreTranslationError#NULL_TRANSLATION_ENGINE_BUILDER} + *
  • {@linkplain CoreTranslationError#NULL_TRANSLATION_ENGINE} * if translationEngineBuilder is null
  • *
*/ - public Builder setTranslationEngineBuilder(TranslationEngine.Builder translationEngineBuilder) { - validateTranslationEngineBuilderNotNull(translationEngineBuilder); + public Builder addTranslationEngine(TranslationEngine translationEngine) { + validateTranslationEngineNotNull(translationEngine); - this.data.translationEngineBuilder = translationEngineBuilder; - return this; - } - } + this.data.translationEngines.put(translationEngine.getClass(), translationEngine); - /** - * Returns a new instance of Builder - */ - public static Builder builder() { - return new Builder(new Data()); - } + Map, Class> childToParentClassMap = translationEngine.getChildParentClassMap(); - private void validateClassRefNotNull(Class classRef) { - if (classRef == null) { - throw new ContractException(CoreTranslationError.NULL_CLASS_REF); - } - } + for (Class childClassRef : childToParentClassMap.keySet()) { + // Need to duplicate code here because the map doesn't provide the type safety + // that is required by the addParentChildClassRelationship method + Class parentClassRef = childToParentClassMap.get(childClassRef); - /** - * Returns the translationEngineBuilder if and only if it is set, has not had it - * build method called, has not had it's init method called and is of the same - * type as the given classRef - * - * @param the class type of the TranslationEngine.Builder - * @throws ContractException - *
    - *
  • {@linkplain CoreTranslationError#INVALID_TRANSLATION_ENGINE_BUILDER} - * if the given classRef does not match the class or - * the translationEngineBuilder is null
  • - *
- */ - protected T getTranslationEngineBuilder(Class classRef) { - if (this.translationEngine == null) { - if (this.data.translationEngineBuilder.getClass() == classRef) { - return classRef.cast(this.data.translationEngineBuilder); - } - - throw new ContractException(CoreTranslationError.INVALID_TRANSLATION_ENGINE_BUILDER, - "The TranslationEngine is of type: " + this.data.translationEngineBuilder.getClass().getName() - + " and the given classRef was: " + classRef.getName()); - } - // This should never happen, therefore it is an actual runtime exception and not - // a contract exception - throw new RuntimeException("Trying to get TranslatorCoreBuilder after it was built and/or initialized"); - - } + // Note: no 'class is not null' validation here because it was validated prior + // to being put into the engine + if (this.data.parentChildClassRelationshipMap.containsKey(childClassRef)) { + throw new ContractException(CoreTranslationError.DUPLICATE_CLASSREF); + } - /** - * Returns the initialized TranslationEngine - * - * @throws RuntimeException if the Translation Engine has not been initialized - * @throws ContractException - *
    - *
  • {@linkplain CoreTranslationError#NULL_TRANSLATION_ENGINE} - * if translationEngine is null
  • - *
  • {@linkplain CoreTranslationError#INVALID_TRANSLATION_ENGINE} - * if the classRef does not match the class of the - * translationEngine
  • - *
- */ - public T getTranslationEngine(Class classRef) { - validateTranslationEngine(); + this.data.parentChildClassRelationshipMap.put(childClassRef, parentClassRef); + } - if (this.translationEngine.getClass() == classRef) { - return classRef.cast(this.translationEngine); + return this; } - throw new ContractException(CoreTranslationError.INVALID_TRANSLATION_ENGINE); } /** - * Adds the given child parent class relationship Only callable through a - * {@link TranslatorContext} via the {@link Translator#getInitializer()} - * consumer - * - * @param the childClass - * @param the parentClass + * Returns a new instance of Builder */ - protected void addParentChildClassRelationship(Class classRef, Class parentClassRef) { - validateClassRefNotNull(classRef); - validateClassRefNotNull(parentClassRef); - - if (this.data.parentChildClassRelationshipMap.containsKey(classRef)) { - throw new ContractException(CoreTranslationError.DUPLICATE_CLASSREF); - } - - this.data.parentChildClassRelationshipMap.put(classRef, parentClassRef); + public static Builder builder() { + return new Builder(new Data()); } /** @@ -365,10 +287,14 @@ protected void addParentChildClassRelationship(Class classRe * * @param the classType associated with the reader */ - private void readInput(Reader reader, Class inputClassRef) { - Object appObject = this.translationEngine.readInput(reader, inputClassRef); - - this.objects.add(appObject); + void readInput(Path path, Class inputClassRef, TranslationEngine translationEngine) { + Object appObject; + try { + appObject = translationEngine.readInput(path, inputClassRef); + this.objects.add(appObject); + } catch (IOException e) { + throw new RuntimeException(e); + } } /** @@ -378,16 +304,27 @@ private void readInput(Reader reader, Class inputClassRef) { * @param the class of the object to write to the outputFile * @param the optional parent class of the object to write to the outputFile */ - private void writeOutput(Writer writer, M object, Optional> superClass) { - this.translationEngine.writeOutput(writer, object, superClass); + void writeOutput(Path path, M object, Optional> superClass, + TranslationEngine translationEngine) { + try { + translationEngine.writeOutput(path, object, superClass); + } catch (IOException e) { + throw new RuntimeException(e); + } } - protected void buildTranslationEngine() { - this.translationEngine = this.data.translationEngineBuilder.build(); + void initTranslationEngines() { + for (TranslationEngine translationEngine : this.data.translationEngines.values()) { + translationEngine.translationSpecsAreInitialized(); + + this.translationEngines.put(translationEngine.getTranslationEngineType(), translationEngine); + this.translationEngineClassToTypeMap.put(translationEngine.getClass(), + translationEngine.getTranslationEngineType()); + } } - protected void validateTranslationEngine() { - if (this.translationEngine == null) { + void validateTranslationEngine(TranslationEngine translationEngine) { + if (translationEngine == null) { throw new ContractException(CoreTranslationError.NULL_TRANSLATION_ENGINE); } @@ -396,45 +333,37 @@ protected void validateTranslationEngine() { * initTranslators() method, this should never happen, thus it is a * RuntimeException and not a ContractException */ - if (!this.translationEngine.isInitialized()) { + if (!translationEngine.isInitialized()) { throw new RuntimeException("TranslationEngine has been built but has not been initialized."); } } - /* - * First gets an ordered list of translators based on their dependencies Then - * calls each translator's init callback method Then builds the - * translationEngine Then calls the init method on the translationEngine - * Verifies that all translationSpecs have been initialized - */ - private TranslationController initTranslators() { - TranslatorContext translatorContext = new TranslatorContext(this); - - List orderedTranslators = this.getOrderedTranslators(); + void validateTranslationEngines() { + Set> translationEngineClasses = new HashSet<>(); - for (Translator translator : orderedTranslators) { - translator.getInitializer().accept(translatorContext); + if (this.translationEngines.keySet().isEmpty()) { + throw new ContractException(CoreTranslationError.NO_TRANSLATION_ENGINES); } - this.buildTranslationEngine(); - - this.translationEngine.init(); - - this.translationEngine.translationSpecsAreInitialized(); - - return this; - } + // validate each engine that exists irrespective of any mapping + for (TranslationEngine translationEngine : this.translationEngines.values()) { + validateTranslationEngine(translationEngine); + translationEngineClasses.add(translationEngine.getClass()); + } - protected void readInput(Path path, Class classRef) { - Reader reader; - try { - reader = new FileReader(path.toFile()); - this.readInput(reader, classRef); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); + // if the class to type map doesn't contain all of the classes of the engines in + // the engine map + // and + // if the engine map doesn't contain all of the types from the class to type map + // this ensures that every engine has a valid class -> type -> engine mapping + if (!(this.translationEngineClassToTypeMap.keySet().containsAll(translationEngineClasses) + && this.translationEngines.keySet().containsAll(this.translationEngineClassToTypeMap.values()))) { + throw new RuntimeException( + "Not all Translation Engines have an associated Class -> Type -> Engine Mapping. Something went very wrong."); } } + /** * Creates readers for each inputFilePath and passes the reader and classRef to * the TranslationEngine via @@ -447,12 +376,12 @@ protected void readInput(Path path, Class classRef) { * */ public TranslationController readInput() { - validateTranslationEngine(); - for (Path path : this.data.inputFilePathMap.keySet()) { Class classRef = this.data.inputFilePathMap.get(path); + TranslationEngineType type = this.data.inputFilePathEngine.get(path); + TranslationEngine translationEngine = this.translationEngines.get(type); - this.readInput(path, classRef); + this.readInput(path, classRef, translationEngine); } return this; @@ -467,7 +396,7 @@ public TranslationController readInput() { * @param the childClass * @param the optional parentClass/MarkerInterfaceClass */ - protected Pair>> getOutputPath(Class classRef, Integer scenarioId) { + Pair>> getOutputPath(Class classRef, Integer scenarioId) { Pair, Integer> key = new Pair<>(classRef, scenarioId); if (this.data.outputFilePathMap.containsKey(key)) { @@ -577,23 +506,15 @@ public void writeOutput(T object) { */ @SuppressWarnings("unchecked") public void writeOutput(M object, Integer scenarioId) { - validateTranslationEngine(); // this gives an unchecked warning, surprisingly Class classRef = (Class) object.getClass(); Pair>> pathPair = getOutputPath(classRef, scenarioId); Path path = pathPair.getFirst(); + TranslationEngineType type = this.data.outputFilePathEngine.get(path); + TranslationEngine translationEngine = this.translationEngines.get(type); - this.writeOutput(makeFileWriter(path), object, pathPair.getSecond()); - } - - protected FileWriter makeFileWriter(Path path) { - - try { - return new FileWriter(path.toFile()); - } catch (IOException e) { - throw new RuntimeException(e); - } + this.writeOutput(path, object, pathPair.getSecond(), translationEngine); } /** @@ -641,150 +562,4 @@ public List getObjects() { return this.objects; } - /* - * Goes through the list of translators and orders them based on their - * dependencies - */ - protected List getOrderedTranslators() { - return this.getOrderedTranslators(new MutableGraph<>(), new LinkedHashMap<>()); - } - - /* - * Goes through the list of translators and orders them based on their - * dependencies - */ - protected List getOrderedTranslators(MutableGraph mutableGraph, - Map translatorMap) { - - /* - * Add the nodes to the graph, check for duplicate ids, build the mapping from - * plugin id back to plugin - */ - this.addNodes(mutableGraph, translatorMap); - - // Add the edges to the graph - this.addEdges(mutableGraph); - - /* - * Check for missing plugins from the plugin dependencies that were collected - * from the known plugins. - */ - checkForMissingTranslators(mutableGraph, translatorMap); - - /* - * Determine whether the graph is acyclic and generate a graph depth evaluator - * for the graph so that we can determine the order of initialization. - */ - checkForCyclicGraph(mutableGraph); - - // the graph is acyclic, so the depth evaluator is present - GraphDepthEvaluator graphDepthEvaluator = GraphDepthEvaluator - .getGraphDepthEvaluator(mutableGraph.toGraph()).get(); - - List orderedTranslatorIds = graphDepthEvaluator.getNodesInRankOrder(); - - List orderedTranslators = new ArrayList<>(); - for (TranslatorId translatorId : orderedTranslatorIds) { - orderedTranslators.add(translatorMap.get(translatorId)); - } - - return orderedTranslators; - } - - protected void addNodes(MutableGraph mutableGraph, - Map translatorMap) { - for (Translator translator : this.data.translators) { - focalTranslatorId = translator.getTranslatorId(); - translatorMap.put(focalTranslatorId, translator); - // ensure that there are no duplicate plugins - if (mutableGraph.containsNode(focalTranslatorId)) { - throw new ContractException(CoreTranslationError.DUPLICATE_TRANSLATOR); - } - mutableGraph.addNode(focalTranslatorId); - focalTranslatorId = null; - } - } - - protected void addEdges(MutableGraph mutableGraph) { - for (Translator translator : this.data.translators) { - focalTranslatorId = translator.getTranslatorId(); - for (TranslatorId translatorId : translator.getTranslatorDependencies()) { - mutableGraph.addEdge(new Object(), focalTranslatorId, translatorId); - } - focalTranslatorId = null; - } - } - - protected void checkForMissingTranslators(MutableGraph mutableGraph, - Map translatorMap) { - for (TranslatorId translatorId : mutableGraph.getNodes()) { - if (!translatorMap.containsKey(translatorId)) { - List inboundEdges = mutableGraph.getInboundEdges(translatorId); - StringBuilder sb = new StringBuilder(); - sb.append("cannot locate instance of "); - sb.append(translatorId); - sb.append(" needed for "); - boolean first = true; - for (Object edge : inboundEdges) { - if (first) { - first = false; - } else { - sb.append(", "); - } - TranslatorId dependentTranslatorId = mutableGraph.getOriginNode(edge); - sb.append(dependentTranslatorId); - } - throw new ContractException(CoreTranslationError.MISSING_TRANSLATOR, sb.toString()); - } - } - } - - protected void checkForCyclicGraph(MutableGraph mutableGraph) { - Optional> optional = GraphDepthEvaluator - .getGraphDepthEvaluator(mutableGraph.toGraph()); - - if (!optional.isPresent()) { - /* - * Explain in detail why there is a circular dependency - */ - - Graph g = mutableGraph.toGraph(); - g = Graphs.getSourceSinkReducedGraph(g); - g = Graphs.getEdgeReducedGraph(g); - g = Graphs.getSourceSinkReducedGraph(g); - - List> cutGraphs = Graphs.cutGraph(g); - StringBuilder sb = new StringBuilder(); - String lineSeparator = System.getProperty("line.separator"); - sb.append(lineSeparator); - boolean firstCutGraph = true; - - for (Graph cutGraph : cutGraphs) { - if (firstCutGraph) { - firstCutGraph = false; - } else { - sb.append(lineSeparator); - } - sb.append("Dependency group: "); - sb.append(lineSeparator); - Set nodes = cutGraph.getNodes().stream() - .collect(Collectors.toCollection(LinkedHashSet::new)); - - for (TranslatorId node : nodes) { - sb.append("\t"); - sb.append(node); - sb.append(" requires:"); - sb.append(lineSeparator); - for (Object edge : cutGraph.getInboundEdges(node)) { - TranslatorId dependencyNode = cutGraph.getOriginNode(edge); - sb.append("\t"); - sb.append("\t"); - sb.append(dependencyNode); - sb.append(lineSeparator); - } - } - } - throw new ContractException(CoreTranslationError.CIRCULAR_TRANSLATOR_DEPENDENCIES, sb.toString()); - } - } } diff --git a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationEngine.java b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationEngine.java index 4ef5ec1..44d5ee5 100644 --- a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationEngine.java +++ b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationEngine.java @@ -1,7 +1,7 @@ package gov.hhs.aspr.ms.taskit.core; -import java.io.Reader; -import java.io.Writer; +import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -10,8 +10,13 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import util.errors.ContractException; +import util.graph.Graph; +import util.graph.GraphDepthEvaluator; +import util.graph.Graphs; +import util.graph.MutableGraph; /** * Main Translator Class Initializes all {@link TranslationSpec}s and maintains @@ -32,6 +37,9 @@ protected TranslationEngine(Data data) { protected static class Data { protected final Map, BaseTranslationSpec> classToTranslationSpecMap = new LinkedHashMap<>(); protected final Set translationSpecs = new LinkedHashSet<>(); + protected Map, Class> childToParentClassMap = new LinkedHashMap<>(); + protected TranslationEngineType translationEngineType = TranslationEngineType.UNKNOWN; + protected boolean translatorsInitialized = false; protected Data() { } @@ -68,8 +76,13 @@ public boolean equals(Object obj) { } - public static class Builder { + /** + * This class contains protected final methods for all of its abstract methods. + * All descendant classes of this class MUST call these if you want it to function properly. + */ + public abstract static class Builder { protected Data data; + protected final List translators = new ArrayList<>(); protected Builder(Data data) { this.data = data; @@ -93,18 +106,37 @@ private void validateTranslationSpec(TranslationSpec translationSpe } } + private void validateTranslatorNotNull(Translator translator) { + if (translator == null) { + throw new ContractException(CoreTranslationError.NULL_TRANSLATOR); + } + } + + private void validateClassRefNotNull(Class classRef) { + if (classRef == null) { + throw new ContractException(CoreTranslationError.NULL_CLASS_REF); + } + } + + void clearBuilder() { + this.data = new Data(); + } /** * Builder for the TranslationEngine - *

- * Note: Calling this specific method will result in a RuntimeException - *

- * - * @throws RuntimeException If this method is called directly. You should - * instead be calling the child method in the child - * TranslationEngine that extends this class */ - public TranslationEngine build() { - throw new RuntimeException("Tried to call build on abstract Translation Engine"); + public abstract TranslationEngine build(); + + protected final void initTranslators() { + TranslatorContext translatorContext = new TranslatorContext(this); + + List orderedTranslators = this.getOrderedTranslators(); + + for (Translator translator : orderedTranslators) { + translator.getInitializer().accept(translatorContext); + } + + this.data.translatorsInitialized = true; + this.translators.clear(); } /** @@ -127,30 +159,257 @@ public TranslationEngine build() { * if the given translationSpec is already known * */ - public Builder addTranslationSpec(TranslationSpec translationSpec) { + public abstract Builder addTranslationSpec(TranslationSpec translationSpec); + + protected final void _addTranslationSpec(TranslationSpec translationSpec) { validateTranslationSpec(translationSpec); this.data.classToTranslationSpecMap.put(translationSpec.getInputObjectClass(), translationSpec); this.data.classToTranslationSpecMap.put(translationSpec.getAppObjectClass(), translationSpec); this.data.translationSpecs.add(translationSpec); + } - return this; + /** + * Add a {@link Translator} + * + * @throws ContractException + *
    + *
  • {@linkplain CoreTranslationError#NULL_TRANSLATOR} + * if translator is null
  • + *
  • {@linkplain CoreTranslationError#DUPLICATE_TRANSLATOR} + * if translator has alaready been added
  • + *
+ */ + public abstract Builder addTranslator(Translator translator); + + protected final void _addTranslator(Translator translator) { + validateTranslatorNotNull(translator); + + if (this.translators.contains(translator)) { + throw new ContractException(CoreTranslationError.DUPLICATE_TRANSLATOR); + } + + this.translators.add(translator); + } + + /** + * Adds the given classRef markerInterace mapping. + *

+ * explicitly used when calling {@link TranslationController#writeOutput} with a + * class for which a classRef ScenarioId pair does not exist and/or the need to + * output the given class as the markerInterface instead of the concrete class + * + * @param the childClass + * @param the parentClass/MarkerInterfaceClass + * @throws ContractException + *

    + *
  • {@linkplain CoreTranslationError#NULL_CLASS_REF} + * if classRef is null or if markerInterface is + * null
  • + *
  • {@linkplain CoreTranslationError#DUPLICATE_CLASSREF} + * if child parent relationship has already been + * added
  • + *
+ */ + public abstract Builder addParentChildClassRelationship(Class classRef, Class markerInterface); + + protected final void _addParentChildClassRelationship(Class classRef, Class markerInterface) { + validateClassRefNotNull(classRef); + validateClassRefNotNull(markerInterface); + + if (this.data.childToParentClassMap.containsKey(classRef)) { + throw new ContractException(CoreTranslationError.DUPLICATE_CLASSREF); + } + + this.data.childToParentClassMap.put(classRef, markerInterface); + } + + /* + * Goes through the list of translators and orders them based on their + * dependencies + */ + List getOrderedTranslators() { + return this.getOrderedTranslators(new MutableGraph<>(), new LinkedHashMap<>()); + } + + /* + * Goes through the list of translators and orders them based on their + * dependencies + */ + List getOrderedTranslators(MutableGraph mutableGraph, + Map translatorMap) { + + /* + * Add the nodes to the graph, check for duplicate ids, build the mapping from + * plugin id back to plugin + */ + this.addNodes(mutableGraph, translatorMap); + + // Add the edges to the graph + this.addEdges(mutableGraph); + + /* + * Check for missing plugins from the plugin dependencies that were collected + * from the known plugins. + */ + checkForMissingTranslators(mutableGraph, translatorMap); + + /* + * Determine whether the graph is acyclic and generate a graph depth evaluator + * for the graph so that we can determine the order of initialization. + */ + checkForCyclicGraph(mutableGraph); + + // the graph is acyclic, so the depth evaluator is present + GraphDepthEvaluator graphDepthEvaluator = GraphDepthEvaluator + .getGraphDepthEvaluator(mutableGraph.toGraph()).get(); + + List orderedTranslatorIds = graphDepthEvaluator.getNodesInRankOrder(); + + List orderedTranslators = new ArrayList<>(); + for (TranslatorId translatorId : orderedTranslatorIds) { + orderedTranslators.add(translatorMap.get(translatorId)); + } + + return orderedTranslators; + } + + void addNodes(MutableGraph mutableGraph, Map translatorMap) { + TranslatorId focalTranslatorId = null; + for (Translator translator : this.translators) { + focalTranslatorId = translator.getTranslatorId(); + translatorMap.put(focalTranslatorId, translator); + // ensure that there are no duplicate plugins + if (mutableGraph.containsNode(focalTranslatorId)) { + throw new ContractException(CoreTranslationError.DUPLICATE_TRANSLATOR); + } + mutableGraph.addNode(focalTranslatorId); + focalTranslatorId = null; + } + } + + void addEdges(MutableGraph mutableGraph) { + TranslatorId focalTranslatorId = null; + for (Translator translator : this.translators) { + focalTranslatorId = translator.getTranslatorId(); + for (TranslatorId translatorId : translator.getTranslatorDependencies()) { + mutableGraph.addEdge(new Object(), focalTranslatorId, translatorId); + } + focalTranslatorId = null; + } + } + + void checkForMissingTranslators(MutableGraph mutableGraph, + Map translatorMap) { + for (TranslatorId translatorId : mutableGraph.getNodes()) { + if (!translatorMap.containsKey(translatorId)) { + List inboundEdges = mutableGraph.getInboundEdges(translatorId); + StringBuilder sb = new StringBuilder(); + sb.append("cannot locate instance of "); + sb.append(translatorId); + sb.append(" needed for "); + boolean first = true; + for (Object edge : inboundEdges) { + if (first) { + first = false; + } else { + sb.append(", "); + } + TranslatorId dependentTranslatorId = mutableGraph.getOriginNode(edge); + sb.append(dependentTranslatorId); + } + throw new ContractException(CoreTranslationError.MISSING_TRANSLATOR, sb.toString()); + } + } + } + + void checkForCyclicGraph(MutableGraph mutableGraph) { + Optional> optional = GraphDepthEvaluator + .getGraphDepthEvaluator(mutableGraph.toGraph()); + + if (!optional.isPresent()) { + /* + * Explain in detail why there is a circular dependency + */ + + Graph g = mutableGraph.toGraph(); + g = Graphs.getSourceSinkReducedGraph(g); + g = Graphs.getEdgeReducedGraph(g); + g = Graphs.getSourceSinkReducedGraph(g); + + List> cutGraphs = Graphs.cutGraph(g); + StringBuilder sb = new StringBuilder(); + String lineSeparator = System.getProperty("line.separator"); + sb.append(lineSeparator); + boolean firstCutGraph = true; + + for (Graph cutGraph : cutGraphs) { + if (firstCutGraph) { + firstCutGraph = false; + } else { + sb.append(lineSeparator); + } + sb.append("Dependency group: "); + sb.append(lineSeparator); + Set nodes = cutGraph.getNodes().stream() + .collect(Collectors.toCollection(LinkedHashSet::new)); + + for (TranslatorId node : nodes) { + sb.append("\t"); + sb.append(node); + sb.append(" requires:"); + sb.append(lineSeparator); + for (Object edge : cutGraph.getInboundEdges(node)) { + TranslatorId dependencyNode = cutGraph.getOriginNode(edge); + sb.append("\t"); + sb.append("\t"); + sb.append(dependencyNode); + sb.append(lineSeparator); + } + } + } + throw new ContractException(CoreTranslationError.CIRCULAR_TRANSLATOR_DEPENDENCIES, sb.toString()); + } } } + private void validateTranslationEngineType() { + if (this.data.translationEngineType == TranslationEngineType.UNKNOWN) { + throw new ContractException(CoreTranslationError.UNKNWON_TRANSLATION_ENGINE_TYPE); + } + } + + private void validateTranslatorsInitialized() { + if (!this.data.translatorsInitialized) { + throw new ContractException(CoreTranslationError.UNINITIALIZED_TRANSLATORS); + } + } + + // This is package access so the TranslationController can access it but nothing + // else. + Map, Class> getChildParentClassMap() { + Map, Class> copyMap = new LinkedHashMap<>(this.data.childToParentClassMap); + + this.data.childToParentClassMap = null; + + return copyMap; + } + /** - * Returns a new instance of Builder + * returns the {@link TranslationEngineType} of this TranslationEngine + * + * guarenteed to NOT be {@link TranslationEngineType#UNKNOWN} */ - public static Builder builder() { - return new Builder(new Data()); + public TranslationEngineType getTranslationEngineType() { + return this.data.translationEngineType; } /** * Initializes the translationEngine by calling init on each translationSpec * added in the builder */ - public void init() { + protected void initTranslationSpecs() { /* * Calling init on a translationSpec causes the hashCode of the translationSpec * to change. Because of this, before calling init, we need to remove them from @@ -169,6 +428,10 @@ public void init() { this.isInitialized = true; } + protected void validateInit() { + validateTranslationEngineType(); + validateTranslatorsInitialized(); + } /** * returns whether this translationEngine is initialized or not */ @@ -194,6 +457,10 @@ protected void translationSpecsAreInitialized() { } + /** + * Returns a set of all {@link TranslationSpec}s associated with this + * TranslationEngine + */ public Set getTranslationSpecs() { return this.data.translationSpecs; } @@ -202,13 +469,13 @@ public Set getTranslationSpecs() { * abstract method that must be implemented by child TranslatorCores that * defines how to write to output files */ - protected abstract void writeOutput(Writer writer, M appObject, Optional> superClass); + protected abstract void writeOutput(Path path, M appObject, Optional> superClass) throws IOException; /** * abstract method that must be implemented by child TranslatorCores that * defines how to read from input files */ - protected abstract T readInput(Reader reader, Class inputClassRef); + protected abstract T readInput(Path path, Class inputClassRef) throws IOException; /** * Given an object, uses the class of the object to obtain the translationSpec diff --git a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationEngineType.java b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationEngineType.java new file mode 100644 index 0000000..70957dc --- /dev/null +++ b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationEngineType.java @@ -0,0 +1,7 @@ +package gov.hhs.aspr.ms.taskit.core; + +public enum TranslationEngineType { + PROTOBUF, + CUSTOM, + UNKNOWN +} diff --git a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationSpec.java b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationSpec.java index 5334812..25f5362 100644 --- a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationSpec.java +++ b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslationSpec.java @@ -22,12 +22,6 @@ public void init(T translationEngine) { this.initialized = true; } - protected void checkInit() { - if (!this.initialized) { - throw new ContractException(CoreTranslationError.UNITIALIZED_TRANSLATION_SPEC); - } - } - /** * Returns the initialization state of this TranslationSpec */ @@ -118,22 +112,28 @@ public boolean equals(Object obj) { } /** - * Given an inputObject, converts it to it's appObject equivalent + * Returns the class of the app object */ - protected abstract A convertInputObject(I inputObject); + public abstract Class getAppObjectClass(); /** - * Given an appObject, converts it to it's inputObject equivalent + * Returns the class of the input object */ - protected abstract I convertAppObject(A appObject); + public abstract Class getInputObjectClass(); /** - * Returns the class of the app object + * Given an inputObject, converts it to it's appObject equivalent */ - public abstract Class getAppObjectClass(); + protected abstract A convertInputObject(I inputObject); /** - * Returns the class of the input object + * Given an appObject, converts it to it's inputObject equivalent */ - public abstract Class getInputObjectClass(); + protected abstract I convertAppObject(A appObject); + + void checkInit() { + if (!this.initialized) { + throw new ContractException(CoreTranslationError.UNITIALIZED_TRANSLATION_SPEC); + } + } } diff --git a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/Translator.java b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/Translator.java index bb3e54b..be59e88 100644 --- a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/Translator.java +++ b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/Translator.java @@ -19,12 +19,12 @@ private Translator(Data data) { this.data = data; } - protected static class Data { + final static class Data { private TranslatorId translatorId; private Consumer initializer; private final Set dependencies = new LinkedHashSet<>(); - protected Data() { + Data() { } @Override @@ -49,7 +49,7 @@ public boolean equals(Object obj) { } - public static class Builder { + public final static class Builder { private Data data; private Builder(Data data) { diff --git a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslatorContext.java b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslatorContext.java index 3d91fc1..503d54d 100644 --- a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslatorContext.java +++ b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/TranslatorContext.java @@ -3,36 +3,36 @@ import util.errors.ContractException; /** - * Context that is used by {@link Translator}s to get the instance of - * {@link TranslationEngine.Builder} from the {@link TranslationController} + * Context that is used by {@link Translator}s + * + * Note: This class exists because the subclassed TranslationEngine may have + * different build methods than the abstract one, preventing the associated + * consumer that this context is used in from just being a consumer of a + * TranslationEngine.Builder */ -public class TranslatorContext { +public final class TranslatorContext { - private final TranslationController translationController; + private final TranslationEngine.Builder builder; - public TranslatorContext(TranslationController translationController) { - this.translationController = translationController; + public TranslatorContext(final TranslationEngine.Builder builder) { + this.builder = builder; } /** * Returns an instance of the TranslationEngine Builder * * @param the type of the TranslationEngine - * @throws ContractException {@linkplain CoreTranslationError#INVALID_TRANSLATION_ENGINE_BUILDER} + * @throws ContractException {@linkplain CoreTranslationError#INVALID_TRANSLATION_ENGINE_BUILDER_CLASS_REF} * if the given classRef does not match the class or * the translatorCoreBuilder is null */ - public T getTranslationEngineBuilder(Class classRef) { - return this.translationController.getTranslationEngineBuilder(classRef); - } + public T getTranslationEngineBuilder(final Class classRef) { + if (this.builder.getClass().isAssignableFrom(classRef)) { + return classRef.cast(this.builder); + } + + throw new ContractException(CoreTranslationError.INVALID_TRANSLATION_ENGINE_BUILDER_CLASS_REF, + "No Translation Engine Builder was found for the type: " + classRef.getName()); - /** - * Adds the child class parent class relationship to the TranslatorController - * - * @param the type of the child - * @param the type of the parent - */ - public void addParentChildClassRelationship(Class classRef, Class parentClassRef) { - this.translationController.addParentChildClassRelationship(classRef, parentClassRef); } } diff --git a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/testsupport/TestTranslationEngine.java b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/testsupport/TestTranslationEngine.java index c5a5dda..a90b5ed 100644 --- a/core/src/main/java/gov/hhs/aspr/ms/taskit/core/testsupport/TestTranslationEngine.java +++ b/core/src/main/java/gov/hhs/aspr/ms/taskit/core/testsupport/TestTranslationEngine.java @@ -1,8 +1,9 @@ package gov.hhs.aspr.ms.taskit.core.testsupport; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; -import java.io.Reader; -import java.io.Writer; +import java.nio.file.Path; import java.util.Optional; import com.google.gson.Gson; @@ -11,7 +12,9 @@ import com.google.gson.stream.JsonReader; import gov.hhs.aspr.ms.taskit.core.TranslationEngine; +import gov.hhs.aspr.ms.taskit.core.TranslationEngineType; import gov.hhs.aspr.ms.taskit.core.TranslationSpec; +import gov.hhs.aspr.ms.taskit.core.Translator; public class TestTranslationEngine extends TranslationEngine { private final Data data; @@ -22,10 +25,15 @@ private TestTranslationEngine(Data data) { } protected static class Data extends TranslationEngine.Data { - Gson gson = new Gson(); + protected Gson gson = new Gson(); protected Data() { super(); + this.translationEngineType = TranslationEngineType.CUSTOM; + } + + private void setUnknownEngineType() { + this.translationEngineType = TranslationEngineType.UNKNOWN; } @Override @@ -50,16 +58,53 @@ private Builder(TestTranslationEngine.Data data) { @Override public TestTranslationEngine build() { + super.initTranslators(); + + TestTranslationEngine translationEngine = new TestTranslationEngine(this.data); + + translationEngine.initTranslationSpecs(); + translationEngine.validateInit(); + return translationEngine; + } + + public TestTranslationEngine buildWithoutSpecInit() { + super.initTranslators(); + return new TestTranslationEngine(this.data); + } + + public TestTranslationEngine buildWithNoTranslatorInit() { + TestTranslationEngine translationEngine = new TestTranslationEngine(this.data); + translationEngine.initTranslationSpecs(); + + return new TestTranslationEngine(this.data); + } + + public TestTranslationEngine buildWithUnknownType() { + this.data.setUnknownEngineType(); + return new TestTranslationEngine(this.data); } @Override public Builder addTranslationSpec(TranslationSpec translationSpec) { - super.addTranslationSpec(translationSpec); + _addTranslationSpec(translationSpec); + + return this; + } + + @Override + public Builder addTranslator(Translator translator) { + _addTranslator(translator); return this; } + @Override + public Builder addParentChildClassRelationship(Class classRef, Class markerInterface) { + _addParentChildClassRelationship(classRef, markerInterface); + + return this; + } } public static Builder builder() { @@ -67,7 +112,7 @@ public static Builder builder() { } @Override - public void writeOutput(Writer writer, M appObject, Optional> superClass) { + public void writeOutput(Path path, M appObject, Optional> superClass) throws IOException { Object outputObject; if (superClass.isPresent()) { outputObject = convertObjectAsSafeClass(appObject, superClass.get()); @@ -76,18 +121,14 @@ public void writeOutput(Writer writer, M appObject, Optional T readInput(Reader reader, Class inputClassRef) { - JsonObject jsonObject = JsonParser.parseReader(new JsonReader(reader)).getAsJsonObject(); + public T readInput(Path path, Class inputClassRef) throws IOException { + JsonObject jsonObject = JsonParser.parseReader(new JsonReader(new FileReader(path.toFile()))).getAsJsonObject(); return convertObject(this.data.gson.fromJson(jsonObject.toString(), inputClassRef)); } diff --git a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslationController.java b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslationController.java index 43d90ae..3367f2a 100644 --- a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslationController.java +++ b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslationController.java @@ -6,13 +6,10 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.FileWriter; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Optional; import org.apache.commons.math3.util.Pair; @@ -23,17 +20,12 @@ import gov.hhs.aspr.ms.taskit.core.testsupport.TestTranslationEngine; import gov.hhs.aspr.ms.taskit.core.testsupport.testcomplexobject.TestComplexAppObject; import gov.hhs.aspr.ms.taskit.core.testsupport.testcomplexobject.TestComplexObjectTranslator; -import gov.hhs.aspr.ms.taskit.core.testsupport.testcomplexobject.TestComplexObjectTranslatorId; -import gov.hhs.aspr.ms.taskit.core.testsupport.testcomplexobject.translationSpecs.TestComplexObjectTranslationSpec; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestAppObject; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestObjectTranslator; -import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestObjectTranslatorId; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.input.TestInputObject; -import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.translationSpecs.TestObjectTranslationSpec; import util.annotations.UnitTestForCoverage; import util.annotations.UnitTestMethod; import util.errors.ContractException; -import util.graph.MutableGraph; public class AT_TranslationController { @@ -44,49 +36,109 @@ public class AT_TranslationController { @UnitTestForCoverage public void testValidateTranslationEngine() { TranslationController translationController = TranslationController.builder() - .setTranslationEngineBuilder(TestTranslationEngine.builder()).buildWithoutInitAndChecks(); + .addTranslationEngine(TestTranslationEngine.builder().build()).buildWithoutInitAndChecks(); // preconditions ContractException contractException = assertThrows(ContractException.class, () -> { - translationController.validateTranslationEngine(); + translationController.validateTranslationEngines(); + }); + + assertEquals(CoreTranslationError.NO_TRANSLATION_ENGINES, contractException.getErrorType()); + + contractException = assertThrows(ContractException.class, () -> { + for (TranslationEngine translationEngine : translationController.data.translationEngines.values()) { + translationController.translationEngines.put(translationEngine.getTranslationEngineType(), null); + } + translationController.validateTranslationEngines(); }); assertEquals(CoreTranslationError.NULL_TRANSLATION_ENGINE, contractException.getErrorType()); RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> { - translationController.buildTranslationEngine(); - translationController.validateTranslationEngine(); + TestTranslationEngine translationEngine2 = TestTranslationEngine.builder().buildWithoutSpecInit(); + + translationController.translationEngines.put(translationEngine2.getTranslationEngineType(), + translationEngine2); + + translationController.validateTranslationEngines(); }); assertEquals("TranslationEngine has been built but has not been initialized.", runtimeException.getMessage()); + runtimeException = assertThrows(RuntimeException.class, () -> { + for (TranslationEngine translationEngine : translationController.data.translationEngines.values()) { + translationController.translationEngines.put(translationEngine.getTranslationEngineType(), + translationEngine); + translationController.translationEngineClassToTypeMap.put(translationEngine.getClass(), null); + } + translationController.validateTranslationEngines(); + }); + + assertEquals( + "Not all Translation Engines have an associated Class -> Type -> Engine Mapping. Something went very wrong.", + runtimeException.getMessage()); + + runtimeException = assertThrows(RuntimeException.class, () -> { + for (TranslationEngine translationEngine : translationController.data.translationEngines.values()) { + translationController.translationEngines.put(translationEngine.getTranslationEngineType(), + translationEngine); + } + translationController.validateTranslationEngines(); + }); + + assertEquals( + "Not all Translation Engines have an associated Class -> Type -> Engine Mapping. Something went very wrong.", + runtimeException.getMessage()); } @Test - @UnitTestMethod(target = TranslationController.class, name = "getTranslationEngine", args = { Class.class }) - public void testGetTranslationEngine() { - TestTranslationEngine expectedValue = TestTranslationEngine.builder() - .addTranslationSpec(new TestObjectTranslationSpec()) - .addTranslationSpec(new TestComplexObjectTranslationSpec()).build(); - expectedValue.init(); + @UnitTestForCoverage + /* + * purpose of this test is to show that if there isnt a valid TranslationEngine + * class -> Translation Engine Type -> Translation Engine mapping, an exception + * is thrown + */ + public void testValidateTranslationEngines() { + TranslationController translationController = TranslationController.builder().buildWithoutInitAndChecks(); - TranslationController translationController = TranslationController.builder() - .addTranslator(TestObjectTranslator.getTranslator()) - .addTranslator(TestComplexObjectTranslator.getTranslator()) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); + // preconditions + ContractException contractException = assertThrows(ContractException.class, () -> { + translationController.validateTranslationEngines(); + }); - assertEquals(expectedValue, translationController.getTranslationEngine(TestTranslationEngine.class)); + assertEquals(CoreTranslationError.NO_TRANSLATION_ENGINES, contractException.getErrorType()); - // precondition - // translation engine is null and is tested by the test: - // testValidateTranslationEngine() + // class to type map not populated + RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> { + TranslationController translationController2 = TranslationController.builder() + .addTranslationEngine(TestTranslationEngine.builder().build()).buildWithoutInitAndChecks(); + for (TranslationEngine translationEngine : translationController2.data.translationEngines.values()) { + translationController2.translationEngines.put(translationEngine.getTranslationEngineType(), + translationEngine); + } + translationController2.validateTranslationEngines(); + }); - // classRef passed in does not match the class of the translation engine - ContractException contractException = assertThrows(ContractException.class, () -> { - translationController.getTranslationEngine(TranslationEngine.class); + assertEquals( + "Not all Translation Engines have an associated Class -> Type -> Engine Mapping. Something went very wrong.", + runtimeException.getMessage()); + + // class to type map not fully mapped + runtimeException = assertThrows(RuntimeException.class, () -> { + TranslationController translationController2 = TranslationController.builder() + .addTranslationEngine(TestTranslationEngine.builder().build()).buildWithoutInitAndChecks(); + for (TranslationEngine translationEngine : translationController2.data.translationEngines.values()) { + translationController2.translationEngines.put(translationEngine.getTranslationEngineType(), + translationEngine); + translationController2.translationEngineClassToTypeMap.put(translationEngine.getClass(), null); + } + translationController2.validateTranslationEngines(); }); - assertEquals(CoreTranslationError.INVALID_TRANSLATION_ENGINE, contractException.getErrorType()); + assertEquals( + "Not all Translation Engines have an associated Class -> Type -> Engine Mapping. Something went very wrong.", + runtimeException.getMessage()); + } @Test @@ -96,12 +148,14 @@ public void testReadInput() { TestResourceHelper.createTestOutputFile(filePath, fileName); - TranslationController translationController = TranslationController.builder() - .addInputFilePath(filePath.resolve(fileName), TestInputObject.class) - .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class) + TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() .addTranslator(TestObjectTranslator.getTranslator()) - .addTranslator(TestComplexObjectTranslator.getTranslator()) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); + .addTranslator(TestComplexObjectTranslator.getTranslator()).build(); + + TranslationController translationController = TranslationController.builder() + .addInputFilePath(filePath.resolve(fileName), TestInputObject.class, TranslationEngineType.CUSTOM) + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, TranslationEngineType.CUSTOM) + .addTranslationEngine(testTranslationEngine).build(); TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); @@ -119,7 +173,8 @@ public void testReadInput() { // invalid file path RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> { - translationController.readInput(filePath.resolve("badPath"), TestInputObject.class); + translationController.readInput(filePath.resolve("badPath"), TestInputObject.class, + TestTranslationEngine.builder().build()); }); assertTrue(runtimeException.getCause() instanceof IOException); @@ -127,22 +182,22 @@ public void testReadInput() { @Test @UnitTestForCoverage - public void testMakeFileWriter() { - String fileName = "MakeFileWriter-testOutput.json"; + public void testWriteOutput_Engine() { + String fileName = "badFilePath-testoutput.json"; TestResourceHelper.createTestOutputFile(filePath, fileName); + TestTranslationEngine engine = TestTranslationEngine.builder() + .addTranslator(TestObjectTranslator.getTranslator()) + .addTranslator(TestComplexObjectTranslator.getTranslator()).build(); + TranslationController translationController = TranslationController.builder().addTranslationEngine(engine) + .build(); - TranslationController translationController = TranslationController.builder() - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); - - FileWriter actuaFileWriter = translationController.makeFileWriter(filePath.resolve(fileName)); - - assertNotNull(actuaFileWriter); // preconditions // if the filePath is invalid RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> { - translationController.makeFileWriter(filePath); + translationController.writeOutput(filePath, TestObjectUtil.generateTestAppObject(), Optional.empty(), + engine); }); assertTrue(runtimeException.getCause() instanceof IOException); @@ -158,9 +213,9 @@ public void testGetOutputPath() { TestResourceHelper.createTestOutputFile(filePath, fileName2); TranslationController translationController = TranslationController.builder() - .setTranslationEngineBuilder(TestTranslationEngine.builder()) - .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class) - .addOutputFilePath(filePath.resolve(fileName2), Object.class, 1) + .addTranslationEngine(TestTranslationEngine.builder().build()) + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, TranslationEngineType.CUSTOM) + .addOutputFilePath(filePath.resolve(fileName2), Object.class, 1, TranslationEngineType.CUSTOM) .addParentChildClassRelationship(TestAppObject.class, Object.class).build(); Pair>> expectedPair1 = new Pair<>(filePath.resolve(fileName), @@ -202,12 +257,15 @@ public void testWriteOutput_List() { TestResourceHelper.createTestOutputFile(filePath, fileName); TestResourceHelper.createTestOutputFile(filePath, fileName2); - TranslationController translationController = TranslationController.builder() - .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class) - .addOutputFilePath(filePath.resolve(fileName2), TestComplexAppObject.class) + TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() .addTranslator(TestObjectTranslator.getTranslator()) - .addTranslator(TestComplexObjectTranslator.getTranslator()) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); + .addTranslator(TestComplexObjectTranslator.getTranslator()).build(); + + TranslationController translationController = TranslationController.builder() + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, TranslationEngineType.CUSTOM) + .addOutputFilePath(filePath.resolve(fileName2), TestComplexAppObject.class, + TranslationEngineType.CUSTOM) + .addTranslationEngine(testTranslationEngine).build(); List outputObjects = new ArrayList<>(); @@ -232,12 +290,15 @@ public void testWriteOutput_List_ScenarioId() throws IOException { TestResourceHelper.createTestOutputFile(filePath, fileName); TestResourceHelper.createTestOutputFile(filePath, fileName2); - TranslationController translationController = TranslationController.builder() - .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, 1) - .addOutputFilePath(filePath.resolve(fileName2), TestComplexAppObject.class, 1) + TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() .addTranslator(TestObjectTranslator.getTranslator()) - .addTranslator(TestComplexObjectTranslator.getTranslator()) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); + .addTranslator(TestComplexObjectTranslator.getTranslator()).build(); + + TranslationController translationController = TranslationController.builder() + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, 1, TranslationEngineType.CUSTOM) + .addOutputFilePath(filePath.resolve(fileName2), TestComplexAppObject.class, 1, + TranslationEngineType.CUSTOM) + .addTranslationEngine(testTranslationEngine).build(); List outputObjects = new ArrayList<>(); @@ -260,11 +321,13 @@ public void testWriteOutput() throws IOException { TestResourceHelper.createTestOutputFile(filePath, fileName); - TranslationController translationController = TranslationController.builder() - .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class) + TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() .addTranslator(TestObjectTranslator.getTranslator()) - .addTranslator(TestComplexObjectTranslator.getTranslator()) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); + .addTranslator(TestComplexObjectTranslator.getTranslator()).build(); + + TranslationController translationController = TranslationController.builder() + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, TranslationEngineType.CUSTOM) + .addTranslationEngine(testTranslationEngine).build(); TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); @@ -285,11 +348,13 @@ public void testWriteOutput_ScenarioId() throws IOException { TestResourceHelper.createTestOutputFile(filePath, fileName); - TranslationController translationController = TranslationController.builder() - .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, 1) + TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() .addTranslator(TestObjectTranslator.getTranslator()) - .addTranslator(TestComplexObjectTranslator.getTranslator()) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); + .addTranslator(TestComplexObjectTranslator.getTranslator()).build(); + + TranslationController translationController = TranslationController.builder() + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, 1, TranslationEngineType.CUSTOM) + .addTranslationEngine(testTranslationEngine).build(); TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); @@ -310,12 +375,14 @@ public void testGetFirstObject() throws IOException { TestResourceHelper.createTestOutputFile(filePath, fileName); - TranslationController translationController = TranslationController.builder() - .addInputFilePath(filePath.resolve(fileName), TestInputObject.class) - .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class) + TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() .addTranslator(TestObjectTranslator.getTranslator()) - .addTranslator(TestComplexObjectTranslator.getTranslator()) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); + .addTranslator(TestComplexObjectTranslator.getTranslator()).build(); + + TranslationController translationController = TranslationController.builder() + .addInputFilePath(filePath.resolve(fileName), TestInputObject.class, TranslationEngineType.CUSTOM) + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, TranslationEngineType.CUSTOM) + .addTranslationEngine(testTranslationEngine).build(); TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); @@ -347,14 +414,16 @@ public void testGetObjects_OfClass() throws IOException { TestResourceHelper.createTestOutputFile(filePath, fileName); TestResourceHelper.createTestOutputFile(filePath, fileName2); - TranslationController translationController = TranslationController.builder() - .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, 1) - .addOutputFilePath(filePath.resolve(fileName2), TestAppObject.class, 2) - .addInputFilePath(filePath.resolve(fileName), TestInputObject.class) - .addInputFilePath(filePath.resolve(fileName2), TestInputObject.class) + TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() .addTranslator(TestObjectTranslator.getTranslator()) - .addTranslator(TestComplexObjectTranslator.getTranslator()) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); + .addTranslator(TestComplexObjectTranslator.getTranslator()).build(); + + TranslationController translationController = TranslationController.builder() + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, 1, TranslationEngineType.CUSTOM) + .addOutputFilePath(filePath.resolve(fileName2), TestAppObject.class, 2, TranslationEngineType.CUSTOM) + .addInputFilePath(filePath.resolve(fileName), TestInputObject.class, TranslationEngineType.CUSTOM) + .addInputFilePath(filePath.resolve(fileName2), TestInputObject.class, TranslationEngineType.CUSTOM) + .addTranslationEngine(testTranslationEngine).build(); List expectedObjects = TestObjectUtil.getListOfAppObjects(2); @@ -384,14 +453,16 @@ public void testGetObjects() throws IOException { TestResourceHelper.createTestOutputFile(filePath, fileName); TestResourceHelper.createTestOutputFile(filePath, fileName2); - TranslationController translationController = TranslationController.builder() - .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, 1) - .addOutputFilePath(filePath.resolve(fileName2), TestAppObject.class, 2) - .addInputFilePath(filePath.resolve(fileName), TestInputObject.class) - .addInputFilePath(filePath.resolve(fileName2), TestInputObject.class) + TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() .addTranslator(TestObjectTranslator.getTranslator()) - .addTranslator(TestComplexObjectTranslator.getTranslator()) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); + .addTranslator(TestComplexObjectTranslator.getTranslator()).build(); + + TranslationController translationController = TranslationController.builder() + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, 1, TranslationEngineType.CUSTOM) + .addOutputFilePath(filePath.resolve(fileName2), TestAppObject.class, 2, TranslationEngineType.CUSTOM) + .addInputFilePath(filePath.resolve(fileName), TestInputObject.class, TranslationEngineType.CUSTOM) + .addInputFilePath(filePath.resolve(fileName2), TestInputObject.class, TranslationEngineType.CUSTOM) + .addTranslationEngine(testTranslationEngine).build(); List expectedObjects = TestObjectUtil.getListOfAppObjects(2); @@ -409,77 +480,6 @@ public void testGetObjects() throws IOException { assertTrue(actualObjects.containsAll(expectedObjects)); } - @Test - @UnitTestForCoverage - public void testGetOrderedTranslators() { - - TranslationController translationController = TranslationController.builder() - .addTranslator(TestObjectTranslator.getTranslator()) - .addTranslator(TestComplexObjectTranslator.getTranslator()) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); - - List expectedList = new ArrayList<>(); - expectedList.add(TestComplexObjectTranslator.getTranslator()); - expectedList.add(TestObjectTranslator.getTranslator()); - - List actualList = translationController.getOrderedTranslators(); - - assertEquals(expectedList, actualList); - - // preconditions - - // duplicate translator in the graph - - ContractException contractException = assertThrows(ContractException.class, () -> { - MutableGraph mutableGraph = new MutableGraph<>(); - Map translatorMap = new LinkedHashMap<>(); - mutableGraph.addNode(TestObjectTranslatorId.TRANSLATOR_ID); - translationController.addNodes(mutableGraph, translatorMap); - }); - - assertEquals(CoreTranslationError.DUPLICATE_TRANSLATOR, contractException.getErrorType()); - - // missing translator - contractException = assertThrows(ContractException.class, () -> { - MutableGraph mutableGraph = new MutableGraph<>(); - Map translatorMap = new LinkedHashMap<>(); - - // call normally - translationController.getOrderedTranslators(mutableGraph, translatorMap); - // remove a mapping - translatorMap.remove(TestComplexObjectTranslatorId.TRANSLATOR_ID); - TranslatorId thirdId = new TranslatorId() { - }; - mutableGraph.addNode(thirdId); - mutableGraph.addEdge(new Object(), thirdId, TestComplexObjectTranslatorId.TRANSLATOR_ID); - translationController.checkForMissingTranslators(mutableGraph, translatorMap); - }); - - assertEquals(CoreTranslationError.MISSING_TRANSLATOR, contractException.getErrorType()); - - // cyclic graph - contractException = assertThrows(ContractException.class, () -> { - MutableGraph mutableGraph = new MutableGraph<>(); - Map translatorMap = new LinkedHashMap<>(); - - // call normally - translationController.getOrderedTranslators(mutableGraph, translatorMap); - mutableGraph.addEdge(new Object(), TestComplexObjectTranslatorId.TRANSLATOR_ID, - TestObjectTranslatorId.TRANSLATOR_ID); - TranslatorId thirdId = new TranslatorId() { - }; - TranslatorId fourthId = new TranslatorId() { - }; - mutableGraph.addNode(thirdId); - mutableGraph.addNode(fourthId); - mutableGraph.addEdge(new Object(), thirdId, fourthId); - mutableGraph.addEdge(new Object(), fourthId, thirdId); - translationController.checkForCyclicGraph(mutableGraph); - }); - - assertEquals(CoreTranslationError.CIRCULAR_TRANSLATOR_DEPENDENCIES, contractException.getErrorType()); - } - @Test @UnitTestMethod(target = TranslationController.class, name = "builder", args = {}) public void testBuilder() { @@ -490,7 +490,7 @@ public void testBuilder() { @UnitTestMethod(target = TranslationController.Builder.class, name = "build", args = {}) public void testBuild() { TranslationController translationController = TranslationController.builder() - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build(); + .addTranslationEngine(TestTranslationEngine.builder().build()).build(); assertNotNull(translationController); @@ -499,43 +499,46 @@ public void testBuild() { TranslationController.builder().build(); }); - assertEquals(CoreTranslationError.NULL_TRANSLATION_ENGINE_BUILDER, contractException.getErrorType()); + assertEquals(CoreTranslationError.NULL_TRANSLATION_ENGINE, contractException.getErrorType()); } @Test @UnitTestMethod(target = TranslationController.Builder.class, name = "addInputFilePath", args = { Path.class, - Class.class }) + Class.class, TranslationEngineType.class }) public void testAddInputFilePath() { String fileName = "addInputFilePath-testOutput.json"; TestResourceHelper.createTestOutputFile(filePath, fileName); assertDoesNotThrow(() -> TranslationController.builder() - .addInputFilePath(filePath.resolve(fileName), TestInputObject.class) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build()); + .addInputFilePath(filePath.resolve(fileName), TestInputObject.class, TranslationEngineType.CUSTOM) + .addTranslationEngine(TestTranslationEngine.builder().build()).build()); // preconditions ContractException contractException = assertThrows(ContractException.class, () -> { - TranslationController.builder().addInputFilePath(null, TestInputObject.class); + TranslationController.builder().addInputFilePath(null, TestInputObject.class, TranslationEngineType.CUSTOM); }); assertEquals(CoreTranslationError.NULL_PATH, contractException.getErrorType()); contractException = assertThrows(ContractException.class, () -> { - TranslationController.builder().addInputFilePath(filePath.resolve(fileName), null); + TranslationController.builder().addInputFilePath(filePath.resolve(fileName), null, + TranslationEngineType.CUSTOM); }); assertEquals(CoreTranslationError.NULL_CLASS_REF, contractException.getErrorType()); contractException = assertThrows(ContractException.class, () -> { - TranslationController.builder().addInputFilePath(filePath.resolve(fileName), TestInputObject.class) - .addInputFilePath(filePath.resolve(fileName), TestInputObject.class); + TranslationController.builder() + .addInputFilePath(filePath.resolve(fileName), TestInputObject.class, TranslationEngineType.CUSTOM) + .addInputFilePath(filePath.resolve(fileName), TestInputObject.class, TranslationEngineType.CUSTOM); }); assertEquals(CoreTranslationError.DUPLICATE_INPUT_PATH, contractException.getErrorType()); contractException = assertThrows(ContractException.class, () -> { - TranslationController.builder().addInputFilePath(filePath.resolve("badPath"), TestInputObject.class); + TranslationController.builder().addInputFilePath(filePath.resolve("badPath"), TestInputObject.class, + TranslationEngineType.CUSTOM); }); assertEquals(CoreTranslationError.INVALID_INPUT_PATH, contractException.getErrorType()); @@ -544,47 +547,50 @@ public void testAddInputFilePath() { @Test @UnitTestMethod(target = TranslationController.Builder.class, name = "addOutputFilePath", args = { Path.class, - Class.class }) + Class.class, TranslationEngineType.class }) public void testAddOutputFilePath() { String fileName = "addOutputFilePath1-testOutput.json"; String fileName2 = "addOutputFilePath2-testOutput.json"; TestResourceHelper.createTestOutputFile(filePath, fileName); - assertDoesNotThrow( - () -> TranslationController.builder().addOutputFilePath(filePath.resolve(fileName), TestAppObject.class) - .setTranslationEngineBuilder(TestTranslationEngine.builder()).build()); + assertDoesNotThrow(() -> TranslationController.builder() + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, TranslationEngineType.CUSTOM) + .addTranslationEngine(TestTranslationEngine.builder().build()).build()); // preconditions ContractException contractException = assertThrows(ContractException.class, () -> { - TranslationController.builder().addOutputFilePath(null, TestAppObject.class); + TranslationController.builder().addOutputFilePath(null, TestAppObject.class, TranslationEngineType.CUSTOM); }); assertEquals(CoreTranslationError.NULL_PATH, contractException.getErrorType()); contractException = assertThrows(ContractException.class, () -> { - TranslationController.builder().addOutputFilePath(filePath.resolve(fileName), null); + TranslationController.builder().addOutputFilePath(filePath.resolve(fileName), null, + TranslationEngineType.CUSTOM); }); assertEquals(CoreTranslationError.NULL_CLASS_REF, contractException.getErrorType()); contractException = assertThrows(ContractException.class, () -> { - TranslationController.builder().addOutputFilePath(filePath.resolve(fileName), TestAppObject.class) - .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class); + TranslationController.builder() + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, TranslationEngineType.CUSTOM) + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, TranslationEngineType.CUSTOM); }); assertEquals(CoreTranslationError.DUPLICATE_OUTPUT_PATH, contractException.getErrorType()); contractException = assertThrows(ContractException.class, () -> { - TranslationController.builder().addOutputFilePath(filePath.resolve(fileName), TestAppObject.class) - .addOutputFilePath(filePath.resolve(fileName2), TestAppObject.class); + TranslationController.builder() + .addOutputFilePath(filePath.resolve(fileName), TestAppObject.class, TranslationEngineType.CUSTOM) + .addOutputFilePath(filePath.resolve(fileName2), TestAppObject.class, TranslationEngineType.CUSTOM); }); assertEquals(CoreTranslationError.DUPLICATE_CLASSREF_SCENARIO_PAIR, contractException.getErrorType()); contractException = assertThrows(ContractException.class, () -> { TranslationController.builder().addOutputFilePath(filePath.resolve("badpath").resolve(fileName2), - TestAppObject.class); + TestAppObject.class, TranslationEngineType.CUSTOM); }); assertEquals(CoreTranslationError.INVALID_OUTPUT_PATH, contractException.getErrorType()); @@ -592,7 +598,7 @@ public void testAddOutputFilePath() { @Test @UnitTestMethod(target = TranslationController.Builder.class, name = "addOutputFilePath", args = { Path.class, - Class.class, Integer.class }) + Class.class, Integer.class, TranslationEngineType.class }) public void testAddOutputFilePath_ScenarioId() { // Tested by testAddOutputFilePath, which internally calls // addOutputFilePath(path, class, 0) @@ -626,36 +632,33 @@ public void testAddParentChildClassRelationship() { } @Test - @UnitTestMethod(target = TranslationController.Builder.class, name = "addTranslator", args = { Translator.class }) - public void testAddTranslator() { - TranslationController.builder().addTranslator(TestObjectTranslator.getTranslator()); + @UnitTestMethod(target = TranslationController.Builder.class, name = "addTranslationEngine", args = { + TranslationEngine.class }) + public void testAddTransationEngine() { + TestTranslationEngine translationEngine = TestTranslationEngine.builder() + .addTranslator(TestObjectTranslator.getTranslator()) + .addTranslator(TestComplexObjectTranslator.getTranslator()) + .addParentChildClassRelationship(TestAppObject.class, Object.class).build(); + + TranslationController.builder().addTranslationEngine(translationEngine).build(); // preconditions ContractException contractException = assertThrows(ContractException.class, () -> { - TranslationController.builder().addTranslator(null); + TranslationController.builder().addTranslationEngine(null); }); - assertEquals(CoreTranslationError.NULL_TRANSLATOR, contractException.getErrorType()); + assertEquals(CoreTranslationError.NULL_TRANSLATION_ENGINE, contractException.getErrorType()); contractException = assertThrows(ContractException.class, () -> { - TranslationController.builder().addTranslator(TestObjectTranslator.getTranslator()) - .addTranslator(TestObjectTranslator.getTranslator()); - }); - - assertEquals(CoreTranslationError.DUPLICATE_TRANSLATOR, contractException.getErrorType()); - } + TestTranslationEngine translationEngine2 = TestTranslationEngine.builder() + .addTranslator(TestObjectTranslator.getTranslator()) + .addTranslator(TestComplexObjectTranslator.getTranslator()) + .addParentChildClassRelationship(TestAppObject.class, Object.class).build(); - @Test - @UnitTestMethod(target = TranslationController.Builder.class, name = "setTranslationEngineBuilder", args = { - TranslationEngine.Builder.class }) - public void testSetTransationEngineBuilder() { - TranslationController.builder().setTranslationEngineBuilder(TestTranslationEngine.builder()); - - // preconditions - ContractException contractException = assertThrows(ContractException.class, () -> { - TranslationController.builder().setTranslationEngineBuilder(null); + TranslationController.builder().addParentChildClassRelationship(TestAppObject.class, Object.class) + .addTranslationEngine(translationEngine2); }); - assertEquals(CoreTranslationError.NULL_TRANSLATION_ENGINE_BUILDER, contractException.getErrorType()); + assertEquals(CoreTranslationError.DUPLICATE_CLASSREF, contractException.getErrorType()); } } diff --git a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslationEngine.java b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslationEngine.java index 334a65c..5c4c7e1 100644 --- a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslationEngine.java +++ b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslationEngine.java @@ -4,10 +4,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; @@ -15,10 +18,14 @@ import gov.hhs.aspr.ms.taskit.core.testsupport.TestObjectUtil; import gov.hhs.aspr.ms.taskit.core.testsupport.TestTranslationEngine; import gov.hhs.aspr.ms.taskit.core.testsupport.testcomplexobject.TestComplexAppObject; +import gov.hhs.aspr.ms.taskit.core.testsupport.testcomplexobject.TestComplexObjectTranslator; +import gov.hhs.aspr.ms.taskit.core.testsupport.testcomplexobject.TestComplexObjectTranslatorId; import gov.hhs.aspr.ms.taskit.core.testsupport.testcomplexobject.input.TestComplexInputObject; import gov.hhs.aspr.ms.taskit.core.testsupport.testcomplexobject.translationSpecs.TestComplexObjectTranslationSpec; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestAppChildObject; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestAppObject; +import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestObjectTranslator; +import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestObjectTranslatorId; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestObjectWrapper; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.input.TestInputChildObject; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.input.TestInputObject; @@ -26,35 +33,49 @@ import util.annotations.UnitTestForCoverage; import util.annotations.UnitTestMethod; import util.errors.ContractException; +import util.graph.MutableGraph; public class AT_TranslationEngine { @Test - @UnitTestMethod(target = TranslationEngine.class, name = "init", args = {}) - public void testInit() { + @UnitTestMethod(target = TranslationEngine.class, name = "isInitialized", args = {}) + public void testIsInitialized() { TestObjectTranslationSpec testObjectTranslationSpec = new TestObjectTranslationSpec(); TestComplexObjectTranslationSpec testComplexObjectTranslationSpec = new TestComplexObjectTranslationSpec(); TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(testComplexObjectTranslationSpec) - .build(); + .buildWithoutSpecInit(); - testTranslationEngine.init(); + assertFalse(testTranslationEngine.isInitialized); + + testTranslationEngine.initTranslationSpecs(); + assertTrue(testTranslationEngine.isInitialized()); } @Test - @UnitTestMethod(target = TranslationEngine.class, name = "isInitialized", args = {}) - public void testIsInitialized() { - TestObjectTranslationSpec testObjectTranslationSpec = new TestObjectTranslationSpec(); - TestComplexObjectTranslationSpec testComplexObjectTranslationSpec = new TestComplexObjectTranslationSpec(); - TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() - .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(testComplexObjectTranslationSpec) - .build(); + @UnitTestForCoverage + public void testValidateTranslationEngineType() { + // preconditions + // TranslationEngineType is set to UNKNOWN + ContractException contractException = assertThrows(ContractException.class, () -> { + TranslationEngine engine = TestTranslationEngine.builder().buildWithUnknownType(); - assertFalse(testTranslationEngine.isInitialized); + engine.validateInit(); + }); - testTranslationEngine.init(); + assertEquals(CoreTranslationError.UNKNWON_TRANSLATION_ENGINE_TYPE, contractException.getErrorType()); + } - assertTrue(testTranslationEngine.isInitialized()); + @Test + @UnitTestMethod(target = TranslationEngine.class, name = "getTranslationEngineType", args = {}) + public void testGetTranslationEngineType() { + TranslationEngine translationEngine = TestTranslationEngine.builder().build(); + + assertEquals(TranslationEngineType.CUSTOM, translationEngine.getTranslationEngineType()); + + translationEngine = TestTranslationEngine.builder().buildWithUnknownType(); + + assertEquals(TranslationEngineType.UNKNOWN, translationEngine.getTranslationEngineType()); } @Test @@ -66,8 +87,6 @@ public void testTranslationSpecsAreInitialized() { .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(testComplexObjectTranslationSpec) .build(); - testTranslationEngine.init(); - assertDoesNotThrow(() -> testTranslationEngine.translationSpecsAreInitialized()); // preconditions @@ -75,7 +94,7 @@ public void testTranslationSpecsAreInitialized() { assertThrows(RuntimeException.class, () -> { TestTranslationEngine testTranslationEngine2 = TestTranslationEngine.builder() .addTranslationSpec(new TestObjectTranslationSpec()) - .addTranslationSpec(testComplexObjectTranslationSpec).build(); + .addTranslationSpec(testComplexObjectTranslationSpec).buildWithoutSpecInit(); testTranslationEngine2.translationSpecsAreInitialized(); }); @@ -96,6 +115,26 @@ public void testGetTranslationSpecs() { assertTrue(actualTranslationSpecs.contains(testComplexObjectTranslationSpec)); } + @Test + @UnitTestForCoverage + public void testValidateTranslatorsInitialized() { + + assertDoesNotThrow(() -> { + TestTranslationEngine.builder().addTranslator(TestObjectTranslator.getTranslator()) + .addTranslator(TestComplexObjectTranslator.getTranslator()).build(); + + }); + + // preconditions + ContractException contractException = assertThrows(ContractException.class, () -> { + TranslationEngine engine = TestTranslationEngine.builder() + .addTranslator(TestObjectTranslator.getTranslator()) + .addTranslator(TestComplexObjectTranslator.getTranslator()).buildWithNoTranslatorInit(); + engine.validateInit(); + }); + assertEquals(CoreTranslationError.UNINITIALIZED_TRANSLATORS, contractException.getErrorType()); + } + @Test @UnitTestMethod(target = TranslationEngine.class, name = "convertObject", args = { Object.class }) public void testConvertObject() { @@ -105,8 +144,6 @@ public void testConvertObject() { .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(testComplexObjectTranslationSpec) .build(); - testTranslationEngine.init(); - TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); TestInputObject expectedInputObject = TestObjectUtil.getInputFromApp(expectedAppObject); @@ -138,7 +175,6 @@ public void testConvertObjectAsSafeClass() { .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(testComplexObjectTranslationSpec) .build(); - testTranslationEngine.init(); TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); TestInputObject expectedInputObject = TestObjectUtil.getInputFromApp(expectedAppObject); @@ -211,8 +247,6 @@ public Class getInputObjectClass() { .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(testComplexObjectTranslationSpec) .addTranslationSpec(wrapperTranslationSpec).build(); - testTranslationEngine.init(); - TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); TestObjectWrapper expectedWrapper = new TestObjectWrapper(); @@ -255,8 +289,6 @@ public void testGetTranslationSpecForClass() { .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(testComplexObjectTranslationSpec) .build(); - testTranslationEngine.init(); - assertEquals(testObjectTranslationSpec, testTranslationEngine.getTranslationSpecForClass(TestAppObject.class)); assertEquals(testObjectTranslationSpec, testTranslationEngine.getTranslationSpecForClass(TestInputObject.class)); @@ -286,138 +318,73 @@ public void testGetTranslationSpecForClass() { } @Test - @UnitTestMethod(target = TranslationEngine.class, name = "builder", args = {}) - public void testBuilder() { - assertNotNull(TranslationEngine.builder()); - } - - @Test - @UnitTestMethod(target = TranslationEngine.Builder.class, name = "build", args = {}) - public void testBuild() { - assertThrows(RuntimeException.class, () -> { - TranslationEngine.builder().build(); - }); - } + @UnitTestForCoverage + public void testGetOrderedTranslators() { - @Test - @UnitTestMethod(target = TranslationEngine.Builder.class, name = "addTranslationSpec", args = { - TranslationSpec.class }) - public void testAddTranslationSpec() { - TestObjectTranslationSpec testObjectTranslationSpec = new TestObjectTranslationSpec(); - TestComplexObjectTranslationSpec testComplexObjectTranslationSpec = new TestComplexObjectTranslationSpec(); - TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() - .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(testComplexObjectTranslationSpec) - .build(); + TranslationEngine.Builder translationEngineBuilder = TestTranslationEngine.builder() + .addTranslator(TestObjectTranslator.getTranslator()) + .addTranslator(TestComplexObjectTranslator.getTranslator()); - testTranslationEngine.init(); + List expectedList = new ArrayList<>(); + expectedList.add(TestComplexObjectTranslator.getTranslator()); + expectedList.add(TestObjectTranslator.getTranslator()); - // show that the translation specs are retrievable by their own app and input - // classes - assertEquals(testObjectTranslationSpec, - testTranslationEngine.getTranslationSpecForClass(testObjectTranslationSpec.getAppObjectClass())); - assertEquals(testObjectTranslationSpec, - testTranslationEngine.getTranslationSpecForClass(testObjectTranslationSpec.getInputObjectClass())); + List actualList = translationEngineBuilder.getOrderedTranslators(); - assertEquals(testComplexObjectTranslationSpec, - testTranslationEngine.getTranslationSpecForClass(testComplexObjectTranslationSpec.getAppObjectClass())); - assertEquals(testComplexObjectTranslationSpec, testTranslationEngine - .getTranslationSpecForClass(testComplexObjectTranslationSpec.getInputObjectClass())); + assertEquals(expectedList, actualList); // preconditions - // translationSpec is null - ContractException contractException = assertThrows(ContractException.class, () -> { - TestTranslationEngine.builder().addTranslationSpec(null); - }); - - assertEquals(CoreTranslationError.NULL_TRANSLATION_SPEC, contractException.getErrorType()); - - // the translation spec getAppClass method returns null - contractException = assertThrows(ContractException.class, () -> { - TranslationSpec wrapperTranslationSpec = new TranslationSpec() { - - @Override - protected Object convertInputObject(TestObjectWrapper inputObject) { - return inputObject.getWrappedObject(); - } - @Override - protected TestObjectWrapper convertAppObject(Object appObject) { - TestObjectWrapper objectWrapper = new TestObjectWrapper(); + // duplicate translator in the graph - objectWrapper.setWrappedObject(appObject); - - return objectWrapper; - } - - @Override - public Class getAppObjectClass() { - return null; - } - - @Override - public Class getInputObjectClass() { - return TestObjectWrapper.class; - } - }; - TestTranslationEngine.builder().addTranslationSpec(wrapperTranslationSpec); + ContractException contractException = assertThrows(ContractException.class, () -> { + MutableGraph mutableGraph = new MutableGraph<>(); + Map translatorMap = new LinkedHashMap<>(); + mutableGraph.addNode(TestObjectTranslatorId.TRANSLATOR_ID); + translationEngineBuilder.addNodes(mutableGraph, translatorMap); }); - assertEquals(CoreTranslationError.NULL_TRANSLATION_SPEC_APP_CLASS, contractException.getErrorType()); + assertEquals(CoreTranslationError.DUPLICATE_TRANSLATOR, contractException.getErrorType()); - // the translation spec getInputClass method returns null + // missing translator contractException = assertThrows(ContractException.class, () -> { - TranslationSpec wrapperTranslationSpec = new TranslationSpec() { - - @Override - protected Object convertInputObject(TestObjectWrapper inputObject) { - return inputObject.getWrappedObject(); - } - - @Override - protected TestObjectWrapper convertAppObject(Object appObject) { - TestObjectWrapper objectWrapper = new TestObjectWrapper(); - - objectWrapper.setWrappedObject(appObject); - - return objectWrapper; - } - - @Override - public Class getAppObjectClass() { - return Object.class; - } - - @Override - public Class getInputObjectClass() { - return null; - } + MutableGraph mutableGraph = new MutableGraph<>(); + Map translatorMap = new LinkedHashMap<>(); + + // call normally + translationEngineBuilder.getOrderedTranslators(mutableGraph, translatorMap); + // remove a mapping + translatorMap.remove(TestComplexObjectTranslatorId.TRANSLATOR_ID); + TranslatorId thirdId = new TranslatorId() { }; - TestTranslationEngine.builder().addTranslationSpec(wrapperTranslationSpec); - }); - - assertEquals(CoreTranslationError.NULL_TRANSLATION_SPEC_INPUT_CLASS, contractException.getErrorType()); - - // if the translation spec has already been added (same, but different - // instances) - contractException = assertThrows(ContractException.class, () -> { - TestObjectTranslationSpec testObjectTranslationSpec1 = new TestObjectTranslationSpec(); - TestObjectTranslationSpec testObjectTranslationSpec2 = new TestObjectTranslationSpec(); - - TestTranslationEngine.builder().addTranslationSpec(testObjectTranslationSpec1) - .addTranslationSpec(testObjectTranslationSpec2); + mutableGraph.addNode(thirdId); + mutableGraph.addEdge(new Object(), thirdId, TestComplexObjectTranslatorId.TRANSLATOR_ID); + translationEngineBuilder.checkForMissingTranslators(mutableGraph, translatorMap); }); - assertEquals(CoreTranslationError.DUPLICATE_TRANSLATION_SPEC, contractException.getErrorType()); + assertEquals(CoreTranslationError.MISSING_TRANSLATOR, contractException.getErrorType()); - // if the translation spec has already been added (exact same instance) + // cyclic graph contractException = assertThrows(ContractException.class, () -> { - TestObjectTranslationSpec testObjectTranslationSpec1 = new TestObjectTranslationSpec(); - - TestTranslationEngine.builder().addTranslationSpec(testObjectTranslationSpec1) - .addTranslationSpec(testObjectTranslationSpec1); + MutableGraph mutableGraph = new MutableGraph<>(); + Map translatorMap = new LinkedHashMap<>(); + + // call normally + translationEngineBuilder.getOrderedTranslators(mutableGraph, translatorMap); + mutableGraph.addEdge(new Object(), TestComplexObjectTranslatorId.TRANSLATOR_ID, + TestObjectTranslatorId.TRANSLATOR_ID); + TranslatorId thirdId = new TranslatorId() { + }; + TranslatorId fourthId = new TranslatorId() { + }; + mutableGraph.addNode(thirdId); + mutableGraph.addNode(fourthId); + mutableGraph.addEdge(new Object(), thirdId, fourthId); + mutableGraph.addEdge(new Object(), fourthId, thirdId); + translationEngineBuilder.checkForCyclicGraph(mutableGraph); }); - assertEquals(CoreTranslationError.DUPLICATE_TRANSLATION_SPEC, contractException.getErrorType()); + assertEquals(CoreTranslationError.CIRCULAR_TRANSLATOR_DEPENDENCIES, contractException.getErrorType()); } @Test @@ -452,12 +419,7 @@ public void testHashCode() { assertNotEquals(testTranslationEngine2.hashCode(), testTranslationEngine4.hashCode()); assertNotEquals(testTranslationEngine3.hashCode(), testTranslationEngine4.hashCode()); - // same translation specs, but initialized vs not - testTranslationEngine5.init(); - assertNotEquals(testTranslationEngine1.hashCode(), testTranslationEngine5.hashCode()); - - // same translation specs and both initialized - testTranslationEngine1.init(); + // same translation specs assertEquals(testTranslationEngine1.hashCode(), testTranslationEngine5.hashCode()); } @@ -486,10 +448,10 @@ public void testEquals() { TestObjectTranslationSpec testObjectTranslationSpec3 = new TestObjectTranslationSpec(); TestTranslationEngine testTranslationEngine6 = TestTranslationEngine.builder() - .addTranslationSpec(testObjectTranslationSpec2).build(); + .addTranslationSpec(testObjectTranslationSpec2).buildWithoutSpecInit(); TestTranslationEngine testTranslationEngine7 = TestTranslationEngine.builder() - .addTranslationSpec(testObjectTranslationSpec3).build(); + .addTranslationSpec(testObjectTranslationSpec3).buildWithoutSpecInit(); // exact same assertEquals(testTranslationEngine1, testTranslationEngine1); @@ -510,12 +472,10 @@ public void testEquals() { testObjectTranslationSpec3.init(testTranslationEngine5); assertNotEquals(testTranslationEngine6, testTranslationEngine7); - // same translation specs, but initialized vs not - testTranslationEngine5.init(); - assertNotEquals(testTranslationEngine1, testTranslationEngine5); + // init vs not init + assertNotEquals(testTranslationEngine1, testTranslationEngine6); - // same translation specs and both initialized - testTranslationEngine1.init(); + // same translation specs assertEquals(testTranslationEngine1, testTranslationEngine5); TranslationEngine.Data data = new TranslationEngine.Data(); diff --git a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslatorContext.java b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslatorContext.java index 1ce3a7a..e065282 100644 --- a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslatorContext.java +++ b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/AT_TranslatorContext.java @@ -8,7 +8,6 @@ import org.junit.jupiter.api.Test; import gov.hhs.aspr.ms.taskit.core.testsupport.TestTranslationEngine; -import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestAppObject; import util.annotations.UnitTestConstructor; import util.annotations.UnitTestMethod; import util.errors.ContractException; @@ -16,13 +15,9 @@ public class AT_TranslatorContext { @Test - @UnitTestConstructor(target = TranslatorContext.class, args = { TranslationController.class }) + @UnitTestConstructor(target = TranslatorContext.class, args = { TranslationEngine.Builder.class }) public void testConstructor() { - TestTranslationEngine.Builder expectedBuilder = TestTranslationEngine.builder(); - TranslationController translationController = TranslationController.builder() - .setTranslationEngineBuilder(expectedBuilder).build(); - - TranslatorContext translatorContext = new TranslatorContext(translationController); + TranslatorContext translatorContext = new TranslatorContext(TestTranslationEngine.builder()); assertNotNull(translatorContext); } @@ -31,10 +26,10 @@ public void testConstructor() { @UnitTestMethod(target = TranslatorContext.class, name = "getTranslationEngineBuilder", args = { Class.class }) public void testGetTranslationEngineBuilder() { TestTranslationEngine.Builder expectedBuilder = TestTranslationEngine.builder(); - TranslationController translationController = TranslationController.builder() - .setTranslationEngineBuilder(expectedBuilder).buildWithoutInitAndChecks(); + + TranslatorContext translatorContext = new TranslatorContext(expectedBuilder); - TestTranslationEngine.Builder actualBuilder = translationController + TestTranslationEngine.Builder actualBuilder = translatorContext .getTranslationEngineBuilder(TestTranslationEngine.Builder.class); assertTrue(expectedBuilder == actualBuilder); @@ -42,49 +37,10 @@ public void testGetTranslationEngineBuilder() { // invalid class ref ContractException contractException = assertThrows(ContractException.class, () -> { - translationController.getTranslationEngineBuilder(TranslationEngine.Builder.class); + translatorContext.getTranslationEngineBuilder(TranslationEngine.Builder.class); }); - assertEquals(CoreTranslationError.INVALID_TRANSLATION_ENGINE_BUILDER, contractException.getErrorType()); - - assertThrows(RuntimeException.class, () -> { - TranslationController translationController2 = TranslationController.builder() - .setTranslationEngineBuilder(expectedBuilder).build(); - - translationController2.getTranslationEngineBuilder(TranslationEngine.Builder.class); - }); + assertEquals(CoreTranslationError.INVALID_TRANSLATION_ENGINE_BUILDER_CLASS_REF, contractException.getErrorType()); } - @Test - @UnitTestMethod(target = TranslatorContext.class, name = "addParentChildClassRelationship", args = { Class.class, - Class.class }) - public void testAddParentChildClassRelationship() { - TestTranslationEngine.Builder expectedBuilder = TestTranslationEngine.builder(); - TranslationController translationController = TranslationController.builder() - .setTranslationEngineBuilder(expectedBuilder).build(); - - TranslatorContext translatorContext = new TranslatorContext(translationController); - - translatorContext.addParentChildClassRelationship(TestAppObject.class, Object.class); - - // preconditions - ContractException contractException = assertThrows(ContractException.class, () -> { - translatorContext.addParentChildClassRelationship(null, Object.class); - }); - - assertEquals(CoreTranslationError.NULL_CLASS_REF, contractException.getErrorType()); - - contractException = assertThrows(ContractException.class, () -> { - translatorContext.addParentChildClassRelationship(TestAppObject.class, null); - }); - - assertEquals(CoreTranslationError.NULL_CLASS_REF, contractException.getErrorType()); - - contractException = assertThrows(ContractException.class, () -> { - translatorContext.addParentChildClassRelationship(TestAppObject.class, Object.class); - translatorContext.addParentChildClassRelationship(TestAppObject.class, Object.class); - }); - - assertEquals(CoreTranslationError.DUPLICATE_CLASSREF, contractException.getErrorType()); - } } diff --git a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/TranslationEngineTestHelper.java b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/TranslationEngineTestHelper.java new file mode 100644 index 0000000..43c3ad2 --- /dev/null +++ b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/TranslationEngineTestHelper.java @@ -0,0 +1,176 @@ +package gov.hhs.aspr.ms.taskit.core; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import gov.hhs.aspr.ms.taskit.core.testsupport.testcomplexobject.translationSpecs.TestComplexObjectTranslationSpec; +import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestAppObject; +import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestObjectTranslator; +import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestObjectWrapper; +import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.translationSpecs.TestObjectTranslationSpec; +import util.errors.ContractException; + +public final class TranslationEngineTestHelper { + private TranslationEngineTestHelper() { + } + + public static void testAddTranslationSpec(TranslationEngine.Builder builder) { + TestObjectTranslationSpec testObjectTranslationSpec = new TestObjectTranslationSpec(); + TestComplexObjectTranslationSpec testComplexObjectTranslationSpec = new TestComplexObjectTranslationSpec(); + TranslationEngine testTranslationEngine = builder.addTranslationSpec(testObjectTranslationSpec) + .addTranslationSpec(testComplexObjectTranslationSpec).build(); + + // show that the translation specs are retrievable by their own app and input + // classes + assertEquals(testObjectTranslationSpec, + testTranslationEngine.getTranslationSpecForClass(testObjectTranslationSpec.getAppObjectClass())); + assertEquals(testObjectTranslationSpec, + testTranslationEngine.getTranslationSpecForClass(testObjectTranslationSpec.getInputObjectClass())); + + assertEquals(testComplexObjectTranslationSpec, + testTranslationEngine.getTranslationSpecForClass(testComplexObjectTranslationSpec.getAppObjectClass())); + assertEquals(testComplexObjectTranslationSpec, testTranslationEngine + .getTranslationSpecForClass(testComplexObjectTranslationSpec.getInputObjectClass())); + + builder.clearBuilder(); + // preconditions + // translationSpec is null + ContractException contractException = assertThrows(ContractException.class, () -> { + builder.addTranslationSpec(null); + }); + + assertEquals(CoreTranslationError.NULL_TRANSLATION_SPEC, contractException.getErrorType()); + + builder.clearBuilder(); + // the translation spec getAppClass method returns null + contractException = assertThrows(ContractException.class, () -> { + TranslationSpec wrapperTranslationSpec = new TranslationSpec() { + + @Override + protected Object convertInputObject(TestObjectWrapper inputObject) { + return inputObject.getWrappedObject(); + } + + @Override + protected TestObjectWrapper convertAppObject(Object appObject) { + TestObjectWrapper objectWrapper = new TestObjectWrapper(); + + objectWrapper.setWrappedObject(appObject); + + return objectWrapper; + } + + @Override + public Class getAppObjectClass() { + return null; + } + + @Override + public Class getInputObjectClass() { + return TestObjectWrapper.class; + } + }; + builder.addTranslationSpec(wrapperTranslationSpec); + }); + + assertEquals(CoreTranslationError.NULL_TRANSLATION_SPEC_APP_CLASS, contractException.getErrorType()); + + builder.clearBuilder(); + // the translation spec getInputClass method returns null + contractException = assertThrows(ContractException.class, () -> { + TranslationSpec wrapperTranslationSpec = new TranslationSpec() { + + @Override + protected Object convertInputObject(TestObjectWrapper inputObject) { + return inputObject.getWrappedObject(); + } + + @Override + protected TestObjectWrapper convertAppObject(Object appObject) { + TestObjectWrapper objectWrapper = new TestObjectWrapper(); + + objectWrapper.setWrappedObject(appObject); + + return objectWrapper; + } + + @Override + public Class getAppObjectClass() { + return Object.class; + } + + @Override + public Class getInputObjectClass() { + return null; + } + }; + builder.addTranslationSpec(wrapperTranslationSpec); + }); + + assertEquals(CoreTranslationError.NULL_TRANSLATION_SPEC_INPUT_CLASS, contractException.getErrorType()); + + builder.clearBuilder(); + // if the translation spec has already been added (same, but different + // instances) + contractException = assertThrows(ContractException.class, () -> { + TestObjectTranslationSpec testObjectTranslationSpec1 = new TestObjectTranslationSpec(); + TestObjectTranslationSpec testObjectTranslationSpec2 = new TestObjectTranslationSpec(); + + builder.addTranslationSpec(testObjectTranslationSpec1).addTranslationSpec(testObjectTranslationSpec2); + }); + + assertEquals(CoreTranslationError.DUPLICATE_TRANSLATION_SPEC, contractException.getErrorType()); + + builder.clearBuilder(); + // if the translation spec has already been added (exact same instance) + contractException = assertThrows(ContractException.class, () -> { + TestObjectTranslationSpec testObjectTranslationSpec1 = new TestObjectTranslationSpec(); + + builder.addTranslationSpec(testObjectTranslationSpec1).addTranslationSpec(testObjectTranslationSpec1); + }); + + assertEquals(CoreTranslationError.DUPLICATE_TRANSLATION_SPEC, contractException.getErrorType()); + } + + public static void testAddTranslator(TranslationEngine.Builder builder) { + builder.addTranslator(TestObjectTranslator.getTranslator()); + + // preconditions + ContractException contractException = assertThrows(ContractException.class, () -> { + builder.addTranslator(null); + }); + + assertEquals(CoreTranslationError.NULL_TRANSLATOR, contractException.getErrorType()); + + contractException = assertThrows(ContractException.class, () -> { + builder.addTranslator(TestObjectTranslator.getTranslator()) + .addTranslator(TestObjectTranslator.getTranslator()); + }); + + assertEquals(CoreTranslationError.DUPLICATE_TRANSLATOR, contractException.getErrorType()); + } + + public static void testAddParentChildClassRelationship(TranslationEngine.Builder builder) { + builder.addParentChildClassRelationship(TestAppObject.class, Object.class); + + // preconditions + ContractException contractException = assertThrows(ContractException.class, () -> { + builder.addParentChildClassRelationship(null, Object.class); + }); + + assertEquals(CoreTranslationError.NULL_CLASS_REF, contractException.getErrorType()); + + contractException = assertThrows(ContractException.class, () -> { + builder.addParentChildClassRelationship(TestAppObject.class, null); + }); + + assertEquals(CoreTranslationError.NULL_CLASS_REF, contractException.getErrorType()); + + contractException = assertThrows(ContractException.class, () -> { + builder.addParentChildClassRelationship(TestAppObject.class, Object.class) + .addParentChildClassRelationship(TestAppObject.class, Object.class); + }); + + assertEquals(CoreTranslationError.DUPLICATE_CLASSREF, contractException.getErrorType()); + } +} diff --git a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/AT_TestTranslationEngine.java b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/AT_TestTranslationEngine.java index c82174b..5df8582 100644 --- a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/AT_TestTranslationEngine.java +++ b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/AT_TestTranslationEngine.java @@ -2,11 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.io.Writer; @@ -15,7 +11,10 @@ import org.junit.jupiter.api.Test; +import gov.hhs.aspr.ms.taskit.core.TranslationEngineTestHelper; +import gov.hhs.aspr.ms.taskit.core.TranslationEngineType; import gov.hhs.aspr.ms.taskit.core.TranslationSpec; +import gov.hhs.aspr.ms.taskit.core.Translator; import gov.hhs.aspr.ms.taskit.core.testsupport.testcomplexobject.translationSpecs.TestComplexObjectTranslationSpec; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestAppObject; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.input.TestInputObject; @@ -41,37 +40,16 @@ public void testWriteOutput() throws IOException { TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(complexObjectTranslationSpec).build(); - testTranslationEngine.init(); - TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); - FileWriter fileWriter = new FileWriter(filePath.resolve(fileName).toFile()); - FileReader fileReader = new FileReader(filePath.resolve(fileName).toFile()); - - FileWriter fileWriter2 = new FileWriter(filePath.resolve(fileName2).toFile()); - FileReader fileReader2 = new FileReader(filePath.resolve(fileName2).toFile()); - - testTranslationEngine.writeOutput(fileWriter, expectedAppObject, Optional.empty()); - TestAppObject actualAppObject = testTranslationEngine.readInput(fileReader, TestInputObject.class); + testTranslationEngine.writeOutput(filePath.resolve(fileName), expectedAppObject, Optional.empty()); + TestAppObject actualAppObject = testTranslationEngine.readInput(filePath.resolve(fileName), TestInputObject.class); assertEquals(expectedAppObject, actualAppObject); - testTranslationEngine.writeOutput(fileWriter2, TestObjectUtil.getChildAppFromApp(expectedAppObject), + testTranslationEngine.writeOutput(filePath.resolve(fileName2), TestObjectUtil.getChildAppFromApp(expectedAppObject), Optional.of(TestAppObject.class)); - TestAppObject actualAppChildObject = testTranslationEngine.readInput(fileReader2, TestInputObject.class); + TestAppObject actualAppChildObject = testTranslationEngine.readInput(filePath.resolve(fileName2), TestInputObject.class); assertEquals(expectedAppObject, actualAppChildObject); - - // preconditions - // IO error occurs - RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> { - FileWriter fileWriter3 = new FileWriter(filePath.resolve(fileName).toFile()); - // close the file reader - fileWriter3.close(); - testTranslationEngine.writeOutput(fileWriter3, expectedAppObject, Optional.empty()); - }); - - assertTrue(runtimeException.getCause() instanceof IOException); - - filePath.resolve(fileName).toFile().setWritable(true); } @Test @@ -88,23 +66,15 @@ public void testReadInput() throws IOException { TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(complexObjectTranslationSpec).build(); - testTranslationEngine.init(); - TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); - FileWriter fileWriter = new FileWriter(filePath.resolve(fileName).toFile()); - FileReader fileReader = new FileReader(filePath.resolve(fileName).toFile()); - - FileWriter fileWriter2 = new FileWriter(filePath.resolve(fileName2).toFile()); - FileReader fileReader2 = new FileReader(filePath.resolve(fileName2).toFile()); - - testTranslationEngine.writeOutput(fileWriter, expectedAppObject, Optional.empty()); - TestAppObject actualAppObject = testTranslationEngine.readInput(fileReader, TestInputObject.class); + testTranslationEngine.writeOutput(filePath.resolve(fileName), expectedAppObject, Optional.empty()); + TestAppObject actualAppObject = testTranslationEngine.readInput(filePath.resolve(fileName), TestInputObject.class); assertEquals(expectedAppObject, actualAppObject); - testTranslationEngine.writeOutput(fileWriter2, TestObjectUtil.getChildAppFromApp(expectedAppObject), + testTranslationEngine.writeOutput(filePath.resolve(fileName2), TestObjectUtil.getChildAppFromApp(expectedAppObject), Optional.of(TestAppObject.class)); - TestAppObject actualAppChildObject = testTranslationEngine.readInput(fileReader2, TestInputObject.class); + TestAppObject actualAppChildObject = testTranslationEngine.readInput(filePath.resolve(fileName2), TestInputObject.class); assertEquals(expectedAppObject, actualAppChildObject); } @@ -120,13 +90,44 @@ public void testBuild() { assertNotNull(TestTranslationEngine.builder().build()); } + @Test + @UnitTestMethod(target = TestTranslationEngine.Builder.class, name = "buildWithNoTranslatorInit", args = {}) + public void testBuildWithNoTranslatorInit() { + assertNotNull(TestTranslationEngine.builder().buildWithNoTranslatorInit()); + } + + @Test + @UnitTestMethod(target = TestTranslationEngine.Builder.class, name = "buildWithUnknownType", args = {}) + public void testBuildWithUnknownType() { + assertNotNull(TestTranslationEngine.builder().buildWithUnknownType()); + + assertEquals(TranslationEngineType.UNKNOWN, TestTranslationEngine.builder().buildWithUnknownType().getTranslationEngineType()); + } + + @Test + @UnitTestMethod(target = TestTranslationEngine.Builder.class, name = "buildWithoutSpecInit", args = {}) + public void testBuildWithoutSpecInit() { + assertNotNull(TestTranslationEngine.builder().buildWithoutSpecInit()); + } + @Test @UnitTestMethod(target = TestTranslationEngine.Builder.class, name = "addTranslationSpec", args = { TranslationSpec.class }) public void testAddTranslationSpec() { - // covered by AT_TranslationEngine#testAddTranslationSpec - // this is just a wrapper method to ensure that the correct Child Engine builder - // is returned + TranslationEngineTestHelper.testAddTranslationSpec(TestTranslationEngine.builder()); + } + + @Test + @UnitTestMethod(target = TestTranslationEngine.Builder.class, name = "addTranslator", args = { Translator.class }) + public void testAddTranslator() { + TranslationEngineTestHelper.testAddTranslator(TestTranslationEngine.builder()); + } + + @Test + @UnitTestMethod(target = TestTranslationEngine.Builder.class, name = "addParentChildClassRelationship", args = { + Class.class, Class.class }) + public void testAddParentChildClassRelationship() { + TranslationEngineTestHelper.testAddParentChildClassRelationship(TestTranslationEngine.builder()); } @Test diff --git a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/testcomplexobject/translationSpecs/AT_TestComplexObjectTranslationSpec.java b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/testcomplexobject/translationSpecs/AT_TestComplexObjectTranslationSpec.java index 5c733cd..7cd9550 100644 --- a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/testcomplexobject/translationSpecs/AT_TestComplexObjectTranslationSpec.java +++ b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/testcomplexobject/translationSpecs/AT_TestComplexObjectTranslationSpec.java @@ -31,11 +31,9 @@ public void testConvertInputObject() { TestObjectTranslationSpec testObjectTranslationSpec = new TestObjectTranslationSpec(); TestComplexObjectTranslationSpec complexObjectTranslationSpec = new TestComplexObjectTranslationSpec(); - TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() + TestTranslationEngine.builder() .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(complexObjectTranslationSpec).build(); - testTranslationEngine.init(); - TestComplexAppObject actualComplexAppObject = complexObjectTranslationSpec .convertInputObject(testComplexInputObject); @@ -51,11 +49,9 @@ public void testConvertAppObject() { TestObjectTranslationSpec testObjectTranslationSpec = new TestObjectTranslationSpec(); TestComplexObjectTranslationSpec complexObjectTranslationSpec = new TestComplexObjectTranslationSpec(); - TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() + TestTranslationEngine.builder() .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(complexObjectTranslationSpec).build(); - testTranslationEngine.init(); - TestComplexInputObject actualComplexInputObject = complexObjectTranslationSpec .convertAppObject(testComplexAppObject); diff --git a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/testobject/translationSpecs/AT_TestObjectTranslationSpec.java b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/testobject/translationSpecs/AT_TestObjectTranslationSpec.java index 83ef5ea..f7b9280 100644 --- a/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/testobject/translationSpecs/AT_TestObjectTranslationSpec.java +++ b/core/src/test/java/gov/hhs/aspr/ms/taskit/core/testsupport/testobject/translationSpecs/AT_TestObjectTranslationSpec.java @@ -30,11 +30,9 @@ public void testConvertInputObject() { TestObjectTranslationSpec testObjectTranslationSpec = new TestObjectTranslationSpec(); TestComplexObjectTranslationSpec complexObjectTranslationSpec = new TestComplexObjectTranslationSpec(); - TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() + TestTranslationEngine.builder() .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(complexObjectTranslationSpec).build(); - testTranslationEngine.init(); - TestAppObject actualAppObject = testObjectTranslationSpec.convertInputObject(testInputObject); assertEquals(expectedAppObject, actualAppObject); @@ -48,11 +46,9 @@ public void testConvertAppObject() { TestObjectTranslationSpec testObjectTranslationSpec = new TestObjectTranslationSpec(); TestComplexObjectTranslationSpec complexObjectTranslationSpec = new TestComplexObjectTranslationSpec(); - TestTranslationEngine testTranslationEngine = TestTranslationEngine.builder() + TestTranslationEngine.builder() .addTranslationSpec(testObjectTranslationSpec).addTranslationSpec(complexObjectTranslationSpec).build(); - testTranslationEngine.init(); - TestInputObject actualInputObject = testObjectTranslationSpec.convertAppObject(testAppObject); assertEquals(expectedInputObject, actualInputObject); diff --git a/pom.xml b/pom.xml index 81520b8..3167464 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ - 2.4.1 + 3.0.0 UTF-8 diff --git a/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/PrimitiveTranslationSpecs.java b/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/PrimitiveTranslationSpecs.java index 76aab30..394f494 100644 --- a/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/PrimitiveTranslationSpecs.java +++ b/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/PrimitiveTranslationSpecs.java @@ -58,7 +58,7 @@ private PrimitiveTranslationSpecs() { * and their Descriptors are exclusively used to falicitate converting to/from a * Protobuf {@link Any} type */ - protected static Set getPrimitiveDescriptors() { + static Set getPrimitiveDescriptors() { Set set = new LinkedHashSet<>(); set.add(BoolValue.getDefaultInstance().getDescriptorForType()); @@ -82,7 +82,7 @@ protected static Set getPrimitiveDescriptors() { * are exclusively used to falicitate converting to/from a Protobuf {@link Any} * type */ - protected static Set> getPrimitiveTranslatorSpecs() { + static Set> getPrimitiveTranslatorSpecs() { Set> set = new LinkedHashSet<>(); set.addAll(getPrimitiveInputTranslatorSpecMap().values()); @@ -98,7 +98,7 @@ protected static Set getPrimitiveDescriptors() { * and their typeUrls are exclusively used to falicitate converting to/from a * Protobuf {@link Any} type */ - protected static Map> getPrimitiveTypeUrlToClassMap() { + static Map> getPrimitiveTypeUrlToClassMap() { Map> map = new LinkedHashMap<>(); map.put(BoolValue.getDefaultInstance().getDescriptorForType().getFullName(), @@ -133,7 +133,7 @@ protected static Map> getPrimitiveTypeUrlToClassMap() { * and their inputObjectClasses are exclusively used to falicitate converting * to/from a Protobuf {@link Any} type */ - protected static Map, ProtobufTranslationSpec> getPrimitiveInputTranslatorSpecMap() { + static Map, ProtobufTranslationSpec> getPrimitiveInputTranslatorSpecMap() { Map, ProtobufTranslationSpec> map = new LinkedHashMap<>(); map.put(BOOLEAN_TRANSLATOR_SPEC.getInputObjectClass(), BOOLEAN_TRANSLATOR_SPEC); @@ -159,7 +159,7 @@ protected static Map> getPrimitiveTypeUrlToClassMap() { * and their appObjectClasses are exclusively used to falicitate converting * to/from a Protobuf {@link Any} type */ - protected static Map, ProtobufTranslationSpec> getPrimitiveObjectTranslatorSpecMap() { + static Map, ProtobufTranslationSpec> getPrimitiveObjectTranslatorSpecMap() { Map, ProtobufTranslationSpec> map = new LinkedHashMap<>(); // no java version of unsigned int nor unsigned long diff --git a/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/ProtobufCoreTranslationError.java b/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/ProtobufCoreTranslationError.java index c043c89..3ec255c 100644 --- a/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/ProtobufCoreTranslationError.java +++ b/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/ProtobufCoreTranslationError.java @@ -3,11 +3,11 @@ import util.errors.ContractError; public enum ProtobufCoreTranslationError implements ContractError { - INVALID_READ_INPUT_CLASS_REF("The inputClassRef is not of the parent type: Message.class"), INVALID_INPUT_CLASS("The input class is neither a Protobuf Message, nor a Protobuf Enum"), + INVALID_READ_INPUT_CLASS_REF("The inputClassRef is not of the parent type: Message.class"), + INVALID_TRANSLATION_SPEC("Added Translation Specs need to be of parent type Protobuf TranslationSpecs"), UNKNOWN_TYPE_URL( - "The given type url does not have a corresponding classRef. Either the typeUrl was never provided, or the typeUrl is malformed."), - INVALID_TRANSLATION_SPEC("Added Translation Specs need to be of parent type Protobuf TranslationSpecs"); + "The given type url does not have a corresponding classRef. Either the typeUrl was never provided, or the typeUrl is malformed."); private final String description; diff --git a/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/ProtobufTranslationEngine.java b/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/ProtobufTranslationEngine.java index 028d2db..a796f78 100644 --- a/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/ProtobufTranslationEngine.java +++ b/protobuf/src/main/java/gov/hhs/aspr/ms/taskit/protobuf/ProtobufTranslationEngine.java @@ -1,10 +1,14 @@ package gov.hhs.aspr.ms.taskit.protobuf; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.nio.file.Path; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -13,13 +17,9 @@ import java.util.Optional; import java.util.Set; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.stream.JsonReader; import com.google.protobuf.Any; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; -import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; import com.google.protobuf.ProtocolMessageEnum; import com.google.protobuf.util.JsonFormat; @@ -29,7 +29,9 @@ import gov.hhs.aspr.ms.taskit.core.CoreTranslationError; import gov.hhs.aspr.ms.taskit.core.TranslationEngine; +import gov.hhs.aspr.ms.taskit.core.TranslationEngineType; import gov.hhs.aspr.ms.taskit.core.TranslationSpec; +import gov.hhs.aspr.ms.taskit.core.Translator; import gov.hhs.aspr.ms.taskit.protobuf.translationSpecs.AnyTranslationSpec; import util.errors.ContractException; @@ -37,7 +39,7 @@ * Protobuf TranslationEngine that allows for conversion between POJOs and * Protobuf Messages, extends {@link TranslationEngine} */ -public class ProtobufTranslationEngine extends TranslationEngine { +public final class ProtobufTranslationEngine extends TranslationEngine { private final Data data; private ProtobufTranslationEngine(Data data) { @@ -45,7 +47,7 @@ private ProtobufTranslationEngine(Data data) { this.data = data; } - private static class Data extends TranslationEngine.Data { + private final static class Data extends TranslationEngine.Data { // this is used specifically for Any message types to pack and unpack them private final Map> typeUrlToClassMap = new LinkedHashMap<>(); @@ -67,10 +69,11 @@ private Data() { this.classToTranslationSpecMap.putAll(PrimitiveTranslationSpecs.getPrimitiveInputTranslatorSpecMap()); this.classToTranslationSpecMap.putAll(PrimitiveTranslationSpecs.getPrimitiveObjectTranslatorSpecMap()); this.translationSpecs.addAll(PrimitiveTranslationSpecs.getPrimitiveTranslatorSpecs()); + this.translationEngineType = TranslationEngineType.PROTOBUF; } } - public static class Builder extends TranslationEngine.Builder { + public final static class Builder extends TranslationEngine.Builder { private ProtobufTranslationEngine.Data data; private Set descriptorSet = new LinkedHashSet<>(); private final Set defaultValueFieldsToPrint = new LinkedHashSet<>(); @@ -89,6 +92,8 @@ private Builder(ProtobufTranslationEngine.Data data) { */ @Override public ProtobufTranslationEngine build() { + initTranslators(); + TypeRegistry.Builder typeRegistryBuilder = TypeRegistry.newBuilder(); this.descriptorSet.addAll(PrimitiveTranslationSpecs.getPrimitiveDescriptors()); @@ -115,7 +120,11 @@ public ProtobufTranslationEngine build() { } this.data.jsonPrinter = printer; - return new ProtobufTranslationEngine(this.data); + ProtobufTranslationEngine translationEngine = new ProtobufTranslationEngine(this.data); + + translationEngine.initTranslationSpecs(); + + return translationEngine; } /** @@ -167,15 +176,30 @@ public Builder addFieldToIncludeDefaultValue(FieldDescriptor fieldDescriptor) { */ @Override public Builder addTranslationSpec(TranslationSpec translationSpec) { + _addTranslationSpec(translationSpec); + if (!ProtobufTranslationSpec.class.isAssignableFrom(translationSpec.getClass())) { throw new ContractException(ProtobufCoreTranslationError.INVALID_TRANSLATION_SPEC); } - super.addTranslationSpec(translationSpec); populate(translationSpec.getInputObjectClass()); return this; } + @Override + public Builder addTranslator(Translator translator) { + _addTranslator(translator); + + return this; + } + + @Override + public Builder addParentChildClassRelationship(Class classRef, Class markerInterface) { + _addParentChildClassRelationship(classRef, markerInterface); + + return this; + } + /** * checks the class to determine if it is a ProtocolMessageEnum or a Message and * if so, gets the Descriptor (which is akin to a class but for a Protobuf @@ -187,7 +211,7 @@ public Builder addTranslationSpec(TranslationSpec translationSpec) * {@linkplain Message} nor * {@linkplain ProtocolMessageEnum} */ - protected void populate(Class classRef) { + void populate(Class classRef) { String typeUrl; if (ProtocolMessageEnum.class.isAssignableFrom(classRef) && ProtocolMessageEnum.class != classRef) { typeUrl = getDefaultEnum(classRef.asSubclass(ProtocolMessageEnum.class)).getDescriptorForType() @@ -211,7 +235,7 @@ protected void populate(Class classRef) { /** * given a Class ref to a Protobuf Message, get the defaultInstance of it */ - protected U getDefaultMessage(Class classRef) { + U getDefaultMessage(Class classRef) { try { Method method = classRef.getMethod("getDefaultInstance"); Object obj = method.invoke(null); @@ -226,7 +250,7 @@ protected U getDefaultMessage(Class classRef) { * given a Class ref to a ProtocolMessageEnum, get the default value for it, * enum number 0 within the proto enum */ - protected U getDefaultEnum(Class classRef) { + U getDefaultEnum(Class classRef) { try { Method method = classRef.getMethod("forNumber", int.class); Object obj = method.invoke(null, 0); @@ -236,6 +260,7 @@ protected U getDefaultEnum(Class classRef) { throw new RuntimeException(e); } } + } /** @@ -245,15 +270,15 @@ public static Builder builder() { return new Builder(new Data()); } - protected Parser getJsonParser() { + Parser getJsonParser() { return this.data.jsonParser; } - protected Printer getJsonPrinter() { + Printer getJsonPrinter() { return this.data.jsonPrinter; } - protected void setDebug(boolean debug) { + void setDebug(boolean debug) { this.debug = debug; } @@ -268,7 +293,7 @@ protected void setDebug(boolean debug) { * @param the type of the appObject * @throws RuntimeException if there is an IOException during writing */ - protected void writeOutput(Writer writer, M appObject, Optional> superClass) { + protected void writeOutput(Path path, M appObject, Optional> superClass) throws IOException { Message message; if (Message.class.isAssignableFrom(appObject.getClass())) { message = Message.class.cast(appObject); @@ -277,7 +302,7 @@ protected void writeOutput(Writer writer, M appObject, Optional } else { message = convertObject(appObject); } - writeOutput(writer, message); + writeOutput(path, message); } /** @@ -291,15 +316,16 @@ protected void writeOutput(Writer writer, M appObject, Optional * @param the type of the Message * @throws RuntimeException if there is an IOException during writing */ - private void writeOutput(Writer writer, U message) { + private void writeOutput(Path path, U message) { try { - String messageToWrite = this.data.jsonPrinter.print(message); + Writer writer = new FileWriter(path.toFile()); + this.data.jsonPrinter.appendTo(message, writer); if (debug) { + String messageToWrite = this.data.jsonPrinter.print(message); printJsonToConsole(messageToWrite); } - writer.write(messageToWrite); writer.flush(); } catch (IOException e) { throw new RuntimeException(e); @@ -322,6 +348,7 @@ private void printJsonToConsole(String jsonString) { * * @param the type of the inputClass * @param the return type + * @throws FileNotFoundException * @throws RuntimeException *
    *
  • if there is an issue getting the builder method @@ -334,13 +361,12 @@ private void printJsonToConsole(String jsonString) { * if the given inputClassRef is not assingable from * {@linkplain Message} */ - protected T readInput(Reader reader, Class inputClassRef) { + protected T readInput(Path path, Class inputClassRef) throws IOException { if (!Message.class.isAssignableFrom(inputClassRef)) { throw new ContractException(ProtobufCoreTranslationError.INVALID_READ_INPUT_CLASS_REF); } - JsonObject jsonObject = JsonParser.parseReader(new JsonReader(reader)).getAsJsonObject(); - return parseJson(jsonObject, inputClassRef.asSubclass(Message.class)); + return parseJson(new FileReader(path.toFile()), inputClassRef.asSubclass(Message.class)); } /** @@ -362,13 +388,12 @@ protected T readInput(Reader reader, Class inputClassRef) { *
  • *
*/ - protected T parseJson(JsonObject inputJson, Class inputClassRef) { - JsonObject jsonObject = inputJson.deepCopy(); + T parseJson(Reader reader, Class inputClassRef) { Message.Builder builder = getBuilderForMessage(inputClassRef); try { - this.data.jsonParser.merge(jsonObject.toString(), builder); + this.data.jsonParser.merge(reader, builder); Message message = builder.build(); if (debug) { @@ -376,12 +401,12 @@ protected T parseJson(JsonObject inputJson, Class inpu } return convertObject(message); - } catch (InvalidProtocolBufferException e) { + } catch (IOException e) { throw new RuntimeException(e); } } - protected Message.Builder getBuilderForMessage(Class messageClass) { + Message.Builder getBuilderForMessage(Class messageClass) { Method[] messageMethods = messageClass.getDeclaredMethods(); diff --git a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/core/ProtobufTranslationEngineTestHelper.java b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/core/ProtobufTranslationEngineTestHelper.java new file mode 100644 index 0000000..0739a28 --- /dev/null +++ b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/core/ProtobufTranslationEngineTestHelper.java @@ -0,0 +1,176 @@ +package gov.hhs.aspr.ms.taskit.core; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestAppObject; +import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestObjectTranslator; +import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestObjectWrapper; +import gov.hhs.aspr.ms.taskit.protobuf.testsupport.testcomplexobject.translationSpecs.TestProtobufComplexObjectTranslationSpec; +import gov.hhs.aspr.ms.taskit.protobuf.testsupport.testobject.translationSpecs.TestProtobufObjectTranslationSpec; +import util.errors.ContractException; + +public final class ProtobufTranslationEngineTestHelper { + private ProtobufTranslationEngineTestHelper() { + } + + public static void testAddTranslationSpec(TranslationEngine.Builder builder) { + TestProtobufObjectTranslationSpec testObjectTranslationSpec = new TestProtobufObjectTranslationSpec(); + TestProtobufComplexObjectTranslationSpec testComplexObjectTranslationSpec = new TestProtobufComplexObjectTranslationSpec(); + TranslationEngine testTranslationEngine = builder.addTranslationSpec(testObjectTranslationSpec) + .addTranslationSpec(testComplexObjectTranslationSpec).build(); + + // show that the translation specs are retrievable by their own app and input + // classes + assertEquals(testObjectTranslationSpec, + testTranslationEngine.getTranslationSpecForClass(testObjectTranslationSpec.getAppObjectClass())); + assertEquals(testObjectTranslationSpec, + testTranslationEngine.getTranslationSpecForClass(testObjectTranslationSpec.getInputObjectClass())); + + assertEquals(testComplexObjectTranslationSpec, + testTranslationEngine.getTranslationSpecForClass(testComplexObjectTranslationSpec.getAppObjectClass())); + assertEquals(testComplexObjectTranslationSpec, testTranslationEngine + .getTranslationSpecForClass(testComplexObjectTranslationSpec.getInputObjectClass())); + + builder.clearBuilder(); + // preconditions + // translationSpec is null + ContractException contractException = assertThrows(ContractException.class, () -> { + builder.addTranslationSpec(null); + }); + + assertEquals(CoreTranslationError.NULL_TRANSLATION_SPEC, contractException.getErrorType()); + + builder.clearBuilder(); + // the translation spec getAppClass method returns null + contractException = assertThrows(ContractException.class, () -> { + TranslationSpec wrapperTranslationSpec = new TranslationSpec() { + + @Override + protected Object convertInputObject(TestObjectWrapper inputObject) { + return inputObject.getWrappedObject(); + } + + @Override + protected TestObjectWrapper convertAppObject(Object appObject) { + TestObjectWrapper objectWrapper = new TestObjectWrapper(); + + objectWrapper.setWrappedObject(appObject); + + return objectWrapper; + } + + @Override + public Class getAppObjectClass() { + return null; + } + + @Override + public Class getInputObjectClass() { + return TestObjectWrapper.class; + } + }; + builder.addTranslationSpec(wrapperTranslationSpec); + }); + + assertEquals(CoreTranslationError.NULL_TRANSLATION_SPEC_APP_CLASS, contractException.getErrorType()); + + builder.clearBuilder(); + // the translation spec getInputClass method returns null + contractException = assertThrows(ContractException.class, () -> { + TranslationSpec wrapperTranslationSpec = new TranslationSpec() { + + @Override + protected Object convertInputObject(TestObjectWrapper inputObject) { + return inputObject.getWrappedObject(); + } + + @Override + protected TestObjectWrapper convertAppObject(Object appObject) { + TestObjectWrapper objectWrapper = new TestObjectWrapper(); + + objectWrapper.setWrappedObject(appObject); + + return objectWrapper; + } + + @Override + public Class getAppObjectClass() { + return Object.class; + } + + @Override + public Class getInputObjectClass() { + return null; + } + }; + builder.addTranslationSpec(wrapperTranslationSpec); + }); + + assertEquals(CoreTranslationError.NULL_TRANSLATION_SPEC_INPUT_CLASS, contractException.getErrorType()); + + builder.clearBuilder(); + // if the translation spec has already been added (same, but different + // instances) + contractException = assertThrows(ContractException.class, () -> { + TestProtobufObjectTranslationSpec testObjectTranslationSpec1 = new TestProtobufObjectTranslationSpec(); + TestProtobufObjectTranslationSpec testObjectTranslationSpec2 = new TestProtobufObjectTranslationSpec(); + + builder.addTranslationSpec(testObjectTranslationSpec1).addTranslationSpec(testObjectTranslationSpec2); + }); + + assertEquals(CoreTranslationError.DUPLICATE_TRANSLATION_SPEC, contractException.getErrorType()); + + builder.clearBuilder(); + // if the translation spec has already been added (exact same instance) + contractException = assertThrows(ContractException.class, () -> { + TestProtobufObjectTranslationSpec testObjectTranslationSpec1 = new TestProtobufObjectTranslationSpec(); + + builder.addTranslationSpec(testObjectTranslationSpec1).addTranslationSpec(testObjectTranslationSpec1); + }); + + assertEquals(CoreTranslationError.DUPLICATE_TRANSLATION_SPEC, contractException.getErrorType()); + } + + public static void testAddTranslator(TranslationEngine.Builder builder) { + builder.addTranslator(TestObjectTranslator.getTranslator()); + + // preconditions + ContractException contractException = assertThrows(ContractException.class, () -> { + builder.addTranslator(null); + }); + + assertEquals(CoreTranslationError.NULL_TRANSLATOR, contractException.getErrorType()); + + contractException = assertThrows(ContractException.class, () -> { + builder.addTranslator(TestObjectTranslator.getTranslator()) + .addTranslator(TestObjectTranslator.getTranslator()); + }); + + assertEquals(CoreTranslationError.DUPLICATE_TRANSLATOR, contractException.getErrorType()); + } + + public static void testAddParentChildClassRelationship(TranslationEngine.Builder builder) { + builder.addParentChildClassRelationship(TestAppObject.class, Object.class); + + // preconditions + ContractException contractException = assertThrows(ContractException.class, () -> { + builder.addParentChildClassRelationship(null, Object.class); + }); + + assertEquals(CoreTranslationError.NULL_CLASS_REF, contractException.getErrorType()); + + contractException = assertThrows(ContractException.class, () -> { + builder.addParentChildClassRelationship(TestAppObject.class, null); + }); + + assertEquals(CoreTranslationError.NULL_CLASS_REF, contractException.getErrorType()); + + contractException = assertThrows(ContractException.class, () -> { + builder.addParentChildClassRelationship(TestAppObject.class, Object.class) + .addParentChildClassRelationship(TestAppObject.class, Object.class); + }); + + assertEquals(CoreTranslationError.DUPLICATE_CLASSREF, contractException.getErrorType()); + } +} diff --git a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/AT_ProtobufTranslationEngine.java b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/AT_ProtobufTranslationEngine.java index 401740e..be98bd9 100644 --- a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/AT_ProtobufTranslationEngine.java +++ b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/AT_ProtobufTranslationEngine.java @@ -7,9 +7,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; +import java.io.StringReader; import java.nio.file.Path; import java.util.Optional; @@ -25,7 +24,9 @@ import com.google.protobuf.ProtocolMessageEnum; import gov.hhs.aspr.ms.taskit.core.CoreTranslationError; +import gov.hhs.aspr.ms.taskit.core.ProtobufTranslationEngineTestHelper; import gov.hhs.aspr.ms.taskit.core.TranslationSpec; +import gov.hhs.aspr.ms.taskit.core.Translator; import gov.hhs.aspr.ms.taskit.core.testsupport.TestResourceHelper; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestAppChildObject; import gov.hhs.aspr.ms.taskit.core.testsupport.testobject.TestAppObject; @@ -53,8 +54,6 @@ public class AT_ProtobufTranslationEngine { public void testGetAnyFromObject() { ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder().build(); - protobufTranslationEngine.init(); - Integer integer = 1500; Int32Value int32Value = Int32Value.of(integer); Any expectedAny = Any.pack(int32Value); @@ -72,8 +71,6 @@ public void testGetAnyFromObjectAsSafeClass() { .addTranslationSpec(new TestProtobufObjectTranslationSpec()) .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); - TestAppObject testAppObject = TestObjectUtil.generateTestAppObject(); TestAppChildObject testAppChildObject = TestObjectUtil.getChildAppFromApp(testAppObject); @@ -91,8 +88,6 @@ public void testGetAnyFromObjectAsSafeClass() { ProtobufTranslationEngine protobufTranslationEngine2 = ProtobufTranslationEngine.builder() .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine2.init(); - TestAppObject testAppObject2 = TestObjectUtil.generateTestAppObject(); TestAppChildObject testAppChildObject2 = TestObjectUtil.getChildAppFromApp(testAppObject2); @@ -109,8 +104,6 @@ public void testGetObjectFromAny() { .addTranslationSpec(new TestProtobufObjectTranslationSpec()) .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); - TestAppObject expectedObject = TestObjectUtil.generateTestAppObject(); TestInputObject expectedInputObject = TestObjectUtil.getInputFromApp(expectedObject); @@ -129,8 +122,6 @@ public void testGetClassFromTypeUrl() { .addTranslationSpec(new TestProtobufObjectTranslationSpec()) .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); - Class testInputObjectClass = TestInputObject.class; Class testComplexInputObjectClass = TestComplexInputObject.class; @@ -147,8 +138,6 @@ public void testGetClassFromTypeUrl() { ProtobufTranslationEngine protobufTranslationEngine2 = ProtobufTranslationEngine.builder() .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine2.init(); - protobufTranslationEngine2.getClassFromTypeUrl("badUrl"); }); @@ -166,16 +155,12 @@ public void testDebugPrint() throws IOException { .addTranslationSpec(new TestProtobufObjectTranslationSpec()) .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); protobufTranslationEngine.setDebug(true); TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); - FileWriter fileWriter = new FileWriter(filePath.resolve(fileName).toFile()); - FileReader fileReader = new FileReader(filePath.resolve(fileName).toFile()); - - protobufTranslationEngine.writeOutput(fileWriter, expectedAppObject, Optional.empty()); - TestAppObject actualAppObject = protobufTranslationEngine.readInput(fileReader, TestInputObject.class); + protobufTranslationEngine.writeOutput(filePath.resolve(fileName), expectedAppObject, Optional.empty()); + TestAppObject actualAppObject = protobufTranslationEngine.readInput(filePath.resolve(fileName), TestInputObject.class); assertEquals(expectedAppObject, actualAppObject); } @@ -187,8 +172,6 @@ public void testParseJson() { .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).setIgnoringUnknownFields(false) .build(); - protobufTranslationEngine.init(); - // preconditions // json has unknown property and the ignoringUnknownFields property is set to // false @@ -197,7 +180,7 @@ public void testParseJson() { jsonObject.addProperty("unknownProperty", "unknownValue"); RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> { - protobufTranslationEngine.parseJson(jsonObject, TestInputObject.class); + protobufTranslationEngine.parseJson(new StringReader(jsonObject.toString()), TestInputObject.class); }); assertEquals(InvalidProtocolBufferException.class, runtimeException.getCause().getClass()); @@ -211,8 +194,6 @@ public void testGetBuilderForMessage() { .addTranslationSpec(new TestProtobufObjectTranslationSpec()) .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); - // preconditions /* * Note on these preconditions: Because of the type enforced on readInput() @@ -255,29 +236,21 @@ public void testReadInput() throws IOException { .addTranslationSpec(new TestProtobufObjectTranslationSpec()) .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); - TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); - FileWriter fileWriter = new FileWriter(filePath.resolve(fileName).toFile()); - FileReader fileReader = new FileReader(filePath.resolve(fileName).toFile()); - - FileWriter fileWriter2 = new FileWriter(filePath.resolve(fileName2).toFile()); - FileReader fileReader2 = new FileReader(filePath.resolve(fileName2).toFile()); - - protobufTranslationEngine.writeOutput(fileWriter, expectedAppObject, Optional.empty()); - TestAppObject actualAppObject = protobufTranslationEngine.readInput(fileReader, TestInputObject.class); + protobufTranslationEngine.writeOutput(filePath.resolve(fileName), expectedAppObject, Optional.empty()); + TestAppObject actualAppObject = protobufTranslationEngine.readInput(filePath.resolve(fileName), TestInputObject.class); assertEquals(expectedAppObject, actualAppObject); - protobufTranslationEngine.writeOutput(fileWriter2, TestObjectUtil.getChildAppFromApp(expectedAppObject), + protobufTranslationEngine.writeOutput(filePath.resolve(fileName2), TestObjectUtil.getChildAppFromApp(expectedAppObject), Optional.of(TestAppObject.class)); - TestAppObject actualAppChildObject = protobufTranslationEngine.readInput(fileReader2, TestInputObject.class); + TestAppObject actualAppChildObject = protobufTranslationEngine.readInput(filePath.resolve(fileName2), TestInputObject.class); assertEquals(expectedAppObject, actualAppChildObject); // preconditions // input class is not a Message class ContractException contractException = assertThrows(ContractException.class, () -> { - protobufTranslationEngine.readInput(fileReader2, TestAppObject.class); + protobufTranslationEngine.readInput(filePath.resolve(fileName2), TestAppObject.class); }); assertEquals(ProtobufCoreTranslationError.INVALID_READ_INPUT_CLASS_REF, contractException.getErrorType()); @@ -299,44 +272,31 @@ public void testWriteOutput() throws IOException { .addTranslationSpec(new TestProtobufObjectTranslationSpec()) .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); - TestAppObject expectedAppObject = TestObjectUtil.generateTestAppObject(); - FileWriter fileWriter = new FileWriter(filePath.resolve(fileName).toFile()); - FileReader fileReader = new FileReader(filePath.resolve(fileName).toFile()); - - FileWriter fileWriter2 = new FileWriter(filePath.resolve(fileName2).toFile()); - FileReader fileReader2 = new FileReader(filePath.resolve(fileName2).toFile()); - - protobufTranslationEngine.writeOutput(fileWriter, expectedAppObject, Optional.empty()); - TestAppObject actualAppObject = protobufTranslationEngine.readInput(fileReader, TestInputObject.class); + protobufTranslationEngine.writeOutput(filePath.resolve(fileName), expectedAppObject, Optional.empty()); + TestAppObject actualAppObject = protobufTranslationEngine.readInput(filePath.resolve(fileName), TestInputObject.class); assertEquals(expectedAppObject, actualAppObject); - protobufTranslationEngine.writeOutput(fileWriter2, TestObjectUtil.getChildAppFromApp(expectedAppObject), + protobufTranslationEngine.writeOutput(filePath.resolve(fileName2), TestObjectUtil.getChildAppFromApp(expectedAppObject), Optional.of(TestAppObject.class)); - TestAppObject actualAppChildObject = protobufTranslationEngine.readInput(fileReader2, TestInputObject.class); + TestAppObject actualAppChildObject = protobufTranslationEngine.readInput(filePath.resolve(fileName2), TestInputObject.class); assertEquals(expectedAppObject, actualAppChildObject); // this test is just for coverage, but this method should never be directly // called TestInputObject inputObject = TestObjectUtil.generateTestInputObject(); - protobufTranslationEngine.writeOutput(fileWriter2, inputObject, Optional.empty()); - actualAppObject = protobufTranslationEngine.readInput(fileReader2, TestInputObject.class); + protobufTranslationEngine.writeOutput(filePath.resolve(fileName2), inputObject, Optional.empty()); + actualAppObject = protobufTranslationEngine.readInput(filePath.resolve(fileName2), TestInputObject.class); assertEquals(TestObjectUtil.getAppFromInput(inputObject), actualAppObject); // preconditions // IO error occurs RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> { - FileWriter fileWriter3 = new FileWriter(filePath.resolve(fileName).toFile()); - // close the file reader - fileWriter3.close(); - protobufTranslationEngine.writeOutput(fileWriter3, expectedAppObject, Optional.empty()); + protobufTranslationEngine.writeOutput(filePath.resolve("/foo"), expectedAppObject, Optional.empty()); }); assertTrue(runtimeException.getCause() instanceof IOException); - - filePath.resolve(fileName).toFile().setWritable(true); } @Test @@ -451,17 +411,12 @@ public void testAddFieldToIncludeDefaultValue() throws InvalidProtocolBufferExce @UnitTestMethod(target = ProtobufTranslationEngine.Builder.class, name = "addTranslationSpec", args = { TranslationSpec.class }) public void testAddTranslationSpec() { - /* - * this test will only test the difference between the ProtobufTranslationEngine - * and the TranslationEngine, which is only the populateMethod - */ + ProtobufTranslationEngineTestHelper.testAddTranslationSpec(ProtobufTranslationEngine.builder()); ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder() .addTranslationSpec(new TestProtobufObjectTranslationSpec()) .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); - assertDoesNotThrow(() -> { protobufTranslationEngine.getClassFromTypeUrl(TestInputObject.getDescriptor().getFullName()); protobufTranslationEngine.getClassFromTypeUrl(TestComplexInputObject.getDescriptor().getFullName()); @@ -479,6 +434,20 @@ public void testAddTranslationSpec() { // ProtocolMessageEnum, and is tested in the testPopulate() test } + @Test + @UnitTestMethod(target = ProtobufTranslationEngine.Builder.class, name = "addTranslator", args = { + Translator.class }) + public void testAddTranslator() { + ProtobufTranslationEngineTestHelper.testAddTranslator(ProtobufTranslationEngine.builder()); + } + + @Test + @UnitTestMethod(target = ProtobufTranslationEngine.Builder.class, name = "addParentChildClassRelationship", args = { + Class.class, Class.class }) + public void testAddParentChildClassRelationship() { + ProtobufTranslationEngineTestHelper.testAddParentChildClassRelationship(ProtobufTranslationEngine.builder()); + } + @Test @UnitTestMethod(target = ProtobufTranslationEngine.Builder.class, name = "setIgnoringUnknownFields", args = { boolean.class }) diff --git a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testcomplexobject/translationSpecs/AT_TestProtobufComplexObjectTranslationSpec.java b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testcomplexobject/translationSpecs/AT_TestProtobufComplexObjectTranslationSpec.java index f334822..6dcb4cb 100644 --- a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testcomplexobject/translationSpecs/AT_TestProtobufComplexObjectTranslationSpec.java +++ b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testcomplexobject/translationSpecs/AT_TestProtobufComplexObjectTranslationSpec.java @@ -26,7 +26,6 @@ public void testConstructor() { public void testConvertInputObject() { ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder() .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); TestProtobufComplexObjectTranslationSpec complexObjectTranslationSpec = new TestProtobufComplexObjectTranslationSpec(); complexObjectTranslationSpec.init(protobufTranslationEngine); @@ -44,7 +43,6 @@ public void testConvertInputObject() { public void testConvertAppObject() { ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder() .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); TestProtobufComplexObjectTranslationSpec complexObjectTranslationSpec = new TestProtobufComplexObjectTranslationSpec(); complexObjectTranslationSpec.init(protobufTranslationEngine); diff --git a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testobject/translationSpecs/AT_TestProtobufEnumTranslationSpec.java b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testobject/translationSpecs/AT_TestProtobufEnumTranslationSpec.java index 612a966..b20aab6 100644 --- a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testobject/translationSpecs/AT_TestProtobufEnumTranslationSpec.java +++ b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testobject/translationSpecs/AT_TestProtobufEnumTranslationSpec.java @@ -24,7 +24,6 @@ public void testConstructor() { @UnitTestForCoverage public void testConvertInputObject() { ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder().build(); - protobufTranslationEngine.init(); TestProtobufEnumTranslationSpec enumTranslationSpec = new TestProtobufEnumTranslationSpec(); enumTranslationSpec.init(protobufTranslationEngine); @@ -41,7 +40,6 @@ public void testConvertInputObject() { @UnitTestForCoverage public void testConvertAppObject() { ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder().build(); - protobufTranslationEngine.init(); TestProtobufEnumTranslationSpec enumTranslationSpec = new TestProtobufEnumTranslationSpec(); enumTranslationSpec.init(protobufTranslationEngine); diff --git a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testobject/translationSpecs/AT_TestProtobufObjectTranslationSpec.java b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testobject/translationSpecs/AT_TestProtobufObjectTranslationSpec.java index c54ff72..e1d6460 100644 --- a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testobject/translationSpecs/AT_TestProtobufObjectTranslationSpec.java +++ b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/testsupport/testobject/translationSpecs/AT_TestProtobufObjectTranslationSpec.java @@ -27,7 +27,6 @@ public void testConstructor() { public void testConvertInputObject() { ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder() .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); TestProtobufObjectTranslationSpec objectTranslationSpec = new TestProtobufObjectTranslationSpec(); objectTranslationSpec.init(protobufTranslationEngine); @@ -45,7 +44,6 @@ public void testConvertInputObject() { public void testConvertAppObject() { ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder() .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); TestProtobufObjectTranslationSpec objectTranslationSpec = new TestProtobufObjectTranslationSpec(); objectTranslationSpec.init(protobufTranslationEngine); diff --git a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/translationSpecs/AT_AnyTranslationSpec.java b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/translationSpecs/AT_AnyTranslationSpec.java index 01d40a4..d8fdd00 100644 --- a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/translationSpecs/AT_AnyTranslationSpec.java +++ b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/translationSpecs/AT_AnyTranslationSpec.java @@ -42,7 +42,6 @@ public void testConvertInputObject() { .addTranslationSpec(new TestProtobufObjectTranslationSpec()) .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); AnyTranslationSpec anyTranslationSpec = new AnyTranslationSpec(); anyTranslationSpec.init(protobufTranslationEngine); @@ -85,7 +84,6 @@ public void testUnpackMessage() { .addTranslationSpec(new TestProtobufObjectTranslationSpec()) .addTranslationSpec(new TestProtobufComplexObjectTranslationSpec()).build(); - protobufTranslationEngine.init(); AnyTranslationSpec anyTranslationSpec = new AnyTranslationSpec(); anyTranslationSpec.init(protobufTranslationEngine); @@ -108,7 +106,6 @@ public void testConvertAppObject() { ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder() .addTranslationSpec(new TestProtobufEnumTranslationSpec()).build(); - protobufTranslationEngine.init(); AnyTranslationSpec anyTranslationSpec = new AnyTranslationSpec(); anyTranslationSpec.init(protobufTranslationEngine); diff --git a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/translationSpecs/AT_EnumTranslationSpec.java b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/translationSpecs/AT_EnumTranslationSpec.java index 182af04..2a375f2 100644 --- a/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/translationSpecs/AT_EnumTranslationSpec.java +++ b/protobuf/src/test/java/gov/hhs/aspr/ms/taskit/protobuf/translationSpecs/AT_EnumTranslationSpec.java @@ -30,7 +30,6 @@ public void testConstructor() { public void testConvertInputObject() { ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder() .addTranslationSpec(new TestProtobufEnumTranslationSpec()).build(); - protobufTranslationEngine.init(); EnumTranslationSpec enumTranslationSpec = new EnumTranslationSpec(); enumTranslationSpec.init(protobufTranslationEngine); @@ -60,7 +59,6 @@ public void testConvertInputObject() { public void testConvertAppObject() { ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder() .addTranslationSpec(new TestProtobufEnumTranslationSpec()).build(); - protobufTranslationEngine.init(); EnumTranslationSpec enumTranslationSpec = new EnumTranslationSpec(); enumTranslationSpec.init(protobufTranslationEngine);