diff --git a/.gitmodules b/.gitmodules index 0cf1e8a6d073c7c58b0881fe53f5642e6a1022e7..0163ef86549d9ee0ba792961799f705881d1f893 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ [submodule "relast-preprocessor"] path = relast-preprocessor url = ../relast-preprocessor.git + branch = jastadd-fix-inc-param-debug [submodule "ragconnect.base/src/main/jastadd/mustache"] path = ragconnect.base/src/main/jastadd/mustache url = ../mustache diff --git a/libs/jastadd2.jar b/libs/jastadd2.jar new file mode 100644 index 0000000000000000000000000000000000000000..6c69edae63a847e4274af7405c754b31b0518d3b Binary files /dev/null and b/libs/jastadd2.jar differ 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 b14ed91e9b6ba31824e7ffb72995c781e9689015..c7879cf20250fa47aa418da03797aeb71d4e19c1 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 @@ -225,6 +225,7 @@ public class Compiler extends AbstractCompiler { ASTNode.loggingEnabledForWrites = optionLogWrites.value(); // reuse "--incremental" option of JastAdd ASTNode.incrementalOptionActive = getConfiguration().incremental() && getConfiguration().traceFlush(); + printMessage("ASTNode.incrementalOptionActive = " + ASTNode.incrementalOptionActive); ASTNode.usesMqtt = optionProtocols.hasValue(OPTION_PROTOCOL_MQTT); ASTNode.usesRest = optionProtocols.hasValue(OPTION_PROTOCOL_REST); return ragConnect; diff --git a/ragconnect.base/src/main/resources/MqttHandler.jadd b/ragconnect.base/src/main/resources/MqttHandler.jadd index 987c0c451317098cdeabd7b2d7009a2e8ceda301..ab56c0405dd518204378d840d85954e0e27b784f 100644 --- a/ragconnect.base/src/main/resources/MqttHandler.jadd +++ b/ragconnect.base/src/main/resources/MqttHandler.jadd @@ -161,17 +161,13 @@ 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(); - // TODO: maybe copy list here to avoid concurrent modification. or use a concurrent list. java.util.List<java.util.function.Consumer<byte[]>> callbackList = new java.util.ArrayList<>(callbacks.get(topicString)); if (callbackList == null || callbackList.isEmpty()) { logger.debug("Got a message at {}, but no callback to call. Forgot to subscribe?", topic); } else { byte[] message = body.toByteArray(); - logger.debug("initial: {}", callbackList); for (java.util.function.Consumer<byte[]> callback : callbackList) { - logger.debug("before: {}", callbackList); callback.accept(message); - logger.debug("after: {}", callbackList); } } ack.onSuccess(null); // always acknowledge message diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache index d82f43093071a7bd4fb5ad05d341b5fc13f4e1cf..45e5e11c33c8988286c4b209f9d04ee956f5da29 100644 --- a/ragconnect.base/src/main/resources/ragconnect.mustache +++ b/ragconnect.base/src/main/resources/ragconnect.mustache @@ -1,6 +1,6 @@ {{#usesMqtt}}{{> mqtt}}{{/usesMqtt}} {{> handler}} -aspect ROS2RAG { +aspect RagConnect { {{#ReceiveDefinitions}} {{> receiveDefinition}} {{/ReceiveDefinitions}} diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle index 0788d6600618ef2adb3f1f524737278208bf6960..d2e772edfb66165bc18e1bf33614d2e5468ecb59 100644 --- a/ragconnect.tests/build.gradle +++ b/ragconnect.tests/build.gradle @@ -35,7 +35,9 @@ repositories { dependencies { implementation project(':ragconnect.base') - runtime group: 'org.jastadd', name: 'jastadd', version: '2.3.4' +// runtime group: 'org.jastadd', name: 'jastadd', version: '2.3.4' + runtime 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' @@ -74,6 +76,7 @@ task allTests(type: Test, dependsOn: testClasses) { useJUnitPlatform { includeTags 'mqtt' +// excludeTags '!NewTest' } } @@ -339,8 +342,46 @@ task compileTutorialTest(type: RelastTest) { moreInputFiles 'src/test/01-input/tutorial/Test.jadd', 'src/test/02-after-ragconnect/tutorial/MqttHandler.jadd', 'src/test/02-after-ragconnect/tutorial/RagConnect.jadd' -// extraJastAddOptions "--tracing=cache,flush" } compileTestJava.dependsOn compileTutorialTest compileTutorialTest.dependsOn preprocessTutorialTest + +// --- Test: incremental --- +task preprocessIncrementalTest(type: JavaExec, group: 'verification') { + doFirst { + delete 'src/test/02-after-ragconnect/incremental/Test.relast', + 'src/test/02-after-ragconnect/incremental/MqttHandler.jadd', + 'src/test/02-after-ragconnect/incremental/RagConnect.jadd' + } + + classpath = sourceSets.main.runtimeClasspath + main = 'org.jastadd.ragconnect.compiler.Compiler' + args '--o=src/test/02-after-ragconnect/incremental', + 'src/test/01-input/incremental/Test.relast', + 'src/test/01-input/incremental/Test.connect', + '--rootNode=A', + '--tracing=cache,flush', + '--incremental=param', + '--logReads', '--logWrites', '--verbose' +} + +task compileIncrementalTest(type: RelastTest) { + useJastAddNames = true + jastAddList = 'JastAddList' + relastFiles 'src/test/02-after-ragconnect/incremental/Test.relast', + 'src/test/02-after-ragconnect/incremental/RagConnect.relast' + grammarName = 'src/test/03-after-relast/incremental/incremental' + packageName = 'incremental.ast' + moreInputFiles 'src/test/01-input/incremental/Test.jadd', + 'src/test/02-after-ragconnect/incremental/MqttHandler.jadd', + 'src/test/02-after-ragconnect/incremental/RagConnect.jadd' + extraJastAddOptions '--tracing=cache,flush', + '--incremental=param', + '--cache=all', + '--rewrite=cnta', + '--flush=full' +} + +compileTestJava.dependsOn compileIncrementalTest +compileIncrementalTest.dependsOn preprocessIncrementalTest 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/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..4c323595ae5e339a0bfeeb0e357a206b33682542 --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java @@ -0,0 +1,167 @@ +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("NewTest") +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); + } + + @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 = new String(bytes); + }); + handler.newConnection(TOPIC_OUT_B1, bytes -> { + dataB1.numberOfStringValues += 1; + dataB1.lastStringValue = new String(bytes); + }); + handler.newConnection(TOPIC_OUT_B2, bytes -> { + dataB2.numberOfStringValues += 1; + dataB2.lastStringValue = new String(bytes); + }); + + model.connectInput(mqttUri(TOPIC_IN)); + model.connectOutputOnA(mqttUri(TOPIC_OUT_A), writeCurrentValue); + b1.connectOutputOnB(mqttUri(TOPIC_OUT_B1), writeCurrentValue); + 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 { + handler.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/relast-preprocessor b/relast-preprocessor index c00441c03dc6723a08de0fcb041254a99497774f..b538a7f709167c5f56fe65e6d9e9f02179cacaef 160000 --- a/relast-preprocessor +++ b/relast-preprocessor @@ -1 +1 @@ -Subproject commit c00441c03dc6723a08de0fcb041254a99497774f +Subproject commit b538a7f709167c5f56fe65e6d9e9f02179cacaef