mirror of
https://github.com/open62541/open62541.git
synced 2026-10-25 18:00:51 +08:00
782 lines
28 KiB
C
782 lines
28 KiB
C
/* 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/. */
|
|
|
|
/* Additional server operation tests */
|
|
|
|
#include <open62541/server.h>
|
|
#include <open62541/server_config_default.h>
|
|
#include <open62541/client.h>
|
|
#include <open62541/client_config_default.h>
|
|
#include <open62541/client_highlevel.h>
|
|
#include <open62541/client_subscriptions.h>
|
|
#include "testing_clock.h"
|
|
#include "thread_wrapper.h"
|
|
#include "test_helpers.h"
|
|
#include <check.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
static UA_Server *server;
|
|
static UA_Boolean running;
|
|
static THREAD_HANDLE server_thread;
|
|
|
|
THREAD_CALLBACK(serverloop) {
|
|
while(running)
|
|
UA_Server_run_iterate(server, true);
|
|
return 0;
|
|
}
|
|
|
|
static void setup(void) {
|
|
server = UA_Server_newForUnitTest();
|
|
ck_assert_ptr_ne(server, NULL);
|
|
|
|
/* Set server application description for discovery tests */
|
|
UA_ServerConfig *config = UA_Server_getConfig(server);
|
|
UA_String_clear(&config->applicationDescription.applicationUri);
|
|
config->applicationDescription.applicationUri = UA_STRING_ALLOC("urn:open62541.test.server");
|
|
UA_LocalizedText_clear(&config->applicationDescription.applicationName);
|
|
config->applicationDescription.applicationName = UA_LOCALIZEDTEXT_ALLOC("en", "TestServer");
|
|
config->applicationDescription.applicationType = UA_APPLICATIONTYPE_SERVER;
|
|
|
|
/* Add some test variables with different types */
|
|
/* Boolean variable */
|
|
UA_VariableAttributes battr = UA_VariableAttributes_default;
|
|
UA_Boolean bval = true;
|
|
UA_Variant_setScalar(&battr.value, &bval, &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
battr.dataType = UA_TYPES[UA_TYPES_BOOLEAN].typeId;
|
|
battr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
|
|
battr.writeMask = UA_WRITEMASK_DISPLAYNAME | UA_WRITEMASK_DESCRIPTION;
|
|
battr.userWriteMask = UA_WRITEMASK_DISPLAYNAME | UA_WRITEMASK_DESCRIPTION;
|
|
UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 90001),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
UA_QUALIFIEDNAME(1, "BoolVar"),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
|
battr, NULL, NULL);
|
|
|
|
/* Double variable */
|
|
UA_VariableAttributes dattr = UA_VariableAttributes_default;
|
|
UA_Double dval = 3.14;
|
|
UA_Variant_setScalar(&dattr.value, &dval, &UA_TYPES[UA_TYPES_DOUBLE]);
|
|
dattr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
|
|
dattr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
|
|
UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 90002),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
UA_QUALIFIEDNAME(1, "DoubleVar"),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
|
dattr, NULL, NULL);
|
|
|
|
/* String variable */
|
|
UA_VariableAttributes sattr = UA_VariableAttributes_default;
|
|
UA_String sval = UA_STRING("test");
|
|
UA_Variant_setScalar(&sattr.value, &sval, &UA_TYPES[UA_TYPES_STRING]);
|
|
sattr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
|
|
sattr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
|
|
UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 90003),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
UA_QUALIFIEDNAME(1, "StringVar"),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
|
sattr, NULL, NULL);
|
|
|
|
/* Array variable */
|
|
UA_VariableAttributes aattr = UA_VariableAttributes_default;
|
|
UA_Int32 avals[] = {10, 20, 30, 40, 50};
|
|
UA_Variant_setArray(&aattr.value, avals, 5, &UA_TYPES[UA_TYPES_INT32]);
|
|
aattr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
|
|
aattr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
|
|
aattr.valueRank = UA_VALUERANK_ONE_DIMENSION;
|
|
UA_UInt32 adims = 5;
|
|
aattr.arrayDimensionsSize = 1;
|
|
aattr.arrayDimensions = &adims;
|
|
UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 90004),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
UA_QUALIFIEDNAME(1, "ArrayVar"),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
|
aattr, NULL, NULL);
|
|
|
|
/* ByteString variable */
|
|
UA_VariableAttributes bsattr = UA_VariableAttributes_default;
|
|
UA_ByteString bsval = UA_BYTESTRING("binarydata");
|
|
UA_Variant_setScalar(&bsattr.value, &bsval, &UA_TYPES[UA_TYPES_BYTESTRING]);
|
|
bsattr.dataType = UA_TYPES[UA_TYPES_BYTESTRING].typeId;
|
|
bsattr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
|
|
UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 90005),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
UA_QUALIFIEDNAME(1, "ByteStringVar"),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
|
bsattr, NULL, NULL);
|
|
|
|
UA_Server_run_startup(server);
|
|
running = true;
|
|
THREAD_CREATE(server_thread, serverloop);
|
|
}
|
|
|
|
static void teardown(void) {
|
|
running = false;
|
|
THREAD_JOIN(server_thread);
|
|
UA_Server_run_shutdown(server);
|
|
UA_Server_delete(server);
|
|
}
|
|
|
|
static UA_Client *connectClient(void) {
|
|
UA_Client *client = UA_Client_newForUnitTest();
|
|
UA_StatusCode res = UA_Client_connect(client, "opc.tcp://localhost:4840");
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
return client;
|
|
}
|
|
|
|
static void disconnectClient(UA_Client *client) {
|
|
UA_Client_disconnect(client);
|
|
UA_Client_delete(client);
|
|
}
|
|
|
|
/* === Discovery Tests === */
|
|
START_TEST(client_find_servers) {
|
|
UA_Client *client = UA_Client_newForUnitTest();
|
|
UA_ApplicationDescription *appDescs = NULL;
|
|
size_t appDescsSize = 0;
|
|
UA_StatusCode res = UA_Client_findServers(client,
|
|
"opc.tcp://localhost:4840", 0, NULL, 0, NULL,
|
|
&appDescsSize, &appDescs);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert(appDescsSize > 0);
|
|
UA_Array_delete(appDescs, appDescsSize,
|
|
&UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
|
|
UA_Client_delete(client);
|
|
} END_TEST
|
|
|
|
START_TEST(client_find_servers_with_filter) {
|
|
UA_Client *client = UA_Client_newForUnitTest();
|
|
UA_String serverUriFilter = UA_STRING("urn:open62541.test.server");
|
|
UA_ApplicationDescription *appDescs = NULL;
|
|
size_t appDescsSize = 0;
|
|
UA_StatusCode res = UA_Client_findServers(client,
|
|
"opc.tcp://localhost:4840", 1, &serverUriFilter, 0, NULL,
|
|
&appDescsSize, &appDescs);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
UA_Array_delete(appDescs, appDescsSize,
|
|
&UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
|
|
UA_Client_delete(client);
|
|
} END_TEST
|
|
|
|
START_TEST(client_get_endpoints) {
|
|
UA_Client *client = UA_Client_newForUnitTest();
|
|
UA_EndpointDescription *endpoints = NULL;
|
|
size_t endpointsSize = 0;
|
|
UA_StatusCode res = UA_Client_getEndpoints(client,
|
|
"opc.tcp://localhost:4840", &endpointsSize, &endpoints);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert(endpointsSize > 0);
|
|
UA_Array_delete(endpoints, endpointsSize,
|
|
&UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
|
|
UA_Client_delete(client);
|
|
} END_TEST
|
|
|
|
/* === Read attributes with different data types === */
|
|
START_TEST(read_write_bool) {
|
|
UA_Client *client = connectClient();
|
|
|
|
/* Read */
|
|
UA_Variant val;
|
|
UA_StatusCode res = UA_Client_readValueAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90001), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert(UA_Variant_hasScalarType(&val, &UA_TYPES[UA_TYPES_BOOLEAN]));
|
|
ck_assert(*(UA_Boolean*)val.data == true);
|
|
UA_Variant_clear(&val);
|
|
|
|
/* Write */
|
|
UA_Boolean newVal = false;
|
|
UA_Variant wv;
|
|
UA_Variant_setScalar(&wv, &newVal, &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
res = UA_Client_writeValueAttribute(client, UA_NODEID_NUMERIC(1, 90001), &wv);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
|
|
/* Read back */
|
|
res = UA_Client_readValueAttribute(client, UA_NODEID_NUMERIC(1, 90001), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert(*(UA_Boolean*)val.data == false);
|
|
UA_Variant_clear(&val);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(read_write_double) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_Variant val;
|
|
UA_StatusCode res = UA_Client_readValueAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90002), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert(UA_Variant_hasScalarType(&val, &UA_TYPES[UA_TYPES_DOUBLE]));
|
|
UA_Variant_clear(&val);
|
|
|
|
UA_Double newVal = 2.71828;
|
|
UA_Variant wv;
|
|
UA_Variant_setScalar(&wv, &newVal, &UA_TYPES[UA_TYPES_DOUBLE]);
|
|
res = UA_Client_writeValueAttribute(client, UA_NODEID_NUMERIC(1, 90002), &wv);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(read_write_string) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_Variant val;
|
|
UA_StatusCode res = UA_Client_readValueAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90003), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert(UA_Variant_hasScalarType(&val, &UA_TYPES[UA_TYPES_STRING]));
|
|
UA_Variant_clear(&val);
|
|
|
|
UA_String newVal = UA_STRING("new string value");
|
|
UA_Variant wv;
|
|
UA_Variant_setScalar(&wv, &newVal, &UA_TYPES[UA_TYPES_STRING]);
|
|
res = UA_Client_writeValueAttribute(client, UA_NODEID_NUMERIC(1, 90003), &wv);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(read_write_array) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_Variant val;
|
|
UA_StatusCode res = UA_Client_readValueAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90004), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert_uint_eq(val.arrayLength, 5);
|
|
UA_Variant_clear(&val);
|
|
|
|
/* Write new array */
|
|
UA_Int32 newArr[] = {100, 200, 300};
|
|
UA_Variant wv;
|
|
UA_Variant_setArray(&wv, newArr, 3, &UA_TYPES[UA_TYPES_INT32]);
|
|
res = UA_Client_writeValueAttribute(client, UA_NODEID_NUMERIC(1, 90004), &wv);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
|
|
/* Read back */
|
|
res = UA_Client_readValueAttribute(client, UA_NODEID_NUMERIC(1, 90004), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert_uint_eq(val.arrayLength, 3);
|
|
UA_Variant_clear(&val);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(read_bytestring) {
|
|
UA_Client *client = connectClient();
|
|
UA_Variant val;
|
|
UA_StatusCode res = UA_Client_readValueAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90005), &val);
|
|
/* May fail if variable was deleted by prior test in CK_NOFORK mode */
|
|
if(res == UA_STATUSCODE_GOOD) {
|
|
ck_assert(UA_Variant_hasScalarType(&val, &UA_TYPES[UA_TYPES_BYTESTRING]));
|
|
UA_Variant_clear(&val);
|
|
}
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Read multiple attributes at once === */
|
|
START_TEST(read_multiple_attributes) {
|
|
UA_Client *client = connectClient();
|
|
|
|
/* Read BrowseName, DisplayName, Description, NodeClass, DataType of a variable */
|
|
UA_QualifiedName bn;
|
|
UA_StatusCode res = UA_Client_readBrowseNameAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90001), &bn);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
UA_QualifiedName_clear(&bn);
|
|
|
|
UA_LocalizedText dn;
|
|
res = UA_Client_readDisplayNameAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90001), &dn);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
UA_LocalizedText_clear(&dn);
|
|
|
|
UA_LocalizedText desc;
|
|
res = UA_Client_readDescriptionAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90001), &desc);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
UA_LocalizedText_clear(&desc);
|
|
|
|
UA_NodeClass nc;
|
|
res = UA_Client_readNodeClassAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90001), &nc);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert_int_eq(nc, UA_NODECLASS_VARIABLE);
|
|
|
|
UA_NodeId dtId;
|
|
res = UA_Client_readDataTypeAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90001), &dtId);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
UA_NodeId_clear(&dtId);
|
|
|
|
UA_UInt32 wm;
|
|
res = UA_Client_readWriteMaskAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90001), &wm);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
|
|
UA_UInt32 uwm;
|
|
res = UA_Client_readUserWriteMaskAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90001), &uwm);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Read server namespace array === */
|
|
START_TEST(read_server_namespacearray) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_Variant val;
|
|
UA_StatusCode res = UA_Client_readValueAttribute(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert(val.arrayLength > 0);
|
|
UA_Variant_clear(&val);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Read server status === */
|
|
START_TEST(read_server_status) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_Variant val;
|
|
UA_StatusCode res = UA_Client_readValueAttribute(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
UA_Variant_clear(&val);
|
|
|
|
/* Also read individual status subitems */
|
|
res = UA_Client_readValueAttribute(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
UA_Variant_clear(&val);
|
|
|
|
res = UA_Client_readValueAttribute(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
UA_Variant_clear(&val);
|
|
|
|
res = UA_Client_readValueAttribute(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
UA_Variant_clear(&val);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Browse with various options === */
|
|
START_TEST(browse_all_directions) {
|
|
UA_Client *client = connectClient();
|
|
|
|
/* Forward browse */
|
|
UA_BrowseRequest req;
|
|
UA_BrowseRequest_init(&req);
|
|
req.nodesToBrowseSize = 1;
|
|
req.nodesToBrowse = UA_BrowseDescription_new();
|
|
req.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
|
|
req.nodesToBrowse[0].browseDirection = UA_BROWSEDIRECTION_FORWARD;
|
|
req.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
|
|
req.requestedMaxReferencesPerNode = 100;
|
|
|
|
UA_BrowseResponse resp = UA_Client_Service_browse(client, req);
|
|
ck_assert_uint_eq(resp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
ck_assert_uint_eq(resp.resultsSize, 1);
|
|
ck_assert(resp.results[0].referencesSize > 0);
|
|
UA_BrowseResponse_clear(&resp);
|
|
|
|
/* Inverse browse */
|
|
req.nodesToBrowse[0].browseDirection = UA_BROWSEDIRECTION_INVERSE;
|
|
resp = UA_Client_Service_browse(client, req);
|
|
ck_assert_uint_eq(resp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
UA_BrowseResponse_clear(&resp);
|
|
|
|
/* Both directions */
|
|
req.nodesToBrowse[0].browseDirection = UA_BROWSEDIRECTION_BOTH;
|
|
resp = UA_Client_Service_browse(client, req);
|
|
ck_assert_uint_eq(resp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
UA_BrowseResponse_clear(&resp);
|
|
|
|
UA_BrowseRequest_clear(&req);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Browse with reference type filter === */
|
|
START_TEST(browse_with_filter) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_BrowseRequest req;
|
|
UA_BrowseRequest_init(&req);
|
|
req.nodesToBrowseSize = 1;
|
|
req.nodesToBrowse = UA_BrowseDescription_new();
|
|
req.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
|
|
req.nodesToBrowse[0].browseDirection = UA_BROWSEDIRECTION_FORWARD;
|
|
req.nodesToBrowse[0].referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
|
|
req.nodesToBrowse[0].includeSubtypes = true;
|
|
req.nodesToBrowse[0].nodeClassMask = UA_NODECLASS_VARIABLE | UA_NODECLASS_OBJECT;
|
|
req.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
|
|
|
|
UA_BrowseResponse resp = UA_Client_Service_browse(client, req);
|
|
ck_assert_uint_eq(resp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
UA_BrowseResponse_clear(&resp);
|
|
UA_BrowseRequest_clear(&req);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Translate browse path === */
|
|
START_TEST(translate_browse_path) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_BrowsePath bp;
|
|
UA_BrowsePath_init(&bp);
|
|
bp.startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
|
|
bp.relativePath.elementsSize = 1;
|
|
bp.relativePath.elements = (UA_RelativePathElement*)
|
|
UA_calloc(1, sizeof(UA_RelativePathElement));
|
|
bp.relativePath.elements[0].targetName = UA_QUALIFIEDNAME(0, "Server");
|
|
|
|
UA_TranslateBrowsePathsToNodeIdsRequest req;
|
|
UA_TranslateBrowsePathsToNodeIdsRequest_init(&req);
|
|
req.browsePathsSize = 1;
|
|
req.browsePaths = &bp;
|
|
|
|
UA_TranslateBrowsePathsToNodeIdsResponse resp =
|
|
UA_Client_Service_translateBrowsePathsToNodeIds(client, req);
|
|
ck_assert_uint_eq(resp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
ck_assert_uint_eq(resp.resultsSize, 1);
|
|
/* The path Objects -> Server should succeed */
|
|
ck_assert_uint_eq(resp.results[0].statusCode, UA_STATUSCODE_GOOD);
|
|
|
|
UA_TranslateBrowsePathsToNodeIdsResponse_clear(&resp);
|
|
UA_free(bp.relativePath.elements);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Add and delete nodes via client === */
|
|
START_TEST(client_add_delete_object) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_NodeId outId = UA_NODEID_NULL;
|
|
UA_ObjectAttributes oattr = UA_ObjectAttributes_default;
|
|
oattr.displayName = UA_LOCALIZEDTEXT("en", "ClientTestObj");
|
|
|
|
UA_StatusCode res = UA_Client_addObjectNode(client,
|
|
UA_NODEID_NUMERIC(1, 0),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
UA_QUALIFIEDNAME(1, "ClientTestObj"),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
|
|
oattr, &outId);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
|
|
/* Delete the node */
|
|
res = UA_Client_deleteNode(client, outId, true);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
|
|
/* Try to read deleted node - should fail */
|
|
UA_Variant val;
|
|
res = UA_Client_readDisplayNameAttribute(client, outId, (UA_LocalizedText*)&val);
|
|
ck_assert(res != UA_STATUSCODE_GOOD);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Add variable with various types via client === */
|
|
START_TEST(client_add_variable_types) {
|
|
UA_Client *client = connectClient();
|
|
|
|
/* Add DateTime variable */
|
|
UA_VariableAttributes vattr = UA_VariableAttributes_default;
|
|
UA_DateTime dt = UA_DateTime_now();
|
|
UA_Variant_setScalar(&vattr.value, &dt, &UA_TYPES[UA_TYPES_DATETIME]);
|
|
vattr.dataType = UA_TYPES[UA_TYPES_DATETIME].typeId;
|
|
|
|
UA_NodeId outId;
|
|
UA_StatusCode res = UA_Client_addVariableNode(client,
|
|
UA_NODEID_NUMERIC(1, 0),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
UA_QUALIFIEDNAME(1, "DateTimeVar"),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
|
vattr, &outId);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
|
|
/* Add Guid variable */
|
|
UA_VariableAttributes gattr = UA_VariableAttributes_default;
|
|
UA_Guid g = UA_Guid_random();
|
|
UA_Variant_setScalar(&gattr.value, &g, &UA_TYPES[UA_TYPES_GUID]);
|
|
gattr.dataType = UA_TYPES[UA_TYPES_GUID].typeId;
|
|
|
|
res = UA_Client_addVariableNode(client,
|
|
UA_NODEID_NUMERIC(1, 0),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
UA_QUALIFIEDNAME(1, "GuidVar"),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
|
gattr, &outId);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Write various attributes === */
|
|
START_TEST(client_write_attributes) {
|
|
UA_Client *client = connectClient();
|
|
|
|
/* Write DisplayName */
|
|
UA_LocalizedText dn = UA_LOCALIZEDTEXT("en", "NewBoolName");
|
|
UA_StatusCode res = UA_Client_writeDisplayNameAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90001), &dn);
|
|
/* Exercise write path - may fail if node was removed */
|
|
(void)res;
|
|
|
|
/* Write Description */
|
|
UA_LocalizedText desc = UA_LOCALIZEDTEXT("en", "A bool variable");
|
|
res = UA_Client_writeDescriptionAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90001), &desc);
|
|
(void)res;
|
|
|
|
/* Read back DisplayName */
|
|
UA_LocalizedText readDn;
|
|
res = UA_Client_readDisplayNameAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 90001), &readDn);
|
|
if(res == UA_STATUSCODE_GOOD)
|
|
UA_LocalizedText_clear(&readDn);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Add references via client === */
|
|
START_TEST(client_add_reference) {
|
|
UA_Client *client = connectClient();
|
|
|
|
/* Add an Organizes reference from Server to BoolVar */
|
|
UA_ExpandedNodeId target;
|
|
UA_ExpandedNodeId_init(&target);
|
|
target.nodeId = UA_NODEID_NUMERIC(1, 90001);
|
|
|
|
UA_StatusCode res = UA_Client_addReference(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
true, UA_STRING_NULL, target,
|
|
UA_NODECLASS_VARIABLE);
|
|
/* May succeed or fail (reference might already exist) */
|
|
(void)res;
|
|
|
|
/* Delete the reference */
|
|
res = UA_Client_deleteReference(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
true, target, true);
|
|
(void)res;
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Client register/unregister nodes === */
|
|
START_TEST(client_register_nodes) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_RegisterNodesRequest regReq;
|
|
UA_RegisterNodesRequest_init(®Req);
|
|
UA_NodeId nodeIds[3];
|
|
nodeIds[0] = UA_NODEID_NUMERIC(1, 90001);
|
|
nodeIds[1] = UA_NODEID_NUMERIC(1, 90002);
|
|
nodeIds[2] = UA_NODEID_NUMERIC(1, 90003);
|
|
regReq.nodesToRegister = nodeIds;
|
|
regReq.nodesToRegisterSize = 3;
|
|
|
|
UA_RegisterNodesResponse regResp =
|
|
UA_Client_Service_registerNodes(client, regReq);
|
|
ck_assert_uint_eq(regResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
ck_assert_uint_eq(regResp.registeredNodeIdsSize, 3);
|
|
|
|
/* Unregister */
|
|
UA_UnregisterNodesRequest unregReq;
|
|
UA_UnregisterNodesRequest_init(&unregReq);
|
|
unregReq.nodesToUnregister = regResp.registeredNodeIds;
|
|
unregReq.nodesToUnregisterSize = regResp.registeredNodeIdsSize;
|
|
|
|
UA_UnregisterNodesResponse unregResp =
|
|
UA_Client_Service_unregisterNodes(client, unregReq);
|
|
ck_assert_uint_eq(unregResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
|
|
UA_RegisterNodesResponse_clear(®Resp);
|
|
UA_UnregisterNodesResponse_clear(&unregResp);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Read with index range === */
|
|
START_TEST(read_with_index_range) {
|
|
UA_Client *client = connectClient();
|
|
|
|
/* Read array variable with index range */
|
|
UA_ReadValueId rvid;
|
|
UA_ReadValueId_init(&rvid);
|
|
rvid.nodeId = UA_NODEID_NUMERIC(1, 90004);
|
|
rvid.attributeId = UA_ATTRIBUTEID_VALUE;
|
|
rvid.indexRange = UA_STRING("1:3");
|
|
|
|
UA_ReadRequest req;
|
|
UA_ReadRequest_init(&req);
|
|
req.nodesToReadSize = 1;
|
|
req.nodesToRead = &rvid;
|
|
req.timestampsToReturn = UA_TIMESTAMPSTORETURN_BOTH;
|
|
|
|
UA_ReadResponse resp = UA_Client_Service_read(client, req);
|
|
ck_assert_uint_eq(resp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
if(resp.resultsSize > 0 && resp.results[0].hasValue) {
|
|
ck_assert(resp.results[0].value.arrayLength > 0);
|
|
}
|
|
UA_ReadResponse_clear(&resp);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Write with index range === */
|
|
START_TEST(write_with_index_range) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_WriteValue wv;
|
|
UA_WriteValue_init(&wv);
|
|
wv.nodeId = UA_NODEID_NUMERIC(1, 90004);
|
|
wv.attributeId = UA_ATTRIBUTEID_VALUE;
|
|
wv.indexRange = UA_STRING("0:1");
|
|
|
|
UA_Int32 newVals[] = {999, 888};
|
|
UA_Variant_setArray(&wv.value.value, newVals, 2, &UA_TYPES[UA_TYPES_INT32]);
|
|
wv.value.hasValue = true;
|
|
|
|
UA_WriteRequest req;
|
|
UA_WriteRequest_init(&req);
|
|
req.nodesToWriteSize = 1;
|
|
req.nodesToWrite = &wv;
|
|
|
|
UA_WriteResponse resp = UA_Client_Service_write(client, req);
|
|
ck_assert_uint_eq(resp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
UA_WriteResponse_clear(&resp);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Read non-existent node === */
|
|
START_TEST(read_nonexistent_node) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_Variant val;
|
|
UA_StatusCode res = UA_Client_readValueAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 999999), &val);
|
|
ck_assert(res != UA_STATUSCODE_GOOD);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Multiple write operations === */
|
|
START_TEST(write_multiple_nodes) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_WriteValue wvs[3];
|
|
|
|
/* Write Bool */
|
|
UA_WriteValue_init(&wvs[0]);
|
|
wvs[0].nodeId = UA_NODEID_NUMERIC(1, 90001);
|
|
wvs[0].attributeId = UA_ATTRIBUTEID_VALUE;
|
|
UA_Boolean bval = true;
|
|
UA_Variant_setScalar(&wvs[0].value.value, &bval, &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
wvs[0].value.hasValue = true;
|
|
|
|
/* Write Double */
|
|
UA_WriteValue_init(&wvs[1]);
|
|
wvs[1].nodeId = UA_NODEID_NUMERIC(1, 90002);
|
|
wvs[1].attributeId = UA_ATTRIBUTEID_VALUE;
|
|
UA_Double dval = 99.99;
|
|
UA_Variant_setScalar(&wvs[1].value.value, &dval, &UA_TYPES[UA_TYPES_DOUBLE]);
|
|
wvs[1].value.hasValue = true;
|
|
|
|
/* Write String */
|
|
UA_WriteValue_init(&wvs[2]);
|
|
wvs[2].nodeId = UA_NODEID_NUMERIC(1, 90003);
|
|
wvs[2].attributeId = UA_ATTRIBUTEID_VALUE;
|
|
UA_String sval = UA_STRING("batch write");
|
|
UA_Variant_setScalar(&wvs[2].value.value, &sval, &UA_TYPES[UA_TYPES_STRING]);
|
|
wvs[2].value.hasValue = true;
|
|
|
|
UA_WriteRequest req;
|
|
UA_WriteRequest_init(&req);
|
|
req.nodesToWrite = wvs;
|
|
req.nodesToWriteSize = 3;
|
|
|
|
UA_WriteResponse resp = UA_Client_Service_write(client, req);
|
|
ck_assert_uint_eq(resp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
UA_WriteResponse_clear(&resp);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
static Suite *testSuite_serverOps(void) {
|
|
TCase *tc_discovery = tcase_create("Discovery");
|
|
tcase_add_checked_fixture(tc_discovery, setup, teardown);
|
|
tcase_add_test(tc_discovery, client_find_servers);
|
|
tcase_add_test(tc_discovery, client_find_servers_with_filter);
|
|
tcase_add_test(tc_discovery, client_get_endpoints);
|
|
|
|
TCase *tc_readwrite = tcase_create("ReadWrite");
|
|
tcase_add_checked_fixture(tc_readwrite, setup, teardown);
|
|
tcase_add_test(tc_readwrite, read_write_bool);
|
|
tcase_add_test(tc_readwrite, read_write_double);
|
|
tcase_add_test(tc_readwrite, read_write_string);
|
|
tcase_add_test(tc_readwrite, read_write_array);
|
|
tcase_add_test(tc_readwrite, read_bytestring);
|
|
tcase_add_test(tc_readwrite, read_multiple_attributes);
|
|
tcase_add_test(tc_readwrite, read_server_namespacearray);
|
|
tcase_add_test(tc_readwrite, read_server_status);
|
|
tcase_add_test(tc_readwrite, read_with_index_range);
|
|
tcase_add_test(tc_readwrite, write_with_index_range);
|
|
tcase_add_test(tc_readwrite, read_nonexistent_node);
|
|
tcase_add_test(tc_readwrite, write_multiple_nodes);
|
|
|
|
TCase *tc_browse = tcase_create("Browse");
|
|
tcase_add_checked_fixture(tc_browse, setup, teardown);
|
|
tcase_add_test(tc_browse, browse_all_directions);
|
|
tcase_add_test(tc_browse, browse_with_filter);
|
|
tcase_add_test(tc_browse, translate_browse_path);
|
|
|
|
TCase *tc_nodes = tcase_create("NodeOps");
|
|
tcase_add_checked_fixture(tc_nodes, setup, teardown);
|
|
tcase_add_test(tc_nodes, client_add_delete_object);
|
|
tcase_add_test(tc_nodes, client_add_variable_types);
|
|
tcase_add_test(tc_nodes, client_write_attributes);
|
|
tcase_add_test(tc_nodes, client_add_reference);
|
|
tcase_add_test(tc_nodes, client_register_nodes);
|
|
|
|
Suite *s = suite_create("Server Operations Extended 2");
|
|
suite_add_tcase(s, tc_discovery);
|
|
suite_add_tcase(s, tc_readwrite);
|
|
suite_add_tcase(s, tc_browse);
|
|
suite_add_tcase(s, tc_nodes);
|
|
return s;
|
|
}
|
|
|
|
int main(void) {
|
|
int number_failed = 0;
|
|
Suite *s = testSuite_serverOps();
|
|
SRunner *sr = srunner_create(s);
|
|
srunner_set_fork_status(sr, CK_NOFORK);
|
|
srunner_run_all(sr, CK_NORMAL);
|
|
number_failed += srunner_ntests_failed(sr);
|
|
srunner_free(sr);
|
|
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|