You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2020/02/27 07:34:27 UTC
[plc4x] branch feature/driver-testsuite updated: - Some further
updates on the driver feature/driver-testsuite -- Renamed the xml constants
(hopefully more intuitive) -- Streamlined the DriverTestsuiteRunner code
This is an automated email from the ASF dual-hosted git repository.
cdutz pushed a commit to branch feature/driver-testsuite
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/feature/driver-testsuite by this push:
new f821ccb - Some further updates on the driver feature/driver-testsuite -- Renamed the xml constants (hopefully more intuitive) -- Streamlined the DriverTestsuiteRunner code
f821ccb is described below
commit f821ccb279ddbb9bd43b76cc3a6c56ea0c588cc3
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Thu Feb 27 08:34:18 2020 +0100
- Some further updates on the driver feature/driver-testsuite
-- Renamed the xml constants (hopefully more intuitive)
-- Streamlined the DriverTestsuiteRunner code
---
.../test/resources/testsuite/S7DriverTestsuite.xml | 50 +++---
.../plc4x/test/driver/DriverTestsuiteRunner.java | 190 ++++++++++++---------
.../apache/plc4x/test/driver/model/StepType.java | 8 +-
.../main/resources/schemas/driver-testsuite.xsd | 24 +--
4 files changed, 159 insertions(+), 113 deletions(-)
diff --git a/plc4j/drivers/s7/src/test/resources/testsuite/S7DriverTestsuite.xml b/plc4j/drivers/s7/src/test/resources/testsuite/S7DriverTestsuite.xml
index 015d5cd..28bd370 100644
--- a/plc4j/drivers/s7/src/test/resources/testsuite/S7DriverTestsuite.xml
+++ b/plc4j/drivers/s7/src/test/resources/testsuite/S7DriverTestsuite.xml
@@ -26,7 +26,7 @@
<setup>
<!-- First the driver is expected to send a COTP connection request -->
- <send-plc-message name="Send COTP Connection Request">
+ <outgoing-plc-message name="Send COTP Connection Request">
<TPKTPacket className="org.apache.plc4x.java.s7.readwrite.TPKTPacket">
<payload className="org.apache.plc4x.java.s7.readwrite.COTPPacketConnectionRequest">
<parameters>
@@ -46,9 +46,9 @@
<protocolClass>CLASS_0</protocolClass>
</payload>
</TPKTPacket>
- </send-plc-message>
+ </outgoing-plc-message>
<!-- The PLC will send a COTP connection response -->
- <receive-plc-message name="Receive COTP Connection Response">
+ <incoming-plc-message name="Receive COTP Connection Response">
<TPKTPacket className="org.apache.plc4x.java.s7.readwrite.TPKTPacket">
<payload className="org.apache.plc4x.java.s7.readwrite.COTPPacketConnectionResponse">
<parameters>
@@ -68,9 +68,9 @@
<protocolClass>CLASS_0</protocolClass>
</payload>
</TPKTPacket>
- </receive-plc-message>
+ </incoming-plc-message>
<!-- After that the driver will send a S7 connection request -->
- <send-plc-message name="Send S7 Connection Request">
+ <outgoing-plc-message name="Send S7 Connection Request">
<TPKTPacket className="org.apache.plc4x.java.s7.readwrite.TPKTPacket">
<payload className="org.apache.plc4x.java.s7.readwrite.COTPPacketData">
<parameters/>
@@ -87,9 +87,9 @@
<tpduRef>1</tpduRef>
</payload>
</TPKTPacket>
- </send-plc-message>
+ </outgoing-plc-message>
<!-- The PLC will send a S7 connection response -->
- <receive-plc-message name="Receive S7 Connection Response">
+ <incoming-plc-message name="Receive S7 Connection Response">
<TPKTPacket className="org.apache.plc4x.java.s7.readwrite.TPKTPacket">
<payload className="org.apache.plc4x.java.s7.readwrite.COTPPacketData">
<parameters/>
@@ -108,9 +108,9 @@
<tpduRef>0</tpduRef>
</payload>
</TPKTPacket>
- </receive-plc-message>
+ </incoming-plc-message>
<!-- Next we'll query some type information -->
- <send-plc-message name="Send S7 Identification Request">
+ <outgoing-plc-message name="Send S7 Identification Request">
<TPKTPacket className="org.apache.plc4x.java.s7.readwrite.TPKTPacket">
<payload className="org.apache.plc4x.java.s7.readwrite.COTPPacketData">
<parameters/>
@@ -149,9 +149,9 @@
<tpduRef>2</tpduRef>
</payload>
</TPKTPacket>
- </send-plc-message>
+ </outgoing-plc-message>
<!-- Which the PLC will gladly provide to us -->
- <receive-plc-message name="Receive S7 Identification Response">
+ <incoming-plc-message name="Receive S7 Identification Response">
<TPKTPacket className="org.apache.plc4x.java.s7.readwrite.TPKTPacket">
<payload className="org.apache.plc4x.java.s7.readwrite.COTPPacketData">
<parameters/>
@@ -213,13 +213,13 @@
<tpduRef>0</tpduRef>
</payload>
</TPKTPacket>
- </receive-plc-message>
+ </incoming-plc-message>
</setup>
<testcase>
<name>Single element read request</name>
<steps>
- <api-request name="Send Read Request">
+ <api-request name="Receive Read Request from application">
<TestReadRequest className="org.apache.plc4x.test.driver.model.api.TestReadRequest">
<fields>
<field className="org.apache.plc4x.test.driver.model.api.TestField">
@@ -227,8 +227,9 @@
<address>%Q0.0:BOOL</address>
</field>
</fields>
- </TestReadRequest> </api-request>
- <send-plc-message name="Send S7 Read Request">
+ </TestReadRequest>
+ </api-request>
+ <outgoing-plc-message name="Send S7 Read Request">
<TPKTPacket className="org.apache.plc4x.java.s7.readwrite.TPKTPacket">
<payload className="org.apache.plc4x.java.s7.readwrite.COTPPacketData">
<parameters/>
@@ -254,8 +255,8 @@
<tpduRef>10</tpduRef>
</payload>
</TPKTPacket>
- </send-plc-message>
- <receive-plc-message name="Receive S7 Read Response">
+ </outgoing-plc-message>
+ <incoming-plc-message name="Receive S7 Read Response">
<TPKTPacket className="org.apache.plc4x.java.s7.readwrite.TPKTPacket">
<payload className="org.apache.plc4x.java.s7.readwrite.COTPPacketData">
<parameters/>
@@ -281,10 +282,17 @@
<tpduRef>0</tpduRef>
</payload>
</TPKTPacket>
- </receive-plc-message>
- <!--api-response-message>
-
- </api-response-message-->
+ </incoming-plc-message>
+ <api-response name="Report Read Response to application">
+ <TestReadRequest className="org.apache.plc4x.test.driver.model.api.TestReadRequest">
+ <fields>
+ <field className="org.apache.plc4x.test.driver.model.api.TestField">
+ <name>hurz</name>
+ <address>%Q0.0:BOOL</address>
+ </field>
+ </fields>
+ </TestReadRequest>
+ </api-response>
<delay>1000</delay>
</steps>
</testcase>
diff --git a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/DriverTestsuiteRunner.java b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/DriverTestsuiteRunner.java
index ab378ad..85d872c 100644
--- a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/DriverTestsuiteRunner.java
+++ b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/DriverTestsuiteRunner.java
@@ -18,6 +18,7 @@ under the License.
*/
package org.apache.plc4x.test.driver;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import io.netty.buffer.ByteBuf;
@@ -29,6 +30,7 @@ import org.apache.plc4x.java.api.PlcConnection;
import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcResponse;
import org.apache.plc4x.java.spi.connection.ChannelExposingConnection;
import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
import org.apache.plc4x.java.spi.generation.*;
@@ -56,6 +58,7 @@ import org.xmlunit.diff.Diff;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class DriverTestsuiteRunner {
@@ -64,6 +67,8 @@ public class DriverTestsuiteRunner {
private final String testsuiteDocument;
+ private CompletableFuture<? extends PlcResponse> responseFuture;
+
public DriverTestsuiteRunner(String testsuiteDocument) {
this.testsuiteDocument = testsuiteDocument;
}
@@ -196,72 +201,35 @@ public class DriverTestsuiteRunner {
}
private void executeStep(TestStep testStep, PlcConnection plcConnection, Plc4xEmbeddedChannel embeddedChannel, boolean bigEndian) throws DriverTestsuiteException {
- LOGGER.info(" - Running step: '" + testStep.getName() + "' - " + testStep.getType());
+ LOGGER.info(String.format(" - Running step: '%s' - %s", testStep.getName(), testStep.getType()));
final ObjectMapper mapper = new XmlMapper().enableDefaultTyping();
final Element payload = testStep.getPayload();
try {
switch (testStep.getType()) {
- case SEND_PLC_BYTES: {
- final ByteBuf byteBuf = embeddedChannel.readOutbound();
- if(byteBuf == null) {
- throw new DriverTestsuiteException("No outbound message available");
- }
- final byte[] data = new byte[byteBuf.readableBytes()];
- byteBuf.readBytes(data);
-
- // TODO: Compare the read bytes with the expected byte array.
-
+ case OUTGOING_PLC_BYTES: {
+ // As we're in the asynchronous world, give the driver some time to respond.
+ shortDelay();
+ // Prepare a ByteBuf that contains the data which would have been sent to the PLC.
+ final byte[] data = getOutboundBytes(embeddedChannel);
+ // Validate the data actually matches the expected message.
+ validateBytes(payload, data, bigEndian);
break;
}
- case SEND_PLC_MESSAGE: {
- try {
- TimeUnit.MILLISECONDS.sleep(200);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new DriverTestsuiteException("Interrupted during delay.");
- }
-
- final ByteBuf byteBuf = embeddedChannel.readOutbound();
- if(byteBuf == null) {
- throw new DriverTestsuiteException("No outbound message available");
- }
- final byte[] data = new byte[byteBuf.readableBytes()];
- byteBuf.readBytes(data);
-
- final ReadBuffer readBuffer = new ReadBuffer(data, !bigEndian);
- try {
- final String className = payload.attributeValue(new QName("className"));
- final MessageIO messageIO = getMessageIOType(className).newInstance();
- final Object parsedOutput = messageIO.parse(readBuffer);
- final String xmlString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(parsedOutput);
- final String referenceXml = payload.asXML();
- final Diff diff = DiffBuilder.compare(referenceXml).withTest(xmlString).ignoreComments().ignoreWhitespace().build();
- if (diff.hasDifferences()) {
- System.out.println(xmlString);
- throw new DriverTestsuiteException("Differences were found after parsing.\n" + diff.toString());
- }
- } catch (ParseException e) {
- throw new DriverTestsuiteException("Error parsing message", e);
- }
+ case OUTGOING_PLC_MESSAGE: {
+ // As we're in the asynchronous world, give the driver some time to respond.
+ shortDelay();
+ // Prepare a ByteBuf that contains the data which would have been sent to the PLC.
+ final byte[] data = getOutboundBytes(embeddedChannel);
+ // Validate the data actually matches the expected message.
+ validateMessage(payload, data, bigEndian);
break;
}
- case RECEIVE_PLC_BYTES: {
- break;
- }
- case RECEIVE_PLC_MESSAGE: {
- final String className = payload.attributeValue(new QName("className"));
- final MessageIO messageIO = getMessageIOType(className).newInstance();
- final String referenceXml = payload.asXML();
- final Message message = mapper.readValue(referenceXml, getMessageType(className));
- final WriteBuffer writeBuffer = new WriteBuffer(1024, !bigEndian);
- try {
- messageIO.serialize(writeBuffer, message);
- final byte[] data = new byte[writeBuffer.getPos()];
- System.arraycopy(writeBuffer.getData(), 0, data, 0, writeBuffer.getPos());
- embeddedChannel.writeInbound(Unpooled.wrappedBuffer(data));
- } catch (ParseException e) {
- throw new DriverTestsuiteException("Error serializing message", e);
- }
+ case INCOMING_PLC_BYTES:
+ case INCOMING_PLC_MESSAGE: {
+ // Get a byte representation of the incoming message.
+ final byte[] data = getBytesFromXml(payload, bigEndian);
+ // Send the bytes to the channel.
+ embeddedChannel.writeInbound(Unpooled.wrappedBuffer(data));
break;
}
case API_REQUEST: {
@@ -274,30 +242,34 @@ public class DriverTestsuiteRunner {
builder.addItem(testField.getName(), testField.getAddress());
}
final PlcReadRequest plc4xRequest = builder.build();
- // Don't block here or the test will not be able to process the rest.
- plc4xRequest.execute().whenComplete((result, throwable) -> {
- // TODO: Do something in here ...
- if(result != null) {
- System.out.println("Success");
- } else {
- System.out.println("Failure " + throwable.getMessage());
- }
- });
+ // Currently we can only process one response at at time, throw an error if more
+ // are submitted.
+ if(responseFuture != null) {
+ throw new DriverTestsuiteException("Previous response not handled.");
+ }
+ // Save the response for being used later on.
+ responseFuture = plc4xRequest.execute();
} else if(request instanceof TestWriteRequest) {
-
+ // TODO: Implement ...
}
break;
}
case API_RESPONSE: {
+ if(responseFuture == null) {
+ throw new DriverTestsuiteException("No response expected.");
+ }
+ try {
+ final PlcResponse plcResponse = responseFuture.get(1000, TimeUnit.MILLISECONDS);
+ // TODO: Implement ...
+ final String serializedResponse = mapper.writeValueAsString(plcResponse);
+ System.out.println(serializedResponse);
+ } catch(Exception e) {
+ throw new DriverTestsuiteException("Got no response within 1000ms.");
+ }
break;
}
case DELAY: {
- try {
- TimeUnit.MILLISECONDS.sleep(1000);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new DriverTestsuiteException("Interrupted during delay.");
- }
+ delay(1000);
break;
}
case TERMINATE: {
@@ -306,8 +278,6 @@ public class DriverTestsuiteRunner {
}
} catch (IOException e) {
throw new DriverTestsuiteException("Error processing the xml", e);
- } catch (IllegalAccessException | InstantiationException e) {
- throw new DriverTestsuiteException("Error instantiating MessageIO class", e);
}
LOGGER.info(" Done");
}
@@ -335,6 +305,7 @@ public class DriverTestsuiteRunner {
throw new PlcRuntimeException("Expecting ChannelExposingConnection");
}
+ @SuppressWarnings("unchecked")
private Class<? extends Message> getMessageType(String messageClassName) throws DriverTestsuiteException {
try {
final Class<?> messageClass = Class.forName(messageClassName);
@@ -362,4 +333,71 @@ public class DriverTestsuiteRunner {
}
}
+ private byte[] getOutboundBytes(Plc4xEmbeddedChannel embeddedChannel) throws DriverTestsuiteException {
+ final ByteBuf byteBuf = embeddedChannel.readOutbound();
+ if(byteBuf == null) {
+ throw new DriverTestsuiteException("No outbound message available");
+ }
+ final byte[] data = new byte[byteBuf.readableBytes()];
+ byteBuf.readBytes(data);
+ return data;
+ }
+
+ private byte[] getBytesFromXml(Element referenceXml, boolean bigEndian) throws DriverTestsuiteException {
+ final String className = referenceXml.attributeValue(new QName("className"));
+ final WriteBuffer writeBuffer = new WriteBuffer(1024, !bigEndian);
+ try {
+ final MessageIO messageIO = getMessageIOType(className).newInstance();
+ final String referenceXmlString = referenceXml.asXML();
+ final ObjectMapper mapper = new XmlMapper().enableDefaultTyping();
+ final Message message = mapper.readValue(referenceXmlString, getMessageType(className));
+ try {
+ messageIO.serialize(writeBuffer, message);
+ final byte[] data = new byte[message.getLengthInBytes()];
+ System.arraycopy(writeBuffer.getData(), 0, data, 0, writeBuffer.getPos());
+ return data;
+ } catch (ParseException e) {
+ throw new DriverTestsuiteException("Error serializing message", e);
+ }
+ } catch (IllegalAccessException | JsonProcessingException | InstantiationException e) {
+ throw new DriverTestsuiteException("Error parsing message", e);
+ }
+ }
+
+ private void validateBytes(Element referenceXml, byte[] data, boolean bigEndian) throws DriverTestsuiteException {
+ // TODO: Implement this ...
+ }
+
+ private void validateMessage(Element referenceXml, byte[] data, boolean bigEndian) throws DriverTestsuiteException {
+ final ObjectMapper mapper = new XmlMapper().enableDefaultTyping();
+ final ReadBuffer readBuffer = new ReadBuffer(data, !bigEndian);
+ try {
+ final String className = referenceXml.attributeValue(new QName("className"));
+ final MessageIO<?,?> messageIO = getMessageIOType(className).newInstance();
+ final Object parsedOutput = messageIO.parse(readBuffer);
+ final String xmlString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(parsedOutput);
+ final String referenceXmlString = referenceXml.asXML();
+ final Diff diff = DiffBuilder.compare(referenceXmlString).withTest(xmlString).ignoreComments().ignoreWhitespace().build();
+ if (diff.hasDifferences()) {
+ LOGGER.warn(xmlString);
+ throw new DriverTestsuiteException("Differences were found after parsing.\n" + diff.toString());
+ }
+ } catch (ParseException | IllegalAccessException | JsonProcessingException | InstantiationException e) {
+ throw new DriverTestsuiteException("Error parsing message", e);
+ }
+ }
+
+ private void shortDelay() throws DriverTestsuiteException {
+ delay(23);
+ }
+
+ private void delay(int milliseconds) throws DriverTestsuiteException {
+ try {
+ TimeUnit.MILLISECONDS.sleep(milliseconds);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new DriverTestsuiteException("Interrupted during delay.");
+ }
+ }
+
}
diff --git a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/model/StepType.java b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/model/StepType.java
index c371331..374a2f8 100644
--- a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/model/StepType.java
+++ b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/model/StepType.java
@@ -30,10 +30,10 @@ public enum StepType {
API_RESPONSE: -------->
*/
- SEND_PLC_MESSAGE,
- SEND_PLC_BYTES,
- RECEIVE_PLC_MESSAGE,
- RECEIVE_PLC_BYTES,
+ OUTGOING_PLC_MESSAGE,
+ OUTGOING_PLC_BYTES,
+ INCOMING_PLC_MESSAGE,
+ INCOMING_PLC_BYTES,
API_REQUEST,
API_RESPONSE,
DELAY,
diff --git a/plc4j/utils/test-utils/src/main/resources/schemas/driver-testsuite.xsd b/plc4j/utils/test-utils/src/main/resources/schemas/driver-testsuite.xsd
index 7008c34..a9576ae 100644
--- a/plc4j/utils/test-utils/src/main/resources/schemas/driver-testsuite.xsd
+++ b/plc4j/utils/test-utils/src/main/resources/schemas/driver-testsuite.xsd
@@ -49,10 +49,10 @@
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:choice>
- <xs:element name="send-plc-message" type="messageStep"/>
- <xs:element name="send-plc-bytes" type="bytesStep"/>
- <xs:element name="receive-plc-message" type="messageStep"/>
- <xs:element name="receive-plc-bytes" type="bytesStep"/>
+ <xs:element name="outgoing-plc-message" type="messageStep"/>
+ <xs:element name="outgoing-plc-bytes" type="bytesStep"/>
+ <xs:element name="incoming-plc-message" type="messageStep"/>
+ <xs:element name="incoming-plc-bytes" type="bytesStep"/>
<xs:element name="api-request" type="messageStep"/>
<xs:element name="api-response" type="messageStep"/>
<xs:element name="delay" type="xs:integer"/>
@@ -67,10 +67,10 @@
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:choice>
- <xs:element name="send-plc-message" type="messageStep"/>
- <xs:element name="send-plc-bytes" type="bytesStep"/>
- <xs:element name="receive-plc-message" type="messageStep"/>
- <xs:element name="receive-plc-bytes" type="bytesStep"/>
+ <xs:element name="outgoing-plc-message" type="messageStep"/>
+ <xs:element name="outgoing-plc-bytes" type="bytesStep"/>
+ <xs:element name="incoming-plc-message" type="messageStep"/>
+ <xs:element name="incoming-plc-bytes" type="bytesStep"/>
<xs:element name="api-request" type="messageStep"/>
<xs:element name="api-response" type="messageStep"/>
<xs:element name="delay" type="xs:integer"/>
@@ -92,10 +92,10 @@
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:choice>
- <xs:element name="send-plc-message" type="messageStep"/>
- <xs:element name="send-plc-bytes" type="bytesStep"/>
- <xs:element name="receive-plc-message" type="messageStep"/>
- <xs:element name="receive-plc-bytes" type="bytesStep"/>
+ <xs:element name="outgoing-plc-message" type="messageStep"/>
+ <xs:element name="outgoing-plc-bytes" type="bytesStep"/>
+ <xs:element name="incoming-plc-message" type="messageStep"/>
+ <xs:element name="incoming-plc-bytes" type="bytesStep"/>
<xs:element name="api-request" type="messageStep"/>
<xs:element name="api-response" type="messageStep"/>
<xs:element name="delay" type="xs:integer"/>