diff --git a/.gitignore b/.gitignore index 78aba4bb88ae15cc77fd1bb1b454f37c145e3839..64adf30139f5da14a6e83494de50159c10b76665 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ .idea/ .gradle/ build/ +public/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e5275bd786ef8e40f8ee96b03564c36f41f22394..76278f6e80dfc8b78a152a6f653ffff7c3c5461c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,11 +1,14 @@ variables: GIT_SUBMODULE_STRATEGY: recursive + GIT_DEPTH: 1000 stages: - build - test -# - publish - +#- ragdoc_build +#- ragdoc_view +#- publish +> before_script: - export GRADLE_USER_HOME=`pwd`/.gradle @@ -21,38 +24,84 @@ build: - ./gradlew --console=plain --no-daemon assemble jar artifacts: paths: - - "/builds/jastadd/ragconnect/ragconnect.base/build/libs/ragconnect-*.jar" + - "ragconnect.base/build/libs/ragconnect-*.jar" + - "ragconnect.base/src/gen" expire_in: 1 week test: image: openjdk:11 stage: test services: - - name: "eclipse-mosquitto:1.6.9" + - name: "eclipse-mosquitto:1.6" alias: "mqtt" + needs: + - build script: - ./gradlew --console=plain --no-daemon allTests artifacts: + when: always reports: - junit: "/builds/jastadd/ragconnect/ragconnect.tests/build/test-results/test/TEST-*.xml" + junit: "ragconnect.tests/build/test-results/**/TEST-*.xml" expire_in: 1 week #publish: # image: openjdk:11 # stage: publish +# needs: +# - test # script: # - "./gradlew publish" # only: +# - dev # - master -#pages: -# image: python:3.7-alpine -# stage: publish +#ragdoc_build: +# image: +# name: "git-st.inf.tu-dresden.de:4567/jastadd/ragdoc-builder" +# entrypoint: [""] +# stage: ragdoc_build +# needs: +# - build # script: -# - pip install -U sphinx sphinx-rtd-theme recommonmark sphinxemoji sphinx-markdown-tables -# - sphinx-build -b html pages/ public +# - JAVA_FILES=$(find ragconnect.base/src/ -name '*.java') +# - /ragdoc-builder/start-builder.sh -excludeGenerated -d data/ $JAVA_FILES # artifacts: # paths: -# - public +# - "data/" + +#ragdoc_view: +# image: +# name: "git-st.inf.tu-dresden.de:4567/jastadd/ragdoc-view:relations" +# entrypoint: [""] +# stage: ragdoc_view +# needs: +# - ragdoc_build +# script: +# - DATA_DIR=$(pwd -P)/data +# - mkdir -p pages/docs/ragdoc +# - OUTPUT_DIR=$(pwd -P)/pages/docs/ragdoc +# - cd /ragdoc-view/src/ && rm -rf data && ln -s $DATA_DIR +# - /ragdoc-view/build-view.sh --output-path=$OUTPUT_DIR # only: -# - master +# - dev +# - master +# artifacts: +# paths: +# - "pages/docs/ragdoc" + +#pages: +# image: python:3.8-buster +# stage: publish +# needs: +# - ragdoc_view +# - test +# before_script: +# - pip install -U mkdocs mkdocs-macros-plugin mkdocs-git-revision-date-localized-plugin +# script: +# - cd pages && mkdocs build +#only: +# - dev +# - master +#artifacts: +# paths: +# - public diff --git a/.gitmodules b/.gitmodules index 0cf1e8a6d073c7c58b0881fe53f5642e6a1022e7..14c250a57ed1d3278b5d6ce961c8fae45e9266cb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ [submodule "relast-preprocessor"] path = relast-preprocessor url = ../relast-preprocessor.git + branch = develop [submodule "ragconnect.base/src/main/jastadd/mustache"] path = ragconnect.base/src/main/jastadd/mustache - url = ../mustache + url = git@git-st.inf.tu-dresden.de:jastadd/jastadd-ceti/mustache.git diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3b162ddae3e87812e517c2c20ce639186cf05946 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# RagConnect + +[RagConnect](https://git-st.inf.tu-dresden.de/jastadd/ragconnect) is a preprocessor to enable easy connection to/from models based on [Reference Attribute Grammars](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.108.8792&rep=rep1&type=pdf) and [Relational Reference Attribute Grammars](https://doi.org/10.1016/j.cola.2019.100940) built with [JastAdd](http://jastadd.org/). + +Documentation can be found at <http://connect.relational-rags.eu> including an API documentation. + +The most recent version is listed at the [package registry](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/packages). diff --git a/libs/buildSrc.jar b/libs/buildSrc.jar deleted file mode 100644 index b0ca2cbc4fc7f12022592944d7446a429d855add..0000000000000000000000000000000000000000 Binary files a/libs/buildSrc.jar and /dev/null differ diff --git a/libs/jastadd2.jar b/libs/jastadd2.jar new file mode 100644 index 0000000000000000000000000000000000000000..d615b895453d660f0e7397fffad58a05029169fd Binary files /dev/null and b/libs/jastadd2.jar differ diff --git a/pages/.gitattributes b/pages/.gitattributes index 6b5d9827cd5416e9e2568615d135b563f76cdbc5..1397af0c29996b68da5f01a8a92d8f4aca20427a 100644 --- a/pages/.gitattributes +++ b/pages/.gitattributes @@ -1 +1,2 @@ -_static/poster-presentation.mp4 filter=lfs diff=lfs merge=lfs -text +docs/img/poster-presentation.mp4 filter=lfs diff=lfs merge=lfs -text +docs/img/moving-robot.mp4 filter=lfs diff=lfs merge=lfs -text diff --git a/pages/.gitignore b/pages/.gitignore index 87174b686c0670d3fb6a66535729af2a8a249d72..14338f39f89e2a45e3473a6597e640aa9458f9a8 100644 --- a/pages/.gitignore +++ b/pages/.gitignore @@ -1 +1,2 @@ -/public/ +/docs/ragdoc/ +__pycache__ diff --git a/pages/Makefile b/pages/Makefile deleted file mode 100644 index 7878ee5305af7d6c04860113aa9ef008991d07af..0000000000000000000000000000000000000000 --- a/pages/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = public - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/pages/_static/moving-robot.mp4 b/pages/_static/moving-robot.mp4 deleted file mode 100644 index 05f00803bcceb88eb08a7b4dd44f297e359c8b32..0000000000000000000000000000000000000000 Binary files a/pages/_static/moving-robot.mp4 and /dev/null differ diff --git a/pages/conf.py b/pages/conf.py deleted file mode 100644 index bdd91e4f410636b477239f5ff4285eced4516914..0000000000000000000000000000000000000000 --- a/pages/conf.py +++ /dev/null @@ -1,66 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) -import sphinx_rtd_theme - - -# -- Project information ----------------------------------------------------- - -project = 'RagConnect' -copyright = '2021, René Schöne, Johannes Mey' -author = 'René Schöne, Johannes Mey' - -# The full version, including alpha/beta/rc tags -ragconnectVersionFileName = '../ragconnect.base/src/main/resources/ragConnectVersion.properties' -with open(ragconnectVersionFileName) as ragconnectVersionFile: - versionFileContent = ragconnectVersionFile.read() -release = version = versionFileContent[versionFileContent.rindex('version=') + 8:].strip() -print('Version: ' + version) - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx_rtd_theme', - 'recommonmark', - 'sphinxemoji.sphinxemoji', - 'sphinx_markdown_tables' -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'sphinx_rtd_theme' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -sphinxemoji_style = 'twemoji' diff --git a/pages/custom_theme/footer.html b/pages/custom_theme/footer.html new file mode 100644 index 0000000000000000000000000000000000000000..2d6c164695b3a1ed19711d3497caf142a8416ebb --- /dev/null +++ b/pages/custom_theme/footer.html @@ -0,0 +1,11 @@ +{% block footer %} +<p>{% if config.copyright %} +<small>{{ config.copyright }}<br></small> +{% endif %} +<hr> +Built with <a href="https://www.mkdocs.org/">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>. +{% if page and page.meta and page.meta.git_revision_date_localized %} +<small><br><i>Last updated {{ page.meta.git_revision_date_localized }}</i></small> +{% endif %} +</p> +{% endblock %} diff --git a/pages/adding.md b/pages/docs/adding.md similarity index 100% rename from pages/adding.md rename to pages/docs/adding.md diff --git a/pages/docs/changelog.md b/pages/docs/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..b2607fdf120d0b44ee8dd4a560cc4ad4047f0524 --- /dev/null +++ b/pages/docs/changelog.md @@ -0,0 +1,34 @@ +# Changelog + +## 0.3.1 + +- Full support for incremental dependency tracking +- Full support for subtree endpoint definitions ([#9](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/9)) +- Internal: Use updated gradle plugin for tests ([#18](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/18)) +- Bugfix [#22](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/22): Correct handling of malformed URIs passed when connecting an endpoint +- Bugfix [#23](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/23): Correct handling of OptComponents as endpoints +- Bugfix [#27](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/27): Correctly handle whitespaces in grammars + +## 0.3.0 + +- Added [API documentation](ragdoc/index.html) to documentation +- Add methods to `disconnect` an endpoint +- Internal: PoC for incremental dependency tracking and subtree endpoint definitions ([#14](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/14)) +- Bugfix [#17](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/17): Added missing support for `boolean` + +## 0.2.2 + +- Allow normal tokens to be used in send definitions + +## 0.2.1 + +- New communication protocol: REST +- Selection of protocol when `connect` methods are called, by scheme of given URI +- Development changes: + - Supported printing out YAML data used for mustache templates + - Moved string constants to `MRagConnect` structure + +## 0.2.0 + +- Version submitted in paper "A Connection from ROS to RAG-Based Models" (2020) +- Supported communication protocols: MQTT diff --git a/pages/docs/extending.md b/pages/docs/extending.md new file mode 100644 index 0000000000000000000000000000000000000000..61cabca224b7834f08cca7d96f3913c7cb9feb37 --- /dev/null +++ b/pages/docs/extending.md @@ -0,0 +1,85 @@ +# Extending `RagConnect` + +To add a new communication protocol, the following locations have to be changed (replace `ABC` and `abc` with the name of the protocol): + +Within `ragconnect.base/src/main/resources`: + +{% raw %} +- Add a new handler `ABCHandler`, if appropriate, similar to the existing handlers + - If further methods are needed for handler initialization, add a new template `abc.mustache` containing those procedures. Add `{{#usesABC}}{{> abc}}{{/usesABC}}` at the top of `ragconnect.mustache` to use this template +- In `receiveDefinition.mustache` and `sendDefinition.mustache`: add a new case in the switch statement defining the logic to happen for both definitions. If the new protocol is close to a PUSH semantic, follow `mqtt`. If it is closer to PULL semantic, follow `rest`. +{% endraw %} + +Within `ragconnect.base/src/main/jastadd`: + +- In `backend/Configuration`: + - Add a new static boolean flag `usesABC` to indicate whether the protocol is used +- In `backend/Generation`: + - Add new attributes for type `MRagConnect` for handler attribute and handler field, if needed + - Add attributes for newly introduced references in changed mustache templates, if any + - Add a newly constructed handler within the definition of `RagConnect.toMustache` with the needed fields (class name, construction snippet, handler attribute, handler field, the boolean flag you just added to Configuration) +- In `backend/MustacheNodesToYAML`: + - Add key-value-pair for `usesABC` (and handler, if any) + - Add key-value-pairs for newly introduced referemces in changed mustache templates, if any + +In `ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java`: + +- Add a new choice for `--protocols` similar to the existing ones +- Set the flag `usesABC` if the choice is given. +- Add code to add the handler to the list `handlers` if the choice is given, i.e., if `ASTNode.usesABC` + +Furthermore, new test cases are appreciated, see [below](#writing-tests). + +## Writing Tests + +To add new tests, have a look at the module `ragconnect.tests`. +It has three parts: +1) In `src/test/01-input/*` are the [specifications](#specifications) that are going to be compiled (in principle using the steps described in [the guide to add RagConnect](adding)). +2) In `src/test/java`, the jUnit 5 [test classes](#test-classes) are implemented. They mostly correspond 1-to-1 to a directory of the first part. +3) In `build.gradle` the [instructions how to compile](#buildgradle) the specifications using the gradle plugin [PreprocessorPlugin][preprocessor-plugin] (`org.jastadd.preprocessor:testing`). + +### Specifications + +Every specification must have at least a `README.md` to describe the purpose of the test, a grammar `Test.relast`, and a RagConnect specification `Test.connect`. +Usually an aspect file `Test.jadd` is included. + +### Test Classes + +Based on jUnit 5, the test classes testing some behaviour. If sending and/or receiving functionality is used, consider extending `AbstractMqttTest` in order to avoid duplicate code. In case of extending this class, please order the methods according to their lifecycle, i.e.: +- createModel +- setupReceiverAndConnect +- communicateSendInitialValue +- communicateOnlyUpdatedValue +- closeConnections + +Within `AbstractMqttTest`, an `MqttHandler` named `publisher` is available to publish content. +Some convenience methods are provided in `TestUtils`, e.g., the `DefaultMappings`, and `mqttUri` to prepend `"mqtt://"` and the correct host for the mqtt broker (`localhost` or a CI-specific host). +All tests are required to run both locally, and within the CI. + +### build.gradle + +Use the [PreprocessorPlugin][preprocessor-plugin], the build process can be written concisely in three parts per task: + +```groovy +task compileTreeAllowedTokens(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/treeAllowedTokens') + inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.relast'), + file('src/test/01-input/treeAllowedTokens/Test.connect'), + file('src/test/01-input/treeAllowedTokens/TestDependencies.connect')] + rootNode = 'Root' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/treeAllowedTokens/treeAllowedTokens' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'treeAllowedTokens.ast' + inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.jadd')] + } +} +``` + +[preprocessor-plugin]: https://git-st.inf.tu-dresden.de/jastadd/testing diff --git a/pages/docs/img/moving-robot.mp4 b/pages/docs/img/moving-robot.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..40da2c6367777d89f1b0193b19312363f0ef0db1 --- /dev/null +++ b/pages/docs/img/moving-robot.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:814b592f202dd72ae2cdb7b0841faefbc963d7847d9f568aca75547c647ee1e3 +size 1867731 diff --git a/pages/_static/mpm4cps-slides.pdf b/pages/docs/img/mpm4cps-slides.pdf similarity index 100% rename from pages/_static/mpm4cps-slides.pdf rename to pages/docs/img/mpm4cps-slides.pdf diff --git a/pages/_static/poster-presentation.mp4 b/pages/docs/img/poster-presentation.mp4 similarity index 100% rename from pages/_static/poster-presentation.mp4 rename to pages/docs/img/poster-presentation.mp4 diff --git a/pages/_static/poster.pdf b/pages/docs/img/poster.pdf similarity index 100% rename from pages/_static/poster.pdf rename to pages/docs/img/poster.pdf diff --git a/pages/images/robo3d.png b/pages/docs/img/robo3d.png similarity index 100% rename from pages/images/robo3d.png rename to pages/docs/img/robo3d.png diff --git a/pages/images/ros2rag-process.png b/pages/docs/img/ros2rag-process.png similarity index 100% rename from pages/images/ros2rag-process.png rename to pages/docs/img/ros2rag-process.png diff --git a/pages/docs/index.md b/pages/docs/index.md new file mode 100644 index 0000000000000000000000000000000000000000..d345eec5f7a9f7a688dbf28ced0f95e7dbd74566 --- /dev/null +++ b/pages/docs/index.md @@ -0,0 +1,3 @@ +# RagConnect Documentation + +[RagConnect](https://git-st.inf.tu-dresden.de/jastadd/ragconnect) is a preprocessor to enable easy connection to/from models based on [Reference Attribute Grammars](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.108.8792&rep=rep1&type=pdf) and [Relational Reference Attribute Grammars](https://doi.org/10.1016/j.cola.2019.100940) built with [JastAdd](http://jastadd.org/). diff --git a/pages/inner-workings.md b/pages/docs/inner-workings.md similarity index 93% rename from pages/inner-workings.md rename to pages/docs/inner-workings.md index 9a244b9e72debabc3cdb138c7654fb66ce217f53..6d8b41e13847f2ae337f08c65068cbef6085e065 100644 --- a/pages/inner-workings.md +++ b/pages/docs/inner-workings.md @@ -1,6 +1,8 @@ # Inner workings of `RagConnect` - +Please see [API documentation](ragdoc/index.html) for more details. + + `RagConnect` uses the [relast-preprocessor](https://git-st.inf.tu-dresden.de/jastadd/relast-preprocessor) to parse `.relast` grammar files. This results in an ASTNode of type `Program`. It further uses a dedicated parser for `.connect` files containing endpoint-, mapping-, and dependency-definitions. This results in an ASTNode of type `RagConnect`. diff --git a/pages/use_cases.md b/pages/docs/use_cases.md similarity index 74% rename from pages/use_cases.md rename to pages/docs/use_cases.md index c479b65fdf40260ec8759617fa9f20e7529302fe..b815b7a5f6fab004f4573e29054858a10fd5d7db 100644 --- a/pages/use_cases.md +++ b/pages/docs/use_cases.md @@ -1,17 +1,17 @@ # Use cases with `RagConnect` -## 1⃣ MPM4CPS Paper - Codename 'Ros2Rag' +## MPM4CPS Paper - Codename 'Ros2Rag' -In the publication [*"Connecting conceptual models using Relational Reference Attribute Grammars"*](https://doi.org/10.1145/3417990.3421437), a use case involving a simulated robot arm and two different models conencted to it was shown. +In the publication [*"Connecting conceptual models using Relational Reference Attribute Grammars"*](https://doi.org/10.1145/3417990.3421437), a use case involving a simulated robot arm and two different models connected to it was shown. One model was used to ensure a low speed of the robot when within a safety zone (purple boxes in the picture below), and the other model executes a workflow to control the robot. - + -[📽 Recording of the simulated robot during the use case](_static/moving-robot.mp4) +[📽 Recording of the simulated robot during the use case](img/moving-robot.mp4) -This paper was presented on October, 16h during the [MPM4CPS workshop](https://msdl.uantwerpen.be/conferences/MPM4CPS/2020/) within the [MODELS 2020 conference](https://conf.researchr.org/home/models-2020). For more information, see the [presented slides](_static/mpm4cps-slides.pdf) or the [a recording of the session](https://youtu.be/Hgc1qFfmr44?t=1220). +This paper was presented on October, 16h during the [MPM4CPS workshop](https://msdl.uantwerpen.be/conferences/MPM4CPS/2020/) within the [MODELS 2020 conference](https://conf.researchr.org/home/models-2020). For more information, see the [presented slides](img/mpm4cps-slides.pdf) or the [a recording of the session](https://youtu.be/Hgc1qFfmr44?t=1220). -There is also a [poster](_static/poster.pdf) and a [pre-recorded presentation](_static/poster-presentation.mp4) of this poster. +There is also a [poster](img/poster.pdf) and a [pre-recorded presentation](img/poster-presentation.mp4) of this poster. The repository with the used source code can be found at: <https://git-st.inf.tu-dresden.de/ceti/ros/mpm4cps2020> The usage is dockerized, so starting the application only involves the commands listed below. diff --git a/pages/using.md b/pages/docs/using.md similarity index 100% rename from pages/using.md rename to pages/docs/using.md diff --git a/pages/extending.md b/pages/extending.md deleted file mode 100644 index baa6c5380f5dad2c3e351a3027c6c9a287e96f6d..0000000000000000000000000000000000000000 --- a/pages/extending.md +++ /dev/null @@ -1,26 +0,0 @@ -# Extending `RagConnect` - -To add a new communication protocol, the following locations have to be changed (replace `ABC` and `abc` with the name of the protocol): - -Within `ragconnect.base/src/main/resources`: -- Add a new handler `ABCHandler`, if appropriate, similar to the existing handlers - - If further methods are needed for handler initialization, add a new template `abc.mustache` containing those procedures. Add `{{#usesABC}}{{> abc}}{{/usesABC}}` at the top of `ragconnect.mustache` to use this template -- In `receiveDefinition.mustache` and `sendDefinition.mustache`: add a new case in the switch statement defining the logic to happen for both definitions. If the new protocol is close to a PUSH semantic, follow `mqtt`. If it is closer to PULL semantic, follow `rest`. - -Within `ragconnect.base/src/main/jastadd`: -- In `backend/Configuration`: - - Add a new static boolean flag `usesABC` to indicate whether the protocol is used -- In `backend/Generation`: - - Add new attributes for type `MRagConnect` for handler attribute and handler field, if needed - - Add attributes for newly introduced references in changed mustache templates, if any - - Add a newly constructed handler within the definition of `RagConnect.toMustache` with the needed fields (class name, construction snippet, handler attribute, handler field, the boolean flag you just added to Configuration) -- In `backend/MustacheNodesToYAML`: - - Add key-value-pair for `usesABC` (and handler, if any) - - Add key-value-pairs for newly introduced referemces in changed mustache templates, if any - -In `ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java`: -- Add a new choice for `--protocols` similar to the existing ones -- Set the flag `usesABC` if the choice is given. -- Add code to add the handler to the list `handlers` if the choice is given, i.e., if `ASTNode.usesABC` - -Furthermore, new test cases are appreciated. They can be added in the [ragconnect.rests repository](https://git-st.inf.tu-dresden.de/jastadd/ragconnect-tests) diff --git a/pages/index.rst b/pages/index.rst deleted file mode 100644 index 2da98fa853da557649b89085755852cac20cef7c..0000000000000000000000000000000000000000 --- a/pages/index.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. RagConnect documentation master file, created by - sphinx-quickstart on Fri Aug 28 10:16:26 2020. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -RagConnect Documentation -======================== - -`RagConnect <https://git-st.inf.tu-dresden.de/jastadd/ragconnect>`_ is a preprocessor to enable easy connection to/from models based on `Reference Attribute Grammars <http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.108.8792&rep=rep1&type=pdf>`_ and `Relation Reference Attribute Grammars <https://doi.org/10.1016/j.cola.2019.100940>`_ built with `JastAdd <http://jastadd.org/>`_. - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - use_cases.md - adding.md - inner-workings.md - using.md - extending.md - - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`search` diff --git a/pages/main.py b/pages/main.py new file mode 100644 index 0000000000000000000000000000000000000000..5f574f69198e31785d5d9a4e2b228d5567d61384 --- /dev/null +++ b/pages/main.py @@ -0,0 +1,25 @@ +ragconnectVersionFileName = '../ragconnect.base/src/main/resources/ragConnectVersion.properties' + + +def get_version(): + with open(ragconnectVersionFileName) as ragconnectVersionFile: + versionFileContent = ragconnectVersionFile.read() + return versionFileContent[versionFileContent.rindex('version=') + 8:].strip() + + +def define_env(env): + """ + This is the hook for defining variables, macros and filters + + - variables: the dictionary that contains the environment variables + - macro: a decorator function, to declare a macro. + """ + env.conf['site_name'] = 'RagConnect ' + get_version() + + @env.macro + def ragconnect_version(): + return get_version() + + +if __name__ == '__main__': + print(get_version()) diff --git a/pages/make.bat b/pages/make.bat deleted file mode 100644 index c6abfba46443f5335943b13912c8d7b6d62ae644..0000000000000000000000000000000000000000 --- a/pages/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=public - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/pages/material/mpm4cps-slides.pdf b/pages/material/mpm4cps-slides.pdf deleted file mode 100644 index 266f4e68e8806a398189cee8939ec393bf7f4ef4..0000000000000000000000000000000000000000 Binary files a/pages/material/mpm4cps-slides.pdf and /dev/null differ diff --git a/pages/mkdocs.yml b/pages/mkdocs.yml new file mode 100644 index 0000000000000000000000000000000000000000..6363c73ae7ac43b9c4d04c4eb0036ef36fb5019a --- /dev/null +++ b/pages/mkdocs.yml @@ -0,0 +1,22 @@ +site_name: RagConnect +nav: + - use_cases.md + - adding.md + - inner-workings.md + - using.md + - extending.md + - changelog.md + - API documentation: ragdoc/index.html +theme: + name: readthedocs + custom_dir: custom_theme/ +plugins: + - search + - git-revision-date-localized: + type: datetime + timezone: Europe/Berlin + locale: en + fallback_to_build_date: True + - macros +repo_url: https://git-st.inf.tu-dresden.de/jastadd/ragconnect +site_dir: ../public diff --git a/ragconnect.base/build.gradle b/ragconnect.base/build.gradle index 5c25104cfc36370e958807e99961cc26502ccb24..753814f384186d096929fd2794ac6a19129bfb1f 100644 --- a/ragconnect.base/build.gradle +++ b/ragconnect.base/build.gradle @@ -27,22 +27,21 @@ repositories { dependencies { implementation project(':relast-preprocessor') - implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: "${log4j_version}" - implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: "${log4j_version}" - implementation group: 'org.apache.logging.log4j', name: 'log4j-jul', version: "${log4j_version}" implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: "${mustache_java_version}" // https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 api group: 'org.apache.commons', name: 'commons-lang3', version: '3.0' runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.4' +// runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.5' + runtimeOnly fileTree(include: ['jastadd2.jar'], dir: '../libs') api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' } def versionFile = 'src/main/resources/ragConnectVersion.properties' -def oldProps = new Properties() +def props = new Properties() try { - file(versionFile).withInputStream { stream -> oldProps.load(stream) } - version = oldProps['version'] + file(versionFile).withInputStream { stream -> props.load(stream) } + version = props['version'] } catch (e) { // this happens, if either the properties file is not present, or cannot be read from throw new GradleException("File ${versionFile} not found or unreadable. Aborting.", e) @@ -56,9 +55,9 @@ task printVersion() { task newVersion() { doFirst { - def props = new Properties() - props['version'] = value - props.store(file(versionFile).newWriter(), null) + def newProps = new Properties() + newProps['version'] = value + newProps.store(file(versionFile).newWriter(), null) } } @@ -69,14 +68,12 @@ idea.module.generatedSourceDirs += genSrc jar { manifest { attributes "Main-Class": 'org.jastadd.ragconnect.compiler.Compiler' - - // Log4J + Java 11 compatibility, see https://stackoverflow.com/q/53049346/2493208 - attributes "Multi-Release": true } from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE archiveBaseName = 'ragconnect' } @@ -84,7 +81,7 @@ jar { File preprocessorGrammar = file('../relast-preprocessor/src/main/jastadd/RelAst.relast') File ragConnectGrammar = file('./src/main/jastadd/RagConnect.relast') File intermediateGrammar = file('./src/main/jastadd/intermediate/MustacheNodes.relast') -File mustacheGrammar = file('./src/main/jastadd/mustache/Mustache.relast') +File mustacheGrammar = file('../relast-preprocessor/src/main/jastadd/mustache/Mustache.relast') task relast(type: JavaExec) { group = 'Build' main = "-jar" @@ -226,3 +223,4 @@ publishing { } publish.dependsOn jar +jar.dependsOn ":relast-preprocessor:jar" diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag index a1367a9c21bf2f293dd33216ea20a8941f5411b5..ac891b0e819b1e57e135afced64495a76396db5e 100644 --- a/ragconnect.base/src/main/jastadd/Analysis.jrag +++ b/ragconnect.base/src/main/jastadd/Analysis.jrag @@ -1,10 +1,10 @@ aspect Analysis { // --- lookupTokenEndpointDefinition --- inh java.util.List<TokenEndpointDefinition> TokenEndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token); - eq RagConnect.getEndpointDefinition().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token); + eq RagConnect.getConnectSpecificationFile().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token); syn java.util.List<TokenEndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) { java.util.List<TokenEndpointDefinition> result = new java.util.ArrayList<>(); - for (EndpointDefinition def : getEndpointDefinitionList()) { + for (EndpointDefinition def : allEndpointDefinitionList()) { if (def.isTokenEndpointDefinition() && def.asTokenEndpointDefinition().getToken().equals(token)) { result.add(def.asTokenEndpointDefinition()); } @@ -12,10 +12,23 @@ aspect Analysis { return result; } + // --- lookupTypeEndpointDefinition --- + inh java.util.List<TypeEndpointDefinition> TypeEndpointDefinition.lookupTypeEndpointDefinitions(TypeComponent type); + eq RagConnect.getConnectSpecificationFile().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type); + syn java.util.List<TypeEndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) { + java.util.List<TypeEndpointDefinition> result = new java.util.ArrayList<>(); + for (EndpointDefinition def : allEndpointDefinitionList()) { + if (def.isTypeEndpointDefinition() && def.asTypeEndpointDefinition().getType().equals(type)) { + result.add(def.asTypeEndpointDefinition()); + } + } + return result; + } + // --- lookupDependencyDefinition --- inh DependencyDefinition DependencyDefinition.lookupDependencyDefinition(TypeDecl source, String id); - eq RagConnect.getDependencyDefinition().lookupDependencyDefinition(TypeDecl source, String id) { - for (DependencyDefinition def : getDependencyDefinitionList()) { + eq RagConnect.getConnectSpecificationFile().lookupDependencyDefinition(TypeDecl source, String id) { + for (DependencyDefinition def : allDependencyDefinitionList()) { if (def.getID().equals(id) && def.getSource().containingTypeDecl().equals(source)) { return def; } @@ -31,10 +44,12 @@ aspect Analysis { } syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this; + // --- matchesType --- syn boolean TokenEndpointDefinition.matchesType(TokenEndpointDefinition other); eq ReceiveTokenEndpointDefinition.matchesType(TokenEndpointDefinition other) = other.isReceiveTokenEndpointDefinition(); eq SendTokenEndpointDefinition.matchesType(TokenEndpointDefinition other) = other.isSendTokenEndpointDefinition(); + // --- assignableTo --- syn boolean MappingDefinitionType.assignableTo(JavaTypeUse target); eq JavaMappingDefinitionType.assignableTo(JavaTypeUse target) = getType().assignableTo(target); eq JavaArrayMappingDefinitionType.assignableTo(JavaTypeUse target) { @@ -47,6 +62,8 @@ aspect Analysis { } syn String JavaTypeUse.primitivePrettyPrint() { switch(getName()) { + case "boolean": + case "Boolean": return "boolean"; case "int": case "Integer": return "int"; case "short": @@ -65,7 +82,23 @@ aspect Analysis { // --- shouldSendValue --- syn boolean TokenEndpointDefinition.shouldSendValue() = isSendTokenEndpointDefinition() && !getToken().getNTA(); + syn boolean TypeEndpointDefinition.shouldSendValue() = isSendTypeEndpointDefinition() && !getType().getNTA(); // --- needProxyToken --- syn boolean TokenComponent.needProxyToken() = !getDependencySourceDefinitionList().isEmpty() || getTokenEndpointDefinitionList().stream().anyMatch(TokenEndpointDefinition::shouldSendValue); + + // --- effectiveUsedAt --- + coll Set<EndpointDefinition> MappingDefinition.effectiveUsedAt() + [new java.util.HashSet<EndpointDefinition>()] + root RagConnect; + EndpointDefinition contributes this + to MappingDefinition.effectiveUsedAt() + for each effectiveMappings(); + + // --- typeIsList --- + syn boolean EndpointDefinition.typeIsList() = false; + eq TypeEndpointDefinition.typeIsList() { + return getType().isListComponent(); + } + } diff --git a/ragconnect.base/src/main/jastadd/Configuration.jadd b/ragconnect.base/src/main/jastadd/Configuration.jadd index 32f13d45b54541d43af1a5417d2daf9082bb5711..8c7213850afc5471c70c215d5462442b7da32326 100644 --- a/ragconnect.base/src/main/jastadd/Configuration.jadd +++ b/ragconnect.base/src/main/jastadd/Configuration.jadd @@ -1,8 +1,12 @@ aspect Configuration { public static boolean ASTNode.loggingEnabledForReads = false; public static boolean ASTNode.loggingEnabledForWrites = false; + public static boolean ASTNode.loggingEnabledForIncremental = false; public static TypeDecl ASTNode.rootNode; + public static String ASTNode.JastAddList = "List"; public static boolean ASTNode.usesMqtt; public static boolean ASTNode.usesRest; public static boolean ASTNode.usesJava; + public static boolean ASTNode.incrementalOptionActive; + public static boolean ASTNode.experimentalJastAdd329; } diff --git a/ragconnect.base/src/main/jastadd/Errors.jrag b/ragconnect.base/src/main/jastadd/Errors.jrag index 88f3aece6a64041a95f2784be4882d01008ff8c5..1aa3666c2b19bf446667f1a763bfb4824aa44f25 100644 --- a/ragconnect.base/src/main/jastadd/Errors.jrag +++ b/ragconnect.base/src/main/jastadd/Errors.jrag @@ -39,10 +39,6 @@ aspect Errors { DependencyDefinition contributes error("The name of a dependency definition must not be equal to a list-node on the source") when isAlreadyDefinedAsList() to RagConnect.errors(); - - DependencyDefinition contributes error("There must be a send endpoint definition targeting " + getSource().parentTypeypeAndName() + " for dependency definition " + getID()) - when targetEndpointDefinition() == null - to RagConnect.errors(); } aspect ErrorHelpers { @@ -54,7 +50,7 @@ aspect ErrorHelpers { } return false; } - syn String TokenComponent.parentTypeypeAndName() = containingTypeDecl().getName() + "." + getName(); + syn String TokenComponent.parentTypeDeclAndName() = containingTypeDecl().getName() + "." + getName(); } aspect ErrorMessage { diff --git a/ragconnect.base/src/main/jastadd/NameResolution.jrag b/ragconnect.base/src/main/jastadd/NameResolution.jrag index f282ba55983feeae814e12823ff31f1d6480b6a8..3b75bc0eb633830cb0e63c80d5d51e7d04e1b4f0 100644 --- a/ragconnect.base/src/main/jastadd/NameResolution.jrag +++ b/ragconnect.base/src/main/jastadd/NameResolution.jrag @@ -2,7 +2,7 @@ aspect NameResolution { refine RefResolverStubs eq EndpointDefinition.resolveMappingByToken(String id, int position) { // return a MappingDefinition - for (MappingDefinition mappingDefinition : ragconnect().getMappingDefinitionList()) { + for (MappingDefinition mappingDefinition : ragconnect().allMappingDefinitionList()) { if (mappingDefinition.getID().equals(id)) { return mappingDefinition; } @@ -11,4 +11,36 @@ aspect NameResolution { return null; } + refine RefResolverStubs eq ASTNode.globallyResolveTypeComponentByToken(String id) { + // return a TypeComponent. id is of the form 'parent_type_name + "." + child_type_name' + int dotIndex = id.indexOf("."); + String parentTypeName = id.substring(0, dotIndex); + String childTypeName = id.substring(dotIndex + 1); + TypeDecl type = program().resolveTypeDecl(parentTypeName); + // iterate over components and find the matching typeComponent + for (Component comp : type.getComponentList()) { + if (comp.isTypeComponent() && comp.getName().equals(childTypeName)) { + return comp.asTypeComponent(); + } + } + System.err.println("Could not resolve TypeComponent '" + id + "'."); + return null; + } + + refine RefResolverStubs eq ASTNode.globallyResolveComponentByToken(String id) { + // return a Component. id is of the form 'parent_type_name + "." + child_type_name' + int dotIndex = id.indexOf("."); + String parentTypeName = id.substring(0, dotIndex); + String childTypeName = id.substring(dotIndex + 1); + TypeDecl type = program().resolveTypeDecl(parentTypeName); + // iterate over components and find the matching typeComponent + for (Component comp : type.getComponentList()) { + if (comp.getName().equals(childTypeName)) { + return comp; + } + } + System.err.println("Could not resolve Component '" + id + "'."); + return null; + } + } diff --git a/ragconnect.base/src/main/jastadd/Navigation.jrag b/ragconnect.base/src/main/jastadd/Navigation.jrag index 1c2c4af4a40db2ff7ffb0bf962716d515553fd97..d3890f7d22a195127e7688435f9168aa64a37eac 100644 --- a/ragconnect.base/src/main/jastadd/Navigation.jrag +++ b/ragconnect.base/src/main/jastadd/Navigation.jrag @@ -1,4 +1,7 @@ -aspect Navigation { +import java.util.List; +import java.util.ArrayList; + +aspect RagConnectNavigation { // --- program --- eq RagConnect.getChild().program() = getProgram(); @@ -9,15 +12,54 @@ aspect Navigation { eq RagConnect.getChild().ragconnect() = this; eq MRagConnect.getChild().ragconnect() = getRagConnect(); + // --- containedConnectSpecification --- + inh ConnectSpecification ASTNode.containedConnectSpecification(); + eq RagConnect.getChild().containedConnectSpecification() = null; + eq MRagConnect.getChild().containedConnectSpecification() = null; + eq Document.getChild().containedConnectSpecification() = null; + eq Program.getChild().containedConnectSpecification() = null; + eq ConnectSpecification.getChild().containedConnectSpecification() = this; + // --- containedFile - eq Grammar.getChild().containedFile() = null; eq RagConnect.getChild().containedFile() = null; eq MRagConnect.getChild().containedFile() = null; // --- containedFileName --- - eq Grammar.getChild().containedFileName() = null; // should be in PP - eq RagConnect.getChild().containedFileName() = getFileName(); - eq MRagConnect.getChild().containedFileName() = null; + eq ConnectSpecificationFile.containedFileName() = getFileName(); + refine Navigation eq ASTNode.containedFileName() { + if (containedFile() == null) { + return containedConnectSpecification().containedFileName(); + } + return refined(); +// return containedFile().getFileName(); + } + + //--- allEndpointDefinitionList --- + syn List<EndpointDefinition> RagConnect.allEndpointDefinitionList() { + List<EndpointDefinition> result = new ArrayList<>(); + for (var spec : getConnectSpecificationFileList()) { + spec.getEndpointDefinitionList().forEach(result::add); + } + return result; + } + + //--- allDependencyDefinitionList --- + syn List<DependencyDefinition> RagConnect.allDependencyDefinitionList() { + List<DependencyDefinition> result = new ArrayList<>(); + for (var spec : getConnectSpecificationFileList()) { + spec.getDependencyDefinitionList().forEach(result::add); + } + return result; + } + + //--- allMappingDefinitionList --- + syn List<MappingDefinition> RagConnect.allMappingDefinitionList() { + List<MappingDefinition> result = new ArrayList<>(); + for (var spec : getConnectSpecificationFileList()) { + spec.getMappingDefinitionList().forEach(result::add); + } + return result; + } // --- isTokenEndpointDefinition --- syn boolean EndpointDefinition.isTokenEndpointDefinition() = false; @@ -27,13 +69,13 @@ aspect Navigation { syn TokenEndpointDefinition EndpointDefinition.asTokenEndpointDefinition() = null; eq TokenEndpointDefinition.asTokenEndpointDefinition() = this; - // --- isSendTokenEndpointDefinition --- - syn boolean EndpointDefinition.isSendTokenEndpointDefinition() = false; - eq SendTokenEndpointDefinition.isSendTokenEndpointDefinition() = true; + // --- isTypeEndpointDefinition --- + syn boolean EndpointDefinition.isTypeEndpointDefinition() = false; + eq TypeEndpointDefinition.isTypeEndpointDefinition() = true; - // --- asSendTokenEndpointDefinition --- - syn SendTokenEndpointDefinition EndpointDefinition.asSendTokenEndpointDefinition() = null; - eq SendTokenEndpointDefinition.asSendTokenEndpointDefinition() = this; + // --- asTypeEndpointDefinition --- + syn TypeEndpointDefinition EndpointDefinition.asTypeEndpointDefinition() = null; + eq TypeEndpointDefinition.asTypeEndpointDefinition() = this; // --- isReceiveTokenEndpointDefinition --- syn boolean EndpointDefinition.isReceiveTokenEndpointDefinition() = false; @@ -43,13 +85,41 @@ aspect Navigation { syn ReceiveTokenEndpointDefinition EndpointDefinition.asReceiveTokenEndpointDefinition() = null; eq ReceiveTokenEndpointDefinition.asReceiveTokenEndpointDefinition() = this; + // --- isSendTokenEndpointDefinition --- + syn boolean EndpointDefinition.isSendTokenEndpointDefinition() = false; + eq SendTokenEndpointDefinition.isSendTokenEndpointDefinition() = true; + + // --- asSendTokenEndpointDefinition --- + syn SendTokenEndpointDefinition EndpointDefinition.asSendTokenEndpointDefinition() = null; + eq SendTokenEndpointDefinition.asSendTokenEndpointDefinition() = this; + + // --- isReceiveTypeEndpointDefinition --- + syn boolean EndpointDefinition.isReceiveTypeEndpointDefinition() = false; + eq ReceiveTypeEndpointDefinition.isReceiveTypeEndpointDefinition() = true; + + // --- asReceiveTypeEndpointDefinition --- + syn ReceiveTypeEndpointDefinition EndpointDefinition.asReceiveTypeEndpointDefinition() = null; + eq ReceiveTypeEndpointDefinition.asReceiveTypeEndpointDefinition() = this; + + // --- isSendTypeEndpointDefinition --- + syn boolean EndpointDefinition.isSendTypeEndpointDefinition() = false; + eq SendTypeEndpointDefinition.isSendTypeEndpointDefinition() = true; + + // --- asSendTypeEndpointDefinition --- + syn SendTypeEndpointDefinition EndpointDefinition.asSendTypeEndpointDefinition() = null; + eq SendTypeEndpointDefinition.asSendTypeEndpointDefinition() = this; + // --- targetEndpointDefinition --- - syn SendTokenEndpointDefinition DependencyDefinition.targetEndpointDefinition() { + syn EndpointDefinition DependencyDefinition.targetEndpointDefinition() { // resolve definition in here, as we do not need resolveMethod in any other place (yet) - for (EndpointDefinition endpointDefinition : ragconnect().getEndpointDefinitionList()) { + for (EndpointDefinition endpointDefinition : ragconnect().allEndpointDefinitionList()) { if (endpointDefinition.isSendTokenEndpointDefinition() && endpointDefinition.asSendTokenEndpointDefinition().getToken().equals(this.getTarget())) { - return endpointDefinition.asSendTokenEndpointDefinition(); + return endpointDefinition; + } + if (endpointDefinition.isSendTypeEndpointDefinition() && + endpointDefinition.asSendTypeEndpointDefinition().getType().equals(this.getTarget())) { + return endpointDefinition; } } return null; @@ -68,4 +138,7 @@ aspect Navigation { // --- rootTypeComponents --- syn JastAddList<MTypeComponent> MHandler.rootTypeComponents() = mragconnect().getRootTypeComponents(); + + // --- isListComponent --- (defined in PP, but only on TypeComponent) + syn boolean Component.isListComponent() = false; } diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast index 9958106ba82c6efe32126c9f7b795ac7c66378e6..c1cbbb2445e55956f41ee780eab2e216175b6b6f 100644 --- a/ragconnect.base/src/main/jastadd/RagConnect.relast +++ b/ragconnect.base/src/main/jastadd/RagConnect.relast @@ -1,4 +1,7 @@ -RagConnect ::= EndpointDefinition* DependencyDefinition* MappingDefinition* Program <FileName> ; +RagConnect ::= ConnectSpecificationFile* Program ; + +abstract ConnectSpecification ::= EndpointDefinition* DependencyDefinition* MappingDefinition* ; +ConnectSpecificationFile : ConnectSpecification ::= <FileName> ; abstract EndpointDefinition ::= <AlwaysApply:boolean> ; @@ -10,9 +13,15 @@ rel TokenEndpointDefinition.Token <-> TokenComponent.TokenEndpointDefinition*; ReceiveTokenEndpointDefinition : TokenEndpointDefinition; SendTokenEndpointDefinition : TokenEndpointDefinition; +abstract TypeEndpointDefinition : EndpointDefinition ::= <UseList:boolean> ; +rel TypeEndpointDefinition.Type <-> TypeComponent.TypeEndpointDefinition*; + +ReceiveTypeEndpointDefinition : TypeEndpointDefinition ::= <WithAdd:boolean>; +SendTypeEndpointDefinition : TypeEndpointDefinition; + DependencyDefinition ::= <ID>; rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*; -rel DependencyDefinition.Target -> TokenComponent; +rel DependencyDefinition.Target -> Component; MappingDefinition ::= <ID> FromType:MappingDefinitionType <FromVariableName> ToType:MappingDefinitionType <Content> ; abstract MappingDefinitionType ::= ; diff --git a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd index 0c9190a1cfeed156c6353495f327244bef3ed4b0..7a09e6547c1ce7c635e2d2f37f9d7db77248ae68 100644 --- a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd +++ b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd @@ -4,6 +4,9 @@ Design considerations */ aspect AttributesForMustache { + // --- EndpointDefinition --- + syn String EndpointDefinition.idTokenName() = "InternalRagconnectTopicInList"; + // --- MRagConnect --- eq MRagConnect.getRootTypeComponent(int i).isFirst() = i == 0; @@ -18,48 +21,115 @@ aspect AttributesForMustache { syn String MRagConnect.javaHandlerAttribute() = "_javaHandler"; syn String MRagConnect.javaHandlerField() = "_javaHandler"; + syn boolean MRagConnect.hasTreeListEndpoints() = !sendingTreeListEndpoints().isEmpty() || !receivingTreeListEndpoints().isEmpty(); + syn List<MTypeEndpointDefinition> MRagConnect.sendingTreeListEndpoints() { + List<MTypeEndpointDefinition> result = new ArrayList<>(); + for (var mEndpointDef : getTypeSendDefinitionList()) { + if (mEndpointDef.typeIsList()) { + result.add(mEndpointDef); + } + } + return result; + } + syn List<MTypeEndpointDefinition> MRagConnect.receivingTreeListEndpoints() { + List<MTypeEndpointDefinition> result = new ArrayList<>(); + for (var mEndpointDef : getTypeReceiveDefinitionList()) { + if (mEndpointDef.typeIsList()) { + result.add(mEndpointDef); + } + } + return result; + } + syn List<TypeDecl> MRagConnect.typesForReceivingListEndpoints() { + return receivingTreeListEndpoints().stream() + .map(mEndpointDef -> mEndpointDef.type().getTypeDecl()) + .distinct() + .collect(java.util.stream.Collectors.toList()); + } + // --- MEndpointDefinition --- syn String MEndpointDefinition.preemptiveExpectedValue(); syn String MEndpointDefinition.preemptiveReturn(); - syn TokenEndpointDefinition MEndpointDefinition.endpointDef(); + syn EndpointDefinition MEndpointDefinition.endpointDef(); syn String MEndpointDefinition.firstInputVarName(); + syn String MEndpointDefinition.parentTypeName(); + syn String MEndpointDefinition.entityName(); + syn String MEndpointDefinition.updateMethod(); + syn String MEndpointDefinition.writeMethod(); + syn String MEndpointDefinition.getterMethod(); eq MEndpointDefinition.getInnerMappingDefinition(int i).isLast() = i == getNumInnerMappingDefinition() - 1; eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : getInnerMappingDefinition(i - 1).outputVarName(); syn String MEndpointDefinition.connectParameterName() = "uriString"; - syn String MEndpointDefinition.connectMethod() = "connect" + tokenName(); + syn String MEndpointDefinition.connectMethod() = "connect" + entityName(); + syn String MEndpointDefinition.internalConnectMethod() = "_internal_" + connectMethod(); + syn boolean MEndpointDefinition.isTypeEndpointDefinition() = endpointDef().isTypeEndpointDefinition(); syn String MEndpointDefinition.disconnectMethod() { // if both (send and receive) are defined for the token, ensure methods with different names - String extra = endpointDef().lookupTokenEndpointDefinitions(token()).size() > 1 ? uniqueSuffix() : ""; - return "disconnect" + extra + tokenName(); + String extra; + if (endpointDef().isTokenEndpointDefinition()) { + extra = endpointDef().asTokenEndpointDefinition().lookupTokenEndpointDefinitions(token()).size() > 1 ? uniqueSuffix() : ""; + } else if (endpointDef().isTypeEndpointDefinition()) { + extra = endpointDef().asTypeEndpointDefinition().lookupTypeEndpointDefinitions(type()).size() > 1 ? uniqueSuffix() : ""; + } else { + extra = ""; + } + return "disconnect" + extra + entityName(); } - // + syn String MEndpointDefinition.uniqueSuffix(); - eq MSendDefinition.uniqueSuffix() = "Send"; - eq MReceiveDefinition.uniqueSuffix() = "Receive"; + eq MTokenSendDefinition.uniqueSuffix() = "Send"; + eq MTokenReceiveDefinition.uniqueSuffix() = "Receive"; + eq MTypeSendDefinition.uniqueSuffix() = "Send"; + eq MTypeReceiveDefinition.uniqueSuffix() = "Receive"; - syn TokenComponent MEndpointDefinition.token() = endpointDef().getToken(); + // TODO potentially dangerous because asXEndpointDefinition can return null + syn TokenComponent MEndpointDefinition.token() = endpointDef().asTokenEndpointDefinition().getToken(); + syn TypeComponent MEndpointDefinition.type() = endpointDef().asTypeEndpointDefinition().getType(); syn boolean MEndpointDefinition.alwaysApply() = endpointDef().getAlwaysApply(); - syn String MEndpointDefinition.parentTypeName() = token().containingTypeDecl().getName(); + syn boolean MEndpointDefinition.typeIsList() = endpointDef().typeIsList(); syn String MEndpointDefinition.tokenName() = token().getName(); + syn String MEndpointDefinition.typeName() = type().getName(); + syn String MEndpointDefinition.typeDeclName() = type().getTypeDecl().getName(); syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1); syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().toType(); syn String MEndpointDefinition.lastResult() = lastDefinition().outputVarName(); syn String MEndpointDefinition.condition() { + // TODO probably, this has to be structured in a better way if (lastDefinition().mappingDef().getToType().isArray()) { return "java.util.Arrays.equals(" + preemptiveExpectedValue() + ", " + lastResult() + ")"; } - if (token().isPrimitiveType() && lastDefinition().mappingDef().getToType().isPrimitiveType()) { + if (endpointDef().isTokenEndpointDefinition() && token().isPrimitiveType() && lastDefinition().mappingDef().getToType().isPrimitiveType()) { return preemptiveExpectedValue() + " == " + lastResult(); } - if (lastDefinition().mappingDef().isDefaultMappingDefinition()) { + if (endpointDef().isReceiveTypeEndpointDefinition() && endpointDef().asReceiveTypeEndpointDefinition().getWithAdd()) { + // only check if received list is not null + return lastResult() + " == null"; + } + if (endpointDef().isTypeEndpointDefinition() && type().isOptComponent()) { + // use "hasX()" instead of "getX() != null" for optionals + return "has" + typeName() + "()" + " && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")"; + } + if (lastDefinition().mappingDef().getToType().isPrimitiveType() || lastDefinition().mappingDef().isDefaultMappingDefinition()) { return preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")"; } return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null"; } + // --- MTokenEndpointDefinition --- + eq MTokenEndpointDefinition.getterMethod() = "get" + tokenName(); + eq MTokenEndpointDefinition.parentTypeName() = token().containingTypeDecl().getName(); + eq MTokenEndpointDefinition.entityName() = tokenName(); + + // --- MTypeEndpointDefinition --- + syn boolean MTypeEndpointDefinition.isWithAdd() = endpointDef().isReceiveTypeEndpointDefinition() ? endpointDef().asReceiveTypeEndpointDefinition().getWithAdd() : false; + syn boolean MTypeEndpointDefinition.isUseList() = endpointDef().asTypeEndpointDefinition().getUseList(); + eq MTypeEndpointDefinition.getterMethod() = "get" + typeName() + (typeIsList() ? "List" : ""); + eq MTypeEndpointDefinition.parentTypeName() = type().containingTypeDecl().getName(); + eq MTypeEndpointDefinition.entityName() = typeName() + (isUseList() ? "List" : ""); + // --- MInnerMappingDefinition --- inh boolean MInnerMappingDefinition.isLast(); inh String MInnerMappingDefinition.inputVarName(); @@ -68,24 +138,50 @@ aspect AttributesForMustache { syn MappingDefinition MInnerMappingDefinition.mappingDef() = getMMappingDefinition().getMappingDefinition(); syn String MInnerMappingDefinition.outputVarName() = "result" + methodName(); // we do not need "_" in between here, because methodName begins with one - // --- MReceiveDefinition --- - eq MReceiveDefinition.preemptiveExpectedValue() = "get" + tokenName() + "()"; - eq MReceiveDefinition.preemptiveReturn() = "return;"; - eq MReceiveDefinition.endpointDef() = getReceiveTokenEndpointDefinition(); - eq MReceiveDefinition.firstInputVarName() = "message"; - - // --- MSendDefinition --- - eq MSendDefinition.preemptiveExpectedValue() = lastValue(); - eq MSendDefinition.preemptiveReturn() = "return false;"; - eq MSendDefinition.endpointDef() = getSendTokenEndpointDefinition(); - eq MSendDefinition.firstInputVarName() = "get" + tokenName() + "()"; - - syn String MSendDefinition.sender() = "_sender_" + tokenName(); - syn String MSendDefinition.lastValue() = "_lastValue" + tokenName(); - syn String MSendDefinition.updateMethod() = "_update_" + tokenName(); - syn String MSendDefinition.writeMethod() = "_writeLastValue_" + tokenName(); - syn String MSendDefinition.tokenResetMethod() = "get" + tokenName() + "_reset"; - syn boolean MSendDefinition.shouldSendValue() = endpointDef().shouldSendValue(); + // --- MTokenReceiveDefinition --- + eq MTokenReceiveDefinition.preemptiveExpectedValue() = getterMethod() + "()"; + eq MTokenReceiveDefinition.preemptiveReturn() = "return;"; + eq MTokenReceiveDefinition.endpointDef() = getReceiveTokenEndpointDefinition(); + eq MTokenReceiveDefinition.firstInputVarName() = "message"; + eq MTokenReceiveDefinition.updateMethod() = null; + eq MTokenReceiveDefinition.writeMethod() = null; + + // --- MTokenSendDefinition --- + eq MTokenSendDefinition.preemptiveExpectedValue() = lastValue(); + eq MTokenSendDefinition.preemptiveReturn() = "return false;"; + eq MTokenSendDefinition.endpointDef() = getSendTokenEndpointDefinition(); + eq MTokenSendDefinition.firstInputVarName() = getterMethod() + "()"; + eq MTokenSendDefinition.updateMethod() = "_update_" + tokenName(); + eq MTokenSendDefinition.writeMethod() = "_writeLastValue_" + tokenName(); + + syn String MTokenSendDefinition.sender() = "_sender_" + tokenName(); + syn String MTokenSendDefinition.lastValue() = "_lastValue" + tokenName(); + syn String MTokenSendDefinition.tokenResetMethod() = getterMethod() + "_reset"; + syn boolean MTokenSendDefinition.shouldSendValue() = endpointDef().asTokenEndpointDefinition().shouldSendValue(); + + // MTypeReceiveDefinition + eq MTypeReceiveDefinition.preemptiveExpectedValue() = getterMethod() + "()"; + eq MTypeReceiveDefinition.preemptiveReturn() = "return;"; + eq MTypeReceiveDefinition.endpointDef() = getReceiveTypeEndpointDefinition(); + eq MTypeReceiveDefinition.firstInputVarName() = "message"; + eq MTypeReceiveDefinition.updateMethod() = null; + eq MTypeReceiveDefinition.writeMethod() = null; + + syn String MTypeReceiveDefinition.resolveInListAttributeName() = "resolve" + entityName() + "InList"; + syn String MTypeReceiveDefinition.idTokenName() = endpointDef().idTokenName(); + + // MTypeSendDefinition + eq MTypeSendDefinition.preemptiveExpectedValue() = lastValue(); + eq MTypeSendDefinition.preemptiveReturn() = "return false;"; + eq MTypeSendDefinition.endpointDef() = getSendTypeEndpointDefinition(); + eq MTypeSendDefinition.firstInputVarName() = getterMethod() + "()"; + eq MTypeSendDefinition.updateMethod() = "_update_" + typeName(); + eq MTypeSendDefinition.writeMethod() = "_writeLastValue_" + typeName(); + + syn String MTypeSendDefinition.sender() = "_sender_" + typeName(); + syn String MTypeSendDefinition.lastValue() = "_lastValue" + typeName(); + syn String MTypeSendDefinition.tokenResetMethod() = getterMethod() + "_reset"; + syn boolean MTypeSendDefinition.shouldSendValue() = endpointDef().asTypeEndpointDefinition().shouldSendValue(); // --- MMappingDefinition --- syn String MMappingDefinition.toType() = getMappingDefinition().getToType().prettyPrint(); @@ -93,13 +189,14 @@ aspect AttributesForMustache { syn String MMappingDefinition.fromType() = getMappingDefinition().getFromType().prettyPrint(); syn String MMappingDefinition.fromVariableName() = getMappingDefinition().getFromVariableName(); syn String MMappingDefinition.content() = getMappingDefinition().getContent(); + syn boolean MMappingDefinition.isUsed() = !getMappingDefinition().effectiveUsedAt().isEmpty(); // --- MDependencyDefinition --- syn String MDependencyDefinition.targetParentTypeName() = getDependencyDefinition().getTarget().containingTypeDecl().getName(); syn String MDependencyDefinition.dependencyMethod() = "add" + capitalize(getDependencyDefinition().getID()); syn String MDependencyDefinition.sourceParentTypeName() = getDependencyDefinition().getSource().containingTypeDecl().getName(); syn String MDependencyDefinition.internalRelationPrefix() = "_internal_" + getDependencyDefinition().getID(); - syn nta MSendDefinition MDependencyDefinition.targetEndpointDefinition() { + syn nta MEndpointDefinition MDependencyDefinition.targetEndpointDefinition() { return getDependencyDefinition().targetEndpointDefinition().toMustache(); } @@ -113,7 +210,7 @@ aspect AttributesForMustache { syn String MTokenComponent.javaType() = getTokenComponent().effectiveJavaTypeUse().prettyPrint(); syn String MTokenComponent.internalName() = getTokenComponent().needProxyToken() ? "_internal_" + name() : externalName(); syn String MTokenComponent.externalName() = name(); - syn MSendDefinition MTokenComponent.normalTokenSendDef() { + syn MTokenSendDefinition MTokenComponent.normalTokenSendDef() { for (TokenEndpointDefinition endpointDef : getTokenComponent().getTokenEndpointDefinitionList()) { if (endpointDef.shouldSendValue()) { return endpointDef.asSendTokenEndpointDefinition().toMustache(); @@ -126,18 +223,23 @@ aspect AttributesForMustache { syn lazy MRagConnect RagConnect.toMustache() { MRagConnect result = new MRagConnect(); result.setRagConnect(this); - for (EndpointDefinition def : getEndpointDefinitionList()) { - if (def.isSendTokenEndpointDefinition()) { - SendTokenEndpointDefinition sendDef = def.asSendTokenEndpointDefinition(); - result.addSendDefinition(sendDef.toMustache()); + for (EndpointDefinition def : allEndpointDefinitionList()) { + if (def.isReceiveTokenEndpointDefinition()) { + result.addTokenReceiveDefinition(def.asReceiveTokenEndpointDefinition().toMustache()); + } else if (def.isSendTokenEndpointDefinition()) { + result.addTokenSendDefinition(def.asSendTokenEndpointDefinition().toMustache()); + } else if (def.isReceiveTypeEndpointDefinition()) { + result.addTypeReceiveDefinition(def.asReceiveTypeEndpointDefinition().toMustache()); + } else if (def.isSendTypeEndpointDefinition()) { + result.addTypeSendDefinition(def.asSendTypeEndpointDefinition().toMustache()); } else { - result.addReceiveDefinition(def.asReceiveTokenEndpointDefinition().toMustache()); + throw new RuntimeException("Unknown endpoint definition: " + def); } } for (MappingDefinition def : allMappingDefinitions()) { result.addMappingDefinition(def.toMustache()); } - for (DependencyDefinition def : getDependencyDefinitionList()) { + for (DependencyDefinition def : allDependencyDefinitionList()) { result.addDependencyDefinition(def.toMustache()); } for (TokenComponent token : getProgram().allTokenComponents()) { @@ -168,20 +270,35 @@ aspect AttributesForMustache { } } - syn lazy MReceiveDefinition ReceiveTokenEndpointDefinition.toMustache() { - MReceiveDefinition result = new MReceiveDefinition(); + public abstract MEndpointDefinition EndpointDefinition.toMustache(); + syn lazy MTokenReceiveDefinition ReceiveTokenEndpointDefinition.toMustache() { + MTokenReceiveDefinition result = new MTokenReceiveDefinition(); result.setReceiveTokenEndpointDefinition(this); result.addInnerMappings(); return result; } - syn lazy MSendDefinition SendTokenEndpointDefinition.toMustache() { - MSendDefinition result = new MSendDefinition(); + syn lazy MTokenSendDefinition SendTokenEndpointDefinition.toMustache() { + MTokenSendDefinition result = new MTokenSendDefinition(); result.setSendTokenEndpointDefinition(this); result.addInnerMappings(); return result; } + syn lazy MTypeReceiveDefinition ReceiveTypeEndpointDefinition.toMustache() { + MTypeReceiveDefinition result = new MTypeReceiveDefinition(); + result.setReceiveTypeEndpointDefinition(this); + result.addInnerMappings(); + return result; + } + + syn lazy MTypeSendDefinition SendTypeEndpointDefinition.toMustache() { + MTypeSendDefinition result = new MTypeSendDefinition(); + result.setSendTypeEndpointDefinition(this); + result.addInnerMappings(); + return result; + } + syn lazy MMappingDefinition MappingDefinition.toMustache() { MMappingDefinition result = new MMappingDefinition(); result.setMappingDefinition(this); @@ -244,10 +361,10 @@ aspect AspectGeneration { } } -aspect RelationGeneration { +aspect GrammarGeneration { syn java.util.List<Relation> RagConnect.additionalRelations() { java.util.List<Relation> result = new java.util.ArrayList<>(); - for (DependencyDefinition dd : getDependencyDefinitionList()) { + for (DependencyDefinition dd : allDependencyDefinitionList()) { result.add(dd.getRelationToCreate()); } return result; @@ -265,6 +382,37 @@ aspect RelationGeneration { result.addComment(new WhitespaceComment("\n")); return result; } + +// coll java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() [new java.util.HashMap<>()] with put root RagConnect; + +// TypeEndpointDefinition contributes getTokenToCreate() +// when typeIsList() && !getUseList() +// to RagConnect.additionalTokens() +//// for ragconnect() +// ; + + syn java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() { + java.util.Map<TypeDecl, TokenComponent> result = new java.util.HashMap<>(); + for (EndpointDefinition def : allEndpointDefinitionList()) { + if (def.isTypeEndpointDefinition() && def.getTokenToCreate() != null) { + result.put(def.asTypeEndpointDefinition().getType().getTypeDecl(), def.getTokenToCreate()); + } + } + return result; + } + + syn TokenComponent EndpointDefinition.getTokenToCreate() = null; + eq TypeEndpointDefinition.getTokenToCreate() { + if (typeIsList() && !getUseList()) { + TokenComponent result = new TokenComponent(); + result.setName(idTokenName()); + result.setNTA(false); + result.setJavaTypeUse(new SimpleJavaTypeUse("String")); + return result; + } else { + return null; + } + } } aspect GrammarExtension { diff --git a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag b/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag index 7aa82d304079592d605997484d1348a3d3681875..3a7fce51404e43be679d7b8829127099fd78b683 100644 --- a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag +++ b/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag @@ -1,7 +1,7 @@ aspect DefaultMappings { private String RagConnect.baseDefaultMappingTypeNamePart(String typeName) { - return capitalize(typeName).replace("[]", "s"); + return capitalize(typeName).replace("[]", "s").replace("<", "").replace(">", "List"); } private MappingDefinitionType RagConnect.baseDefaultMappingTypeFromName(String typeName) { @@ -10,9 +10,9 @@ aspect DefaultMappings { new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName)); } - private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinition(String fromTypeName, String toTypeName, String content) { + private DefaultMappingDefinition RagConnect.createDefaultMappingDefinition(String prefix, String fromTypeName, String toTypeName, String content) { DefaultMappingDefinition result = new DefaultMappingDefinition(); - result.setID("_Default" + baseDefaultMappingTypeNamePart(fromTypeName) + "To" + baseDefaultMappingTypeNamePart(toTypeName) + "Mapping"); + result.setID(prefix + baseDefaultMappingTypeNamePart(fromTypeName) + "To" + baseDefaultMappingTypeNamePart(toTypeName) + "Mapping"); result.setFromType(baseDefaultMappingTypeFromName(fromTypeName)); result.setFromVariableName("input"); result.setToType(baseDefaultMappingTypeFromName(toTypeName)); @@ -20,6 +20,16 @@ aspect DefaultMappings { return result; } + private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinition(String fromTypeName, String toTypeName, String content) { + return createDefaultMappingDefinition("_Default", fromTypeName, toTypeName, content); + } + + private DefaultMappingDefinition RagConnect.treeDefaultMappingDefinition(String fromTypeName, String toTypeName, String content) { + return createDefaultMappingDefinition("_TreeDefault", fromTypeName, toTypeName, content); + } + + syn nta DefaultMappingDefinition RagConnect.defaultBytesToBooleanMapping() = baseDefaultMappingDefinition( + "byte[]", "boolean", "return input[0] == (byte) 1;"); syn nta DefaultMappingDefinition RagConnect.defaultBytesToIntMapping() = baseDefaultMappingDefinition( "byte[]", "int", "return java.nio.ByteBuffer.wrap(input).getInt();"); syn nta DefaultMappingDefinition RagConnect.defaultBytesToShortMapping() = baseDefaultMappingDefinition( @@ -35,6 +45,52 @@ aspect DefaultMappings { syn nta DefaultMappingDefinition RagConnect.defaultBytesToStringMapping() = baseDefaultMappingDefinition( "byte[]", "String", "return new String(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToTreeMapping(String typeName) { + return treeDefaultMappingDefinition("byte[]", typeName, + "String content = new String(input);\n" + + "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" + + "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + + "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" + + typeName + " result = " + typeName + ".deserialize((com.fasterxml.jackson.databind.JsonNode)mapper.readTree(parser));\n" + + "parser.close();\n" + + "return result;" + ); + } + syn nta DefaultMappingDefinition RagConnect.defaultTreeToBytesMapping(String typeName) { + return treeDefaultMappingDefinition(typeName, "byte[]", + "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" + + "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + + "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n"+ + "input.serialize(generator);\n" + + "generator.flush();\n" + + "return outputStream.toString().getBytes();" + ); + } + + syn nta DefaultMappingDefinition RagConnect.defaultBytesToListTreeMapping(String typeName) { + return treeDefaultMappingDefinition("byte[]", JastAddList + "<" + typeName + ">", + "String content = new String(input);\n" + + "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" + + "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + + "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" + + JastAddList + "<" + typeName + ">" + " result = " + typeName + ".deserializeList((com.fasterxml.jackson.databind.node.ArrayNode)mapper.readTree(parser));\n" + + "parser.close();\n" + + "return result;" + ); + } + syn nta DefaultMappingDefinition RagConnect.defaultListTreeToBytesMapping() { + return treeDefaultMappingDefinition(JastAddList, "byte[]", + "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" + + "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + + "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n"+ + "input.serialize(generator);\n" + + "generator.flush();\n" + + "return outputStream.toString().getBytes();" + ); + } + + syn nta DefaultMappingDefinition RagConnect.defaultBooleanToBytesMapping() = baseDefaultMappingDefinition( + "boolean", "byte[]", "return java.nio.ByteBuffer.allocate(1).put((byte) (input ? 1 : 0)).array();"); syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() = baseDefaultMappingDefinition( "int", "byte[]", "return java.nio.ByteBuffer.allocate(Integer.BYTES).putInt(input).array();"); syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() = baseDefaultMappingDefinition( @@ -50,6 +106,8 @@ aspect DefaultMappings { syn nta DefaultMappingDefinition RagConnect.defaultStringToBytesMapping() = baseDefaultMappingDefinition( "String", "byte[]", "return input.getBytes();"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToBooleanMapping() = baseDefaultMappingDefinition( + "String", "boolean", "return Boolean.parseBoolean(input);"); syn nta DefaultMappingDefinition RagConnect.defaultStringToIntMapping() = baseDefaultMappingDefinition( "String", "int", "return Integer.parseInt(input);"); syn nta DefaultMappingDefinition RagConnect.defaultStringToShortMapping() = baseDefaultMappingDefinition( @@ -63,6 +121,8 @@ aspect DefaultMappings { syn nta DefaultMappingDefinition RagConnect.defaultStringToCharMapping() = baseDefaultMappingDefinition( "String", "char", "return input.charAt(0);"); + syn nta DefaultMappingDefinition RagConnect.defaultBooleanToStringMapping() = baseDefaultMappingDefinition( + "boolean", "String", "return String.valueOf(input);"); syn nta DefaultMappingDefinition RagConnect.defaultIntToStringMapping() = baseDefaultMappingDefinition( "int", "String", "return String.valueOf(input);"); syn nta DefaultMappingDefinition RagConnect.defaultShortToStringMapping() = baseDefaultMappingDefinition( @@ -79,50 +139,39 @@ aspect DefaultMappings { aspect Mappings { // --- effectiveMappings --- - syn java.util.List<MappingDefinition> EndpointDefinition.effectiveMappings(); - eq ReceiveTokenEndpointDefinition.effectiveMappings() { - // if there is a first mapping, check if it is suitable. - // or if no mappings are specified. - // then prepend the suitable default mapping + syn java.util.List<MappingDefinition> EndpointDefinition.effectiveMappings() { java.util.List<MappingDefinition> result; - if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) { - result = new java.util.ArrayList(); - result.add(suitableDefaultMapping()); - result.addAll(getMappingList()); + if (isReceiveTokenEndpointDefinition() || isReceiveTypeEndpointDefinition()) { + // if no mappings are specified, or if first mapping is not suitable. + // then prepend the suitable default mapping + if (getMappingList().isEmpty() || !getMappingList().get(0).getFromType().isByteArray()) { + result = new java.util.ArrayList(); + result.add(suitableReceiveDefaultMapping()); + result.addAll(getMappingList()); + } else { + result = getMappingList(); + } + } else if (isSendTokenEndpointDefinition() || isSendTypeEndpointDefinition()) { + // if no mappings are specified, or if last mapping is not suitable + // then append the suitable default mapping + if (getMappingList().isEmpty() || !getMappingList().get(getMappingList().size() - 1).getToType().isByteArray()) { + result = new java.util.ArrayList(getMappingList()); + result.add(suitableSendDefaultMapping()); + } else { + result = getMappingList(); + } } else { - result = getMappingList(); + throw new RuntimeException("Unknown endpoint definition: " + this); } return result; } - eq SendTokenEndpointDefinition.effectiveMappings() { - // if there is a mapping, check if it is suitable. - // or if no mappings are specified. - // then append the suitable default mapping - java.util.List<MappingDefinition> result; - if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) { - result = new java.util.ArrayList(getMappingList()); - result.add(suitableDefaultMapping()); - } else { - result = getMappingList(); - } - return result; - } - - // --- hasSuitableEdgeMapping --- - syn boolean TokenEndpointDefinition.hasSuitableEdgeMapping(); - eq ReceiveTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(0)); - eq SendTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(getMappingList().size() - 1)); - - // --- isSuitableEdgeMapping(def) --- - syn boolean TokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def); - eq ReceiveTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getFromType().isByteArray(); - eq SendTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getToType().isByteArray(); // --- isPrimitiveType --- syn boolean TokenComponent.isPrimitiveType() = effectiveJavaTypeUse().isPrimitiveType(); syn boolean JavaTypeUse.isPrimitiveType() = false; eq SimpleJavaTypeUse.isPrimitiveType() { switch(getName()) { + case "boolean": case "int": case "short": case "long": @@ -140,13 +189,11 @@ aspect Mappings { syn boolean MappingDefinitionType.isArray() = false; eq JavaArrayMappingDefinitionType.isArray() = true; - // --- suitableDefaultMapping --- - syn DefaultMappingDefinition EndpointDefinition.suitableDefaultMapping(); - eq ReceiveTokenEndpointDefinition.suitableDefaultMapping() { - String typeName = getMappingList().isEmpty() ? - getToken().effectiveJavaTypeUse().getName() : - getMappingList().get(0).getFromType().prettyPrint(); - switch(typeName) { + // --- suitableReceiveDefaultMapping --- + syn DefaultMappingDefinition EndpointDefinition.suitableReceiveDefaultMapping() { + switch (targetTypeName()) { + case "boolean": + case "Boolean": return ragconnect().defaultBytesToBooleanMapping(); case "int": case "Integer": return ragconnect().defaultBytesToIntMapping(); case "short": @@ -160,14 +207,29 @@ aspect Mappings { case "char": case "Character": return ragconnect().defaultBytesToCharMapping(); case "String": return ragconnect().defaultBytesToStringMapping(); - default: return null; + default: + try { + TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName()); + // TODO: also support list-types, if list is first type + return ragconnect().defaultBytesToTreeMapping(typeDecl.getName()); + } catch (Exception ignore) {} + System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this); + return null; } } - eq SendTokenEndpointDefinition.suitableDefaultMapping() { - String typeName = getMappingList().isEmpty() ? - getToken().effectiveJavaTypeUse().getName() : - getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint(); - switch(typeName) { + eq TypeEndpointDefinition.suitableReceiveDefaultMapping() { + try { + TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName()); + return typeIsList() && getUseList() ? ragconnect().defaultBytesToListTreeMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName()); + } catch (Exception ignore) {} + return super.suitableReceiveDefaultMapping(); + } + + // --- suitableSendDefaultMapping --- + syn DefaultMappingDefinition EndpointDefinition.suitableSendDefaultMapping() { + switch (targetTypeName()) { + case "boolean": + case "Boolean": return ragconnect().defaultBooleanToBytesMapping(); case "int": case "Integer": return ragconnect().defaultIntToBytesMapping(); case "short": @@ -181,9 +243,47 @@ aspect Mappings { case "char": case "Character": return ragconnect().defaultCharToBytesMapping(); case "String": return ragconnect().defaultStringToBytesMapping(); - default: return null; + default: + try { + TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName()); + // TODO: also support list-types, if list is last type + return ragconnect().defaultTreeToBytesMapping(typeDecl.getName()); + } catch (Exception ignore) {} + System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this); + return null; } } + eq TypeEndpointDefinition.suitableSendDefaultMapping() { + try { + TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName()); + return typeIsList() && getUseList() ? ragconnect().defaultListTreeToBytesMapping() : ragconnect().defaultTreeToBytesMapping(typeDecl.getName()); + } catch (Exception ignore) {} + return super.suitableSendDefaultMapping(); + } + + // --- targetTypeName --- + syn String EndpointDefinition.targetTypeName(); + eq ReceiveTokenEndpointDefinition.targetTypeName() { + return getMappingList().isEmpty() ? + getToken().effectiveJavaTypeUse().getName() : + getMappingList().get(0).getFromType().prettyPrint(); + } + eq ReceiveTypeEndpointDefinition.targetTypeName() { + return getMappingList().isEmpty() ? + getType().getTypeDecl().getName() : + getMappingList().get(0).getFromType().prettyPrint(); + } + eq SendTokenEndpointDefinition.targetTypeName() { + return getMappingList().isEmpty() ? + getToken().effectiveJavaTypeUse().getName() : + getMappingList().get(getMappingList().size() - 1).getToType().prettyPrint(); + } + eq SendTypeEndpointDefinition.targetTypeName() { + return getMappingList().isEmpty() ? + getType().getTypeDecl().getName() : + getMappingList().get(getMappingList().size() - 1).getToType().prettyPrint(); + } + // eq ReceiveFromRestDefinition.suitableDefaultMapping() { // String typeName = getMappingList().isEmpty() ? // getToken().getJavaTypeUse().getName() : @@ -236,8 +336,10 @@ aspect Mappings { // --- allMappingDefinitions --- syn java.util.List<MappingDefinition> RagConnect.allMappingDefinitions() { java.util.List<MappingDefinition> result = new java.util.ArrayList<>(); - getMappingDefinitionList().iterator().forEachRemaining(result::add); - // byte[] conversion + // user-defined mappings + allMappingDefinitionList().iterator().forEachRemaining(result::add); + // byte[] <-> primitive conversion + result.add(defaultBytesToBooleanMapping()); result.add(defaultBytesToIntMapping()); result.add(defaultBytesToShortMapping()); result.add(defaultBytesToLongMapping()); @@ -245,6 +347,7 @@ aspect Mappings { result.add(defaultBytesToDoubleMapping()); result.add(defaultBytesToCharMapping()); result.add(defaultBytesToStringMapping()); + result.add(defaultBooleanToBytesMapping()); result.add(defaultIntToBytesMapping()); result.add(defaultShortToBytesMapping()); result.add(defaultLongToBytesMapping()); @@ -252,13 +355,22 @@ aspect Mappings { result.add(defaultDoubleToBytesMapping()); result.add(defaultCharToBytesMapping()); result.add(defaultStringToBytesMapping()); + // byte[] <-> tree conversion + for (TypeDecl typeDecl : getProgram().typeDecls()) { + result.add(defaultBytesToTreeMapping(typeDecl.getName())); + result.add(defaultTreeToBytesMapping(typeDecl.getName())); + result.add(defaultBytesToListTreeMapping(typeDecl.getName())); + } + result.add(defaultListTreeToBytesMapping()); // // string conversion +// result.add(defaultStringToBooleanMapping()); // result.add(defaultStringToIntMapping()); // result.add(defaultStringToShortMapping()); // result.add(defaultStringToLongMapping()); // result.add(defaultStringToFloatMapping()); // result.add(defaultStringToDoubleMapping()); // result.add(defaultStringToCharMapping()); +// result.add(defaultBooleanToStringMapping()); // result.add(defaultIntToStringMapping()); // result.add(defaultShortToStringMapping()); // result.add(defaultLongToStringMapping()); diff --git a/ragconnect.base/src/main/jastadd/intermediate/MustacheNodes.relast b/ragconnect.base/src/main/jastadd/intermediate/MustacheNodes.relast index 06843177098ba889b665257c9ba2f965f5ecda88..8255cfe5ad9e67e00e53e95b4ddc110a480dce28 100644 --- a/ragconnect.base/src/main/jastadd/intermediate/MustacheNodes.relast +++ b/ragconnect.base/src/main/jastadd/intermediate/MustacheNodes.relast @@ -1,7 +1,13 @@ -MRagConnect ::= ReceiveDefinition:MReceiveDefinition* SendDefinition:MSendDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent* Handler:MHandler*; +MRagConnect ::= TokenReceiveDefinition:MTokenReceiveDefinition* TokenSendDefinition:MTokenSendDefinition* TypeReceiveDefinition:MTypeReceiveDefinition* TypeSendDefinition:MTypeSendDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent* Handler:MHandler*; + abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*; -MReceiveDefinition : MEndpointDefinition; -MSendDefinition : MEndpointDefinition; +abstract MTokenEndpointDefinition : MEndpointDefinition; +MTokenReceiveDefinition : MTokenEndpointDefinition; +MTokenSendDefinition : MTokenEndpointDefinition; +abstract MTypeEndpointDefinition : MEndpointDefinition; +MTypeReceiveDefinition : MTypeEndpointDefinition; +MTypeSendDefinition : MTypeEndpointDefinition; + MMappingDefinition; MInnerMappingDefinition; MDependencyDefinition; @@ -11,8 +17,10 @@ MHandler ::= <ClassName> <Construction> <AttributeName> <FieldName> <InUse:boole rel MRagConnect.RagConnect -> RagConnect; rel MInnerMappingDefinition.MMappingDefinition -> MMappingDefinition; -rel MReceiveDefinition.ReceiveTokenEndpointDefinition -> ReceiveTokenEndpointDefinition; -rel MSendDefinition.SendTokenEndpointDefinition -> SendTokenEndpointDefinition; +rel MTokenReceiveDefinition.ReceiveTokenEndpointDefinition -> ReceiveTokenEndpointDefinition; +rel MTokenSendDefinition.SendTokenEndpointDefinition -> SendTokenEndpointDefinition; +rel MTypeReceiveDefinition.ReceiveTypeEndpointDefinition -> ReceiveTypeEndpointDefinition; +rel MTypeSendDefinition.SendTypeEndpointDefinition -> SendTypeEndpointDefinition; rel MMappingDefinition.MappingDefinition -> MappingDefinition; rel MDependencyDefinition.DependencyDefinition -> DependencyDefinition; rel MTypeComponent.TypeComponent -> TypeComponent; diff --git a/ragconnect.base/src/main/jastadd/intermediate2mustache/MustacheNodesToYAML.jrag b/ragconnect.base/src/main/jastadd/intermediate2mustache/MustacheNodesToYAML.jrag index 4688e9915ddb04a97c7d53b637977f54e1c9b803..bd3afcd607a3be9757ec920251d48080fccd1901 100644 --- a/ragconnect.base/src/main/jastadd/intermediate2mustache/MustacheNodesToYAML.jrag +++ b/ragconnect.base/src/main/jastadd/intermediate2mustache/MustacheNodesToYAML.jrag @@ -31,19 +31,33 @@ aspect MustacheNodesToYAML { root.put("restHandlerField", restHandlerField()); root.put("restHandlerAttribute", restHandlerAttribute()); - // ReceiveDefinitions + // TokenReceiveDefinitions ListElement receiveDefinitions = new ListElement(); - for (MReceiveDefinition def : getReceiveDefinitionList()) { + for (MTokenReceiveDefinition def : getTokenReceiveDefinitionList()) { receiveDefinitions.addElement(def.toYAML()); } - root.put("ReceiveDefinitions", receiveDefinitions); + root.put("TokenReceiveDefinitions", receiveDefinitions); - // SendDefinitions + // TokenSendDefinitions ListElement sendDefinitions = new ListElement(); - for (MSendDefinition def : getSendDefinitionList()) { + for (MTokenSendDefinition def : getTokenSendDefinitionList()) { sendDefinitions.addElement(def.toYAML()); } - root.put("SendDefinitions", sendDefinitions); + root.put("TokenSendDefinitions", sendDefinitions); + + // TypeReceiveDefinitions + ListElement typeReceiveDefinitions = new ListElement(); + for (MTypeReceiveDefinition def : getTypeReceiveDefinitionList()) { + typeReceiveDefinitions.addElement(def.toYAML()); + } + root.put("TypeReceiveDefinitions", typeReceiveDefinitions); + + // TypeSendDefinitions + ListElement typeSendDefinitions = new ListElement(); + for (MTypeSendDefinition def : getTypeSendDefinitionList()) { + typeSendDefinitions.addElement(def.toYAML()); + } + root.put("TypeSendDefinitions", typeSendDefinitions); // MappingDefinitions ListElement mappingDefinitions = new ListElement(); @@ -94,14 +108,33 @@ aspect MustacheNodesToYAML { return result; } - syn MappingElement MReceiveDefinition.toYAML() { + syn MappingElement MTokenReceiveDefinition.toYAML() { + MappingElement result = super.toYAML(); + result.put("loggingEnabledForReads", loggingEnabledForReads); + return result; + } + + syn MappingElement MTokenSendDefinition.toYAML() { + MappingElement result = super.toYAML(); + result.put("sender", sender()); + result.put("lastValue", lastValue()); + result.put("loggingEnabledForWrites", loggingEnabledForWrites); + result.put("updateMethod", updateMethod()); + result.put("writeMethod", writeMethod()); + result.put("tokenResetMethod", tokenResetMethod()); + return result; + } + + syn MappingElement MTypeReceiveDefinition.toYAML() { MappingElement result = super.toYAML(); + result.put("typeIsList", typeIsList()); result.put("loggingEnabledForReads", loggingEnabledForReads); return result; } - syn MappingElement MSendDefinition.toYAML() { + syn MappingElement MTypeSendDefinition.toYAML() { MappingElement result = super.toYAML(); + result.put("typeIsList", typeIsList()); result.put("sender", sender()); result.put("lastValue", lastValue()); result.put("loggingEnabledForWrites", loggingEnabledForWrites); @@ -181,5 +214,5 @@ aspect Navigation { eq Document.getChild().program() = null; eq Document.getChild().ragconnect() = null; eq Document.getChild().containedFile() = null; - eq Document.getChild().containedFileName() = getFileName(); + eq Document.containedFileName() = getFileName(); } diff --git a/ragconnect.base/src/main/jastadd/parser/Preamble.parser b/ragconnect.base/src/main/jastadd/parser/Preamble.parser index d701db19d6edb6d6c616bc79fc0145ea59d2713d..c157ae91f69b0e8d41c40a5bd7426bb2d7fae803 100644 --- a/ragconnect.base/src/main/jastadd/parser/Preamble.parser +++ b/ragconnect.base/src/main/jastadd/parser/Preamble.parser @@ -4,4 +4,4 @@ import org.jastadd.ragconnect.ast.*; :}; %goal goal; -%goal ragconnect; +%goal connect_specification_file; diff --git a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser index 0432b0df96c44060f6df1d6918a856f62810230d..a7ba289535a01938c1284ad67f46b2a9e81b9fb5 100644 --- a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser +++ b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser @@ -1,9 +1,21 @@ -RagConnect ragconnect - = endpoint_definition.d ragconnect.r {: r.getEndpointDefinitionList().insertChild(d, 0); return r; :} - | dependency_definition.d ragconnect.r {: r.getDependencyDefinitionList().insertChild(d, 0); return r; :} - | mapping_definition.d ragconnect.r {: r.getMappingDefinitionList().insertChild(d, 0); return r; :} - | comment ragconnect.r {: return r; :} - | {: return new RagConnect(); :} +ConnectSpecificationFile connect_specification_file + = endpoint_definition.d connect_specification_file.r + {: + r.getEndpointDefinitionList().insertChild(d, 0); return r; + :} + | dependency_definition.d connect_specification_file.r + {: + r.getDependencyDefinitionList().insertChild(d, 0); return r; + :} + | mapping_definition.d connect_specification_file.r + {: + r.getMappingDefinitionList().insertChild(d, 0); return r; + :} + | comment connect_specification_file.r + {: + return r; + :} + | {: return new ConnectSpecificationFile(); :} ; %embed {: @@ -33,23 +45,58 @@ EndpointDefinition endpoint_definition EndpointDefinition endpoint_definition_type = RECEIVE token_ref {: return new ReceiveTokenEndpointDefinition().setToken(token_ref); :} | SEND token_ref {: return new SendTokenEndpointDefinition().setToken(token_ref); :} + | RECEIVE TREE type_ref {: return new ReceiveTypeEndpointDefinition().setType(type_ref); :} + | RECEIVE TREE WITH ADD type_ref + {: + ReceiveTypeEndpointDefinition result = new ReceiveTypeEndpointDefinition(); + result.setType(type_ref); + result.setWithAdd(true); + return result; + :} + | SEND TREE type_ref {: return new SendTypeEndpointDefinition().setType(type_ref); :} + | RECEIVE LIST type_ref + {: + ReceiveTypeEndpointDefinition result = new ReceiveTypeEndpointDefinition(); + result.setType(type_ref); + result.setUseList(true); + return result; + :} + | RECEIVE LIST WITH ADD type_ref + {: + ReceiveTypeEndpointDefinition result = new ReceiveTypeEndpointDefinition(); + result.setType(type_ref); + result.setWithAdd(true); + result.setUseList(true); + return result; + :} + | SEND LIST type_ref + {: + SendTypeEndpointDefinition result = new SendTypeEndpointDefinition(); + result.setType(type_ref); + result.setUseList(true); + return result; + :} ; TokenComponent token_ref = ID.type_name DOT ID.token_name {: return TokenComponent.createRef(type_name + "." + token_name); :} ; +TypeComponent type_ref + = ID.parent_type_name DOT ID.child_type_name {: return TypeComponent.createRef(parent_type_name + "." + child_type_name); :} +; + ArrayList string_list = ID | string_list COMMA ID ; DependencyDefinition dependency_definition - = ID.target_type DOT ID.target_token CAN_DEPEND_ON ID.source_type DOT ID.source_token AS ID.id SCOL + = ID.target_type DOT ID.target_component CAN_DEPEND_ON ID.source_type DOT ID.source_token AS ID.id SCOL {: DependencyDefinition result = new DependencyDefinition(); result.setSource(TokenComponent.createRef(source_type + "." + source_token)); - result.setTarget(TokenComponent.createRef(target_type + "." + target_token)); + result.setTarget(Component.createRef(target_type + "." + target_component)); result.setID(id); return result; :} diff --git a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex index 5773118d8fda8ffdc536b504aa7056b8784ddfb3..69830108bfb0644cd75008b47b58ee149dc3b2ad 100644 --- a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex +++ b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex @@ -5,3 +5,7 @@ "maps" { return sym(Terminals.MAPS); } "to" { return sym(Terminals.TO); } "as" { return sym(Terminals.AS); } +"tree" { return sym(Terminals.TREE); } +"list" { return sym(Terminals.LIST); } +"with" { return sym(Terminals.WITH); } +"add" { return sym(Terminals.ADD); } diff --git a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java index 6f8cf3307070db058911b2e98d795293b7b9a1e9..c16c6261b688eb6e784c9c8954578d81f0b8cd24 100644 --- a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java +++ b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java @@ -3,11 +3,11 @@ package org.jastadd.ragconnect.compiler; import beaver.Parser; import org.jastadd.option.BooleanOption; import org.jastadd.option.ValueOption; -import org.jastadd.relast.compiler.AbstractCompiler; -import org.jastadd.relast.compiler.CompilerException; import org.jastadd.ragconnect.ast.*; import org.jastadd.ragconnect.parser.RagConnectParser; import org.jastadd.ragconnect.scanner.RagConnectScanner; +import org.jastadd.relast.compiler.AbstractCompiler; +import org.jastadd.relast.compiler.CompilerException; import java.io.*; import java.nio.file.Files; @@ -15,6 +15,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; public class Compiler extends AbstractCompiler { @@ -25,11 +27,15 @@ public class Compiler extends AbstractCompiler { private BooleanOption optionVerbose; private BooleanOption optionLogReads; private BooleanOption optionLogWrites; + private BooleanOption optionLogIncremental; + private BooleanOption optionExperimentalJastAdd329; private static final String OPTION_PROTOCOL_MQTT = "mqtt"; private static final String OPTION_PROTOCOL_REST = "rest"; private static final String OPTION_PROTOCOL_JAVA = "java"; + private final static Logger LOGGER = Logger.getLogger(Compiler.class.getName()); + public Compiler() { super("ragconnect", true); } @@ -44,8 +50,11 @@ public class Compiler extends AbstractCompiler { getConfiguration().printHelp(System.out); return 0; } + if (optionVerbose.value()) { + LOGGER.setLevel(Level.FINE); + } - printMessage("Running RagConnect " + readVersion()); + LOGGER.info(() -> "Running RagConnect " + readVersion()); if (!getConfiguration().outputDir().exists()) { try { @@ -74,10 +83,11 @@ public class Compiler extends AbstractCompiler { } */ if (!ragConnect.errors().isEmpty()) { - System.err.println("Errors:"); + StringBuilder sb = new StringBuilder("Errors:\n"); for (ErrorMessage e : ragConnect.errors()) { - System.err.println(e); + sb.append(e).append("\n"); } + LOGGER.severe(sb::toString); System.exit(1); } @@ -89,7 +99,7 @@ public class Compiler extends AbstractCompiler { return 0; } - printMessage("Writing output files"); + LOGGER.fine("Writing output files"); final List<String> handlers = new ArrayList<>(); if (ASTNode.usesMqtt) { handlers.add("MqttHandler.jadd"); @@ -123,12 +133,11 @@ public class Compiler extends AbstractCompiler { } public static void main(String[] args) { - System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"); System.setProperty("mustache.debug", "true"); try { new Compiler().run(args); } catch (CompilerException e) { - e.printStackTrace(); + LOGGER.log(Level.SEVERE, e.getMessage(), e); System.exit(1); } } @@ -153,12 +162,6 @@ public class Compiler extends AbstractCompiler { } } - private void printMessage(String message) { - if (optionVerbose.value()) { - System.out.println(message); - } - } - private void writeToFile(Path path, String str) throws CompilerException { try (BufferedWriter writer = Files.newBufferedWriter(path)) { writer.append(str); @@ -192,6 +195,12 @@ public class Compiler extends AbstractCompiler { optionLogWrites = addOption( new BooleanOption("logWrites", "Enable logging for every write.") .defaultValue(false)); + optionLogIncremental = addOption( + new BooleanOption("logIncremental", "Enable logging for observer in incremental dependency tracking.") + .defaultValue(false)); + optionExperimentalJastAdd329 = addOption( + new BooleanOption("experimental-jastadd-329", "Use trace events INC_FLUSH_START and INC_FLUSH_END (JastAdd issue #329).") + .defaultValue(false)); } private RagConnect parseProgram(Collection<String> files) throws CompilerException { @@ -212,64 +221,75 @@ public class Compiler extends AbstractCompiler { case "ast": case "relast": // processGrammar - parseGrammar(program, filename); + program.addGrammarFile(parseGrammar(filename)); atLeastOneGrammar = true; break; case "connect": case "ragconnect": // process ragConnect - RagConnect parsedRagConnect = parseRagConnect(program, filename); - mergeRagConnectDefinitions(ragConnect, parsedRagConnect); + ragConnect.addConnectSpecificationFile(parseConnectSpec(filename)); atLeastOneRagConnect = true; break; default: - throw new CompilerException("Unknown file extension in " + filename); + throw new CompilerException("Unknown file extension " + extension + " in " + filename); } } if (!atLeastOneGrammar) { - System.err.println("No grammar file specified! (*.ast, *.relast)"); + LOGGER.severe(() -> "No grammar file specified! (*.ast, *.relast)"); } if (!atLeastOneRagConnect) { - System.err.println("No ragconnect file specified! (*.connect, *.ragconnect)"); + LOGGER.severe(() -> "No ragconnect file specified! (*.connect, *.ragconnect)"); } if (!atLeastOneGrammar && !atLeastOneRagConnect) { System.exit(1); } + + // here, the program subtree is also flushed and resolved ragConnect.flushTreeCache(); ragConnect.treeResolveAll(); + ragConnect.additionalRelations().forEach(ragConnectGrammarPart::addDeclaration); + ragConnect.additionalTokens().forEach(TypeDecl::addComponent); ASTNode.loggingEnabledForReads = optionLogReads.value(); ASTNode.loggingEnabledForWrites = optionLogWrites.value(); + ASTNode.loggingEnabledForIncremental = optionLogIncremental.value(); + ASTNode.experimentalJastAdd329 = optionExperimentalJastAdd329.value(); + + // reuse "--incremental" option of JastAdd + ASTNode.incrementalOptionActive = getConfiguration().incremental() && getConfiguration().traceFlush(); + LOGGER.fine(() -> "ASTNode.incrementalOptionActive = " + ASTNode.incrementalOptionActive); + + // reuse "--List" option of JastAdd + ASTNode.JastAddList = getConfiguration().listType(); + ASTNode.usesMqtt = optionProtocols.hasValue(OPTION_PROTOCOL_MQTT); ASTNode.usesJava = optionProtocols.hasValue(OPTION_PROTOCOL_JAVA); ASTNode.usesRest = optionProtocols.hasValue(OPTION_PROTOCOL_REST); return ragConnect; } - private void parseGrammar(Program program, String filename) throws CompilerException { + private GrammarFile parseGrammar(String filename) throws CompilerException { try (BufferedReader reader = Files.newBufferedReader(Paths.get(filename))) { RagConnectScanner scanner = new RagConnectScanner(reader); RagConnectParser parser = new RagConnectParser(); GrammarFile grammarFile = (GrammarFile) parser.parse(scanner); if (optionVerbose.value()) { - grammarFile.dumpTree(System.out); + LOGGER.fine(grammarFile::dumpTree); } - program.addGrammarFile(grammarFile); - //grammarFile.treeResolveAll(); grammarFile.setFileName(toBaseName(filename)); + return grammarFile; } catch (IOException | Parser.Exception e) { throw new CompilerException("Could not parse grammar file " + filename, e); } } - private RagConnect parseRagConnect(Program program, String filename) throws CompilerException { + private ConnectSpecificationFile parseConnectSpec(String filename) throws CompilerException { try (BufferedReader reader = Files.newBufferedReader(Paths.get(filename))) { RagConnectScanner scanner = new RagConnectScanner(reader); RagConnectParser parser = new RagConnectParser(); - RagConnect ragConnect = (RagConnect) parser.parse(scanner, RagConnectParser.AltGoals.ragconnect); - ragConnect.setProgram(program); - ragConnect.setFileName(toBaseName(filename)); - return ragConnect; + ConnectSpecificationFile specificationFile = (ConnectSpecificationFile) parser.parse(scanner, RagConnectParser.AltGoals.connect_specification_file); + specificationFile.setFileName(toBaseName(filename)); + return specificationFile; } catch (IOException | Parser.Exception e) { throw new CompilerException("Could not parse connect file " + filename, e); } @@ -284,12 +304,6 @@ public class Compiler extends AbstractCompiler { return new File(filename).getName(); } - private void mergeRagConnectDefinitions(RagConnect ragConnect, RagConnect ragConnectToIntegrate) { - ragConnect.setEndpointDefinitionList(ragConnectToIntegrate.getEndpointDefinitionList()); - ragConnect.setMappingDefinitionList(ragConnectToIntegrate.getMappingDefinitionList()); - ragConnect.setDependencyDefinitionList(ragConnectToIntegrate.getDependencyDefinitionList()); - } - // protected void printUsage() { // System.out.println("Usage: java -jar ragconnect.jar [--option1] [--option2=value] ... <filename1> <filename2> ... "); // System.out.println("Options:"); diff --git a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java deleted file mode 100644 index 83c282f205af2ff337c3eea7d2b1b7fda8e3cc47..0000000000000000000000000000000000000000 --- a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.jastadd.ragconnect.compiler; - -import org.jastadd.ragconnect.ast.Document; -import org.jastadd.ragconnect.ast.ListElement; -import org.jastadd.ragconnect.ast.MappingElement; - -/** - * Testing Ros2Rag without parser. - * - * @author rschoene - Initial contribution - */ -public class SimpleMain { - - private static void printManualYAML() { - Document doc = new Document(); - MappingElement root = new MappingElement(); - MappingElement firstLevel = new MappingElement(); - firstLevel.put("server", "tcp://localhost:1883"); - firstLevel.put("robot_speed_factor", ".7"); - - MappingElement theTopics = new MappingElement(); - theTopics.put("robotConfig", "robotconfig"); - theTopics.put("trajectory", "trajectory"); - theTopics.put("nextStep", "ros2rag/nextStep"); - firstLevel.put("topics", theTopics); - - firstLevel.put("zone_size", "0.5"); - - ListElement theZones = new ListElement(); - theZones.add("1 1"); - theZones.add("0 1"); - theZones.add("-1 1"); - firstLevel.put("zones", theZones); - - MappingElement pandaParts = new MappingElement(); - MappingElement thePanda = new MappingElement(); - thePanda.put("Link0", "panda_link0"); - thePanda.put("Link1", "panda_link1"); - thePanda.put("Link2", "panda_link2"); - thePanda.put("Link3", "panda_link3"); - thePanda.put("Link4", "panda_link4"); - thePanda.put("Link5", "panda_link5"); - thePanda.put("Link6", "panda_link6"); - thePanda.put("RightFinger", "panda_rightfinger"); - thePanda.put("LeftFinger", "panda_leftfinger"); - pandaParts.put("panda", thePanda); - firstLevel.put("parts", pandaParts); - - MappingElement endEffectorParts = new MappingElement(); - MappingElement endEffector = new MappingElement(); - endEffector.put("EndEffector", "panda_hand"); - endEffectorParts.put("panda", endEffector); - firstLevel.put("end_effectors", endEffectorParts); - - ListElement theGoalPoses = new ListElement(); - theGoalPoses.add(makePose("0.4 0.4 0.3")); - theGoalPoses.add(makePose("-0.4 0.4 0.3")); - theGoalPoses.add(makePose("-0.4 -0.4 0.3")); - theGoalPoses.add(makePose("0.4 0.4 0.3")); - theGoalPoses.add(makePose("-0.4 0.4 0.3")); - theGoalPoses.add(makePose("0.4 0.4 0.3")); - firstLevel.put("goal_poses", theGoalPoses); - - root.put("panda_mqtt_connector", firstLevel); - doc.setRootElement(root); - - System.out.println(doc.prettyPrint()); - } - - private static MappingElement makePose(String position) { - MappingElement goalPose = new MappingElement(); - goalPose.put("position", position); - goalPose.put("orientation", "1 1 0 0"); - goalPose.put("work", "20000"); - return goalPose; - } - - public static void main(String[] args) { - printManualYAML(); - } -} diff --git a/ragconnect.base/src/main/resources/ListAspect.mustache b/ragconnect.base/src/main/resources/ListAspect.mustache new file mode 100644 index 0000000000000000000000000000000000000000..764b5441862c96a010f6d07f86e2c1aad2dec81c --- /dev/null +++ b/ragconnect.base/src/main/resources/ListAspect.mustache @@ -0,0 +1,24 @@ +{{#hasTreeListEndpoints}} +public void {{JastAddList}}.serialize(com.fasterxml.jackson.core.JsonGenerator g) throws SerializationException { + try { + g.writeStartArray(); + for (T child : this) { + child.serialize(g); + } + g.writeEndArray(); + } catch (java.io.IOException e) { + throw new SerializationException("unable to serialize {{JastAddList}}", e); + } +} + +{{#typesForReceivingListEndpoints}} +public static {{JastAddList}}<{{Name}}> {{Name}}.deserializeList(com.fasterxml.jackson.databind.node.ArrayNode node) throws DeserializationException { + {{JastAddList}}<{{Name}}> result = new {{JastAddList}}<>(); + for (java.util.Iterator<com.fasterxml.jackson.databind.JsonNode> it = node.elements(); it.hasNext();) { + com.fasterxml.jackson.databind.JsonNode element = it.next(); + result.add(deserialize(element)); + } + return result; +} +{{/typesForReceivingListEndpoints}} +{{/hasTreeListEndpoints}} diff --git a/ragconnect.base/src/main/resources/MqttHandler.jadd b/ragconnect.base/src/main/resources/MqttHandler.jadd index f5e6ba99b3058f59f6b9b40ae0d9c07f18da88c3..a8f065e818c32874703d7f2775c589492fb6e8ee 100644 --- a/ragconnect.base/src/main/resources/MqttHandler.jadd +++ b/ragconnect.base/src/main/resources/MqttHandler.jadd @@ -1,10 +1,10 @@ -import java.io.IOException; +import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; - -aspect MqttHandler { +import java.util.function.BiConsumer;aspect MqttHandler { public class MqttServerHandler { private final java.util.Map<String, MqttHandler> handlers = new java.util.HashMap<>(); - private final java.util.Map<ConnectToken, Object> tokensForRemoval = new java.util.HashMap<>(); + private final java.util.Map<ConnectToken, java.util.function.BiConsumer<String, byte[]>> tokensForRemoval = new java.util.HashMap<>(); private long time; private java.util.concurrent.TimeUnit unit; private String name; @@ -15,7 +15,7 @@ public class MqttServerHandler { public MqttServerHandler(String name) { this.name = name; - setupWaitUntilReady(1, TimeUnit.SECONDS); + setupWaitUntilReady(1, java.util.concurrent.TimeUnit.SECONDS); } public void setupWaitUntilReady(long time, java.util.concurrent.TimeUnit unit) { @@ -23,7 +23,7 @@ public class MqttServerHandler { this.unit = unit; } - public MqttHandler resolveHandler(java.net.URI uri) throws IOException { + public MqttHandler resolveHandler(java.net.URI uri) throws java.io.IOException { MqttHandler handler = handlers.get(uri.getHost()); if (handler == null) { // first connect to that server @@ -39,33 +39,37 @@ public class MqttServerHandler { return handler; } - public ConnectToken newConnection(java.net.URI uri, java.util.function.Consumer<byte[]> callback) throws IOException { + public ConnectToken newConnection(java.net.URI uri, java.util.function.BiConsumer<String, byte[]> callback) throws java.io.IOException { ConnectToken connectToken = new ConnectToken(uri); resolveHandler(uri).newConnection(extractTopic(uri), callback); tokensForRemoval.put(connectToken, callback); return connectToken; } - public boolean disconnect(ConnectToken connectToken) throws IOException { + public boolean disconnect(ConnectToken connectToken) throws java.io.IOException { MqttHandler handler = resolveHandler(connectToken.uri); return handler != null ? handler.disconnect(extractTopic(connectToken.uri), tokensForRemoval.get(connectToken)) : false; } - public void publish(java.net.URI uri, byte[] bytes) throws IOException { + public void publish(java.net.URI uri, byte[] bytes) throws java.io.IOException { resolveHandler(uri).publish(extractTopic(uri), bytes); } - public void publish(java.net.URI uri, byte[] bytes, boolean retain) throws IOException { + public void publish(java.net.URI uri, byte[] bytes, boolean retain) throws java.io.IOException { resolveHandler(uri).publish(extractTopic(uri), bytes, retain); } public void publish(java.net.URI uri, byte[] bytes, - org.fusesource.mqtt.client.QoS qos, boolean retain) throws IOException { + org.fusesource.mqtt.client.QoS qos, boolean retain) throws java.io.IOException { resolveHandler(uri).publish(extractTopic(uri), bytes, qos, retain); } public static String extractTopic(java.net.URI uri) { String path = uri.getPath(); + if (uri.getFragment() != null) { + // do not also append fragment, as it is illegal, that anything follows "#" in a mqtt topic anyway + path += "#"; + } if (path.charAt(0) == '/') { path = path.substring(1); } @@ -99,7 +103,8 @@ public class MqttHandler { private boolean sendWelcomeMessage = true; private org.fusesource.mqtt.client.QoS qos; /** Dispatch knowledge */ - private final java.util.Map<String, java.util.List<java.util.function.Consumer<byte[]>>> callbacks; + private final java.util.Map<String, java.util.List<java.util.function.BiConsumer<String, byte[]>>> normalCallbacks; + private final java.util.Map<java.util.regex.Pattern, java.util.List<java.util.function.BiConsumer<String, byte[]>>> wildcardCallbacks; public MqttHandler() { this("RagConnect"); @@ -108,7 +113,8 @@ public class MqttHandler { public MqttHandler(String name) { this.name = java.util.Objects.requireNonNull(name, "Name must be set"); this.logger = org.apache.logging.log4j.LogManager.getLogger(MqttHandler.class); - this.callbacks = new java.util.HashMap<>(); + this.normalCallbacks = new java.util.HashMap<>(); + this.wildcardCallbacks = new java.util.HashMap<>(); this.readyLatch = new java.util.concurrent.CountDownLatch(1); this.qos = org.fusesource.mqtt.client.QoS.AT_LEAST_ONCE; } @@ -119,17 +125,23 @@ public class MqttHandler { } /** - * Sets the host (with default port) to receive messages from, and connects to it. - * @throws IOException if could not connect, or could not subscribe to a topic + * Sets the host to receive messages from, and connects to it. + * @param host name of the host to connect to, format is either <code>"$name"</code> or <code>"$name:$port"</code> + * @throws java.io.IOException if could not connect, or could not subscribe to a topic * @return self */ public MqttHandler setHost(String host) throws java.io.IOException { + if (host.contains(":")) { + int colon_index = host.indexOf(":"); + return setHost(host.substring(0, colon_index), + Integer.parseInt(host.substring(colon_index + 1))); + } return setHost(host, DEFAULT_PORT); } /** * Sets the host to receive messages from, and connects to it. - * @throws IOException if could not connect, or could not subscribe to a topic + * @throws java.io.IOException if could not connect, or could not subscribe to a topic * @return self */ public MqttHandler setHost(String host, int port) throws java.io.IOException { @@ -160,13 +172,17 @@ public class MqttHandler { org.fusesource.mqtt.client.Callback<org.fusesource.mqtt.client.Callback<Void>> ack) { // this method is called, whenever a MQTT message is received String topicString = topic.toString(); - java.util.List<java.util.function.Consumer<byte[]>> callbackList = callbacks.get(topicString); - if (callbackList == null || callbackList.isEmpty()) { - logger.debug("Got a message, but no callback to call. Forgot to subscribe?"); + java.util.List<java.util.function.BiConsumer<String, byte[]>> callbackList = callbacksFor(topicString); + if (callbackList.isEmpty()) { + logger.debug("Got a message at {}, but no callback to call. Forgot to subscribe?", topic); } else { byte[] message = body.toByteArray(); - for (java.util.function.Consumer<byte[]> callback : callbackList) { - callback.accept(message); + for (java.util.function.BiConsumer<String, byte[]> callback : callbackList) { + try { + callback.accept(topicString, message); + } catch (Exception e) { + logger.catching(e); + } } } ack.onSuccess(null); // always acknowledge message @@ -188,20 +204,20 @@ public class MqttHandler { throwIf(error); // actually establish the connection - connection.connect(new org.fusesource.mqtt.client.Callback<Void>() { + connection.connect(new org.fusesource.mqtt.client.Callback<>() { @Override public void onSuccess(Void value) { if (MqttHandler.this.sendWelcomeMessage) { connection.publish("components", - (name + " is connected").getBytes(), - org.fusesource.mqtt.client.QoS.AT_LEAST_ONCE, - false, - new org.fusesource.mqtt.client.Callback<Void>() { - @Override - public void onSuccess(Void value) { - logger.debug("success sending welcome message"); - setReady(); - } + (name + " is connected").getBytes(), + org.fusesource.mqtt.client.QoS.AT_LEAST_ONCE, + false, + new org.fusesource.mqtt.client.Callback<>() { + @Override + public void onSuccess(Void value) { + logger.debug("success sending welcome message"); + setReady(); + } @Override public void onFailure(Throwable value) { @@ -222,6 +238,20 @@ public class MqttHandler { return this; } + private java.util.List<java.util.function.BiConsumer<String, byte[]>> callbacksFor(String topicString) { + java.util.List<java.util.function.BiConsumer<String, byte[]>> result = new java.util.ArrayList<>(); + List<BiConsumer<String, byte[]>> normalCallbackList = normalCallbacks.get(topicString); + if (normalCallbackList != null) { + result.addAll(normalCallbackList); + } + wildcardCallbacks.forEach((topicPattern, callback) -> { + if (topicPattern.matcher(topicString).matches()) { + result.addAll(callback); + } + }); + return result; + } + public java.net.URI getHost() { return host; } @@ -240,61 +270,147 @@ public class MqttHandler { this.qos = qos; } + /** + * Establish a new connection for some topic. + * @param topic the topic to create a connection for, may contain the wildcards "*" and "#" + * @param callback the callback to run if a new message arrives for this topic + * @return true if successful stored this connection, false otherwise (e.g., on failed subscribe) + */ public boolean newConnection(String topic, java.util.function.Consumer<byte[]> callback) { + return newConnection(topic, (ignoredTopicString, bytes) -> callback.accept(bytes)); + } + + /** + * Establish a new connection for some topic. + * @param topic the topic to create a connection for, may contain the wildcards "*" and "#" + * @param callback the callback to run if a new message arrives for this topic + * @return true if successful stored this connection, false otherwise (e.g., on failed subscribe) + */ + public boolean newConnection(String topic, java.util.function.BiConsumer<String, byte[]> callback) { if (readyLatch.getCount() > 0) { System.err.println("Handler not ready"); return false; } // register callback logger.debug("new connection for {}", topic); - if (callbacks.get(topic) == null) { - callbacks.put(topic, new java.util.ArrayList<>()); - + final boolean needSubscribe; + if (isWildcardTopic(topic)) { + String regexForTopic = topic.replace("*", "[^/]*").replace("#", ".*"); + java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(regexForTopic); + wildcardCallbacks.computeIfAbsent(pattern, p -> new java.util.ArrayList<>()) + .add(callback); + needSubscribe = true; + } else { // normal topic + java.util.List<java.util.function.BiConsumer<String, byte[]>> callbacksForTopic = normalCallbacks.get(topic); + if (callbacksForTopic == null || callbacksForTopic.isEmpty()) { + callbacksForTopic = new java.util.ArrayList<>(); + normalCallbacks.put(topic, callbacksForTopic); + needSubscribe = true; + } else { + needSubscribe = false; + } + callbacksForTopic.add(callback); + } + if (needSubscribe) { // subscribe at broker + CountDownLatch operationFinished = new CountDownLatch(1); + java.util.concurrent.atomic.AtomicReference<Boolean> success = new java.util.concurrent.atomic.AtomicReference<>(true); org.fusesource.mqtt.client.Topic[] topicArray = { new org.fusesource.mqtt.client.Topic(topic, this.qos) }; connection.getDispatchQueue().execute(() -> { - connection.subscribe(topicArray, new org.fusesource.mqtt.client.Callback<byte[]>() { + connection.subscribe(topicArray, new org.fusesource.mqtt.client.Callback<>() { @Override public void onSuccess(byte[] qoses) { logger.debug("Subscribed to {}, qoses: {}", topic, qoses); + operationFinished.countDown(); } @Override public void onFailure(Throwable cause) { logger.error("Could not subscribe to {}", topic, cause); + success.set(false); + operationFinished.countDown(); } }); }); + try { + operationFinished.await(2, TimeUnit.SECONDS); + return success.get(); + } catch (InterruptedException e) { + return false; + } + } else { + return true; } - callbacks.get(topic).add(callback); - return true; } - public boolean disconnect(String topic, Object callback) { - java.util.List<java.util.function.Consumer<byte[]>> callbackList = callbacks.get(topic); - if (callbackList == null) { + private boolean isWildcardTopic(String topic) { + return topic.contains("*") || topic.contains("#"); + } + + public boolean disconnect(String topic, java.util.function.BiConsumer<String, byte[]> callback) { + boolean needUnsubscribe = false; + java.util.concurrent.atomic.AtomicReference<Boolean> success = new java.util.concurrent.atomic.AtomicReference<>(true); + + boolean foundTopicInCallbacks = false; + + // check if wildcard is to be removed + if (isWildcardTopic(topic)) { + java.util.regex.Pattern wildcardPatternToRemove = null; + for (java.util.Map.Entry<java.util.regex.Pattern, java.util.List<java.util.function.BiConsumer<String, byte[]>>> entry : wildcardCallbacks.entrySet()) { + if (entry.getKey().pattern().equals(topic)) { + foundTopicInCallbacks = true; + // if still successful, update with whether callback could be removed + success.compareAndSet(true, (entry.getValue().remove(callback))); + if (entry.getValue().isEmpty()) { + wildcardPatternToRemove = entry.getKey(); + needUnsubscribe = true; + } + break; + } + } + ; + if (wildcardPatternToRemove != null) { + wildcardCallbacks.remove(wildcardPatternToRemove); + } + } else if (normalCallbacks.containsKey(topic)) { + foundTopicInCallbacks = true; + // if still successful, update with whether callback could be removed + var normalCallbackList = normalCallbacks.get(topic); + success.compareAndSet(true, normalCallbackList.remove(callback)); + needUnsubscribe |= normalCallbackList.isEmpty(); + } + + if (!foundTopicInCallbacks) { logger.warn("Disconnect for not connected topic '{}'", topic); return false; } - java.util.concurrent.atomic.AtomicReference<Boolean> success = new java.util.concurrent.atomic.AtomicReference<>(); - success.set(callbackList.remove(callback)); - if (callbackList.isEmpty()) { + + if (needUnsubscribe) { + java.util.concurrent.CountDownLatch operationFinished = new java.util.concurrent.CountDownLatch(1); // no callbacks anymore for this topic, unsubscribe from mqtt connection.getDispatchQueue().execute(() -> { org.fusesource.hawtbuf.UTF8Buffer topicBuffer = org.fusesource.hawtbuf.Buffer.utf8(topic); org.fusesource.hawtbuf.UTF8Buffer[] topicArray = new org.fusesource.hawtbuf.UTF8Buffer[]{topicBuffer}; - connection.unsubscribe(topicArray, new org.fusesource.mqtt.client.Callback<Void>() { + connection.unsubscribe(topicArray, new org.fusesource.mqtt.client.Callback<>() { @Override public void onSuccess(Void value) { - // empty, all good + operationFinished.countDown(); } @Override public void onFailure(Throwable cause) { success.set(false); + logger.warn("Could not disconnect from {}", topic, cause); + operationFinished.countDown(); } }); }); + try { + operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS); + } catch (InterruptedException e) { + logger.catching(e); + success.set(false); + } } return success.get(); } @@ -323,7 +439,7 @@ public class MqttHandler { return; } connection.getDispatchQueue().execute(() -> { - connection.disconnect(new org.fusesource.mqtt.client.Callback<Void>() { + connection.disconnect(new org.fusesource.mqtt.client.Callback<>() { @Override public void onSuccess(Void value) { logger.info("Disconnected {} from {}", name, host); @@ -347,7 +463,7 @@ public class MqttHandler { public void publish(String topic, byte[] bytes, org.fusesource.mqtt.client.QoS qos, boolean retain) { connection.getDispatchQueue().execute(() -> { - connection.publish(topic, bytes, qos, retain, new org.fusesource.mqtt.client.Callback<Void>() { + connection.publish(topic, bytes, qos, retain, new org.fusesource.mqtt.client.Callback<>() { @Override public void onSuccess(Void value) { logger.debug("Published some bytes to {}", topic); diff --git a/ragconnect.base/src/main/resources/handleUri.mustache b/ragconnect.base/src/main/resources/handleUri.mustache index 1a20e7c4df2a4c54ccba4c825fff55ca89a51dec..ff0ea7af7f920ac09a14de75a4afe1882540ac94 100644 --- a/ragconnect.base/src/main/resources/handleUri.mustache +++ b/ragconnect.base/src/main/resources/handleUri.mustache @@ -1,11 +1,23 @@ -String scheme,host, path; +String scheme, host, path; java.net.URI uri; try { uri = new java.net.URI({{connectParameterName}}); scheme = uri.getScheme(); host = uri.getHost(); - path = uri.getPath(); + path = uri.getPath() + (uri.getFragment() != null ? "#" : ""); } catch (java.net.URISyntaxException e) { System.err.println(e.getMessage()); // Maybe re-throw error? return false; } +if (scheme == null || scheme.isBlank()) { + System.err.println("Missing or empty scheme in " + uri); + return false; +} +if (host == null || host.isBlank()) { + System.err.println("Missing or empty host in " + uri); + return false; +} +if (path == null || path.isBlank()) { + System.err.println("Missing or empty path in " + uri); + return false; +} diff --git a/ragconnect.base/src/main/resources/log4j2.xml b/ragconnect.base/src/main/resources/log4j2.xml deleted file mode 100644 index 98cfd73c75df58d8598521bc10b043e214ec4ad8..0000000000000000000000000000000000000000 --- a/ragconnect.base/src/main/resources/log4j2.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Configuration status="INFO"> - <Appenders> - <Console name="Console" target="SYSTEM_OUT"> - <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> - </Console> - </Appenders> - <Loggers> - <Root level="info"> - <AppenderRef ref="Console"/> - </Root> - </Loggers> -</Configuration> \ No newline at end of file diff --git a/ragconnect.base/src/main/resources/mappingApplication.mustache b/ragconnect.base/src/main/resources/mappingApplication.mustache index 064004b94886c155be883e7b186557c6fa87d38c..bc59e0b318dcd430b5f21fd0a67d157b35a4bb7c 100644 --- a/ragconnect.base/src/main/resources/mappingApplication.mustache +++ b/ragconnect.base/src/main/resources/mappingApplication.mustache @@ -1,8 +1,11 @@ -{{lastDefinitionToType}} {{lastResult}}; +{{{lastDefinitionToType}}} {{lastResult}}; try { {{#InnerMappingDefinitions}} - {{^last}}{{toType}} {{/last}}{{outputVarName}} = {{methodName}}({{inputVarName}}); + {{^last}}{{{toType}}} {{/last}}{{outputVarName}} = {{methodName}}({{inputVarName}}); {{/InnerMappingDefinitions}} +} catch (RagConnectRejectMappingException e) { + // do not print message in case of rejection + {{preemptiveReturn}} } catch (Exception e) { e.printStackTrace(); {{preemptiveReturn}} diff --git a/ragconnect.base/src/main/resources/mappingDefinition.mustache b/ragconnect.base/src/main/resources/mappingDefinition.mustache index fe26e949b7a8e446bf97947e1632624741a83726..09f161f37c3742850b09969d4eb85c7798d2d346 100644 --- a/ragconnect.base/src/main/resources/mappingDefinition.mustache +++ b/ragconnect.base/src/main/resources/mappingDefinition.mustache @@ -1,3 +1,3 @@ -protected static {{toType}} ASTNode.{{methodName}}({{fromType}} {{fromVariableName}}) throws Exception { - {{{content}}}{{!maybe print line by line to get better indentation}} +protected static {{{toType}}} ASTNode.{{methodName}}({{{fromType}}} {{fromVariableName}}) throws Exception { + {{{content}}} } diff --git a/ragconnect.base/src/main/resources/ragConnectVersion.properties b/ragconnect.base/src/main/resources/ragConnectVersion.properties index 266543a066a77220bc13c577f38b08368a2f4d40..7e24342af2954e314a43f78b900f8921a74678a8 100644 --- a/ragconnect.base/src/main/resources/ragConnectVersion.properties +++ b/ragconnect.base/src/main/resources/ragConnectVersion.properties @@ -1,2 +1,2 @@ -#Tue Jan 19 12:08:02 CET 2021 -version=0.2.5 +#Thu Jun 24 17:13:44 CEST 2021 +version=0.3.2-alpha diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache index 49d1721cbf0cf0131fe2248fe4e736906448bccf..c08529a22bfacc7ec5caa3e6e6569e51bc5182a1 100644 --- a/ragconnect.base/src/main/resources/ragconnect.mustache +++ b/ragconnect.base/src/main/resources/ragconnect.mustache @@ -1,16 +1,31 @@ {{#usesMqtt}}{{> mqtt}}{{/usesMqtt}} {{> handler}} -aspect ROS2RAG { - {{#ReceiveDefinitions}} +aspect RagConnect { + {{#TokenReceiveDefinitions}} {{> receiveDefinition}} - {{/ReceiveDefinitions}} + {{/TokenReceiveDefinitions}} - {{#SendDefinitions}} + {{#TokenSendDefinitions}} {{> sendDefinition}} - {{/SendDefinitions}} + {{/TokenSendDefinitions}} + + {{#TypeReceiveDefinitions}} + {{> receiveDefinition}} + {{/TypeReceiveDefinitions}} + + {{#TypeSendDefinitions}} + {{> sendDefinition}} + {{/TypeSendDefinitions}} + + class RagConnectRejectMappingException extends RuntimeException {} + private static void ASTNode.reject() { + throw new RagConnectRejectMappingException(); + } {{#MappingDefinitions}} + {{#isUsed}} {{> mappingDefinition}} + {{/isUsed}} {{/MappingDefinitions}} {{#DependencyDefinitions}} @@ -20,4 +35,144 @@ aspect ROS2RAG { {{#TokenComponents}} {{> tokenComponent}} {{/TokenComponents}} + + {{> ListAspect}} + + public void {{rootNodeName}}.ragconnectCheckIncremental() { + {{#incrementalOptionActive}} + // check if --tracing is active + trace().getReceiver(); + // check if tracing of INC_FLUSH_ATTR is possible, i.e., if --tracing=flush + ASTState.Trace.Event checkTracing = ASTState.Trace.Event.INC_FLUSH_ATTR; + // check if --rewrite is active + mayHaveRewrite(); + // check if --incremental is active + Object checkIncremental = inc_throwAway_visited; + {{/incrementalOptionActive}} + } +} + +{{#incrementalOptionActive}} +aspect RagConnectObserver { + + class RagConnectObserver implements ASTState.Trace.Receiver { + + class RagConnectObserverEntry { + final ConnectToken connectToken; + final ASTNode node; + final String attributeString; + final Runnable attributeCall; + + RagConnectObserverEntry(ConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) { + this.connectToken = connectToken; + this.node = node; + this.attributeString = attributeString; + this.attributeCall = attributeCall; + } + } + +{{#experimentalJastAdd329}} + class RagConnectObserverStartEntry { + final ASTNode node; + final String attributeString; + final Object flushIncToken; + RagConnectObserverStartEntry(ASTNode node, String attributeString, Object flushIncToken) { + this.node = node; + this.attributeString = attributeString; + this.flushIncToken = flushIncToken; + } + } +{{/experimentalJastAdd329}} + + ASTState.Trace.Receiver oldReceiver; + + java.util.List<RagConnectObserverEntry> observedNodes = new java.util.ArrayList<>(); + +{{#experimentalJastAdd329}} + java.util.Set<RagConnectObserverEntry> entryQueue = new java.util.HashSet<>(); + RagConnectObserverStartEntry startEntry = null; +{{/experimentalJastAdd329}} + + RagConnectObserver(ASTNode node) { + // set the receiver. potentially dangerous because overriding existing receiver! + oldReceiver = node.trace().getReceiver(); + node.trace().setReceiver(this); + } + + void add(ConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) { + {{#loggingEnabledForIncremental}} + System.out.println("** observer add: " + node + " on " + attributeString); + {{/loggingEnabledForIncremental}} + observedNodes.add(new RagConnectObserverEntry(connectToken, node, attributeString, attributeCall)); + } + void remove(ConnectToken connectToken) { + observedNodes.removeIf(entry -> entry.connectToken.equals(connectToken)); + } + @Override + public void accept(ASTState.Trace.Event event, ASTNode node, String attribute, Object params, Object value) { + oldReceiver.accept(event, node, attribute, params, value); +{{#experimentalJastAdd329}} + // react to INC_FLUSH_START and remember entry + if (event == ASTState.Trace.Event.INC_FLUSH_START && startEntry == null) { + {{#loggingEnabledForIncremental}} + System.out.println("** observer start: " + node + " on " + attribute); + {{/loggingEnabledForIncremental}} + startEntry = new RagConnectObserverStartEntry(node, attribute, value); + return; + } + + // react to INC_FLUSH_END and process queued entries, if it matches start entry + if (event == ASTState.Trace.Event.INC_FLUSH_END && + node == startEntry.node && + attribute == startEntry.attributeString && + value == startEntry.flushIncToken) { + // create a copy of the queue to avoid entering this again causing an endless recursion + RagConnectObserverEntry[] entriesToProcess = entryQueue.toArray(new RagConnectObserverEntry[entryQueue.size()]); + entryQueue.clear(); + startEntry = null; + {{#loggingEnabledForIncremental}} + System.out.println("** observer process (" + entriesToProcess.length + "): " + node + " on " + attribute); + {{/loggingEnabledForIncremental}} + for (RagConnectObserverEntry entry : entriesToProcess) { + entry.attributeCall.run(); + } + return; + } + +{{/experimentalJastAdd329}} + // ignore all other events but INC_FLUSH_ATTR + if (event != ASTState.Trace.Event.INC_FLUSH_ATTR) { + return; + } + + {{#loggingEnabledForIncremental}} + System.out.println("** observer check INC_FLUSH_ATTR event: " + node + " on " + attribute); + {{/loggingEnabledForIncremental}} + // iterate through list, if matching pair. could maybe be more efficient. + for (RagConnectObserverEntry entry : observedNodes) { + if (entry.node.equals(node) && entry.attributeString.equals(attribute)) { + // hit. call the attribute/nta-token + {{#loggingEnabledForIncremental}} + System.out.println("** observer hit: " + entry.node + " on " + entry.attributeString); + {{/loggingEnabledForIncremental}} +{{#experimentalJastAdd329}} + entryQueue.add(entry); +{{/experimentalJastAdd329}} +{{^experimentalJastAdd329}} + entry.attributeCall.run(); +{{/experimentalJastAdd329}} + } + } + } + } + + private static RagConnectObserver ASTNode._ragConnectObserverInstance; + RagConnectObserver ASTNode._ragConnectObserver() { + if (_ragConnectObserverInstance == null) { + // does not matter, which node is used to create the observer as ASTState/tracing is also static + _ragConnectObserverInstance = new RagConnectObserver(this); + } + return _ragConnectObserverInstance; + } } +{{/incrementalOptionActive}} diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache index 6acddb20204a43b3fac6d4287eaa5f5db7c40246..9a8e309964319ee768879aa9f0c4b6a6b9d316a4 100644 --- a/ragconnect.base/src/main/resources/receiveDefinition.mustache +++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache @@ -1,12 +1,104 @@ +{{#typeIsList}} +{{^UseList}} +/* first try with resolve to type +syn {{typeName}} {{parentTypeName}}.{{resolveInListAttributeName}}(String topic) { + for ({{typeName}} element : get{{entityName}}()) { + if (element.get{{idTokenName}}().equals(topic)) { + return element; + } + } + return null; +} +*/ +syn int {{parentTypeName}}.{{resolveInListAttributeName}}(String topic) { + for (int index = 0; index < getNum{{entityName}}(); index++) { + if (get{{entityName}}(index).get{{idTokenName}}().equals(topic)) { + return index; + } + } + return -1; +} +{{/UseList}} +{{/typeIsList}} + +/** + * Connects the receive endpoint {{entityName}}. +{{#typeIsList}}{{#isWithAdd}} + * New values are appended to the end of the list. +{{/isWithAdd}}{{/typeIsList}} + * @param {{connectParameterName}} string describing protocol and path as an URI +{{#typeIsList}}{{^UseList}}{{^isWithAdd}} + * @param index index of node in list to connect (the list is expected to have enough elements) +{{/isWithAdd}}{{/UseList}}{{/typeIsList}} + * @return true if connect was successful, false otherwise + * @throws java.io.IOException if connect failed + */ +public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}{{#typeIsList}}{{^UseList}}{{^isWithAdd}}, int index{{/isWithAdd}}{{/UseList}}{{/typeIsList}}) throws java.io.IOException { + java.util.function.BiConsumer<String, byte[]> consumer = (topic, message) -> { + {{> mappingApplication}} +{{#loggingEnabledForReads}} + System.out.println("[Receive] " + {{connectParameterName}} + " -> {{entityName}} = " + {{lastResult}}); +{{/loggingEnabledForReads}} +{{#isTypeEndpointDefinition}} + {{lastResult}}.treeResolveAll(); + {{#typeIsList}} + {{#UseList}} + {{#isWithAdd}} + {{getterMethod}}().addAll({{lastResult}}); + {{/isWithAdd}} + {{^isWithAdd}} + set{{entityName}}({{lastResult}}); + {{/isWithAdd}} + {{/UseList}} + {{^UseList}} + {{lastResult}}.set{{idTokenName}}(topic); + {{#isWithAdd}} + {{getterMethod}}().add({{lastResult}}); + {{/isWithAdd}} + {{^isWithAdd}} + set{{entityName}}({{lastResult}}, index); + {{/isWithAdd}} + {{/UseList}} + {{/typeIsList}} + {{^typeIsList}} + set{{entityName}}({{lastResult}}); + {{/typeIsList}} +{{/isTypeEndpointDefinition}} +{{^isTypeEndpointDefinition}} + set{{entityName}}({{lastResult}}); +{{/isTypeEndpointDefinition}} + }; + return {{internalConnectMethod}}({{connectParameterName}}, consumer); +} + +{{#typeIsList}}{{^UseList}}{{^isWithAdd}} +/** + * Connects the receive endpoint {{entityName}} using a "wildcard" URI (if supported by the chosen protocol). + * @param {{connectParameterName}} string describing protocol and path as an URI + * @return true if connect was successful, false otherwise + * @throws java.io.IOException if connect failed +*/ public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}) throws java.io.IOException { - {{>handleUri}} - java.util.function.Consumer<byte[]> consumer = message -> { + java.util.function.BiConsumer<String, byte[]> consumer = (topic, message) -> { {{> mappingApplication}} - {{#loggingEnabledForReads}} - System.out.println("[Receive] " + {{connectParameterName}} + " -> {{tokenName}} = " + {{lastResult}}); - {{/loggingEnabledForReads}} - set{{tokenName}}({{lastResult}}); +{{#loggingEnabledForReads}} + System.out.println("[Receive] " + {{connectParameterName}} + " (" + topic + ") -> {{entityName}} = " + {{lastResult}}); +{{/loggingEnabledForReads}} + {{lastResult}}.set{{idTokenName}}(topic); + int resolvedIndex = {{resolveInListAttributeName}}(topic); + if (resolvedIndex == -1) { + add{{entityName}}({{lastResult}}); + } else { + set{{entityName}}({{lastResult}}, resolvedIndex); + } }; + return {{internalConnectMethod}}({{connectParameterName}}, consumer); +} +{{/isWithAdd}}{{/UseList}}{{/typeIsList}} + +private boolean {{parentTypeName}}.{{internalConnectMethod}}(String {{connectParameterName}}, + java.util.function.BiConsumer<String, byte[]> consumer) throws java.io.IOException { + {{>handleUri}} ConnectToken connectToken; switch (scheme) { {{#usesMqtt}} @@ -29,7 +121,8 @@ public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterNam {{#usesRest}} case "rest": connectToken = {{restHandlerAttribute}}().newPUTConnection(uri, input -> { - consumer.accept(input.getBytes()); + // TODO wildcard-topic not supported yet + consumer.accept("", input.getBytes()); }); if (connectToken == null) { return false; diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache index 5ddec008088bb8ea855dab43f5a67c5117ee3950..bf05fadf4c4c8281aea696425b2982617eec2029 100644 --- a/ragconnect.base/src/main/resources/sendDefinition.mustache +++ b/ragconnect.base/src/main/resources/sendDefinition.mustache @@ -3,6 +3,12 @@ private byte[] {{parentTypeName}}.{{lastValue}} = null; public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}, boolean writeCurrentValue) throws java.io.IOException { {{>handleUri}} + ConnectToken connectToken; + if (connectTokens.computeIfAbsent(this, astNode -> new java.util.HashMap<java.net.URI, ConnectToken>()) + .get(uri) != null) { + System.err.println("Already connected for " + uri + " on " + this + "!"); + return true; + } switch (scheme) { {{#usesMqtt}} case "mqtt": @@ -10,7 +16,7 @@ public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterNam final String topic = {{mqttHandlerAttribute}}().extractTopic(uri); {{sender}} = () -> { {{#loggingEnabledForWrites}} - System.out.println("[Send] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{connectParameterName}}); + System.out.println("[Send] {{entityName}} = " + {{getterMethod}}() + " -> " + {{connectParameterName}}); {{/loggingEnabledForWrites}} handler.publish(topic, {{lastValue}}); }; @@ -18,6 +24,7 @@ public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterNam if (writeCurrentValue) { {{writeMethod}}(); } + connectToken = new ConnectToken(uri); break; {{/usesMqtt}} {{#usesJava}} @@ -35,26 +42,40 @@ public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterNam {{/usesJava}} {{#usesRest}} case "rest": - ConnectToken connectToken = {{restHandlerAttribute}}().newGETConnection(uri, () -> { + connectToken = {{restHandlerAttribute}}().newGETConnection(uri, () -> { {{updateMethod}}(); return new String({{lastValue}}); }); if (connectToken == null) { return false; } - connectTokens.computeIfAbsent(this, astNode -> new java.util.HashMap<java.net.URI, ConnectToken>()) - .put(uri, connectToken); break; {{/usesRest}} default: System.err.println("Unknown protocol '" + scheme + "'."); return false; } + connectTokens.computeIfAbsent(this, astNode -> new java.util.HashMap<java.net.URI, ConnectToken>()) + .put(uri, connectToken); + {{#incrementalOptionActive}} + _ragConnectObserver().add(connectToken, this, "{{getterMethod}}", () -> { + if (this.{{updateMethod}}()) { + this.{{writeMethod}}(); + } + }); + {{/incrementalOptionActive}} return true; } public boolean {{parentTypeName}}.{{disconnectMethod}}(String {{connectParameterName}}) throws java.io.IOException { {{>handleUri}} + ConnectToken connectToken = connectTokens.get(this).remove(uri); + if (connectToken == null) { + System.err.println("Disconnect without connect for " + uri + " on " + this + "!"); + } + {{#incrementalOptionActive}} + _ragConnectObserver().remove(connectToken); + {{/incrementalOptionActive}} switch (scheme) { {{#usesMqtt}} case "mqtt": @@ -69,7 +90,7 @@ public boolean {{parentTypeName}}.{{disconnectMethod}}(String {{connectParameter {{/usesJava}} {{#usesRest}} case "rest": - {{restHandlerAttribute}}().disconnect(connectTokens.get(this).get(uri)); + {{restHandlerAttribute}}().disconnect(connectToken); break; {{/usesRest}} default: diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle index ccc0134eb86898cd001183828f81652f2a8c2010..c43e47044c47358624b646fec196634ac28786bf 100644 --- a/ragconnect.tests/build.gradle +++ b/ragconnect.tests/build.gradle @@ -1,13 +1,20 @@ buildscript { - repositories.mavenCentral() + repositories { + mavenCentral() +// mavenLocal() + maven { + name "gitlab-maven" + url "https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven" + } + } dependencies { classpath 'org.jastadd:jastaddgradle:1.13.3' - classpath fileTree(include: ['buildSrc.jar'], dir: '../libs') + classpath 'org.jastadd.preprocessor:testing:0.2.10' } } -import org.jastadd.relast.plugin.RelastPlugin -import org.jastadd.relast.plugin.RelastTest +import org.jastadd.preprocessor.testing.plugin.PreprocessorPlugin +import org.jastadd.preprocessor.testing.plugin.RagConnectTest plugins { id 'java' @@ -18,23 +25,28 @@ plugins { } apply plugin: 'jastadd' -apply plugin: RelastPlugin +apply plugin: PreprocessorPlugin group = 'de.tudresden.inf.st' repositories { mavenCentral() - jcenter() } dependencies { implementation project(':ragconnect.base') - runtime group: 'org.jastadd', name: 'jastadd', version: '2.3.4' + runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.5' +// runtimeOnly fileTree(include: ['jastadd2.jar'], dir: '../libs') + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.4.0' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.4.0' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.12.1' + // jackson (for serialization of types) + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.12.1' + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.1' + // mqtt testImplementation group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15' @@ -43,11 +55,19 @@ dependencies { testImplementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.11.2' testImplementation group: 'org.glassfish.jersey.core', name: 'jersey-client', version: '2.31' testImplementation group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '2.31' + testImplementation group: 'javax.activation', name: 'activation', version: '1.1.1' testImplementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' api group: 'com.google.protobuf', name: 'protobuf-java', version: '3.0.0' } +//task helper { +// doLast { +// println(defaultOnlyRead.inputs.files.files) +// println(defaultOnlyRead.outputs.files.files) +// } +//} + test { useJUnitPlatform { excludeTags 'mqtt' @@ -72,9 +92,21 @@ task allTests(type: Test, dependsOn: testClasses) { } } -relastTest { +task specificTest(type: Test, dependsOn: testClasses) { + description = 'Run test tagged with tag given by "-PincludeTags="' + group = 'verification' + String tags = project.hasProperty("includeTags") ? + project.property("includeTags") : '' + + useJUnitPlatform { + includeTags tags + } +} + +preprocessorTesting { //noinspection GroovyAssignabilityCheck - compilerLocation = '../libs/relast.jar' + relastCompilerLocation = '../libs/relast.jar' +// ragconnectCompilerLocation = '../libs/ragconnect.jar' } File genSrc = file("src/test/java-gen") @@ -82,259 +114,452 @@ sourceSets.test.java.srcDir genSrc idea.module.generatedSourceDirs += genSrc clean { - delete 'src/test/02-after-ragconnect/*/', 'src/test/03-after-relast/*/', 'src/test/java-gen/*/' + delete fileTree(dir: 'src/test/02-after-ragconnect/', exclude: '.gitignore') + delete fileTree(dir: 'src/test/03-after-relast/', exclude: '.gitignore') + delete fileTree(dir: 'src/test/java-gen/', exclude: '.gitignore') } // --- Test: Example --- -task preprocessExampleTest(type: JavaExec, group: 'verification') { - doFirst { - delete 'src/test/02-after-ragconnect/example/Test.relast', - 'src/test/02-after-ragconnect/example/MqttHandler.jadd', - 'src/test/02-after-ragconnect/example/RagConnect.jadd' - } - - classpath = sourceSets.main.runtimeClasspath - main = 'org.jastadd.ragconnect.compiler.Compiler' - args '--o=src/test/02-after-ragconnect/example', - 'src/test/01-input/example/Test.relast', - 'src/test/01-input/example/Test.connect', - '--rootNode=Model', - '--logReads', '--logWrites', '--protocols=mqtt' -} - -task compileExampleTest(type: RelastTest) { - useJastAddNames = true - jastAddList = 'JastAddList' - relastFiles 'src/test/02-after-ragconnect/example/Test.relast', - 'src/test/02-after-ragconnect/example/RagConnect.relast' - grammarName = 'src/test/03-after-relast/example/example' - packageName = 'example.ast' - moreInputFiles 'src/test/01-input/example/Test.jadd', - 'src/test/02-after-ragconnect/example/MqttHandler.jadd', - 'src/test/02-after-ragconnect/example/RagConnect.jadd' +task compileExampleTest(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/example') + inputFiles = [file('src/test/01-input/example/Test.relast'), + file('src/test/01-input/example/Test.connect')] + rootNode = 'Model' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/example/example' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'example.ast' + inputFiles = [file('src/test/01-input/example/Test.jadd')] + } } -compileTestJava.dependsOn compileExampleTest -compileExampleTest.dependsOn preprocessExampleTest - // --- Test: default-only-read --- -task preprocessDefaultOnlyReadTest(type: JavaExec, group: 'verification') { - doFirst { - delete 'src/test/02-after-ragconnect/defaultOnlyRead/Test.relast', - 'src/test/02-after-ragconnect/defaultOnlyRead/MqttHandler.jadd', - 'src/test/02-after-ragconnect/defaultOnlyRead/RagConnect.jadd' - } - - classpath = sourceSets.main.runtimeClasspath - main = 'org.jastadd.ragconnect.compiler.Compiler' - args '--o=src/test/02-after-ragconnect/defaultOnlyRead', - 'src/test/01-input/defaultOnlyRead/Test.relast', - 'src/test/01-input/defaultOnlyRead/Test.connect', - '--rootNode=A', '--protocols=mqtt' +task compileDefaultOnlyRead(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/defaultOnlyRead') + inputFiles = [file('src/test/01-input/defaultOnlyRead/Test.relast'), + file('src/test/01-input/defaultOnlyRead/Test.connect')] + rootNode = 'A' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/defaultOnlyRead/defaultOnlyRead' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'defaultOnlyRead.ast' + } } -task compileDefaultOnlyReadTest(type: RelastTest) { - useJastAddNames = true - jastAddList = 'JastAddList' - relastFiles 'src/test/02-after-ragconnect/defaultOnlyRead/Test.relast', - 'src/test/02-after-ragconnect/defaultOnlyRead/RagConnect.relast' - grammarName = 'src/test/03-after-relast/defaultOnlyRead/defaultOnlyRead' - packageName = 'defaultOnlyRead.ast' - moreInputFiles 'src/test/02-after-ragconnect/defaultOnlyRead/MqttHandler.jadd', - 'src/test/02-after-ragconnect/defaultOnlyRead/RagConnect.jadd' +// --- Test: default-only-write --- +task compileDefaultOnlyWrite(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/defaultOnlyWrite') + inputFiles = [file('src/test/01-input/defaultOnlyWrite/Test.relast'), + file('src/test/01-input/defaultOnlyWrite/Test.connect')] + rootNode = 'A' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/defaultOnlyWrite/defaultOnlyWrite' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'defaultOnlyWrite.ast' + inputFiles = [file('src/test/01-input/defaultOnlyWrite/Test.jadd')] + } } -compileTestJava.dependsOn compileDefaultOnlyReadTest -compileDefaultOnlyReadTest.dependsOn preprocessDefaultOnlyReadTest - -// --- Test: default-only-write --- -task preprocessDefaultOnlyWriteTest(type: JavaExec, group: 'verification') { - doFirst { - delete 'src/test/02-after-ragconnect/defaultOnlyWrite/Test.relast', - 'src/test/02-after-ragconnect/defaultOnlyWrite/MqttHandler.jadd', - 'src/test/02-after-ragconnect/defaultOnlyWrite/RagConnect.jadd' - } - - classpath = sourceSets.main.runtimeClasspath - main = 'org.jastadd.ragconnect.compiler.Compiler' - args '--o=src/test/02-after-ragconnect/defaultOnlyWrite', - 'src/test/01-input/defaultOnlyWrite/Test.relast', - 'src/test/01-input/defaultOnlyWrite/Test.connect', - '--rootNode=A', '--protocols=mqtt' +// --- Test: read1write2 --- +task compileRead1write2(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/read1write2') + inputFiles = [file('src/test/01-input/read1write2/Test.relast'), + file('src/test/01-input/read1write2/Test.connect')] + rootNode = 'A' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/read1write2/read1write2' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'read1write2.ast' + inputFiles = [file('src/test/01-input/read1write2/Test.jadd')] + } } -task compileDefaultOnlyWriteTest(type: RelastTest) { - useJastAddNames = true - jastAddList = 'JastAddList' - relastFiles 'src/test/02-after-ragconnect/defaultOnlyWrite/Test.relast', - 'src/test/02-after-ragconnect/defaultOnlyWrite/RagConnect.relast' - grammarName = 'src/test/03-after-relast/defaultOnlyWrite/defaultOnlyWrite' - packageName = 'defaultOnlyWrite.ast' - moreInputFiles 'src/test/01-input/defaultOnlyWrite/Test.jadd', - 'src/test/02-after-ragconnect/defaultOnlyWrite/MqttHandler.jadd', - 'src/test/02-after-ragconnect/defaultOnlyWrite/RagConnect.jadd' +// --- Test: read2write1 --- +task compileRead2write1(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/read2write1') + inputFiles = [file('src/test/01-input/read2write1/Test.relast'), + file('src/test/01-input/read2write1/Test.connect')] + rootNode = 'A' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/read2write1/read2write1' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'read2write1.ast' + inputFiles = [file('src/test/01-input/read2write1/Test.jadd')] + } } -compileTestJava.dependsOn compileDefaultOnlyWriteTest -compileDefaultOnlyWriteTest.dependsOn preprocessDefaultOnlyWriteTest +// --- Test: via --- +task compileVia(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/via') + inputFiles = [file('src/test/01-input/via/Test.relast'), + file('src/test/01-input/via/Test.connect')] + rootNode = 'A' + protocols = ['mqtt', 'rest'] + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/via/via' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'via.ast' + inputFiles = [file('src/test/01-input/via/Test.jadd')] + } +} -// --- Test: read1write2 --- -task preprocessRead1Write2Test(type: JavaExec, group: 'verification') { - doFirst { - delete 'src/test/02-after-ragconnect/read1write2/Test.relast', - 'src/test/02-after-ragconnect/read1write2/MqttHandler.jadd', - 'src/test/02-after-ragconnect/read1write2/RagConnect.jadd' - } - - classpath = sourceSets.main.runtimeClasspath - main = 'org.jastadd.ragconnect.compiler.Compiler' - args '--o=src/test/02-after-ragconnect/read1write2', - 'src/test/01-input/read1write2/Test.relast', - 'src/test/01-input/read1write2/Test.connect', - '--rootNode=A', '--protocols=mqtt' +// --- Test: token-value-send --- +task compileTokenValueSend(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/tokenValueSend') + inputFiles = [file('src/test/01-input/tokenValueSend/Test.relast'), + file('src/test/01-input/tokenValueSend/Test.connect')] + rootNode = 'A' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/tokenValueSend/tokenValueSend' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'tokenValueSend.ast' + inputFiles = [file('src/test/01-input/tokenValueSend/Test.jadd')] + } } -task compileRead1Write2Test(type: RelastTest) { - useJastAddNames = true - jastAddList = 'JastAddList' - relastFiles 'src/test/02-after-ragconnect/read1write2/Test.relast', - 'src/test/02-after-ragconnect/read1write2/RagConnect.relast' - grammarName = 'src/test/03-after-relast/read1write2/read1write2' - packageName = 'read1write2.ast' - moreInputFiles 'src/test/01-input/read1write2/Test.jadd', - 'src/test/02-after-ragconnect/read1write2/MqttHandler.jadd', - 'src/test/02-after-ragconnect/read1write2/RagConnect.jadd' +// --- Test: tutorial --- +task compileTutorial(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/tutorial') + inputFiles = [file('src/test/01-input/tutorial/Test.relast'), + file('src/test/01-input/tutorial/Test.connect')] + rootNode = 'A' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/tutorial/tutorial' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'tutorial.ast' + inputFiles = [file('src/test/01-input/tutorial/Test.jadd')] + } } -compileTestJava.dependsOn compileRead1Write2Test -compileRead1Write2Test.dependsOn preprocessRead1Write2Test +// --- Test: incremental --- +task compileIncremental(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/incremental') + inputFiles = [file('src/test/01-input/incremental/Test.relast'), + file('src/test/01-input/incremental/Test.connect')] + rootNode = 'A' + logWrites = true + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/incremental/incremental' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'incremental.ast' + inputFiles = [file('src/test/01-input/incremental/Test.jadd')] + extraOptions = ['--tracing=cache,flush', + '--incremental=param', + '--cache=all', + '--rewrite=cnta', + '--flush=full'] + } +} -// --- Test: read2write1 --- -task preprocessRead2Write1Test(type: JavaExec, group: 'verification') { - doFirst { - delete 'src/test/02-after-ragconnect/read2write1/Test.relast', - 'src/test/02-after-ragconnect/read2write1/MqttHandler.jadd', - 'src/test/02-after-ragconnect/read2write1/RagConnect.jadd' - } - - classpath = sourceSets.main.runtimeClasspath - main = 'org.jastadd.ragconnect.compiler.Compiler' - args '--o=src/test/02-after-ragconnect/read2write1', - 'src/test/01-input/read2write1/Test.relast', - 'src/test/01-input/read2write1/Test.connect', - '--rootNode=A', '--protocols=mqtt' +// --- Test: mapping --- +task compileMapping(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/mapping') + inputFiles = [file('src/test/01-input/mapping/Test.relast'), + file('src/test/01-input/mapping/Test.connect')] + rootNode = 'A' + logReads = true + logWrites = true + verbose = true + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/mapping/mapping' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'mapping.ast' + inputFiles = [file('src/test/01-input/mapping/Test.jadd')] + } } -task compileRead2Write1Test(type: RelastTest) { - useJastAddNames = true - jastAddList = 'JastAddList' - relastFiles 'src/test/02-after-ragconnect/read2write1/Test.relast', - 'src/test/02-after-ragconnect/read2write1/RagConnect.relast' - grammarName = 'src/test/03-after-relast/read2write1/read2write1' - packageName = 'read2write1.ast' - moreInputFiles 'src/test/01-input/read2write1/Test.jadd', - 'src/test/02-after-ragconnect/read2write1/MqttHandler.jadd', - 'src/test/02-after-ragconnect/read2write1/RagConnect.jadd' +// --- Test: tree-manual --- +task compileTreeManual(type: RagConnectTest, dependsOn: ':ragconnect.base:compileJava') { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/tree') + inputFiles = [file('src/test/01-input/tree/Test.relast'), + file('src/test/01-input/tree/Test.connect'), + file('src/test/01-input/tree/TestDependencies.connect')] + rootNode = 'Root' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/tree/tree' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'tree.ast' + inputFiles = [file('src/test/01-input/tree/Test.jadd')] + } } -compileTestJava.dependsOn compileRead2Write1Test -compileRead2Write1Test.dependsOn preprocessRead2Write1Test +// --- Test: tree-incremental --- +task compileTreeIncremental(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/treeInc') + inputFiles = [file('src/test/01-input/tree/Test.relast'), + file('src/test/01-input/tree/Test.connect')] + rootNode = 'Root' + logWrites = true + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/treeInc/treeInc' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'treeInc.ast' + inputFiles = [file('src/test/01-input/tree/Test.jadd')] + extraOptions = ['--tracing=cache,flush', + '--incremental=param', + '--cache=all', + '--rewrite=cnta', + '--flush=full'] + } +} -// --- Test: via --- -task preprocessViaTest(type: JavaExec, group: 'verification') { - doFirst { - delete 'src/test/02-after-ragconnect/via/Test.relast', - 'src/test/02-after-ragconnect/via/MqttHandler.jadd', - 'src/test/02-after-ragconnect/via/RestHandler.jadd', - 'src/test/02-after-ragconnect/via/RagConnect.jadd' - } - - classpath = sourceSets.main.runtimeClasspath - main = 'org.jastadd.ragconnect.compiler.Compiler' - args '--o=src/test/02-after-ragconnect/via', - 'src/test/01-input/via/Test.relast', - 'src/test/01-input/via/Test.connect', - '--rootNode=A', - '--protocols=mqtt,rest' +// --- Test: tree-allowed-tokens --- +task compileTreeAllowedTokens(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/treeAllowedTokens') + inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.relast'), + file('src/test/01-input/treeAllowedTokens/Test.connect'), + file('src/test/01-input/treeAllowedTokens/TestDependencies.connect')] + rootNode = 'Root' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/treeAllowedTokens/treeAllowedTokens' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'treeAllowedTokens.ast' + inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.jadd')] + } } -task compileViaTest(type: RelastTest) { - useJastAddNames = true - jastAddList = 'JastAddList' - relastFiles 'src/test/02-after-ragconnect/via/Test.relast', - 'src/test/02-after-ragconnect/via/RagConnect.relast' - grammarName = 'src/test/03-after-relast/via/via' - packageName = 'via.ast' - moreInputFiles 'src/test/01-input/via/Test.jadd', - 'src/test/02-after-ragconnect/via/MqttHandler.jadd', - 'src/test/02-after-ragconnect/via/RestHandler.jadd', - 'src/test/02-after-ragconnect/via/RagConnect.jadd' +// --- Test: tree-allowed-tokens-incremental --- +task compileTreeAllowedTokensIncremental(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/treeAllowedTokensInc') + inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.relast'), + file('src/test/01-input/treeAllowedTokens/Test.connect')] + rootNode = 'Root' + logWrites = true + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/treeAllowedTokensInc/treeAllowedTokensInc' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'treeAllowedTokensInc.ast' + inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.jadd')] + extraOptions = ['--tracing=cache,flush', + '--incremental=param', + '--cache=all', + '--rewrite=cnta', + '--flush=full'] + } } -compileTestJava.dependsOn compileViaTest -compileViaTest.dependsOn preprocessViaTest +// --- Test: list-manual --- +task compileListManual(type: RagConnectTest, dependsOn: ':ragconnect.base:jar') { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/list') + inputFiles = [file('src/test/01-input/list/Test.relast'), + file('src/test/01-input/list/Test.connect'), + file('src/test/01-input/list/TestDependencies.connect')] + rootNode = 'Root' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/list/list' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'list.ast' + inputFiles = [file('src/test/01-input/list/Test.jadd')] + } +} -// --- Test: token-value-send --- -task preprocessTokenValueSendTest(type: JavaExec, group: 'verification') { - doFirst { - delete 'src/test/02-after-ragconnect/tokenValueSend/Test.relast', - 'src/test/02-after-ragconnect/tokenValueSend/MqttHandler.jadd', - 'src/test/02-after-ragconnect/tokenValueSend/RestHandler.jadd', - 'src/test/02-after-ragconnect/tokenValueSend/RagConnect.jadd' - } - - classpath = sourceSets.main.runtimeClasspath - main = 'org.jastadd.ragconnect.compiler.Compiler' - args '--o=src/test/02-after-ragconnect/tokenValueSend', - 'src/test/01-input/tokenValueSend/Test.relast', - 'src/test/01-input/tokenValueSend/Test.connect', - '--rootNode=A', - '--protocols=mqtt' +// --- Test: list-incremental --- +task compileListIncremental(type: RagConnectTest, dependsOn: ':ragconnect.base:jar') { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/listInc') + inputFiles = [file('src/test/01-input/list/Test.relast'), + file('src/test/01-input/list/Test.connect')] + rootNode = 'Root' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/listInc/listInc' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'listInc.ast' + inputFiles = [file('src/test/01-input/list/Test.jadd')] + extraOptions = ['--tracing=cache,flush', + '--incremental=param', + '--cache=all', + '--rewrite=cnta', + '--flush=full'] + } } -task compileTokenValueSendTest(type: RelastTest) { - useJastAddNames = true - jastAddList = 'JastAddList' - relastFiles 'src/test/02-after-ragconnect/tokenValueSend/Test.relast', - 'src/test/02-after-ragconnect/tokenValueSend/RagConnect.relast' - grammarName = 'src/test/03-after-relast/tokenValueSend/tokenValueSend' - packageName = 'tokenValueSend.ast' - moreInputFiles 'src/test/01-input/tokenValueSend/Test.jadd', - 'src/test/02-after-ragconnect/tokenValueSend/MqttHandler.jadd', - 'src/test/02-after-ragconnect/tokenValueSend/RagConnect.jadd' +// --- Test: singleList-manual --- +task compileSingleListManual(type: RagConnectTest, dependsOn: ':ragconnect.base:jar') { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/singleList') + inputFiles = [file('src/test/01-input/singleList/Test.relast'), + file('src/test/01-input/singleList/Test.connect'), + file('src/test/01-input/singleList/TestDependencies.connect')] + rootNode = 'Root' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/singleList/singleList' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'singleList.ast' + inputFiles = [file('src/test/01-input/singleList/Test.jadd')] + } } -compileTestJava.dependsOn compileTokenValueSendTest -compileTokenValueSendTest.dependsOn preprocessTokenValueSendTest +// --- Test: singleList-incremental --- +task compileSingleListIncremental(type: RagConnectTest, dependsOn: ':ragconnect.base:jar') { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/singleListInc') + inputFiles = [file('src/test/01-input/singleList/Test.relast'), + file('src/test/01-input/singleList/Test.connect')] + rootNode = 'Root' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/singleListInc/singleListInc' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'singleListInc.ast' + inputFiles = [file('src/test/01-input/singleList/Test.jadd')] + extraOptions = ['--tracing=cache,flush', + '--incremental=param', + '--cache=all', + '--rewrite=cnta', + '--flush=full'] + } +} -// --- Test: tutorial --- -task preprocessTutorialTest(type: JavaExec, group: 'verification') { - doFirst { - delete 'src/test/02-after-ragconnect/tutorial/Test.relast', - 'src/test/02-after-ragconnect/tutorial/MqttHandler.jadd', - 'src/test/02-after-ragconnect/tutorial/RagConnect.jadd' - } - - classpath = sourceSets.main.runtimeClasspath - main = 'org.jastadd.ragconnect.compiler.Compiler' - args '--o=src/test/02-after-ragconnect/tutorial', - 'src/test/01-input/tutorial/Test.relast', - 'src/test/01-input/tutorial/Test.connect', - '--rootNode=A', - '--protocols=mqtt' +// --- Test: singleListVariant-manual --- +task compileSingleListVariantManual(type: RagConnectTest, dependsOn: ':ragconnect.base:jar') { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/singleListVariant') + inputFiles = [file('src/test/01-input/singleListVariant/Test.relast'), + file('src/test/01-input/singleListVariant/Test.connect'), + file('src/test/01-input/singleListVariant/TestDependencies.connect')] + rootNode = 'Root' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/singleListVariant/singleListVariant' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'singleListVariant.ast' + inputFiles = [file('src/test/01-input/singleListVariant/Test.jadd')] + } } -task compileTutorialTest(type: RelastTest) { - useJastAddNames = true - jastAddList = 'JastAddList' - relastFiles 'src/test/02-after-ragconnect/tutorial/Test.relast', - 'src/test/02-after-ragconnect/tutorial/RagConnect.relast' - grammarName = 'src/test/03-after-relast/tutorial/tutorial' - packageName = 'tutorial.ast' - moreInputFiles 'src/test/01-input/tutorial/Test.jadd', - 'src/test/02-after-ragconnect/tutorial/MqttHandler.jadd', - 'src/test/02-after-ragconnect/tutorial/RagConnect.jadd' +// --- Test: singleListVariant-incremental --- +task compileSingleListVariantIncremental(type: RagConnectTest, dependsOn: ':ragconnect.base:jar') { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/singleListVariantInc') + inputFiles = [file('src/test/01-input/singleListVariant/Test.relast'), + file('src/test/01-input/singleListVariant/Test.connect')] + rootNode = 'Root' + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/singleListVariantInc/singleListVariantInc' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'singleListVariantInc.ast' + inputFiles = [file('src/test/01-input/singleListVariant/Test.jadd')] + extraOptions = ['--tracing=cache,flush', + '--incremental=param', + '--cache=all', + '--rewrite=cnta', + '--flush=full'] + } } -compileTestJava.dependsOn compileTutorialTest -compileTutorialTest.dependsOn preprocessTutorialTest +//task cleanCurrentManualTest(type: Delete) { +// delete "src/test/02-after-ragconnect/singleListVariant" +// delete "src/test/03-after-relast/singleListVariant" +// delete "src/test/java-gen/singleListVariant/ast" +//} +//task cleanCurrentIncrementalTest(type: Delete) { +// delete "src/test/02-after-ragconnect/singleListVariantInc" +// delete "src/test/03-after-relast/singleListVariantInc" +// delete "src/test/java-gen/singleListVariantInc/ast" +//} +//compileSingleListVariantManual.dependsOn cleanCurrentManualTest +//compileSingleListVariantIncremental.dependsOn cleanCurrentIncrementalTest + diff --git a/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.connect b/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.connect index e274994175513ff743409068375f419b6e3141fc..69a2165b1dc0ace787a1dbe8143807c90b3197c8 100644 --- a/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.connect +++ b/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.connect @@ -1,4 +1,5 @@ // --- update definitions --- +receive NativeTypes.BooleanValue; receive NativeTypes.IntValue; receive NativeTypes.ShortValue; receive NativeTypes.LongValue; @@ -7,9 +8,52 @@ receive NativeTypes.DoubleValue; receive NativeTypes.CharValue; receive NativeTypes.StringValue; +receive NativeTypes.BooleanValueTransformed using BooleanTransformation; +receive NativeTypes.IntValueTransformed using IntTransformation; +receive NativeTypes.ShortValueTransformed using ShortTransformation; +receive NativeTypes.LongValueTransformed using LongTransformation; +receive NativeTypes.FloatValueTransformed using FloatTransformation; +receive NativeTypes.DoubleValueTransformed using DoubleTransformation; +receive NativeTypes.CharValueTransformed using CharTransformation; +receive NativeTypes.StringValueTransformed using StringTransformation; + +receive BoxedTypes.BooleanValue; receive BoxedTypes.IntValue; receive BoxedTypes.ShortValue; receive BoxedTypes.LongValue; receive BoxedTypes.FloatValue; receive BoxedTypes.DoubleValue; receive BoxedTypes.CharValue; + +receive BoxedTypes.BooleanValueTransformed using BooleanTransformation; +receive BoxedTypes.IntValueTransformed using IntTransformation; +receive BoxedTypes.ShortValueTransformed using ShortTransformation; +receive BoxedTypes.LongValueTransformed using LongTransformation; +receive BoxedTypes.FloatValueTransformed using FloatTransformation; +receive BoxedTypes.DoubleValueTransformed using DoubleTransformation; +receive BoxedTypes.CharValueTransformed using CharTransformation; + +BooleanTransformation maps boolean b to boolean {: + return b; +:} +IntTransformation maps int i to int {: + return i; +:} +ShortTransformation maps short s to short {: + return s; +:} +LongTransformation maps long l to long {: + return l; +:} +FloatTransformation maps float f to float {: + return f; +:} +DoubleTransformation maps double d to double {: + return d; +:} +CharTransformation maps char c to char {: + return c; +:} +StringTransformation maps String s to String {: + return s; +:} diff --git a/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.relast b/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.relast index 30eba76b346dace40c72ca4e172b88ef206ea063..060d5a5c16e793a20c79d99a267e58833b0c8ac1 100644 --- a/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.relast +++ b/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.relast @@ -1,3 +1,3 @@ A ::= NativeTypes* BoxedTypes* ; -NativeTypes ::= <IntValue:int> <ShortValue:short> <LongValue:long> <FloatValue:float> <DoubleValue:double> <CharValue:char> <StringValue:String> ; -BoxedTypes ::= <IntValue:Integer> <ShortValue:Short> <LongValue:Long> <FloatValue:Float> <DoubleValue:Double> <CharValue:Character> ; +NativeTypes ::= <BooleanValue:boolean> <IntValue:int> <ShortValue:short> <LongValue:long> <FloatValue:float> <DoubleValue:double> <CharValue:char> <StringValue:String> <BooleanValueTransformed:boolean> <IntValueTransformed:int> <ShortValueTransformed:short> <LongValueTransformed:long> <FloatValueTransformed:float> <DoubleValueTransformed:double> <CharValueTransformed:char> <StringValueTransformed:String> ; +BoxedTypes ::= <BooleanValue:Boolean> <IntValue:Integer> <ShortValue:Short> <LongValue:Long> <FloatValue:Float> <DoubleValue:Double> <CharValue:Character> <BooleanValueTransformed:Boolean> <IntValueTransformed:Integer> <ShortValueTransformed:Short> <LongValueTransformed:Long> <FloatValueTransformed:Float> <DoubleValueTransformed:Double> <CharValueTransformed:Character> ; diff --git a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.connect b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.connect index a48f49f60f66c3358ee513e79f2bbeb43df8d5e9..44ca4ba05574a67b29268a1b4ff61185a30ea15f 100644 --- a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.connect +++ b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.connect @@ -1,5 +1,6 @@ // --- update definitions --- // native types, synthesized +send NativeTypesSyn.BooleanValue; send NativeTypesSyn.IntValue; send NativeTypesSyn.ShortValue; send NativeTypesSyn.LongValue; @@ -8,7 +9,18 @@ send NativeTypesSyn.DoubleValue; send NativeTypesSyn.CharValue; send NativeTypesSyn.StringValue; +// native types, synthesized, transformed +send NativeTypesSyn.BooleanValueTransformed using BooleanTransformation; +send NativeTypesSyn.IntValueTransformed using IntTransformation; +send NativeTypesSyn.ShortValueTransformed using ShortTransformation; +send NativeTypesSyn.LongValueTransformed using LongTransformation; +send NativeTypesSyn.FloatValueTransformed using FloatTransformation; +send NativeTypesSyn.DoubleValueTransformed using DoubleTransformation; +send NativeTypesSyn.CharValueTransformed using CharTransformation; +send NativeTypesSyn.StringValueTransformed using StringTransformation; + // boxed types, synthesized +send BoxedTypesSyn.BooleanValue; send BoxedTypesSyn.IntValue; send BoxedTypesSyn.ShortValue; send BoxedTypesSyn.LongValue; @@ -16,7 +28,17 @@ send BoxedTypesSyn.FloatValue; send BoxedTypesSyn.DoubleValue; send BoxedTypesSyn.CharValue; +// boxed types, synthesized, transformed +send BoxedTypesSyn.BooleanValueTransformed using BooleanTransformation; +send BoxedTypesSyn.IntValueTransformed using IntTransformation; +send BoxedTypesSyn.ShortValueTransformed using ShortTransformation; +send BoxedTypesSyn.LongValueTransformed using LongTransformation; +send BoxedTypesSyn.FloatValueTransformed using FloatTransformation; +send BoxedTypesSyn.DoubleValueTransformed using DoubleTransformation; +send BoxedTypesSyn.CharValueTransformed using CharTransformation; + // --- dependency definitions --- +NativeTypesSyn.BooleanValue canDependOn NativeTypesSyn.DriverSyn as nativeBooleanDependency; NativeTypesSyn.IntValue canDependOn NativeTypesSyn.DriverSyn as nativeIntDependency; NativeTypesSyn.ShortValue canDependOn NativeTypesSyn.DriverSyn as nativeShortDependency; NativeTypesSyn.LongValue canDependOn NativeTypesSyn.DriverSyn as nativeLongDependency; @@ -24,6 +46,8 @@ NativeTypesSyn.FloatValue canDependOn NativeTypesSyn.DriverSyn as nativeFloatDep NativeTypesSyn.DoubleValue canDependOn NativeTypesSyn.DriverSyn as nativeDoubleDependency; NativeTypesSyn.CharValue canDependOn NativeTypesSyn.DriverSyn as nativeCharDependency; NativeTypesSyn.StringValue canDependOn NativeTypesSyn.DriverSyn as nativeStringDependency; + +BoxedTypesSyn.BooleanValue canDependOn BoxedTypesSyn.DriverSyn as boxedBooleanDependency; BoxedTypesSyn.IntValue canDependOn BoxedTypesSyn.DriverSyn as boxedIntDependency; BoxedTypesSyn.ShortValue canDependOn BoxedTypesSyn.DriverSyn as boxedShortDependency; BoxedTypesSyn.LongValue canDependOn BoxedTypesSyn.DriverSyn as boxedLongDependency; @@ -31,6 +55,23 @@ BoxedTypesSyn.FloatValue canDependOn BoxedTypesSyn.DriverSyn as boxedFloatDepend BoxedTypesSyn.DoubleValue canDependOn BoxedTypesSyn.DriverSyn as boxedDoubleDependency; BoxedTypesSyn.CharValue canDependOn BoxedTypesSyn.DriverSyn as boxedCharDependency; +NativeTypesSyn.BooleanValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeBooleanTransformedDependency; +NativeTypesSyn.IntValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeIntTransformedDependency; +NativeTypesSyn.ShortValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeShortTransformedDependency; +NativeTypesSyn.LongValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeLongTransformedDependency; +NativeTypesSyn.FloatValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeFloatTransformedDependency; +NativeTypesSyn.DoubleValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeDoubleTransformedDependency; +NativeTypesSyn.CharValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeCharTransformedDependency; +NativeTypesSyn.StringValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeStringTransformedDependency; + +BoxedTypesSyn.BooleanValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedBooleanTransformedDependency; +BoxedTypesSyn.IntValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedIntTransformedDependency; +BoxedTypesSyn.ShortValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedShortTransformedDependency; +BoxedTypesSyn.LongValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedLongTransformedDependency; +BoxedTypesSyn.FloatValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedFloatTransformedDependency; +BoxedTypesSyn.DoubleValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedDoubleTransformedDependency; +BoxedTypesSyn.CharValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedCharTransformedDependency; + // --- inherited attributes not supported --- //// native types, inherited @@ -49,3 +90,28 @@ BoxedTypesSyn.CharValue canDependOn BoxedTypesSyn.DriverSyn as boxedCharDependen //send BoxedTypesInh.FloatValue; //send BoxedTypesInh.DoubleValue; //send BoxedTypesInh.CharValue; + +BooleanTransformation maps boolean b to boolean {: + return b; +:} +IntTransformation maps int i to int {: + return i; +:} +ShortTransformation maps short s to short {: + return s; +:} +LongTransformation maps long l to long {: + return l; +:} +FloatTransformation maps float f to float {: + return f; +:} +DoubleTransformation maps double d to double {: + return d; +:} +CharTransformation maps char c to char {: + return c; +:} +StringTransformation maps String s to String {: + return s; +:} diff --git a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.jadd b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.jadd index 49bbf604d398f7a9d92e879b2b515c9e83dd3182..76f3f159b673f1b03c1001a2d7a251bc9ca54dfa 100644 --- a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.jadd +++ b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.jadd @@ -1,5 +1,6 @@ aspect Computation { // native types, synthesized + syn boolean NativeTypesSyn.getBooleanValue() = Integer.parseInt(getDriverSyn()) > 1; syn int NativeTypesSyn.getIntValue() = Integer.parseInt(getDriverSyn()); syn short NativeTypesSyn.getShortValue() = Short.parseShort(getDriverSyn()); syn long NativeTypesSyn.getLongValue() = Long.parseLong(getDriverSyn()); @@ -8,7 +9,17 @@ aspect Computation { syn char NativeTypesSyn.getCharValue() = getDriverSyn().charAt(0); syn String NativeTypesSyn.getStringValue() = new String(getDriverSyn()); + syn boolean NativeTypesSyn.getBooleanValueTransformed() = Integer.parseInt(getDriverSyn()) > 1; + syn int NativeTypesSyn.getIntValueTransformed() = Integer.parseInt(getDriverSyn()); + syn short NativeTypesSyn.getShortValueTransformed() = Short.parseShort(getDriverSyn()); + syn long NativeTypesSyn.getLongValueTransformed() = Long.parseLong(getDriverSyn()); + syn float NativeTypesSyn.getFloatValueTransformed() = Float.parseFloat(getDriverSyn()); + syn double NativeTypesSyn.getDoubleValueTransformed() = Double.parseDouble(getDriverSyn()); + syn char NativeTypesSyn.getCharValueTransformed() = getDriverSyn().charAt(0); + syn String NativeTypesSyn.getStringValueTransformed() = new String(getDriverSyn()); + // boxed types, synthesized + syn Boolean BoxedTypesSyn.getBooleanValue() = Integer.parseInt(getDriverSyn()) > 1; syn Integer BoxedTypesSyn.getIntValue() = Integer.valueOf(getDriverSyn()); syn Short BoxedTypesSyn.getShortValue() = Short.valueOf(getDriverSyn()); syn Long BoxedTypesSyn.getLongValue() = Long.valueOf(getDriverSyn()); @@ -16,6 +27,14 @@ aspect Computation { syn Double BoxedTypesSyn.getDoubleValue() = Double.valueOf(getDriverSyn()); syn Character BoxedTypesSyn.getCharValue() = getDriverSyn().charAt(0); + syn Boolean BoxedTypesSyn.getBooleanValueTransformed() = Integer.parseInt(getDriverSyn()) > 1; + syn Integer BoxedTypesSyn.getIntValueTransformed() = Integer.valueOf(getDriverSyn()); + syn Short BoxedTypesSyn.getShortValueTransformed() = Short.valueOf(getDriverSyn()); + syn Long BoxedTypesSyn.getLongValueTransformed() = Long.valueOf(getDriverSyn()); + syn Float BoxedTypesSyn.getFloatValueTransformed() = Float.valueOf(getDriverSyn()); + syn Double BoxedTypesSyn.getDoubleValueTransformed() = Double.valueOf(getDriverSyn()); + syn Character BoxedTypesSyn.getCharValueTransformed() = getDriverSyn().charAt(0); + // --- inherited attributes not supported --- // // native types, inherited // inh int NativeTypesInh.getIntValue(); diff --git a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.relast b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.relast index e0fd7829b88b8fac04b64393e0262c69b4ac6a62..b9203c6ac13736308216c42326551e60dcd6c84c 100644 --- a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.relast +++ b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.relast @@ -1,8 +1,8 @@ A ::= NativeTypesSyn* BoxedTypesSyn* <DriverInh:String>; // native types, synthesized -NativeTypesSyn ::= <DriverSyn:String> /<IntValue:int>/ /<ShortValue:short>/ /<LongValue:long>/ /<FloatValue:float>/ /<DoubleValue:double>/ /<CharValue:char>/ /<StringValue:String>/ ; +NativeTypesSyn ::= <DriverSyn:String> /<BooleanValue:boolean>/ /<IntValue:int>/ /<ShortValue:short>/ /<LongValue:long>/ /<FloatValue:float>/ /<DoubleValue:double>/ /<CharValue:char>/ /<StringValue:String>/ /<BooleanValueTransformed:boolean>/ /<IntValueTransformed:int>/ /<ShortValueTransformed:short>/ /<LongValueTransformed:long>/ /<FloatValueTransformed:float>/ /<DoubleValueTransformed:double>/ /<CharValueTransformed:char>/ /<StringValueTransformed:String>/ ; // boxed types, synthesized -BoxedTypesSyn ::= <DriverSyn:String> /<IntValue:Integer>/ /<ShortValue:Short>/ /<LongValue:Long>/ /<FloatValue:Float>/ /<DoubleValue:Double>/ /<CharValue:Character>/ ; +BoxedTypesSyn ::= <DriverSyn:String> /<BooleanValue:Boolean>/ /<IntValue:Integer>/ /<ShortValue:Short>/ /<LongValue:Long>/ /<FloatValue:Float>/ /<DoubleValue:Double>/ /<CharValue:Character>/ /<BooleanValueTransformed:Boolean>/ /<IntValueTransformed:Integer>/ /<ShortValueTransformed:Short>/ /<LongValueTransformed:Long>/ /<FloatValueTransformed:Float>/ /<DoubleValueTransformed:Double>/ /<CharValueTransformed:Character>/ ; // --- inherited attributes not supported --- //// native types, inherited diff --git a/ragconnect.tests/src/test/01-input/errors/Errors.expected b/ragconnect.tests/src/test/01-input/errors/Errors.expected deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/ragconnect.tests/src/test/01-input/errors/Part.expected b/ragconnect.tests/src/test/01-input/errors/Part.expected new file mode 100644 index 0000000000000000000000000000000000000000..70ea4a4baeaffb5b35df672c03e90821eb3cec82 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/errors/Part.expected @@ -0,0 +1,11 @@ +Part1.connect Line 3, column 1: Receive definition already defined for DoubledValue +Part1.connect Line 4, column 1: Receive definition already defined for DoubledValue +Part1.connect Line 10, column 1: Receiving target token must not be an NTA token! +Part1.connect Line 13, column 1: No suitable default mapping found for type java.util.List +Part1.connect Line 13, column 1: to-type of last mapping (java.util.List) not assignable to type of the Token (String)! +Part1.connect Line 16, column 1: to-type of last mapping (List) not assignable to type of the Token (String)! +Part1.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the Token (String)! +Part2.connect Line 5, column 1: Send definition already defined for DoubledValue +Part2.connect Line 6, column 1: Send definition already defined for DoubledValue +Part2.connect Line 17, column 1: The name of a dependency definition must not be equal to a list-node on the source +Part2.connect Line 22, column 1: Dependency definition already defined for D with name DoubledValue diff --git a/ragconnect.tests/src/test/01-input/errors/Part1.connect b/ragconnect.tests/src/test/01-input/errors/Part1.connect new file mode 100644 index 0000000000000000000000000000000000000000..a1be3918b36ee19561a278639555db14efb8da06 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/errors/Part1.connect @@ -0,0 +1,33 @@ +// --- update receive definitions --- +// Error: there must not be two receive definitions for the same token +receive B.DoubledValue ; +receive B.DoubledValue using IntToInt ; + +// NOT HANDLED \\ Error: the token must be resolvable within the parent type +// NOT HANDLED \\ receive B.NonExisting ; + +// Error: the Token must not be a TokenNTA (i.e., check for !Token.getNTA()) +receive B.ErrorNTA ; + +// Error: from-type of first mapping must be byte[] or a supported primitive type +receive B.ErrorTypeOfFirstMapping using ListToList ; + +// Error: to-type of last mapping must be type of the Token +receive B.ErrorTypeOfLastMapping using StringToList ; + +// Error: types of mappings must match (modulo inheritance) +receive B.ErrorTypeMismatch using StringToList, IntToInt ; + +// --- update send definitions --- +// NOT HANDLED \\ Error: the token must be resolvable within the parent type +// NOT HANDLED \\ receive C.NonExisting ; + +// Error: Token must be a TokenNTA (i.e., check for Token.getNTA()) +send C.ErrorNotNTA ; + +// Error: from-type of first mapping must be type of Token +send C.ErrorTypeOfFirstMapping using IntToInt ; + +// Error: to-type of last mapping must be byte[] or a supported primitive type +send C.ErrorTypeOfLastMapping1 using StringToList ; +send C.ErrorTypeOfLastMapping2 ; diff --git a/ragconnect.tests/src/test/01-input/errors/Part2.connect b/ragconnect.tests/src/test/01-input/errors/Part2.connect new file mode 100644 index 0000000000000000000000000000000000000000..4d1148260a36855528b37f8b45020e076ff9ff1e --- /dev/null +++ b/ragconnect.tests/src/test/01-input/errors/Part2.connect @@ -0,0 +1,38 @@ +// Error: types of mappings must match (modulo inheritance) +send C.ErrorTypeMismatch using StringToList, IntToInt ; + +// Error: no more than one send mapping for each TokenComponent +send C.DoubledValue ; +send C.DoubledValue using IntToInt ; + +// --- dependency definitions --- +// NOT HANDLED \\ Error: Both, source and target must be resolvable within the parent type +// NOT HANDLED \\ D.SourceNonExistingTarget canDependOn D.NonExisting as NonExistingTarget ; +// NOT HANDLED \\ D.NonExisting canDependOn D.TargetNonExistingSource as NonExistingSource ; + +// Error: There must be a send update definition for the target token +D.SourceNoWriteDef canDependOn D.TargetNoWriteDef as NoWriteDef ; + +// Error: The name of a dependency definition must not be equal to a list-node on the source +D.SourceSameAsListNode canDependOn D.TargetSameAsListNode as MyList ; +send D.TargetSameAsListNode; + +// Error: There must not be two dependency definitions with the same name +D.SourceDoubledValue canDependOn D.TargetDoubledValue as DoubledValue ; +D.SourceDoubledValue canDependOn D.TargetDoubledValue as DoubledValue ; +send D.TargetDoubledValue; + +// --- mapping definitions --- +ListToList maps java.util.List<String> list to java.util.List<String> {: + return list; +:} + +StringToList maps String s to List<String> {: + java.util.List<String> result = new java.util.ArrayList<>(); + result.add(s); + return result; +:} + +IntToInt maps int number to int {: + return number + 1; +:} diff --git a/ragconnect.tests/src/test/01-input/errors/Errors.connect b/ragconnect.tests/src/test/01-input/errors/Standard.connect similarity index 100% rename from ragconnect.tests/src/test/01-input/errors/Errors.connect rename to ragconnect.tests/src/test/01-input/errors/Standard.connect diff --git a/ragconnect.tests/src/test/01-input/errors/Standard.expected b/ragconnect.tests/src/test/01-input/errors/Standard.expected new file mode 100644 index 0000000000000000000000000000000000000000..3ee660570525834a63c41f26820966a7504f7789 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/errors/Standard.expected @@ -0,0 +1,11 @@ +Standard.connect Line 3, column 1: Receive definition already defined for DoubledValue +Standard.connect Line 4, column 1: Receive definition already defined for DoubledValue +Standard.connect Line 10, column 1: Receiving target token must not be an NTA token! +Standard.connect Line 13, column 1: No suitable default mapping found for type java.util.List +Standard.connect Line 13, column 1: to-type of last mapping (java.util.List) not assignable to type of the Token (String)! +Standard.connect Line 16, column 1: to-type of last mapping (List) not assignable to type of the Token (String)! +Standard.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the Token (String)! +Standard.connect Line 39, column 1: Send definition already defined for DoubledValue +Standard.connect Line 40, column 1: Send definition already defined for DoubledValue +Standard.connect Line 51, column 1: The name of a dependency definition must not be equal to a list-node on the source +Standard.connect Line 56, column 1: Dependency definition already defined for D with name DoubledValue diff --git a/ragconnect.tests/src/test/01-input/incremental/README.md b/ragconnect.tests/src/test/01-input/incremental/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8fb46cc058138cbedeeda5eaa1a40ab97e088e28 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/incremental/README.md @@ -0,0 +1,3 @@ +# Tutorial + +Idea: Test the example from the [documentation](https://jastadd.pages.st.inf.tu-dresden.de/ragconnect/using.html) with activated incremental dependency tracking diff --git a/ragconnect.tests/src/test/01-input/incremental/Test.connect b/ragconnect.tests/src/test/01-input/incremental/Test.connect new file mode 100644 index 0000000000000000000000000000000000000000..8135be4e3afc9801b45d858ce9bda9016351e4a4 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/incremental/Test.connect @@ -0,0 +1,13 @@ +// endpoint definitions +receive A.Input ; +send A.OutputOnA ; +send B.OutputOnB using Transformation ; + +// mapping definitions +Transformation maps String s to String {: + return s + "Postfix"; +:} + +// dependency definitions +A.OutputOnA canDependOn A.Input as dependencyA ; +B.OutputOnB canDependOn A.Input as dependencyB ; diff --git a/ragconnect.tests/src/test/01-input/incremental/Test.jadd b/ragconnect.tests/src/test/01-input/incremental/Test.jadd new file mode 100644 index 0000000000000000000000000000000000000000..4cb9dbf4def3102924b530c58ece410e73fa255b --- /dev/null +++ b/ragconnect.tests/src/test/01-input/incremental/Test.jadd @@ -0,0 +1,7 @@ +aspect Computation { + syn String A.getOutputOnA() = "a" + getInput(); + + syn String B.getOutputOnB() = "b" + input(); + inh String B.input(); + eq A.getB().input() = getInput(); +} diff --git a/ragconnect.tests/src/test/01-input/incremental/Test.relast b/ragconnect.tests/src/test/01-input/incremental/Test.relast new file mode 100644 index 0000000000000000000000000000000000000000..9009e2787814bb8020d1459fc5d9758f9cc1ca06 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/incremental/Test.relast @@ -0,0 +1,2 @@ +A ::= <Input:String> /<OutputOnA:String>/ B* ; +B ::= /<OutputOnB:String>/ ; diff --git a/ragconnect.tests/src/test/01-input/list/README.md b/ragconnect.tests/src/test/01-input/list/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7634a3d2069a10b5c07bef272f757bd5ee2df8c9 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/list/README.md @@ -0,0 +1,34 @@ +# List + +Idea: send and receive lists of subtrees. +Once without incremental evaluation (i.e., using manual dependencies), and the other time with incremental evaluation + +## Execution-Model + +``` +SenderRoot ReceiverRoot +|- A* ---( mqtt: a ) ---+------> A* --------------------| +| \------> WidthAddFromA:A* ------| +|- SingleA:A* ,-> FromSingleA:A* --------| + \---( mqtt: single-a ) -+-> WithAddFromSingleA:A* -| +``` + +## Execution-Trace (SendInitialValue) + +| Input | # | A* | WidthAddFromA | FromSingleA | WithAddFromSingleA:A | +|---|---|---|---|---|---| +| 0 | 1 | [] | [0] | [] | [0] | +| 1 | 2 | [1] | [1] | [1] | [0,1] | +| 1 | 2 | [1] | [1] | [1] | [0,1] | +| 2 | 3 | [1,2] | [2] | [1,1,2] | [0,1,2] | +| 3 | 4 | [1,2,3] | [3] | [1,1,2,1,2,3] | [0,1,2,3] | + +## Execution-Trace (OnlyUpdate) + +| Input | # | A* | WidthAddFromA | FromSingleA | WithAddFromSingleA:A | +|---|---|---|---|---|---| +| - | 0 | [] | [] | [] | [] | +| 1 | 1 | [1] | [1] | [1] | [1] | +| 1 | 1 | [1] | [1] | [1] | [1] | +| 2 | 2 | [1,2] | [2] | [1,1,2] | [1,2] | +| 3 | 3 | [1,2,3] | [3] | [1,1,2,1,2,3] | [1,2,3] | diff --git a/ragconnect.tests/src/test/01-input/list/Test.connect b/ragconnect.tests/src/test/01-input/list/Test.connect new file mode 100644 index 0000000000000000000000000000000000000000..c0efaea9d5ad307c0c43b7d577b1cff5884c0d8b --- /dev/null +++ b/ragconnect.tests/src/test/01-input/list/Test.connect @@ -0,0 +1,6 @@ +send list SenderRoot.A ; +send list SenderRoot.SingleA ; +receive list ReceiverRoot.A ; +receive list ReceiverRoot.FromSingleA ; +receive list with add ReceiverRoot.WithAddFromA ; +receive list with add ReceiverRoot.WithAddFromSingleA ; diff --git a/ragconnect.tests/src/test/01-input/list/Test.jadd b/ragconnect.tests/src/test/01-input/list/Test.jadd new file mode 100644 index 0000000000000000000000000000000000000000..9d8ab5395a6c58e790023f902e53bae5763de76e --- /dev/null +++ b/ragconnect.tests/src/test/01-input/list/Test.jadd @@ -0,0 +1,37 @@ +aspect Computation { + syn JastAddList<A> SenderRoot.getAList() { + var result = new JastAddList<A>(); + for (int i = 1; i <= getInput(); i++) { + A a = new A().setID(i); + B b = new B().setID(i + 1); + a.addB(b); + result.addChild(a); + } + return result; + } + syn JastAddList<A> SenderRoot.getSingleAList() { + var result = new JastAddList<A>(); + A a = new A().setID(getInput()); + result.addChild(a); + return result; + } + + syn boolean ASTNode.isNameable() = false; + eq Nameable.isNameable() = true; +} + +aspect Testing { + class ReceiverRoot implements org.jastadd.ragconnect.tests.list.AbstractListTest.TestWrapperReceiverRoot {} + class A implements org.jastadd.ragconnect.tests.list.AbstractListTest.TestWrapperA {} + class B implements org.jastadd.ragconnect.tests.list.AbstractListTest.TestWrapperB {} + class JastAddList<T> implements org.jastadd.ragconnect.tests.list.AbstractListTest.TestWrapperJastAddList<T> {} +} + +aspect NameResolution { + // overriding customID guarantees to produce the same JSON representation for equal lists + // otherwise, the value for id is different each time + @Override + protected String Nameable.customID() { + return getClass().getSimpleName() + getID(); + } +} diff --git a/ragconnect.tests/src/test/01-input/list/Test.relast b/ragconnect.tests/src/test/01-input/list/Test.relast new file mode 100644 index 0000000000000000000000000000000000000000..2680b5cd43fc68d144386fc4494a52cc1ed5e002 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/list/Test.relast @@ -0,0 +1,8 @@ +Root ::= SenderRoot* ReceiverRoot* ; + +Nameable ::= <ID:int> ; +SenderRoot : Nameable ::= <Input:int> /A*/ /SingleA:A*/ ; + +ReceiverRoot : Nameable ::= A* FromSingleA:A* WithAddFromA:A* WithAddFromSingleA:A* ; +A : Nameable ::= B* ; +B : Nameable ; diff --git a/ragconnect.tests/src/test/01-input/list/TestDependencies.connect b/ragconnect.tests/src/test/01-input/list/TestDependencies.connect new file mode 100644 index 0000000000000000000000000000000000000000..bce44247ea8db263a29ce66f08bb10346c3c4245 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/list/TestDependencies.connect @@ -0,0 +1,2 @@ +SenderRoot.A canDependOn SenderRoot.Input as InputDependencyToA ; +SenderRoot.SingleA canDependOn SenderRoot.Input as InputDependencyToSingleA ; diff --git a/ragconnect.tests/src/test/01-input/mapping/README.md b/ragconnect.tests/src/test/01-input/mapping/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4414f2eca557de36c2f3f0ff76f367d8fec9e20b --- /dev/null +++ b/ragconnect.tests/src/test/01-input/mapping/README.md @@ -0,0 +1,3 @@ +# Default Mapping + +Idea: Check different numbers of sequential mappings diff --git a/ragconnect.tests/src/test/01-input/mapping/Test.connect b/ragconnect.tests/src/test/01-input/mapping/Test.connect new file mode 100644 index 0000000000000000000000000000000000000000..10f7c839afa6742ed20484e92c1108c6b6d3c1ca --- /dev/null +++ b/ragconnect.tests/src/test/01-input/mapping/Test.connect @@ -0,0 +1,53 @@ +receive NativeTypes.IntValue using String2Int ; +receive NativeTypes.ShortValue using String2Int, Int2Short ; +receive NativeTypes.LongValue using String2Int, Int2Short, Short2Long ; +receive NativeTypes.FloatValue using String2Int, Int2Short, Short2Long, Long2Float ; +receive NativeTypes.DoubleValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double ; +receive NativeTypes.CharValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double, Double2Char ; +receive NativeTypes.BooleanValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double, Double2Char, Char2Boolean ; + +send NativeTypes.WriteIntValue using String2Int ; +send NativeTypes.WriteShortValue using String2Int, Int2Short ; +send NativeTypes.WriteLongValue using String2Int, Int2Short, Short2Long ; +send NativeTypes.WriteFloatValue using String2Int, Int2Short, Short2Long, Long2Float ; +send NativeTypes.WriteDoubleValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double ; +send NativeTypes.WriteCharValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double, Double2Char ; +send NativeTypes.WriteBooleanValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double, Double2Char, Char2Boolean ; + +receive BoxedTypes.IntValue using String2Int ; +receive BoxedTypes.ShortValue using String2Int, Int2Short ; +receive BoxedTypes.LongValue using String2Int, Int2Short, Short2Long ; +receive BoxedTypes.FloatValue using String2Int, Int2Short, Short2Long, Long2Float ; +receive BoxedTypes.DoubleValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double ; +receive BoxedTypes.CharValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double, Double2Char ; +receive BoxedTypes.BooleanValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double, Double2Char, Char2Boolean ; + +String2Int maps String s to int {: + return Integer.parseInt(s); +:} +Int2Short maps int i to short {: + return (short) i; +:} +Short2Long maps short s to long {: + return (long) s; +:} +Long2Float maps long l to float {: + return (float) (l + 0.01); +:} +Float2Double maps float f to double {: + return (double) f; +:} +Double2Char maps double d to char {: + return (char) ((int) d); +:} +Char2Boolean maps char c to boolean {: + return c > 21; +:} + +NativeTypes.WriteIntValue canDependOn NativeTypes.Driver as nativeIntDependency; +NativeTypes.WriteShortValue canDependOn NativeTypes.Driver as nativeShortDependency; +NativeTypes.WriteLongValue canDependOn NativeTypes.Driver as nativeLongDependency; +NativeTypes.WriteFloatValue canDependOn NativeTypes.Driver as nativeFloatDependency; +NativeTypes.WriteDoubleValue canDependOn NativeTypes.Driver as nativeDoubleDependency; +NativeTypes.WriteCharValue canDependOn NativeTypes.Driver as nativeCharDependency; +NativeTypes.WriteBooleanValue canDependOn NativeTypes.Driver as nativeBooleanDependency; diff --git a/ragconnect.tests/src/test/01-input/mapping/Test.jadd b/ragconnect.tests/src/test/01-input/mapping/Test.jadd new file mode 100644 index 0000000000000000000000000000000000000000..1ac351565b5f370453e431847f3c42237ec93861 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/mapping/Test.jadd @@ -0,0 +1,10 @@ +aspect Computation { + // native types, synthesized + syn String NativeTypes.getWriteIntValue() = getDriver(); + syn String NativeTypes.getWriteShortValue() = getDriver(); + syn String NativeTypes.getWriteLongValue() = getDriver(); + syn String NativeTypes.getWriteFloatValue() = getDriver(); + syn String NativeTypes.getWriteDoubleValue() = getDriver(); + syn String NativeTypes.getWriteCharValue() = getDriver(); + syn String NativeTypes.getWriteBooleanValue() = getDriver(); +} diff --git a/ragconnect.tests/src/test/01-input/mapping/Test.relast b/ragconnect.tests/src/test/01-input/mapping/Test.relast new file mode 100644 index 0000000000000000000000000000000000000000..718d16768e344af165efc4cfcd1653f9a92f6b02 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/mapping/Test.relast @@ -0,0 +1,3 @@ +A ::= NativeTypes BoxedTypes ; +NativeTypes ::= <IntValue:int> <ShortValue:short> <LongValue:long> <FloatValue:float> <DoubleValue:double> <CharValue:char> <BooleanValue:boolean> <Driver:String> /<WriteIntValue:String>/ /<WriteShortValue:String>/ /<WriteLongValue:String>/ /<WriteFloatValue:String>/ /<WriteDoubleValue:String>/ /<WriteCharValue:String>/ /<WriteBooleanValue:String>/; +BoxedTypes ::= <IntValue:Integer> <ShortValue:Short> <LongValue:Long> <FloatValue:Float> <DoubleValue:Double> <CharValue:Character> <BooleanValue:Boolean> ; diff --git a/ragconnect.tests/src/test/01-input/regression-tests/issue27/.gitignore b/ragconnect.tests/src/test/01-input/regression-tests/issue27/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..607b7610731b1ddf9dc05e605bd374a4b59ea360 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/regression-tests/issue27/.gitignore @@ -0,0 +1 @@ +/*.noNewLine.* diff --git a/ragconnect.tests/src/test/01-input/regression-tests/issue27/README.md b/ragconnect.tests/src/test/01-input/regression-tests/issue27/README.md new file mode 100644 index 0000000000000000000000000000000000000000..96595af1b32ba8487f2e8e663a944b0be52dff28 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/regression-tests/issue27/README.md @@ -0,0 +1,3 @@ +# Issue27 + +Regression test for failing parser when missing newline at end of specification. diff --git a/ragconnect.tests/src/test/01-input/regression-tests/issue27/Test.connect b/ragconnect.tests/src/test/01-input/regression-tests/issue27/Test.connect new file mode 100644 index 0000000000000000000000000000000000000000..28827bb62d7b801cf2f8bc605afc90c122f0cb6a --- /dev/null +++ b/ragconnect.tests/src/test/01-input/regression-tests/issue27/Test.connect @@ -0,0 +1 @@ +receive A.Name ; diff --git a/ragconnect.tests/src/test/01-input/regression-tests/issue27/Test.relast b/ragconnect.tests/src/test/01-input/regression-tests/issue27/Test.relast new file mode 100644 index 0000000000000000000000000000000000000000..4c479a5756a37e42c74459c22006912445baa9b3 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/regression-tests/issue27/Test.relast @@ -0,0 +1 @@ +A ::= <Name:String> ; diff --git a/ragconnect.tests/src/test/01-input/singleList/README.md b/ragconnect.tests/src/test/01-input/singleList/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6a76bb4d8457d1f246edaeb96d6d72048ef4ced1 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/singleList/README.md @@ -0,0 +1,47 @@ +# Single List + +Idea: send and receive single values for lists of subtrees. +Once without incremental evaluation (i.e., using manual dependencies), and the other time with incremental evaluation + +## Execution-Model + +``` +SenderRoot ReceiverRoot +|- A1 --( a/1 ) --\ | +|- A2 --( a/2 ) --+=\ /--> A* --------------| +|- A3 --( a/3 ) ----+==+--> WithAdd:A* ------| +|- A4 --( a/4 ) --+=/ | +|- IO --( a/5 ) --/ | + /--> UsingWc:A* ------| + ( a/# ) -+--> UsingWcWithA:A* -| +``` + +## Computation + +A _n_ = Input _n_ + 1, e.g., A1 = Input1 + 1 + +## Execution-Trace (SendInitialValue) + +| Input | [A1,A2,A3,A4,IO] | # | A* | UsingWcA | WithAddA | UsingWcWithAddA:A | +|---|---|---|---|---|---|---| +| * | [1,2,3,4,0] | 5 | [1,2,3,4,0] | [1,2,3,4,0] | [1,2,3,4,0] | [1,2,3,4,0] | +| I1:1 | [2,2,3,4,0] | 6 | [2,2,3,4,0] | [2,2,3,4,0] | [1,2,3,4,0,2] | [1,2,3,4,0,2] | +| I1:1 | [2,2,3,4,0] | 6 | [2,2,3,4,0] | [2,2,3,4,0] | [1,2,3,4,0,2] | [1,2,3,4,0,2] | +| I1:2 | [3,2,3,4,0] | 7 | [3,2,3,4,0] | [3,2,3,4,0] | [1,2,3,4,0,2,3] | [1,2,3,4,0,2,3] | +| IO:5 | [3,2,3,4,5] | 8 | [3,2,3,4,5] | [3,2,3,4,5] | [1,2,3,4,0,2,3,5] | [1,2,3,4,0,2,3,5] +| I3:4 | [3,2,7,4,5] | 9 | [3,2,7,4,5] | [3,2,7,4,5] | [1,2,3,4,0,2,3,5,7] | [1,2,3,4,0,2,3,5,7] | + +*: (1:0, 2:0, 3:0, 4:0, 5:0) + +## Execution-Trace (OnlyUpdate) + +| Input | [A1,A2,A3,A4,IO] | # | A* | UsingWcA | WithAddA | UsingWcWithAddA:A | +|---|---|---|---|---|---|---| +| * | [-,-,-,-,-] | 0 | [0,0,0,0,0] | [] | [] | [] | +| I1:1 | [2,-,-,-,-] | 1 | [2,0,0,0,0] | [2] | [2] | [2] | +| I1:1 | [2,-,-,-,-] | 1 | [2,0,0,0,0] | [2] | [2] | [2] | +| I1:2 | [3,-,-,-,-] | 2 | [3,0,0,0,0] | [3] | [2,3] | [2,3] | +| IO:5 | [2,-,-,-,5] | 3 | [3,0,0,0,5] | [3,5] | [2,3,5] | [2,3,5] +| I3:4 | [2,-,7,-,5] | 4 | [3,0,7,0,5] | [3,5,7] | [2,3,5,7] | [2,3,5,7] | + +*: (1:0, 2:0, 3:0, 4:0, 5:0) diff --git a/ragconnect.tests/src/test/01-input/singleList/Test.connect b/ragconnect.tests/src/test/01-input/singleList/Test.connect new file mode 100644 index 0000000000000000000000000000000000000000..21b2796544ae65e96da5b03f088e5f7966620a7f --- /dev/null +++ b/ragconnect.tests/src/test/01-input/singleList/Test.connect @@ -0,0 +1,14 @@ +send tree SenderRoot.A1 ; +send tree SenderRoot.A2 ; +send tree SenderRoot.A3 ; +send tree SenderRoot.A4 ; +send SenderRoot.InOutput using IntToA ; + +receive tree ReceiverRoot.A ; +receive tree ReceiverRoot.UsingWildcardA ; +receive tree with add ReceiverRoot.WithAddA ; +receive tree with add ReceiverRoot.UsingWildcardWithAddA ; + +IntToA maps int i to A {: + return new A().setID(i); +:} diff --git a/ragconnect.tests/src/test/01-input/singleList/Test.jadd b/ragconnect.tests/src/test/01-input/singleList/Test.jadd new file mode 100644 index 0000000000000000000000000000000000000000..005b419bb1f625a460e219079dcd3a5361b5844d --- /dev/null +++ b/ragconnect.tests/src/test/01-input/singleList/Test.jadd @@ -0,0 +1,25 @@ +aspect Computation { + syn A SenderRoot.getA1() = new A().setID(getInput1() + 1); + syn A SenderRoot.getA2() = new A().setID(getInput2() + 2); + syn A SenderRoot.getA3() = new A().setID(getInput3() + 3); + syn A SenderRoot.getA4() = new A().setID(getInput4() + 4); + + syn boolean ASTNode.isNameable() = false; + eq Nameable.isNameable() = true; +} + +aspect Testing { + class SenderRoot implements org.jastadd.ragconnect.tests.singleList.AbstractSingleListTest.TestWrapperSenderRoot {} + class ReceiverRoot implements org.jastadd.ragconnect.tests.singleList.AbstractSingleListTest.TestWrapperReceiverRoot {} + class A implements org.jastadd.ragconnect.tests.singleList.AbstractSingleListTest.TestWrapperA {} + class JastAddList<T> implements org.jastadd.ragconnect.tests.singleList.AbstractSingleListTest.TestWrapperJastAddList<T> {} +} + +aspect NameResolution { + // overriding customID guarantees to produce the same JSON representation for equal lists + // otherwise, the value for id is different each time + @Override + protected String Nameable.customID() { + return getClass().getSimpleName() + getID(); + } +} diff --git a/ragconnect.tests/src/test/01-input/singleList/Test.relast b/ragconnect.tests/src/test/01-input/singleList/Test.relast new file mode 100644 index 0000000000000000000000000000000000000000..0d97bb6c6b2fa4be6a4628b5e7e45926817364d0 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/singleList/Test.relast @@ -0,0 +1,11 @@ +Root ::= SenderRoot* ReceiverRoot* ; + +Nameable ::= <ID:int> ; +SenderRoot : Nameable ::= <Input1:int> /A1:A/ + <Input2:int> /A2:A/ + <Input3:int> /A3:A/ + <Input4:int> /A4:A/ + <InOutput:int> ; + +ReceiverRoot : Nameable ::= A* UsingWildcardA:A* WithAddA:A* UsingWildcardWithAddA:A* ; +A : Nameable ; diff --git a/ragconnect.tests/src/test/01-input/singleList/TestDependencies.connect b/ragconnect.tests/src/test/01-input/singleList/TestDependencies.connect new file mode 100644 index 0000000000000000000000000000000000000000..f1429b6aa1cee28ca3d2a1421060af74a28fbcf8 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/singleList/TestDependencies.connect @@ -0,0 +1,4 @@ +SenderRoot.A1 canDependOn SenderRoot.Input1 as InputDependencyToA1 ; +SenderRoot.A2 canDependOn SenderRoot.Input2 as InputDependencyToA2 ; +SenderRoot.A3 canDependOn SenderRoot.Input3 as InputDependencyToA3 ; +SenderRoot.A4 canDependOn SenderRoot.Input4 as InputDependencyToA4 ; diff --git a/ragconnect.tests/src/test/01-input/singleListVariant/README.md b/ragconnect.tests/src/test/01-input/singleListVariant/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4004ac42c136c25f2447efe5c1161c98754f32c1 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/singleListVariant/README.md @@ -0,0 +1,61 @@ +# Single List + +Idea: send and receive single values for lists of subtrees. +Test different variants of the structure/shape of the send/received value. + +## Execution-Model + +TODO: check again (old model copied from `singleList`) + +``` +SenderRoot/ReceiverRoot + |- T_Empty ::= /* empty */ ; + |- T_Token ::= <Value:String> ; + |- T_OneChild ::= Other ; + |- T_OneOpt ::= [Other] ; + |- T_OneList ::= Other* ; + |- T_TwoChildren ::= Left:Other Right:Other ; + |- T_OneOfEach ::= First:Other [Second:Other] Third:Other* <Fourth:String> ; + |- abstract T_Abstract ::= <ValueAbstract> ; +``` + +## Computation + +``` +T.ID = Input +T.token = Input +T.Other.ID = Input + 1 +``` + +## Execution-Trace (SendInitialValue) + +Inputs: + +- 1 +- 1 +- 2 +- 3 + +| Input | [A1,A2,A3,A4,IO] | # | A* | UsingWcA | WithAddA | UsingWcWithAddA:A | +|---|---|---|---|---|---|---| +| * | [1,2,3,4,0] | 5 | [1,2,3,4,0] | [1,2,3,4,0] | [1,2,3,4,0] | [1,2,3,4,0] | +| I1:1 | [2,2,3,4,0] | 6 | [2,2,3,4,0] | [2,2,3,4,0] | [1,2,3,4,0,2] | [1,2,3,4,0,2] | +| I1:1 | [2,2,3,4,0] | 6 | [2,2,3,4,0] | [2,2,3,4,0] | [1,2,3,4,0,2] | [1,2,3,4,0,2] | +| I1:2 | [3,2,3,4,0] | 7 | [3,2,3,4,0] | [3,2,3,4,0] | [1,2,3,4,0,2,3] | [1,2,3,4,0,2,3] | +| IO:5 | [3,2,3,4,5] | 8 | [3,2,3,4,5] | [3,2,3,4,5] | [1,2,3,4,0,2,3,5] | [1,2,3,4,0,2,3,5] +| I3:4 | [3,2,7,4,5] | 9 | [3,2,7,4,5] | [3,2,7,4,5] | [1,2,3,4,0,2,3,5,7] | [1,2,3,4,0,2,3,5,7] | + +*: (1:0, 2:0, 3:0, 4:0, 5:0) + +## Execution-Trace (OnlyUpdate) + +| Input | [A1,A2,A3,A4,IO] | # | A* | UsingWcA | WithAddA | UsingWcWithAddA:A | +|---|---|---|---|---|---|---| +| * | [-,-,-,-,-] | 0 | [0,0,0,0,0] | [] | [] | [] | +| I1:1 | [2,-,-,-,-] | 1 | [2,0,0,0,0] | [2] | [2] | [2] | +| I1:1 | [2,-,-,-,-] | 1 | [2,0,0,0,0] | [2] | [2] | [2] | +| I1:2 | [3,-,-,-,-] | 2 | [3,0,0,0,0] | [3] | [2,3] | [2,3] | +| IO:5 | [2,-,-,-,5] | 3 | [3,0,0,0,5] | [3,5] | [2,3,5] | [2,3,5] +| I3:4 | [2,-,7,-,5] | 4 | [3,0,7,0,5] | [3,5,7] | [2,3,5,7] | [2,3,5,7] | + +*: (1:0, 2:0, 3:0, 4:0, 5:0) diff --git a/ragconnect.tests/src/test/01-input/singleListVariant/Test.connect b/ragconnect.tests/src/test/01-input/singleListVariant/Test.connect new file mode 100644 index 0000000000000000000000000000000000000000..23365109901425e8a6060c3ef3c2d8fe4cfc7656 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/singleListVariant/Test.connect @@ -0,0 +1,28 @@ +send tree SenderRoot.T_Empty ; +send tree SenderRoot.T_Token ; +send tree SenderRoot.T_OneChild ; +send tree SenderRoot.T_OneOpt ; +send tree SenderRoot.T_OneList ; +send tree SenderRoot.T_TwoChildren ; +send tree SenderRoot.T_OneOfEach ; +send tree SenderRoot.T_Abstract ; + +receive tree ReceiverRoot.T_Empty ; +receive tree ReceiverRoot.T_Token ; +receive tree ReceiverRoot.T_OneChild ; +receive tree ReceiverRoot.T_OneOpt ; +receive tree ReceiverRoot.T_OneList ; +receive tree ReceiverRoot.T_TwoChildren ; +receive tree ReceiverRoot.T_OneOfEach ; +receive tree ReceiverRoot.T_Abstract ; + +receive tree ReceiverRoot.MyEmpty ; + +receive tree with add ReceiverRoot.EmptyWithAdd ; +receive tree with add ReceiverRoot.TokenWithAdd ; +receive tree with add ReceiverRoot.OneChildWithAdd ; +receive tree with add ReceiverRoot.OneOptWithAdd ; +receive tree with add ReceiverRoot.OneListWithAdd ; +receive tree with add ReceiverRoot.TwoChildrenWithAdd ; +receive tree with add ReceiverRoot.OneOfEachWithAdd ; +receive tree with add ReceiverRoot.AbstractWithAdd ; diff --git a/ragconnect.tests/src/test/01-input/singleListVariant/Test.jadd b/ragconnect.tests/src/test/01-input/singleListVariant/Test.jadd new file mode 100644 index 0000000000000000000000000000000000000000..10ed1174a4cbb99e46cd5ba582df829d547bd446 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/singleListVariant/Test.jadd @@ -0,0 +1,75 @@ +aspect Computation { + syn T_Empty SenderRoot.getT_Empty() = new T_Empty().setID(getInput()); + syn T_Token SenderRoot.getT_Token() = new T_Token().setID(getInput()) + .setValue(Integer.toString(getInput())); + syn T_OneChild SenderRoot.getT_OneChild() { + T_OneChild result = new T_OneChild().setID(getInput()); + result.setOther(createOther()); + return result; + } + syn T_OneOpt SenderRoot.getT_OneOpt() { + T_OneOpt result = new T_OneOpt().setID(getInput()); + if (getShouldSetOptAndList()) { + result.setOther(createOther()); + } + return result; + } + syn T_OneList SenderRoot.getT_OneList() { + T_OneList result = new T_OneList().setID(getInput()); + if (getShouldSetOptAndList()) { + result.addOther(createOther()); + } + return result; + } + syn T_TwoChildren SenderRoot.getT_TwoChildren() { + T_TwoChildren result = new T_TwoChildren().setID(getInput()); + result.setLeft(createOther()); + result.setRight(createOther()); + return result; + } + syn T_OneOfEach SenderRoot.getT_OneOfEach() { + T_OneOfEach result = new T_OneOfEach().setID(getInput()); + result.setFirst(createOther()); + if (getShouldSetOptAndList()) { + result.setSecond(createOther()); + result.addThird(createOther()); + } + result.setFourth(Integer.toString(getInput())); + return result; + } + syn T_Abstract SenderRoot.getT_Abstract() = new T_SubClass() + .setValueSub(Integer.toString(getInput())) + .setID(getInput()) + .setValueAbstract(Integer.toString(getInput())); + + private Other SenderRoot.createOther() { + return new Other().setID(getInput() + 1); + } + + syn boolean ASTNode.isNameable() = false; + eq Nameable.isNameable() = true; +} + +aspect Testing { + class SenderRoot implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperSenderRoot {} + class ReceiverRoot implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperReceiverRoot {} + class Other implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperOther {} + class T_Empty implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperT_Empty {} + class T_Token implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperT_Token {} + class T_OneChild implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperT_OneChild {} + class T_OneOpt implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperT_OneOpt {} + class T_OneList implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperT_OneList {} + class T_TwoChildren implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperT_TwoChildren {} + class T_OneOfEach implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperT_OneOfEach {} + class T_Abstract implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperT_Abstract {} + class JastAddList<T> implements org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.TestWrapperJastAddList<T> {} +} + +aspect NameResolution { + // overriding customID guarantees to produce the same JSON representation for equal lists + // otherwise, the value for id is different each time + @Override + protected String Nameable.customID() { + return getClass().getSimpleName() + getID(); + } +} diff --git a/ragconnect.tests/src/test/01-input/singleListVariant/Test.relast b/ragconnect.tests/src/test/01-input/singleListVariant/Test.relast new file mode 100644 index 0000000000000000000000000000000000000000..4ac99ae124e19d182d9ad23b4c4ae65980272013 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/singleListVariant/Test.relast @@ -0,0 +1,44 @@ +Root ::= SenderRoot* ReceiverRoot* ; + +Nameable ::= <ID:int> ; +SenderRoot : Nameable ::= <Input:int> <ShouldSetOptAndList:boolean> +/T_Empty/ +/T_Token/ +/T_OneChild/ +/T_OneOpt/ +/T_OneList/ +/T_TwoChildren/ +/T_OneOfEach/ +/T_Abstract/ +; + +ReceiverRoot : Nameable ::= +T_Empty* +T_Token* +T_OneChild* +T_OneOpt* +T_OneList* +T_TwoChildren* +T_OneOfEach* +T_Abstract* +MyEmpty:T_Empty* +EmptyWithAdd:T_Empty* +TokenWithAdd:T_Token* +OneChildWithAdd:T_OneChild* +OneOptWithAdd:T_OneOpt* +OneListWithAdd:T_OneList* +TwoChildrenWithAdd:T_TwoChildren* +OneOfEachWithAdd:T_OneOfEach* +AbstractWithAdd:T_Abstract* +; + +T_Empty : Nameable ::= /* empty */ ; +T_Token : Nameable ::= <Value:String> ; +T_OneChild : Nameable ::= Other ; +T_OneOpt : Nameable ::= [Other] ; +T_OneList : Nameable ::= Other* ; +T_TwoChildren : Nameable ::= Left:Other Right:Other ; +T_OneOfEach : Nameable ::= First:Other [Second:Other] Third:Other* <Fourth:String> ; +abstract T_Abstract : Nameable ::= <ValueAbstract>; +T_SubClass : T_Abstract ::= <ValueSub> ; +Other : Nameable ; diff --git a/ragconnect.tests/src/test/01-input/singleListVariant/TestDependencies.connect b/ragconnect.tests/src/test/01-input/singleListVariant/TestDependencies.connect new file mode 100644 index 0000000000000000000000000000000000000000..2ac87895268cfa631fcfee65a8c2c90f4c13b3c7 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/singleListVariant/TestDependencies.connect @@ -0,0 +1,16 @@ +SenderRoot.T_Empty canDependOn SenderRoot.Input as InputDependencyToT_Empty ; +SenderRoot.T_Token canDependOn SenderRoot.Input as InputDependencyToT_Token ; +SenderRoot.T_OneChild canDependOn SenderRoot.Input as InputDependencyToT_OneChild ; + +SenderRoot.T_OneOpt canDependOn SenderRoot.ShouldSetOptAndList as ShouldSetOptAndListDependencyToT_OneOpt ; +SenderRoot.T_OneOpt canDependOn SenderRoot.Input as InputDependencyToT_OneOpt ; + +SenderRoot.T_OneList canDependOn SenderRoot.ShouldSetOptAndList as ShouldSetOptAndListDependencyToT_OneList ; +SenderRoot.T_OneList canDependOn SenderRoot.Input as InputDependencyToT_OneList ; + +SenderRoot.T_TwoChildren canDependOn SenderRoot.Input as InputDependencyToT_TwoChildren ; + +SenderRoot.T_OneOfEach canDependOn SenderRoot.ShouldSetOptAndList as ShouldSetOptAndListDependencyToT_OneOfEach ; +SenderRoot.T_OneOfEach canDependOn SenderRoot.Input as InputDependencyToT_OneOfEach ; + +SenderRoot.T_Abstract canDependOn SenderRoot.Input as InputDependencyToT_Abstract ; diff --git a/ragconnect.tests/src/test/01-input/tree/README.md b/ragconnect.tests/src/test/01-input/tree/README.md new file mode 100644 index 0000000000000000000000000000000000000000..060a0e39663f01d33bffb960c316c7de8da59f68 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/tree/README.md @@ -0,0 +1,4 @@ +# Tree + +Idea: send and receive subtrees, test different relations within the subtree which was sent. +Once without incremental evaluation (i.e., using manual dependencies), and the other time with incremental evaluation diff --git a/ragconnect.tests/src/test/01-input/tree/Test.connect b/ragconnect.tests/src/test/01-input/tree/Test.connect new file mode 100644 index 0000000000000000000000000000000000000000..857f6297f12249451b026051201ebfc004c2f6a6 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/tree/Test.connect @@ -0,0 +1,2 @@ +send tree SenderRoot.Alfa ; +receive tree ReceiverRoot.Alfa ; diff --git a/ragconnect.tests/src/test/01-input/tree/Test.jadd b/ragconnect.tests/src/test/01-input/tree/Test.jadd new file mode 100644 index 0000000000000000000000000000000000000000..475e20f88bbe93daadb59f153b49f4e8d5ac99b4 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/tree/Test.jadd @@ -0,0 +1,81 @@ +aspect Computation { + syn Alfa SenderRoot.getAlfa() { + Alfa result = new Alfa(); + for (int i = 0; i < 4; i++) { + result.addBravo(new Bravo().setID(i)); + result.addCharlie(new Charlie().setID(i)); + result.addDelta(new Delta().setID(i)); + } + Bravo inputBravo = result.getBravo(getInput()); + Charlie inputCharlie = result.getCharlie(getInput()); + Delta inputDelta = result.getDelta(getInput()); + + // rel Alfa.MyBravo -> Bravo ; + result.setMyBravo(inputBravo); + // rel Alfa.OptionalBravo? -> Bravo ; + result.setOptionalBravo(inputBravo); + // rel Alfa.MultiBravo* -> Bravo ; + result.addMultiBravo(inputBravo); + result.addMultiBravo(result.getBravo(getInput() + 1)); + + // rel Charlie.MyAlfa? -> Alfa ; + inputCharlie.setMyAlfa(result); + + // rel Alfa.SingleBi1Delta <-> Delta.SingleBack1Alfa? ; + result.setSingleBi1Delta(inputDelta); + // rel Alfa.MultiBi2Delta* <-> Delta.SingleBack2Alfa? ; + result.addMultiBi2Delta(inputDelta); + result.addMultiBi2Delta(result.getDelta(getInput() + 1)); + // rel Alfa.MultiBi3Delta* <-> Delta.MultiBack3Alfa* ; + result.addMultiBi3Delta(inputDelta); + result.addMultiBi3Delta(result.getDelta(getInput() + 1)); + + // rel Alfa.Myself -> Alfa ; + result.setMyself(result); + + // rel Bravo.MyCharlie -> Charlie ; + for (int i = 0; i < 4; i++) { + if (i == getInput()) { + result.getBravo(i).setMyCharlie(inputCharlie); + } else { + result.getBravo(i).setMyCharlie(result.getCharlie(0)); + } + } + // rel Bravo.OptionalCharlie? -> Charlie ; + inputBravo.setOptionalCharlie(inputCharlie); + // rel Bravo.MultiCharlie* -> Charlie ; + inputBravo.addMultiCharlie(inputCharlie); + inputBravo.addMultiCharlie(result.getCharlie(getInput() + 1)); + + // rel Bravo.SingleBi1Delta? <-> Delta.SingleBack1Bravo? ; + inputBravo.setSingleBi1Delta(inputDelta); + // rel Bravo.MultiBi2Delta* <-> Delta.SingleBack2Bravo? ; + inputBravo.addMultiBi2Delta(inputDelta); + inputBravo.addMultiBi2Delta(result.getDelta(getInput() + 1)); + // rel Bravo.MultiBi3Delta* <-> Delta.MultiBack3Bravo* ; + inputBravo.addMultiBi3Delta(inputDelta); + inputBravo.addMultiBi3Delta(result.getDelta(getInput() + 1)); + result.getBravo(getInput() + 1).addMultiBi3Delta(inputDelta); + result.getBravo(getInput() + 1).addMultiBi3Delta(result.getDelta(getInput() + 1)); + + return result; + } + + syn boolean ASTNode.isNameable() = false; + eq Nameable.isNameable() = true; +} + +aspect Testing { + class ReceiverRoot implements org.jastadd.ragconnect.tests.tree.AbstractTreeTest.TestWrapperReceiverRoot {} + class Alfa implements org.jastadd.ragconnect.tests.tree.AbstractTreeTest.TestWrapperAlfa {} + class Bravo implements org.jastadd.ragconnect.tests.tree.AbstractTreeTest.TestWrapperBravo {} + class Charlie implements org.jastadd.ragconnect.tests.tree.AbstractTreeTest.TestWrapperCharlie {} + class Delta implements org.jastadd.ragconnect.tests.tree.AbstractTreeTest.TestWrapperDelta {} +} + +aspect NameResolution { + @Override + protected String Nameable.customID() { + return getClass().getSimpleName() + getID(); + } +} diff --git a/ragconnect.tests/src/test/01-input/tree/Test.relast b/ragconnect.tests/src/test/01-input/tree/Test.relast new file mode 100644 index 0000000000000000000000000000000000000000..f65444660383fa1f44d3b4519f01d16098164bf3 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/tree/Test.relast @@ -0,0 +1,36 @@ +Root ::= SenderRoot* ReceiverRoot* ; + +Nameable ::= <ID:int> ; +SenderRoot : Nameable ::= <Input:int> /Alfa/ ; + +ReceiverRoot : Nameable ::= Alfa ; +Alfa : Nameable ::= <Value:String> Bravo* Charlie* Delta* ; + +Bravo : Nameable ; +Charlie : Nameable ; +Delta : Nameable ; + +rel Alfa.Myself -> Alfa ; + +// Alfa -> Bravo +rel Alfa.MyBravo -> Bravo ; +rel Alfa.OptionalBravo? -> Bravo ; +rel Alfa.MultiBravo* -> Bravo ; + +// Charlie -> Alfa +rel Charlie.MyAlfa? -> Alfa ; + +// Alfa <-> Delta +rel Alfa.SingleBi1Delta <-> Delta.SingleBack1Alfa? ; +rel Alfa.MultiBi2Delta* <-> Delta.SingleBack2Alfa? ; +rel Alfa.MultiBi3Delta* <-> Delta.MultiBack3Alfa* ; + +// Bravo -> Charlie +rel Bravo.MyCharlie -> Charlie ; +rel Bravo.OptionalCharlie? -> Charlie ; +rel Bravo.MultiCharlie* -> Charlie ; + +// Bravo <-> Delta +rel Bravo.SingleBi1Delta? <-> Delta.SingleBack1Bravo? ; +rel Bravo.MultiBi2Delta* <-> Delta.SingleBack2Bravo? ; +rel Bravo.MultiBi3Delta* <-> Delta.MultiBack3Bravo* ; diff --git a/ragconnect.tests/src/test/01-input/tree/TestDependencies.connect b/ragconnect.tests/src/test/01-input/tree/TestDependencies.connect new file mode 100644 index 0000000000000000000000000000000000000000..aa5df696a54404332115d0069279a07f886f2e22 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/tree/TestDependencies.connect @@ -0,0 +1 @@ +SenderRoot.Alfa canDependOn SenderRoot.Input as InputDependency ; diff --git a/ragconnect.tests/src/test/01-input/treeAllowedTokens/README.md b/ragconnect.tests/src/test/01-input/treeAllowedTokens/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c215115106a71daf358568368426b33421cfdd65 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/treeAllowedTokens/README.md @@ -0,0 +1,4 @@ +# Tree Allowed Tokens + +Idea: send and receive subtrees, test different token types within the subtree which was sent. +Once without incremental evaluation (i.e., using manual dependencies), and the other time with incremental evaluation diff --git a/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.connect b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.connect new file mode 100644 index 0000000000000000000000000000000000000000..d872ce56c59097e678e6cff0b25ec422130b6129 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.connect @@ -0,0 +1,26 @@ +send tree SenderRoot.Alfa ; +receive tree ReceiverRoot.Alfa ; + +receive SenderRoot.Input1WhenFlagIsTrue ; +receive SenderRoot.Input1WhenFlagIsFalse ; +receive SenderRoot.Input2 ; +receive SenderRoot.Input3 ; + +send tree SenderRoot.AlfaPrimitive using Alfa2String ; +receive tree ReceiverRoot.AlfaPrimitive using String2Alfa ; + +Alfa2String maps Alfa alfa to String {: + StringBuilder sb = new StringBuilder(); + sb.append(alfa.getID()).append(":").append(alfa.getStringValue()).append(";"); + return sb.toString(); +:} + +String2Alfa maps String s to Alfa {: + int colonIndex = s.indexOf(":"); + int id = Integer.parseInt(s.substring(0, colonIndex)); + String value = s.substring(colonIndex + 1, s.length() - 1); + Alfa result = new Alfa(); + result.setID(id); + result.setStringValue(value); + return result; +:} diff --git a/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.jadd b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.jadd new file mode 100644 index 0000000000000000000000000000000000000000..5ac99ca749c7f962416f4c34bb8544a2fdd2e38f --- /dev/null +++ b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.jadd @@ -0,0 +1,48 @@ +aspect Computation { + syn Alfa SenderRoot.getAlfa() { + Alfa result = new Alfa(); + + // Inputs: <Flag:boolean> <Input1WhenFlagIsTrue:int> <Input1WhenFlagIsFalse:int> <Input2:String> <Input3:double> + + result.setBooleanValue(getFlag()); // boolean + result.setIntValue(getFlag() ? getInput1WhenFlagIsTrue() : getInput1WhenFlagIsFalse()); // int + result.setShortValue((short) (getFlag() ? getInput1WhenFlagIsTrue() : getInput1WhenFlagIsFalse())); // short + result.setLongValue(getFlag() ? getInput1WhenFlagIsTrue() : getInput1WhenFlagIsFalse()); // long + result.setFloatValue((float) getInput3()); // float + result.setDoubleValue(getInput3()); // double + result.setCharValue(getInput2().charAt(0)); // char + result.setStringValue(getInput2()); // String + // needed format: 2011-12-03T10:15:30Z + result.setInstantValue(java.time.Instant.parse(getInput2())); // java.time.Instant + // setting days + result.setPeriodValue(java.time.Period.of(0, 0, getFlag() ? getInput1WhenFlagIsTrue() : getInput1WhenFlagIsFalse())); // java.time.Period + result.setEnumValue(getFlag() ? MyEnum.TRUE : MyEnum.FALSE); // MyEnum + + return result; + } + + syn Alfa SenderRoot.getAlfaPrimitive() { + Alfa result = new Alfa(); + result.setStringValue(getInput2()); + return result; + } + + syn boolean ASTNode.isNameable() = false; + eq Nameable.isNameable() = true; +} + +aspect Enum { + public enum MyEnum { FALSE, TRUE; } +} +aspect Testing { + class ReceiverRoot implements org.jastadd.ragconnect.tests.treeAllowedTokens.AbstractTreeAllowedTokensTest.TestWrapperReceiverRoot {} + class Alfa implements org.jastadd.ragconnect.tests.treeAllowedTokens.AbstractTreeAllowedTokensTest.TestWrapperAlfa {} +} + +aspect NameResolution { + // this should most likely be generated + @Override + protected String Alfa.customID() { + return "0"; + } +} diff --git a/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.relast b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.relast new file mode 100644 index 0000000000000000000000000000000000000000..591ebb22441a29b5a8561b0ea539367a44d9d2e8 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.relast @@ -0,0 +1,8 @@ +Root ::= SenderRoot* ReceiverRoot* ; + +Nameable ::= <ID:int> ; +SenderRoot : Nameable ::= <Flag:boolean> <Input1WhenFlagIsTrue:int> <Input1WhenFlagIsFalse:int> <Input2:String> <Input3:double> /Alfa/ /AlfaPrimitive:Alfa/ ; + +ReceiverRoot : Nameable ::= Alfa AlfaPrimitive:Alfa ; + +Alfa : Nameable ::= <BooleanValue:boolean> <IntValue:int> <ShortValue:short> <LongValue:long> <FloatValue:float> <DoubleValue:double> <CharValue:char> <StringValue:String> <InstantValue:java.time.Instant> <PeriodValue:java.time.Period> <EnumValue:MyEnum> ; diff --git a/ragconnect.tests/src/test/01-input/treeAllowedTokens/TestDependencies.connect b/ragconnect.tests/src/test/01-input/treeAllowedTokens/TestDependencies.connect new file mode 100644 index 0000000000000000000000000000000000000000..afa4a832ca4bde7ca584c8b1d8f0fa69ee0955a8 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/treeAllowedTokens/TestDependencies.connect @@ -0,0 +1,7 @@ +SenderRoot.Alfa canDependOn SenderRoot.Flag as FlagDependency ; +SenderRoot.Alfa canDependOn SenderRoot.Input1WhenFlagIsTrue as Input1WhenFlagIsTrueDependency ; +SenderRoot.Alfa canDependOn SenderRoot.Input1WhenFlagIsFalse as Input1WhenFlagIsFalseDependency ; +SenderRoot.Alfa canDependOn SenderRoot.Input2 as Input2Dependency ; +SenderRoot.Alfa canDependOn SenderRoot.Input3 as Input3Dependency ; + +SenderRoot.AlfaPrimitive canDependOn SenderRoot.Input2 as PrimitiveInput2Dependency ; diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java index 36baf2cf5ffda1fbb6225daf8f054a40c3a2c331..330c950b8114cb1364ea30a86cd76c7dc81eb4a2 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java @@ -1,10 +1,7 @@ package org.jastadd.ragconnect.tests; import defaultOnlyRead.ast.MqttHandler; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -17,21 +14,23 @@ import java.util.concurrent.TimeUnit; @Tag("mqtt") public abstract class AbstractMqttTest { - static boolean checkDone = false; - static Boolean checkResult; + private static boolean checkDone = false; + protected static MqttHandler publisher; @BeforeAll - public static void checkMqttConnection() { + public static void createPublishAndOnceCheckMqttConnection() { + boolean checkResult; + try { + publisher = new MqttHandler("Publisher") + .dontSendWelcomeMessage() + .setHost(TestUtils.getMqttHost()); + checkResult = true; + } catch (IOException e) { + checkResult = false; + } if (!checkDone) { checkDone = true; - try { - checkResult = new MqttHandler() - .dontSendWelcomeMessage() - .setHost(TestUtils.getMqttHost()) - .waitUntilReady(2, TimeUnit.SECONDS); - } catch (IOException e) { - checkResult = false; - } + checkResult &= publisher.waitUntilReady(2, TimeUnit.SECONDS); } if (!checkResult) { throw new IllegalStateException("Mqtt Broker not ready!"); @@ -52,6 +51,10 @@ public abstract class AbstractMqttTest { communicateSendInitialValue(); } + /** + * Actual test code for communication when sending initial value. + * @throws InterruptedException because of TestUtils.waitForMqtt() + */ protected abstract void communicateSendInitialValue() throws InterruptedException; @Tag("mqtt") @@ -63,9 +66,15 @@ public abstract class AbstractMqttTest { communicateOnlyUpdatedValue(); } + /** + * Actual test code for communication without sending any value upon connecting. + * @throws InterruptedException because of TestUtils.waitForMqtt() + */ protected abstract void communicateOnlyUpdatedValue() throws InterruptedException; - + /** + * Create the model, and set required default values. + */ protected abstract void createModel(); /** @@ -79,9 +88,9 @@ public abstract class AbstractMqttTest { * * And then add dependencies, initialise receiver, add connections to those receivers, * and finally call generated connect* methods on model elements. - * @param writeCurrentValue + * @param writeCurrentValue if the initial/current value shall be sent upon connecting */ - protected abstract void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException; + protected abstract void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException, InterruptedException; @AfterEach public void alwaysCloseConnections() { @@ -101,4 +110,11 @@ public abstract class AbstractMqttTest { */ protected abstract void closeConnections(); + @AfterAll + public static void closePublisher() { + if (publisher != null) { + publisher.close(); + } + } + } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java index 65302cfa47f0cd83788385ab0d644d2627868a9c..d9cf9d92a456a02cd2acba9e5f8990527d5842e9 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java @@ -2,14 +2,15 @@ package org.jastadd.ragconnect.tests; import defaultOnlyRead.ast.A; import defaultOnlyRead.ast.BoxedTypes; -import defaultOnlyRead.ast.MqttHandler; import defaultOnlyRead.ast.NativeTypes; +import org.junit.jupiter.api.Test; import java.io.IOException; -import java.nio.ByteBuffer; +import java.nio.file.Paths; import java.util.concurrent.TimeUnit; import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -20,7 +21,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; */ public class DefaultOnlyReadTest extends AbstractMqttTest { - private static final String TOPIC_NATIVE_INT = "native/int"; + private static final String TOPIC_NATIVE_INT = "native/boolean"; + private static final String TOPIC_NATIVE_BOOLEAN = "native/int"; private static final String TOPIC_NATIVE_SHORT = "native/short"; private static final String TOPIC_NATIVE_LONG = "native/long"; private static final String TOPIC_NATIVE_FLOAT = "native/float"; @@ -28,6 +30,7 @@ public class DefaultOnlyReadTest extends AbstractMqttTest { private static final String TOPIC_NATIVE_CHAR = "native/char"; private static final String TOPIC_NATIVE_STRING = "native/string"; + private static final String TOPIC_BOXED_BOOLEAN = "boxed/boolean"; private static final String TOPIC_BOXED_INTEGER = "boxed/Integer"; private static final String TOPIC_BOXED_SHORT = "boxed/Short"; private static final String TOPIC_BOXED_LONG = "boxed/Long"; @@ -40,42 +43,74 @@ public class DefaultOnlyReadTest extends AbstractMqttTest { private NativeTypes floats; private NativeTypes chars; private BoxedTypes allBoxed; - private MqttHandler sender; + + @Test + public void checkNotJacksonReference() { + testJaddContainReferenceToJackson( + Paths.get("src", "test", + "02-after-ragconnect", "defaultOnlyRead", "RagConnect.jadd"), false); + } @Override - public void closeConnections() { - if (sender != null) { - sender.close(); - } - if (model != null) { - model.ragconnectCloseConnections(); - } + protected void createModel() { + model = new A(); + integers = new NativeTypes(); + model.addNativeTypes(integers); + floats = new NativeTypes(); + model.addNativeTypes(floats); + chars = new NativeTypes(); + model.addNativeTypes(chars); + allBoxed = new BoxedTypes(); + model.addBoxedTypes(allBoxed); } @Override protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); - integers.connectIntValue(mqttUri(TOPIC_NATIVE_INT)); - integers.connectShortValue(mqttUri(TOPIC_NATIVE_SHORT)); - integers.connectLongValue(mqttUri(TOPIC_NATIVE_LONG)); - floats.connectFloatValue(mqttUri(TOPIC_NATIVE_FLOAT)); - floats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE)); - chars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR)); - chars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING)); - allBoxed.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER)); - allBoxed.connectShortValue(mqttUri(TOPIC_BOXED_SHORT)); - allBoxed.connectLongValue(mqttUri(TOPIC_BOXED_LONG)); - allBoxed.connectFloatValue(mqttUri(TOPIC_BOXED_FLOAT)); - allBoxed.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE)); - allBoxed.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER)); - - sender = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); - assertTrue(sender.waitUntilReady(2, TimeUnit.SECONDS)); + assertTrue(integers.connectBooleanValue(mqttUri(TOPIC_NATIVE_BOOLEAN))); + assertTrue(integers.connectIntValue(mqttUri(TOPIC_NATIVE_INT))); + assertTrue(integers.connectShortValue(mqttUri(TOPIC_NATIVE_SHORT))); + assertTrue(integers.connectLongValue(mqttUri(TOPIC_NATIVE_LONG))); + assertTrue(floats.connectFloatValue(mqttUri(TOPIC_NATIVE_FLOAT))); + assertTrue(floats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE))); + assertTrue(chars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR))); + assertTrue(chars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING))); + + assertTrue(integers.connectBooleanValueTransformed(mqttUri(TOPIC_NATIVE_BOOLEAN))); + assertTrue(integers.connectIntValueTransformed(mqttUri(TOPIC_NATIVE_INT))); + assertTrue(integers.connectShortValueTransformed(mqttUri(TOPIC_NATIVE_SHORT))); + assertTrue(integers.connectLongValueTransformed(mqttUri(TOPIC_NATIVE_LONG))); + assertTrue(floats.connectFloatValueTransformed(mqttUri(TOPIC_NATIVE_FLOAT))); + assertTrue(floats.connectDoubleValueTransformed(mqttUri(TOPIC_NATIVE_DOUBLE))); + assertTrue(chars.connectCharValueTransformed(mqttUri(TOPIC_NATIVE_CHAR))); + assertTrue(chars.connectStringValueTransformed(mqttUri(TOPIC_NATIVE_STRING))); + + assertTrue(allBoxed.connectBooleanValue(mqttUri(TOPIC_BOXED_BOOLEAN))); + assertTrue(allBoxed.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER))); + assertTrue(allBoxed.connectShortValue(mqttUri(TOPIC_BOXED_SHORT))); + assertTrue(allBoxed.connectLongValue(mqttUri(TOPIC_BOXED_LONG))); + assertTrue(allBoxed.connectFloatValue(mqttUri(TOPIC_BOXED_FLOAT))); + assertTrue(allBoxed.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE))); + assertTrue(allBoxed.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER))); + + assertTrue(allBoxed.connectBooleanValueTransformed(mqttUri(TOPIC_BOXED_BOOLEAN))); + assertTrue(allBoxed.connectIntValueTransformed(mqttUri(TOPIC_BOXED_INTEGER))); + assertTrue(allBoxed.connectShortValueTransformed(mqttUri(TOPIC_BOXED_SHORT))); + assertTrue(allBoxed.connectLongValueTransformed(mqttUri(TOPIC_BOXED_LONG))); + assertTrue(allBoxed.connectFloatValueTransformed(mqttUri(TOPIC_BOXED_FLOAT))); + assertTrue(allBoxed.connectDoubleValueTransformed(mqttUri(TOPIC_BOXED_DOUBLE))); + assertTrue(allBoxed.connectCharValueTransformed(mqttUri(TOPIC_BOXED_CHARACTER))); + } + + @Override + protected void communicateSendInitialValue() { + // empty } @Override protected void communicateOnlyUpdatedValue() throws InterruptedException { + final boolean expectedBooleanValue = true; final int expectedIntValue = 1; final short expectedShortValue = 2; final long expectedLongValue = 3L; @@ -84,23 +119,26 @@ public class DefaultOnlyReadTest extends AbstractMqttTest { final char expectedCharValue = 'c'; final String expectedStringValue = "6.3"; - sender.publish(TOPIC_NATIVE_INT, ByteBuffer.allocate(4).putInt(expectedIntValue).array()); - sender.publish(TOPIC_NATIVE_SHORT, ByteBuffer.allocate(2).putShort(expectedShortValue).array()); - sender.publish(TOPIC_NATIVE_LONG, ByteBuffer.allocate(8).putLong(expectedLongValue).array()); - sender.publish(TOPIC_NATIVE_FLOAT, ByteBuffer.allocate(4).putFloat(expectedFloatValue).array()); - sender.publish(TOPIC_NATIVE_DOUBLE, ByteBuffer.allocate(8).putDouble(expectedDoubleValue).array()); - sender.publish(TOPIC_NATIVE_CHAR, ByteBuffer.allocate(2).putChar(expectedCharValue).array()); - sender.publish(TOPIC_NATIVE_STRING, expectedStringValue.getBytes()); - - sender.publish(TOPIC_BOXED_INTEGER, ByteBuffer.allocate(4).putInt(expectedIntValue).array()); - sender.publish(TOPIC_BOXED_SHORT, ByteBuffer.allocate(2).putShort(expectedShortValue).array()); - sender.publish(TOPIC_BOXED_LONG, ByteBuffer.allocate(8).putLong(expectedLongValue).array()); - sender.publish(TOPIC_BOXED_FLOAT, ByteBuffer.allocate(4).putFloat(expectedFloatValue).array()); - sender.publish(TOPIC_BOXED_DOUBLE, ByteBuffer.allocate(8).putDouble(expectedDoubleValue).array()); - sender.publish(TOPIC_BOXED_CHARACTER, ByteBuffer.allocate(2).putChar(expectedCharValue).array()); + publisher.publish(TOPIC_NATIVE_BOOLEAN, TestUtils.DefaultMappings.BoolToBytes(expectedBooleanValue)); + publisher.publish(TOPIC_NATIVE_INT, TestUtils.DefaultMappings.IntToBytes(expectedIntValue)); + publisher.publish(TOPIC_NATIVE_SHORT, TestUtils.DefaultMappings.ShortToBytes(expectedShortValue)); + publisher.publish(TOPIC_NATIVE_LONG, TestUtils.DefaultMappings.LongToBytes(expectedLongValue)); + publisher.publish(TOPIC_NATIVE_FLOAT, TestUtils.DefaultMappings.FloatToBytes(expectedFloatValue)); + publisher.publish(TOPIC_NATIVE_DOUBLE, TestUtils.DefaultMappings.DoubleToBytes(expectedDoubleValue)); + publisher.publish(TOPIC_NATIVE_CHAR, TestUtils.DefaultMappings.CharToBytes(expectedCharValue)); + publisher.publish(TOPIC_NATIVE_STRING, TestUtils.DefaultMappings.StringToBytes(expectedStringValue)); + + publisher.publish(TOPIC_BOXED_BOOLEAN, TestUtils.DefaultMappings.BoolToBytes(expectedBooleanValue)); + publisher.publish(TOPIC_BOXED_INTEGER, TestUtils.DefaultMappings.IntToBytes(expectedIntValue)); + publisher.publish(TOPIC_BOXED_SHORT, TestUtils.DefaultMappings.ShortToBytes(expectedShortValue)); + publisher.publish(TOPIC_BOXED_LONG, TestUtils.DefaultMappings.LongToBytes(expectedLongValue)); + publisher.publish(TOPIC_BOXED_FLOAT, TestUtils.DefaultMappings.FloatToBytes(expectedFloatValue)); + publisher.publish(TOPIC_BOXED_DOUBLE, TestUtils.DefaultMappings.DoubleToBytes(expectedDoubleValue)); + publisher.publish(TOPIC_BOXED_CHARACTER, TestUtils.DefaultMappings.CharToBytes(expectedCharValue)); TestUtils.waitForMqtt(); + assertEquals(expectedBooleanValue, integers.getBooleanValue()); assertEquals(expectedIntValue, integers.getIntValue()); assertEquals(expectedShortValue, integers.getShortValue()); assertEquals(expectedLongValue, integers.getLongValue()); @@ -109,30 +147,37 @@ public class DefaultOnlyReadTest extends AbstractMqttTest { assertEquals(expectedCharValue, chars.getCharValue()); assertEquals(expectedStringValue, chars.getStringValue()); + assertEquals(expectedBooleanValue, integers.getBooleanValueTransformed()); + assertEquals(expectedIntValue, integers.getIntValueTransformed()); + assertEquals(expectedShortValue, integers.getShortValueTransformed()); + assertEquals(expectedLongValue, integers.getLongValueTransformed()); + assertEquals(expectedFloatValue, floats.getFloatValueTransformed(), TestUtils.DELTA); + assertEquals(expectedDoubleValue, floats.getDoubleValueTransformed(), TestUtils.DELTA); + assertEquals(expectedCharValue, chars.getCharValueTransformed()); + assertEquals(expectedStringValue, chars.getStringValueTransformed()); + + assertEquals(expectedBooleanValue, allBoxed.getBooleanValue()); assertEquals(expectedIntValue, allBoxed.getIntValue().intValue()); assertEquals(expectedShortValue, allBoxed.getShortValue().shortValue()); assertEquals(expectedLongValue, allBoxed.getLongValue().longValue()); assertEquals(expectedFloatValue, allBoxed.getFloatValue(), TestUtils.DELTA); assertEquals(expectedDoubleValue, allBoxed.getDoubleValue(), TestUtils.DELTA); assertEquals(expectedCharValue, allBoxed.getCharValue().charValue()); - } - @Override - protected void communicateSendInitialValue() { - // empty + assertEquals(expectedBooleanValue, allBoxed.getBooleanValueTransformed()); + assertEquals(expectedIntValue, allBoxed.getIntValueTransformed().intValue()); + assertEquals(expectedShortValue, allBoxed.getShortValueTransformed().shortValue()); + assertEquals(expectedLongValue, allBoxed.getLongValueTransformed().longValue()); + assertEquals(expectedFloatValue, allBoxed.getFloatValueTransformed(), TestUtils.DELTA); + assertEquals(expectedDoubleValue, allBoxed.getDoubleValueTransformed(), TestUtils.DELTA); + assertEquals(expectedCharValue, allBoxed.getCharValueTransformed().charValue()); } @Override - protected void createModel() { - model = new A(); - integers = new NativeTypes(); - model.addNativeTypes(integers); - floats = new NativeTypes(); - model.addNativeTypes(floats); - chars = new NativeTypes(); - model.addNativeTypes(chars); - allBoxed = new BoxedTypes(); - model.addBoxedTypes(allBoxed); + public void closeConnections() { + if (model != null) { + model.ragconnectCloseConnections(); + } } } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java index c312d4c534d624f0a6c2ee891eba36ddb63be584..d8ce0c083148b671590c6129d16f8d3ed071e313 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java @@ -4,11 +4,13 @@ import defaultOnlyWrite.ast.A; import defaultOnlyWrite.ast.BoxedTypesSyn; import defaultOnlyWrite.ast.MqttHandler; import defaultOnlyWrite.ast.NativeTypesSyn; +import org.junit.jupiter.api.Test; import java.io.IOException; +import java.nio.file.Paths; import java.util.concurrent.TimeUnit; -import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.jastadd.ragconnect.tests.TestUtils.*; import static org.junit.jupiter.api.Assertions.*; /** @@ -18,6 +20,7 @@ import static org.junit.jupiter.api.Assertions.*; */ public class DefaultOnlyWriteTest extends AbstractMqttTest { + private static final String TOPIC_NATIVE_BOOLEAN = "native/boolean"; private static final String TOPIC_NATIVE_INT = "native/int"; private static final String TOPIC_NATIVE_SHORT = "native/short"; private static final String TOPIC_NATIVE_LONG = "native/long"; @@ -26,6 +29,16 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest { private static final String TOPIC_NATIVE_CHAR = "native/char"; private static final String TOPIC_NATIVE_STRING = "native/string"; + private static final String TOPIC_NATIVE_BOOLEAN_TRANSFORMED = "native/boolean/t"; + private static final String TOPIC_NATIVE_INT_TRANSFORMED = "native/int/t"; + private static final String TOPIC_NATIVE_SHORT_TRANSFORMED = "native/short/t"; + private static final String TOPIC_NATIVE_LONG_TRANSFORMED = "native/long/t"; + private static final String TOPIC_NATIVE_FLOAT_TRANSFORMED = "native/float/t"; + private static final String TOPIC_NATIVE_DOUBLE_TRANSFORMED = "native/double/t"; + private static final String TOPIC_NATIVE_CHAR_TRANSFORMED = "native/char/t"; + private static final String TOPIC_NATIVE_STRING_TRANSFORMED = "native/string/t"; + + private static final String TOPIC_BOXED_BOOLEAN = "boxed/Boolean"; private static final String TOPIC_BOXED_INTEGER = "boxed/Integer"; private static final String TOPIC_BOXED_SHORT = "boxed/Short"; private static final String TOPIC_BOXED_LONG = "boxed/Long"; @@ -33,6 +46,14 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest { private static final String TOPIC_BOXED_DOUBLE = "boxed/Double"; private static final String TOPIC_BOXED_CHARACTER = "boxed/Character"; + private static final String TOPIC_BOXED_BOOLEAN_TRANSFORMED = "boxed/Boolean/t"; + private static final String TOPIC_BOXED_INTEGER_TRANSFORMED = "boxed/Integer/t"; + private static final String TOPIC_BOXED_SHORT_TRANSFORMED = "boxed/Short/t"; + private static final String TOPIC_BOXED_LONG_TRANSFORMED = "boxed/Long/t"; + private static final String TOPIC_BOXED_FLOAT_TRANSFORMED = "boxed/Float/t"; + private static final String TOPIC_BOXED_DOUBLE_TRANSFORMED = "boxed/Double/t"; + private static final String TOPIC_BOXED_CHARACTER_TRANSFORMED = "boxed/Character/t"; + private A model; private NativeTypesSyn nativeIntegers; private NativeTypesSyn nativeFloats; @@ -41,61 +62,14 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest { private BoxedTypesSyn boxedFloats; private BoxedTypesSyn boxedChars; private MqttHandler receiver; - private ReceiverData data; - - @Override - public void closeConnections() { - if (receiver != null) { - receiver.close(); - } - if (model != null) { - model.ragconnectCloseConnections(); - } - } - - - - - @Override - protected void communicateSendInitialValue() throws InterruptedException { - // check initial value - TestUtils.waitForMqtt(); - checkData(1, 1, 1.1, 'a', "ab"); - - // set new value - setData("2", "2.2", "cd"); - - // check new value - TestUtils.waitForMqtt(); - checkData(2, 2, 2.2, 'c', "cd"); - - // set new value - setData("3", "3.2", "ee"); - - // check new value - TestUtils.waitForMqtt(); - checkData(3, 3, 3.2, 'e', "ee"); - } - - @Override - protected void communicateOnlyUpdatedValue() throws InterruptedException { - // check initial value (will be default values) - TestUtils.waitForMqtt(); - checkData(0, null, null, null, null); - - // set new value - setData("2", "2.2", "cd"); - - // check new value - TestUtils.waitForMqtt(); - checkData(1, 2, 2.2, 'c', "cd"); - - // set new value - setData("3", "3.2", "ee"); - - // check new value - TestUtils.waitForMqtt(); - checkData(2, 3, 3.2, 'e', "ee"); + private ReceiverData dataNormal; + private ReceiverData dataTransformed; + + @Test + public void checkNotJacksonReference() { + testJaddContainReferenceToJackson( + Paths.get("src", "test", + "02-after-ragconnect", "defaultOnlyWrite", "RagConnect.jadd"), false); } @Override @@ -126,6 +100,7 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest { receiver = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); assertTrue(receiver.waitUntilReady(2, TimeUnit.SECONDS)); + nativeIntegers.addNativeBooleanDependency(nativeIntegers); nativeIntegers.addNativeIntDependency(nativeIntegers); nativeIntegers.addNativeShortDependency(nativeIntegers); nativeIntegers.addNativeLongDependency(nativeIntegers); @@ -134,6 +109,16 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest { nativeChars.addNativeCharDependency(nativeChars); nativeChars.addNativeStringDependency(nativeChars); + nativeIntegers.addNativeBooleanTransformedDependency(nativeIntegers); + nativeIntegers.addNativeIntTransformedDependency(nativeIntegers); + nativeIntegers.addNativeShortTransformedDependency(nativeIntegers); + nativeIntegers.addNativeLongTransformedDependency(nativeIntegers); + nativeFloats.addNativeFloatTransformedDependency(nativeFloats); + nativeFloats.addNativeDoubleTransformedDependency(nativeFloats); + nativeChars.addNativeCharTransformedDependency(nativeChars); + nativeChars.addNativeStringTransformedDependency(nativeChars); + + boxedIntegers.addBoxedBooleanDependency(boxedIntegers); boxedIntegers.addBoxedIntDependency(boxedIntegers); boxedIntegers.addBoxedShortDependency(boxedIntegers); boxedIntegers.addBoxedLongDependency(boxedIntegers); @@ -141,74 +126,162 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest { boxedFloats.addBoxedDoubleDependency(boxedFloats); boxedChars.addBoxedCharDependency(boxedChars); - data = new ReceiverData(); + boxedIntegers.addBoxedBooleanTransformedDependency(boxedIntegers); + boxedIntegers.addBoxedIntTransformedDependency(boxedIntegers); + boxedIntegers.addBoxedShortTransformedDependency(boxedIntegers); + boxedIntegers.addBoxedLongTransformedDependency(boxedIntegers); + boxedFloats.addBoxedFloatTransformedDependency(boxedFloats); + boxedFloats.addBoxedDoubleTransformedDependency(boxedFloats); + boxedChars.addBoxedCharTransformedDependency(boxedChars); + + dataNormal = createReceiver(false); + dataTransformed = createReceiver(true); + + assertTrue(nativeIntegers.connectBooleanValue(mqttUri(TOPIC_NATIVE_BOOLEAN), writeCurrentValue)); + assertTrue(nativeIntegers.connectIntValue(mqttUri(TOPIC_NATIVE_INT), writeCurrentValue)); + assertTrue(nativeIntegers.connectShortValue(mqttUri(TOPIC_NATIVE_SHORT), writeCurrentValue)); + assertTrue(nativeIntegers.connectLongValue(mqttUri(TOPIC_NATIVE_LONG), writeCurrentValue)); + assertTrue(nativeFloats.connectFloatValue(mqttUri(TOPIC_NATIVE_FLOAT), writeCurrentValue)); + assertTrue(nativeFloats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE), writeCurrentValue)); + assertTrue(nativeChars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR), writeCurrentValue)); + assertTrue(nativeChars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING), writeCurrentValue)); + + assertTrue(nativeIntegers.connectBooleanValueTransformed(mqttUri(TOPIC_NATIVE_BOOLEAN_TRANSFORMED), writeCurrentValue)); + assertTrue(nativeIntegers.connectIntValueTransformed(mqttUri(TOPIC_NATIVE_INT_TRANSFORMED), writeCurrentValue)); + assertTrue(nativeIntegers.connectShortValueTransformed(mqttUri(TOPIC_NATIVE_SHORT_TRANSFORMED), writeCurrentValue)); + assertTrue(nativeIntegers.connectLongValueTransformed(mqttUri(TOPIC_NATIVE_LONG_TRANSFORMED), writeCurrentValue)); + assertTrue(nativeFloats.connectFloatValueTransformed(mqttUri(TOPIC_NATIVE_FLOAT_TRANSFORMED), writeCurrentValue)); + assertTrue(nativeFloats.connectDoubleValueTransformed(mqttUri(TOPIC_NATIVE_DOUBLE_TRANSFORMED), writeCurrentValue)); + assertTrue(nativeChars.connectCharValueTransformed(mqttUri(TOPIC_NATIVE_CHAR_TRANSFORMED), writeCurrentValue)); + assertTrue(nativeChars.connectStringValueTransformed(mqttUri(TOPIC_NATIVE_STRING_TRANSFORMED), writeCurrentValue)); + + assertTrue(boxedIntegers.connectBooleanValue(mqttUri(TOPIC_BOXED_BOOLEAN), writeCurrentValue)); + assertTrue(boxedIntegers.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER), writeCurrentValue)); + assertTrue(boxedIntegers.connectShortValue(mqttUri(TOPIC_BOXED_SHORT), writeCurrentValue)); + assertTrue(boxedIntegers.connectLongValue(mqttUri(TOPIC_BOXED_LONG), writeCurrentValue)); + assertTrue(boxedFloats.connectFloatValue(mqttUri(TOPIC_BOXED_FLOAT), writeCurrentValue)); + assertTrue(boxedFloats.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE), writeCurrentValue)); + assertTrue(boxedChars.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER), writeCurrentValue)); + + assertTrue(boxedIntegers.connectBooleanValueTransformed(mqttUri(TOPIC_BOXED_BOOLEAN_TRANSFORMED), writeCurrentValue)); + assertTrue(boxedIntegers.connectIntValueTransformed(mqttUri(TOPIC_BOXED_INTEGER_TRANSFORMED), writeCurrentValue)); + assertTrue(boxedIntegers.connectShortValueTransformed(mqttUri(TOPIC_BOXED_SHORT_TRANSFORMED), writeCurrentValue)); + assertTrue(boxedIntegers.connectLongValueTransformed(mqttUri(TOPIC_BOXED_LONG_TRANSFORMED), writeCurrentValue)); + assertTrue(boxedFloats.connectFloatValueTransformed(mqttUri(TOPIC_BOXED_FLOAT_TRANSFORMED), writeCurrentValue)); + assertTrue(boxedFloats.connectDoubleValueTransformed(mqttUri(TOPIC_BOXED_DOUBLE_TRANSFORMED), writeCurrentValue)); + assertTrue(boxedChars.connectCharValueTransformed(mqttUri(TOPIC_BOXED_CHARACTER_TRANSFORMED), writeCurrentValue)); + } + + private ReceiverData createReceiver(boolean transformed) { + ReceiverData result = new ReceiverData(); - receiver.newConnection(TOPIC_NATIVE_INT, bytes -> { - data.numberOfNativeIntValues += 1; - data.lastNativeIntValue = java.nio.ByteBuffer.wrap(bytes).getInt(); + receiver.newConnection(transformed ? TOPIC_NATIVE_BOOLEAN_TRANSFORMED : TOPIC_NATIVE_BOOLEAN, bytes -> { + result.numberOfNativeBoolValues += 1; + result.lastNativeBoolValue = TestUtils.DefaultMappings.BytesToBool(bytes); + }); + receiver.newConnection(transformed ? TOPIC_NATIVE_INT_TRANSFORMED : TOPIC_NATIVE_INT, bytes -> { + result.numberOfNativeIntValues += 1; + result.lastNativeIntValue = TestUtils.DefaultMappings.BytesToInt(bytes); }); - receiver.newConnection(TOPIC_NATIVE_SHORT, bytes -> { - data.numberOfNativeShortValues += 1; - data.lastNativeShortValue = java.nio.ByteBuffer.wrap(bytes).getShort(); + receiver.newConnection(transformed ? TOPIC_NATIVE_SHORT_TRANSFORMED : TOPIC_NATIVE_SHORT, bytes -> { + result.numberOfNativeShortValues += 1; + result.lastNativeShortValue = TestUtils.DefaultMappings.BytesToShort(bytes); }); - receiver.newConnection(TOPIC_NATIVE_LONG, bytes -> { - data.numberOfNativeLongValues += 1; - data.lastNativeLongValue = java.nio.ByteBuffer.wrap(bytes).getLong(); + receiver.newConnection(transformed ? TOPIC_NATIVE_LONG_TRANSFORMED : TOPIC_NATIVE_LONG, bytes -> { + result.numberOfNativeLongValues += 1; + result.lastNativeLongValue = TestUtils.DefaultMappings.BytesToLong(bytes); }); - receiver.newConnection(TOPIC_NATIVE_FLOAT, bytes -> { - data.numberOfNativeFloatValues += 1; - data.lastNativeFloatValue = java.nio.ByteBuffer.wrap(bytes).getFloat(); + receiver.newConnection(transformed ? TOPIC_NATIVE_FLOAT_TRANSFORMED : TOPIC_NATIVE_FLOAT, bytes -> { + result.numberOfNativeFloatValues += 1; + result.lastNativeFloatValue = TestUtils.DefaultMappings.BytesToFloat(bytes); }); - receiver.newConnection(TOPIC_NATIVE_DOUBLE, bytes -> { - data.numberOfNativeDoubleValues += 1; - data.lastNativeDoubleValue = java.nio.ByteBuffer.wrap(bytes).getDouble(); + receiver.newConnection(transformed ? TOPIC_NATIVE_DOUBLE_TRANSFORMED : TOPIC_NATIVE_DOUBLE, bytes -> { + result.numberOfNativeDoubleValues += 1; + result.lastNativeDoubleValue = TestUtils.DefaultMappings.BytesToDouble(bytes); }); - receiver.newConnection(TOPIC_NATIVE_CHAR, bytes -> { - data.numberOfNativeCharValues += 1; - data.lastNativeCharValue = java.nio.ByteBuffer.wrap(bytes).getChar(); + receiver.newConnection(transformed ? TOPIC_NATIVE_CHAR_TRANSFORMED : TOPIC_NATIVE_CHAR, bytes -> { + result.numberOfNativeCharValues += 1; + result.lastNativeCharValue = TestUtils.DefaultMappings.BytesToChar(bytes); }); - receiver.newConnection(TOPIC_NATIVE_STRING, bytes -> { - data.numberOfNativeStringValues += 1; - data.lastNativeStringValue = new String(bytes); + receiver.newConnection(transformed ? TOPIC_NATIVE_STRING_TRANSFORMED : TOPIC_NATIVE_STRING, bytes -> { + result.numberOfNativeStringValues += 1; + result.lastNativeStringValue = TestUtils.DefaultMappings.BytesToString(bytes); }); - receiver.newConnection(TOPIC_BOXED_INTEGER, bytes -> { - data.numberOfBoxedIntValues += 1; - data.lastBoxedIntValue = java.nio.ByteBuffer.wrap(bytes).getInt(); + receiver.newConnection(transformed ? TOPIC_BOXED_BOOLEAN_TRANSFORMED : TOPIC_BOXED_BOOLEAN, bytes -> { + result.numberOfBoxedBoolValues += 1; + result.lastBoxedBoolValue = TestUtils.DefaultMappings.BytesToBool(bytes); }); - receiver.newConnection(TOPIC_BOXED_SHORT, bytes -> { - data.numberOfBoxedShortValues += 1; - data.lastBoxedShortValue = java.nio.ByteBuffer.wrap(bytes).getShort(); + receiver.newConnection(transformed ? TOPIC_BOXED_INTEGER_TRANSFORMED : TOPIC_BOXED_INTEGER, bytes -> { + result.numberOfBoxedIntValues += 1; + result.lastBoxedIntValue = TestUtils.DefaultMappings.BytesToInt(bytes); }); - receiver.newConnection(TOPIC_BOXED_LONG, bytes -> { - data.numberOfBoxedLongValues += 1; - data.lastBoxedLongValue = java.nio.ByteBuffer.wrap(bytes).getLong(); + receiver.newConnection(transformed ? TOPIC_BOXED_SHORT_TRANSFORMED : TOPIC_BOXED_SHORT, bytes -> { + result.numberOfBoxedShortValues += 1; + result.lastBoxedShortValue = TestUtils.DefaultMappings.BytesToShort(bytes); }); - receiver.newConnection(TOPIC_BOXED_FLOAT, bytes -> { - data.numberOfBoxedFloatValues += 1; - data.lastBoxedFloatValue = java.nio.ByteBuffer.wrap(bytes).getFloat(); + receiver.newConnection(transformed ? TOPIC_BOXED_LONG_TRANSFORMED : TOPIC_BOXED_LONG, bytes -> { + result.numberOfBoxedLongValues += 1; + result.lastBoxedLongValue = TestUtils.DefaultMappings.BytesToLong(bytes); }); - receiver.newConnection(TOPIC_BOXED_DOUBLE, bytes -> { - data.numberOfBoxedDoubleValues += 1; - data.lastBoxedDoubleValue = java.nio.ByteBuffer.wrap(bytes).getDouble(); + receiver.newConnection(transformed ? TOPIC_BOXED_FLOAT_TRANSFORMED : TOPIC_BOXED_FLOAT, bytes -> { + result.numberOfBoxedFloatValues += 1; + result.lastBoxedFloatValue = TestUtils.DefaultMappings.BytesToFloat(bytes); }); - receiver.newConnection(TOPIC_BOXED_CHARACTER, bytes -> { - data.numberOfBoxedCharValues += 1; - data.lastBoxedCharValue = java.nio.ByteBuffer.wrap(bytes).getChar(); + receiver.newConnection(transformed ? TOPIC_BOXED_DOUBLE_TRANSFORMED : TOPIC_BOXED_DOUBLE, bytes -> { + result.numberOfBoxedDoubleValues += 1; + result.lastBoxedDoubleValue = TestUtils.DefaultMappings.BytesToDouble(bytes); }); + receiver.newConnection(transformed ? TOPIC_BOXED_CHARACTER_TRANSFORMED : TOPIC_BOXED_CHARACTER, bytes -> { + result.numberOfBoxedCharValues += 1; + result.lastBoxedCharValue = TestUtils.DefaultMappings.BytesToChar(bytes); + }); + return result; + } + + @Override + protected void communicateSendInitialValue() throws InterruptedException { + // check initial value + checkData(1, 1, false, 1, 1.1, 'a', "ab"); + + // set new value + setData("2", "2.2", "cd"); - nativeIntegers.connectIntValue(mqttUri(TOPIC_NATIVE_INT), writeCurrentValue); - nativeIntegers.connectShortValue(mqttUri(TOPIC_NATIVE_SHORT), writeCurrentValue); - nativeIntegers.connectLongValue(mqttUri(TOPIC_NATIVE_LONG), writeCurrentValue); - nativeFloats.connectFloatValue(mqttUri(TOPIC_NATIVE_FLOAT), writeCurrentValue); - nativeFloats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE), writeCurrentValue); - nativeChars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR), writeCurrentValue); - nativeChars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING), writeCurrentValue); - boxedIntegers.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER), writeCurrentValue); - boxedIntegers.connectShortValue(mqttUri(TOPIC_BOXED_SHORT), writeCurrentValue); - boxedIntegers.connectLongValue(mqttUri(TOPIC_BOXED_LONG), writeCurrentValue); - boxedFloats.connectFloatValue(mqttUri(TOPIC_BOXED_FLOAT), writeCurrentValue); - boxedFloats.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE), writeCurrentValue); - boxedChars.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER), writeCurrentValue); + // check new value + checkData(2, 2, true, 2, 2.2, 'c', "cd"); + + // set new value + setData("3", "3.2", "ee"); + + // check new value + checkData(3, 2, true, 3, 3.2, 'e', "ee"); + } + + @Override + protected void communicateOnlyUpdatedValue() throws InterruptedException { + // check initial value (will be default values) + checkData(0, 0, null, null, null, null, null); + + // set new value + setData("2", "2.2", "cd"); + + // check new value + checkData(1, 1, true, 2, 2.2, 'c', "cd"); + + // set new value + setData("3", "3.2", "ee"); + + // check new value + checkData(2, 1, true, 3, 3.2, 'e', "ee"); + } + + @Override + public void closeConnections() { + if (receiver != null) { + receiver.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } } private void setData(String integerDriver, String floatDriver, String stringDriver) { @@ -221,63 +294,78 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest { boxedChars.setDriverSyn(stringDriver); } - private void checkData(int expectedNumberOfValues, - Integer expectedInt, Double expectedDouble, - Character expectedChar, String expectedString) { - assertEquals(expectedNumberOfValues, data.numberOfNativeIntValues); - assertEquals(expectedNumberOfValues, data.numberOfNativeShortValues); - assertEquals(expectedNumberOfValues, data.numberOfNativeLongValues); - assertEquals(expectedNumberOfValues, data.numberOfNativeFloatValues); - assertEquals(expectedNumberOfValues, data.numberOfNativeDoubleValues); - assertEquals(expectedNumberOfValues, data.numberOfNativeCharValues); - assertEquals(expectedNumberOfValues, data.numberOfNativeStringValues); - - assertEquals(expectedNumberOfValues, data.numberOfBoxedIntValues); - assertEquals(expectedNumberOfValues, data.numberOfBoxedShortValues); - assertEquals(expectedNumberOfValues, data.numberOfBoxedLongValues); - assertEquals(expectedNumberOfValues, data.numberOfBoxedFloatValues); - assertEquals(expectedNumberOfValues, data.numberOfBoxedDoubleValues); - assertEquals(expectedNumberOfValues, data.numberOfBoxedCharValues); - - if (expectedInt != null) { - assertEquals(expectedInt.intValue(), data.lastNativeIntValue); - assertEquals(expectedInt.shortValue(), data.lastNativeShortValue); - assertEquals(expectedInt.longValue(), data.lastNativeLongValue); - assertEquals(expectedInt.intValue(), data.lastBoxedIntValue.intValue()); - assertEquals(expectedInt.shortValue(), data.lastBoxedShortValue.shortValue()); - assertEquals(expectedInt.longValue(), data.lastBoxedLongValue.longValue()); - } else { - assertEquals(0, data.lastNativeIntValue); - assertEquals(0, data.lastNativeShortValue); - assertEquals(0, data.lastNativeLongValue); - assertNull(data.lastBoxedIntValue); - assertNull(data.lastBoxedShortValue); - assertNull(data.lastBoxedLongValue); - } - - if (expectedDouble != null) { - assertEquals(expectedDouble.floatValue(), data.lastNativeFloatValue, TestUtils.DELTA); - assertEquals(expectedDouble, data.lastNativeDoubleValue, TestUtils.DELTA); - assertEquals(expectedDouble.floatValue(), data.lastBoxedFloatValue, TestUtils.DELTA); - assertEquals(expectedDouble, data.lastBoxedDoubleValue, TestUtils.DELTA); - } else { - assertEquals(0f, data.lastNativeFloatValue, TestUtils.DELTA); - assertEquals(0d, data.lastNativeDoubleValue, TestUtils.DELTA); - assertNull(data.lastBoxedFloatValue); - assertNull(data.lastBoxedDoubleValue); - } - - if (expectedChar != null) { - assertEquals(expectedChar.charValue(), data.lastNativeCharValue); - assertEquals(expectedChar, data.lastBoxedCharValue); - } else { - assertEquals('\0', data.lastNativeCharValue); - assertNull(data.lastBoxedCharValue); + private void checkData(int expectedNumberOfValues, int expectedNumberOfBoolValues, + Boolean expectedBool, Integer expectedInt, Double expectedDouble, + Character expectedChar, String expectedString) throws InterruptedException { + TestUtils.waitForMqtt(); + for (ReceiverData data : new ReceiverData[]{dataNormal, dataTransformed}) { + assertEquals(expectedNumberOfBoolValues, data.numberOfNativeBoolValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeIntValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeShortValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeLongValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeFloatValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeDoubleValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeCharValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeStringValues); + + assertEquals(expectedNumberOfBoolValues, data.numberOfBoxedBoolValues); + assertEquals(expectedNumberOfValues, data.numberOfBoxedIntValues); + assertEquals(expectedNumberOfValues, data.numberOfBoxedShortValues); + assertEquals(expectedNumberOfValues, data.numberOfBoxedLongValues); + assertEquals(expectedNumberOfValues, data.numberOfBoxedFloatValues); + assertEquals(expectedNumberOfValues, data.numberOfBoxedDoubleValues); + assertEquals(expectedNumberOfValues, data.numberOfBoxedCharValues); + + if (expectedBool != null) { + assertEquals(expectedBool, data.lastNativeBoolValue); + assertEquals(expectedBool, data.lastBoxedBoolValue); + } else { + assertFalse(data.lastNativeBoolValue); + assertNull(data.lastBoxedBoolValue); + } + + if (expectedInt != null) { + assertEquals(expectedInt.intValue(), data.lastNativeIntValue); + assertEquals(expectedInt.shortValue(), data.lastNativeShortValue); + assertEquals(expectedInt.longValue(), data.lastNativeLongValue); + assertEquals(expectedInt.intValue(), data.lastBoxedIntValue.intValue()); + assertEquals(expectedInt.shortValue(), data.lastBoxedShortValue.shortValue()); + assertEquals(expectedInt.longValue(), data.lastBoxedLongValue.longValue()); + } else { + assertEquals(0, data.lastNativeIntValue); + assertEquals(0, data.lastNativeShortValue); + assertEquals(0, data.lastNativeLongValue); + assertNull(data.lastBoxedIntValue); + assertNull(data.lastBoxedShortValue); + assertNull(data.lastBoxedLongValue); + } + + if (expectedDouble != null) { + assertEquals(expectedDouble.floatValue(), data.lastNativeFloatValue, TestUtils.DELTA); + assertEquals(expectedDouble, data.lastNativeDoubleValue, TestUtils.DELTA); + assertEquals(expectedDouble.floatValue(), data.lastBoxedFloatValue, TestUtils.DELTA); + assertEquals(expectedDouble, data.lastBoxedDoubleValue, TestUtils.DELTA); + } else { + assertEquals(0f, data.lastNativeFloatValue, TestUtils.DELTA); + assertEquals(0d, data.lastNativeDoubleValue, TestUtils.DELTA); + assertNull(data.lastBoxedFloatValue); + assertNull(data.lastBoxedDoubleValue); + } + + if (expectedChar != null) { + assertEquals(expectedChar.charValue(), data.lastNativeCharValue); + assertEquals(expectedChar, data.lastBoxedCharValue); + } else { + assertEquals('\0', data.lastNativeCharValue); + assertNull(data.lastBoxedCharValue); + } + assertEquals(expectedString, data.lastNativeStringValue); } - assertEquals(expectedString, data.lastNativeStringValue); } private static class ReceiverData { + boolean lastNativeBoolValue; + int numberOfNativeBoolValues = 0; int lastNativeIntValue; int numberOfNativeIntValues = 0; short lastNativeShortValue; @@ -293,6 +381,8 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest { String lastNativeStringValue; int numberOfNativeStringValues = 0; + Boolean lastBoxedBoolValue; + int numberOfBoxedBoolValues = 0; Integer lastBoxedIntValue; int numberOfBoxedIntValues = 0; Short lastBoxedShortValue; diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Errors.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Errors.java index 17d853fba71c2ee46c6c6c7b5424958af0a5f8d2..4d32adff23abd12f2052f320466b79dc0f8673f5 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Errors.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Errors.java @@ -2,7 +2,6 @@ package org.jastadd.ragconnect.tests; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jastadd.ragconnect.compiler.Compiler; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -10,21 +9,25 @@ import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import static org.jastadd.ragconnect.tests.TestUtils.exec; +import static org.assertj.core.api.Assertions.assertThat; import static org.jastadd.ragconnect.tests.TestUtils.readFile; import static org.junit.jupiter.api.Assertions.assertTrue; -class Errors { +public class Errors { private static final Logger logger = LogManager.getLogger(Errors.class); private static final String FILENAME_PATTERN = "$FILENAME"; - private static final String INPUT_DIRECTORY = "./src/test/01-input/errors/"; - private static final String OUTPUT_DIRECTORY = "./src/test/02-after-ragconnect/errors/"; + private static final String ERROR_DIRECTORY = "errors/"; + private static final String OUTPUT_DIRECTORY = TestUtils.OUTPUT_DIRECTORY_PREFIX + ERROR_DIRECTORY; + + private static final String DEFAULT_GRAMMAR_NAME = "Errors"; @BeforeAll public static void createOutputDirectory() { @@ -34,40 +37,31 @@ class Errors { @Test void testStandardErrors() throws IOException { - test("Errors", "A"); + test("Standard", "A","Standard"); + } + + @Test + void testTwoPartsErrors() throws IOException { + test("Part", "A","Part1", "Part2"); } @SuppressWarnings("SameParameterValue") - private void test(String name, String rootNode) throws IOException { - String grammarFile = INPUT_DIRECTORY + name + ".relast"; - String ragconnectFile = INPUT_DIRECTORY + name + ".connect"; - String outFile = OUTPUT_DIRECTORY + name + ".out"; - String expectedFile = INPUT_DIRECTORY + name + ".expected"; + private void test(String expectedName, String rootNode, String... connectNames) throws IOException { + String grammarFile = ERROR_DIRECTORY + DEFAULT_GRAMMAR_NAME + ".relast"; + List<String> connectFiles = Arrays.stream(connectNames) + .map(connectName -> ERROR_DIRECTORY + connectName + ".connect") + .collect(Collectors.toList()); + Path outPath = TestUtils.runCompiler(grammarFile, connectFiles, rootNode, ERROR_DIRECTORY, 1); - try { - logger.debug("user.dir: {}", System.getProperty("user.dir")); - String[] args = { - "--o=" + OUTPUT_DIRECTORY, - grammarFile, - ragconnectFile, - "--rootNode=" + rootNode, - "--verbose" - }; - int returnValue = exec(Compiler.class, args, new File(outFile)); - Assertions.assertEquals(1, returnValue, "RagConnect did not return with value 1"); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); - } + final String startOfErrorsPattern = "SEVERE: Errors:"; + String out = readFile(outPath, Charset.defaultCharset()); + assertThat(out).contains(startOfErrorsPattern); + out = out.substring(out.indexOf(startOfErrorsPattern) + 16); - String out = readFile(outFile, Charset.defaultCharset()); - String expected = readFile(expectedFile, Charset.defaultCharset()); -// if (inFiles.size() == 1) { - expected = expected.replace(FILENAME_PATTERN, name); -// } else { -// for (int i = 0; i < inFiles.size(); i++) { -// expected = expected.replace(FILENAME_PATTERN + (i + 1), inFiles.get(i)); -// } -// } + Path expectedPath = Paths.get(TestUtils.INPUT_DIRECTORY_PREFIX) + .resolve(ERROR_DIRECTORY) + .resolve(expectedName + ".expected"); + String expected = readFile(expectedPath, Charset.defaultCharset()); List<String> outList = Arrays.asList(out.split("\n")); Collections.sort(outList); List<String> expectedList = Arrays.stream(expected.split("\n")) @@ -75,10 +69,9 @@ class Errors { .filter(s -> !s.isEmpty() && !s.startsWith("//")) .collect(Collectors.toList()); - // FIXME errors not handled correctly at the moment -// Assertions.assertLinesMatch(expectedList, outList); + Assertions.assertLinesMatch(expectedList, outList); - logger.info("ragconnect for " + name + " returned:\n{}", out); + logger.info("ragconnect for " + expectedName + " returned:\n{}", out); } } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java index c3df78d1b17658a1327afa0f4e2f308eb5ee159b..5ad824bcd8de42d998d7825c6930892ecc57e37d 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java @@ -37,16 +37,70 @@ public class ExampleTest extends AbstractMqttTest { } @Override - public void closeConnections() { - if (handler != null) { - handler.close(); - } - if (model != null) { - model.ragconnectCloseConnections(); - } + protected void createModel() { + model = new Model(); + + ZoneModel zoneModel = new ZoneModel(); + + IntPosition firstPosition = makePosition(0, 0, 0); + IntPosition secondPosition = makePosition(-1, 0, 0); + IntPosition thirdPosition = makePosition(1, 0, 0); + + Zone safetyZone = new Zone(); + safetyZone.addCoordinate(new Coordinate(firstPosition)); + safetyZone.addCoordinate(new Coordinate(secondPosition)); + safetyZone.addCoordinate(new Coordinate(thirdPosition)); + zoneModel.addSafetyZone(safetyZone); + model.setZoneModel(zoneModel); + + robotArm = new RobotArm(); + + link1 = new Link(); + link1.setName("joint1"); + link1.setCurrentPosition(firstPosition); + + link2 = new Link(); + link2.setName("joint2"); + link2.setCurrentPosition(secondPosition); + + EndEffector endEffector = new EndEffector(); + endEffector.setName("gripper"); + endEffector.setCurrentPosition(makePosition(2, 2, 3)); + + robotArm.addLink(link1); + robotArm.addLink(link2); + robotArm.setEndEffector(endEffector); + model.setRobotArm(robotArm); } + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // add dependencies + robotArm.addDependency1(link1); + robotArm.addDependency1(link2); + robotArm.addDependency1(robotArm.getEndEffector()); + + data = new ReceiverData(); + handler.newConnection(TOPIC_CONFIG, bytes -> { + data.numberOfConfigs += 1; + try { + data.lastConfig = RobotConfig.parseFrom(bytes); + data.failedLastConversion = false; + } catch (InvalidProtocolBufferException e) { + data.failedLastConversion = true; + } + }); + + assertTrue(robotArm.connectAppropriateSpeed(mqttUri(TOPIC_CONFIG), writeCurrentValue)); + assertTrue(link1.connectCurrentPosition(mqttUri(TOPIC_JOINT1))); + assertTrue(link2.connectCurrentPosition(mqttUri(TOPIC_JOINT2))); + } @Override protected void communicateSendInitialValue() throws InterruptedException { @@ -182,90 +236,34 @@ public class ExampleTest extends AbstractMqttTest { assertFalse(data.failedLastConversion); } + @Override + public void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } + @Test public void testFailedConversion() throws IOException { createModel(); setupReceiverAndConnect(false); - handler.publish(TOPIC_JOINT1, "not-a-pandaLinkState".getBytes()); + publisher.publish(TOPIC_JOINT1, "not-a-pandaLinkState".getBytes()); assertEquals(0, data.numberOfConfigs); assertTrue(data.failedLastConversion); } private void sendData(String topic, float x, float y, float z) { - handler.publish(topic, RobotState.newBuilder() + publisher.publish(topic, RobotState.newBuilder() .setPosition(RobotState.Position.newBuilder().setX(x).setY(y).setZ(z).build()) .build() .toByteArray() ); } - @Override - protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { - model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); - - handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); - assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); - - // add dependencies - robotArm.addDependency1(link1); - robotArm.addDependency1(link2); - robotArm.addDependency1(robotArm.getEndEffector()); - - data = new ReceiverData(); - - handler.newConnection(TOPIC_CONFIG, bytes -> { - data.numberOfConfigs += 1; - try { - data.lastConfig = RobotConfig.parseFrom(bytes); - data.failedLastConversion = false; - } catch (InvalidProtocolBufferException e) { - data.failedLastConversion = true; - } - }); - - robotArm.connectAppropriateSpeed(mqttUri(TOPIC_CONFIG), writeCurrentValue); - link1.connectCurrentPosition(mqttUri(TOPIC_JOINT1)); - link2.connectCurrentPosition(mqttUri(TOPIC_JOINT2)); - } - - @Override - protected void createModel() { - model = new Model(); - - ZoneModel zoneModel = new ZoneModel(); - - IntPosition firstPosition = makePosition(0, 0, 0); - IntPosition secondPosition = makePosition(-1, 0, 0); - IntPosition thirdPosition = makePosition(1, 0, 0); - - Zone safetyZone = new Zone(); - safetyZone.addCoordinate(new Coordinate(firstPosition)); - safetyZone.addCoordinate(new Coordinate(secondPosition)); - safetyZone.addCoordinate(new Coordinate(thirdPosition)); - zoneModel.addSafetyZone(safetyZone); - model.setZoneModel(zoneModel); - - robotArm = new RobotArm(); - - link1 = new Link(); - link1.setName("joint1"); - link1.setCurrentPosition(firstPosition); - - link2 = new Link(); - link2.setName("joint2"); - link2.setCurrentPosition(secondPosition); - - EndEffector endEffector = new EndEffector(); - endEffector.setName("gripper"); - endEffector.setCurrentPosition(makePosition(2, 2, 3)); - - robotArm.addLink(link1); - robotArm.addLink(link2); - robotArm.setEndEffector(endEffector); - model.setRobotArm(robotArm); - } - private static IntPosition makePosition(int x, int y, int z) { return IntPosition.of(x, y, z); } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..580c4fc81a2952f0042eb273ffcb9be35b0a3df5 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java @@ -0,0 +1,168 @@ +package org.jastadd.ragconnect.tests; + +import incremental.ast.A; +import incremental.ast.B; +import incremental.ast.MqttHandler; +import org.junit.jupiter.api.Tag; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Testcase "Incremental Dependency". + * + * @author rschoene - Initial contribution + */ +@Tag("Incremental") +public class IncrementalDependencyTest extends AbstractMqttTest { + + private static final String TOPIC_IN = "in/a"; + private static final String TOPIC_OUT_A = "out/a"; + private static final String TOPIC_OUT_B1 = "out/b1"; + private static final String TOPIC_OUT_B2 = "out/b2"; + + private MqttHandler handler; + private A model; + private B b1; + private B b2; + + private ReceiverData dataA; + private ReceiverData dataB1; + private ReceiverData dataB2; + + @Override + protected void createModel() { + model = new A(); + model.setInput("Start"); + b1 = new B(); + b2 = new B(); + model.addB(b1); + model.addB(b2); + model.ragconnectCheckIncremental(); + } + + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler("TestHandler") + .dontSendWelcomeMessage() + .setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // no dependencies for the model are set here + + dataA = new ReceiverData(); + dataB1 = new ReceiverData(); + dataB2 = new ReceiverData(); + + handler.newConnection(TOPIC_OUT_A, bytes -> { + dataA.numberOfStringValues += 1; + dataA.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); + }); + handler.newConnection(TOPIC_OUT_B1, bytes -> { + dataB1.numberOfStringValues += 1; + dataB1.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); + }); + handler.newConnection(TOPIC_OUT_B2, bytes -> { + dataB2.numberOfStringValues += 1; + dataB2.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); + }); + + assertTrue(model.connectInput(mqttUri(TOPIC_IN))); + assertTrue(model.connectOutputOnA(mqttUri(TOPIC_OUT_A), writeCurrentValue)); + assertTrue(b1.connectOutputOnB(mqttUri(TOPIC_OUT_B1), writeCurrentValue)); + assertTrue(b2.connectOutputOnB(mqttUri(TOPIC_OUT_B2), writeCurrentValue)); + } + + @Override + protected void communicateSendInitialValue() throws InterruptedException { + // check initial value + TestUtils.waitForMqtt(); + checkData(1, "aStart", + "bStartPostfix", + "bStartPostfix"); + + // send and check new value + sendData("101"); + checkData(2, "a101", + "b101Postfix", + "b101Postfix"); + + // send and check same value + sendData("101"); + checkData(2, "a101", + "b101Postfix", + "b101Postfix"); + + // send and check new value + sendData("201"); + checkData(3, "a201", + "b201Postfix", + "b201Postfix"); + } + + @Override + protected void communicateOnlyUpdatedValue() throws InterruptedException { + // check initial value + TestUtils.waitForMqtt(); + checkData(0, null, + null, + null); + + // send and check new value + sendData("102"); + checkData(1, "a102", + "b102Postfix", + "b102Postfix"); + + // send and check same value + sendData("102"); + checkData(1, "a102", + "b102Postfix", + "b102Postfix"); + + // send and check new value + sendData("202"); + checkData(2, "a202", + "b202Postfix", + "b202Postfix"); + + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } + + private void sendData(String input) throws InterruptedException { + publisher.publish(TOPIC_IN, input.getBytes()); + TestUtils.waitForMqtt(); + } + + private void checkData(int expectedNumberOfValues, String expectedLastAValue, + String expectedLastB1Value, String expectedLastB2Value) { + dataA.assertEqualData(expectedNumberOfValues, expectedLastAValue); + dataB1.assertEqualData(expectedNumberOfValues, expectedLastB1Value); + dataB2.assertEqualData(expectedNumberOfValues, expectedLastB2Value); + } + + private static class ReceiverData { + String lastStringValue; + int numberOfStringValues = 0; + + public void assertEqualData(int expectedNumberOfValues, String expectedLastValue) { + assertEquals(expectedNumberOfValues, this.numberOfStringValues); + assertEquals(expectedLastValue, this.lastStringValue); + } + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java new file mode 100644 index 0000000000000000000000000000000000000000..52d750223efb102f73e5f63d83aaf8c85ae6847f --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java @@ -0,0 +1,231 @@ +package org.jastadd.ragconnect.tests; + +import mapping.ast.A; +import mapping.ast.BoxedTypes; +import mapping.ast.MqttHandler; +import mapping.ast.NativeTypes; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test case "mapping". + * + * @author rschoene - Initial contribution + */ +public class MappingTest extends AbstractMqttTest { + + private static final String TOPIC_INPUT = "input"; + private static final String TOPIC_WRITE_NATIVE_INT = "native/int"; + private static final String TOPIC_WRITE_NATIVE_SHORT = "native/short"; + private static final String TOPIC_WRITE_NATIVE_LONG = "native/long"; + private static final String TOPIC_WRITE_NATIVE_FLOAT = "native/float"; + private static final String TOPIC_WRITE_NATIVE_DOUBLE = "native/double"; + private static final String TOPIC_WRITE_NATIVE_CHAR = "native/char"; + private static final String TOPIC_WRITE_NATIVE_BOOLEAN = "native/boolean"; + + private A model; + private NativeTypes natives; + private BoxedTypes boxes; + private MqttHandler handler; + private ReceiverData data; + + @Override + protected void createModel() { + model = new A(); + natives = new NativeTypes(); + natives.setDriver("1"); + boxes = new BoxedTypes(); + model.setNativeTypes(natives); + model.setBoxedTypes(boxes); + } + + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + natives.addNativeIntDependency(natives); + natives.addNativeShortDependency(natives); + natives.addNativeLongDependency(natives); + natives.addNativeFloatDependency(natives); + natives.addNativeDoubleDependency(natives); + natives.addNativeCharDependency(natives); + natives.addNativeBooleanDependency(natives); + + data = new ReceiverData(); + handler.newConnection(TOPIC_WRITE_NATIVE_INT, bytes -> { + data.numberOfNativeIntValues += 1; + data.lastNativeIntValue = TestUtils.DefaultMappings.BytesToInt(bytes); + }); + handler.newConnection(TOPIC_WRITE_NATIVE_SHORT, bytes -> { + data.numberOfNativeShortValues += 1; + data.lastNativeShortValue = TestUtils.DefaultMappings.BytesToShort(bytes); + }); + handler.newConnection(TOPIC_WRITE_NATIVE_LONG, bytes -> { + data.numberOfNativeLongValues += 1; + data.lastNativeLongValue = TestUtils.DefaultMappings.BytesToLong(bytes); + }); + handler.newConnection(TOPIC_WRITE_NATIVE_FLOAT, bytes -> { + data.numberOfNativeFloatValues += 1; + data.lastNativeFloatValue = TestUtils.DefaultMappings.BytesToFloat(bytes); + }); + handler.newConnection(TOPIC_WRITE_NATIVE_DOUBLE, bytes -> { + data.numberOfNativeDoubleValues += 1; + data.lastNativeDoubleValue = TestUtils.DefaultMappings.BytesToDouble(bytes); + }); + handler.newConnection(TOPIC_WRITE_NATIVE_CHAR, bytes -> { + data.numberOfNativeCharValues += 1; + data.lastNativeCharValue = TestUtils.DefaultMappings.BytesToChar(bytes); + }); + handler.newConnection(TOPIC_WRITE_NATIVE_BOOLEAN, bytes -> { + data.numberOfNativeBooleanValues += 1; + data.lastNativeBooleanValue = TestUtils.DefaultMappings.BytesToBool(bytes); + }); + + assertTrue(natives.connectWriteIntValue(mqttUri(TOPIC_WRITE_NATIVE_INT), writeCurrentValue)); + assertTrue(natives.connectWriteShortValue(mqttUri(TOPIC_WRITE_NATIVE_SHORT), writeCurrentValue)); + assertTrue(natives.connectWriteLongValue(mqttUri(TOPIC_WRITE_NATIVE_LONG), writeCurrentValue)); + assertTrue(natives.connectWriteFloatValue(mqttUri(TOPIC_WRITE_NATIVE_FLOAT), writeCurrentValue)); + assertTrue(natives.connectWriteDoubleValue(mqttUri(TOPIC_WRITE_NATIVE_DOUBLE), writeCurrentValue)); + assertTrue(natives.connectWriteCharValue(mqttUri(TOPIC_WRITE_NATIVE_CHAR), writeCurrentValue)); + assertTrue(natives.connectWriteBooleanValue(mqttUri(TOPIC_WRITE_NATIVE_BOOLEAN), writeCurrentValue)); + + assertTrue(natives.connectIntValue(mqttUri(TOPIC_INPUT))); + assertTrue(natives.connectShortValue(mqttUri(TOPIC_INPUT))); + assertTrue(natives.connectLongValue(mqttUri(TOPIC_INPUT))); + assertTrue(natives.connectFloatValue(mqttUri(TOPIC_INPUT))); + assertTrue(natives.connectDoubleValue(mqttUri(TOPIC_INPUT))); + assertTrue(natives.connectCharValue(mqttUri(TOPIC_INPUT))); + assertTrue(natives.connectBooleanValue(mqttUri(TOPIC_INPUT))); + + assertTrue(boxes.connectIntValue(mqttUri(TOPIC_INPUT))); + assertTrue(boxes.connectShortValue(mqttUri(TOPIC_INPUT))); + assertTrue(boxes.connectLongValue(mqttUri(TOPIC_INPUT))); + assertTrue(boxes.connectFloatValue(mqttUri(TOPIC_INPUT))); + assertTrue(boxes.connectDoubleValue(mqttUri(TOPIC_INPUT))); + assertTrue(boxes.connectCharValue(mqttUri(TOPIC_INPUT))); + assertTrue(boxes.connectBooleanValue(mqttUri(TOPIC_INPUT))); + } + + @Override + protected void communicateSendInitialValue() throws InterruptedException { + checkSendData(1, 1, (short) 1, 1, 1.01f, 1.01d, (char) 1, 1, false); + // no check for initial received data (no input set yet) + + // send first value + sendAndSetData("21", "31"); + checkSendData(2, 21, (short) 21, 21, 21.01f, 21.01d, (char) 21, 1, false); + checkReceiveData(31, (short) 31, 31, 31.01f, 31.01d, (char) 31, true); + + // send same value + sendAndSetData("21", "31"); + checkSendData(2, 21, (short) 21, 21, 21.01f, 21.01d, (char) 21, 1, false); + checkReceiveData(31, (short) 31, 31, 31.01f, 31.01d, (char) 31, true); + + // send new value + sendAndSetData("22", "32"); + checkSendData(3, 22, (short) 22, 22, 22.01f, 22.01d, (char) 22, 2, true); + checkReceiveData(32, (short) 32, 32, 32.01f, 32.01d, (char) 32, true); + } + + @Override + protected void communicateOnlyUpdatedValue() throws InterruptedException { + checkSendData(0, 0, (short) 0, 0, 0f, 0d, (char) 0, 0, false); + // no check for initial received data (no input set yet) + + // send first value + sendAndSetData("41", "51"); + checkSendData(1, 41, (short) 41, 41, 41.01f, 41.01d, (char) 41, 1, true); + checkReceiveData(51, (short) 51, 51, 51.01f, 51.01d, (char) 51, true); + + // send same value + sendAndSetData("41", "51"); + checkSendData(1, 41, (short) 41, 41, 41.01f, 41.01d, (char) 41, 1, true); + checkReceiveData(51, (short) 51, 51, 51.01f, 51.01d, (char) 51, true); + + // send new value + sendAndSetData("42", "52"); + checkSendData(2, 42, (short) 42, 42, 42.01f, 42.01d, (char) 42, 1, true); + checkReceiveData(52, (short) 52, 52, 52.01f, 52.01d, (char) 52, true); + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } + + private void sendAndSetData(String driver, String input) { + natives.setDriver(driver); + publisher.publish(TOPIC_INPUT, input.getBytes()); + } + + private void checkSendData(int expectedNumberOfValues, int expectedInt, short expectedShort, long expectedLong, float expectedFloat, double expectedDouble, char expectedChar, int expectedNumberOfBooleanValues, boolean expectedBoolean) throws InterruptedException { + TestUtils.waitForMqtt(); + assertEquals(expectedNumberOfValues, data.numberOfNativeIntValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeShortValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeLongValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeFloatValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeDoubleValues); + assertEquals(expectedNumberOfValues, data.numberOfNativeCharValues); + assertEquals(expectedNumberOfBooleanValues, data.numberOfNativeBooleanValues); + + if (expectedNumberOfValues == 0) { + return; + } + assertEquals(expectedInt, data.lastNativeIntValue); + assertEquals(expectedShort, data.lastNativeShortValue); + assertEquals(expectedLong, data.lastNativeLongValue); + assertEquals(expectedFloat, data.lastNativeFloatValue, TestUtils.DELTA); + assertEquals(expectedDouble, data.lastNativeDoubleValue, TestUtils.DELTA); + assertEquals(expectedChar, data.lastNativeCharValue); + assertEquals(expectedBoolean, data.lastNativeBooleanValue); + } + + private void checkReceiveData(int expectedInt, short expectedShort, long expectedLong, float expectedFloat, double expectedDouble, char expectedChar, boolean expectedBoolean) { + assertEquals(expectedInt, natives.getIntValue()); + assertEquals(expectedShort, natives.getShortValue()); + assertEquals(expectedLong, natives.getLongValue()); + assertEquals(expectedFloat, natives.getFloatValue(), TestUtils.DELTA); + assertEquals(expectedDouble, natives.getDoubleValue(), TestUtils.DELTA); + assertEquals(expectedChar, natives.getCharValue()); + assertEquals(expectedBoolean, natives.getBooleanValue()); + + assertEquals(expectedInt, boxes.getIntValue()); + assertEquals(expectedShort, boxes.getShortValue()); + assertEquals(expectedLong, boxes.getLongValue()); + assertEquals(expectedFloat, boxes.getFloatValue(), TestUtils.DELTA); + assertEquals(expectedDouble, boxes.getDoubleValue(), TestUtils.DELTA); + assertEquals(expectedChar, boxes.getCharValue()); + assertEquals(expectedBoolean, boxes.getBooleanValue()); + } + + private static class ReceiverData { + int lastNativeIntValue; + int numberOfNativeIntValues = 0; + short lastNativeShortValue; + int numberOfNativeShortValues = 0; + long lastNativeLongValue; + int numberOfNativeLongValues = 0; + float lastNativeFloatValue; + int numberOfNativeFloatValues = 0; + double lastNativeDoubleValue; + int numberOfNativeDoubleValues = 0; + char lastNativeCharValue; + int numberOfNativeCharValues = 0; + boolean lastNativeBooleanValue; + int numberOfNativeBooleanValues = 0; + } + +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MqttHandlerTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MqttHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..091990a64a31c01a6f2c59b934cfe15b6fb76ece --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MqttHandlerTest.java @@ -0,0 +1,72 @@ +package org.jastadd.ragconnect.tests; + +import example.ast.MqttHandler; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Testing the {@link MqttHandler} used in the "example" test case. + * + * @author rschoene - Initial contribution + */ +@Tag("mqtt") +public class MqttHandlerTest { + + @Test + public void defaultBehaviour() { + MqttHandler handler = new MqttHandler(); + try { + handler.setHost(TestUtils.getMqttHost()); + } catch (IOException e) { + fail("Fail during setHost", e); + } + boolean ready = handler.waitUntilReady(2, TimeUnit.SECONDS); + assertTrue(ready); + handler.close(); + } + + @Test + public void testWelcomeMessage() throws Exception { + MqttHandler welcomeMessageSubscriber = new MqttHandler(); + List<String> receivedMessages = new ArrayList<>(); + welcomeMessageSubscriber.setHost(TestUtils.getMqttHost()); + assertTrue(welcomeMessageSubscriber.waitUntilReady(2, TimeUnit.SECONDS)); + welcomeMessageSubscriber.newConnection("components", bytes -> receivedMessages.add(new String(bytes))); + assertThat(receivedMessages).isEmpty(); + + MqttHandler handler = new MqttHandler(); + handler.setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + TestUtils.waitForMqtt(); + + assertEquals(1, receivedMessages.size()); + } + + @Test + public void testDontSendWelcomeMessage() throws Exception { + MqttHandler welcomeMessageSubscriber = new MqttHandler(); + List<String> receivedMessages = new ArrayList<>(); + welcomeMessageSubscriber.setHost(TestUtils.getMqttHost()); + assertTrue(welcomeMessageSubscriber.waitUntilReady(2, TimeUnit.SECONDS)); + welcomeMessageSubscriber.newConnection("components", bytes -> receivedMessages.add(new String(bytes))); + assertThat(receivedMessages).isEmpty(); + + MqttHandler handler = new MqttHandler().dontSendWelcomeMessage(); + handler.setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + TestUtils.waitForMqtt(); + + assertThat(receivedMessages).isEmpty(); + } + +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java index 2940aacbf26ae1a6e63cd7c763868d6f48639217..76eac958448c69e0ac1158074a4ddac12f4027f9 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java @@ -37,78 +37,6 @@ public class Read1Write2Test extends AbstractMqttTest { private ReceiverData dataOther1; private ReceiverData dataOther2; - @Override - public void closeConnections() { - if (handler != null) { - handler.close(); - } - if (model != null) { - model.ragconnectCloseConnections(); - } - } - - - - @Override - protected void communicateSendInitialValue() throws InterruptedException { - // check initial value - TestUtils.waitForMqtt(); - checkData(1, Integer.parseInt(INITIAL_VALUE), prefixed(INITIAL_VALUE), 1, Integer.parseInt(INITIAL_VALUE), prefixed(INITIAL_VALUE)); - - // set new value - sendData("2", "3"); - - // check new value - TestUtils.waitForMqtt(); - checkData(2, 2, prefixed("2"), 2, 3, prefixed("3")); - - // set new value - sendData("4", "4"); - - // check new value - TestUtils.waitForMqtt(); - checkData(3, 4, prefixed("4"), 3, 4, prefixed("4")); - - // set new value only for same - setDataOnlySame("77"); - - // check new value - TestUtils.waitForMqtt(); - checkData(4, 77, prefixed("77"), 3, 4, prefixed("4")); - } - - private String prefixed(String s) { - return "prefix" + s; - } - - @Override - protected void communicateOnlyUpdatedValue() throws InterruptedException { -// check initial value - TestUtils.waitForMqtt(); - checkData(0, null, null, 0, null, null); - - // set new value - sendData("2", "3"); - - // check new value - TestUtils.waitForMqtt(); - checkData(1, 2, prefixed("2"), 1, 3, prefixed("3")); - - // set new value - sendData("4", "4"); - - // check new value - TestUtils.waitForMqtt(); - checkData(2, 4, prefixed("4"), 2, 4, prefixed("4")); - - // set new value only for same - setDataOnlySame("77"); - - // check new value - TestUtils.waitForMqtt(); - checkData(3, 77, prefixed("77"), 2, 4, prefixed("4")); - } - @Override protected void createModel() { // Setting value for Input without dependencies does not trigger any updates @@ -147,45 +75,107 @@ public class Read1Write2Test extends AbstractMqttTest { handler.newConnection(TOPIC_SAME_WRITE_INT, bytes -> { dataSame.numberOfIntValues += 1; - dataSame.lastIntValue = java.nio.ByteBuffer.wrap(bytes).getInt(); + dataSame.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes); }); handler.newConnection(TOPIC_SAME_WRITE_STRING, bytes -> { dataSame.numberOfStringValues += 1; - dataSame.lastStringValue = new String(bytes); + dataSame.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); }); handler.newConnection(TOPIC_DIFFERENT_WRITE1_INT, bytes -> { dataOther1.numberOfIntValues += 1; - dataOther1.lastIntValue = java.nio.ByteBuffer.wrap(bytes).getInt(); + dataOther1.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes); }); handler.newConnection(TOPIC_DIFFERENT_WRITE1_STRING, bytes -> { dataOther1.numberOfStringValues += 1; - dataOther1.lastStringValue = new String(bytes); + dataOther1.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); }); handler.newConnection(TOPIC_DIFFERENT_WRITE2_INT, bytes -> { dataOther2.numberOfIntValues += 1; - dataOther2.lastIntValue = java.nio.ByteBuffer.wrap(bytes).getInt(); + dataOther2.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes); }); handler.newConnection(TOPIC_DIFFERENT_WRITE2_STRING, bytes -> { dataOther2.numberOfStringValues += 1; - dataOther2.lastStringValue = new String(bytes); + dataOther2.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); }); - onSameNonterminal.connectInput(mqttUri(TOPIC_SAME_READ)); - onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), writeCurrentValue); - onSameNonterminal.connectOutString(mqttUri(TOPIC_SAME_WRITE_STRING), writeCurrentValue); + assertTrue(onSameNonterminal.connectInput(mqttUri(TOPIC_SAME_READ))); + assertTrue(onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), writeCurrentValue)); + assertTrue(onSameNonterminal.connectOutString(mqttUri(TOPIC_SAME_WRITE_STRING), writeCurrentValue)); - onDifferentNonterminal.connectInput(mqttUri(TOPIC_DIFFERENT_READ)); - other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), writeCurrentValue); - other1.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE1_STRING), writeCurrentValue); - other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), writeCurrentValue); - other2.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE2_STRING), writeCurrentValue); + assertTrue(onDifferentNonterminal.connectInput(mqttUri(TOPIC_DIFFERENT_READ))); + assertTrue(other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), writeCurrentValue)); + assertTrue(other1.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE1_STRING), writeCurrentValue)); + assertTrue(other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), writeCurrentValue)); + assertTrue(other2.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE2_STRING), writeCurrentValue)); + } + + @Override + protected void communicateSendInitialValue() throws InterruptedException { + // check initial value + checkData(1, Integer.parseInt(INITIAL_VALUE), prefixed(INITIAL_VALUE), 1, Integer.parseInt(INITIAL_VALUE), prefixed(INITIAL_VALUE)); + + // set new value + sendData("2", "3"); + + // check new value + checkData(2, 2, prefixed("2"), 2, 3, prefixed("3")); + + // set new value + sendData("4", "4"); + + // check new value + checkData(3, 4, prefixed("4"), 3, 4, prefixed("4")); + + // set new value only for same + setDataOnlySame("77"); + + // check new value + checkData(4, 77, prefixed("77"), 3, 4, prefixed("4")); + } + + @Override + protected void communicateOnlyUpdatedValue() throws InterruptedException { +// check initial value + checkData(0, null, null, 0, null, null); + + // set new value + sendData("2", "3"); + + // check new value + checkData(1, 2, prefixed("2"), 1, 3, prefixed("3")); + + // set new value + sendData("4", "4"); + + // check new value + checkData(2, 4, prefixed("4"), 2, 4, prefixed("4")); + + // set new value only for same + setDataOnlySame("78"); + + // check new value + checkData(3, 78, prefixed("78"), 2, 4, prefixed("4")); + } + + @Override + public void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } + + private String prefixed(String s) { + return "prefix" + s; } private void sendData(String inputSame, String inputDifferent) { - handler.publish(TOPIC_SAME_READ, inputSame.getBytes()); - handler.publish(TOPIC_DIFFERENT_READ, inputDifferent.getBytes()); + publisher.publish(TOPIC_SAME_READ, inputSame.getBytes()); + publisher.publish(TOPIC_DIFFERENT_READ, inputDifferent.getBytes()); } private void setDataOnlySame(String inputSame) { @@ -194,32 +184,13 @@ public class Read1Write2Test extends AbstractMqttTest { private void checkData(int numberOfSameValues, Integer lastSameIntValue, String lastSameStringValue, int numberOfDifferentValues, Integer lastDifferentIntValue, - String lastDifferentStringValue) { - /* the value "-2" is never used in the test, so a test will always fail comparing to this value - especially, it is not the initial value */ - ReceiverData expectedDataSame = ReceiverData.of( - numberOfSameValues, - lastSameIntValue != null ? lastSameIntValue : -2, - lastSameStringValue); - compareData(expectedDataSame, dataSame); - ReceiverData expectedDataDifferent = ReceiverData.of( - numberOfDifferentValues, - lastDifferentIntValue != null ? lastDifferentIntValue : -2, - lastDifferentStringValue); - compareData(expectedDataDifferent, dataOther1); - compareData(expectedDataDifferent, dataOther2); - } - - private void compareData(ReceiverData expectedData, - ReceiverData actual) { - assertEquals(expectedData.numberOfIntValues, actual.numberOfIntValues); - assertEquals(expectedData.numberOfStringValues, actual.numberOfStringValues); - if (expectedData.numberOfIntValues > 0) { - assertEquals(expectedData.lastIntValue, actual.lastIntValue); - } - if (expectedData.numberOfStringValues > 0) { - assertEquals(expectedData.lastStringValue, actual.lastStringValue); - } + String lastDifferentStringValue) throws InterruptedException { + TestUtils.waitForMqtt(); + dataSame.assertEqualData(numberOfSameValues, lastSameIntValue, lastSameStringValue); + dataOther1.assertEqualData(numberOfDifferentValues, + lastDifferentIntValue, lastDifferentStringValue); + dataOther2.assertEqualData(numberOfDifferentValues, + lastDifferentIntValue, lastDifferentStringValue); } private static class ReceiverData { @@ -228,14 +199,19 @@ public class Read1Write2Test extends AbstractMqttTest { String lastStringValue; int numberOfStringValues = 0; - static ReceiverData of(int numberOfValues, int lastIntValue, String lastStringValue) { - ReceiverData result = new ReceiverData(); - result.lastIntValue = lastIntValue; - result.lastStringValue = lastStringValue; - result.numberOfIntValues = numberOfValues; - result.numberOfStringValues = numberOfValues; - return result; + void assertEqualData(int expectedNumberOfValues, + Integer expectedLastIntValue, + String expectedLastStringValue) { + /* the value "-2" is never used in the test, so a test will always fail comparing to this value + especially, it is not the initial value */ + assertEquals(expectedNumberOfValues, this.numberOfIntValues); + assertEquals(expectedNumberOfValues, this.numberOfStringValues); + if (expectedNumberOfValues > 0) { + assertEquals(expectedLastIntValue != null ? expectedLastIntValue : -2, this.lastIntValue); + } + if (expectedNumberOfValues > 0) { + assertEquals(expectedLastStringValue, this.lastStringValue); + } } } - } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java index 4790b173bab7cab2d6022e35c40125af7b79ed5f..f6a0d33a4b70689a1c883f866d33442defd2b786 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java @@ -37,21 +37,71 @@ public class Read2Write1Test extends AbstractMqttTest { private ReceiverData dataOther2; @Override - public void closeConnections() { - if (handler != null) { - handler.close(); - } - if (model != null) { - model.ragconnectCloseConnections(); - } + protected void createModel() { + // Setting value for Input without dependencies does not trigger any updates + model = new A(); + + onSameNonterminal = new OnSameNonterminal(); + model.setOnSameNonterminal(onSameNonterminal); + onSameNonterminal.setInput1(INITIAL_VALUE); + onSameNonterminal.setInput2(INITIAL_VALUE); + + onDifferentNonterminal = new OnDifferentNonterminal(); + other1 = new TheOther(); + other2 = new TheOther(); + onDifferentNonterminal.addTheOther(other1); + onDifferentNonterminal.addTheOther(other2); + model.setOnDifferentNonterminal(onDifferentNonterminal); + onDifferentNonterminal.setInput1(INITIAL_VALUE); + onDifferentNonterminal.setInput2(INITIAL_VALUE); } + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + onSameNonterminal.addInt1Dependency(onSameNonterminal); + onSameNonterminal.addInt2Dependency(onSameNonterminal); + other1.addInt1Dependency(onDifferentNonterminal); + other1.addInt2Dependency(onDifferentNonterminal); + other2.addInt1Dependency(onDifferentNonterminal); + other2.addInt2Dependency(onDifferentNonterminal); + + dataSame = new Read2Write1Test.ReceiverData(); + dataOther1 = new Read2Write1Test.ReceiverData(); + dataOther2 = new Read2Write1Test.ReceiverData(); + + handler.newConnection(TOPIC_SAME_WRITE_INT, bytes -> { + dataSame.numberOfIntValues += 1; + dataSame.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes); + }); + + handler.newConnection(TOPIC_DIFFERENT_WRITE1_INT, bytes -> { + dataOther1.numberOfIntValues += 1; + dataOther1.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes); + }); + + handler.newConnection(TOPIC_DIFFERENT_WRITE2_INT, bytes -> { + dataOther2.numberOfIntValues += 1; + dataOther2.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes); + }); + + assertTrue(onSameNonterminal.connectInput1(mqttUri(TOPIC_SAME_READ1))); + assertTrue(onSameNonterminal.connectInput2(mqttUri(TOPIC_SAME_READ2))); + assertTrue(onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), writeCurrentValue)); + assertTrue(onDifferentNonterminal.connectInput1(mqttUri(TOPIC_DIFFERENT_READ1))); + assertTrue(onDifferentNonterminal.connectInput2(mqttUri(TOPIC_DIFFERENT_READ2))); + assertTrue(other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), writeCurrentValue)); + assertTrue(other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), writeCurrentValue)); + } @Override protected void communicateSendInitialValue() throws InterruptedException { // check initial value - TestUtils.waitForMqtt(); checkData(1, Integer.parseInt(INITIAL_VALUE + INITIAL_VALUE), 1, Integer.parseInt(INITIAL_VALUE + INITIAL_VALUE)); @@ -59,7 +109,6 @@ public class Read2Write1Test extends AbstractMqttTest { sendData(true, "2", true, "3"); // check new value. same: 2, 0. different: 3, 0. - TestUtils.waitForMqtt(); checkData(2, 20, 2, 30); @@ -67,27 +116,20 @@ public class Read2Write1Test extends AbstractMqttTest { sendData(false, "4", false, "4"); // check new value. same: 2, 4. different: 3, 4. - TestUtils.waitForMqtt(); checkData(3, 24, 3, 34); // set new value only for same - setDataOnlySame(true, "77"); + setDataOnlySame("77"); // check new value. same: 77, 4. different: 3, 4. - TestUtils.waitForMqtt(); checkData(4, 774, 3, 34); } - private String prefixed(String s) { - return "prefix" + s; - } - @Override protected void communicateOnlyUpdatedValue() throws InterruptedException { // check initial value - TestUtils.waitForMqtt(); checkData(0, null, 0, null); @@ -95,7 +137,6 @@ public class Read2Write1Test extends AbstractMqttTest { sendData(true, "2", true, "3"); // check new value. same: 2, 0. different: 3, 0. - TestUtils.waitForMqtt(); checkData(1, 20, 1, 30); @@ -103,129 +144,61 @@ public class Read2Write1Test extends AbstractMqttTest { sendData(false, "4", false, "4"); // check new value. same: 2, 4. different: 3, 4. - TestUtils.waitForMqtt(); checkData(2, 24, 2, 34); // set new value only for same - setDataOnlySame(true, "77"); + setDataOnlySame("78"); - // check new value. same: 77, 4. different: 3, 4. - TestUtils.waitForMqtt(); - checkData(3, 774, + // check new value. same: 78, 4. different: 3, 4. + checkData(3, 784, 2, 34); } @Override - protected void createModel() { - // Setting value for Input without dependencies does not trigger any updates - model = new A(); - - onSameNonterminal = new OnSameNonterminal(); - model.setOnSameNonterminal(onSameNonterminal); - onSameNonterminal.setInput1(INITIAL_VALUE); - onSameNonterminal.setInput2(INITIAL_VALUE); - - onDifferentNonterminal = new OnDifferentNonterminal(); - other1 = new TheOther(); - other2 = new TheOther(); - onDifferentNonterminal.addTheOther(other1); - onDifferentNonterminal.addTheOther(other2); - model.setOnDifferentNonterminal(onDifferentNonterminal); - onDifferentNonterminal.setInput1(INITIAL_VALUE); - onDifferentNonterminal.setInput2(INITIAL_VALUE); - } - - @Override - protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { - model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); - - handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); - assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); - - onSameNonterminal.addInt1Dependency(onSameNonterminal); - onSameNonterminal.addInt2Dependency(onSameNonterminal); - other1.addInt1Dependency(onDifferentNonterminal); - other1.addInt2Dependency(onDifferentNonterminal); - other2.addInt1Dependency(onDifferentNonterminal); - other2.addInt2Dependency(onDifferentNonterminal); - - dataSame = new Read2Write1Test.ReceiverData(); - dataOther1 = new Read2Write1Test.ReceiverData(); - dataOther2 = new Read2Write1Test.ReceiverData(); - - handler.newConnection(TOPIC_SAME_WRITE_INT, bytes -> { - dataSame.numberOfIntValues += 1; - dataSame.lastIntValue = java.nio.ByteBuffer.wrap(bytes).getInt(); - }); - - handler.newConnection(TOPIC_DIFFERENT_WRITE1_INT, bytes -> { - dataOther1.numberOfIntValues += 1; - dataOther1.lastIntValue = java.nio.ByteBuffer.wrap(bytes).getInt(); - }); - - handler.newConnection(TOPIC_DIFFERENT_WRITE2_INT, bytes -> { - dataOther2.numberOfIntValues += 1; - dataOther2.lastIntValue = java.nio.ByteBuffer.wrap(bytes).getInt(); - }); - - onSameNonterminal.connectInput1(mqttUri(TOPIC_SAME_READ1)); - onSameNonterminal.connectInput2(mqttUri(TOPIC_SAME_READ2)); - onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), writeCurrentValue); - - onDifferentNonterminal.connectInput1(mqttUri(TOPIC_DIFFERENT_READ1)); - onDifferentNonterminal.connectInput2(mqttUri(TOPIC_DIFFERENT_READ2)); - other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), writeCurrentValue); - other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), writeCurrentValue); + public void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } } private void sendData(boolean useSameInput1, String inputSame, boolean useDifferentInput1, String inputDifferent) { - handler.publish(useSameInput1 ? TOPIC_SAME_READ1 : TOPIC_SAME_READ2, + publisher.publish(useSameInput1 ? TOPIC_SAME_READ1 : TOPIC_SAME_READ2, inputSame.getBytes()); - handler.publish(useDifferentInput1 ? TOPIC_DIFFERENT_READ1 : TOPIC_DIFFERENT_READ2, + publisher.publish(useDifferentInput1 ? TOPIC_DIFFERENT_READ1 : TOPIC_DIFFERENT_READ2, inputDifferent.getBytes()); } - private void setDataOnlySame(boolean useSameInput1, String inputSame) { - handler.publish(useSameInput1 ? TOPIC_SAME_READ1 : TOPIC_DIFFERENT_READ2, - inputSame.getBytes()); + private void setDataOnlySame(String inputSame) { + publisher.publish(TOPIC_SAME_READ1, inputSame.getBytes()); } private void checkData(int numberOfSameValues, Integer lastSameIntValue, - int numberOfDifferentValues, Integer lastDifferentIntValue) { + int numberOfDifferentValues, Integer lastDifferentIntValue) + throws InterruptedException { + TestUtils.waitForMqtt(); /* the value "-2" is never used in the test, so a test will always fail comparing to this value especially, it is not the initial value */ - ReceiverData expectedDataSame = ReceiverData.of( - numberOfSameValues, - lastSameIntValue != null ? lastSameIntValue : -2 - ); - compareData(expectedDataSame, dataSame); - ReceiverData expectedDataDifferent = ReceiverData.of( - numberOfDifferentValues, - lastDifferentIntValue != null ? lastDifferentIntValue : -2 - ); - compareData(expectedDataDifferent, dataOther1); - compareData(expectedDataDifferent, dataOther2); - } - - private void compareData(ReceiverData expectedData, - ReceiverData actual) { - assertEquals(expectedData.numberOfIntValues, actual.numberOfIntValues); - if (expectedData.numberOfIntValues > 0) { - assertEquals(expectedData.lastIntValue, actual.lastIntValue); - } + dataSame.assertEqualData(numberOfSameValues, lastSameIntValue); + dataOther1.assertEqualData(numberOfDifferentValues, lastDifferentIntValue); + dataOther2.assertEqualData(numberOfDifferentValues, lastDifferentIntValue); } private static class ReceiverData { int lastIntValue; int numberOfIntValues = 0; - static ReceiverData of(int numberOfValues, int lastIntValue) { - ReceiverData result = new ReceiverData(); - result.lastIntValue = lastIntValue; - result.numberOfIntValues = numberOfValues; - return result; + void assertEqualData(int expectedNumberOfValues, Integer expectedLastIntValue) { + /* the value "-2" is never used in the test, so a test will always fail comparing to this value + especially, it is not the initial value */ + assertEquals(expectedNumberOfValues, this.numberOfIntValues); + if (expectedNumberOfValues > 0) { + assertEquals(expectedLastIntValue != null ? expectedLastIntValue : -2, this.lastIntValue); + } } } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RegressionTests.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RegressionTests.java new file mode 100644 index 0000000000000000000000000000000000000000..67c82177d96fe34c6379b9d62537f785292827df --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RegressionTests.java @@ -0,0 +1,65 @@ +package org.jastadd.ragconnect.tests; + +import org.junit.jupiter.api.Test; +import via.ast.A; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Regression tests for fixed issues. + * + * @author rschoene - Initial contribution + */ +public class RegressionTests { + + private static final String REGRESSION_TEST_OUTPUT_DIRECTORY = "regression-test/"; + + @Test + public void issue22() { + // use model of "via" test case as it uses both mqtt and rest as protocols + A a = new A(); + try { + // should fail because of missing scheme + assertFalse(a.connectBoth2BothInput("missing/scheme")); + + // should fail because of missing host + assertFalse(a.connectBoth2BothInput("mqtt://")); + + // should fail because of missing part + assertFalse(a.connectBoth2BothInput("mqtt://localhost")); + + // should fail because of unknown scheme + assertFalse(a.connectBoth2BothInput("badScheme://host/some/topic")); + } catch (IOException e) { + fail(e); + } + } + + @Test + public void issue27() throws IOException { + String grammarFile = "regression-tests/issue27/Test.relast"; + String connectFile = "regression-tests/issue27/Test.connect"; + grammarFile = ensureNoTrailingNewLine(grammarFile); + connectFile = ensureNoTrailingNewLine(connectFile); + TestUtils.runCompiler(grammarFile, Collections.singletonList(connectFile), "A", REGRESSION_TEST_OUTPUT_DIRECTORY, 0); + } + + private String ensureNoTrailingNewLine(String inputFileSuffix) throws IOException { + int dotIndex = inputFileSuffix.lastIndexOf('.'); + String outFileSuffix = inputFileSuffix.substring(0, dotIndex) + ".noNewLine" + inputFileSuffix.substring(dotIndex); + Path inputPath = Paths.get(TestUtils.INPUT_DIRECTORY_PREFIX).resolve(inputFileSuffix); + Path outputPath = Paths.get(TestUtils.INPUT_DIRECTORY_PREFIX).resolve(outFileSuffix); + + String content = Files.readString(inputPath); + Files.writeString(outputPath, content.stripTrailing(), StandardOpenOption.CREATE); + + return outFileSuffix; + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java index 143c74e984af40f9acf883ed7be2f3eb8adb253a..ce79190fe184f5b777366c25e9eff07cc8fc3576 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java @@ -1,12 +1,24 @@ package org.jastadd.ragconnect.tests; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jastadd.ragconnect.compiler.Compiler; +import org.junit.jupiter.api.Assertions; + import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + /** * Utility methods for tests. * @@ -14,7 +26,10 @@ import java.util.concurrent.TimeUnit; */ public class TestUtils { + private static final Logger logger = LogManager.getLogger(TestUtils.class); public static final double DELTA = 0.001d; + public static final String INPUT_DIRECTORY_PREFIX = "./src/test/01-input/"; + public static final String OUTPUT_DIRECTORY_PREFIX = "./src/test/02-after-ragconnect/"; public static String getMqttHost() { if (System.getenv("GITLAB_CI") != null) { @@ -38,6 +53,41 @@ public class TestUtils { return 1883; } + public static Path runCompiler(String grammarFile, Iterable<String> connectFiles, String rootNode, String outputDirectory, int expectedReturnValue) { + + assertThat(connectFiles).isNotEmpty(); + + Path outPath = Paths.get(OUTPUT_DIRECTORY_PREFIX) + .resolve(outputDirectory) + .resolve("Compiler.out"); + ensureCreated(outPath.getParent()); + + try { + logger.debug("user.dir: {}", System.getProperty("user.dir")); + List<String> args = new ArrayList<>() {{ + add("--o=" + OUTPUT_DIRECTORY_PREFIX + outputDirectory); + add("--rootNode=" + rootNode); + add("--verbose"); + add(INPUT_DIRECTORY_PREFIX + grammarFile); + }}; + connectFiles.forEach(connectFile -> args.add(INPUT_DIRECTORY_PREFIX + connectFile)); + + int returnValue = exec(Compiler.class, args.toArray(new String[0]), outPath.toFile()); + Assertions.assertEquals(expectedReturnValue, returnValue, "RagConnect did not return with value " + expectedReturnValue); + } catch (IOException | InterruptedException e) { + fail(e); + } + return outPath; + } + + private static void ensureCreated(Path directory) { + File directoryFile = directory.toFile(); + if (directoryFile.exists() && directoryFile.isDirectory()) { + return; + } + assertTrue(directoryFile.mkdirs()); + } + public static int exec(Class<?> klass, String[] args, File err) throws IOException, InterruptedException { String javaHome = System.getProperty("java.home"); @@ -61,13 +111,214 @@ public class TestUtils { return process.exitValue(); } - public static String readFile(String path, Charset encoding) + public static void testJaddContainReferenceToJackson(Path path, boolean shouldContain) { + try { + String content = Files.readString(path); + boolean actualContain = content.contains("com.fasterxml.jackson.databind.ObjectMapper"); + if (actualContain && !shouldContain) { + fail(path + " should not depend on jackson library, but does"); + } + if (!actualContain && shouldContain) { + fail(path + " does not depend on jackson library"); + } + } catch (IOException e) { + fail(e); + } + } + + public static String readFile(Path path, Charset encoding) throws IOException { - byte[] encoded = Files.readAllBytes(Paths.get(path)); + byte[] encoded = Files.readAllBytes(path); return new String(encoded, encoding); } - static void waitForMqtt() throws InterruptedException { - TimeUnit.SECONDS.sleep(2); + public static void waitForMqtt() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(1500); + } + + @SuppressWarnings({"unused", "rawtypes"}) + public static class DefaultMappings { + static class ReadNode extends defaultOnlyRead.ast.ASTNode { + public static boolean _apply__DefaultBytesToBooleanMapping(byte[] input) throws Exception { + return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToBooleanMapping(input); + } + public static int _apply__DefaultBytesToIntMapping(byte[] input) throws Exception { + return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToIntMapping(input); + } + public static short _apply__DefaultBytesToShortMapping(byte[] input) throws Exception { + return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToShortMapping(input); + } + public static long _apply__DefaultBytesToLongMapping(byte[] input) throws Exception { + return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToLongMapping(input); + } + public static float _apply__DefaultBytesToFloatMapping(byte[] input) throws Exception { + return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToFloatMapping(input); + } + public static double _apply__DefaultBytesToDoubleMapping(byte[] input) throws Exception { + return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToDoubleMapping(input); + } + public static char _apply__DefaultBytesToCharMapping(byte[] input) throws Exception { + return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToCharMapping(input); + } + public static String _apply__DefaultBytesToStringMapping(byte[] input) throws Exception { + return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToStringMapping(input); + } + } + + static class WriteNode extends defaultOnlyWrite.ast.ASTNode { + public static byte[] _apply__DefaultBooleanToBytesMapping(boolean input) throws Exception { + return defaultOnlyWrite.ast.ASTNode._apply__DefaultBooleanToBytesMapping(input); + } + public static byte[] _apply__DefaultIntToBytesMapping(int input) throws Exception { + return defaultOnlyWrite.ast.ASTNode._apply__DefaultIntToBytesMapping(input); + } + public static byte[] _apply__DefaultShortToBytesMapping(short input) throws Exception { + return defaultOnlyWrite.ast.ASTNode._apply__DefaultShortToBytesMapping(input); + } + public static byte[] _apply__DefaultLongToBytesMapping(long input) throws Exception { + return defaultOnlyWrite.ast.ASTNode._apply__DefaultLongToBytesMapping(input); + } + public static byte[] _apply__DefaultFloatToBytesMapping(float input) throws Exception { + return defaultOnlyWrite.ast.ASTNode._apply__DefaultFloatToBytesMapping(input); + } + public static byte[] _apply__DefaultDoubleToBytesMapping(double input) throws Exception { + return defaultOnlyWrite.ast.ASTNode._apply__DefaultDoubleToBytesMapping(input); + } + public static byte[] _apply__DefaultCharToBytesMapping(char input) throws Exception { + return defaultOnlyWrite.ast.ASTNode._apply__DefaultCharToBytesMapping(input); + } + public static byte[] _apply__DefaultStringToBytesMapping(String input) throws Exception { + return defaultOnlyWrite.ast.ASTNode._apply__DefaultStringToBytesMapping(input); + } + } + + public static boolean BytesToBool(byte[] input) { + try { + return ReadNode._apply__DefaultBytesToBooleanMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + public static int BytesToInt(byte[] input) { + try { + return ReadNode._apply__DefaultBytesToIntMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + public static short BytesToShort(byte[] input) { + try { + return ReadNode._apply__DefaultBytesToShortMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + public static long BytesToLong(byte[] input) { + try { + return ReadNode._apply__DefaultBytesToLongMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + public static float BytesToFloat(byte[] input) { + try { + return ReadNode._apply__DefaultBytesToFloatMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + public static double BytesToDouble(byte[] input) { + try { + return ReadNode._apply__DefaultBytesToDoubleMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + public static char BytesToChar(byte[] input) { + try { + return ReadNode._apply__DefaultBytesToCharMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + public static String BytesToString(byte[] input) { + try { + return ReadNode._apply__DefaultBytesToStringMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + public static byte[] BoolToBytes(boolean input) { + try { + return WriteNode._apply__DefaultBooleanToBytesMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + public static byte[] IntToBytes(int input) { + try { + return WriteNode._apply__DefaultIntToBytesMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + public static byte[] ShortToBytes(short input) { + try { + return WriteNode._apply__DefaultShortToBytesMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + public static byte[] LongToBytes(long input) { + try { + return WriteNode._apply__DefaultLongToBytesMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + public static byte[] FloatToBytes(float input) { + try { + return WriteNode._apply__DefaultFloatToBytesMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + public static byte[] DoubleToBytes(double input) { + try { + return WriteNode._apply__DefaultDoubleToBytesMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + public static byte[] CharToBytes(char input) { + try { + return WriteNode._apply__DefaultCharToBytesMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + public static byte[] StringToBytes(String input) { + try { + return WriteNode._apply__DefaultStringToBytesMapping(input); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } } } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java index bc68f5bdb8b19167deb50fa54b52e933235bceb3..6d99149688b33d8401037fb358f736c2509b0921 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java @@ -39,13 +39,60 @@ public class TokenValueSendTest extends AbstractMqttTest { private ReceiverData dataThreeOther; @Override - public void closeConnections() { - if (handler != null) { - handler.close(); - } - if (model != null) { - model.ragconnectCloseConnections(); - } + protected void createModel() { + // Setting value for Input without dependencies does not trigger any updates + model = new A(); + + one = new OnlySend(); + one.setValue(INITIAL_VALUE); + model.setOnlySend(one); + + two = new ReceiveAndSend(); + two.setValue(INITIAL_VALUE); + model.setReceiveAndSend(two); + + three = new ReceiveSendAndDepend(); + three.setValue(INITIAL_VALUE); + model.setReceiveSendAndDepend(three); + } + + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + three.addDependency1(three); + + dataOne = new ReceiverData(); + dataTwo = new ReceiverData(); + dataThree = new ReceiverData(); + dataThreeOther = new ReceiverData(); + + handler.newConnection(TOPIC_SEND_ONE, bytes -> { + dataOne.numberOfStringValues += 1; + dataOne.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); + }); + handler.newConnection(TOPIC_SEND_TWO, bytes -> { + dataTwo.numberOfStringValues += 1; + dataTwo.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); + }); + + handler.newConnection(TOPIC_SEND_THREE_VALUE, bytes -> { + dataThree.numberOfStringValues += 1; + dataThree.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); + }); + handler.newConnection(TOPIC_SEND_THREE_OTHER, bytes -> { + dataThreeOther.numberOfStringValues += 1; + dataThreeOther.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); + }); + + assertTrue(one.connectValue(mqttUri(TOPIC_SEND_ONE), writeCurrentValue)); + assertTrue(two.connectValue(mqttUri(TOPIC_RECEIVE_TWO))); + assertTrue(two.connectValue(mqttUri(TOPIC_SEND_TWO), writeCurrentValue)); + assertTrue(three.connectValue(mqttUri(TOPIC_RECEIVE_THREE_VALUE))); + assertTrue(three.connectValue(mqttUri(TOPIC_SEND_THREE_VALUE), writeCurrentValue)); + assertTrue(three.connectOtherOutput(mqttUri(TOPIC_SEND_THREE_OTHER), writeCurrentValue)); } @Override @@ -169,65 +216,18 @@ public class TokenValueSendTest extends AbstractMqttTest { } @Override - protected void createModel() { - // Setting value for Input without dependencies does not trigger any updates - model = new A(); - - one = new OnlySend(); - one.setValue(INITIAL_VALUE); - model.setOnlySend(one); - - two = new ReceiveAndSend(); - two.setValue(INITIAL_VALUE); - model.setReceiveAndSend(two); - - three = new ReceiveSendAndDepend(); - three.setValue(INITIAL_VALUE); - model.setReceiveSendAndDepend(three); - } - - protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { - model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); - - handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); - assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); - - three.addDependency1(three); - - dataOne = new ReceiverData(); - dataTwo = new ReceiverData(); - dataThree = new ReceiverData(); - dataThreeOther = new ReceiverData(); - - handler.newConnection(TOPIC_SEND_ONE, bytes -> { - dataOne.numberOfStringValues += 1; - dataOne.lastStringValue = new String(bytes); - }); - handler.newConnection(TOPIC_SEND_TWO, bytes -> { - dataTwo.numberOfStringValues += 1; - dataTwo.lastStringValue = new String(bytes); - }); - - handler.newConnection(TOPIC_SEND_THREE_VALUE, bytes -> { - dataThree.numberOfStringValues += 1; - dataThree.lastStringValue = new String(bytes); - }); - handler.newConnection(TOPIC_SEND_THREE_OTHER, bytes -> { - dataThreeOther.numberOfStringValues += 1; - dataThreeOther.lastStringValue = new String(bytes); - }); - - one.connectValue(mqttUri(TOPIC_SEND_ONE), writeCurrentValue); - two.connectValue(mqttUri(TOPIC_RECEIVE_TWO)); - two.connectValue(mqttUri(TOPIC_SEND_TWO), writeCurrentValue); - three.connectValue(mqttUri(TOPIC_RECEIVE_THREE_VALUE)); - three.connectValue(mqttUri(TOPIC_SEND_THREE_VALUE), writeCurrentValue); - three.connectOtherOutput(mqttUri(TOPIC_SEND_THREE_OTHER), writeCurrentValue); + public void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } } private void sendData(String inputTwo, String inputThree) { - handler.publish(TOPIC_RECEIVE_TWO, inputTwo.getBytes()); - handler.publish(TOPIC_RECEIVE_THREE_VALUE, inputThree.getBytes()); + publisher.publish(TOPIC_RECEIVE_TWO, inputTwo.getBytes()); + publisher.publish(TOPIC_RECEIVE_THREE_VALUE, inputThree.getBytes()); } private void setData(String inputOne, String inputTwo, String inputThree) { diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java index d7f346252639630d5818af4ceeef314b7ccecd9e..64ad5fbd147ba6c7e22d24ba7f92fe8d7114ced7 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java @@ -2,11 +2,11 @@ package org.jastadd.ragconnect.tests; import tutorial.ast.A; import tutorial.ast.B; -import tutorial.ast.MqttHandler; import java.io.IOException; import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Testcase "Tutorial". @@ -15,21 +15,10 @@ import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; */ public class TutorialTest extends AbstractMqttTest { - private MqttHandler handler; private A a; private B b1; private B b2; - @Override - protected void closeConnections() { - if (handler != null) { - handler.close(); - } - if (a != null) { - a.ragconnectCloseConnections(); - } - } - @Override protected void createModel() { a = new A(); @@ -50,10 +39,10 @@ public class TutorialTest extends AbstractMqttTest { // b2.OutputOnB -> a.Input b2.addDependencyB(a); - a.connectInput(mqttUri("topic/for/input")); - a.connectOutputOnA(mqttUri("a/out"), true); - b1.connectOutputOnB(mqttUri("b1/out"), true); - b2.connectOutputOnB(mqttUri("b2/out"), false); + assertTrue(a.connectInput(mqttUri("topic/for/input"))); + assertTrue(a.connectOutputOnA(mqttUri("a/out"), true)); + assertTrue(b1.connectOutputOnB(mqttUri("b1/out"), true)); + assertTrue(b2.connectOutputOnB(mqttUri("b2/out"), false)); } @Override @@ -65,4 +54,11 @@ public class TutorialTest extends AbstractMqttTest { protected void communicateOnlyUpdatedValue() { // empty } + + @Override + protected void closeConnections() { + if (a != null) { + a.ragconnectCloseConnections(); + } + } } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java index 3c173522680500703be35e96289f0a6e35117b3a..7ab46f7616a1cf90737c2e0b3ff752f553d38292 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java @@ -57,16 +57,68 @@ public class ViaTest extends AbstractMqttTest { private WebTarget senderBoth2Rest; @Override - public void closeConnections() { - if (handler != null) { - handler.close(); - } - if (model != null) { - model.ragconnectCloseConnections(); - } + protected void createModel() { + // Setting value for Input without dependencies does not trigger any updates + model = new A(); + model.setMqtt2MqttInput("100"); + model.setRest2RestInput("200"); + model.setMqtt2RestInput("300"); + model.setRest2MqttInput("400"); + model.setBoth2BothInput("500"); } + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + model.addDependencyMqtt2Mqtt(model); + model.addDependencyRest2Rest(model); + model.addDependencyMqtt2Rest(model); + model.addDependencyRest2Mqtt(model); + model.addDependencyBoth2Mqtt(model); + model.addDependencyBoth2Rest(model); + + dataMqtt2Mqtt = new ReceiverData(); + dataRest2Mqtt = new ReceiverData(); + dataBoth2Mqtt = new ReceiverData(); + + handler.newConnection(TOPIC_MQTT_2_MQTT_SEND, bytes -> { + dataMqtt2Mqtt.numberOfStringValues += 1; + dataMqtt2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); + }); + handler.newConnection(TOPIC_REST_2_MQTT_SEND, bytes -> { + dataRest2Mqtt.numberOfStringValues += 1; + dataRest2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); + }); + handler.newConnection(TOPIC_BOTH_2_MQTT_SEND, bytes -> { + dataBoth2Mqtt.numberOfStringValues += 1; + dataBoth2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes); + }); + + Client client = ClientBuilder.newClient(); + dataRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_SEND); + dataMqtt2Rest = client.target(REST_SERVER_BASE_URL + PATH_MQTT_2_REST_SEND); + dataBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_2_REST_SEND); + senderRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_RECEIVE); + senderRest2Mqtt = client.target(REST_SERVER_BASE_URL + PATH_REST_2_MQTT_RECEIVE); + senderBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_REST_RECEIVE); + assertTrue(model.connectMqtt2MqttInput(mqttUri(TOPIC_MQTT_2_MQTT_RECEIVE))); + assertTrue(model.connectMqtt2MqttOutput(mqttUri(TOPIC_MQTT_2_MQTT_SEND), writeCurrentValue)); + assertTrue(model.connectMqtt2RestInput(mqttUri(TOPIC_MQTT_2_REST_RECEIVE))); + assertTrue(model.connectMqtt2RestOutput(restUri(PATH_MQTT_2_REST_SEND, REST_PORT), writeCurrentValue)); + assertTrue(model.connectRest2MqttInput(restUri(PATH_REST_2_MQTT_RECEIVE, REST_PORT))); + assertTrue(model.connectRest2MqttOutput(mqttUri(TOPIC_REST_2_MQTT_SEND), writeCurrentValue)); + assertTrue(model.connectRest2RestInput(restUri(PATH_REST_2_REST_RECEIVE, REST_PORT))); + assertTrue(model.connectRest2RestOutput(restUri(PATH_REST_2_REST_SEND, REST_PORT), writeCurrentValue)); + assertTrue(model.connectBoth2BothInput(mqttUri(TOPIC_BOTH_MQTT_RECEIVE))); + assertTrue(model.connectBoth2BothInput(restUri(PATH_BOTH_REST_RECEIVE, REST_PORT))); + assertTrue(model.connectBoth2MqttOutput(mqttUri(TOPIC_BOTH_2_MQTT_SEND), writeCurrentValue)); + assertTrue(model.connectBoth2RestOutput(restUri(PATH_BOTH_2_REST_SEND, REST_PORT), writeCurrentValue)); + } @Override protected void communicateSendInitialValue() throws InterruptedException { @@ -142,7 +194,7 @@ public class ViaTest extends AbstractMqttTest { @Override protected void communicateOnlyUpdatedValue() throws InterruptedException { -// check initial value + // check initial value TestUtils.waitForMqtt(); checkData(0, null, "200-R2R-ToRest", @@ -212,16 +264,26 @@ public class ViaTest extends AbstractMqttTest { "512-B2R-ToRest"); } + @Override + public void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } + private void sendData(String inputMqtt2Mqtt, String inputRest2Rest, String inputMqtt2Rest, String inputRest2Mqtt) { - handler.publish(TOPIC_MQTT_2_MQTT_RECEIVE, inputMqtt2Mqtt.getBytes()); + publisher.publish(TOPIC_MQTT_2_MQTT_RECEIVE, inputMqtt2Mqtt.getBytes()); senderRest2Rest.request().put(Entity.entity(inputRest2Rest, MediaType.TEXT_PLAIN_TYPE)); - handler.publish(TOPIC_MQTT_2_REST_RECEIVE, inputMqtt2Rest.getBytes()); + publisher.publish(TOPIC_MQTT_2_REST_RECEIVE, inputMqtt2Rest.getBytes()); senderRest2Mqtt.request().put(Entity.entity(inputRest2Mqtt, MediaType.TEXT_PLAIN_TYPE)); } private void sendDataForBoth(String input, boolean useMqtt) { if (useMqtt) { - handler.publish(TOPIC_BOTH_MQTT_RECEIVE, input.getBytes()); + publisher.publish(TOPIC_BOTH_MQTT_RECEIVE, input.getBytes()); } else { senderBoth2Rest.request().put(Entity.entity(input, MediaType.TEXT_PLAIN_TYPE)); } @@ -248,70 +310,6 @@ public class ViaTest extends AbstractMqttTest { return dataBoth2Rest.request().get().readEntity(String.class); } - @Override - protected void createModel() { - // Setting value for Input without dependencies does not trigger any updates - model = new A(); - model.setMqtt2MqttInput("100"); - model.setRest2RestInput("200"); - model.setMqtt2RestInput("300"); - model.setRest2MqttInput("400"); - model.setBoth2BothInput("500"); - } - - @Override - protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { - model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); - - handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); - assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); - - model.addDependencyMqtt2Mqtt(model); - model.addDependencyRest2Rest(model); - model.addDependencyMqtt2Rest(model); - model.addDependencyRest2Mqtt(model); - model.addDependencyBoth2Mqtt(model); - model.addDependencyBoth2Rest(model); - - dataMqtt2Mqtt = new ReceiverData(); - dataRest2Mqtt = new ReceiverData(); - dataBoth2Mqtt = new ReceiverData(); - - handler.newConnection(TOPIC_MQTT_2_MQTT_SEND, bytes -> { - dataMqtt2Mqtt.numberOfStringValues += 1; - dataMqtt2Mqtt.lastStringValue = new String(bytes); - }); - handler.newConnection(TOPIC_REST_2_MQTT_SEND, bytes -> { - dataRest2Mqtt.numberOfStringValues += 1; - dataRest2Mqtt.lastStringValue = new String(bytes); - }); - handler.newConnection(TOPIC_BOTH_2_MQTT_SEND, bytes -> { - dataBoth2Mqtt.numberOfStringValues += 1; - dataBoth2Mqtt.lastStringValue = new String(bytes); - }); - - Client client = ClientBuilder.newClient(); - dataRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_SEND); - dataMqtt2Rest = client.target(REST_SERVER_BASE_URL + PATH_MQTT_2_REST_SEND); - dataBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_2_REST_SEND); - senderRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_RECEIVE); - senderRest2Mqtt = client.target(REST_SERVER_BASE_URL + PATH_REST_2_MQTT_RECEIVE); - senderBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_REST_RECEIVE); - - model.connectMqtt2MqttInput(mqttUri(TOPIC_MQTT_2_MQTT_RECEIVE)); - model.connectMqtt2MqttOutput(mqttUri(TOPIC_MQTT_2_MQTT_SEND), writeCurrentValue); - model.connectMqtt2RestInput(mqttUri(TOPIC_MQTT_2_REST_RECEIVE)); - model.connectMqtt2RestOutput(restUri(PATH_MQTT_2_REST_SEND, REST_PORT), writeCurrentValue); - model.connectRest2MqttInput(restUri(PATH_REST_2_MQTT_RECEIVE, REST_PORT)); - model.connectRest2MqttOutput(mqttUri(TOPIC_REST_2_MQTT_SEND), writeCurrentValue); - model.connectRest2RestInput(restUri(PATH_REST_2_REST_RECEIVE, REST_PORT)); - model.connectRest2RestOutput(restUri(PATH_REST_2_REST_SEND, REST_PORT), writeCurrentValue); - model.connectBoth2BothInput(mqttUri(TOPIC_BOTH_MQTT_RECEIVE)); - model.connectBoth2BothInput(restUri(PATH_BOTH_REST_RECEIVE, REST_PORT)); - model.connectBoth2MqttOutput(mqttUri(TOPIC_BOTH_2_MQTT_SEND), writeCurrentValue); - model.connectBoth2RestOutput(restUri(PATH_BOTH_2_REST_SEND, REST_PORT), writeCurrentValue); - } - private static class ReceiverData { String lastStringValue; int numberOfStringValues = 0; diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/AbstractListTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/AbstractListTest.java new file mode 100644 index 0000000000000000000000000000000000000000..18a6e59bcc07ffc5c61006c671d15b8c55a8d501 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/AbstractListTest.java @@ -0,0 +1,179 @@ +package org.jastadd.ragconnect.tests.list; + +import org.jastadd.ragconnect.tests.AbstractMqttTest; +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.List; +import java.util.function.Function; + +import static java.util.Collections.addAll; +import static org.assertj.core.util.Lists.newArrayList; +import static org.jastadd.ragconnect.tests.list.AbstractListTest.IntList.list; +import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Base class for test cases "list manual" and "list incremental". + * + * @author rschoene - Initial contribution + */ +@Tag("List") +public abstract class AbstractListTest extends AbstractMqttTest { + + public interface TestWrapperJastAddList<T> extends Iterable<T> { + int getNumChild(); + } + public interface TestWrapperReceiverRoot { + TestWrapperJastAddList<? extends TestWrapperA> getAList(); + TestWrapperJastAddList<? extends TestWrapperA> getAs(); + int getNumA(); + TestWrapperA getA(int index); + + TestWrapperJastAddList<? extends TestWrapperA> getFromSingleAList(); + TestWrapperJastAddList<? extends TestWrapperA> getFromSingleAs(); + + TestWrapperJastAddList<? extends TestWrapperA> getWithAddFromAList(); + TestWrapperJastAddList<? extends TestWrapperA> getWithAddFromAs(); + + TestWrapperJastAddList<? extends TestWrapperA> getWithAddFromSingleAList(); + TestWrapperJastAddList<? extends TestWrapperA> getWithAddFromSingleAs(); + + boolean connectAList(String mqttUri) throws IOException; + boolean connectFromSingleAList(String mqttUri) throws IOException; + boolean connectWithAddFromAList(String mqttUri) throws IOException; + boolean connectWithAddFromSingleAList(String mqttUri) throws IOException; + } + public interface TestWrapperA { + AbstractListTest.TestWrapperB getB(int i); + int getNumB(); + int getID(); + } + public interface TestWrapperB { + int getID(); + } + + AbstractListTest(String shortName) { + this.shortName = shortName; + } + + protected static final String TOPIC_A = "a"; + protected static final String TOPIC_SINGLE_A = "single-a"; + + protected TestWrapperReceiverRoot receiverRoot; + protected ReceiverData data; + protected ReceiverData dataSingle; + + private final String shortName; + + @Test + public void checkJacksonReference() { + testJaddContainReferenceToJackson( + Paths.get("src", "test", + "02-after-ragconnect", shortName, "RagConnect.jadd"), true); + } + + @Override + protected void communicateSendInitialValue() throws InterruptedException { + checkTree(1, list(), list(0), list(), list(0)); + + setInput(1); + checkTree(2, list(1), list(1), list(1), list(0, 1)); + + setInput(1); + checkTree(2, list(1), list(1), list(1), list(0, 1)); + + setInput(2); + checkTree(3, list(1, 2), list(2), list(1, 1, 2), list(0, 1, 2)); + + setInput(3); + checkTree(4, list(1, 2, 3), list(3), list(1, 1, 2, 1, 2, 3), list(0, 1, 2, 3)); + } + + @Override + protected void communicateOnlyUpdatedValue() throws InterruptedException { + checkTree(0, list(), list(), list(), list()); + + setInput(1); + checkTree(1, list(1), list(1), list(1), list(1)); + + setInput(1); + checkTree(1, list(1), list(1), list(1), list(1)); + + setInput(2); + checkTree(2, list(1, 2), list(2), list(1, 1, 2), list(1, 2)); + + setInput(3); + checkTree(3, list(1, 2, 3), list(3), list(1, 1, 2, 1, 2, 3), list(1, 2, 3)); + } + + protected abstract void setInput(int input); + + private void checkTree(int expectedTransmissions, IntList normalA, IntList fromSingleA, IntList withAddA, IntList withAddFromSingleA) throws InterruptedException { + TestUtils.waitForMqtt(); + + assertEquals(expectedTransmissions, data.numberOfElements, "transmissions for normal"); + assertEquals(expectedTransmissions, dataSingle.numberOfElements, "transmissions for single"); + + checkList(normalA.toList(), receiverRoot.getNumA(), receiverRoot::getA, true); + checkList(normalA.toList(), receiverRoot.getAList(), true); + checkList(normalA.toList(), receiverRoot.getAs(), true); + + checkList(fromSingleA.toList(), receiverRoot.getFromSingleAList(), false); + checkList(fromSingleA.toList(), receiverRoot.getFromSingleAs(), false); + + checkList(withAddA.toList(), receiverRoot.getWithAddFromAList(), true); + checkList(withAddA.toList(), receiverRoot.getWithAddFromAs(), true); + + checkList(withAddFromSingleA.toList(), receiverRoot.getWithAddFromSingleAList(), false); + checkList(withAddFromSingleA.toList(), receiverRoot.getWithAddFromSingleAs(), false); + } + + private void checkList(List<Integer> expectedList, int numChildren, Function<Integer, TestWrapperA> getA, boolean expectB) { + assertEquals(expectedList.size(), numChildren, "same list size"); + for (int index = 0; index < expectedList.size(); index++) { + TestWrapperA a = getA.apply(index); + assertEquals(expectedList.get(index), a.getID(), "correct ID for A"); + if (expectB) { + assertEquals(1, a.getNumB(), "one B child"); + assertEquals(expectedList.get(index) + 1, a.getB(0).getID(), "correct ID for B child"); + } + } + } + + private void checkList(List<Integer> expectedList, TestWrapperJastAddList<? extends TestWrapperA> actualList, boolean expectB) { + assertEquals(expectedList.size(), actualList.getNumChild(), "same list size"); + int index = 0; + for (TestWrapperA a : actualList) { + assertEquals(expectedList.get(index), a.getID(), "correct ID for A"); + if (expectB) { + assertEquals(1, a.getNumB(), "one B child"); + assertEquals(expectedList.get(index) + 1, a.getB(0).getID(), "correct ID for B child"); + } + index++; + } + } + + protected static class ReceiverData { + int numberOfElements = 0; + } + + protected static class IntList { + private final List<Integer> integers = newArrayList(); + public IntList(Integer... values) { + addAll(integers, values); + } + + public List<Integer> toList() { + return integers; + } + + public static IntList list(Integer... values) { + return new IntList(values); + } + } + +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e5fbae01e28d2c31eed1511c7ff9a0712cd5e3c4 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java @@ -0,0 +1,85 @@ +package org.jastadd.ragconnect.tests.list; + +import listInc.ast.*; +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test case "list incremental". + * + * @author rschoene - Initial contribution + */ +@Tag("Incremental") +public class ListIncrementalTest extends AbstractListTest { + + private Root model; + private SenderRoot senderRoot; + private MqttHandler handler; + + ListIncrementalTest() { + super("listInc"); + } + + @Override + protected void createModel() { + model = new Root(); + senderRoot = new SenderRoot(); + senderRoot.setInput(0); + model.addSenderRoot(senderRoot); + + receiverRoot = new ReceiverRoot(); + model.addReceiverRoot((ReceiverRoot) receiverRoot); + } + + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // no dependencies + + data = new ReceiverData(); + dataSingle = new ReceiverData(); + handler.newConnection(TOPIC_A, bytes -> data.numberOfElements += 1); + handler.newConnection(TOPIC_SINGLE_A, bytes -> dataSingle.numberOfElements += 1); + + // connect. important: first receivers, then senders. to not miss initial value. + assertTrue(receiverRoot.connectAList(mqttUri(TOPIC_A))); + assertTrue(receiverRoot.connectFromSingleAList(mqttUri(TOPIC_SINGLE_A))); + assertTrue(receiverRoot.connectWithAddFromAList(mqttUri(TOPIC_A))); + assertTrue(receiverRoot.connectWithAddFromSingleAList(mqttUri(TOPIC_SINGLE_A))); + + assertTrue(senderRoot.connectAList(mqttUri(TOPIC_A), writeCurrentValue)); + assertTrue(senderRoot.connectSingleAList(mqttUri(TOPIC_SINGLE_A), writeCurrentValue)); + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } + + @Override + protected void setInput(int input) { + senderRoot.setInput(input); + assertEquals(input, senderRoot.getNumA(), "size of normal NTA"); + if (input > 0) { + assertEquals(input, senderRoot.getA(input - 1).getID(), "ID value of last A in normal list"); + } + assertEquals(1, senderRoot.getNumSingleA(), "size of single NTA"); + assertEquals(input, senderRoot.getSingleA(0).getID(), "ID value of single A"); + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f3e6eee5c93ea62e6edf939a1e6f5f3ef5419463 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java @@ -0,0 +1,84 @@ +package org.jastadd.ragconnect.tests.list; + +import list.ast.MqttHandler; +import list.ast.ReceiverRoot; +import list.ast.Root; +import list.ast.SenderRoot; +import org.jastadd.ragconnect.tests.TestUtils; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test case "list manual". + * + * @author rschoene - Initial contribution + */ +public class ListManualTest extends AbstractListTest { + + private Root model; + private SenderRoot senderRoot; + private MqttHandler handler; + + ListManualTest() { + super("list"); + } + + @Override + protected void createModel() { + model = new Root(); + senderRoot = new SenderRoot(); + senderRoot.setInput(0); + model.addSenderRoot(senderRoot); + + receiverRoot = new ReceiverRoot(); + model.addReceiverRoot((ReceiverRoot) receiverRoot); + } + + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // add dependencies + senderRoot.addInputDependencyToA(senderRoot); + senderRoot.addInputDependencyToSingleA(senderRoot); + + data = new ReceiverData(); + dataSingle = new ReceiverData(); + handler.newConnection(TOPIC_A, bytes -> data.numberOfElements += 1); + handler.newConnection(TOPIC_SINGLE_A, bytes -> dataSingle.numberOfElements += 1); + + // connect. important: first receivers, then senders. to not miss initial value. + assertTrue(receiverRoot.connectAList(mqttUri(TOPIC_A))); + assertTrue(receiverRoot.connectFromSingleAList(mqttUri(TOPIC_SINGLE_A))); + assertTrue(receiverRoot.connectWithAddFromAList(mqttUri(TOPIC_A))); + assertTrue(receiverRoot.connectWithAddFromSingleAList(mqttUri(TOPIC_SINGLE_A))); + + assertTrue(senderRoot.connectAList(mqttUri(TOPIC_A), writeCurrentValue)); + assertTrue(senderRoot.connectSingleAList(mqttUri(TOPIC_SINGLE_A), writeCurrentValue)); + } + + @Override + protected void setInput(int input) { + senderRoot.setInput(input); + assertEquals(input, senderRoot.getNumA(), "size of normal NTA"); + assertEquals(1, senderRoot.getNumSingleA(), "size of single NTA"); + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3c2779f8d4188c0f1fff7f8773d27401e94dd597 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java @@ -0,0 +1,297 @@ +package org.jastadd.ragconnect.tests.singleList; + +import org.jastadd.ragconnect.tests.AbstractMqttTest; +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import singleList.ast.MqttHandler; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +import static java.util.Collections.addAll; +import static org.assertj.core.util.Lists.newArrayList; +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson; +import static org.jastadd.ragconnect.tests.singleList.AbstractSingleListTest.IntList.list; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Base class for test cases "singleList manual" and "singleList incremental". + * + * @author rschoene - Initial contribution + */ +@Tag("List") +@Tag("SingleList") +public abstract class AbstractSingleListTest extends AbstractMqttTest { + + public interface TestWrapperJastAddList<T> extends Iterable<T> { + int getNumChild(); + } + public interface TestWrapperReceiverRoot { + TestWrapperJastAddList<? extends TestWrapperA> getAList(); + TestWrapperJastAddList<? extends TestWrapperA> getAs(); + int getNumA(); + int getNumWithAddA(); + TestWrapperA getA(int index); + + TestWrapperJastAddList<? extends TestWrapperA> getWithAddAList(); + TestWrapperJastAddList<? extends TestWrapperA> getUsingWildcardAList(); + TestWrapperJastAddList<? extends TestWrapperA> getUsingWildcardWithAddAList(); + + @SuppressWarnings("unused") boolean connectA(String mqttUri) throws IOException; + boolean connectA(String mqttUri, int index) throws IOException; + boolean connectUsingWildcardA(String mqttUri) throws IOException; + @SuppressWarnings("unused") boolean connectUsingWildcardA(String mqttUri, int index) throws IOException; + boolean connectWithAddA(String mqttUri) throws IOException; + boolean connectUsingWildcardWithAddA(String mqttUri) throws IOException; + } + @SuppressWarnings("UnusedReturnValue") + public interface TestWrapperSenderRoot { + boolean connectA1(String mqttUri, boolean writeCurrentValue) throws IOException; + boolean connectA2(String mqttUri, boolean writeCurrentValue) throws IOException; + boolean connectA3(String mqttUri, boolean writeCurrentValue) throws IOException; + boolean connectA4(String mqttUri, boolean writeCurrentValue) throws IOException; + boolean connectInOutput(String mqttUri, boolean writeCurrentValue) throws IOException; + + TestWrapperSenderRoot setInput1(int input); + TestWrapperSenderRoot setInput2(int input); + TestWrapperSenderRoot setInput3(int input); + TestWrapperSenderRoot setInput4(int input); + TestWrapperSenderRoot setInOutput(int input); + + TestWrapperA getA1(); + TestWrapperA getA2(); + TestWrapperA getA3(); + TestWrapperA getA4(); + } + public interface TestWrapperA { + int getID(); + } + + AbstractSingleListTest(String shortName) { + this.shortName = shortName; + } + + protected static final String TOPIC_A_1 = "a/first"; + protected static final String TOPIC_A_2 = "a/second"; + protected static final String TOPIC_A_3 = "a/third"; + protected static final String TOPIC_A_4 = "a/fourth"; + protected static final String TOPIC_A_5_INOUT = "a/special"; + protected static final String TOPIC_A_WILDCARD = "a/#"; + + protected TestWrapperSenderRoot senderRoot; + protected TestWrapperReceiverRoot receiverRoot; + protected ReceiverData data; + + private final String shortName; + + @Test + public void checkJacksonReference() { + testJaddContainReferenceToJackson( + Paths.get("src", "test", + "02-after-ragconnect", shortName, "RagConnect.jadd"), true); + } + + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException, InterruptedException { + // late model initialization + senderRoot.setInput1(0); + senderRoot.setInput2(0); + senderRoot.setInput3(0); + senderRoot.setInput4(0); + senderRoot.setInOutput(0); + + setupReceiverAndConnectPart(); + + // connect. important: first receivers, then senders. to not miss initial value. + + // receive: explicit topic subscription + assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A_1), 0)); + assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A_2), 1)); + assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A_3), 2)); + assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A_4), 3)); + assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A_5_INOUT), 4)); + + assertTrue(receiverRoot.connectWithAddA(mqttUri(TOPIC_A_1))); + assertTrue(receiverRoot.connectWithAddA(mqttUri(TOPIC_A_2))); + assertTrue(receiverRoot.connectWithAddA(mqttUri(TOPIC_A_3))); + assertTrue(receiverRoot.connectWithAddA(mqttUri(TOPIC_A_4))); + assertTrue(receiverRoot.connectWithAddA(mqttUri(TOPIC_A_5_INOUT))); + + // receive: wildcard subscription + assertTrue(receiverRoot.connectUsingWildcardA(mqttUri(TOPIC_A_WILDCARD))); + assertTrue(receiverRoot.connectUsingWildcardWithAddA(mqttUri(TOPIC_A_WILDCARD))); + + // send: explicit topics, wait between connections to ensure correct arrival at receiver + MqttHandler checkArrivalHandler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + Map<String, CountDownLatch> arrived = new HashMap<>() {{ + put(TOPIC_A_1, new CountDownLatch(1)); + put(TOPIC_A_2, new CountDownLatch(1)); + put(TOPIC_A_3, new CountDownLatch(1)); + put(TOPIC_A_4, new CountDownLatch(1)); + }}; + checkArrivalHandler.waitUntilReady(2, TimeUnit.SECONDS); + checkArrivalHandler.newConnection("#", (topic, bytes) -> + Optional.ofNullable(arrived.get(topic)).ifPresent(CountDownLatch::countDown)); + + assertTrue(senderRoot.connectA4(mqttUri(TOPIC_A_4), writeCurrentValue)); + if (writeCurrentValue) { + assertTrue(arrived.get(TOPIC_A_4).await(2, TimeUnit.SECONDS)); + } + + assertTrue(senderRoot.connectA3(mqttUri(TOPIC_A_3), writeCurrentValue)); + if (writeCurrentValue) { + assertTrue(arrived.get(TOPIC_A_3).await(2, TimeUnit.SECONDS)); + } + + assertTrue(senderRoot.connectA2(mqttUri(TOPIC_A_2), writeCurrentValue)); + if (writeCurrentValue) { + assertTrue(arrived.get(TOPIC_A_2).await(2, TimeUnit.SECONDS)); + } + + assertTrue(senderRoot.connectA1(mqttUri(TOPIC_A_1), writeCurrentValue)); + if (writeCurrentValue) { + assertTrue(arrived.get(TOPIC_A_1).await(2, TimeUnit.SECONDS)); + } + + assertTrue(senderRoot.connectInOutput(mqttUri(TOPIC_A_5_INOUT), writeCurrentValue)); + // no need to wait here, because first "checkTree" will wait anyway + checkArrivalHandler.close(); + } + + abstract protected void setupReceiverAndConnectPart() throws IOException; + + @Override + protected void communicateSendInitialValue() throws InterruptedException { + checkTree(5, list(1, 2, 3, 4, 0), list(4, 3, 2, 1, 0), // normal: (normal / wildcard) + list(4, 3, 2, 1, 0), list(4, 3, 2, 1, 0)); // withAdd: (normal / wildcard) + + // A1 will be 2 (1+1, previously 1) + setInput(1, 1); + checkTree(6, list(2, 2, 3, 4, 0), list(4, 3, 2, 2, 0), // normal: (normal / wildcard) + list(4, 3, 2, 1, 0, 2), list(4, 3, 2, 1, 0, 2)); // withAdd: (normal / wildcard) + + // A1 should stay at 2 + setInput(1, 1); + checkTree(6, list(2, 2, 3, 4, 0), list(4, 3, 2, 2, 0), // normal: (normal / wildcard) + list(4, 3, 2, 1, 0, 2), list(4, 3, 2, 1, 0, 2)); // withAdd: (normal / wildcard) + + // A1 will be 3 (2+1, previously 2) + setInput(1, 2); + checkTree(7, list(3, 2, 3, 4, 0), list(4, 3, 2, 3, 0), // normal: (normal / wildcard) + list(4, 3, 2, 1, 0, 2, 3), list(4, 3, 2, 1, 0, 2, 3)); // withAdd: (normal / wildcard) + + // InOut will be 5 (previously 0) + setInput(5, 5); + checkTree(8, list(3, 2, 3, 4, 5), list(4, 3, 2, 3, 5), // normal: (normal / wildcard) + list(4, 3, 2, 1, 0, 2, 3, 5), list(4, 3, 2, 1, 0, 2, 3, 5)); // withAdd: (normal / wildcard) + + // A3 will be 7 (4+3, previously 3) + setInput(3, 4); + checkTree(9, list(3, 2, 7, 4, 5), list(4, 7, 2, 3, 5), // normal: (normal / wildcard) + list(4, 3, 2, 1, 0, 2, 3, 5, 7), list(4, 3, 2, 1, 0, 2, 3, 5, 7)); // withAdd: (normal / wildcard) + } + + @Override + protected void communicateOnlyUpdatedValue() throws InterruptedException { + checkTree(0, list(0, 0, 0, 0, 0), list(), // normal + list(), list()); // withAdd + + // A1 will be 2 (1+1, previously 1) + setInput(1, 1); + checkTree(1, list(2, 0, 0, 0, 0), list(2), // normal + list(2), list(2)); // withAdd + + // A1 should stay at 2 + setInput(1, 1); + checkTree(1, list(2, 0, 0, 0, 0), list(2), // normal + list(2), list(2)); // withAdd + + // A1 will be 3 (2+1, previously 2) + setInput(1, 2); + checkTree(2, list(3, 0, 0, 0, 0), list(3), // normal + list(2, 3), list(2, 3)); // withAdd + + // InOut will be 5 (previously 0) + setInput(5, 5); + checkTree(3, list(3, 0, 0, 0, 5), list(3, 5), // normal + list(2, 3, 5), list(2, 3, 5)); // withAdd + + // A3 will be 7 (4+3, previously 3) + setInput(3, 4); + checkTree(4, list(3, 0, 7, 0, 5), list(3,5,7), // normal + list(2, 3, 5, 7), list(2, 3, 5, 7)); // withAdd + } + + protected void setInput(int index, int input) { + int actualComputedValue; + switch (index) { + case 1: senderRoot.setInput1(input); actualComputedValue = senderRoot.getA1().getID(); break; + case 2: senderRoot.setInput2(input); actualComputedValue = senderRoot.getA2().getID(); break; + case 3: senderRoot.setInput3(input); actualComputedValue = senderRoot.getA3().getID(); break; + case 4: senderRoot.setInput4(input); actualComputedValue = senderRoot.getA4().getID(); break; + case 5: senderRoot.setInOutput(input); return; + default: fail("Wrong index " + index); return; + } + assertEquals(input + index, actualComputedValue, "ID value of single A"); + } + + private void checkTree(int expectedTransmissions, IntList normalA, IntList usingWildcardA, IntList withAddA, IntList usingWildcardWithAddA) throws InterruptedException { + TestUtils.waitForMqtt(); + assertEquals(expectedTransmissions, data.numberOfElements, "transmissions for any A"); + + checkList(normalA.toList(), receiverRoot.getNumA(), receiverRoot::getA); + checkList(normalA.toList(), receiverRoot.getAList()); + + checkList(usingWildcardA.toList(), receiverRoot.getUsingWildcardAList()); + + checkList(withAddA.toList(), receiverRoot.getWithAddAList()); + + checkList(usingWildcardWithAddA.toList(), receiverRoot.getUsingWildcardWithAddAList()); + } + + private void checkList(List<Integer> expectedList, int numChildren, Function<Integer, TestWrapperA> getA) { + assertEquals(expectedList.size(), numChildren, "same list size"); + for (int index = 0; index < expectedList.size(); index++) { + TestWrapperA a = getA.apply(index); + assertEquals(expectedList.get(index), a.getID(), "correct ID for A"); + } + } + + private void checkList(List<Integer> expectedList, TestWrapperJastAddList<? extends TestWrapperA> actualList) { + assertEquals(expectedList.size(), actualList.getNumChild(), "same list size"); + int index = 0; + for (TestWrapperA a : actualList) { + assertEquals(expectedList.get(index), a.getID(), "correct ID for A"); + index++; + } + } + + protected static class ReceiverData { + int numberOfElements = 0; + } + + protected static class IntList { + private final List<Integer> integers = newArrayList(); + public IntList(Integer... values) { + addAll(integers, values); + } + + public List<Integer> toList() { + return integers; + } + + public static IntList list(Integer... values) { + return new IntList(values); + } + } + +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListIncrementalTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7a946e44499f71dc0dfb92271ca1fc414275597b --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListIncrementalTest.java @@ -0,0 +1,67 @@ +package org.jastadd.ragconnect.tests.singleList; + +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; +import singleListInc.ast.*; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test case "list incremental". + * + * @author rschoene - Initial contribution + */ +@Tag("Incremental") +public class SingleListIncrementalTest extends AbstractSingleListTest { + + private Root model; + private MqttHandler handler; + + SingleListIncrementalTest() { + super("singleListInc"); + } + + @Override + protected void createModel() { + model = new Root(); + senderRoot = new SenderRoot(); + model.addSenderRoot((SenderRoot) senderRoot); + + ReceiverRoot localReceiverRoot = new ReceiverRoot(); + model.addReceiverRoot(localReceiverRoot); + + // first prepare non-wildcard lists + for (int i = 0; i < 5; i++) { + localReceiverRoot.addA(new A()); + } + receiverRoot = localReceiverRoot; + assertEquals(5, receiverRoot.getNumA()); + } + + @Override + protected void setupReceiverAndConnectPart() throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // no dependencies + + data = new ReceiverData(); + handler.newConnection(TOPIC_A_WILDCARD, bytes -> data.numberOfElements += 1); + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListManualTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c2896f633bc299159f0ab74913c3a9c0456d8370 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListManualTest.java @@ -0,0 +1,69 @@ +package org.jastadd.ragconnect.tests.singleList; + +import org.jastadd.ragconnect.tests.TestUtils; +import singleList.ast.*; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test case "list manual". + * + * @author rschoene - Initial contribution + */ +public class SingleListManualTest extends AbstractSingleListTest { + + private Root model; + private MqttHandler handler; + + SingleListManualTest() { + super("singleList"); + } + + @Override + protected void createModel() { + model = new Root(); + senderRoot = new SenderRoot(); + model.addSenderRoot((SenderRoot) senderRoot); + + ReceiverRoot localReceiverRoot = new ReceiverRoot(); + model.addReceiverRoot(localReceiverRoot); + + // first prepare non-wildcard lists + for (int i = 0; i < 5; i++) { + localReceiverRoot.addA(new A()); + } + receiverRoot = localReceiverRoot; + assertEquals(5, receiverRoot.getNumA()); + } + + @Override + protected void setupReceiverAndConnectPart() throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // add dependencies + ((SenderRoot) senderRoot).addInputDependencyToA1((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addInputDependencyToA2((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addInputDependencyToA3((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addInputDependencyToA4((SenderRoot) senderRoot); + + data = new ReceiverData(); + handler.newConnection(TOPIC_A_WILDCARD, bytes -> data.numberOfElements += 1); + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java new file mode 100644 index 0000000000000000000000000000000000000000..465ad8ac024d595f29a87757c776db6c88e72fe4 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java @@ -0,0 +1,406 @@ +package org.jastadd.ragconnect.tests.singleListVariant; + +import org.jastadd.ragconnect.tests.AbstractMqttTest; +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.List; +import java.util.function.BiConsumer; + +import static java.lang.Math.abs; +import static java.util.Collections.addAll; +import static org.assertj.core.util.Lists.newArrayList; +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson; +import static org.jastadd.ragconnect.tests.singleListVariant.AbstractSingleListVariantTest.IntList.list; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Base class for test cases "singleList manual" and "singleList incremental". + * + * @author rschoene - Initial contribution + */ +@Tag("List") +@Tag("SingleList") +public abstract class AbstractSingleListVariantTest extends AbstractMqttTest { + + public interface TestWrapperJastAddList<T> extends Iterable<T> { + int getNumChild(); + } + public interface TestWrapperReceiverRoot { + TestWrapperJastAddList<? extends TestWrapperT_Empty> getT_EmptyList(); + TestWrapperJastAddList<? extends TestWrapperT_Token> getT_TokenList(); + TestWrapperJastAddList<? extends TestWrapperT_OneChild> getT_OneChildList(); + TestWrapperJastAddList<? extends TestWrapperT_OneOpt> getT_OneOptList(); + TestWrapperJastAddList<? extends TestWrapperT_OneList> getT_OneListList(); + TestWrapperJastAddList<? extends TestWrapperT_TwoChildren> getT_TwoChildrenList(); + TestWrapperJastAddList<? extends TestWrapperT_OneOfEach> getT_OneOfEachList(); + TestWrapperJastAddList<? extends TestWrapperT_Abstract> getT_AbstractList(); + + TestWrapperJastAddList<? extends TestWrapperT_Empty> getMyEmptyList(); + + TestWrapperJastAddList<? extends TestWrapperT_Empty> getEmptyWithAddList(); + TestWrapperJastAddList<? extends TestWrapperT_Token> getTokenWithAddList(); + TestWrapperJastAddList<? extends TestWrapperT_OneChild> getOneChildWithAddList(); + TestWrapperJastAddList<? extends TestWrapperT_OneOpt> getOneOptWithAddList(); + TestWrapperJastAddList<? extends TestWrapperT_OneList> getOneListWithAddList(); + TestWrapperJastAddList<? extends TestWrapperT_TwoChildren> getTwoChildrenWithAddList(); + TestWrapperJastAddList<? extends TestWrapperT_OneOfEach> getOneOfEachWithAddList(); + TestWrapperJastAddList<? extends TestWrapperT_Abstract> getAbstractWithAddList(); + + boolean connectT_Empty(String mqttUri) throws IOException; + boolean connectT_Token(String mqttUri) throws IOException; + boolean connectT_OneChild(String mqttUri) throws IOException; + boolean connectT_OneOpt(String mqttUri) throws IOException; + boolean connectT_OneList(String mqttUri) throws IOException; + boolean connectT_TwoChildren(String mqttUri) throws IOException; + boolean connectT_OneOfEach(String mqttUri) throws IOException; + boolean connectT_Abstract(String mqttUri) throws IOException; + + boolean connectMyEmpty(String mqttUri) throws IOException; + + boolean connectEmptyWithAdd(String mqttUri) throws IOException; + boolean connectTokenWithAdd(String mqttUri) throws IOException; + boolean connectOneChildWithAdd(String mqttUri) throws IOException; + boolean connectOneOptWithAdd(String mqttUri) throws IOException; + boolean connectOneListWithAdd(String mqttUri) throws IOException; + boolean connectTwoChildrenWithAdd(String mqttUri) throws IOException; + boolean connectOneOfEachWithAdd(String mqttUri) throws IOException; + boolean connectAbstractWithAdd(String mqttUri) throws IOException; + } + @SuppressWarnings("UnusedReturnValue") + public interface TestWrapperSenderRoot { + boolean connectT_Empty(String mqttUri, boolean writeCurrentValue) throws IOException; + boolean connectT_Token(String mqttUri, boolean writeCurrentValue) throws IOException; + boolean connectT_OneChild(String mqttUri, boolean writeCurrentValue) throws IOException; + boolean connectT_OneOpt(String mqttUri, boolean writeCurrentValue) throws IOException; + boolean connectT_OneList(String mqttUri, boolean writeCurrentValue) throws IOException; + boolean connectT_TwoChildren(String mqttUri, boolean writeCurrentValue) throws IOException; + boolean connectT_OneOfEach(String mqttUri, boolean writeCurrentValue) throws IOException; + boolean connectT_Abstract(String mqttUri, boolean writeCurrentValue) throws IOException; + + TestWrapperSenderRoot setInput(int input); + TestWrapperSenderRoot setShouldSetOptAndList(boolean shouldSetOptAndList); + TestWrapperT_Empty getT_Empty(); + TestWrapperT_OneOpt getT_OneOpt(); + } + public interface TestWrapperNameable { + int getID(); + } + public interface TestWrapperOther extends TestWrapperNameable {} + public interface TestWrapperT_Empty extends TestWrapperNameable {} + public interface TestWrapperT_Token extends TestWrapperNameable { + String getValue(); + } + public interface TestWrapperT_OneChild extends TestWrapperNameable { + TestWrapperNameable getOther(); + } + public interface TestWrapperT_OneOpt extends TestWrapperNameable { + boolean hasOther(); + TestWrapperNameable getOther(); + } + public interface TestWrapperT_OneList extends TestWrapperNameable { + int getNumOther(); + TestWrapperNameable getOther(int index); + } + public interface TestWrapperT_TwoChildren extends TestWrapperNameable { + TestWrapperNameable getLeft(); + TestWrapperNameable getRight(); + } + public interface TestWrapperT_OneOfEach extends TestWrapperNameable { + TestWrapperNameable getFirst(); + boolean hasSecond(); + TestWrapperNameable getSecond(); + int getNumThird(); + TestWrapperNameable getThird(int index); + String getFourth(); + } + public interface TestWrapperT_Abstract extends TestWrapperNameable { + String getValueAbstract(); + String getValueSub(); + } + + AbstractSingleListVariantTest(String shortName) { + this.shortName = shortName; + } + + protected static final String TOPIC_T_Empty = "t/Empty"; + protected static final String TOPIC_T_Token = "t/Token"; + protected static final String TOPIC_T_OneChild = "t/OneChild"; + protected static final String TOPIC_T_OneOpt = "t/OneOpt"; + protected static final String TOPIC_T_OneList = "t/OneList"; + protected static final String TOPIC_T_TwoChildren = "t/TwoChildren"; + protected static final String TOPIC_T_OneOfEach = "t/OneOfEach"; + protected static final String TOPIC_T_Abstract = "t/Abstract"; + protected static final String TOPIC_T_all = "t/#"; + + protected TestWrapperSenderRoot senderRoot; + protected TestWrapperReceiverRoot receiverRoot; + protected ReceiverData data; + + private final String shortName; + + @Test + public void checkJacksonReference() { + testJaddContainReferenceToJackson( + Paths.get("src", "test", + "02-after-ragconnect", shortName, "RagConnect.jadd"), true); + } + + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException, InterruptedException { + // late model initialization + setInput(0); + setShouldSetOptAndList(false); + + setupReceiverAndConnectPart(); + + // connect. important: first receivers, then senders. to not miss initial value. + + // receive: unnamed + assertTrue(receiverRoot.connectT_Empty(mqttUri(TOPIC_T_Empty))); + assertTrue(receiverRoot.connectT_Token(mqttUri(TOPIC_T_Token))); + assertTrue(receiverRoot.connectT_OneChild(mqttUri(TOPIC_T_OneChild))); + assertTrue(receiverRoot.connectT_OneOpt(mqttUri(TOPIC_T_OneOpt))); + assertTrue(receiverRoot.connectT_OneList(mqttUri(TOPIC_T_OneList))); + assertTrue(receiverRoot.connectT_TwoChildren(mqttUri(TOPIC_T_TwoChildren))); + assertTrue(receiverRoot.connectT_OneOfEach(mqttUri(TOPIC_T_OneOfEach))); + assertTrue(receiverRoot.connectT_Abstract(mqttUri(TOPIC_T_Abstract))); + + // receive: named + assertTrue(receiverRoot.connectMyEmpty(mqttUri(TOPIC_T_Empty))); + + // receive: with add + assertTrue(receiverRoot.connectEmptyWithAdd(mqttUri(TOPIC_T_Empty))); + assertTrue(receiverRoot.connectTokenWithAdd(mqttUri(TOPIC_T_Token))); + assertTrue(receiverRoot.connectOneChildWithAdd(mqttUri(TOPIC_T_OneChild))); + assertTrue(receiverRoot.connectOneOptWithAdd(mqttUri(TOPIC_T_OneOpt))); + assertTrue(receiverRoot.connectOneListWithAdd(mqttUri(TOPIC_T_OneList))); + assertTrue(receiverRoot.connectTwoChildrenWithAdd(mqttUri(TOPIC_T_TwoChildren))); + assertTrue(receiverRoot.connectOneOfEachWithAdd(mqttUri(TOPIC_T_OneOfEach))); + assertTrue(receiverRoot.connectAbstractWithAdd(mqttUri(TOPIC_T_Abstract))); + + // send + assertTrue(senderRoot.connectT_Empty(mqttUri(TOPIC_T_Empty), writeCurrentValue)); + assertTrue(senderRoot.connectT_Token(mqttUri(TOPIC_T_Token), writeCurrentValue)); + assertTrue(senderRoot.connectT_OneChild(mqttUri(TOPIC_T_OneChild), writeCurrentValue)); + assertTrue(senderRoot.connectT_OneOpt(mqttUri(TOPIC_T_OneOpt), writeCurrentValue)); + assertTrue(senderRoot.connectT_OneList(mqttUri(TOPIC_T_OneList), writeCurrentValue)); + assertTrue(senderRoot.connectT_TwoChildren(mqttUri(TOPIC_T_TwoChildren), writeCurrentValue)); + assertTrue(senderRoot.connectT_OneOfEach(mqttUri(TOPIC_T_OneOfEach), writeCurrentValue)); + assertTrue(senderRoot.connectT_Abstract(mqttUri(TOPIC_T_Abstract), writeCurrentValue)); + } + + abstract protected void setupReceiverAndConnectPart() throws IOException; + + @Override + protected void communicateSendInitialValue() throws InterruptedException { + // transmissions: 8 * 1 = 8 + checkTree(8, list(-0), list(0), list(-0)); + + setInput(1); + // transmissions: 8 + 8 = 16 + checkTree(16, list(-1), list(0, 1), list(-0, -1)); + + setInput(1); + // transmissions: 16 + checkTree(16, list(-1), list(0, 1), list(-0, -1)); + + setShouldSetOptAndList(true); + // transmissions: 16 + 3 = 19 + checkTree(19, list(1), list(0, 1), list(-0, -1, 1)); + + setShouldSetOptAndList(true); + // transmissions: 19 + checkTree(19, list(1), list(0, 1), list(-0, -1, 1)); + + setInput(2); + // transmissions: 19 + 8 = 27 + checkTree(27, list(2), list(0, 1, 2), list(-0, -1, 1, 2)); + + setInput(5); + // transmissions: 27 + 8 = 35 + checkTree(35, list(5), list(0, 1, 2, 5), list(-0, -1, 1, 2, 5)); + } + + @Override + protected void communicateOnlyUpdatedValue() throws InterruptedException { + // transmissions: 0 + checkTree(0, list(), list(), list()); + + setInput(1); + // transmissions: 8 * 1 = 0 + checkTree(8, list(-1), list(1), list(-1)); + + setInput(1); + // transmissions: 8 + checkTree(8, list(-1), list(1), list(-1)); + + setShouldSetOptAndList(true); + // transmissions: 8 + 3 = 11 + checkTree(11, list(1), list(1), list(-1, 1)); + + setShouldSetOptAndList(true); + // transmissions: 11 + checkTree(11, list(1), list(1), list(-1, 1)); + + setInput(2); + // transmissions: 11 + 8 = 19 + checkTree(19, list(2), list(1, 2), list(-1, 1, 2)); + + setInput(5); + // transmissions: 19 + 8 = 27 + checkTree(27, list(5), list(1, 2, 5), list(-1, 1, 2, 5)); + } + + protected void setInput(int input) { + senderRoot.setInput(input); + assertEquals(input, senderRoot.getT_Empty().getID(), "ID value of empty"); + } + + protected void setShouldSetOptAndList(boolean shouldSetOptAndList) { + senderRoot.setShouldSetOptAndList(shouldSetOptAndList); + assertEquals(shouldSetOptAndList, senderRoot.getT_OneOpt().hasOther(), "opt is filled or not"); + } + + /** + * Check against expected lists of IDs. + * If an ID is negative, do not check Opts and Lists, but use absolute value for comparison. + * The tests starts with ID 0 and does not use opts and lists at this point, so checking with > 0 is used. + * @param expectedTransmissions expected number of total transmissions + * @param expectedList ids for unnamed and named endpoints without add + * @param expectedWithAddList ids for endpoints with add, but not those with opts and lists + * (only positive numbers can/need to be passed) + * @param expectedWithAddListForOptAndList ids for endpoints with add and with opts and lists + * @throws InterruptedException if interrupted in TestUtils.waitForMqtt + */ + private void checkTree(int expectedTransmissions, + IntList expectedList, + IntList expectedWithAddList, + IntList expectedWithAddListForOptAndList) + throws InterruptedException { + TestUtils.waitForMqtt(); + assertEquals(expectedTransmissions, data.numberOfElements, "transmissions for any element"); + + // check unnamed + checkList(expectedList.toList(), receiverRoot.getT_EmptyList(), (e, n) -> {}); + checkList(expectedList.toList(), receiverRoot.getT_TokenList(), + (e, n) -> assertEquals(Integer.toString(abs(e)), n.getValue())); + checkList(expectedList.toList(), receiverRoot.getT_OneChildList(), + (e, n) -> assertEquals(abs(e) + 1, n.getOther().getID())); + checkList(expectedList.toList(), receiverRoot.getT_OneOptList(), + (e, n) -> { + assertEquals(e > 0, n.hasOther()); + if (n.hasOther()) { + assertEquals(abs(e) + 1, n.getOther().getID()); + } + }); + checkList(expectedList.toList(), receiverRoot.getT_OneListList(), + (e, n) -> { + assertEquals(e > 0 ? 1 : 0, n.getNumOther()); + if (n.getNumOther() > 0) { + assertEquals(abs(e) + 1, n.getOther(0).getID()); + } + }); + checkList(expectedList.toList(), receiverRoot.getT_TwoChildrenList(), + (e, n) -> { + assertEquals(abs(e) + 1, n.getLeft().getID()); + assertEquals(abs(e) + 1, n.getRight().getID()); + }); + checkList(expectedList.toList(), receiverRoot.getT_OneOfEachList(), + (e, n) -> { + assertEquals(abs(e) + 1, n.getFirst().getID()); + assertEquals(e > 0, n.hasSecond()); + if (n.hasSecond()) { + assertEquals(abs(e) + 1, n.getSecond().getID()); + } + assertEquals(e > 0 ? 1 : 0, n.getNumThird()); + if (n.getNumThird() > 0) { + assertEquals(abs(e) + 1, n.getThird(0).getID()); + } + assertEquals(Integer.toString(abs(e)), n.getFourth()); + }); + checkList(expectedList.toList(), receiverRoot.getT_AbstractList(), + (e, n) -> { + assertEquals(Integer.toString(abs(e)), n.getValueAbstract()); + assertEquals(Integer.toString(abs(e)), n.getValueSub()); + }); + + // check named + checkList(expectedList.toList(), receiverRoot.getMyEmptyList(), (e, n) -> {}); + + // check with add + checkList(expectedWithAddList.toList(), receiverRoot.getEmptyWithAddList(), (e, n) -> {}); + checkList(expectedWithAddList.toList(), receiverRoot.getTokenWithAddList(), + (e, n) -> assertEquals(Integer.toString(abs(e)), n.getValue())); + checkList(expectedWithAddList.toList(), receiverRoot.getOneChildWithAddList(), + (e, n) -> assertEquals(abs(e) + 1, n.getOther().getID())); + checkList(expectedWithAddListForOptAndList.toList(), receiverRoot.getOneOptWithAddList(), + (e, n) -> { + if (n.hasOther()) { + assertEquals(abs(e) + 1, n.getOther().getID()); + } + }); + checkList(expectedWithAddListForOptAndList.toList(), receiverRoot.getOneListWithAddList(), + (e, n) -> { + if (n.getNumOther() > 0) { + assertEquals(abs(e) + 1, n.getOther(0).getID()); + } + }); + checkList(expectedWithAddList.toList(), receiverRoot.getTwoChildrenWithAddList(), + (e, n) -> { + assertEquals(abs(e) + 1, n.getLeft().getID()); + assertEquals(abs(e) + 1, n.getRight().getID()); + }); + checkList(expectedWithAddListForOptAndList.toList(), receiverRoot.getOneOfEachWithAddList(), + (e, n) -> { + assertEquals(abs(e) + 1, n.getFirst().getID()); + if (n.hasSecond()) { + assertEquals(abs(e) + 1, n.getSecond().getID()); + } + if (n.getNumThird() > 0) { + assertEquals(abs(e) + 1, n.getThird(0).getID()); + } + assertEquals(Integer.toString(abs(e)), n.getFourth()); + }); + checkList(expectedWithAddList.toList(), receiverRoot.getAbstractWithAddList(), + (e, n) -> { + assertEquals(Integer.toString(abs(e)), n.getValueAbstract()); + assertEquals(Integer.toString(abs(e)), n.getValueSub()); + }); + } + + private <T extends TestWrapperNameable> void checkList(List<Integer> expectedList, TestWrapperJastAddList<T> actualList, BiConsumer<Integer, T> additionalTest) { + assertEquals(expectedList.size(), actualList.getNumChild(), "same list size"); + int index = 0; + for (T element : actualList) { + assertEquals(abs(expectedList.get(index)), element.getID(), "correct ID for A"); + additionalTest.accept(expectedList.get(index), element); + index++; + } + } + + protected static class ReceiverData { + int numberOfElements = 0; + } + + protected static class IntList { + private final List<Integer> integers = newArrayList(); + public IntList(Integer... values) { + addAll(integers, values); + } + + public List<Integer> toList() { + return integers; + } + + public static IntList list(Integer... values) { + return new IntList(values); + } + } + +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalVariantTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalVariantTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9a55da378b93cc3bcc741d1904dc58b3d9ae96bd --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalVariantTest.java @@ -0,0 +1,62 @@ +package org.jastadd.ragconnect.tests.singleListVariant; + +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; +import singleListVariantInc.ast.*; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test case "list incremental". + * + * @author rschoene - Initial contribution + */ +@Tag("Incremental") +public class SingleListVariantIncrementalVariantTest extends AbstractSingleListVariantTest { + + private Root model; + private MqttHandler handler; + + SingleListVariantIncrementalVariantTest() { + super("singleListVariantInc"); + } + + @Override + protected void createModel() { + model = new Root(); + senderRoot = new SenderRoot(); + model.addSenderRoot((SenderRoot) senderRoot); + + ReceiverRoot localReceiverRoot = new ReceiverRoot(); + model.addReceiverRoot(localReceiverRoot); + receiverRoot = localReceiverRoot; + assertEquals(0, receiverRoot.getT_EmptyList().getNumChild()); + } + + @Override + protected void setupReceiverAndConnectPart() throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // no dependencies + + data = new ReceiverData(); + handler.newConnection(TOPIC_T_all, bytes -> data.numberOfElements += 1); + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantManualVariantTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantManualVariantTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c65a09ec65add4afd7fa5ca8a2809b28e0be4df2 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantManualVariantTest.java @@ -0,0 +1,72 @@ +package org.jastadd.ragconnect.tests.singleListVariant; + +import org.jastadd.ragconnect.tests.TestUtils; +import singleListVariant.ast.*; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test case "list manual". + * + * @author rschoene - Initial contribution + */ +public class SingleListVariantManualVariantTest extends AbstractSingleListVariantTest { + + private Root model; + private MqttHandler handler; + + SingleListVariantManualVariantTest() { + super("singleListVariant"); + } + + @Override + protected void createModel() { + model = new Root(); + senderRoot = new SenderRoot(); + model.addSenderRoot((SenderRoot) senderRoot); + + ReceiverRoot localReceiverRoot = new ReceiverRoot(); + model.addReceiverRoot(localReceiverRoot); + receiverRoot = localReceiverRoot; + assertEquals(0, receiverRoot.getT_EmptyList().getNumChild()); + } + + @Override + protected void setupReceiverAndConnectPart() throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // add dependencies: input + ((SenderRoot) senderRoot).addInputDependencyToT_Empty((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addInputDependencyToT_Token((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addInputDependencyToT_OneChild((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addInputDependencyToT_OneOpt((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addInputDependencyToT_OneList((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addInputDependencyToT_TwoChildren((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addInputDependencyToT_OneOfEach((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addInputDependencyToT_Abstract((SenderRoot) senderRoot); + // add dependencies: shouldSetOptAndList + ((SenderRoot) senderRoot).addShouldSetOptAndListDependencyToT_OneOpt((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addShouldSetOptAndListDependencyToT_OneList((SenderRoot) senderRoot); + ((SenderRoot) senderRoot).addShouldSetOptAndListDependencyToT_OneOfEach((SenderRoot) senderRoot); + + data = new ReceiverData(); + handler.newConnection(TOPIC_T_all, bytes -> data.numberOfElements += 1); + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/AbstractTreeTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/AbstractTreeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ebcccfe9898e374a5dc8d5886d83f999fc0def36 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/AbstractTreeTest.java @@ -0,0 +1,211 @@ +package org.jastadd.ragconnect.tests.tree; + +import org.jastadd.ragconnect.tests.AbstractMqttTest; +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; + +import java.io.IOException; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Base class for test cases "tree manual" and "tree incremental". + * + * @author rschoene - Initial contribution + */ +@Tag("Tree") +public abstract class AbstractTreeTest extends AbstractMqttTest { + protected static final String TOPIC_ALFA = "alfa"; + protected ReceiverData data; + protected TestWrapperReceiverRoot receiverRoot; + + public interface TestWrapperReceiverRoot { + TestWrapperAlfa getAlfa(); + boolean connectAlfa(String mqttUri) throws IOException; + } + public interface TestWrapperAlfa { + TestWrapperBravo getBravo(int i); + int getNumBravo(); + int getNumCharlie(); + int getNumDelta(); + + TestWrapperAlfa getMyself(); + TestWrapperBravo getMyBravo(); + boolean hasOptionalBravo(); + TestWrapperBravo getOptionalBravo(); + TestWrapperCharlie getCharlie(int i); + TestWrapperDelta getDelta(int i); + <T extends TestWrapperBravo> List<T> getMultiBravoList(); + } + public interface TestWrapperBravo { + + TestWrapperCharlie getMyCharlie(); + boolean hasOptionalCharlie(); + TestWrapperCharlie getOptionalCharlie(); + <T extends TestWrapperCharlie> List<T> getMultiCharlieList(); + boolean hasSingleBi1Delta(); + TestWrapperDelta getSingleBi1Delta(); + <T extends TestWrapperDelta> List<T> getMultiBi2DeltaList(); + <T extends TestWrapperDelta> List<T> getMultiBi3DeltaList(); + } + public interface TestWrapperCharlie { + boolean hasMyAlfa(); + TestWrapperAlfa getMyAlfa(); + } + public interface TestWrapperDelta { + boolean hasSingleBack1Alfa(); + TestWrapperAlfa getSingleBack1Alfa(); + boolean hasSingleBack2Alfa(); + TestWrapperAlfa getSingleBack2Alfa(); + <T extends TestWrapperAlfa> List<T> getMultiBack3AlfaList(); + boolean hasSingleBack1Bravo(); + TestWrapperBravo getSingleBack1Bravo(); + TestWrapperBravo getSingleBack2Bravo(); + boolean hasSingleBack2Bravo(); + <T extends TestWrapperBravo> List<T> getMultiBack3BravoList(); + } + + @Override + protected void communicateSendInitialValue() throws InterruptedException { + checkTree(1, 0); + + setInput(1); + checkTree(2, 1); + + setInput(1); + checkTree(2, 1); + + setInput(2); + checkTree(3, 2); + } + + @Override + protected void communicateOnlyUpdatedValue() throws InterruptedException { + checkTree(0, null); + + setInput(1); + checkTree(1, 1); + + setInput(1); + checkTree(1, 1); + + setInput(2); + checkTree(2, 2); + } + + protected abstract void setInput(int input); + + protected void checkTree(int expectedCount, Integer expectedInput) throws InterruptedException { + TestUtils.waitForMqtt(); + + assertEquals(expectedCount, data.numberOfTrees); + if (expectedInput == null) { + assertNull(receiverRoot.getAlfa()); + } else { + assertNotNull(receiverRoot.getAlfa()); + TestWrapperAlfa alfa = receiverRoot.getAlfa(); + assertEquals(4, alfa.getNumBravo()); + assertEquals(4, alfa.getNumCharlie()); + assertEquals(4, alfa.getNumDelta()); + + TestWrapperBravo inputBravo = alfa.getBravo(expectedInput); + TestWrapperCharlie inputCharlie = alfa.getCharlie(expectedInput); + TestWrapperDelta inputDelta = alfa.getDelta(expectedInput); + + // Alfa -> Alfa + assertEquals(alfa, alfa.getMyself()); + + // Alfa -> Bravo + assertEquals(inputBravo, alfa.getMyBravo()); + assertTrue(alfa.hasOptionalBravo()); + assertEquals(inputBravo, alfa.getOptionalBravo()); + assertThat(alfa.getMultiBravoList()).containsExactly( + inputBravo, alfa.getBravo(expectedInput + 1)); + + // Charlie -> Alfa + for (int i = 0; i < 4; i++) { + TestWrapperCharlie Charlie = alfa.getCharlie(i); + if (i == expectedInput) { + assertTrue(Charlie.hasMyAlfa()); + assertEquals(alfa, Charlie.getMyAlfa()); + } else { + assertFalse(Charlie.hasMyAlfa()); + } + } + + // Alfa <-> Delta + for (int i = 0; i < 4; i++) { + TestWrapperDelta Delta = alfa.getDelta(i); + if (i == expectedInput) { + assertTrue(Delta.hasSingleBack1Alfa()); + assertEquals(alfa, Delta.getSingleBack1Alfa()); + } else { + assertFalse(Delta.hasSingleBack1Alfa()); + } + if (i == expectedInput || i == expectedInput + 1) { + assertTrue(Delta.hasSingleBack2Alfa()); + assertEquals(alfa, Delta.getSingleBack2Alfa()); + assertThat(Delta.getMultiBack3AlfaList()).containsExactly(alfa); + } else { + assertFalse(Delta.hasSingleBack2Alfa()); + assertThat(Delta.getMultiBack3AlfaList()).isEmpty(); + } + } + + // Bravo -> Charlie + for (int i = 0; i < 4; i++) { + TestWrapperBravo Bravo = alfa.getBravo(i); + + if (i == expectedInput) { + assertEquals(inputCharlie, Bravo.getMyCharlie()); + assertTrue(Bravo.hasOptionalCharlie()); + assertEquals(inputCharlie, Bravo.getOptionalCharlie()); + assertThat(Bravo.getMultiCharlieList()).containsExactly( + inputCharlie, alfa.getCharlie(expectedInput + 1)); + } else { + assertEquals(alfa.getCharlie(0), Bravo.getMyCharlie()); + assertFalse(Bravo.hasOptionalCharlie()); + assertThat(Bravo.getMultiCharlieList()).isEmpty(); + } + } + + // Bravo <-> Delta + for (int i = 0; i < 4; i++) { + TestWrapperBravo Bravo = alfa.getBravo(i); + TestWrapperDelta Delta = alfa.getDelta(i); + + if (i == expectedInput) { + assertTrue(Bravo.hasSingleBi1Delta()); + assertTrue(Delta.hasSingleBack1Bravo()); + assertEquals(inputDelta, Bravo.getSingleBi1Delta()); + assertEquals(inputBravo, Delta.getSingleBack1Bravo()); + assertThat(Bravo.getMultiBi2DeltaList()).containsExactly( + inputDelta, alfa.getDelta(expectedInput + 1)); + } else { + assertFalse(Bravo.hasSingleBi1Delta()); + assertFalse(Delta.hasSingleBack1Bravo()); + assertThat(Bravo.getMultiBi2DeltaList()).isEmpty(); + } + if (i == expectedInput || i == expectedInput + 1) { + assertThat(Bravo.getMultiBi3DeltaList()).containsExactly( + inputDelta, alfa.getDelta(expectedInput + 1)); + assertTrue(Delta.hasSingleBack2Bravo()); + assertEquals(inputBravo, Delta.getSingleBack2Bravo()); + assertThat(Delta.getMultiBack3BravoList()).containsExactly( + inputBravo, alfa.getBravo(expectedInput + 1)); + } else { + assertThat(Bravo.getMultiBi3DeltaList()).isEmpty(); + assertFalse(Delta.hasSingleBack2Bravo()); + assertThat(Delta.getMultiBack3BravoList()).isEmpty(); + } + } + } + } + + protected static class ReceiverData { + int numberOfTrees = 0; + } + +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6722e159ed064ee253dd79c01fbf3e009ebba874 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java @@ -0,0 +1,76 @@ +package org.jastadd.ragconnect.tests.tree; + +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import treeInc.ast.*; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Test case "tree incremental" + * + * @author rschoene - Initial contribution + */ +@Tag("Incremental") +public class TreeIncrementalTest extends AbstractTreeTest { + + private Root model; + private SenderRoot senderRoot; + private MqttHandler handler; + + @Test + public void checkJacksonReference() { + testJaddContainReferenceToJackson( + Paths.get("src", "test", + "02-after-ragconnect", "treeInc", "RagConnect.jadd"), true); + } + + @Override + protected void createModel() { + model = new Root(); + senderRoot = new SenderRoot(); + model.addSenderRoot(senderRoot); + + receiverRoot = new ReceiverRoot(); + model.addReceiverRoot((ReceiverRoot) receiverRoot); + } + + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // no dependencies + + data = new ReceiverData(); + handler.newConnection(TOPIC_ALFA, bytes -> data.numberOfTrees += 1); + + // connect. important: first receiver, then sender. to not miss initial value. + assertTrue(receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA))); + assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue)); + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } + + protected void setInput(int input) { + senderRoot.setInput(input); + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d60e8fa0590bc60306997a8896856da99b0a7956 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java @@ -0,0 +1,78 @@ +package org.jastadd.ragconnect.tests.tree; + +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import tree.ast.MqttHandler; +import tree.ast.ReceiverRoot; +import tree.ast.Root; +import tree.ast.SenderRoot; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.concurrent.TimeUnit; + +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test case "tree manual" + * + * @author rschoene - Initial contribution + */ +public class TreeManualTest extends AbstractTreeTest { + + private Root model; + private SenderRoot senderRoot; + private MqttHandler handler; + + @Test + public void checkJacksonReference() { + testJaddContainReferenceToJackson( + Paths.get("src", "test", + "02-after-ragconnect", "tree", "RagConnect.jadd"), true); + } + + @Override + protected void createModel() { + model = new Root(); + senderRoot = new SenderRoot(); + model.addSenderRoot(senderRoot); + + receiverRoot = new ReceiverRoot(); + model.addReceiverRoot((ReceiverRoot) receiverRoot); + } + + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // add dependencies + senderRoot.addInputDependency(senderRoot); + + data = new ReceiverData(); + handler.newConnection(TOPIC_ALFA, bytes -> data.numberOfTrees += 1); + + // connect. important: first receiver, then sender. to not miss initial value. + assertTrue(receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA))); + assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue)); + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } + + protected void setInput(int input) { + senderRoot.setInput(input); + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/AbstractTreeAllowedTokensTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/AbstractTreeAllowedTokensTest.java new file mode 100644 index 0000000000000000000000000000000000000000..12b2c99912ac7f2b4401aeefd76a3f91fce06f7b --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/AbstractTreeAllowedTokensTest.java @@ -0,0 +1,221 @@ +package org.jastadd.ragconnect.tests.treeAllowedTokens; + +import org.jastadd.ragconnect.tests.AbstractMqttTest; +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; + +import java.io.IOException; +import java.time.Instant; +import java.time.Period; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Base class for test cases "tree allowed tokens manual" and "tree allowed tokens incremental". + * + * @author rschoene - Initial contribution + */ +@Tag("Tree") +public abstract class AbstractTreeAllowedTokensTest extends AbstractMqttTest { + + protected static final String TOPIC_INPUT1TRUE = "input1/true"; + protected static final String TOPIC_INPUT1FALSE = "input1/false"; + protected static final String TOPIC_INPUT2 = "input2"; + protected static final String TOPIC_INPUT3 = "input3"; + protected static final String TOPIC_ALFA = "alfa"; + protected static final String TOPIC_ALFA_PRIMITIVE = "primitive"; + + protected static final String INSTANT_A = "1999-12-03T10:15:30Z"; + protected static final String INSTANT_B = "2011-12-03T10:15:30Z"; + protected static final String INSTANT_C = "2012-12-03T10:15:30Z"; + + protected TestWrapperReceiverRoot receiverRoot; + + public interface TestWrapperReceiverRoot { + TestWrapperAlfa getAlfa(); + TestWrapperAlfa getAlfaPrimitive(); + boolean connectAlfa(String mqttUri) throws IOException; + boolean connectAlfaPrimitive(String mqttUri) throws IOException; + } + public interface TestWrapperAlfa { + boolean getBooleanValue(); + int getIntValue(); + short getShortValue(); + long getLongValue(); + float getFloatValue(); + double getDoubleValue(); + String getStringValue(); + char getCharValue(); + Instant getInstantValue(); + Period getPeriodValue(); + } + + @Override + protected void communicateSendInitialValue() throws InterruptedException { + checkTree(1, false, 0, INSTANT_A, 0); + checkPrimitiveTree(1, INSTANT_A); + + // flag: false. sendInput1WhenFalse(2) + sendInput1WhenFalse(2); + checkTree(2, false, 2, INSTANT_A, 0); + checkPrimitiveTree(1, INSTANT_A); + + // flag: false. setFlag(false) -> no change + setFlag(false); + checkTree(2, false, 2, INSTANT_A, 0); + checkPrimitiveTree(1, INSTANT_A); + + // flag: false. setFlag(true) + setFlag(true); + checkTree(3, true, 0, INSTANT_A, 0); + checkPrimitiveTree(1, INSTANT_A); + + // flag: true. sendInput1WhenFalse(3) -> no change + sendInput1WhenFalse(3); + checkTree(3, true, 0, INSTANT_A, 0); + checkPrimitiveTree(1, INSTANT_A); + + // flag: true. sendInput1WhenTrue(4) + sendInput1WhenTrue(4); + checkTree(4, true, 4, INSTANT_A, 0); + checkPrimitiveTree(1, INSTANT_A); + + // sendInput2(INSTANT_B) + sendInput2(INSTANT_B); + checkTree(5, true, 4, INSTANT_B, 0); + checkPrimitiveTree(2, INSTANT_B); + + // sendInput2(INSTANT_B) -> no change + sendInput2(INSTANT_B); + checkTree(5, true, 4, INSTANT_B, 0); + checkPrimitiveTree(2, INSTANT_B); + + // sendInput3(5.1) + sendInput3(5.1); + checkTree(6, true, 4, INSTANT_B, 5.1); + checkPrimitiveTree(2, INSTANT_B); + + // sendInput3(5.1) -> no change + sendInput3(5.1); + checkTree(6, true, 4, INSTANT_B, 5.1); + checkPrimitiveTree(2, INSTANT_B); + } + + @Override + protected void communicateOnlyUpdatedValue() throws InterruptedException { + checkTree(0, false, null, null, 0); + checkPrimitiveTree(0, null); + + // flag: false. sendInput1WhenFalse(12) + sendInput1WhenFalse(12); + checkTree(1, false, 12, INSTANT_A, 0); + checkPrimitiveTree(0, null); + + // flag: false. setFlag(false) -> no change + setFlag(false); + checkTree(1, false, 12, INSTANT_A, 0); + checkPrimitiveTree(0, null); + + // flag: false. setFlag(true) + setFlag(true); + checkTree(2, true, 0, INSTANT_A, 0); + checkPrimitiveTree(0, null); + + // flag: true. sendInput1WhenFalse(13) -> no change + sendInput1WhenFalse(13); + checkTree(2, true, 0, INSTANT_A, 0); + checkPrimitiveTree(0, null); + + // flag: true. sendInput1WhenTrue(14) + sendInput1WhenTrue(14); + checkTree(3, true, 14, INSTANT_A, 0); + checkPrimitiveTree(0, null); + + // sendInput2(INSTANT_C) + sendInput2(INSTANT_C); + checkTree(4, true, 14, INSTANT_C, 0); + checkPrimitiveTree(1, INSTANT_C); + + // sendInput2(INSTANT_C) -> no change + sendInput2(INSTANT_C); + checkTree(4, true, 14, INSTANT_C, 0); + checkPrimitiveTree(1, INSTANT_C); + + // sendInput3(15.1) + sendInput3(15.1); + checkTree(5, true, 14, INSTANT_C, 15.1); + checkPrimitiveTree(1, INSTANT_C); + + // sendInput3(15.1) -> no change + sendInput3(15.1); + checkTree(5, true, 14, INSTANT_C, 15.1); + checkPrimitiveTree(1, INSTANT_C); + } + + protected void sendInput1WhenFalse(int value) { + publisher.publish(TOPIC_INPUT1FALSE, TestUtils.DefaultMappings.IntToBytes(value)); + } + + protected void sendInput1WhenTrue(int value) { + publisher.publish(TOPIC_INPUT1TRUE, TestUtils.DefaultMappings.IntToBytes(value)); + } + + protected void sendInput2(String value) { + publisher.publish(TOPIC_INPUT2, TestUtils.DefaultMappings.StringToBytes(value)); + } + + protected void sendInput3(double value) { + publisher.publish(TOPIC_INPUT3, TestUtils.DefaultMappings.DoubleToBytes(value)); + } + + protected abstract void setFlag(boolean value); + + protected void checkTree(int expectedCount, boolean expectedBooleanValue, Integer expectedIntValue, String expectedStringValue, double expectedDoubleValue) throws InterruptedException { + TestUtils.waitForMqtt(); + + assertEquals(expectedCount, data.numberOfTrees); + if (expectedStringValue == null) { + assertNull(receiverRoot.getAlfa()); + } else { + assertNotNull(receiverRoot.getAlfa()); + TestWrapperAlfa alfa = receiverRoot.getAlfa(); + + assertEquals(expectedBooleanValue, alfa.getBooleanValue()); + assertEquals(expectedIntValue, alfa.getIntValue()); + assertEquals(expectedIntValue.shortValue(), alfa.getShortValue()); + assertEquals(expectedIntValue.longValue(), alfa.getLongValue()); + + assertEquals(expectedDoubleValue, alfa.getFloatValue(), TestUtils.DELTA); + assertEquals(expectedDoubleValue, alfa.getDoubleValue(), TestUtils.DELTA); + + assertEquals(expectedStringValue, alfa.getStringValue()); + assertEquals(expectedStringValue.charAt(0), alfa.getCharValue()); + assertEquals(Instant.parse(expectedStringValue), alfa.getInstantValue()); + assertEquals(Period.of(0, 0, expectedIntValue), alfa.getPeriodValue()); + + checkMyEnum(alfa, expectedBooleanValue); + } + } + + protected void checkPrimitiveTree(int expectedCount, String expectedStringValue) { + assertEquals(expectedCount, data.numberOfPrimitiveTrees); + if (expectedStringValue == null) { + assertNull(receiverRoot.getAlfaPrimitive()); + } else { + assertNotNull(receiverRoot.getAlfaPrimitive()); + + TestWrapperAlfa alfaPrimitive = receiverRoot.getAlfaPrimitive(); + assertEquals(expectedStringValue, alfaPrimitive.getStringValue()); + } + } + + protected abstract void checkMyEnum(TestWrapperAlfa alfa, boolean expectedBooleanValue); + + protected ReceiverData data; + + protected static class ReceiverData { + int numberOfTrees = 0; + int numberOfPrimitiveTrees = 0; + } + +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e848ca4059b962aa3431209e23de5ed9ccad4a24 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java @@ -0,0 +1,92 @@ +package org.jastadd.ragconnect.tests.treeAllowedTokens; + +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import treeAllowedTokensInc.ast.*; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.concurrent.TimeUnit; + +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test case "tree allowed tokens incremental" + * + * @author rschoene - Initial contribution + */ +@Tag("Incremental") +public class TreeAllowedTokensIncrementalTest extends AbstractTreeAllowedTokensTest { + + private Root model; + private SenderRoot senderRoot; + private MqttHandler handler; + + @Test + public void checkJacksonReference() { + testJaddContainReferenceToJackson( + Paths.get("src", "test", + "02-after-ragconnect", "treeAllowedTokens", "RagConnect.jadd"), true); + } + + @Override + protected void createModel() { + model = new Root(); + senderRoot = new SenderRoot(); + senderRoot.setFlag(false); + senderRoot.setInput2(INSTANT_A); + model.addSenderRoot(senderRoot); + + receiverRoot = new ReceiverRoot(); + model.addReceiverRoot((ReceiverRoot) receiverRoot); + + model.ragconnectCheckIncremental(); + } + + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // no dependencies + + data = new ReceiverData(); + handler.newConnection(TOPIC_ALFA, bytes -> data.numberOfTrees += 1); + handler.newConnection(TOPIC_ALFA_PRIMITIVE, bytes -> data.numberOfPrimitiveTrees += 1); + + // connect. important: first receiver, then sender. to not miss initial value. + assertTrue(senderRoot.connectInput1WhenFlagIsFalse(mqttUri(TOPIC_INPUT1FALSE))); + assertTrue(senderRoot.connectInput1WhenFlagIsTrue(mqttUri(TOPIC_INPUT1TRUE))); + assertTrue(senderRoot.connectInput2(mqttUri(TOPIC_INPUT2))); + assertTrue(senderRoot.connectInput3(mqttUri(TOPIC_INPUT3))); + assertTrue(receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA))); + assertTrue(receiverRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE))); + assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue)); + assertTrue(senderRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE), writeCurrentValue)); + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } + + protected void setFlag(boolean value) { + senderRoot.setFlag(value); + } + + @Override + protected void checkMyEnum(TestWrapperAlfa alfa, boolean expectedBooleanValue) { + assertEquals(expectedBooleanValue ? MyEnum.TRUE : MyEnum.FALSE, ((Alfa) alfa).getEnumValue()); + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3903834434d1b5031e1db5a68e6a6d5dbdef9a43 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java @@ -0,0 +1,94 @@ +package org.jastadd.ragconnect.tests.treeAllowedTokens; + +import org.jastadd.ragconnect.tests.TestUtils; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import treeAllowedTokens.ast.*; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.concurrent.TimeUnit; + +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Test case "tree allowed tokens manual" + * + * @author rschoene - Initial contribution + */ +public class TreeAllowedTokensManualTest extends AbstractTreeAllowedTokensTest { + + private Root model; + private SenderRoot senderRoot; + private MqttHandler handler; + + @Test + public void checkJacksonReference() { + testJaddContainReferenceToJackson( + Paths.get("src", "test", + "02-after-ragconnect", "treeAllowedTokens", "RagConnect.jadd"), true); + } + + @Override + protected void createModel() { + model = new Root(); + senderRoot = new SenderRoot(); + senderRoot.setFlag(false); + senderRoot.setInput2(INSTANT_A); + model.addSenderRoot(senderRoot); + + receiverRoot = new ReceiverRoot(); + model.addReceiverRoot((ReceiverRoot) receiverRoot); + } + + @Override + protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + // add dependencies + senderRoot.addFlagDependency(senderRoot); + senderRoot.addInput1WhenFlagIsFalseDependency(senderRoot); + senderRoot.addInput1WhenFlagIsTrueDependency(senderRoot); + senderRoot.addInput2Dependency(senderRoot); + senderRoot.addInput3Dependency(senderRoot); + senderRoot.addPrimitiveInput2Dependency(senderRoot); + + data = new ReceiverData(); + handler.newConnection(TOPIC_ALFA, bytes -> data.numberOfTrees += 1); + handler.newConnection(TOPIC_ALFA_PRIMITIVE, bytes -> data.numberOfPrimitiveTrees += 1); + + // connect. important: first receiver, then sender. to not miss initial value. + assertTrue(senderRoot.connectInput1WhenFlagIsFalse(mqttUri(TOPIC_INPUT1FALSE))); + assertTrue(senderRoot.connectInput1WhenFlagIsTrue(mqttUri(TOPIC_INPUT1TRUE))); + assertTrue(senderRoot.connectInput2(mqttUri(TOPIC_INPUT2))); + assertTrue(senderRoot.connectInput3(mqttUri(TOPIC_INPUT3))); + assertTrue(receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA))); + assertTrue(receiverRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE))); + assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue)); + assertTrue(senderRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE), writeCurrentValue)); + } + + @Override + protected void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } + + protected void setFlag(boolean value) { + senderRoot.setFlag(value); + } + + @Override + protected void checkMyEnum(TestWrapperAlfa alfa, boolean expectedBooleanValue) { + assertEquals(expectedBooleanValue ? MyEnum.TRUE : MyEnum.FALSE, ((Alfa) alfa).getEnumValue()); + } +} diff --git a/settings.gradle b/settings.gradle index e7769874a5f3186274ceecbcd445feeb656cf9a4..8a597cfa491920744bb8b5e1d3f103fb4fc95bb1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,9 @@ +pluginManagement { + plugins { + id 'org.jastadd' version '1.13.3' + } +} + rootProject.name = 'ragconnect' include 'relast-preprocessor'