Skip to content
Snippets Groups Projects
Commit 2a281c3e authored by René Schöne's avatar René Schöne
Browse files

working on attributes as endpoint target

- change syntax in connect file, avoid colon
- begin with AttributeTest
- discovered error when endpoint is connected twice
parent 2e0b5b38
No related branches found
No related tags found
1 merge request!25Resolve "Feature: Send endpoint for attributes"
This commit is part of merge request !25. Comments created here will be created in the context of that merge request.
Showing
with 215 additions and 22 deletions
......@@ -2,6 +2,19 @@ aspect Analysis {
// --- isAlreadyDefined ---
syn boolean EndpointDefinition.isAlreadyDefined() = getEndpointTarget().isAlreadyDefined();
syn boolean EndpointTarget.isAlreadyDefined();
eq AttributeEndpointTarget.isAlreadyDefined() {
// define lookup here, as not used elsewhere
int numberOfSameDefs = 0;
for (EndpointTarget target : ragconnect().givenEndpointTargetList()) {
if (target.isAttributeEndpointTarget()) {
AttributeEndpointTarget other = target.asAttributeEndpointTarget();
if (other.getParentTypeDecl().equals(this.getParentTypeDecl()) && other.getName().equals(this.getName())) {
numberOfSameDefs += 1;
}
}
}
return numberOfSameDefs > 1;
}
eq TokenEndpointTarget.isAlreadyDefined() {
return lookupTokenEndpointDefinitions(getToken()).stream()
.filter(containingEndpointDefinition()::matchesType)
......@@ -63,8 +76,9 @@ aspect Analysis {
}
}
// TODO rename entityIsNormalAttribute to actual meaning
syn boolean EndpointTarget.entityIsNormalAttribute();
// TODO AttributeEndpointTarget.entityIsNormalAttribute
eq AttributeEndpointTarget.entityIsNormalAttribute() = true;
eq TokenEndpointTarget.entityIsNormalAttribute() = !getToken().getNTA();
eq TypeEndpointTarget.entityIsNormalAttribute() = !getType().getNTA();
eq ContextFreeTypeEndpointTarget.entityIsNormalAttribute() = false;
......
......@@ -168,6 +168,11 @@ aspect MustacheMappingApplicationAndDefinition {
syn String MEndpointDefinition.preemptiveReturn();
syn String MEndpointDefinition.firstInputVarName();
// TODO check MAttributeSendDefinition
eq MAttributeSendDefinition.firstInputVarName() = getterMethodCall();
eq MAttributeSendDefinition.preemptiveExpectedValue() = lastValueGetterCall();
eq MAttributeSendDefinition.preemptiveReturn() = "return false;";
eq MTokenReceiveDefinition.firstInputVarName() = "message";
eq MTokenReceiveDefinition.preemptiveExpectedValue() = getterMethodCall();
eq MTokenReceiveDefinition.preemptiveReturn() = "return;";
......@@ -323,7 +328,7 @@ aspect MustacheRagConnect {
aspect MustacheReceiveAndSendAndHandleUri {
// === EndpointDefinition ===
syn String EndpointDefinition.connectMethodName() = "connect" + entityName();
syn String EndpointDefinition.connectMethodName() = "connect" + capitalize(entityName());
syn String EndpointDefinition.connectParameterName() = "uriString";
......@@ -348,7 +353,7 @@ aspect MustacheReceiveAndSendAndHandleUri {
} else {
extra = "";
}
return "disconnect" + extra + entityName();
return "disconnect" + extra + capitalize(entityName());
}
syn String EndpointDefinition.entityName() = getEndpointTarget().entityName();
......@@ -371,7 +376,7 @@ aspect MustacheReceiveAndSendAndHandleUri {
syn String EndpointTarget.parentTypeName();
syn String EndpointTarget.entityName();
// TODO AttributeEndpointTarget.getterName
eq AttributeEndpointTarget.getterMethodName() = getName();
eq AttributeEndpointTarget.parentTypeName() = getParentTypeDecl().getName();
eq AttributeEndpointTarget.entityName() = getName();
......@@ -463,9 +468,14 @@ aspect MustacheSendDefinition {
syn String EndpointTarget.senderName() = ragconnect().internalRagConnectPrefix() + "_sender_" + entityName();
eq ContextFreeTypeEndpointTarget.senderName() = null;
// TOOO both updateMethodName and writeMethodName could be defined for MEndpointDefinition using getEndpointDefinition().enitityName()
syn String MEndpointDefinition.updateMethodName();
syn String MEndpointDefinition.writeMethodName();
// TODO check MAttributeSendDefinition. maybe name-clash possible if there is an attribute with same name as a child
eq MAttributeSendDefinition.updateMethodName() = ragconnect().internalRagConnectPrefix() + "_update_" + getEndpointDefinition().entityName();
eq MAttributeSendDefinition.writeMethodName() = ragconnect().internalRagConnectPrefix() + "_writeLastValue_" + getEndpointDefinition().entityName();
eq MTokenReceiveDefinition.updateMethodName() = null;
eq MTokenReceiveDefinition.writeMethodName() = null;
......@@ -582,6 +592,12 @@ aspect AttributesForMustache {
return result;
}
abstract MEndpointDefinition EndpointTarget.createMEndpointDefinition(boolean isSend);
MEndpointDefinition AttributeEndpointTarget.createMEndpointDefinition(boolean isSend) {
if (!isSend) {
throw new IllegalArgumentException("AttributeEndpointTarget can only be sent!");
}
return new MAttributeSendDefinition();
}
MEndpointDefinition TokenEndpointTarget.createMEndpointDefinition(boolean isSend) {
return isSend ? new MTokenSendDefinition() : new MTokenReceiveDefinition();
}
......
abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*;
rel MEndpointDefinition.EndpointDefinition -> EndpointDefinition;
MAttributeSendDefinition : MEndpointDefinition;
abstract MTokenEndpointDefinition : MEndpointDefinition;
MTokenReceiveDefinition : MTokenEndpointDefinition;
MTokenSendDefinition : MTokenEndpointDefinition;
......
......@@ -25,6 +25,12 @@ aspect NewStuff {
syn boolean EndpointTarget.isUntypedEndpointTarget() = false;
eq UntypedEndpointTarget.isUntypedEndpointTarget() = true;
/** Tests if EndpointTarget is a AttributeEndpointTarget.
* @return 'true' if this is a AttributeEndpointTarget, otherwise 'false'
*/
syn boolean EndpointTarget.isAttributeEndpointTarget() = false;
eq AttributeEndpointTarget.isAttributeEndpointTarget() = true;
/** casts a EndpointTarget into a TokenEndpointTarget if possible.
* @return 'this' cast to a TokenEndpointTarget or 'null'
*/
......@@ -52,6 +58,13 @@ aspect NewStuff {
syn UntypedEndpointTarget EndpointTarget.asUntypedEndpointTarget();
eq EndpointTarget.asUntypedEndpointTarget() = null;
eq UntypedEndpointTarget.asUntypedEndpointTarget() = this;
/** casts a EndpointTarget into a AttributeEndpointTarget if possible.
* @return 'this' cast to a AttributeEndpointTarget or 'null'
*/
syn AttributeEndpointTarget EndpointTarget.asAttributeEndpointTarget();
eq EndpointTarget.asAttributeEndpointTarget() = null;
eq AttributeEndpointTarget.asAttributeEndpointTarget() = this;
}
aspect RagConnectNavigation {
......
......@@ -63,7 +63,7 @@ EndpointDefinition endpoint_definition_type
EndpointTarget endpoint_target
= ID.type_name DOT ID.child_name {: return new UntypedEndpointTarget(type_name, child_name, false); :}
| ID.type_name DOT ID.child_name BRACKETS COLON ID.attribute_type_name
| ID.type_name DOT ID.child_name BRACKET_LEFT ID.attribute_type_name BRACKET_RIGHT
{: return new UntypedEndpointTarget(type_name, child_name + ":" + attribute_type_name, true); :}
| ID.type_name {: return new UntypedEndpointTarget(type_name, "", false); :}
;
......
......@@ -8,5 +8,5 @@
"with" { return sym(Terminals.WITH); }
"indexed" { return sym(Terminals.INDEXED); }
"add" { return sym(Terminals.ADD); }
"()" { return sym(Terminals.BRACKETS); }
":" { return sym(Terminals.COLON); }
"(" { return sym(Terminals.BRACKET_LEFT); }
")" { return sym(Terminals.BRACKET_RIGHT); }
......@@ -110,6 +110,10 @@ aspect RagConnectHandler {
senders.forEach(Runnable::run);
}
void run(RagConnectToken token) {
tokenToSender.get(token).run();
}
byte[] getLastValue() {
return lastValue;
}
......@@ -150,6 +154,10 @@ aspect RagConnectHandler {
java.util.Optional.ofNullable(publishers.get(index)).ifPresent(RagConnectPublisher::run);
}
void run(int index, RagConnectToken token) {
java.util.Optional.ofNullable(publishers.get(index)).ifPresent(publisher -> publisher.run(token));
}
byte[] getLastValue(int index) {
RagConnectPublisher publisher = publishers.get(index);
if (publisher == null) {
......
......@@ -18,7 +18,7 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
}{{#IndexBasedListAccess}}, index{{/IndexBasedListAccess}}, connectToken);
{{updateMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}});
if (writeCurrentValue) {
{{writeMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}});
{{writeMethodName}}({{#IndexBasedListAccess}}index, {{/IndexBasedListAccess}}connectToken);
}
success = true;
break;
......@@ -49,7 +49,7 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
{{#IndexBasedListAccess}}index,{{/IndexBasedListAccess}}
() -> {
if (this.{{updateMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}})) {
this.{{writeMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}});
this.{{writeMethodName}}({{#IndexBasedListAccess}}index, {{/IndexBasedListAccess}}connectToken);
}
}
);
......@@ -106,8 +106,8 @@ protected boolean {{parentTypeName}}.{{updateMethodName}}({{#IndexBasedListAcces
return {{senderName}} != null;
}
protected void {{parentTypeName}}.{{writeMethodName}}({{#IndexBasedListAccess}}int index{{/IndexBasedListAccess}}) {
{{senderName}}.run({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}});
protected void {{parentTypeName}}.{{writeMethodName}}({{#IndexBasedListAccess}}int index, {{/IndexBasedListAccess}}RagConnectToken token) {
{{senderName}}.run({{#IndexBasedListAccess}}index, {{/IndexBasedListAccess}}token);
}
{{#needForwardingNTA}}
......
......@@ -50,6 +50,7 @@ dependencies {
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.4.0'
testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.12.1'
testImplementation group: 'org.awaitility', name: 'awaitility', version: '4.1.1'
testImplementation group: 'de.tudresden.inf.st', name: 'dumpAst', version: '0.3.5'
// jackson (for serialization of types)
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.12.1'
......
send SenderRoot.basic():String ;
send SenderRoot.simple():String ;
send SenderRoot.transformed():int ;
send SenderRoot.toReferenceType():A ;
send SenderRoot.toNTA():A ;
send SenderRoot.basic(String) ;
send SenderRoot.simple(String) ;
send SenderRoot.transformed(int) ;
send SenderRoot.toReferenceType(A) ;
send SenderRoot.toNTA(A) ;
AddSuffix maps A a to A {:
A result = new A();
String changedValue = a.getValue() + "post";
result.setValue(changedValue);
result.setInner(new Inner("inner" + changedValue));
result.setInner(new Inner("inner" + a.getInner().getInnerValue()));
return result;
:}
......
Root ::= SenderRoot ReceiverRoot;
Root ::= SenderRoot* ReceiverRoot;
SenderRoot ::= <Input> ;
ReceiverRoot ::=
<FromBasic>
......
package org.jastadd.ragconnect.tests;
import attribute.ast.*;
import attributeInc.ast.*;
import de.tudresden.inf.st.jastadd.dumpAst.ast.Dumper;
import org.junit.jupiter.api.Tag;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import static java.util.function.Predicate.isEqual;
import static org.awaitility.Awaitility.await;
import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
import static org.jastadd.ragconnect.tests.TestUtils.waitForMqtt;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Test case "attribute".
......@@ -13,27 +26,150 @@ import java.io.IOException;
@Tag("Incremental")
@Tag("New")
public class AttributeTest extends AbstractMqttTest {
Root model;
MqttHandler handler;
private static final String TOPIC_WILDCARD = "attr/#";
private static final String TOPIC_BASIC = "attr/string/basic";
private static final String TOPIC_SIMPLE_NO_MAPPING = "attr/string/simple/plain";
private static final String TOPIC_SIMPLE_WITH_MAPPING = "attr/string/simple/mapped";
private static final String TOPIC_TRANSFORMED_NO_MAPPING = "attr/int/transformed/plain";
private static final String TOPIC_TRANSFORMED_WITH_MAPPING = "attr/int/transformed/mapped";
private static final String TOPIC_REFERENCE_TYPE_NO_MAPPING = "attr/a/ref/plain";
private static final String TOPIC_REFERENCE_TYPE_WITH_MAPPING = "attr/a/ref/mapped";
private static final String TOPIC_NTA_NO_MAPPING = "attr/a/nta/plain";
private static final String TOPIC_NTA_WITH_MAPPING = "attr/a/nta/mapped";
private static final String INITIAL_STRING = "initial";
private MqttHandler handler;
private ReceiverData data;
private Root model;
private SenderRoot senderString;
private SenderRoot senderInt;
private SenderRoot senderA;
private ReceiverRoot receiverRoot;
@Override
protected void createModel() {
model = new Root();
// model.trace().setReceiver(TestUtils::logEvent);
senderString = new SenderRoot().setInput(INITIAL_STRING);
senderInt = new SenderRoot().setInput(INITIAL_STRING);
senderA = new SenderRoot().setInput(INITIAL_STRING);
receiverRoot = new ReceiverRoot();
model.addSenderRoot(senderString);
model.addSenderRoot(senderInt);
model.addSenderRoot(senderA);
model.setReceiverRoot(receiverRoot);
}
@Override
protected void setupReceiverAndConnect() throws IOException, InterruptedException {
model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
handler = new MqttHandler().setHost(TestUtils.getMqttHost()).dontSendWelcomeMessage();
assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
data = new ReceiverData();
assertTrue(handler.newConnection(TOPIC_WILDCARD, bytes -> data.numberOfValues += 1));
// connect receive
assertTrue(receiverRoot.connectFromBasic(mqttUri(TOPIC_BASIC)));
assertTrue(receiverRoot.connectFromSimpleNoMapping(mqttUri(TOPIC_SIMPLE_NO_MAPPING)));
assertTrue(receiverRoot.connectFromSimpleWithMapping(mqttUri(TOPIC_SIMPLE_WITH_MAPPING)));
assertTrue(receiverRoot.connectFromTransformedNoMapping(mqttUri(TOPIC_TRANSFORMED_NO_MAPPING)));
assertTrue(receiverRoot.connectFromTransformedWithMapping(mqttUri(TOPIC_TRANSFORMED_WITH_MAPPING)));
assertTrue(receiverRoot.connectFromReferenceTypeNoMapping(mqttUri(TOPIC_REFERENCE_TYPE_NO_MAPPING)));
assertTrue(receiverRoot.connectFromReferenceTypeWithMapping(mqttUri(TOPIC_REFERENCE_TYPE_WITH_MAPPING)));
assertTrue(receiverRoot.connectFromNTANoMapping(mqttUri(TOPIC_NTA_NO_MAPPING)));
assertTrue(receiverRoot.connectFromNTAWithMapping(mqttUri(TOPIC_NTA_WITH_MAPPING)));
// connect send, and wait to receive (if writeCurrentValue is set)
assertTrue(senderString.connectBasic(mqttUri(TOPIC_BASIC), isWriteCurrentValue()));
assertTrue(senderString.connectSimple(mqttUri(TOPIC_SIMPLE_NO_MAPPING), isWriteCurrentValue()));
assertTrue(senderString.connectSimple(mqttUri(TOPIC_SIMPLE_WITH_MAPPING), isWriteCurrentValue()));
assertTrue(senderInt.connectTransformed(mqttUri(TOPIC_TRANSFORMED_NO_MAPPING), isWriteCurrentValue()));
assertTrue(senderInt.connectTransformed(mqttUri(TOPIC_TRANSFORMED_WITH_MAPPING), isWriteCurrentValue()));
assertTrue(senderA.connectToReferenceType(mqttUri(TOPIC_REFERENCE_TYPE_NO_MAPPING), isWriteCurrentValue()));
assertTrue(senderA.connectToReferenceType(mqttUri(TOPIC_REFERENCE_TYPE_WITH_MAPPING), isWriteCurrentValue()));
assertTrue(senderA.connectToNTA(mqttUri(TOPIC_NTA_NO_MAPPING), isWriteCurrentValue()));
assertTrue(senderA.connectToNTA(mqttUri(TOPIC_NTA_WITH_MAPPING), isWriteCurrentValue()));
waitForValue(senderString.basic(), receiverRoot::getFromBasic);
waitForValue(senderString.simple(), receiverRoot::getFromSimpleNoMapping);
waitForNonNull(receiverRoot::getFromReferenceTypeNoMapping);
waitForNonNull(receiverRoot::getFromNTANoMapping);
}
private <T> void waitForValue(T expectedValue, Callable<T> callable) {
if (isWriteCurrentValue()) {
await().until(callable, isEqual(expectedValue));
}
}
private <T> void waitForNonNull(Callable<T> callable) {
if (isWriteCurrentValue()) {
await().until(callable, Predicate.not(isEqual(null)));
}
}
@Override
protected void communicateSendInitialValue() throws IOException, InterruptedException {
// basic, simple <-- senderString
// transformed <-- senderInt
// ref-type, nta <-- senderA
check(9, INITIAL_STRING, INITIAL_STRING + "Post", INITIAL_STRING, INITIAL_STRING, INITIAL_STRING);
senderString.setInput("test-01");
check(12, "test-01", "test-01Post", INITIAL_STRING, INITIAL_STRING, INITIAL_STRING);
}
@Override
protected void communicateOnlyUpdatedValue() throws IOException, InterruptedException {
waitForMqtt();
}
private void check(int numberOfValues, String basic, String simple, String transformed,
String refType, String nta) {
awaitEquals(numberOfValues, () -> data.numberOfValues, "numberOfValues");
try {
Path tempFile = Files.createTempFile("receiverRoot", "yml");
Dumper.read(receiverRoot).dumpAsYaml(tempFile, false);
String content = Files.readString(tempFile);
logger.debug("receiverRoot\n" + content);
} catch (IOException e) {
e.printStackTrace();
}
awaitEquals(basic, receiverRoot::getFromBasic, "basic");
awaitEquals(simple, receiverRoot::getFromSimpleNoMapping, "simple");
awaitEquals(simple + "post", receiverRoot::getFromSimpleWithMapping, "simple mapped");
int transformedLength = transformed.length();
awaitEquals(transformedLength, receiverRoot::getFromTransformedNoMapping, "transformed");
awaitEquals(transformedLength + 1, receiverRoot::getFromTransformedWithMapping, "transformed mapped");
checkA(refType, "1",
receiverRoot.getFromReferenceTypeNoMapping(), "ref-type");
checkA(refType + "post", "inner1",
receiverRoot.getFromReferenceTypeWithMapping(), "ref-type mapped");
checkA(nta, "2",
receiverRoot.getFromNTANoMapping(), "nta");
checkA(nta + "post", "inner2",
receiverRoot.getFromNTAWithMapping(), "nta mapped");
}
private void checkA(String expectedValue, String expectedInner, A actual, String message) {
awaitEquals(expectedValue, actual::getValue, message + " value");
awaitEquals(expectedInner, actual.getInner()::getInnerValue, message + " inner");
}
private <T> void awaitEquals(T expected, Callable<T> actual, String alias) {
await(alias).atMost(1500, TimeUnit.MILLISECONDS).until(actual, isEqual(expected));
}
@Override
......@@ -45,4 +181,8 @@ public class AttributeTest extends AbstractMqttTest {
model.ragconnectCloseConnections();
}
}
private static class ReceiverData {
int numberOfValues = 0;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment