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

WIP: Working on bugfixes for attributes

- parse complete JavaUse as type for attribute targets
- add new observer entries (inc. eval) for NTA and attribute targets
- tests currently not passing
parent fe5bffe0
Branches
No related tags found
1 merge request!32Resolve "Allow collection and circular attributes as endpoint targets"
Pipeline #13781 failed
Showing
with 310 additions and 100 deletions
......@@ -34,7 +34,7 @@ dependencies {
relast group: 'org.jastadd', name: 'relast', version: "0.3.0-137"
implementation group: 'org.jastadd', name: 'relast-preprocessor', version: "${preprocessor_version}"
implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: "${mustache_java_version}"
runtimeOnly group: 'org.jastadd', name: 'jastadd2', version: '2.3.5-dresden'
jastadd2 group: 'org.jastadd', name: 'jastadd2', version: '2.3.5-dresden'
api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
}
......
......@@ -506,13 +506,36 @@ aspect MustacheSendDefinition {
syn String EndpointDefinition.forwardingNTA_Name() = getEndpointTarget().forwardingNTA_Name();
syn String EndpointDefinition.forwardingNTA_Type() = getEndpointTarget().forwardingNTA_Type();
syn boolean EndpointDefinition.targetIsAttribute() = getEndpointTarget().isAttributeEndpointTarget();
syn boolean EndpointDefinition.indexBasedAccessAndTargetIsNTA() {
return typeIsList() && getIndexBasedListAccess() && !needForwardingNTA();
}
syn boolean EndpointDefinition.relationEndpointWithListRole() = getEndpointTarget().relationEndpointWithListRole();
syn String EndpointDefinition.senderName() = getEndpointTarget().senderName();
syn java.util.List<SendIncrementalObserverEntry> EndpointDefinition.sendIncrementalObserverEntries() {
// todo maybe getterMethodName needs to be change for indexed send
java.util.List<SendIncrementalObserverEntry> result = new java.util.ArrayList<>();
// "{{getterMethodName}}{{#IndexBasedListAccess}}_int{{/IndexBasedListAccess}}"
result.add(SendIncrementalObserverEntry.of(getterMethodName() + (getIndexBasedListAccess() ? "_int" : ""),
getIndexBasedListAccess(), "index"));
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"));
}
return result;
}
syn boolean EndpointDefinition.shouldNotResetValue() = getSend() && !getEndpointTarget().hasAttributeResetMethod();
syn String EndpointDefinition.tokenResetMethodName() = getterMethodName() + "_reset";
syn String EndpointDefinition.tokenResetMethodName() = getEndpointTarget().tokenResetMethodName();
syn String EndpointDefinition.updateMethodName() = toMustache().updateMethodName();
......@@ -552,6 +575,9 @@ containingEndpointDefinition().getIndexBasedListAccess());
syn String EndpointTarget.senderName() = ragconnect().internalRagConnectPrefix() + "_sender_" + entityName();
eq ContextFreeTypeEndpointTarget.senderName() = null;
syn String EndpointTarget.tokenResetMethodName() = getterMethodName() + (
typeIsList() && containingEndpointDefinition().getIndexBasedListAccess() ? "List" : "") + "_reset";
syn String MEndpointDefinition.updateMethodName();
syn String MEndpointDefinition.writeMethodName();
......@@ -584,6 +610,13 @@ containingEndpointDefinition().getIndexBasedListAccess());
syn String EndpointDefinition.typeName() = type().getName();
syn String MEndpointDefinition.typeName() = getEndpointDefinition().typeName();
static SendIncrementalObserverEntry SendIncrementalObserverEntry.of(String attributeString, boolean useParams, Object params) {
return new SendIncrementalObserverEntry()
.setParams(useParams ? params : null)
.setCompareParams(useParams)
.setAttributeString(attributeString);
}
}
aspect MustacheTokenComponent {
......
......@@ -15,3 +15,5 @@ MContextFreeTypeSendDefinition : MContextFreeTypeEndpointDefinition;
MInnerMappingDefinition;
rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition;
SendIncrementalObserverEntry ::= <Params:Object> <CompareParams:boolean> <AttributeString>;
......@@ -63,8 +63,8 @@ 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 BRACKET_LEFT ID.attribute_type_name BRACKET_RIGHT
{: return new UntypedEndpointTarget(type_name, child_name + ":" + attribute_type_name, true); :}
| ID.type_name DOT ID.child_name BRACKET_LEFT java_type_use.attribute_type_name BRACKET_RIGHT
{: return new UntypedEndpointTarget(type_name, child_name + ":" + attribute_type_name.prettyPrint(), true); :}
| ID.type_name {: return new UntypedEndpointTarget(type_name, "", false); :}
;
......
......@@ -110,6 +110,7 @@ aspect RagConnectObserver {
final boolean compareParams;
final Object params;
final Runnable attributeCall;
//final RagConnectToken connectToken;
final java.util.List<RagConnectToken> connectList = new java.util.ArrayList<>();
RagConnectObserverEntry(ASTNode node, String attributeString,
......@@ -126,11 +127,11 @@ aspect RagConnectObserver {
}
boolean baseMembersEqualTo(ASTNode otherNode, String otherAttributeString,
boolean otherCompareParams, Object otherParams) {
boolean forceCompareParams, Object otherParams) {
return this.node.equals(otherNode) &&
this.attributeString.equals(otherAttributeString) &&
this.compareParams == otherCompareParams &&
(!this.compareParams || java.util.Objects.equals(this.params, otherParams));
//this.compareParams == otherCompareParams &&
(!(this.compareParams || forceCompareParams) || java.util.Objects.equals(this.params, otherParams));
}
}
......@@ -162,11 +163,21 @@ aspect RagConnectObserver {
node.trace().setReceiver(this);
}
void add(RagConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) {
internal_add(connectToken, node, attributeString, false, null, attributeCall);
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);
}
void add(RagConnectToken connectToken, ASTNode node, String attributeString, Object params, Runnable attributeCall) {
internal_add(connectToken, node, attributeString, true, params, attributeCall);
}
private void internal_add(RagConnectToken connectToken, ASTNode node, String attributeString,
......@@ -176,10 +187,12 @@ aspect RagConnectObserver {
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!
boolean needNewEntry = true;
for (RagConnectObserverEntry entry : observedNodes) {
if (entry.baseMembersEqualTo(node, attributeString, compareParams, params)) {
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;
}
......@@ -193,16 +206,14 @@ aspect RagConnectObserver {
}
void remove(RagConnectToken connectToken) {
RagConnectObserverEntry entryToDelete = null;
java.util.List<RagConnectObserverEntry> entriesToDelete = new java.util.ArrayList<>();
for (RagConnectObserverEntry entry : observedNodes) {
entry.connectList.remove(connectToken);
if (entry.connectList.isEmpty()) {
entryToDelete = entry;
}
entriesToDelete.add(entry);
}
if (entryToDelete != null) {
observedNodes.remove(entryToDelete);
}
observedNodes.removeAll(entriesToDelete);
}
@Override
......@@ -256,7 +267,7 @@ aspect RagConnectObserver {
{{/configLoggingEnabledForIncremental}}
// iterate through list, if matching pair. could maybe be more efficient.
for (RagConnectObserverEntry entry : observedNodes) {
if (entry.node.equals(node) && entry.attributeString.equals(attribute) && (!entry.compareParams || java.util.Objects.equals(entry.params, params))) {
if (entry.baseMembersEqualTo(node, attribute, false, params)) {
// hit. call the attribute/nta-token
{{#configLoggingEnabledForIncremental}}
{{logDebug}}("** observer hit: {{log_}} on {{log_}}", entry.node, entry.attributeString);
......
......@@ -28,6 +28,7 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
final String topic = {{attributeName}}().extractTopic(uri);
{{senderName}}.add(() -> {
{{#configLoggingEnabledForWrites}}
{{!FIXME circular computation for collection attributes!!}}
{{logDebug}}("[Send] {{entityName}} = {{log_}} -> {{log_}}", {{getterMethodCall}}, {{connectParameterName}});
{{/configLoggingEnabledForWrites}}
if ({{lastValueGetterCall}} != null) {
......@@ -63,18 +64,19 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
if (success) {
connectTokenMap.add(this, false, connectToken);
{{#configIncrementalOptionActive}}
{{!todo maybe getterMethodName needs to be change for indexed send}}
{{#sendIncrementalObserverEntries}}
{{observerInstanceSingletonMethodName}}().add(
connectToken,
this,
"{{getterMethodName}}{{#IndexBasedListAccess}}_int{{/IndexBasedListAccess}}",
{{#IndexBasedListAccess}}index,{{/IndexBasedListAccess}}
{{#CompareParams}}{{Params}},{{/CompareParams}}
() -> {
if (this.{{updateMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}})) {
this.{{writeMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}});
}
}
},
"{{AttributeString}}"
);
{{/sendIncrementalObserverEntries}}
{{/configIncrementalOptionActive}}
}
return success;
......
......@@ -46,7 +46,7 @@ dependencies {
ragconnect project(':ragconnect.base')
testImplementation project(':ragconnect.base')
implementation group: 'org.jastadd', name: 'jastadd2', version: '2.3.5-dresden-5'
implementation group: 'org.jastadd', name: 'jastadd2', version: '2.3.5-dresden-7'
relast group: 'org.jastadd', name: 'relast', version: "0.3.0-137"
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.4.0'
......
......@@ -3,6 +3,8 @@ send SenderRoot.simple(String) ;
send SenderRoot.transformed(int) ;
send SenderRoot.toReferenceType(A) ;
send SenderRoot.toNTA(A) ;
send SenderRoot.circularAttribute(int);
send SenderRoot.collectionAttribute(Set<String>) using SetToString;
AddSuffix maps A a to A {:
A result = new A();
......@@ -20,6 +22,10 @@ AddPlusOne maps int i to int {:
return i + 1;
:}
SetToString maps Set<String> set to String {:
return set.toString();
:}
receive ReceiverRoot.FromBasic;
receive ReceiverRoot.FromSimpleNoMapping;
receive ReceiverRoot.FromSimpleWithMapping using AddStringSuffix;
......@@ -29,3 +35,7 @@ receive ReceiverRoot.FromReferenceTypeNoMapping;
receive ReceiverRoot.FromReferenceTypeWithMapping using AddSuffix;
receive ReceiverRoot.FromNTANoMapping;
receive ReceiverRoot.FromNTAWithMapping using AddSuffix;
receive ReceiverRoot.FromCircularNoMapping;
receive ReceiverRoot.FromCircularWithMapping using AddPlusOne;
receive ReceiverRoot.FromCollectionNoMapping;
receive ReceiverRoot.FromCollectionWithMapping using AddStringSuffix;
import java.util.Set;
import java.util.HashSet;
aspect Computation {
syn String SenderRoot.basic() = getInput();
syn String SenderRoot.simple() = getInput() + "Post";
......@@ -18,6 +20,12 @@ aspect Computation {
result.setInner(inner);
return result;
}
syn int SenderRoot.circularAttribute() circular [0] {
return Integer.parseInt(getInput()) + 2;
}
coll Set<String> SenderRoot.collectionAttribute() [new HashSet<>()] root SenderRoot ;
A contributes getValue() to SenderRoot.collectionAttribute();
SenderRoot contributes nta toNTA() to SenderRoot.collectionAttribute();
}
aspect MakeCodeCompile {
......
Root ::= SenderRoot* ReceiverRoot;
SenderRoot ::= <Input> ;
SenderRoot ::= <Input> A* ;
ReceiverRoot ::=
<FromBasic>
<FromSimpleNoMapping>
......@@ -9,6 +9,10 @@ ReceiverRoot ::=
FromReferenceTypeNoMapping:A
FromReferenceTypeWithMapping:A
FromNTANoMapping:A
FromNTAWithMapping:A ;
FromNTAWithMapping:A
<FromCircularNoMapping:int>
<FromCircularWithMapping:int>
<FromCollectionNoMapping>
<FromCollectionWithMapping> ;
A ::= <Value> Inner ;
Inner ::= <InnerValue> ;
send indexed SenderRoot.MultipleA;
send indexed SenderRoot.MultipleAWithSuffix using AddSuffix;
send indexed SenderRoot.A;
send indexed SenderRoot.ComputedA using AddSuffix;
AddSuffix maps A a to A {:
A result = new A();
......@@ -12,3 +13,5 @@ AddSuffix maps A a to A {:
receive indexed ReceiverRoot.ManyA;
receive indexed ReceiverRoot.ManyAWithSuffix;
receive indexed ReceiverRoot.FromNTA;
receive indexed ReceiverRoot.FromNTAWithSuffix;
aspect Computation {
syn JastAddList<A> SenderRoot.getAList() {
JastAddList<A> result = new JastAddList<>();
getMultipleAList().forEach(a -> result.add(a.touchedTerminals()));
return result;
}
syn JastAddList<A> SenderRoot.getComputedAList() {
JastAddList<A> result = new JastAddList<>();
getMultipleAWithSuffixList().forEach(a -> result.add(a.touchedTerminals()));
return result;
}
A A.touchedTerminals() {
getValue();
getInner().getInnerValue();
return this;
}
}
aspect NameResolution {
// overriding customID guarantees to produce the same JSON representation for equal lists
// otherwise, the value for id is different each time
......
Root ::= SenderRoot ReceiverRoot;
SenderRoot ::= MultipleA:A* MultipleAWithSuffix:A* ;
ReceiverRoot ::= ManyA:A* ManyAWithSuffix:A* ;
SenderRoot ::= MultipleA:A* MultipleAWithSuffix:A* /A*/ /ComputedA:A*/ ;
ReceiverRoot ::= ManyA:A* ManyAWithSuffix:A* FromNTA:A* FromNTAWithSuffix:A* ;
A ::= <Value> Inner ;
Inner ::= <InnerValue> ;
......@@ -25,7 +25,7 @@ public abstract class AbstractMqttTest extends RagConnectTest {
/**
* if the initial/current value shall be sent upon connecting
*/
private boolean writeCurrentValue;
protected boolean writeCurrentValue;
public boolean isWriteCurrentValue() {
return writeCurrentValue;
......@@ -58,7 +58,7 @@ public abstract class AbstractMqttTest extends RagConnectTest {
@Tag("mqtt")
@RepeatedIfExceptionsTest(repeats = TEST_REPETITIONS)
public final void testCommunicateSendInitialValue() throws IOException, InterruptedException {
public void testCommunicateSendInitialValue() throws IOException, InterruptedException {
this.writeCurrentValue = true;
createModel();
......@@ -76,7 +76,7 @@ public abstract class AbstractMqttTest extends RagConnectTest {
@Tag("mqtt")
@RepeatedIfExceptionsTest(repeats = TEST_REPETITIONS)
public final void testCommunicateOnlyUpdatedValue() throws IOException, InterruptedException {
public void testCommunicateOnlyUpdatedValue() throws IOException, InterruptedException {
this.writeCurrentValue = false;
createModel();
......
......@@ -9,8 +9,10 @@ import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static java.util.function.Predicate.isEqual;
import static org.assertj.core.api.Assertions.assertThat;
import static org.jastadd.ragconnect.tests.TestUtils.*;
import static org.junit.jupiter.api.Assertions.*;
......@@ -20,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.*;
* @author rschoene - Initial contribution
*/
@Tag("Incremental")
@Tag("New")
public class AttributeTest extends AbstractMqttTest {
private static final String TOPIC_WILDCARD = "attr/#";
......@@ -32,15 +35,22 @@ public class AttributeTest extends AbstractMqttTest {
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 TOPIC_CIRCULAR_NO_MAPPING = "attr/a/circular/plain";
private static final String TOPIC_CIRCULAR_WITH_MAPPING = "attr/a/circular/mapped";
private static final String TOPIC_COLLECTION_NO_MAPPING = "attr/a/collection/plain";
private static final String TOPIC_COLLECTION_WITH_MAPPING = "attr/a/collection/mapped";
private static final String INITIAL_STRING = "initial";
private static final String INITIAL_STRING_FOR_INT = "1";
private static final String INITIAL_STRING_FOR_INT_PLUS_2 = Integer.toString(Integer.parseInt(INITIAL_STRING_FOR_INT) + 2);
private static final String CHECK_BASIC = "basic";
private static final String CHECK_SIMPLE = "simple";
private static final String CHECK_TRANSFORMED = "transformed";
private static final String CHECK_A = "a";
private static final String CHECK_NTA = "nta";
private static final String CHECK_CIRCULAR = "circular";
private static final String CHECK_COLLECTION = "collection";
private MqttHandler handler;
private ReceiverData data;
......@@ -82,6 +92,8 @@ public class AttributeTest extends AbstractMqttTest {
.setCheckForString(CHECK_TRANSFORMED, this::checkTransformed)
.setCheckForString(CHECK_A, this::checkA)
.setCheckForString(CHECK_NTA, this::checkNta)
.setCheckForString(CHECK_CIRCULAR, this::checkCircular)
.setCheckForString(CHECK_COLLECTION, this::checkCollection)
;
// connect receive
......@@ -94,14 +106,22 @@ public class AttributeTest extends AbstractMqttTest {
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)));
assertTrue(receiverRoot.connectFromCircularNoMapping(mqttUri(TOPIC_CIRCULAR_NO_MAPPING)));
assertTrue(receiverRoot.connectFromCircularWithMapping(mqttUri(TOPIC_CIRCULAR_WITH_MAPPING)));
assertTrue(receiverRoot.connectFromCollectionNoMapping(mqttUri(TOPIC_COLLECTION_NO_MAPPING)));
assertTrue(receiverRoot.connectFromCollectionWithMapping(mqttUri(TOPIC_COLLECTION_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(senderString.connectCollectionAttribute(mqttUri(TOPIC_COLLECTION_NO_MAPPING), isWriteCurrentValue()));
assertTrue(senderString.connectCollectionAttribute(mqttUri(TOPIC_COLLECTION_WITH_MAPPING), isWriteCurrentValue()));
assertTrue(senderInt.connectTransformed(mqttUri(TOPIC_TRANSFORMED_NO_MAPPING), isWriteCurrentValue()));
assertTrue(senderInt.connectTransformed(mqttUri(TOPIC_TRANSFORMED_WITH_MAPPING), isWriteCurrentValue()));
assertTrue(senderInt.connectCircularAttribute(mqttUri(TOPIC_CIRCULAR_NO_MAPPING), isWriteCurrentValue()));
assertTrue(senderInt.connectCircularAttribute(mqttUri(TOPIC_CIRCULAR_WITH_MAPPING), isWriteCurrentValue()));
assertTrue(senderA.connectToReferenceType(mqttUri(TOPIC_REFERENCE_TYPE_NO_MAPPING), isWriteCurrentValue()));
assertTrue(senderA.connectToReferenceType(mqttUri(TOPIC_REFERENCE_TYPE_WITH_MAPPING), isWriteCurrentValue()));
......@@ -111,58 +131,69 @@ public class AttributeTest extends AbstractMqttTest {
waitForValue(senderString.basic(), receiverRoot::getFromBasic);
waitForValue(senderString.simple(), receiverRoot::getFromSimpleNoMapping);
waitForValue(senderInt.transformed(), receiverRoot::getFromTransformedNoMapping);
waitForNonNull(receiverRoot::getFromCollectionNoMapping);
waitForNonNull(receiverRoot::getFromReferenceTypeNoMapping);
waitForNonNull(receiverRoot::getFromNTANoMapping);
}
@Override
protected void communicateSendInitialValue() throws IOException, InterruptedException {
// basic, simple(2) <-- senderString
// transformed(2) <-- senderInt
// ref-type(2), nta(2) <-- senderA
checker.addToNumberOfValues(9)
// 13 = basic, simple(2), collection(2), transformed(2), circular(2), ref-type(2), nta(2)
checker.addToNumberOfValues(13)
.put(CHECK_BASIC, INITIAL_STRING)
.put(CHECK_SIMPLE, INITIAL_STRING + "Post")
.put(CHECK_TRANSFORMED, INITIAL_STRING_FOR_INT)
.put(CHECK_A, INITIAL_STRING)
.put(CHECK_NTA, INITIAL_STRING);
.put(CHECK_NTA, INITIAL_STRING)
.put(CHECK_CIRCULAR, INITIAL_STRING_FOR_INT_PLUS_2)
.put(CHECK_COLLECTION, "[" + INITIAL_STRING + "]");
communicateBoth();
}
@Override
protected void communicateOnlyUpdatedValue() throws IOException, InterruptedException {
// basic, simple(2) <-- senderString
// transformed(2) <-- senderInt
// ref-type(2), nta(2) <-- senderA
checker.put(CHECK_BASIC, (String) null)
.put(CHECK_SIMPLE, (String) null)
.put(CHECK_TRANSFORMED, (String) null)
.put(CHECK_A, (String) null)
.put(CHECK_NTA, (String) null);
.put(CHECK_NTA, (String) null)
.put(CHECK_COLLECTION, (String) null);
communicateBoth();
}
private void communicateBoth() throws IOException {
// basic, simple(2), collection(2) <-- senderString
// transformed(2), circular(2) <-- senderInt
// ref-type(2), nta(2) <-- senderA
checker.check();
senderString.setInput("test-01");
checker.addToNumberOfValues(3)
checker.addToNumberOfValues(5) // basic, simple(2), collection(2)
.put(CHECK_BASIC, "test-01")
.put(CHECK_SIMPLE, "test-01Post")
.put(CHECK_COLLECTION, "[test-01]")
.check();
// no change for same value
senderString.setInput("test-01");
checker.check();
// TODO check checks for (circular and) collection
senderString.addA(new A().setValue("test-02").setInner(new Inner().setInnerValue("inner")));
checker.addToNumberOfValues(2) // collection(2)
.put(CHECK_COLLECTION, "[test-01, test-02]")
.check();
senderInt.setInput("20");
checker.addToNumberOfValues(2)
checker.addToNumberOfValues(4) // transformed(2), circular(2)
.put(CHECK_TRANSFORMED, "20")
.put(CHECK_CIRCULAR, "22")
.check();
senderA.setInput("test-03");
checker.addToNumberOfValues(4)
checker.addToNumberOfValues(4) // ref-type(2), nta(2)
.put(CHECK_A, "test-03")
.put(CHECK_NTA, "test-03")
.check();
......@@ -170,14 +201,22 @@ public class AttributeTest extends AbstractMqttTest {
assertTrue(senderString.disconnectSimple(mqttUri(TOPIC_SIMPLE_NO_MAPPING)));
assertTrue(senderString.disconnectSimple(mqttUri(TOPIC_SIMPLE_WITH_MAPPING)));
senderString.setInput("test-04");
checker.incNumberOfValues()
checker.addToNumberOfValues(3) // basic, collection(2)
.put(CHECK_BASIC, "test-04")
.put(CHECK_COLLECTION, "[test-02, test-04]")
.check();
assertTrue(senderString.disconnectCollectionAttribute(mqttUri(TOPIC_COLLECTION_NO_MAPPING)));
assertTrue(senderString.disconnectCollectionAttribute(mqttUri(TOPIC_COLLECTION_WITH_MAPPING)));
senderString.setInput("test-05");
checker.incNumberOfValues() // basic
.put(CHECK_BASIC, "test-05")
.check();
assertTrue(senderA.disconnectToNTA(mqttUri(TOPIC_NTA_NO_MAPPING)));
senderA.setInput("test-05");
senderA.setInput("test-06");
checker.addToNumberOfValues(3)
.put(CHECK_A, "test-05")
.put(CHECK_A, "test-06")
.check();
}
......@@ -208,18 +247,17 @@ public class AttributeTest extends AbstractMqttTest {
}
private void checkTransformed(String name, String expected) {
_checkInt(name, expected, receiverRoot::getFromTransformedNoMapping, receiverRoot::getFromTransformedWithMapping);
}
private void _checkInt(String name, String expected, Supplier<Integer> noMapping, Supplier<Integer> withMapping) {
if (expected != null) {
assertEquals(Integer.parseInt(expected),
receiverRoot.getFromTransformedNoMapping(), "transformed");
assertEquals(Integer.parseInt(expected) + 1,
receiverRoot.getFromTransformedWithMapping(), "transformed mapped");
assertEquals(Integer.parseInt(expected), noMapping.get(), name);
assertEquals(Integer.parseInt(expected) + 1, withMapping.get(), name + " mapped");
} else {
assertEquals(0,
receiverRoot.getFromTransformedNoMapping(), "transformed null");
assertEquals(0,
receiverRoot.getFromTransformedWithMapping(), "transformed mapped null");
assertEquals(0, noMapping.get(), name + " null");
assertEquals(0, withMapping.get(), name + " mapped null");
}
}
private void checkA(String name, String expected) {
......@@ -245,6 +283,31 @@ public class AttributeTest extends AbstractMqttTest {
}
}
private void checkCircular(String name, String expected) {
_checkInt(name, expected, receiverRoot::getFromCircularNoMapping, receiverRoot::getFromCircularWithMapping);
}
private void checkCollection(String name, String expected) {
if (expected != null) {
// TODO probably need to tokenize actual and expected, and compare their elements without order
assertThat(receiverRoot.getFromCollectionWithMapping()).hasSizeGreaterThan(4).endsWith("post");
checkCollectionContent(name, expected, receiverRoot.getFromCollectionNoMapping());
checkCollectionContent(name + " mapped", expected, receiverRoot.getFromCollectionWithMapping().substring(0, receiverRoot.getFromCollectionWithMapping().length() - 4));
} else {
assertEquals("", receiverRoot.getFromCollectionNoMapping(), "collection null");
assertEquals("", receiverRoot.getFromCollectionWithMapping(), "collection mapped null");
}
}
private void checkCollectionContent(String name, String expected, String actual) {
assertThat(actual).as(name)
.startsWith("[")
.endsWith("]");
String[] actualValues = actual.substring(1, actual.length() - 1).split(", ");
String[] expectedValues = expected.substring(1, expected.length() - 1).split(", ");
assertThat(actualValues).containsExactlyInAnyOrder(expectedValues);
}
private void assertA(String expectedValue, String expectedInner, A actual, String message) {
assertEquals(expectedValue, actual.getValue(), message + " value");
assertEquals(expectedInner, actual.getInner().getInnerValue(), message + " inner");
......
......@@ -2,12 +2,17 @@ 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;
import java.util.function.Supplier;
import static org.assertj.core.groups.Tuple.tuple;
import static org.jastadd.ragconnect.tests.TestUtils.*;
......@@ -20,6 +25,7 @@ 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/#";
......@@ -33,6 +39,8 @@ public class IndexedSendTest extends AbstractMqttTest {
private static final String CHECK_MANY_A = "many-a";
private static final String CHECK_WITH_SUFFIX = "many-a-with-suffix";
private boolean connectNTAsInstead;
private MqttHandler handler;
private ReceiverData data;
private TestUtils.TestChecker checker;
......@@ -83,70 +91,91 @@ public class IndexedSendTest extends AbstractMqttTest {
checker = new TestChecker();
checker.setActualNumberOfValues(() -> data.numberOfValues)
.setCheckForTuple(CHECK_MANY_A, (name, expected) ->
Assertions.assertThat(receiverRoot.getManyAList()).extracting("Value")
.as(name)
.containsExactlyElementsOf(expected.toList()))
checkList(name, expected, receiverRoot::getManyAList))
.setCheckForTuple(CHECK_WITH_SUFFIX, (name, expected) ->
Assertions.assertThat(receiverRoot.getManyAWithSuffixList()).extracting("Value")
.as(name)
.containsExactlyElementsOf(expected.toList()));
checkList(name, expected, receiverRoot::getManyAWithSuffixList));
// connect receive
assertTrue(receiverRoot.connectManyA(mqttUri(TOPIC_A_MANY_NORMAL_WILDCARD)));
assertTrue(receiverRoot.connectManyAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_WILDCARD)));
// connect send, and wait to receive (if writeCurrentValue is set)
assertTrue(senderRoot.connectMultipleA(mqttUri(TOPIC_A_MANY_NORMAL_0), 0, isWriteCurrentValue()));
assertTrue(connectNTAsInstead ?
senderRoot.connectA(mqttUri(TOPIC_A_MANY_NORMAL_0), 0, isWriteCurrentValue()) :
senderRoot.connectMultipleA(mqttUri(TOPIC_A_MANY_NORMAL_0), 0, isWriteCurrentValue()));
waitForValue(receiverRoot::getNumManyA, 1);
assertTrue(senderRoot.connectMultipleA(mqttUri(TOPIC_A_MANY_NORMAL_1), 1, isWriteCurrentValue()));
assertTrue(connectNTAsInstead ?
senderRoot.connectA(mqttUri(TOPIC_A_MANY_NORMAL_1), 1, isWriteCurrentValue()) :
senderRoot.connectMultipleA(mqttUri(TOPIC_A_MANY_NORMAL_1), 1, isWriteCurrentValue()));
waitForValue(receiverRoot::getNumManyA, 2);
assertTrue(senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_0), 0, isWriteCurrentValue()));
assertTrue(connectNTAsInstead ?
senderRoot.connectComputedA(mqttUri(TOPIC_A_MANY_SUFFIX_0), 0, isWriteCurrentValue()) :
senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_0), 0, isWriteCurrentValue()));
waitForValue(receiverRoot::getNumManyAWithSuffix, 1);
assertTrue(senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_1), 1, isWriteCurrentValue()));
assertTrue(connectNTAsInstead ?
senderRoot.connectComputedA(mqttUri(TOPIC_A_MANY_SUFFIX_1), 1, isWriteCurrentValue()) :
senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_1), 1, isWriteCurrentValue()));
waitForValue(receiverRoot::getNumManyAWithSuffix, 2);
}
private void checkList(String name, Tuple expected, Supplier<JastAddList<A>> actual) {
Assertions.assertThat(actual.get()).extracting("Value")
.as(name)
.containsExactlyElementsOf(expected.toList());
}
private void waitForValue(Callable<Integer> callable, int expectedValue) {
if (isWriteCurrentValue()) {
awaitMqtt().until(callable, Predicate.isEqual(expectedValue));
}
}
@Tag("mqtt")
// @RepeatedIfExceptionsTest(repeats = TEST_REPETITIONS)
@Test
public void testCommunicateSendInitialValueWithNTAs() throws IOException, InterruptedException {
this.writeCurrentValue = true;
this.connectNTAsInstead = true;
try {
createModel();
setupReceiverAndConnect();
logger.info("Calling communicateSendInitialValue");
communicateSendInitialValue();
} finally {
this.connectNTAsInstead = false;
}
}
@Tag("mqtt")
// @RepeatedIfExceptionsTest(repeats = TEST_REPETITIONS)
@Test
public void testCommunicateOnlyUpdatedValueWithNTAs() throws IOException, InterruptedException {
this.writeCurrentValue = false;
this.connectNTAsInstead = true;
try {
createModel();
setupReceiverAndConnect();
logger.info("Calling communicateOnlyUpdatedValue");
communicateOnlyUpdatedValue();
} finally {
this.connectNTAsInstead = false;
}
}
@Override
protected void communicateSendInitialValue() throws IOException, InterruptedException {
checker.addToNumberOfValues(4)
.put(CHECK_MANY_A, tuple("am0", "am1"))
.put(CHECK_WITH_SUFFIX, tuple("am0post", "am1post"));
listA0.setValue("changedValue");
checker.incNumberOfValues().put(CHECK_MANY_A, tuple("changedValue", "am1")).check();
// setting same value must not change data, and must not trigger a new sent message
listA0.setValue("changedValue");
checker.check();
listA1.setValue("");
checker.incNumberOfValues().put(CHECK_MANY_A, tuple("changedValue", "")).check();
listA1InSuffix.setValue("re");
checker.incNumberOfValues().put(CHECK_WITH_SUFFIX, tuple("am0post", "repost")).check();
// adding a new element does not automatically send it
A listA3InSuffix = createA("out");
senderRoot.addMultipleAWithSuffix(listA3InSuffix);
checker.check();
// only after connecting it, the element gets sent
assertTrue(senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_2), 2, true));
checker.incNumberOfValues().put(CHECK_WITH_SUFFIX, tuple("am0post", "repost", "outpost")).check();
// after successful disconnect, no messages will be sent
assertTrue(senderRoot.disconnectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_0)));
listA0InSuffix.setValue("willBeIgnored");
checker.check();
communicateBoth("am1", "am0post");
}
@Override
......@@ -154,10 +183,10 @@ public class IndexedSendTest extends AbstractMqttTest {
checker.put(CHECK_MANY_A, tuple())
.put(CHECK_WITH_SUFFIX, tuple());
communicateBoth();
communicateBoth(null, null);
}
private void communicateBoth() throws IOException {
private void communicateBoth(String manyAtIndex1, String suffixAtIndex0) throws IOException {
// Sink.ManyA <-- Root.MultipleA
// Sink.ManyAWithSuffix <-- Root.MultipleAWithSuffix
checker.check();
......@@ -165,18 +194,26 @@ public class IndexedSendTest extends AbstractMqttTest {
assertEquals(listA0.getValue(), senderRoot._ragconnect_MultipleA(0).getValue());
listA0.setValue("changedValue");
assertEquals(listA0.getValue(), senderRoot._ragconnect_MultipleA(0).getValue());
checker.incNumberOfValues().put(CHECK_MANY_A, tuple("changedValue")).check();
checker.incNumberOfValues()
.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();
// first element in suffix-list
listA1InSuffix.setValue("re");
checker.incNumberOfValues().put(CHECK_WITH_SUFFIX, tuple("repost")).check();
checker.incNumberOfValues()
.put(CHECK_WITH_SUFFIX, suffixAtIndex0 != null ? tuple(suffixAtIndex0, "repost") : tuple("repost"))
.check();
// adding a new element does not automatically send it
A listA3InSuffix = createA("out");
......@@ -185,7 +222,9 @@ public class IndexedSendTest extends AbstractMqttTest {
// only after connecting it, the element gets sent
assertTrue(senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_2), 2, true));
checker.incNumberOfValues().put(CHECK_WITH_SUFFIX, tuple("repost", "outpost")).check();
checker.incNumberOfValues()
.put(CHECK_WITH_SUFFIX, suffixAtIndex0 != null ? tuple(suffixAtIndex0, "repost", "outpost") : tuple("repost", "outpost"))
.check();
// after successful disconnect, no messages will be sent
assertTrue(senderRoot.disconnectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_0)));
......@@ -193,6 +232,12 @@ public class IndexedSendTest extends AbstractMqttTest {
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();
}
@Override
protected void closeConnections() {
if (handler != null) {
......
......@@ -22,6 +22,7 @@ import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
......@@ -226,20 +227,30 @@ public class TestUtils {
}
public TestChecker setActual(String name, Callable<T> actual) {
values.computeIfAbsent(name, ActualAndExpected::new).actual = actual;
_computeIfAbsent(name).actual = actual;
return parent;
}
public TestChecker setCheck(String name, BiConsumer<String, T> check) {
values.computeIfAbsent(name, ActualAndExpected::new).customCheck = check;
_computeIfAbsent(name).customCheck = check;
return parent;
}
public TestChecker put(String name, T expected) {
values.computeIfAbsent(name, ActualAndExpected::new).expected = expected;
_computeIfAbsent(name).expected = expected;
return parent;
}
public TestChecker updateExpected(String name, Function<T, T> updater) {
ActualAndExpected<T> aae = _computeIfAbsent(name);
aae.expected = updater.apply(aae.expected);
return parent;
}
private ActualAndExpected<T> _computeIfAbsent(String name) {
return values.computeIfAbsent(name, ActualAndExpected::new);
}
ActualAndExpected<T> get(String name) {
return values.get(name);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment