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 2019/07/01 13:56:43 UTC
[plc4x] branch develop updated: - Implemented a helper to create
test-suites for generated protocols in java. - Refactored the code
generation to be a bit more generic and use shared interfaces.
This is an automated email from the ASF dual-hosted git repository.
cdutz pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/develop by this push:
new 1f3144b - Implemented a helper to create test-suites for generated protocols in java. - Refactored the code generation to be a bit more generic and use shared interfaces.
1f3144b is described below
commit 1f3144bf26db3db40cac2a1ac54082456296c673
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Mon Jul 1 15:56:35 2019 +0200
- Implemented a helper to create test-suites for generated protocols in java.
- Refactored the code generation to be a bit more generic and use shared interfaces.
---
.../language/java/JavaLanguageTemplateHelper.java | 30 ++++-
.../main/resources/templates/java/io-template.ftlh | 48 ++++---
.../resources/templates/java/pojo-template.ftlh | 3 +-
.../java/org/apache/plc4x/java/utils/Message.java | 23 ++++
.../org/apache/plc4x/java/utils/MessageIO.java | 28 +++++
plc4j/utils/pom.xml | 5 +-
plc4j/utils/protocol-test-utils/pom.xml | 72 +++++++++++
.../protocol/test/ProtocolTestsuiteRunner.java | 140 +++++++++++++++++++++
.../exceptions/ProtocolTestsuiteException.java | 32 +++++
.../protocol/test/model/ProtocolTestsuite.java | 42 +++++++
.../apache/plc4x/protocol/test/model/Testcase.java | 61 +++++++++
.../src/main/resources/schemas/testsuite.xsd | 54 ++++++++
pom.xml | 16 +++
sandbox/test-java-knxnetip-driver/pom.xml | 6 +
.../org/apache/plc4x/java/knxnetip/IOTest.java | 14 ++-
.../plc4x/java/knxnetip/KNXNetIpTestsuite.java | 30 +++++
.../test/resources/testsuite/KNXNetIPTestsuite.xml | 60 +++++++++
.../src/test/java/BenchmarkGeneratedS7.java | 5 +-
18 files changed, 639 insertions(+), 30 deletions(-)
diff --git a/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java b/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
index 39af1a1..5f43830 100644
--- a/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
+++ b/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
@@ -24,14 +24,14 @@ import org.apache.plc4x.plugins.codegenerator.protocol.freemarker.FreemarkerLang
import org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition;
import org.apache.plc4x.plugins.codegenerator.types.definitions.DiscriminatedComplexTypeDefinition;
import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
-import org.apache.plc4x.plugins.codegenerator.types.fields.ArrayField;
-import org.apache.plc4x.plugins.codegenerator.types.fields.OptionalField;
-import org.apache.plc4x.plugins.codegenerator.types.fields.TypedField;
+import org.apache.plc4x.plugins.codegenerator.types.fields.*;
import org.apache.plc4x.plugins.codegenerator.types.references.ComplexTypeReference;
import org.apache.plc4x.plugins.codegenerator.types.references.SimpleTypeReference;
import org.apache.plc4x.plugins.codegenerator.types.references.TypeReference;
import org.apache.plc4x.plugins.codegenerator.types.terms.*;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Matcher;
@@ -331,6 +331,30 @@ public class JavaLanguageTemplateHelper implements FreemarkerLanguageTemplateHel
}
}
+ public Collection<ComplexTypeReference> getComplexTypes(ComplexTypeDefinition complexTypeDefinition) {
+ Map<String, ComplexTypeReference> types = new HashMap<>();
+ for (Field field : complexTypeDefinition.getFields()) {
+ if(field instanceof TypedField) {
+ TypedField typedField = (TypedField) field;
+ if(typedField.getType() instanceof ComplexTypeReference) {
+ ComplexTypeReference complexTypeReference = (ComplexTypeReference) typedField.getType();
+ types.put(complexTypeReference.getName(), complexTypeReference);
+ }
+ } else if(field instanceof SwitchField) {
+ SwitchField switchField = (SwitchField) field;
+ for (DiscriminatedComplexTypeDefinition cas : switchField.getCases()) {
+ types.put(cas.getName(), new ComplexTypeReference() {
+ @Override
+ public String getName() {
+ return cas.getName();
+ }
+ });
+ }
+ }
+ }
+ return types.values();
+ }
+
public boolean isSimpleType(TypeReference typeReference) {
return typeReference instanceof SimpleTypeReference;
}
diff --git a/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh b/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh
index d790f0b..085f4a2 100644
--- a/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh
+++ b/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh
@@ -39,10 +39,8 @@ ${packageName?replace(".", "/")}/io/${typeName}IO.java
package ${packageName}.io;
import ${packageName}.*;
-import org.apache.plc4x.java.utils.EvaluationHelper;
-import org.apache.plc4x.java.utils.ReadBuffer;
-import org.apache.plc4x.java.utils.ParseException;
-import org.apache.plc4x.java.utils.WriteBuffer;
+<#if helper.getComplexTypes(type)?has_content>import ${packageName}.io.*;</#if>
+import org.apache.plc4x.java.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,11 +49,29 @@ import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
-public class ${typeName}IO {
+public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscriminatedType(type)>IO.${typeName}Builder</#if>, ${typeName}> {
private static final Logger LOGGER = LoggerFactory.getLogger(${typeName}IO.class);
- public static <#if helper.isDiscriminatedType(type)>${typeName}Builder<#else>${typeName}</#if> parse(ReadBuffer io<#if type.parserArguments?has_content>, <#list type.parserArguments as parserArgument>${helper.getLanguageTypeNameForSpecType(parserArgument.type)} ${parserArgument.name}<#sep>, </#sep></#list></#if>) throws ParseException {
+<#if helper.getComplexTypes(type)?has_content>
+ // IO Helpers.
+<#list helper.getComplexTypes(type) as complexType>
+ private final ${complexType.name}IO ${complexType.name?uncap_first}IO;
+</#list>
+
+ public ${typeName}IO() {
+<#list helper.getComplexTypes(type) as complexType>
+ ${complexType.name?uncap_first}IO = new ${complexType.name}IO();
+</#list>
+ }
+
+</#if>
+ public ${typeName}<#if helper.isDiscriminatedType(type)>Builder</#if> parse(ReadBuffer io, Object... args) throws ParseException {
+<#if type.parserArguments?has_content>
+<#list type.parserArguments as parserArgument>
+ ${helper.getLanguageTypeNameForSpecType(parserArgument.type)} ${parserArgument.name} = (${helper.getLanguageTypeNameForSpecType(parserArgument.type)}) args[${parserArgument?index}];
+</#list>
+</#if>
int startPos = io.getPos();
int curPos;
<#list type.fields as field>
@@ -70,7 +86,7 @@ public class ${typeName}IO {
<#if helper.isCountArray(field)>
${helper.getLanguageTypeNameForField(field)}[] ${field.name} = new ${helper.getLanguageTypeNameForField(field)}[${field.name}Size];
for(int i = 0; i < ${field.name}Size; i++) {
- ${field.name}[i] = <#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>${parserArgument}<#sep>, </#sep></#list></#if>)</#if>;
+ ${field.name}[i] = <#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name?uncap_first}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>${parserArgument}<#sep>, </#sep></#list></#if>)</#if>;
}
<#else>
List<${helper.getLanguageTypeNameForField(field)}> ${field.name}List = <#if helper.isCountArray(field)>new ArrayList<>(size)<#else>new LinkedList<>()</#if>;
@@ -79,7 +95,7 @@ public class ${typeName}IO {
<#if field.lengthExpression.contains("curPos")>
curPos = io.getPos() - startPos;
</#if>
- ${field.name}List.add(<#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>(${helper.getArgumentType(field.type, parserArgument?index)}) (${parserArgument})<#sep>, </#sep></#list></#if>)</#if>);
+ ${field.name}List.add(<#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name?uncap_first}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>(${helper.getArgumentType(field.type, parserArgument?index)}) (${parserArgument})<#sep>, </#sep></#list></#if>)</#if>);
}
${helper.getLanguageTypeNameForField(field)}[] ${field.name} = ${field.name}List.toArray(new ${helper.getLanguageTypeNameForField(field)}[0]);
</#if>
@@ -107,7 +123,7 @@ public class ${typeName}IO {
// Optional Field (Can be skipped, if a given expression evaluates to false)
${helper.getLanguageTypeNameForField(field)} ${field.name} = ${helper.getNullValueForType(field.type)};
if(${helper.toDeserializationExpression(field.conditionExpression)}) {
- ${field.name} = <#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name}IO.parse(io);</#if>;
+ ${field.name} = <#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name?uncap_first}IO.parse(io);</#if>;
}
<#break>
<#case "reserved">
@@ -123,7 +139,7 @@ public class ${typeName}IO {
<#case "simple">
// Simple field
- ${helper.getLanguageTypeNameForField(field)} ${field.name} = <#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>${parserArgument}<#sep>, </#sep></#list></#if>)</#if>;
+ ${helper.getLanguageTypeNameForField(field)} ${field.name} = <#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name?uncap_first}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>${parserArgument}<#sep>, </#sep></#list></#if>)</#if>;
<#break>
<#case "switch">
@@ -131,7 +147,7 @@ public class ${typeName}IO {
${typeName}Builder builder = null;
<#list field.cases as case>
if(<#list case.discriminatorValues as discriminatorValue>EvaluationHelper.equals(${helper.toSwitchExpression(field.discriminatorNames[discriminatorValue?index])}, ${discriminatorValue})<#sep> && </#sep></#list>) {
- builder = ${case.name}IO.parse(io<#if type.parserArguments?has_content>, <#list type.parserArguments as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>);
+ builder = ${case.name?uncap_first}IO.parse(io<#if type.parserArguments?has_content>, <#list type.parserArguments as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>);
}<#sep> else </#sep>
</#list>
if (builder == null) {
@@ -151,7 +167,7 @@ public class ${typeName}IO {
</#if>
}
- public static void serialize(WriteBuffer io, ${typeName} value) throws ParseException {
+ public void serialize(WriteBuffer io, ${typeName} value) throws ParseException {
<#list type.fields as field>
<#switch field.typeName>
<#case "array">
@@ -162,7 +178,7 @@ public class ${typeName}IO {
<#if helper.isSimpleType(field.type)>
io.${helper.getWriteBufferReadMethodCall(field.type, "element")};
<#else>
- ${helper.getLanguageTypeNameForField(field)}IO.serialize(io, element);
+ ${field.type.name?uncap_first}IO.serialize(io, element);
</#if>
}
}
@@ -193,7 +209,7 @@ public class ${typeName}IO {
<#if helper.isSimpleType(field.type)>
io.${helper.getWriteBufferReadMethodCall(field.type, "value.get" + field.name?cap_first + "()")};
<#else>
- ${helper.getLanguageTypeNameForField(field)}IO.serialize(io, value.get${field.name?cap_first}());
+ ${field.type.name?uncap_first}IO.serialize(io, value.get${field.name?cap_first}());
</#if>
}
<#break>
@@ -208,7 +224,7 @@ public class ${typeName}IO {
<#if helper.isSimpleType(field.type)>
io.${helper.getWriteBufferReadMethodCall(field.type, "value.get" + field.name?cap_first + "()")};
<#else>
- ${helper.getLanguageTypeNameForField(field)}IO.serialize(io, value.get${field.name?cap_first}());
+ ${field.type.name?uncap_first}IO.serialize(io, value.get${field.name?cap_first}());
</#if>
<#break>
<#case "switch">
@@ -216,7 +232,7 @@ public class ${typeName}IO {
// Switch field (Depending on the discriminator values, passes the instantiation to a sub-type)
<#list field.cases as case>
if(value instanceof ${case.name}) {
- ${case.name}IO.serialize(io, (${case.name}) value);
+ ${case.name?uncap_first}IO.serialize(io, (${case.name}) value);
}<#sep> else </#sep>
</#list>
<#break>
diff --git a/build-utils/language-java/src/main/resources/templates/java/pojo-template.ftlh b/build-utils/language-java/src/main/resources/templates/java/pojo-template.ftlh
index 33abe95..5b095bf 100644
--- a/build-utils/language-java/src/main/resources/templates/java/pojo-template.ftlh
+++ b/build-utils/language-java/src/main/resources/templates/java/pojo-template.ftlh
@@ -39,10 +39,11 @@ ${packageName?replace(".", "/")}/${typeName}.java
package ${packageName};
import com.fasterxml.jackson.annotation.*;
+import org.apache.plc4x.java.utils.Message;
import org.apache.plc4x.java.utils.SizeAware;
<#if type.abstract>@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "className")</#if>
-public<#if type.abstract> abstract</#if> class ${typeName}<#if type.parentType??> extends ${type.parentType.name}</#if> implements SizeAware {
+public<#if type.abstract> abstract</#if> class ${typeName}<#if type.parentType??> extends ${type.parentType.name}</#if> implements SizeAware, Message {
<#if helper.isDiscriminatedType(type)>
diff --git a/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/Message.java b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/Message.java
new file mode 100644
index 0000000..7bc60b6
--- /dev/null
+++ b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/Message.java
@@ -0,0 +1,23 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+package org.apache.plc4x.java.utils;
+
+public interface Message {
+}
diff --git a/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/MessageIO.java b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/MessageIO.java
new file mode 100644
index 0000000..03eb18d
--- /dev/null
+++ b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/MessageIO.java
@@ -0,0 +1,28 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+package org.apache.plc4x.java.utils;
+
+public interface MessageIO<PARSER_TYPE, SERIALIZER_TYPE> {
+
+ PARSER_TYPE parse(ReadBuffer io, Object... args) throws ParseException;
+
+ void serialize(WriteBuffer io, SERIALIZER_TYPE value) throws ParseException;
+
+}
diff --git a/plc4j/utils/pom.xml b/plc4j/utils/pom.xml
index cde00e4..59de591 100644
--- a/plc4j/utils/pom.xml
+++ b/plc4j/utils/pom.xml
@@ -35,11 +35,12 @@
<modules>
<module>driver-base-java</module>
+ <module>protocol-test-utils</module>
+ <module>raw-sockets</module>
+ <module>test-utils</module>
<module>connection-pool</module>
<module>opm</module>
- <module>raw-sockets</module>
- <module>test-utils</module>
<module>scraper</module>
</modules>
diff --git a/plc4j/utils/protocol-test-utils/pom.xml b/plc4j/utils/protocol-test-utils/pom.xml
new file mode 100644
index 0000000..3cc9b8e
--- /dev/null
+++ b/plc4j/utils/protocol-test-utils/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.plc4x</groupId>
+ <artifactId>plc4j-utils</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>plc4j-utils-protocol-test-utils</artifactId>
+
+ <name>PLC4J: Utils: Protocol Test Utils</name>
+ <description>Set of utilities designed to help testing protocols.</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.plc4x</groupId>
+ <artifactId>plc4j-utils-driver-base-java</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.dom4j</groupId>
+ <artifactId>dom4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-xml</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.xmlunit</groupId>
+ <artifactId>xmlunit-core</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/ProtocolTestsuiteRunner.java b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/ProtocolTestsuiteRunner.java
new file mode 100644
index 0000000..ab00e7e
--- /dev/null
+++ b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/ProtocolTestsuiteRunner.java
@@ -0,0 +1,140 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+package org.apache.plc4x.protocol.test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.plc4x.java.utils.MessageIO;
+import org.apache.plc4x.java.utils.ParseException;
+import org.apache.plc4x.java.utils.ReadBuffer;
+import org.apache.plc4x.protocol.test.exceptions.ProtocolTestsuiteException;
+import org.apache.plc4x.protocol.test.model.ProtocolTestsuite;
+import org.apache.plc4x.protocol.test.model.Testcase;
+import org.dom4j.*;
+import org.junit.jupiter.api.DynamicTest;
+import org.junit.jupiter.api.TestFactory;
+import org.dom4j.io.SAXReader;
+import org.xmlunit.builder.DiffBuilder;
+import org.xmlunit.diff.Diff;
+
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ProtocolTestsuiteRunner {
+
+ private static final Namespace TEST_NAMESPACE = new Namespace("test", "https://plc4x.apache.org/schemas/testsuite.xsd");
+
+ private final String testsuiteDocument;
+
+ public ProtocolTestsuiteRunner(String testsuiteDocument) {
+ this.testsuiteDocument = testsuiteDocument;
+ }
+
+ @TestFactory
+ public List<DynamicTest> getTestsuiteTests() throws ProtocolTestsuiteException {
+ ProtocolTestsuite testSuite = parseTestsuite(ProtocolTestsuiteRunner.class.getResourceAsStream(testsuiteDocument));
+ List<DynamicTest> dynamicTests = new LinkedList<>();
+ for(Testcase testcase : testSuite.getTestcases()) {
+ String testcaseName = testcase.getName();
+ String testcaseLabel = testSuite.getName() + ": " + testcaseName;
+ DynamicTest test = DynamicTest.dynamicTest(testcaseLabel, () ->
+ run(testcase)
+ );
+ dynamicTests.add(test);
+ }
+ return dynamicTests;
+ }
+
+ private ProtocolTestsuite parseTestsuite(InputStream testsuiteDocumentXml) throws ProtocolTestsuiteException {
+ try {
+ SAXReader reader = new SAXReader();
+ Document document = reader.read(testsuiteDocumentXml);
+ Element testsuiteXml = document.getRootElement();
+ Element testsuiteName = testsuiteXml.element(new QName("name"));
+ List<Element> testcasesXml = testsuiteXml.elements(new QName("testcase"));
+ List<Testcase> testcases = new ArrayList<>(testcasesXml.size());
+ for(Element testcaseXml : testcasesXml) {
+ Element nameElement = testcaseXml.element(new QName("name"));
+ Element descriptionElement = testcaseXml.element(new QName("description"));
+ Element rawElement = testcaseXml.element(new QName("raw"));
+ Element rootTypeElement = testcaseXml.element(new QName("root-type"));
+ Element xmlElement = testcaseXml.element(new QName("xml"));
+
+ String name = nameElement.getTextTrim();
+ String description = (descriptionElement != null) ? descriptionElement.getTextTrim() : null;
+ byte[] raw = Hex.decodeHex(rawElement.getTextTrim());
+ String rootType = rootTypeElement.getTextTrim();
+
+ testcases.add(new Testcase(name, description, raw, rootType, xmlElement));
+ }
+ return new ProtocolTestsuite(testsuiteName.getTextTrim(), testcases);
+ } catch (DocumentException e) {
+ throw new ProtocolTestsuiteException("Error parsing testsuite xml", e);
+ } catch (DecoderException e) {
+ throw new ProtocolTestsuiteException("Error parsing testcase raw data", e);
+ }
+ }
+
+ private void run(Testcase testcase) throws ProtocolTestsuiteException {
+ ObjectMapper mapper = new XmlMapper().enableDefaultTyping();
+ ReadBuffer readBuffer = new ReadBuffer(testcase.getRaw());
+ String referenceXml = testcase.getXml().elements().get(0).asXML();
+
+ MessageIO messageIO = getMessageIOForTestcase(testcase);
+ try {
+ Object msg = messageIO.parse(readBuffer);
+ String xmlString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(msg);
+ Diff diff = DiffBuilder.compare(referenceXml).withTest(xmlString).ignoreWhitespace().build();
+ if(diff.hasDifferences()) {
+ // TODO: Add some more information ...
+ throw new ProtocolTestsuiteException("Differences were found");
+ }
+ } catch (ParseException e) {
+ throw new ProtocolTestsuiteException("Unable to parse message", e);
+ } catch (JsonProcessingException e) {
+ throw new ProtocolTestsuiteException("Unable to serialize parsed message as XML string", e);
+ }
+ }
+
+ private MessageIO getMessageIOForTestcase(Testcase testcase) throws ProtocolTestsuiteException {
+ String className = testcase.getXml().elements().get(0).attributeValue(new QName("className"));
+ String ioClassName = className.substring(0, className.lastIndexOf('.') + 1) + "io." +
+ testcase.getRootType() + "IO";
+ try {
+ Class<?> ioClass = Class.forName(ioClassName);
+ Object inst = ioClass.getDeclaredConstructor().newInstance();
+ if(inst instanceof MessageIO) {
+ return (MessageIO) inst;
+ } else {
+ throw new ProtocolTestsuiteException("Found IO component class is not of type MessageIO");
+ }
+ } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException |
+ ClassNotFoundException e) {
+ throw new ProtocolTestsuiteException("Unable to instantiate IO component", e);
+ }
+ }
+
+}
diff --git a/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/exceptions/ProtocolTestsuiteException.java b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/exceptions/ProtocolTestsuiteException.java
new file mode 100644
index 0000000..81500b5
--- /dev/null
+++ b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/exceptions/ProtocolTestsuiteException.java
@@ -0,0 +1,32 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+package org.apache.plc4x.protocol.test.exceptions;
+
+public class ProtocolTestsuiteException extends Exception {
+
+ public ProtocolTestsuiteException(String message) {
+ super(message);
+ }
+
+ public ProtocolTestsuiteException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/model/ProtocolTestsuite.java b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/model/ProtocolTestsuite.java
new file mode 100644
index 0000000..68b0ffe
--- /dev/null
+++ b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/model/ProtocolTestsuite.java
@@ -0,0 +1,42 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+package org.apache.plc4x.protocol.test.model;
+
+import java.util.List;
+
+public class ProtocolTestsuite {
+
+ private final String name;
+ private final List<Testcase> testcases;
+
+ public ProtocolTestsuite(String name, List<Testcase> testcases) {
+ this.name = name;
+ this.testcases = testcases;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List<Testcase> getTestcases() {
+ return testcases;
+ }
+
+}
diff --git a/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/model/Testcase.java b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/model/Testcase.java
new file mode 100644
index 0000000..8df639b
--- /dev/null
+++ b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/model/Testcase.java
@@ -0,0 +1,61 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+package org.apache.plc4x.protocol.test.model;
+
+import org.dom4j.Element;
+import org.w3c.dom.Document;
+
+public class Testcase {
+
+ private final String name;
+ private final String description;
+ private final byte[] raw;
+ private final String rootType;
+ private final Element xml;
+
+ public Testcase(String name, String description, byte[] raw, String rootType, Element xml) {
+ this.name = name;
+ this.description = description;
+ this.raw = raw;
+ this.rootType = rootType;
+ this.xml = xml;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public byte[] getRaw() {
+ return raw;
+ }
+
+ public String getRootType() {
+ return rootType;
+ }
+
+ public Element getXml() {
+ return xml;
+ }
+
+}
diff --git a/plc4j/utils/protocol-test-utils/src/main/resources/schemas/testsuite.xsd b/plc4j/utils/protocol-test-utils/src/main/resources/schemas/testsuite.xsd
new file mode 100644
index 0000000..5ef4977
--- /dev/null
+++ b/plc4j/utils/protocol-test-utils/src/main/resources/schemas/testsuite.xsd
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+<xs:schema targetNamespace="https://plc4x.apache.org/schemas/testsuite.xsd"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+ <xs:element name="testsuite">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="name" type="xs:string"/>
+ <!--
+ Each testcase is a set of hexadecimal input and expected output.
+ A test will parse the binary input and compare the parsed result
+ with the expected output. If that matches, it serializes the model
+ back to it's binary representation and then compares the binary
+ output with the original.
+ -->
+ <xs:element name="testcase" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <!-- The name of the test, as it is output by the test-runner -->
+ <xs:element name="name" type="xs:string"/>
+ <!-- Some optional description of what the test should do -->
+ <xs:element name="description" type="xs:string" minOccurs="0"/>
+ <!-- A hexadecimal representation of the input to the parser -->
+ <xs:element name="raw" type="xs:hexBinary"/>
+ <!-- The root type we should use for serializing and parsing -->
+ <xs:element name="root-type" type="xs:string"/>
+ <!-- An Xml serialized representation of the expected model -->
+ <xs:element name="xml" type="xs:anyType"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+</xs:schema>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2fa24cb..e01a7f8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -155,6 +155,7 @@
<spock.version>1.2-groovy-2.5</spock.version>
<t-digest.version>3.2</t-digest.version>
<thrift.version>0.12.0</thrift.version>
+ <xmlunit.version>2.6.3</xmlunit.version>
<!-- Site properties -->
<asciidoctor.maven.plugin.version>2.0.0-RC.1</asciidoctor.maven.plugin.version>
@@ -538,6 +539,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.dom4j</groupId>
+ <artifactId>dom4j</artifactId>
+ <version>2.1.1</version>
+ </dependency>
+ <dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${elasticsearch.version}</version>
@@ -659,6 +665,16 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.xmlunit</groupId>
+ <artifactId>xmlunit-core</artifactId>
+ <version>${xmlunit.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.xmlunit</groupId>
+ <artifactId>xmlunit-matchers</artifactId>
+ <version>${xmlunit.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
diff --git a/sandbox/test-java-knxnetip-driver/pom.xml b/sandbox/test-java-knxnetip-driver/pom.xml
index bc23e19..cdcc0cc 100644
--- a/sandbox/test-java-knxnetip-driver/pom.xml
+++ b/sandbox/test-java-knxnetip-driver/pom.xml
@@ -66,6 +66,12 @@
</dependency>
<dependency>
+ <groupId>org.apache.plc4x</groupId>
+ <artifactId>plc4j-utils-protocol-test-utils</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<scope>test</scope>
diff --git a/sandbox/test-java-knxnetip-driver/src/test/java/org/apache/plc4x/java/knxnetip/IOTest.java b/sandbox/test-java-knxnetip-driver/src/test/java/org/apache/plc4x/java/knxnetip/IOTest.java
index 5653814..8ce22ad 100644
--- a/sandbox/test-java-knxnetip-driver/src/test/java/org/apache/plc4x/java/knxnetip/IOTest.java
+++ b/sandbox/test-java-knxnetip-driver/src/test/java/org/apache/plc4x/java/knxnetip/IOTest.java
@@ -36,7 +36,7 @@ public class IOTest {
byte[] rData = Hex.decodeHex("0610020500180801c0a82a46c4090801c0a82a46c40a0203");
ObjectMapper mapper = new XmlMapper().enableDefaultTyping();
ReadBuffer rBuf = new ReadBuffer(rData);
- KNXNetIPMessage packet = KNXNetIPMessageIO.parse(rBuf);
+ KNXNetIPMessage packet = new KNXNetIPMessageIO().parse(rBuf);
String xml = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(packet);
System.out.println(xml);
KNXNetIPMessage pack2 = mapper.readValue(xml, KNXNetIPMessage.class);
@@ -48,7 +48,7 @@ public class IOTest {
byte[] rData = Hex.decodeHex("0610020500180801c0a82a46c4090801c0a82a46c40a0203");
ObjectMapper mapper = new ObjectMapper().enableDefaultTyping();
ReadBuffer rBuf = new ReadBuffer(rData);
- KNXNetIPMessage packet = KNXNetIPMessageIO.parse(rBuf);
+ KNXNetIPMessage packet = new KNXNetIPMessageIO().parse(rBuf);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(packet);
System.out.println(json);
KNXNetIPMessage pack2 = mapper.readValue(json, KNXNetIPMessage.class);
@@ -59,13 +59,15 @@ public class IOTest {
public void testParser() throws Exception {
byte[] rData = Hex.decodeHex("0610020500180801c0a82a46c4090801c0a82a46c40a0203");
long start = System.currentTimeMillis();
- int numRunsParse = 2000000;
+ int numRunsParse = 20000;
+
+ KNXNetIPMessageIO knxNetIPMessageIO = new KNXNetIPMessageIO();
// Benchmark the parsing code
KNXNetIPMessage packet = null;
for(int i = 0; i < numRunsParse; i++) {
ReadBuffer rBuf = new ReadBuffer(rData);
- packet = KNXNetIPMessageIO.parse(rBuf);
+ packet = knxNetIPMessageIO.parse(rBuf);
}
long endParsing = System.currentTimeMillis();
@@ -73,11 +75,11 @@ public class IOTest {
System.out.println("That's " + ((float) (endParsing - start) / numRunsParse) + "ms per packet");
// Benchmark the serializing code
- int numRunsSerialize = 2000000;
+ int numRunsSerialize = 20000;
byte[] oData = null;
for(int i = 0; i < numRunsSerialize; i++) {
WriteBuffer wBuf = new WriteBuffer(packet.getLengthInBytes());
- KNXNetIPMessageIO.serialize(wBuf, packet);
+ knxNetIPMessageIO.serialize(wBuf, packet);
oData = wBuf.getData();
}
long endSerializing = System.currentTimeMillis();
diff --git a/sandbox/test-java-knxnetip-driver/src/test/java/org/apache/plc4x/java/knxnetip/KNXNetIpTestsuite.java b/sandbox/test-java-knxnetip-driver/src/test/java/org/apache/plc4x/java/knxnetip/KNXNetIpTestsuite.java
new file mode 100644
index 0000000..bb6fcde
--- /dev/null
+++ b/sandbox/test-java-knxnetip-driver/src/test/java/org/apache/plc4x/java/knxnetip/KNXNetIpTestsuite.java
@@ -0,0 +1,30 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+package org.apache.plc4x.java.knxnetip;
+
+import org.apache.plc4x.protocol.test.ProtocolTestsuiteRunner;
+
+public class KNXNetIpTestsuite extends ProtocolTestsuiteRunner {
+
+ public KNXNetIpTestsuite() {
+ super("/testsuite/KNXNetIPTestsuite.xml");
+ }
+
+}
diff --git a/sandbox/test-java-knxnetip-driver/src/test/resources/testsuite/KNXNetIPTestsuite.xml b/sandbox/test-java-knxnetip-driver/src/test/resources/testsuite/KNXNetIPTestsuite.xml
new file mode 100644
index 0000000..39274da
--- /dev/null
+++ b/sandbox/test-java-knxnetip-driver/src/test/resources/testsuite/KNXNetIPTestsuite.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+<test:testsuite xmlns:test="https://plc4x.apache.org/schemas/testsuite.xsd">
+
+ <name>KNXNet/IP</name>
+
+ <testcase>
+ <name>Default</name>
+ <raw>0610020500180801c0a82a46c4090801c0a82a46c40a0203</raw>
+ <root-type>KNXNetIPMessage</root-type>
+ <xml>
+ <ConnectionRequest className="org.apache.plc4x.java.knxnetip.ConnectionRequest">
+ <protocolVersion>16</protocolVersion>
+ <hpaiDiscoveryEndpoint>
+ <hostProtocolCode>1</hostProtocolCode>
+ <ipAddress>
+ <addr>
+ <addr>192</addr>
+ <addr>168</addr>
+ <addr>42</addr>
+ <addr>70</addr>
+ </addr>
+ </ipAddress>
+ <ipPort>50185</ipPort>
+ </hpaiDiscoveryEndpoint>
+ <hpaiDataEndpoint>
+ <hostProtocolCode>1</hostProtocolCode>
+ <ipAddress>
+ <addr>
+ <addr>192</addr>
+ <addr>168</addr>
+ <addr>42</addr>
+ <addr>70</addr>
+ </addr>
+ </ipAddress>
+ <ipPort>50186</ipPort>
+ </hpaiDataEndpoint>
+ <connectionRequestInformation className="org.apache.plc4x.java.knxnetip.ConnectionRequestInformationDeviceManagement"/>
+ </ConnectionRequest>
+ </xml>
+ </testcase>
+
+</test:testsuite>
\ No newline at end of file
diff --git a/sandbox/test-java-s7-driver/src/test/java/BenchmarkGeneratedS7.java b/sandbox/test-java-s7-driver/src/test/java/BenchmarkGeneratedS7.java
index 24fd194..e231fe9 100644
--- a/sandbox/test-java-s7-driver/src/test/java/BenchmarkGeneratedS7.java
+++ b/sandbox/test-java-s7-driver/src/test/java/BenchmarkGeneratedS7.java
@@ -31,12 +31,13 @@ public class BenchmarkGeneratedS7 {
byte[] rData = Hex.decodeHex("0300006702f080320100000001005600000407120a10060001032b84000160120a10020001032b840001a0120a10010001032b840001a9120a10050001032b84000150120a10020001032b84000198120a10040001032b84000140120a10020001032b84000190");
long start = System.currentTimeMillis();
int numRunsParse = 2000000;
+ TPKTPacketIO tpktPacketIO = new TPKTPacketIO();
// Benchmark the parsing code
TPKTPacket packet = null;
for(int i = 0; i < numRunsParse; i++) {
ReadBuffer rBuf = new ReadBuffer(rData);
- packet = TPKTPacketIO.parse(rBuf);
+ packet = tpktPacketIO.parse(rBuf);
}
long endParsing = System.currentTimeMillis();
@@ -48,7 +49,7 @@ public class BenchmarkGeneratedS7 {
byte[] oData = null;
for(int i = 0; i < numRunsSerialize; i++) {
WriteBuffer wBuf = new WriteBuffer(packet.getLengthInBytes());
- TPKTPacketIO.serialize(wBuf, packet);
+ tpktPacketIO.serialize(wBuf, packet);
oData = wBuf.getData();
}
long endSerializing = System.currentTimeMillis();