diff --git a/ragconnect.base/.gitignore b/ragconnect.base/.gitignore index 87b4cdd3d7c6a41502ca98703abeeb69a1d536fb..4c0fcfac7f2f2c14ac6e81d173d3a2f7d9d99a55 100644 --- a/ragconnect.base/.gitignore +++ b/ragconnect.base/.gitignore @@ -3,3 +3,4 @@ src/gen-res/ src/gen/ out/ *.class +/parameters.txt diff --git a/ragconnect.base/build.gradle b/ragconnect.base/build.gradle index 7af3fc622eca2e5c5e771a1bb2c9f1f81b2f9a5c..2d1d47c54cfce1e7e7f498016e4838b830a99c50 100644 --- a/ragconnect.base/build.gradle +++ b/ragconnect.base/build.gradle @@ -187,6 +187,15 @@ ext { } application.mainClassName = "${mainClassName}" +task runParameters(type: JavaExec) { + group 'application' + description 'Run using parameters.txt (line-separated with comments)' + classpath sourceSets.main.runtimeClasspath + main = "org.jastadd.ragconnect.compiler.Compiler" + args new File('ragconnect.base/parameters.txt').text.strip().split("\n").dropWhile { it.startsWith("#") } + standardInput = System.in +} + jar { manifest.attributes "Main-Class": "${mainClassName}" } diff --git a/ragconnect.base/src/main/jastadd/Intermediate.jadd b/ragconnect.base/src/main/jastadd/Intermediate.jadd index dfd74a96e387fd82d9d34b7dd15141220776ebc7..1a24fa97c1c561bd841944a82231fe2fd40aad69 100644 --- a/ragconnect.base/src/main/jastadd/Intermediate.jadd +++ b/ragconnect.base/src/main/jastadd/Intermediate.jadd @@ -521,14 +521,14 @@ aspect MustacheSendDefinition { java.util.List<SendIncrementalObserverEntry> result = new java.util.ArrayList<>(); // "{{getterMethodName}}{{#IndexBasedListAccess}}_int{{/IndexBasedListAccess}}" result.add(SendIncrementalObserverEntry.of(getterMethodName() + (getIndexBasedListAccess() ? "_int" : ""), - getIndexBasedListAccess(), "index")); + getIndexBasedListAccess(), getIndexBasedListAccess() ? "index" : "null")); if (indexBasedAccessAndTargetIsNTA()) { // "{{getterMethodName}}List" result.add(SendIncrementalObserverEntry.of(getterMethodName() + "List", false, "index")); } if (targetIsAttribute()) { // "{{parentTypeName}}_{{getterMethodName}}{{#IndexBasedListAccess}}_int{{/IndexBasedListAccess}}" - result.add(SendIncrementalObserverEntry.of(parentTypeName() + "_" + getterMethodName() + (getIndexBasedListAccess() ? "_int" : ""), getIndexBasedListAccess(), "index")); + result.add(SendIncrementalObserverEntry.of(parentTypeName() + "_" + getterMethodName() + (getIndexBasedListAccess() ? "_int" : ""), getIndexBasedListAccess(), getIndexBasedListAccess() ? "index" : "null")); } return result; } @@ -611,10 +611,10 @@ containingEndpointDefinition().getIndexBasedListAccess()); syn String EndpointDefinition.typeName() = type().getName(); syn String MEndpointDefinition.typeName() = getEndpointDefinition().typeName(); - static SendIncrementalObserverEntry SendIncrementalObserverEntry.of(String attributeString, boolean useParams, Object params) { + static SendIncrementalObserverEntry SendIncrementalObserverEntry.of(String attributeString, boolean compareParams, Object params) { return new SendIncrementalObserverEntry() - .setParams(useParams ? params : null) - .setCompareParams(useParams) + .setParams(params) + .setCompareParams(compareParams) .setAttributeString(attributeString); } } diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache index f2195dd2d858a26ce5382ab2ce259bbf8b0fbeb6..c428021bf1eab32bf8baac8585a3f801ba8ad6ad 100644 --- a/ragconnect.base/src/main/resources/ragconnect.mustache +++ b/ragconnect.base/src/main/resources/ragconnect.mustache @@ -163,36 +163,17 @@ aspect RagConnectObserver { node.trace().setReceiver(this); } - void add(RagConnectToken connectToken, ASTNode node, Runnable attributeCall, String... attributeStrings) { - internal_add(connectToken, node, attributeStrings, false, null, attributeCall); - } - void add(RagConnectToken connectToken, ASTNode node, Object params, Runnable attributeCall, String... attributeStrings) { - internal_add(connectToken, node, attributeStrings, true, params, attributeCall); - } - - private void internal_add(RagConnectToken connectToken, ASTNode node, String[] attributeStrings, - boolean compareParams, Object params, Runnable attributeCall) { - if (attributeStrings.length == 0) { - {{logWarn}}("No attribute string given to observe for {{log_}}!", connectToken.uri); - } - for (String attributeString : attributeStrings) { - internal_add(connectToken, node, attributeString, compareParams, params, attributeCall); - } - } - - private void internal_add(RagConnectToken connectToken, ASTNode node, String attributeString, - boolean compareParams, Object params, Runnable attributeCall) { + void add(RagConnectToken connectToken, ASTNode node, boolean compareParams, Object params, + Runnable attributeCall, String attributeString) { {{#configLoggingEnabledForIncremental}} {{logDebug}}("** observer add: {{log_}} on {{log_}}{{log_}}", node, attributeString, (compareParams ? " (parameterized)" : "")); {{/configLoggingEnabledForIncremental}} - // either add to an existing entry (with same node, attribute) or create new entry - //TODO check case, where runnable differs (and baseMembersEqual returns true). need list of runnables, and map<connectToken, runnable> to remove them! + // either add to an existing entry (with same node, attribute, params) or create new entry boolean needNewEntry = true; for (RagConnectObserverEntry entry : observedNodes) { if (entry.baseMembersEqualTo(node, attributeString, true, params)) { entry.connectList.add(connectToken); - {{logError}}("baseMembersEqualTo node: {{log_}} atrStr: {{log_}} compare: {{log_}} params: {{log_}}", node, attributeString, compareParams, params); needNewEntry = false; break; } diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache index 6bfeee4ea8cc546b500ed3ad6dada275a95038ff..2b8a6eb3536e81d9621f56866a375462562cf299 100644 --- a/ragconnect.base/src/main/resources/sendDefinition.mustache +++ b/ragconnect.base/src/main/resources/sendDefinition.mustache @@ -68,7 +68,8 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete {{observerInstanceSingletonMethodName}}().add( connectToken, this, - {{#CompareParams}}{{Params}},{{/CompareParams}} + {{CompareParams}}, + {{Params}}, () -> { if (this.{{updateMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}})) { this.{{writeMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}}); diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java index d84f713cff4215bea169a51fc5ef72943ee10a17..d649168f1f544ca552aaa7e04851697138bde640 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java @@ -3,12 +3,10 @@ package org.jastadd.ragconnect.tests; import indexedSendInc.ast.*; import org.assertj.core.api.Assertions; import org.assertj.core.groups.Tuple; -import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.IOException; -import java.util.StringJoiner; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; @@ -25,7 +23,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * @author rschoene - Initial contribution */ @Tag("Incremental") -@Tag("New") public class IndexedSendTest extends AbstractMqttTest { private static final String TOPIC_A_MANY_NORMAL_WILDCARD = "a-many/#"; @@ -100,27 +97,37 @@ public class IndexedSendTest extends AbstractMqttTest { assertTrue(receiverRoot.connectManyAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_WILDCARD))); // connect send, and wait to receive (if writeCurrentValue is set) - assertTrue(connectNTAsInstead ? - senderRoot.connectA(mqttUri(TOPIC_A_MANY_NORMAL_0), 0, isWriteCurrentValue()) : - senderRoot.connectMultipleA(mqttUri(TOPIC_A_MANY_NORMAL_0), 0, isWriteCurrentValue())); + assertConnectAOrMultipleA(TOPIC_A_MANY_NORMAL_0, 0); waitForValue(receiverRoot::getNumManyA, 1); - assertTrue(connectNTAsInstead ? - senderRoot.connectA(mqttUri(TOPIC_A_MANY_NORMAL_1), 1, isWriteCurrentValue()) : - senderRoot.connectMultipleA(mqttUri(TOPIC_A_MANY_NORMAL_1), 1, isWriteCurrentValue())); + assertConnectAOrMultipleA(TOPIC_A_MANY_NORMAL_1, 1); waitForValue(receiverRoot::getNumManyA, 2); - assertTrue(connectNTAsInstead ? - senderRoot.connectComputedA(mqttUri(TOPIC_A_MANY_SUFFIX_0), 0, isWriteCurrentValue()) : - senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_0), 0, isWriteCurrentValue())); + assertConnectComputedAOrMultipleAWithSuffix(TOPIC_A_MANY_SUFFIX_0, 0); waitForValue(receiverRoot::getNumManyAWithSuffix, 1); - assertTrue(connectNTAsInstead ? - senderRoot.connectComputedA(mqttUri(TOPIC_A_MANY_SUFFIX_1), 1, isWriteCurrentValue()) : - senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_1), 1, isWriteCurrentValue())); + assertConnectComputedAOrMultipleAWithSuffix(TOPIC_A_MANY_SUFFIX_1, 1); waitForValue(receiverRoot::getNumManyAWithSuffix, 2); } + private void assertConnectAOrMultipleA(String topic, int index) throws IOException { + assertTrue(connectNTAsInstead ? + senderRoot.connectA(mqttUri(topic), index, isWriteCurrentValue()) : + senderRoot.connectMultipleA(mqttUri(topic), index, isWriteCurrentValue())); + } + + private void assertConnectComputedAOrMultipleAWithSuffix(String topic, int index) throws IOException { + assertTrue(connectNTAsInstead ? + senderRoot.connectComputedA(mqttUri(topic), index, isWriteCurrentValue()) : + senderRoot.connectMultipleAWithSuffix(mqttUri(topic), index, isWriteCurrentValue())); + } + + private void assertDisconnectComputedAOrMultipleAWithSuffix(String topic) throws IOException { + assertTrue(connectNTAsInstead ? + senderRoot.disconnectComputedA(mqttUri(topic)) : + senderRoot.disconnectMultipleAWithSuffix(mqttUri(topic))); + } + private void checkList(String name, Tuple expected, Supplier<JastAddList<A>> actual) { Assertions.assertThat(actual.get()).extracting("Value") .as(name) @@ -199,13 +206,10 @@ public class IndexedSendTest extends AbstractMqttTest { .put(CHECK_MANY_A, manyAtIndex1 != null ? tuple("changedValue", manyAtIndex1) : tuple("changedValue")) .check(); - if (!connectNTAsInstead) { return; } // TODO remove after testing NTAs is complete - // setting same value must not change data, and must not trigger a new sent message listA0.setValue("changedValue"); checker.check(); - logger.error(prettyPrint(senderRoot.getAList())); listA1.setValue(""); checker.incNumberOfValues().put(CHECK_MANY_A, tuple("changedValue", "")).check(); @@ -216,26 +220,39 @@ public class IndexedSendTest extends AbstractMqttTest { .check(); // adding a new element does not automatically send it - A listA3InSuffix = createA("out"); - senderRoot.addMultipleAWithSuffix(listA3InSuffix); + A listA2InSuffix = createA("out"); + senderRoot.addMultipleAWithSuffix(listA2InSuffix); + checker.check(); + + // only after connecting it, the element gets sent (for SendInitialValue case) + assertConnectComputedAOrMultipleAWithSuffix(TOPIC_A_MANY_SUFFIX_2, 2); + if (isWriteCurrentValue()) { + checker.incNumberOfValues() + .put(CHECK_WITH_SUFFIX, suffixAtIndex0 != null ? tuple(suffixAtIndex0, "repost", "outpost") : tuple("repost", "outpost")); + } checker.check(); - // only after connecting it, the element gets sent - assertTrue(senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_2), 2, true)); + // changing the value of the newly added element will send it + listA2InSuffix.setValue("goal"); checker.incNumberOfValues() - .put(CHECK_WITH_SUFFIX, suffixAtIndex0 != null ? tuple(suffixAtIndex0, "repost", "outpost") : tuple("repost", "outpost")) - .check(); + .put(CHECK_WITH_SUFFIX, suffixAtIndex0 != null ? tuple(suffixAtIndex0, "repost", "goalpost") : tuple("repost", "goalpost")); + checker.check(); - // after successful disconnect, no messages will be sent - assertTrue(senderRoot.disconnectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_0))); + // after successful disconnect for index 0, no messages will be sent + assertDisconnectComputedAOrMultipleAWithSuffix(TOPIC_A_MANY_SUFFIX_0); listA0InSuffix.setValue("willBeIgnored"); checker.check(); - } - private String prettyPrint(JastAddList<A> aList) { - StringJoiner sj = new StringJoiner(", ", "[", "]"); - aList.forEach(a -> sj.add(a.getValue() + "(" + a.getInner().getInnerValue() + ")")); - return sj.toString(); + // for index 1 (not disconnected), messages will be sent still + listA1InSuffix.setValue("sign"); + checker.incNumberOfValues() + .put(CHECK_WITH_SUFFIX, suffixAtIndex0 != null ? tuple(suffixAtIndex0, "signpost", "goalpost") : tuple("signpost", "goalpost")) + .check(); + + // after successful disconnect for index 1, no messages will be sent anymore + assertDisconnectComputedAOrMultipleAWithSuffix(TOPIC_A_MANY_SUFFIX_1); + listA1InSuffix.setValue("willBeIgnored"); + checker.check(); } @Override 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 7b2403fd55b966be78f70064dc3fd73d92d656a1..4a74e6b3a205f092a53b73897656151464f8e8e1 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 @@ -27,9 +27,7 @@ import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; -import static java.util.Collections.addAll; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.util.Lists.newArrayList; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -99,6 +97,12 @@ public class TestUtils { return outPath; } + public static <T> String prettyPrint(Iterable<T> aList, Function<T, String> elementPrinter) { + StringJoiner sj = new StringJoiner(", ", "[", "]"); + aList.forEach(element -> sj.add(elementPrinter.apply(element))); + return sj.toString(); + } + public static void assertLinesMatch(String directory, String expectedName, String out) throws IOException { Path expectedPath = Paths.get(TestUtils.INPUT_DIRECTORY_PREFIX) .resolve(directory)