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

Added Read1Write2Test, and AbstractMqttTest.

- all tests have fail-fast-behaviour if mqtt broker is not connected
- added some documentation on how to create new test cases
- Base: Fixed logging of writes to output actual values of terminal and topic
parent 50b97146
Branches
No related tags found
No related merge requests found
Pipeline #6894 passed
Showing
with 375 additions and 4 deletions
...@@ -208,7 +208,9 @@ aspect AspectGeneration { ...@@ -208,7 +208,9 @@ aspect AspectGeneration {
.append(writeMethod()).append("() {\n"); .append(writeMethod()).append("() {\n");
if (loggingEnabledForWrites) { if (loggingEnabledForWrites) {
sb.append(ind(2)).append("System.out.println(\"[Write] ").append(getToken().getName()) sb.append(ind(2)).append("System.out.println(\"[Write] ").append(getToken().getName())
.append(" = \" + ").append(lastValue()).append(" + \" -> ").append(writeTopic()).append("\");\n"); .append(" = \" + ")
.append("get").append(getToken().getName()).append("() + \" -> \" + ")
.append(writeTopic()).append(");\n");
} }
// _mqttUpdater().publish(${writeTopic()}, ${lastValue()}); // _mqttUpdater().publish(${writeTopic()}, ${lastValue()});
sb.append(ind(2)).append(mqttUpdaterAttribute()).append("().publish(") sb.append(ind(2)).append(mqttUpdaterAttribute()).append("().publish(")
......
# Creating a new test case
The following things must be done:
- Create a new directory in `src/test/01-input` and put at least grammar, the ros2rag-specification and a README.md in there. Optionally, aspect files as needed.
- Optionally, add new protobuf definitions in `src/test/proto` (they are processed automatically in this directory)
- Copy and modify the compiler instructions in `build.gradle`, e.g. `preprocessExampleTest` and `compileExampleTest` for the `example` case. Also, copy the dependsOn-Definitions.
- Change the name of the tasks
- Change the directory in `delete` spec, and in all arguments
- Add/Remove aspect files
- Change the `packageName` argument in the compile-step
- Change the `--rootNode` argument in the preprocess-step
- For debugging build problems, the `--verbose` flag in the preprocess-step can be helpful
- Create the test case itself in `src/test/java/` in the package `org.jastadd.ros2rag.tests` and import the AST files of your defined target generation package
- Extend the abstract base class `AbstractMqttTest` to have fail-fast-behaviour in case the MQTT-broker is not connected
- Remember to close all used MQTT-handling objects, like MqttUpdater and the model itself after each test
...@@ -149,6 +149,38 @@ task compileDefaultOnlyWriteTest(type: RelastTest) { ...@@ -149,6 +149,38 @@ task compileDefaultOnlyWriteTest(type: RelastTest) {
test.dependsOn compileDefaultOnlyWriteTest test.dependsOn compileDefaultOnlyWriteTest
compileDefaultOnlyWriteTest.dependsOn preprocessDefaultOnlyWriteTest compileDefaultOnlyWriteTest.dependsOn preprocessDefaultOnlyWriteTest
// --- Test: read-1-write-2 ---
task preprocessRead1Write2Test(type: JavaExec, group: 'verification') {
doFirst {
delete 'src/test/02-after-ros2rag/read1write2/Grammar.relast',
'src/test/02-after-ros2rag/read1write2/MqttUpdater.java',
'src/test/02-after-ros2rag/read1write2/ROS2RAG.jadd'
}
classpath = sourceSets.main.runtimeClasspath
main = 'org.jastadd.ros2rag.compiler.Compiler'
//noinspection GroovyAssignabilityCheck
args '--outputDir=src/test/02-after-ros2rag/read1write2',
'--inputGrammar=src/test/01-input/read1write2/Example.relast',
'--inputRos2Rag=src/test/01-input/read1write2/Example.ros2rag',
'--rootNode=A', '--verbose',
'--logReads', '--logWrites'
}
task compileRead1Write2Test(type: RelastTest) {
useJastAddNames = true
jastAddList = 'JastAddList'
relastFiles 'src/test/02-after-ros2rag/read1write2/Grammar.relast'
grammarName = 'src/test/03-after-relast/read1write2/read1write2'
packageName = 'read1write2.ast'
moreInputFiles 'src/test/01-input/read1write2/Example.jadd',
'src/test/02-after-ros2rag/read1write2/MqttUpdater.jadd',
'src/test/02-after-ros2rag/read1write2/ROS2RAG.jadd'
}
test.dependsOn compileRead1Write2Test
compileRead1Write2Test.dependsOn preprocessRead1Write2Test
clean { clean {
delete 'src/test/02-after-ros2rag/*/', 'src/test/03-after-relast/*/' delete 'src/test/02-after-ros2rag/*/', 'src/test/03-after-relast/*/'
} }
aspect Computation{
// OnSameNonterminal
syn int OnSameNonterminal.getOutInteger() = Integer.parseInt(getInput());
syn String OnSameNonterminal.getOutString() = "prefix" + getInput();
// OnDifferentNonterminal
syn int TheOther.getOutInteger() = Integer.parseInt(input());
syn String TheOther.getOutString() = "prefix" + input();
inh String TheOther.input();
eq OnDifferentNonterminal.getTheOther().input() = getInput();
}
A ::= OnSameNonterminal OnDifferentNonterminal ;
OnSameNonterminal ::= <Input:String> /<OutInteger:int>/ /<OutString:String>/ ;
OnDifferentNonterminal ::= <Input:String> TheOther* ;
TheOther ::= /<OutInteger:int>/ /<OutString:String>/ ;
// --- update definitions ---
// OnSameNonterminal
read OnSameNonterminal.Input;
write OnSameNonterminal.OutInteger;
write OnSameNonterminal.OutString;
// OnDifferentNonterminal
read OnDifferentNonterminal.Input;
write TheOther.OutInteger;
write TheOther.OutString;
// --- dependency definitions ---
// OnSameNonterminal
OnSameNonterminal.OutInteger canDependOn OnSameNonterminal.Input as IntDependency;
OnSameNonterminal.OutString canDependOn OnSameNonterminal.Input as StringDependency;
// OnDifferentNonterminal
TheOther.OutInteger canDependOn OnDifferentNonterminal.Input as IntDependency;
TheOther.OutString canDependOn OnDifferentNonterminal.Input as StringDependency;
# Read one - Write two
Idea: Define Read-Update for one terminal, add dependencies to two other terminals, which each have Write-Update defined. Test, whether code gets generated correctly and that write is trigger for each of the two terminals upon read.
package org.jastadd.ros2rag.tests;
import defaultOnlyRead.ast.MqttUpdater;
import org.junit.jupiter.api.BeforeAll;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* Base class for tests ensuring running mqtt broker.
*
* @author rschoene - Initial contribution
*/
public abstract class AbstractMqttTest {
static boolean checkDone = false;
static Boolean checkResult;
@BeforeAll
public static void checkMqttConnection() {
if (!checkDone) {
checkDone = true;
try {
checkResult = new MqttUpdater()
.dontSendWelcomeMessage()
.setHost(TestUtils.getMqttHost())
.waitUntilReady(2, TimeUnit.SECONDS);
} catch (IOException e) {
checkResult = false;
}
}
if (!checkResult) {
throw new IllegalStateException("Mqtt Broker not ready!");
}
}
}
...@@ -19,7 +19,7 @@ import static org.junit.Assert.assertTrue; ...@@ -19,7 +19,7 @@ import static org.junit.Assert.assertTrue;
* *
* @author rschoene - Initial contribution * @author rschoene - Initial contribution
*/ */
public class DefaultOnlyReadTest { public class DefaultOnlyReadTest extends AbstractMqttTest {
private static final String TOPIC_NATIVE_INT = "native/int"; private static final String TOPIC_NATIVE_INT = "native/int";
private static final String TOPIC_NATIVE_SHORT = "native/short"; private static final String TOPIC_NATIVE_SHORT = "native/short";
......
...@@ -17,7 +17,7 @@ import static org.junit.Assert.*; ...@@ -17,7 +17,7 @@ import static org.junit.Assert.*;
* *
* @author rschoene - Initial contribution * @author rschoene - Initial contribution
*/ */
public class DefaultOnlyWriteTest { public class DefaultOnlyWriteTest extends AbstractMqttTest {
private static final String TOPIC_NATIVE_INT = "native/int"; private static final String TOPIC_NATIVE_INT = "native/int";
private static final String TOPIC_NATIVE_SHORT = "native/short"; private static final String TOPIC_NATIVE_SHORT = "native/short";
......
...@@ -18,7 +18,7 @@ import static org.junit.Assert.*; ...@@ -18,7 +18,7 @@ import static org.junit.Assert.*;
* *
* @author rschoene - Initial contribution * @author rschoene - Initial contribution
*/ */
public class ExampleTest { public class ExampleTest extends AbstractMqttTest {
private static final String TOPIC_CONFIG = "robot/config"; private static final String TOPIC_CONFIG = "robot/config";
private static final String TOPIC_JOINT1 = "robot/arm/joint1"; private static final String TOPIC_JOINT1 = "robot/arm/joint1";
......
package org.jastadd.ros2rag.tests;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import read1write2.ast.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
/**
* Test case "read-1-write-2".
*
* @author rschoene - Initial contribution
*/
public class Read1Write2Test extends AbstractMqttTest {
private static final String TOPIC_SAME_READ = "same/read";
private static final String TOPIC_SAME_WRITE_INT = "same/write/int";
private static final String TOPIC_SAME_WRITE_STRING = "same/write/string";
private static final String TOPIC_DIFFERENT_READ = "different/read";
private static final String TOPIC_DIFFERENT_WRITE1_INT = "different/write1/int";
private static final String TOPIC_DIFFERENT_WRITE1_STRING = "different/write1/string";
private static final String TOPIC_DIFFERENT_WRITE2_INT = "different/write2/int";
private static final String TOPIC_DIFFERENT_WRITE2_STRING = "different/write2/string";
private static final String INITIAL_VALUE = "-1";
private MqttUpdater handler;
private A model;
private OnSameNonterminal onSameNonterminal;
private OnDifferentNonterminal onDifferentNonterminal;
private TheOther other1;
private TheOther other2;
private ReceiverData dataSame;
private ReceiverData dataOther1;
private ReceiverData dataOther2;
@AfterEach
public void closeConnections() {
if (handler != null) {
handler.close();
}
if (model != null) {
model.MqttCloseConnections();
}
}
@Test
public void buildModel() {
createModel();
}
@Test
public void communicateSendInitialValue() throws IOException, InterruptedException {
createModel();
setupReceiverAndConnect(true);
// check initial value
TimeUnit.SECONDS.sleep(2);
checkData(1, Integer.parseInt(INITIAL_VALUE), prefixed(INITIAL_VALUE), 1, Integer.parseInt(INITIAL_VALUE), prefixed(INITIAL_VALUE));
// set new value
sendData("2", "3");
// check new value
TimeUnit.SECONDS.sleep(2);
checkData(2, 2, prefixed("2"), 2, 3, prefixed("3"));
// set new value
sendData("4", "4");
// check new value
TimeUnit.SECONDS.sleep(2);
checkData(3, 4, prefixed("4"), 3, 4, prefixed("4"));
// set new value only for same
setDataOnlySame("77");
// check new value
TimeUnit.SECONDS.sleep(2);
checkData(4, 77, prefixed("77"), 3, 4, prefixed("4"));
}
private String prefixed(String s) {
return "prefix" + s;
}
@Test
public void communicateOnlyUpdatedValue() throws IOException, InterruptedException {
createModel();
setupReceiverAndConnect(false);
// check initial value
TimeUnit.SECONDS.sleep(2);
checkData(0, null, null, 0, null, null);
// set new value
sendData("2", "3");
// check new value
TimeUnit.SECONDS.sleep(2);
checkData(1, 2, prefixed("2"), 1, 3, prefixed("3"));
// set new value
sendData("4", "4");
// check new value
TimeUnit.SECONDS.sleep(2);
checkData(2, 4, prefixed("4"), 2, 4, prefixed("4"));
// set new value only for same
setDataOnlySame("77");
// check new value
TimeUnit.SECONDS.sleep(2);
checkData(3, 77, prefixed("77"), 2, 4, prefixed("4"));
}
private void createModel() {
// Setting value for Input without dependencies does not trigger any updates
model = new A();
onSameNonterminal = new OnSameNonterminal();
model.setOnSameNonterminal(onSameNonterminal);
onSameNonterminal.setInput(INITIAL_VALUE);
onDifferentNonterminal = new OnDifferentNonterminal();
other1 = new TheOther();
other2 = new TheOther();
onDifferentNonterminal.addTheOther(other1);
onDifferentNonterminal.addTheOther(other2);
model.setOnDifferentNonterminal(onDifferentNonterminal);
onDifferentNonterminal.setInput(INITIAL_VALUE);
}
private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
model.MqttSetHost(TestUtils.getMqttHost());
assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS));
handler = new MqttUpdater().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
onSameNonterminal.addIntDependency(onSameNonterminal);
onSameNonterminal.addStringDependency(onSameNonterminal);
other1.addIntDependency(onDifferentNonterminal);
other1.addStringDependency(onDifferentNonterminal);
other2.addIntDependency(onDifferentNonterminal);
other2.addStringDependency(onDifferentNonterminal);
dataSame = new Read1Write2Test.ReceiverData();
dataOther1 = new Read1Write2Test.ReceiverData();
dataOther2 = new Read1Write2Test.ReceiverData();
handler.newConnection(TOPIC_SAME_WRITE_INT, bytes -> {
dataSame.numberOfIntValues += 1;
dataSame.lastIntValue = java.nio.ByteBuffer.wrap(bytes).getInt();
});
handler.newConnection(TOPIC_SAME_WRITE_STRING, bytes -> {
dataSame.numberOfStringValues += 1;
dataSame.lastStringValue = new String(bytes);
});
handler.newConnection(TOPIC_DIFFERENT_WRITE1_INT, bytes -> {
dataOther1.numberOfIntValues += 1;
dataOther1.lastIntValue = java.nio.ByteBuffer.wrap(bytes).getInt();
});
handler.newConnection(TOPIC_DIFFERENT_WRITE1_STRING, bytes -> {
dataOther1.numberOfStringValues += 1;
dataOther1.lastStringValue = new String(bytes);
});
handler.newConnection(TOPIC_DIFFERENT_WRITE2_INT, bytes -> {
dataOther2.numberOfIntValues += 1;
dataOther2.lastIntValue = java.nio.ByteBuffer.wrap(bytes).getInt();
});
handler.newConnection(TOPIC_DIFFERENT_WRITE2_STRING, bytes -> {
dataOther2.numberOfStringValues += 1;
dataOther2.lastStringValue = new String(bytes);
});
onSameNonterminal.connectInput(TOPIC_SAME_READ);
onSameNonterminal.connectOutInteger(TOPIC_SAME_WRITE_INT, writeCurrentValue);
onSameNonterminal.connectOutString(TOPIC_SAME_WRITE_STRING, writeCurrentValue);
onDifferentNonterminal.connectInput(TOPIC_DIFFERENT_READ);
other1.connectOutInteger(TOPIC_DIFFERENT_WRITE1_INT, writeCurrentValue);
other1.connectOutString(TOPIC_DIFFERENT_WRITE1_STRING, writeCurrentValue);
other2.connectOutInteger(TOPIC_DIFFERENT_WRITE2_INT, writeCurrentValue);
other2.connectOutString(TOPIC_DIFFERENT_WRITE2_STRING, writeCurrentValue);
}
private void sendData(String inputSame, String inputDifferent) {
handler.publish(TOPIC_SAME_READ, inputSame.getBytes());
handler.publish(TOPIC_DIFFERENT_READ, inputDifferent.getBytes());
}
private void setDataOnlySame(String inputSame) {
handler.publish(TOPIC_SAME_READ, inputSame.getBytes());
}
private void checkData(int numberOfSameValues, Integer lastSameIntValue, String lastSameStringValue,
int numberOfDifferentValues, Integer lastDifferentIntValue,
String lastDifferentStringValue) {
/* the value "-2" is never used in the test, so a test will always fail comparing to this value
especially, it is not the initial value */
ReceiverData expectedDataSame = ReceiverData.of(
numberOfSameValues,
lastSameIntValue != null ? lastSameIntValue : -2,
lastSameStringValue);
compareData(expectedDataSame, dataSame);
ReceiverData expectedDataDifferent = ReceiverData.of(
numberOfDifferentValues,
lastDifferentIntValue != null ? lastDifferentIntValue : -2,
lastDifferentStringValue);
compareData(expectedDataDifferent, dataOther1);
compareData(expectedDataDifferent, dataOther2);
}
private void compareData(ReceiverData expectedData,
ReceiverData actual) {
assertEquals(expectedData.numberOfIntValues, actual.numberOfIntValues);
assertEquals(expectedData.numberOfStringValues, actual.numberOfStringValues);
if (expectedData.numberOfIntValues > 0) {
assertEquals(expectedData.lastIntValue, actual.lastIntValue);
}
if (expectedData.numberOfStringValues > 0) {
assertEquals(expectedData.lastStringValue, actual.lastStringValue);
}
}
private static class ReceiverData {
int lastIntValue;
int numberOfIntValues = 0;
String lastStringValue;
int numberOfStringValues = 0;
static ReceiverData of(int numberOfValues, int lastIntValue, String lastStringValue) {
ReceiverData result = new ReceiverData();
result.lastIntValue = lastIntValue;
result.lastStringValue = lastStringValue;
result.numberOfIntValues = numberOfValues;
result.numberOfStringValues = numberOfValues;
return result;
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment