Browse Source

Add unit test for nodeset compiler and fix additional bugs

Stefan Profanter 7 years ago
parent
commit
b4d3b0dab5

+ 0 - 1
CMakeLists.txt

@@ -113,7 +113,6 @@ option(UA_BUILD_OSS_FUZZ "Special build switch used in oss-fuzz" OFF)
 mark_as_advanced(UA_BUILD_OSS_FUZZ)
 option(UA_DEBUG_DUMP_PKGS "Dump every package received by the server as hexdump format" OFF)
 mark_as_advanced(UA_DEBUG_DUMP_PKGS)
-option(UA_BUILD_EXAMPLES_NODESET_COMPILER "Generate an OPC UA information model from a nodeset XML (experimental)" OFF)
 
 # Advanced Build Targets
 option(UA_BUILD_SELFSIGNED_CERTIFICATE "Generate self-signed certificate" OFF)

+ 97 - 0
tests/CMakeLists.txt

@@ -194,3 +194,100 @@ add_test_valgrind(check_client_subscriptions ${TESTS_BINARY_DIR}/check_client_su
 add_executable(check_client_highlevel check_client_highlevel.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
 target_link_libraries(check_client_highlevel ${LIBS})
 add_test_valgrind(check_client_highlevel ${TESTS_BINARY_DIR}/check_client_highlevel)
+
+#############################
+#                           #
+# Test for Nodeset Compiler #
+#                           #
+#############################
+
+# Generate types for DI namespace
+set(UA_TYPES_OUT "ua_types_di")
+add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}_generated.c
+                   ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}_generated.h
+                   ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}_generated_handling.h
+                   ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}_generated_encoding_binary.h
+                   PRE_BUILD
+                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
+                   --namespace=2
+                   --type-csv=${PROJECT_SOURCE_DIR}/tests/ua-nodeset/DI/OpcUaDiModel.csv
+                   --type-bsd=${PROJECT_SOURCE_DIR}/tests/ua-nodeset/DI/Opc.Ua.Di.Types.bsd
+                   --no-builtin
+                   ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}
+                   DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
+                   ${PROJECT_SOURCE_DIR}/tests/ua-nodeset/DI/OpcUaDiModel.csv
+                   ${PROJECT_SOURCE_DIR}/tests/ua-nodeset/DI/Opc.Ua.Di.Types.bsd)
+
+# Generate types for ADI namespace
+set(UA_TYPES_OUT "ua_types_adi")
+add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}_generated.c
+                   ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}_generated.h
+                   ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}_generated_handling.h
+                   ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}_generated_encoding_binary.h
+                   PRE_BUILD
+                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
+                   --namespace=3
+                   --type-csv=${PROJECT_SOURCE_DIR}/tests/ua-nodeset/ADI/OpcUaAdiModel.csv
+                   --type-bsd=${PROJECT_SOURCE_DIR}/tests/ua-nodeset/ADI/Opc.Ua.Adi.Types.bsd
+                   --no-builtin
+                   ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}
+                   DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
+                   ${PROJECT_SOURCE_DIR}/tests/ua-nodeset/ADI/OpcUaAdiModel.csv
+                   ${PROJECT_SOURCE_DIR}/tests/ua-nodeset/ADI/Opc.Ua.Adi.Types.bsd)
+
+# generate DI namespace
+add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_di.c
+                   ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_di.h
+                   PRE_BUILD
+                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                   --types-array=UA_TYPES
+                   --types-array=UA_TYPES_DI
+                   --existing ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/Opc.Ua.NodeSet2.xml
+                   --xml ${PROJECT_SOURCE_DIR}/tests/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
+                   ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_di
+                   DEPENDS ${UA_NAMESPACE0_XML}
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
+                   ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/Opc.Ua.NodeSet2.xml
+                   ${PROJECT_SOURCE_DIR}/tests/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
+                   )
+
+
+# generate ADI namespace based on DI and ADI
+add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_adi.c
+                   ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_adi.h
+                   PRE_BUILD
+                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                   --types-array=UA_TYPES
+                   --types-array=UA_TYPES_DI
+                   --types-array=UA_TYPES_ADI
+                   --existing ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/Opc.Ua.NodeSet2.xml
+                   --existing ${PROJECT_SOURCE_DIR}/tests/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
+                   --xml ${PROJECT_SOURCE_DIR}/tests/ua-nodeset/ADI/Opc.Ua.Adi.NodeSet2.xml
+                   ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_adi
+                   DEPENDS ${UA_NAMESPACE0_XML}
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
+                   ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
+                   ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/Opc.Ua.NodeSet2.xml
+                   ${PROJECT_SOURCE_DIR}/tests/ua-nodeset/ADI/Opc.Ua.Adi.NodeSet2.xml
+                   )
+
+add_executable(check_nodeset_compiler
+               check_nodeset_compiler.c
+               ${PROJECT_BINARY_DIR}/src_generated/ua_types_di_generated.c
+               ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_di.c
+               ${PROJECT_BINARY_DIR}/src_generated/ua_types_adi_generated.c
+               ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_adi.c
+               $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
+target_link_libraries(check_nodeset_compiler ${LIBS})
+add_test_valgrind(check_nodeset_compiler ${TESTS_BINARY_DIR}/check_nodeset_compiler)

+ 60 - 0
tests/check_nodeset_compiler.c

@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ua_server.h"
+#include "ua_config_standard.h"
+
+#include "ua_namespace_di.h"
+#include "ua_namespace_adi.h"
+
+#include "check.h"
+#include "testing_clock.h"
+
+UA_Server *server = NULL;
+UA_ServerConfig *config = NULL;
+
+static void setup(void) {
+    config = UA_ServerConfig_new_default();
+    server = UA_Server_new(config);
+    UA_Server_run_startup(server);
+}
+
+static void teardown(void) {
+    UA_Server_run_shutdown(server);
+    UA_Server_delete(server);
+    UA_ServerConfig_delete(config);
+}
+
+
+START_TEST(Server_addDiNodeset) {
+    UA_StatusCode retval = ua_namespace_di(server);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+}
+END_TEST
+
+START_TEST(Server_addAdiNodeset) {
+    UA_StatusCode retval = ua_namespace_adi(server);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+}
+END_TEST
+
+static Suite* testSuite_Client(void) {
+    Suite *s = suite_create("Server Nodeset Compiler");
+    TCase *tc_server = tcase_create("Server DI and ADI nodeset");
+    tcase_add_checked_fixture(tc_server, setup, teardown);
+    tcase_add_test(tc_server, Server_addDiNodeset);
+    tcase_add_test(tc_server, Server_addAdiNodeset);
+    suite_add_tcase(s, tc_server);
+    return s;
+}
+
+int main(void) {
+    Suite *s = testSuite_Client();
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr,CK_NORMAL);
+    int number_failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 10 - 8
tools/nodeset_compiler/backend_open62541_datatypes.py

@@ -8,19 +8,21 @@ def generateBooleanCode(value):
     return "false"
 
 def generateStringCode(value, alloc=False):
-    return "UA_STRING{}(\"{}\")".format("_ALLOC" if alloc else "", value)
+    return "UA_STRING{}(\"{}\")".format("_ALLOC" if alloc else "", value.replace('"', r'\"'))
 
 def generateXmlElementCode(value, alloc=False):
-    return "UA_XMLELEMENT{}(\"{}\")".format("_ALLOC" if alloc else "", value)
+    return "UA_XMLELEMENT{}(\"{}\")".format("_ALLOC" if alloc else "", value.replace('"', r'\"'))
 
 def generateByteStringCode(value, alloc=False):
-    return "UA_BYTESTRING{}(\"{}\")".format("_ALLOC" if alloc else "", value)
+    return "UA_BYTESTRING{}(\"{}\")".format("_ALLOC" if alloc else "", value.replace('"', r'\"'))
 
 def generateLocalizedTextCode(value, alloc=False):
-    return "UA_LOCALIZEDTEXT{}(\"{}\", \"{}\")".format("_ALLOC" if alloc else "", value.locale, value.text)
+    return "UA_LOCALIZEDTEXT{}(\"{}\", \"{}\")".format("_ALLOC" if alloc else "",
+                                                       value.locale, value.text.replace('"', r'\"'))
 
 def generateQualifiedNameCode(value, alloc=False):
-    return "UA_QUALIFIEDNAME{}(ns{}, \"{}\")".format("_ALLOC" if alloc else "", str(value.ns), value.name)
+    return "UA_QUALIFIEDNAME{}(ns{}, \"{}\")".format("_ALLOC" if alloc else "",
+                                                     str(value.ns), value.name.replace('"', r'\"'))
 
 def generateNodeIdCode(value):
     if not value:
@@ -28,14 +30,14 @@ def generateNodeIdCode(value):
     if value.i != None:
         return "UA_NODEID_NUMERIC(ns%s,%s)" % (value.ns, value.i)
     elif value.s != None:
-        return "UA_NODEID_STRING(ns%s,%s)" % (value.ns, value.s)
+        return "UA_NODEID_STRING(ns%s,%s)" % (value.ns, value.s.replace('"', r'\"'))
     raise Exception(str(value) + " no NodeID generation for bytestring and guid..")
 
 def generateExpandedNodeIdCode(value):
     if value.i != None:
         return "UA_EXPANDEDNODEID_NUMERIC(ns%s, %s)" % (str(value.ns), str(value.i))
     elif value.s != None:
-        return "UA_EXPANDEDNODEID_STRING(ns%s, %s)" % (str(value.ns), value.s)
+        return "UA_EXPANDEDNODEID_STRING(ns%s, %s)" % (str(value.ns), value.s.replace('"', r'\"'))
     raise Exception(str(value) + " no NodeID generation for bytestring and guid..")
 
 def generateDateTimeCode(value):
@@ -51,7 +53,7 @@ def generateNodeValueCode(node, instanceName, asIndirect=False):
     elif type(node) == XmlElement:
         return generateXmlElementCode(node.value, asIndirect)
     elif type(node) == ByteString:
-        return generateByteStringCode(re.sub(r"[\r\n]+", "", node.value).replace('"', r'\"'), asIndirect)
+        return generateByteStringCode(re.sub(r"[\r\n]+", "", node.value), asIndirect)
     elif type(node) == LocalizedText:
         return generateLocalizedTextCode(node, asIndirect)
     elif type(node) == NodeId:

+ 4 - 1
tools/nodeset_compiler/nodes.py

@@ -166,6 +166,9 @@ class Node(object):
     def replaceNamespaces(self, nsMapping):
         self.id.ns = nsMapping[self.id.ns]
         self.browseName.ns = nsMapping[self.browseName.ns]
+        if hasattr(self, 'dataType') and isinstance(self.dataType, NodeId):
+            self.dataType.ns = nsMapping[self.dataType.ns]
+
         new_refs = set()
         for ref in self.references:
             ref.source.ns = nsMapping[ref.source.ns]
@@ -225,7 +228,7 @@ class VariableNode(Node):
         Node.__init__(self)
         self.nodeClass = NODE_CLASS_VARIABLE
         self.dataType = NodeId()
-        self.valueRank = -1
+        self.valueRank = -2
         self.arrayDimensions = []
         # Set access levels to read by default
         self.accessLevel = 1

+ 4 - 1
tools/travis/travis_linux_script.sh

@@ -194,9 +194,12 @@ else
     echo -en 'travis_fold:end:script.build.multithread\\r'
 
     echo -e "\r\n== Debug build and unit tests (64 bit, python 2) ==" && echo -en 'travis_fold:start:script.build.unit_test_valgrind_python2\\r'
+    git clone --depth 1 https://github.com/OPCFoundation/UA-Nodeset.git tests/ua-nodeset
     mkdir -p build && cd build
     # Force to use python2 to test compilation with python2
-    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python2 -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=ON -DUA_ENABLE_VALGRIND_UNIT_TESTS=ON ..
+    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python2 -DUA_NAMESPACE0_XML=../tools/schema/namespace0/Opc.Ua.NodeSet2.xml -DUA_DATATYPES_FILE= \
+    -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON \
+    -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=ON -DUA_ENABLE_VALGRIND_UNIT_TESTS=ON ..
     make -j && make test ARGS="-V"
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf