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

WIP (failing): add support for receiving single elements of lists

- also change interface of connect-method that interact with whole list to have "List" suffix
parent 79c9c65f
No related branches found
No related tags found
1 merge request!10Allow connection endpoints for list nonterminals
Pipeline #10479 failed
Showing
with 535 additions and 45 deletions
......@@ -13,7 +13,7 @@ rel TokenEndpointDefinition.Token <-> TokenComponent.TokenEndpointDefinition*;
ReceiveTokenEndpointDefinition : TokenEndpointDefinition;
SendTokenEndpointDefinition : TokenEndpointDefinition;
abstract TypeEndpointDefinition : EndpointDefinition;
abstract TypeEndpointDefinition : EndpointDefinition ::= <UseList:boolean> ;
rel TypeEndpointDefinition.Type <-> TypeComponent.TypeEndpointDefinition*;
ReceiveTypeEndpointDefinition : TypeEndpointDefinition ::= <WithAdd:boolean>;
......
......@@ -4,6 +4,9 @@ Design considerations
*/
aspect AttributesForMustache {
// --- EndpointDefinition ---
syn String EndpointDefinition.idTokenName() = "InternalRagconnectTopicInList";
// --- MRagConnect ---
eq MRagConnect.getRootTypeComponent(int i).isFirst() = i == 0;
......@@ -118,9 +121,10 @@ aspect AttributesForMustache {
// --- 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();
eq MTypeEndpointDefinition.entityName() = typeName() + (isUseList() ? "List" : "");
// --- MInnerMappingDefinition ---
inh boolean MInnerMappingDefinition.isLast();
......@@ -159,6 +163,9 @@ aspect AttributesForMustache {
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;";
......@@ -348,7 +355,7 @@ 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 : allDependencyDefinitionList()) {
......@@ -369,6 +376,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 {
......
......@@ -210,12 +210,21 @@ aspect Mappings {
default:
try {
TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
return typeIsList() ? ragconnect().defaultBytesToListTreeMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName());
// 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 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()) {
......@@ -237,12 +246,20 @@ aspect Mappings {
default:
try {
TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
return typeIsList() ? ragconnect().defaultListTreeToBytesMapping() : ragconnect().defaultTreeToBytesMapping(typeDecl.getName());
// 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();
......
......@@ -54,6 +54,28 @@ EndpointDefinition endpoint_definition_type
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
......
......@@ -6,5 +6,6 @@
"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); }
......@@ -232,6 +232,7 @@ public class Compiler extends AbstractCompiler {
ragConnect.treeResolveAll();
ragConnect.additionalRelations().forEach(ragConnectGrammarPart::addDeclaration);
ragConnect.additionalTokens().forEach(TypeDecl::addComponent);
ASTNode.loggingEnabledForReads = optionLogReads.value();
ASTNode.loggingEnabledForWrites = optionLogWrites.value();
ASTNode.loggingEnabledForIncremental = optionLogIncremental.value();
......
......@@ -5,7 +5,7 @@ import java.util.concurrent.TimeUnit;
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;
......@@ -40,7 +40,7 @@ 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 IOException {
ConnectToken connectToken = new ConnectToken(uri);
resolveHandler(uri).newConnection(extractTopic(uri), callback);
tokensForRemoval.put(connectToken, callback);
......@@ -100,7 +100,7 @@ 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[]>>> callbacks;
public MqttHandler() {
this("RagConnect");
......@@ -167,14 +167,14 @@ 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 = new java.util.ArrayList<>(callbacks.get(topicString));
java.util.List<java.util.function.BiConsumer<String, 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();
for (java.util.function.Consumer<byte[]> callback : callbackList) {
for (java.util.function.BiConsumer<String, byte[]> callback : callbackList) {
try {
callback.accept(message);
callback.accept(topicString, message);
} catch (Exception e) {
logger.catching(e);
}
......@@ -252,6 +252,10 @@ public class MqttHandler {
}
public boolean newConnection(String topic, java.util.function.Consumer<byte[]> callback) {
return newConnection(topic, (ignoredTopicString, bytes) -> callback.accept(bytes));
}
public boolean newConnection(String topic, java.util.function.BiConsumer<String, byte[]> callback) {
if (readyLatch.getCount() > 0) {
System.err.println("Handler not ready");
return false;
......@@ -282,7 +286,7 @@ public class MqttHandler {
}
public boolean disconnect(String topic, Object callback) {
java.util.List<java.util.function.Consumer<byte[]>> callbackList = callbacks.get(topic);
java.util.List<java.util.function.BiConsumer<String, byte[]>> callbackList = callbacks.get(topic);
if (callbackList == null) {
logger.warn("Disconnect for not connected topic '{}'", topic);
return false;
......
{{#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}}
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}} + " -> {{entityName}} = " + {{lastResult}});
......@@ -8,12 +31,28 @@ public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterNam
{{#isTypeEndpointDefinition}}
{{lastResult}}.treeResolveAll();
{{#typeIsList}}
{{#UseList}}
{{#isWithAdd}}
get{{entityName}}List().addAll({{lastResult}});
{{getterMethod}}().addAll({{lastResult}});
{{/isWithAdd}}
{{^isWithAdd}}
set{{entityName}}List({{lastResult}});
set{{entityName}}({{lastResult}});
{{/isWithAdd}}
{{/UseList}}
{{^UseList}}
{{#isWithAdd}}
{{getterMethod}}().add({{lastResult}});
{{/isWithAdd}}
{{^isWithAdd}}
{{lastResult}}.set{{idTokenName}}(topic);
int resolvedIndex = {{resolveInListAttributeName}}(topic);
if (resolvedIndex == -1) {
add{{entityName}}({{lastResult}});
} else {
set{{entityName}}({{lastResult}}, resolvedIndex);
}
{{/isWithAdd}}
{{/UseList}}
{{/typeIsList}}
{{^typeIsList}}
set{{entityName}}({{lastResult}});
......@@ -36,7 +75,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;
......
......@@ -457,3 +457,49 @@ task compileListIncremental(type: RagConnectTest, dependsOn: ':ragconnect.base:j
'--flush=full']
}
}
// --- 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')]
}
}
// --- 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']
}
}
send tree SenderRoot.A ;
send tree SenderRoot.SingleA ;
receive tree ReceiverRoot.A ;
receive tree ReceiverRoot.FromSingleA ;
receive tree with add ReceiverRoot.WithAddFromA ;
receive tree with add ReceiverRoot.WithAddFromSingleA ;
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 ;
# 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
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 with add 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);
:}
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 B implements org.jastadd.ragconnect.tests.singleList.AbstractSingleListTest.TestWrapperB {}
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();
}
}
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 ::= B* ;
B : Nameable ;
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 ;
......@@ -90,7 +90,7 @@ public abstract class AbstractMqttTest {
* and finally call generated connect* methods on model elements.
* @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() {
......
......@@ -42,10 +42,10 @@ public abstract class AbstractListTest extends AbstractMqttTest {
TestWrapperJastAddList<? extends TestWrapperA> getWithAddFromSingleAList();
TestWrapperJastAddList<? extends TestWrapperA> getWithAddFromSingleAs();
boolean connectA(String mqttUri) throws IOException;
boolean connectFromSingleA(String mqttUri) throws IOException;
boolean connectWithAddFromA(String mqttUri) throws IOException;
boolean connectWithAddFromSingleA(String mqttUri) throws IOException;
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);
......
......@@ -53,13 +53,13 @@ public class ListIncrementalTest extends AbstractListTest {
handler.newConnection(TOPIC_SINGLE_A, bytes -> dataSingle.numberOfElements += 1);
// connect. important: first receivers, then senders. to not miss initial value.
assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A)));
assertTrue(receiverRoot.connectFromSingleA(mqttUri(TOPIC_SINGLE_A)));
assertTrue(receiverRoot.connectWithAddFromA(mqttUri(TOPIC_A)));
assertTrue(receiverRoot.connectWithAddFromSingleA(mqttUri(TOPIC_SINGLE_A)));
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.connectA(mqttUri(TOPIC_A), writeCurrentValue));
assertTrue(senderRoot.connectSingleA(mqttUri(TOPIC_SINGLE_A), writeCurrentValue));
assertTrue(senderRoot.connectAList(mqttUri(TOPIC_A), writeCurrentValue));
assertTrue(senderRoot.connectSingleAList(mqttUri(TOPIC_SINGLE_A), writeCurrentValue));
}
@Override
......
......@@ -56,13 +56,13 @@ public class ListManualTest extends AbstractListTest {
handler.newConnection(TOPIC_SINGLE_A, bytes -> dataSingle.numberOfElements += 1);
// connect. important: first receivers, then senders. to not miss initial value.
assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A)));
assertTrue(receiverRoot.connectFromSingleA(mqttUri(TOPIC_SINGLE_A)));
assertTrue(receiverRoot.connectWithAddFromA(mqttUri(TOPIC_A)));
assertTrue(receiverRoot.connectWithAddFromSingleA(mqttUri(TOPIC_SINGLE_A)));
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.connectA(mqttUri(TOPIC_A), writeCurrentValue));
assertTrue(senderRoot.connectSingleA(mqttUri(TOPIC_SINGLE_A), writeCurrentValue));
assertTrue(senderRoot.connectAList(mqttUri(TOPIC_A), writeCurrentValue));
assertTrue(senderRoot.connectSingleAList(mqttUri(TOPIC_SINGLE_A), writeCurrentValue));
}
@Override
......
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 singleListInc.ast.SenderRoot;
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.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();
TestWrapperA getA(int index);
TestWrapperJastAddList<? extends TestWrapperA> getWithAddAList();
TestWrapperJastAddList<? extends TestWrapperA> getWithAddAs();
boolean connectA(String mqttUri) throws IOException;
boolean connectUsingWildcardA(String mqttUri) 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 {
TestWrapperB getB(int i);
int getNumB();
int getID();
}
public interface TestWrapperB {
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_WILDCARD = "a/#";
protected static final String TOPIC_A_5_INOUT = "a/special";
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)));
assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A_2)));
assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A_3)));
assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A_4)));
assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A_5_INOUT)));
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
assertTrue(senderRoot.connectA1(mqttUri(TOPIC_A_1), writeCurrentValue));
if (writeCurrentValue) TestUtils.waitForMqtt();
assertTrue(senderRoot.connectA2(mqttUri(TOPIC_A_2), writeCurrentValue));
if (writeCurrentValue) TestUtils.waitForMqtt();
assertTrue(senderRoot.connectA3(mqttUri(TOPIC_A_3), writeCurrentValue));
if (writeCurrentValue) TestUtils.waitForMqtt();
assertTrue(senderRoot.connectA4(mqttUri(TOPIC_A_4), writeCurrentValue));
if (writeCurrentValue) TestUtils.waitForMqtt();
assertTrue(senderRoot.connectInOutput(mqttUri(TOPIC_A_5_INOUT), writeCurrentValue));
}
abstract protected void setupReceiverAndConnectPart() throws IOException;
@Override
protected void communicateSendInitialValue() throws InterruptedException {
checkTree(1, list(1, 2, 3, 4, 5), list(1, 2, 3, 4, 5), // normal
list(1, 2, 3, 4, 5), list(1, 2, 3, 4, 5)); // withAdd
// TODO check below
setInput(1, 0);
checkTree(1, list(1), list(2), list(3), list(4));
setInput(1, 1);
checkTree(2, list(2), 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 {
// TODO check below
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 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, actualComputedValue, "ID value of single A");
}
private void checkTree(int expectedTransmissions, IntList normalA, IntList usingWildcardA, IntList withAddA, IntList usingWildcardWithAddA) throws InterruptedException {
TestUtils.waitForMqtt();
// TODO check below
assertEquals(expectedTransmissions, data.numberOfElements, "transmissions for normal");
checkList(normalA.toList(), receiverRoot.getNumA(), receiverRoot::getA, true);
checkList(normalA.toList(), receiverRoot.getAList(), true);
checkList(normalA.toList(), receiverRoot.getAs(), true);
checkList(withAddA.toList(), receiverRoot.getWithAddAList(), true);
checkList(withAddA.toList(), receiverRoot.getWithAddAs(), true);
}
private void checkList(List<Integer> expectedList, int numChildren, Function<Integer, TestWrapperA> getA, boolean expectB) {
// TODO check below
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) {
// TODO check below
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);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment